From 812d33f850c1f83351f8a6c17376e11f178f7c22 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 1 Oct 2019 08:09:01 +0300 Subject: [PATCH 001/185] Add ExpandNumberPiece configuration with OsuLegacySkinTransformer --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 4 ++++ osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs | 1 + 2 files changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 479c250eab..c9ed313593 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -123,6 +123,10 @@ namespace osu.Game.Rulesets.Osu.Skinning return SkinUtils.As(new BindableFloat(legacy_circle_radius)); break; + + case OsuSkinConfiguration.ExpandNumberPiece: + string legacyVersion = source.GetConfig("Version")?.Value ?? "1"; + return SkinUtils.As(new BindableBool(double.TryParse(legacyVersion, out double version) && version < 2.0)); } break; diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index 98219cafe8..85a7f5b0cd 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -7,6 +7,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { HitCirclePrefix, HitCircleOverlap, + ExpandNumberPiece, SliderBorderSize, SliderPathRadius, AllowSliderBallTint, From 6ba1bc381c07d68d7af35d3e631dbe6cac7b9b93 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 1 Oct 2019 08:14:15 +0300 Subject: [PATCH 002/185] Add version value to the DefaultSkinConfiguration dictionary --- osu.Game/Skinning/DefaultSkinConfiguration.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/DefaultSkinConfiguration.cs b/osu.Game/Skinning/DefaultSkinConfiguration.cs index f52fac6077..481360beb9 100644 --- a/osu.Game/Skinning/DefaultSkinConfiguration.cs +++ b/osu.Game/Skinning/DefaultSkinConfiguration.cs @@ -19,6 +19,8 @@ namespace osu.Game.Skinning new Color4(204, 102, 0, 255), new Color4(121, 9, 13, 255) }); + + ConfigDictionary.Add(@"Version", "latest"); } } } From 9e314cd664991c1836d0cb258405b6652e5f4a03 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 1 Oct 2019 08:15:48 +0300 Subject: [PATCH 003/185] Add expand number piece bindable to hit circle --- .../Objects/Drawables/DrawableHitCircle.cs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index bb227d76df..15c768fff9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -10,9 +10,10 @@ 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 osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Scoring; -using osuTK; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -20,6 +21,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public ApproachCircle ApproachCircle { get; } + public IBindable ExpandNumberPiece => expandNumberPiece; + + private readonly BindableBool expandNumberPiece = new BindableBool(); + private readonly IBindable positionBindable = new Bindable(); private readonly IBindable stackHeightBindable = new Bindable(); private readonly IBindable scaleBindable = new Bindable(); @@ -106,6 +111,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + protected override void ApplySkin(ISkinSource skin, bool allowFallback) + { + base.ApplySkin(skin, allowFallback); + + expandNumberPiece.Value = skin.GetConfig(OsuSkinConfiguration.ExpandNumberPiece).Value; + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { Debug.Assert(HitObject.HitWindows != null); From 5aa85968c232927d42a35344437a5f3376fe3321 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 1 Oct 2019 08:23:41 +0300 Subject: [PATCH 004/185] Expand number piece for old skins in legacy circle pieces --- .../Skinning/LegacyMainCirclePiece.cs | 40 ++++++++++++++----- 1 file changed, 31 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 93ae0371df..d93d0506c3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -10,9 +10,12 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; +using System.Collections.Generic; +using System.Linq; namespace osu.Game.Rulesets.Osu.Skinning { @@ -23,17 +26,24 @@ namespace osu.Game.Rulesets.Osu.Skinning Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } + private Sprite hitCircleSprite; + private SkinnableSpriteText hitCircleText; + + private List scalables; + private readonly IBindable state = new Bindable(); private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); + private readonly IBindable expandNumberPiece = new BindableBool(); + + [Resolved] + private DrawableHitObject drawableObject { get; set; } [BackgroundDependencyLoader] - private void load(DrawableHitObject drawableObject, ISkinSource skin) + private void load(ISkinSource skin) { OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; - - Sprite hitCircleSprite; - SkinnableSpriteText hitCircleText; + DrawableHitCircle drawableCircle = (DrawableHitCircle)drawableObject; InternalChildren = new Drawable[] { @@ -58,13 +68,25 @@ namespace osu.Game.Rulesets.Osu.Skinning }; state.BindTo(drawableObject.State); - state.BindValueChanged(updateState, true); - accentColour.BindTo(drawableObject.AccentColour); - accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); - indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); + expandNumberPiece.BindTo(drawableCircle.ExpandNumberPiece); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + state.BindValueChanged(updateState, true); + accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); + expandNumberPiece.BindValueChanged(expand => + { + scalables = InternalChildren.ToList(); + + if (!expand.NewValue) + scalables.Remove(hitCircleText); + }, true); } private void updateState(ValueChangedEvent state) @@ -75,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Skinning { case ArmedState.Hit: this.FadeOut(legacy_fade_duration, Easing.Out); - this.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + scalables.ForEach(d => d.ScaleTo(1.4f, legacy_fade_duration, Easing.Out)); break; } } From ef8f9aa276f9359b1c1f81df3644f52917b7dbfb Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 1 Oct 2019 08:43:03 +0300 Subject: [PATCH 005/185] Fix possible nullref exception --- 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 15c768fff9..bd4cb1f112 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.ApplySkin(skin, allowFallback); - expandNumberPiece.Value = skin.GetConfig(OsuSkinConfiguration.ExpandNumberPiece).Value; + expandNumberPiece.Value = skin.GetConfig(OsuSkinConfiguration.ExpandNumberPiece)?.Value ?? false; } protected override void CheckForResult(bool userTriggered, double timeOffset) From 957bbee3e4eb4569310eacf11c59f7e167795efe Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 3 Oct 2019 05:58:20 +0300 Subject: [PATCH 006/185] Scale pieces individually and use skin source directly --- .../Objects/Drawables/DrawableHitCircle.cs | 11 ------- .../Skinning/LegacyMainCirclePiece.cs | 30 ++++++++----------- 2 files changed, 13 insertions(+), 28 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index bd4cb1f112..c37535521d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -21,10 +21,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public ApproachCircle ApproachCircle { get; } - public IBindable ExpandNumberPiece => expandNumberPiece; - - private readonly BindableBool expandNumberPiece = new BindableBool(); - private readonly IBindable positionBindable = new Bindable(); private readonly IBindable stackHeightBindable = new Bindable(); private readonly IBindable scaleBindable = new Bindable(); @@ -111,13 +107,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - protected override void ApplySkin(ISkinSource skin, bool allowFallback) - { - base.ApplySkin(skin, allowFallback); - - expandNumberPiece.Value = skin.GetConfig(OsuSkinConfiguration.ExpandNumberPiece)?.Value ?? false; - } - protected override void CheckForResult(bool userTriggered, double timeOffset) { Debug.Assert(HitObject.HitWindows != null); diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index d93d0506c3..8ba21d9f89 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -14,8 +14,6 @@ using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; -using System.Collections.Generic; -using System.Linq; namespace osu.Game.Rulesets.Osu.Skinning { @@ -26,21 +24,21 @@ namespace osu.Game.Rulesets.Osu.Skinning Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } - private Sprite hitCircleSprite; + private Sprite hitCircleSprite, hitCircleOverlay; private SkinnableSpriteText hitCircleText; - private List scalables; - private readonly IBindable state = new Bindable(); private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); - private readonly IBindable expandNumberPiece = new BindableBool(); [Resolved] private DrawableHitObject drawableObject { get; set; } + [Resolved] + private ISkinSource skin { get; set; } + [BackgroundDependencyLoader] - private void load(ISkinSource skin) + private void load() { OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; DrawableHitCircle drawableCircle = (DrawableHitCircle)drawableObject; @@ -59,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Font = OsuFont.Numeric.With(size: 40), UseFullGlyphHeight = false, }, confineMode: ConfineMode.NoScaling), - new Sprite + hitCircleOverlay = new Sprite { Texture = skin.GetTexture("hitcircleoverlay"), Anchor = Anchor.Centre, @@ -70,7 +68,6 @@ namespace osu.Game.Rulesets.Osu.Skinning state.BindTo(drawableObject.State); accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); - expandNumberPiece.BindTo(drawableCircle.ExpandNumberPiece); } protected override void LoadComplete() @@ -80,13 +77,6 @@ namespace osu.Game.Rulesets.Osu.Skinning state.BindValueChanged(updateState, true); accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); - expandNumberPiece.BindValueChanged(expand => - { - scalables = InternalChildren.ToList(); - - if (!expand.NewValue) - scalables.Remove(hitCircleText); - }, true); } private void updateState(ValueChangedEvent state) @@ -97,7 +87,13 @@ namespace osu.Game.Rulesets.Osu.Skinning { case ArmedState.Hit: this.FadeOut(legacy_fade_duration, Easing.Out); - scalables.ForEach(d => d.ScaleTo(1.4f, legacy_fade_duration, Easing.Out)); + + hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + hitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + + if (skin.GetConfig(OsuSkinConfiguration.ExpandNumberPiece).Value) + hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + break; } } From 023c4d64d811e1abc49655ba37b766568117333f Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 3 Oct 2019 06:00:22 +0300 Subject: [PATCH 007/185] Remove redundant using directive --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index c37535521d..4579e5cb59 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.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Scoring; using osu.Game.Skinning; using osuTK; From a73f6c6a5a3dfc12518a85442732a4c2469d0535 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 3 Oct 2019 06:46:13 +0300 Subject: [PATCH 008/185] Specify version of osu!classic skin --- osu.Game/Skinning/DefaultLegacySkin.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 4b6eea6b6e..8370b5e8ee 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -20,6 +20,8 @@ namespace osu.Game.Skinning new Color4(18, 124, 255, 255), new Color4(242, 24, 57, 255), }); + + Configuration.ConfigDictionary["Version"] = "2"; } public static SkinInfo Info { get; } = new SkinInfo From 89075c5655618c70b2d05f4943a11dca18350b89 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 3 Oct 2019 06:48:07 +0300 Subject: [PATCH 009/185] Set version of not-configured skins to latest only --- osu.Game/Skinning/DefaultSkinConfiguration.cs | 9 +++++++-- osu.Game/Skinning/LegacySkin.cs | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Skinning/DefaultSkinConfiguration.cs b/osu.Game/Skinning/DefaultSkinConfiguration.cs index 481360beb9..1d22ce8c1d 100644 --- a/osu.Game/Skinning/DefaultSkinConfiguration.cs +++ b/osu.Game/Skinning/DefaultSkinConfiguration.cs @@ -10,7 +10,7 @@ namespace osu.Game.Skinning /// public class DefaultSkinConfiguration : SkinConfiguration { - public DefaultSkinConfiguration() + public DefaultSkinConfiguration(string version) { ComboColours.AddRange(new[] { @@ -20,7 +20,12 @@ namespace osu.Game.Skinning new Color4(121, 9, 13, 255) }); - ConfigDictionary.Add(@"Version", "latest"); + ConfigDictionary["Version"] = version; + } + + public DefaultSkinConfiguration() + : this("1") + { } } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 0b1076be01..0f80aade1e 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -38,7 +38,7 @@ namespace osu.Game.Skinning using (StreamReader reader = new StreamReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); else - Configuration = new DefaultSkinConfiguration(); + Configuration = new DefaultSkinConfiguration("latest"); if (storage != null) { From 3fe56117005f375a8dcbea223cd48a0365d0af85 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 3 Oct 2019 06:48:59 +0300 Subject: [PATCH 010/185] Retrieve numeric version value from legacy configuration --- .../Skinning/OsuLegacySkinTransformer.cs | 3 +-- osu.Game/Skinning/LegacySkin.cs | 13 +++++++++++++ osu.Game/Skinning/LegacySkinConfiguration.cs | 10 ++++++++++ 3 files changed, 24 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Skinning/LegacySkinConfiguration.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index c9ed313593..539b3d7d34 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -125,8 +125,7 @@ namespace osu.Game.Rulesets.Osu.Skinning break; case OsuSkinConfiguration.ExpandNumberPiece: - string legacyVersion = source.GetConfig("Version")?.Value ?? "1"; - return SkinUtils.As(new BindableBool(double.TryParse(legacyVersion, out double version) && version < 2.0)); + return SkinUtils.As(new BindableBool(source.GetConfig(LegacySkinConfiguration.LegacyVersion).Value < 2.0)); } break; diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 0f80aade1e..90265ec066 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -70,6 +70,19 @@ namespace osu.Game.Skinning case GlobalSkinColour colour: return SkinUtils.As(getCustomColour(colour.ToString())); + case LegacySkinConfiguration legacy: + switch (legacy) + { + case LegacySkinConfiguration.LegacyVersion: + var versionString = GetConfig("Version").Value; + if (!double.TryParse(versionString, out double version)) + version = versionString == "latest" ? 2.7 : 1; + + return SkinUtils.As(new BindableDouble(version)); + } + + break; + case SkinCustomColourLookup customColour: return SkinUtils.As(getCustomColour(customColour.Lookup.ToString())); diff --git a/osu.Game/Skinning/LegacySkinConfiguration.cs b/osu.Game/Skinning/LegacySkinConfiguration.cs new file mode 100644 index 0000000000..2e75313d0c --- /dev/null +++ b/osu.Game/Skinning/LegacySkinConfiguration.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 LegacySkinConfiguration + { + LegacyVersion, + } +} From dabc22403009b8d976ce4971c309ca4fea709cea Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 3 Oct 2019 06:49:32 +0300 Subject: [PATCH 011/185] Fix hit circle positioning --- osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 8ba21d9f89..705828131d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -56,7 +56,11 @@ namespace osu.Game.Rulesets.Osu.Skinning { Font = OsuFont.Numeric.With(size: 40), UseFullGlyphHeight = false, - }, confineMode: ConfineMode.NoScaling), + }, confineMode: ConfineMode.NoScaling) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, hitCircleOverlay = new Sprite { Texture = skin.GetTexture("hitcircleoverlay"), From 2d7acef0800903e3d6f52ce98de724576f107bbf Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Thu, 3 Oct 2019 11:06:38 +0300 Subject: [PATCH 012/185] Fix CI issues --- osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs | 4 +--- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 3 ++- osu.Game/Skinning/LegacySkin.cs | 4 ++-- 3 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 705828131d..89c8ea9d6c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -10,7 +10,6 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -41,7 +40,6 @@ namespace osu.Game.Rulesets.Osu.Skinning private void load() { OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; - DrawableHitCircle drawableCircle = (DrawableHitCircle)drawableObject; InternalChildren = new Drawable[] { @@ -95,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Skinning hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); hitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - if (skin.GetConfig(OsuSkinConfiguration.ExpandNumberPiece).Value) + if (skin.GetConfig(OsuSkinConfiguration.ExpandNumberPiece)?.Value ?? true) hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); break; diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 539b3d7d34..1303e2cace 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -125,7 +125,8 @@ namespace osu.Game.Rulesets.Osu.Skinning break; case OsuSkinConfiguration.ExpandNumberPiece: - return SkinUtils.As(new BindableBool(source.GetConfig(LegacySkinConfiguration.LegacyVersion).Value < 2.0)); + double legacyVersion = source.GetConfig(LegacySkinConfiguration.LegacyVersion)?.Value ?? 1.0; + return SkinUtils.As(new BindableBool(legacyVersion < 2.0)); } break; diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 90265ec066..af5309eecd 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -74,9 +74,9 @@ namespace osu.Game.Skinning switch (legacy) { case LegacySkinConfiguration.LegacyVersion: - var versionString = GetConfig("Version").Value; + var versionString = GetConfig("Version")?.Value ?? "1"; if (!double.TryParse(versionString, out double version)) - version = versionString == "latest" ? 2.7 : 1; + version = versionString == "latest" ? 2.7 : 1.0; return SkinUtils.As(new BindableDouble(version)); } From f54eb448fa245c2fde8e5b23b3f526f25560122a Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 15 Oct 2019 22:00:34 +0300 Subject: [PATCH 013/185] Revert skin legacy version changes --- osu.Game/Skinning/DefaultLegacySkin.cs | 2 -- osu.Game/Skinning/DefaultSkinConfiguration.cs | 9 +-------- osu.Game/Skinning/LegacySkin.cs | 15 +-------------- osu.Game/Skinning/LegacySkinConfiguration.cs | 10 ---------- 4 files changed, 2 insertions(+), 34 deletions(-) delete mode 100644 osu.Game/Skinning/LegacySkinConfiguration.cs diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 8370b5e8ee..4b6eea6b6e 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -20,8 +20,6 @@ namespace osu.Game.Skinning new Color4(18, 124, 255, 255), new Color4(242, 24, 57, 255), }); - - Configuration.ConfigDictionary["Version"] = "2"; } public static SkinInfo Info { get; } = new SkinInfo diff --git a/osu.Game/Skinning/DefaultSkinConfiguration.cs b/osu.Game/Skinning/DefaultSkinConfiguration.cs index 8c87e1d054..cd5975edac 100644 --- a/osu.Game/Skinning/DefaultSkinConfiguration.cs +++ b/osu.Game/Skinning/DefaultSkinConfiguration.cs @@ -10,7 +10,7 @@ namespace osu.Game.Skinning /// public class DefaultSkinConfiguration : SkinConfiguration { - public DefaultSkinConfiguration(string version) + public DefaultSkinConfiguration() { ComboColours.AddRange(new[] { @@ -19,13 +19,6 @@ namespace osu.Game.Skinning new Color4(18, 124, 255, 255), new Color4(242, 24, 57, 255), }); - - ConfigDictionary["Version"] = version; - } - - public DefaultSkinConfiguration() - : this("1") - { } } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index cbebdd9bd6..fea15458e4 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -39,7 +39,7 @@ namespace osu.Game.Skinning using (LineBufferedReader reader = new LineBufferedReader(stream)) Configuration = new LegacySkinDecoder().Decode(reader); else - Configuration = new DefaultSkinConfiguration("latest"); + Configuration = new DefaultSkinConfiguration(); if (storage != null) { @@ -71,19 +71,6 @@ namespace osu.Game.Skinning case GlobalSkinColour colour: return SkinUtils.As(getCustomColour(colour.ToString())); - case LegacySkinConfiguration legacy: - switch (legacy) - { - case LegacySkinConfiguration.LegacyVersion: - var versionString = GetConfig("Version")?.Value ?? "1"; - if (!double.TryParse(versionString, out double version)) - version = versionString == "latest" ? 2.7 : 1.0; - - return SkinUtils.As(new BindableDouble(version)); - } - - break; - case SkinCustomColourLookup customColour: return SkinUtils.As(getCustomColour(customColour.Lookup.ToString())); diff --git a/osu.Game/Skinning/LegacySkinConfiguration.cs b/osu.Game/Skinning/LegacySkinConfiguration.cs deleted file mode 100644 index 2e75313d0c..0000000000 --- a/osu.Game/Skinning/LegacySkinConfiguration.cs +++ /dev/null @@ -1,10 +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.Skinning -{ - public enum LegacySkinConfiguration - { - LegacyVersion, - } -} From 9dcbef49d300458c897f3f40c77c9dddaf3bb5b4 Mon Sep 17 00:00:00 2001 From: iiSaLMaN Date: Tue, 15 Oct 2019 22:28:50 +0300 Subject: [PATCH 014/185] Resolve DHO inside load() --- osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 89c8ea9d6c..f9e6400b18 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -30,14 +30,11 @@ namespace osu.Game.Rulesets.Osu.Skinning private readonly Bindable accentColour = new Bindable(); private readonly IBindable indexInCurrentCombo = new Bindable(); - [Resolved] - private DrawableHitObject drawableObject { get; set; } - [Resolved] private ISkinSource skin { get; set; } [BackgroundDependencyLoader] - private void load() + private void load(DrawableHitObject drawableObject) { OsuHitObject osuObject = (OsuHitObject)drawableObject.HitObject; From d6fb2283385c9cec966d01dcf684cb402ab1e7e1 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 12 Dec 2019 16:02:53 +0300 Subject: [PATCH 015/185] Update version retrieval logic in-line with new implementation --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 1303e2cace..1c716b3ee9 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Skinning; using osuTK; +using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Osu.Skinning { @@ -125,8 +126,8 @@ namespace osu.Game.Rulesets.Osu.Skinning break; case OsuSkinConfiguration.ExpandNumberPiece: - double legacyVersion = source.GetConfig(LegacySkinConfiguration.LegacyVersion)?.Value ?? 1.0; - return SkinUtils.As(new BindableBool(legacyVersion < 2.0)); + decimal legacyVersion = source.GetConfig(LegacySetting.Version)?.Value ?? 1.0m; + return SkinUtils.As(new BindableBool(legacyVersion < 2.0m)); } break; From 41ca084fa591253d6406253c76cef99cc1196305 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 17 Dec 2019 22:00:21 +0300 Subject: [PATCH 016/185] Simplify expand number check --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 1c716b3ee9..5926332ea5 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -126,8 +126,8 @@ namespace osu.Game.Rulesets.Osu.Skinning break; case OsuSkinConfiguration.ExpandNumberPiece: - decimal legacyVersion = source.GetConfig(LegacySetting.Version)?.Value ?? 1.0m; - return SkinUtils.As(new BindableBool(legacyVersion < 2.0m)); + bool expand = source.GetConfig(LegacySetting.Version)?.Value < 2.0m; + return SkinUtils.As(new BindableBool(expand)); } break; From 121ce2c3df5b95f48fec3546caee2e0d257b1d8b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 19 Dec 2019 14:44:52 +0300 Subject: [PATCH 017/185] Fix checking for expand incorrectly --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 5926332ea5..3e41dc08d7 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Skinning break; case OsuSkinConfiguration.ExpandNumberPiece: - bool expand = source.GetConfig(LegacySetting.Version)?.Value < 2.0m; + bool expand = !(source.GetConfig(LegacySetting.Version)?.Value >= 2.0m); return SkinUtils.As(new BindableBool(expand)); } From de4c22c70923b2a9434eadc993eda361bd1c6bd6 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 8 Jul 2020 17:58:09 +0300 Subject: [PATCH 018/185] Implement news api request --- .../Online/API/Requests/GetNewsRequest.cs | 27 +++++++++++++++++++ .../Online/API/Requests/GetNewsResponse.cs | 15 +++++++++++ 2 files changed, 42 insertions(+) create mode 100644 osu.Game/Online/API/Requests/GetNewsRequest.cs create mode 100644 osu.Game/Online/API/Requests/GetNewsResponse.cs diff --git a/osu.Game/Online/API/Requests/GetNewsRequest.cs b/osu.Game/Online/API/Requests/GetNewsRequest.cs new file mode 100644 index 0000000000..36d9dc0652 --- /dev/null +++ b/osu.Game/Online/API/Requests/GetNewsRequest.cs @@ -0,0 +1,27 @@ +// 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.IO.Network; +using osu.Game.Extensions; + +namespace osu.Game.Online.API.Requests +{ + public class GetNewsRequest : APIRequest + { + private readonly Cursor cursor; + + public GetNewsRequest(Cursor cursor = null) + { + this.cursor = cursor; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + req.AddCursor(cursor); + return req; + } + + protected override string Target => "news"; + } +} diff --git a/osu.Game/Online/API/Requests/GetNewsResponse.cs b/osu.Game/Online/API/Requests/GetNewsResponse.cs new file mode 100644 index 0000000000..835289a51d --- /dev/null +++ b/osu.Game/Online/API/Requests/GetNewsResponse.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 System.Collections.Generic; +using Newtonsoft.Json; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Online.API.Requests +{ + public class GetNewsResponse : ResponseWithCursor + { + [JsonProperty("news_posts")] + public IEnumerable NewsPosts; + } +} From 49d998c8db2f7a6be17d3fd25898015e5a3920b8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 8 Jul 2020 18:24:13 +0300 Subject: [PATCH 019/185] Refactor NewsOverlay to use displays logic --- .../Visual/Online/TestSceneNewsOverlay.cs | 4 +- osu.Game/Overlays/DashboardOverlay.cs | 8 +-- osu.Game/Overlays/NewsOverlay.cs | 70 +++++++++++++------ 3 files changed, 53 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index d47c972564..0b3ede5d13 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -22,12 +22,12 @@ namespace osu.Game.Tests.Visual.Online AddStep(@"Show front page", () => news.ShowFrontPage()); AddStep(@"Custom article", () => news.Current.Value = "Test Article 101"); - AddStep(@"Article covers", () => news.LoadAndShowContent(new NewsCoverTest())); + AddStep(@"Article covers", () => news.LoadDisplay(new NewsCoverTest())); } private class TestNewsOverlay : NewsOverlay { - public new void LoadAndShowContent(NewsContent content) => base.LoadAndShowContent(content); + public void LoadDisplay(NewsContent content) => base.LoadDisplay(content); } private class NewsCoverTest : NewsContent diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs index a72c3f4fa5..e3a4b0e152 100644 --- a/osu.Game/Overlays/DashboardOverlay.cs +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -19,7 +19,6 @@ namespace osu.Game.Overlays { private CancellationTokenSource cancellationToken; - private Box background; private Container content; private DashboardOverlayHeader header; private LoadingLayer loading; @@ -35,9 +34,10 @@ namespace osu.Game.Overlays { Children = new Drawable[] { - background = new Box + new Box { - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + Colour = ColourProvider.Background5 }, scrollFlow = new OverlayScrollContainer { @@ -66,8 +66,6 @@ namespace osu.Game.Overlays }, loading = new LoadingLayer(content), }; - - background.Colour = ColourProvider.Background5; } protected override void LoadComplete() diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 46d692d44d..ec25827b5a 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -7,37 +7,40 @@ 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.Overlays.News; namespace osu.Game.Overlays { public class NewsOverlay : FullscreenOverlay { - private NewsHeader header; - - private Container content; - public readonly Bindable Current = new Bindable(null); + private Container content; + private LoadingLayer loading; + private OverlayScrollContainer scrollFlow; + public NewsOverlay() : base(OverlayColourScheme.Purple) { } [BackgroundDependencyLoader] - private void load(OsuColour colours) + private void load() { + NewsHeader header; + Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = colours.PurpleDarkAlternative + Colour = ColourProvider.Background5, }, - new OverlayScrollContainer + scrollFlow = new OverlayScrollContainer { RelativeSizeAxes = Axes.Both, + ScrollbarVisible = false, Child = new FillFlowContainer { RelativeSizeAxes = Axes.X, @@ -49,7 +52,7 @@ namespace osu.Game.Overlays { ShowFrontPage = ShowFrontPage }, - content = new Container + content = new Container { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -57,25 +60,16 @@ namespace osu.Game.Overlays }, }, }, + loading = new LoadingLayer(content), }; header.Post.BindTo(Current); - Current.TriggerChange(); } - private CancellationTokenSource loadContentCancellation; - - protected void LoadAndShowContent(NewsContent newContent) + protected override void LoadComplete() { - content.FadeTo(0.2f, 300, Easing.OutQuint); - - loadContentCancellation?.Cancel(); - - LoadComponentAsync(newContent, c => - { - content.Child = c; - content.FadeIn(300, Easing.OutQuint); - }, (loadContentCancellation = new CancellationTokenSource()).Token); + base.LoadComplete(); + Current.BindValueChanged(onCurrentChanged, true); } public void ShowFrontPage() @@ -83,5 +77,37 @@ namespace osu.Game.Overlays Current.Value = null; Show(); } + + private CancellationTokenSource cancellationToken; + + private void onCurrentChanged(ValueChangedEvent current) + { + cancellationToken?.Cancel(); + loading.Show(); + + if (current.NewValue == null) + { + LoadDisplay(Empty()); + return; + } + + LoadDisplay(Empty()); + } + + protected void LoadDisplay(Drawable display) + { + scrollFlow.ScrollToStart(); + LoadComponentAsync(display, loaded => + { + content.Child = loaded; + loading.Hide(); + }, (cancellationToken = new CancellationTokenSource()).Token); + } + + protected override void Dispose(bool isDisposing) + { + cancellationToken?.Cancel(); + base.Dispose(isDisposing); + } } } From 0b4213f3307c5c0145c9d2185e575d56833ea8a5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 8 Jul 2020 20:07:29 +0300 Subject: [PATCH 020/185] Implement FrontPageDisplay --- .../Visual/Online/TestSceneNewsOverlay.cs | 2 + .../News/Displays/FrontpageDisplay.cs | 133 ++++++++++++++++++ osu.Game/Overlays/NewsOverlay.cs | 3 +- 3 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/News/Displays/FrontpageDisplay.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index 0b3ede5d13..151b3df68f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -12,6 +12,8 @@ namespace osu.Game.Tests.Visual.Online { private TestNewsOverlay news; + protected override bool UseOnlineAPI => true; + protected override void LoadComplete() { base.LoadComplete(); diff --git a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs new file mode 100644 index 0000000000..611a072047 --- /dev/null +++ b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs @@ -0,0 +1,133 @@ +// 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.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osuTK; + +namespace osu.Game.Overlays.News.Displays +{ + public class FrontpageDisplay : CompositeDrawable + { + [Resolved] + private IAPIProvider api { get; set; } + + private readonly FillFlowContainer content; + private readonly FrontpageShowMoreButton showMore; + + private GetNewsRequest request; + private Cursor lastCursor; + + public FrontpageDisplay() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding + { + Top = 20, + Bottom = 10, + Left = 35, + Right = 55 + }; + + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + content = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10) + }, + showMore = new FrontpageShowMoreButton + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding + { + Vertical = 15 + }, + Action = fetchPage, + Alpha = 0 + } + } + }; + } + + [BackgroundDependencyLoader] + private void load() + { + fetchPage(); + } + + private void fetchPage() + { + request = new GetNewsRequest(lastCursor); + request.Success += response => Schedule(() => createContent(response)); + api.PerformAsync(request); + } + + private CancellationTokenSource cancellationToken; + + private void createContent(GetNewsResponse response) + { + lastCursor = response.Cursor; + + FillFlowContainer flow; + + flow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10) + }; + + response.NewsPosts.ForEach(p => + { + flow.Add(new NewsCard(p)); + }); + + LoadComponentAsync(flow, loaded => + { + content.Add(loaded); + showMore.IsLoading = false; + showMore.Show(); + }, (cancellationToken = new CancellationTokenSource()).Token); + } + + protected override void Dispose(bool isDisposing) + { + request?.Cancel(); + cancellationToken?.Cancel(); + base.Dispose(isDisposing); + } + + private class FrontpageShowMoreButton : ShowMoreButton + { + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Height = 20; + + IdleColour = colourProvider.Background3; + HoverColour = colourProvider.Background2; + ChevronIconColour = colourProvider.Foreground1; + } + } + } +} diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index ec25827b5a..c6c5d132c1 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays.News; +using osu.Game.Overlays.News.Displays; namespace osu.Game.Overlays { @@ -87,7 +88,7 @@ namespace osu.Game.Overlays if (current.NewValue == null) { - LoadDisplay(Empty()); + LoadDisplay(new FrontpageDisplay()); return; } From 57b935ec50b42a8c1eb2ebff1b04fd42f65e6a63 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 8 Jul 2020 20:17:15 +0300 Subject: [PATCH 021/185] Remove outdated elements --- .../Visual/Online/TestSceneNewsOverlay.cs | 47 +---- .../News/Displays/FrontpageDisplay.cs | 4 +- osu.Game/Overlays/News/NewsArticleCover.cs | 174 ------------------ osu.Game/Overlays/News/NewsContent.cs | 19 -- 4 files changed, 3 insertions(+), 241 deletions(-) delete mode 100644 osu.Game/Overlays/News/NewsArticleCover.cs delete mode 100644 osu.Game/Overlays/News/NewsContent.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index 151b3df68f..f0dc309d01 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -1,68 +1,25 @@ // 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.Game.Overlays; -using osu.Game.Overlays.News; namespace osu.Game.Tests.Visual.Online { public class TestSceneNewsOverlay : OsuTestScene { - private TestNewsOverlay news; + private NewsOverlay news; protected override bool UseOnlineAPI => true; protected override void LoadComplete() { base.LoadComplete(); - Add(news = new TestNewsOverlay()); + Add(news = new NewsOverlay()); AddStep(@"Show", news.Show); AddStep(@"Hide", news.Hide); AddStep(@"Show front page", () => news.ShowFrontPage()); AddStep(@"Custom article", () => news.Current.Value = "Test Article 101"); - - AddStep(@"Article covers", () => news.LoadDisplay(new NewsCoverTest())); - } - - private class TestNewsOverlay : NewsOverlay - { - public void LoadDisplay(NewsContent content) => base.LoadDisplay(content); - } - - private class NewsCoverTest : NewsContent - { - public NewsCoverTest() - { - Spacing = new osuTK.Vector2(0, 10); - - var article = new NewsArticleCover.ArticleInfo - { - Author = "Ephemeral", - CoverUrl = "https://assets.ppy.sh/artists/58/header.jpg", - Time = new DateTime(2019, 12, 4), - Title = "New Featured Artist: Kurokotei" - }; - - Children = new Drawable[] - { - new NewsArticleCover(article) - { - Height = 200 - }, - new NewsArticleCover(article) - { - Height = 120 - }, - new NewsArticleCover(article) - { - RelativeSizeAxes = Axes.None, - Size = new osuTK.Vector2(400, 200), - } - }; - } } } } diff --git a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs index 611a072047..270c62d42f 100644 --- a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs +++ b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs @@ -87,9 +87,7 @@ namespace osu.Game.Overlays.News.Displays { lastCursor = response.Cursor; - FillFlowContainer flow; - - flow = new FillFlowContainer + var flow = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, diff --git a/osu.Game/Overlays/News/NewsArticleCover.cs b/osu.Game/Overlays/News/NewsArticleCover.cs deleted file mode 100644 index e3f5a8cea3..0000000000 --- a/osu.Game/Overlays/News/NewsArticleCover.cs +++ /dev/null @@ -1,174 +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.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.Input.Events; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osuTK.Graphics; - -namespace osu.Game.Overlays.News -{ - public class NewsArticleCover : Container - { - private const int hover_duration = 300; - - private readonly Box gradient; - - public NewsArticleCover(ArticleInfo info) - { - RelativeSizeAxes = Axes.X; - Masking = true; - CornerRadius = 4; - - NewsBackground bg; - - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(OsuColour.Gray(0.2f), OsuColour.Gray(0.1f)) - }, - new DelayedLoadWrapper(bg = new NewsBackground(info.CoverUrl) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - FillMode = FillMode.Fill, - Alpha = 0 - }) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - }, - gradient = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourInfo.GradientVertical(Color4.Black.Opacity(0.1f), Color4.Black.Opacity(0.7f)), - Alpha = 0 - }, - new DateContainer(info.Time) - { - Margin = new MarginPadding - { - Right = 20, - Top = 20, - } - }, - new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding - { - Left = 25, - Bottom = 50, - }, - Font = OsuFont.GetFont(Typeface.Torus, 24, FontWeight.Bold), - Text = info.Title, - }, - new OsuSpriteText - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Margin = new MarginPadding - { - Left = 25, - Bottom = 30, - }, - Font = OsuFont.GetFont(Typeface.Torus, 16, FontWeight.Bold), - Text = "by " + info.Author - } - }; - - bg.OnLoadComplete += d => d.FadeIn(250, Easing.In); - } - - protected override bool OnHover(HoverEvent e) - { - gradient.FadeIn(hover_duration, Easing.OutQuint); - return base.OnHover(e); - } - - protected override void OnHoverLost(HoverLostEvent e) - { - base.OnHoverLost(e); - gradient.FadeOut(hover_duration, Easing.OutQuint); - } - - [LongRunningLoad] - private class NewsBackground : Sprite - { - private readonly string url; - - public NewsBackground(string coverUrl) - { - url = coverUrl ?? "Headers/news"; - } - - [BackgroundDependencyLoader] - private void load(LargeTextureStore store) - { - Texture = store.Get(url); - } - } - - private class DateContainer : Container, IHasTooltip - { - private readonly DateTime date; - - public DateContainer(DateTime date) - { - this.date = date; - - Anchor = Anchor.TopRight; - Origin = Anchor.TopRight; - Masking = true; - CornerRadius = 4; - AutoSizeAxes = Axes.Both; - Children = new Drawable[] - { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), - }, - new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(Typeface.Torus, 12, FontWeight.Bold, false, false), - Text = date.ToString("d MMM yyy").ToUpper(), - Margin = new MarginPadding - { - Vertical = 4, - Horizontal = 8, - } - } - }; - } - - public string TooltipText => date.ToString("dddd dd MMMM yyyy hh:mm:ss UTCz").ToUpper(); - } - - // fake API data struct to use for now as a skeleton for data, as there is no API struct for news article info for now - public class ArticleInfo - { - public string Title { get; set; } - public string CoverUrl { get; set; } - public DateTime Time { get; set; } - public string Author { get; set; } - } - } -} diff --git a/osu.Game/Overlays/News/NewsContent.cs b/osu.Game/Overlays/News/NewsContent.cs deleted file mode 100644 index 5ff210f9f5..0000000000 --- a/osu.Game/Overlays/News/NewsContent.cs +++ /dev/null @@ -1,19 +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; - -namespace osu.Game.Overlays.News -{ - public abstract class NewsContent : FillFlowContainer - { - protected NewsContent() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Direction = FillDirection.Vertical; - Padding = new MarginPadding { Bottom = 100, Top = 20, Horizontal = 50 }; - } - } -} From 900f2d309b84f43d151685488b6824bb8c5c40c5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Jul 2020 01:26:56 +0300 Subject: [PATCH 022/185] Classes naming adjustments --- osu.Game/Overlays/News/Displays/FrontpageDisplay.cs | 10 +++++----- osu.Game/Overlays/NewsOverlay.cs | 2 +- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs index 270c62d42f..f0b25c8143 100644 --- a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs +++ b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs @@ -13,18 +13,18 @@ using osuTK; namespace osu.Game.Overlays.News.Displays { - public class FrontpageDisplay : CompositeDrawable + public class FrontPageDisplay : CompositeDrawable { [Resolved] private IAPIProvider api { get; set; } private readonly FillFlowContainer content; - private readonly FrontpageShowMoreButton showMore; + private readonly FrontPageShowMoreButton showMore; private GetNewsRequest request; private Cursor lastCursor; - public FrontpageDisplay() + public FrontPageDisplay() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.News.Displays Direction = FillDirection.Vertical, Spacing = new Vector2(0, 10) }, - showMore = new FrontpageShowMoreButton + showMore = new FrontPageShowMoreButton { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -115,7 +115,7 @@ namespace osu.Game.Overlays.News.Displays base.Dispose(isDisposing); } - private class FrontpageShowMoreButton : ShowMoreButton + private class FrontPageShowMoreButton : ShowMoreButton { [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index c6c5d132c1..4cd83f83af 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -88,7 +88,7 @@ namespace osu.Game.Overlays if (current.NewValue == null) { - LoadDisplay(new FrontpageDisplay()); + LoadDisplay(new FrontPageDisplay()); return; } From 62e2bc11983cc46feffb35badfa82deee9740e6d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Jul 2020 01:29:27 +0300 Subject: [PATCH 023/185] Fix potential double-request situation --- osu.Game/Overlays/News/Displays/FrontpageDisplay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs index f0b25c8143..73af51c342 100644 --- a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs +++ b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs @@ -76,6 +76,8 @@ namespace osu.Game.Overlays.News.Displays private void fetchPage() { + request?.Cancel(); + request = new GetNewsRequest(lastCursor); request.Success += response => Schedule(() => createContent(response)); api.PerformAsync(request); From dfa22b1e4c48fa297508ed57cc84565899c34cd2 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Jul 2020 02:37:42 +0300 Subject: [PATCH 024/185] Styles improvements --- .../Visual/Online/TestSceneShowMoreButton.cs | 20 +++----- .../Graphics/UserInterface/ShowMoreButton.cs | 47 +++++++++++-------- .../Comments/CommentsShowMoreButton.cs | 11 ----- .../News/Displays/FrontpageDisplay.cs | 26 +++------- .../Profile/Sections/PaginatedContainer.cs | 5 +- .../Profile/Sections/ProfileShowMoreButton.cs | 19 -------- 6 files changed, 42 insertions(+), 86 deletions(-) delete mode 100644 osu.Game/Overlays/Profile/Sections/ProfileShowMoreButton.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs index 273f593c32..f1c69f0ac3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs @@ -4,19 +4,22 @@ using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; using osu.Framework.Allocation; -using osu.Game.Graphics; +using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Online { public class TestSceneShowMoreButton : OsuTestScene { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Red); + public TestSceneShowMoreButton() { - TestButton button = null; + ShowMoreButton button = null; int fireCount = 0; - Add(button = new TestButton + Add(button = new ShowMoreButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -46,16 +49,5 @@ namespace osu.Game.Tests.Visual.Online AddAssert("action fired twice", () => fireCount == 2); AddAssert("is in loading state", () => button.IsLoading); } - - private class TestButton : ShowMoreButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colors) - { - IdleColour = colors.YellowDark; - HoverColour = colors.Yellow; - ChevronIconColour = colors.Red; - } - } } } diff --git a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs index c9cd9f1158..db563b346c 100644 --- a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs +++ b/osu.Game/Graphics/UserInterface/ShowMoreButton.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 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.Sprites; +using osu.Game.Overlays; using osuTK; -using osuTK.Graphics; using System.Collections.Generic; namespace osu.Game.Graphics.UserInterface @@ -16,14 +17,6 @@ namespace osu.Game.Graphics.UserInterface { private const int duration = 200; - private Color4 chevronIconColour; - - protected Color4 ChevronIconColour - { - get => chevronIconColour; - set => chevronIconColour = leftChevron.Colour = rightChevron.Colour = value; - } - public string Text { get => text.Text; @@ -32,22 +25,26 @@ namespace osu.Game.Graphics.UserInterface protected override IEnumerable EffectTargets => new[] { background }; - private ChevronIcon leftChevron; - private ChevronIcon rightChevron; private SpriteText text; private Box background; private FillFlowContainer textContainer; public ShowMoreButton() { - Height = 30; - Width = 140; + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + IdleColour = colourProvider.Background2; + HoverColour = colourProvider.Background1; } protected override Drawable CreateContent() => new CircularContainer { Masking = true, - RelativeSizeAxes = Axes.Both, + AutoSizeAxes = Axes.Both, Children = new Drawable[] { background = new Box @@ -56,22 +53,28 @@ namespace osu.Game.Graphics.UserInterface }, textContainer = new FillFlowContainer { + AlwaysPresent = true, Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, Spacing = new Vector2(7), + Margin = new MarginPadding + { + Horizontal = 20, + Vertical = 5, + }, Children = new Drawable[] { - leftChevron = new ChevronIcon(), + new ChevronIcon(), text = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Text = "show more".ToUpper(), }, - rightChevron = new ChevronIcon(), + new ChevronIcon() } } } @@ -83,15 +86,19 @@ namespace osu.Game.Graphics.UserInterface private class ChevronIcon : SpriteIcon { - private const int icon_size = 8; - public ChevronIcon() { Anchor = Anchor.Centre; Origin = Anchor.Centre; - Size = new Vector2(icon_size); + Size = new Vector2(8); Icon = FontAwesome.Solid.ChevronDown; } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + Colour = colourProvider.Foreground1; + } } } } diff --git a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs b/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs index d2ff7ecb1f..adf64eabb1 100644 --- a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs +++ b/osu.Game/Overlays/Comments/CommentsShowMoreButton.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.Bindables; using osu.Game.Graphics.UserInterface; @@ -11,16 +10,6 @@ namespace osu.Game.Overlays.Comments { public readonly BindableInt Current = new BindableInt(); - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - Height = 20; - - IdleColour = colourProvider.Background2; - HoverColour = colourProvider.Background1; - ChevronIconColour = colourProvider.Foreground1; - } - protected override void LoadComplete() { Current.BindValueChanged(onCurrentChanged, true); diff --git a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs index 73af51c342..386e0b0dca 100644 --- a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs +++ b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.News.Displays private IAPIProvider api { get; set; } private readonly FillFlowContainer content; - private readonly FrontPageShowMoreButton showMore; + private readonly ShowMoreButton showMore; private GetNewsRequest request; private Cursor lastCursor; @@ -30,10 +30,9 @@ namespace osu.Game.Overlays.News.Displays AutoSizeAxes = Axes.Y; Padding = new MarginPadding { - Top = 20, - Bottom = 10, - Left = 35, - Right = 55 + Vertical = 20, + Left = 30, + Right = 50 }; InternalChild = new FillFlowContainer @@ -53,13 +52,13 @@ namespace osu.Game.Overlays.News.Displays Direction = FillDirection.Vertical, Spacing = new Vector2(0, 10) }, - showMore = new FrontPageShowMoreButton + showMore = new ShowMoreButton { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Margin = new MarginPadding { - Vertical = 15 + Top = 15 }, Action = fetchPage, Alpha = 0 @@ -116,18 +115,5 @@ namespace osu.Game.Overlays.News.Displays cancellationToken?.Cancel(); base.Dispose(isDisposing); } - - private class FrontPageShowMoreButton : ShowMoreButton - { - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - Height = 20; - - IdleColour = colourProvider.Background3; - HoverColour = colourProvider.Background2; - ChevronIconColour = colourProvider.Foreground1; - } - } } } diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index a30ff786fb..9720469548 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -14,12 +14,13 @@ using osu.Game.Users; using System.Collections.Generic; using System.Linq; using System.Threading; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Profile.Sections { public abstract class PaginatedContainer : FillFlowContainer { - private readonly ProfileShowMoreButton moreButton; + private readonly ShowMoreButton moreButton; private readonly OsuSpriteText missingText; private APIRequest> retrievalRequest; private CancellationTokenSource loadCancellation; @@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Profile.Sections RelativeSizeAxes = Axes.X, Spacing = new Vector2(0, 2), }, - moreButton = new ProfileShowMoreButton + moreButton = new ShowMoreButton { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Profile/Sections/ProfileShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ProfileShowMoreButton.cs deleted file mode 100644 index 426ebeebe6..0000000000 --- a/osu.Game/Overlays/Profile/Sections/ProfileShowMoreButton.cs +++ /dev/null @@ -1,19 +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.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Profile.Sections -{ - public class ProfileShowMoreButton : ShowMoreButton - { - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - IdleColour = colourProvider.Background2; - HoverColour = colourProvider.Background1; - ChevronIconColour = colourProvider.Foreground1; - } - } -} From 3ba8ec0fd75f73dda2db4d8bb855e7544a7f7aba Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Jul 2020 03:40:14 +0300 Subject: [PATCH 025/185] Don't set null value to show front page --- .../Visual/Online/TestSceneNewsOverlay.cs | 16 ++++--- osu.Game/Overlays/News/NewsHeader.cs | 46 ++++++++++--------- osu.Game/Overlays/NewsOverlay.cs | 26 +++++------ 3 files changed, 45 insertions(+), 43 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index f0dc309d01..e35ef4916b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -7,19 +7,21 @@ namespace osu.Game.Tests.Visual.Online { public class TestSceneNewsOverlay : OsuTestScene { - private NewsOverlay news; - protected override bool UseOnlineAPI => true; protected override void LoadComplete() { base.LoadComplete(); - Add(news = new NewsOverlay()); - AddStep(@"Show", news.Show); - AddStep(@"Hide", news.Hide); - AddStep(@"Show front page", () => news.ShowFrontPage()); - AddStep(@"Custom article", () => news.Current.Value = "Test Article 101"); + NewsOverlay news; + Add(news = new NewsOverlay()); + + AddStep("Show", news.Show); + AddStep("Hide", news.Hide); + + AddStep("Show front page", () => news.ShowFrontPage()); + AddStep("Custom article", () => news.ShowArticle("Test Article 101")); + AddStep("Custom article", () => news.ShowArticle("Test Article 102")); } } } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 8214c71b3a..ee7991c0c6 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -3,44 +3,46 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; -using System; namespace osu.Game.Overlays.News { public class NewsHeader : BreadcrumbControlOverlayHeader { - private const string front_page_string = "frontpage"; + public const string FRONT_PAGE_STRING = "frontpage"; - public readonly Bindable Post = new Bindable(null); - - public Action ShowFrontPage; + public readonly Bindable Post = new Bindable(FRONT_PAGE_STRING); public NewsHeader() { - TabControl.AddItem(front_page_string); - - Current.ValueChanged += e => - { - if (e.NewValue == front_page_string) - ShowFrontPage?.Invoke(); - }; - - Post.ValueChanged += showPost; + TabControl.AddItem(FRONT_PAGE_STRING); + Current.Value = FRONT_PAGE_STRING; + Current.BindValueChanged(onCurrentChanged); + Post.BindValueChanged(onPostChanged, true); } - private void showPost(ValueChangedEvent e) - { - if (e.OldValue != null) - TabControl.RemoveItem(e.OldValue); + public void SetFrontPage() => Post.Value = FRONT_PAGE_STRING; - if (e.NewValue != null) + public void SetArticle(string slug) => Post.Value = slug; + + private void onCurrentChanged(ValueChangedEvent current) + { + if (current.NewValue == FRONT_PAGE_STRING) + Post.Value = FRONT_PAGE_STRING; + } + + private void onPostChanged(ValueChangedEvent post) + { + if (post.OldValue != FRONT_PAGE_STRING) + TabControl.RemoveItem(post.OldValue); + + if (post.NewValue != FRONT_PAGE_STRING) { - TabControl.AddItem(e.NewValue); - Current.Value = e.NewValue; + TabControl.AddItem(post.NewValue); + Current.Value = post.NewValue; } else { - Current.Value = front_page_string; + Current.Value = FRONT_PAGE_STRING; } } diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index 4cd83f83af..db989e71bf 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -15,10 +15,9 @@ namespace osu.Game.Overlays { public class NewsOverlay : FullscreenOverlay { - public readonly Bindable Current = new Bindable(null); - private Container content; private LoadingLayer loading; + private NewsHeader header; private OverlayScrollContainer scrollFlow; public NewsOverlay() @@ -29,8 +28,6 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { - NewsHeader header; - Children = new Drawable[] { new Box @@ -49,10 +46,7 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, Children = new Drawable[] { - header = new NewsHeader - { - ShowFrontPage = ShowFrontPage - }, + header = new NewsHeader(), content = new Container { RelativeSizeAxes = Axes.X, @@ -63,30 +57,34 @@ namespace osu.Game.Overlays }, loading = new LoadingLayer(content), }; - - header.Post.BindTo(Current); } protected override void LoadComplete() { base.LoadComplete(); - Current.BindValueChanged(onCurrentChanged, true); + header.Post.BindValueChanged(onPostChanged, true); } public void ShowFrontPage() { - Current.Value = null; + header.SetFrontPage(); + Show(); + } + + public void ShowArticle(string slug) + { + header.SetArticle(slug); Show(); } private CancellationTokenSource cancellationToken; - private void onCurrentChanged(ValueChangedEvent current) + private void onPostChanged(ValueChangedEvent post) { cancellationToken?.Cancel(); loading.Show(); - if (current.NewValue == null) + if (post.NewValue == NewsHeader.FRONT_PAGE_STRING) { LoadDisplay(new FrontPageDisplay()); return; From aeb664aca759e7061a6140b7c51dabc31fa26132 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Jul 2020 04:00:53 +0300 Subject: [PATCH 026/185] Delete broken file --- .../News/Displays/FrontpageDisplay.cs | 119 ------------------ 1 file changed, 119 deletions(-) delete mode 100644 osu.Game/Overlays/News/Displays/FrontpageDisplay.cs diff --git a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs deleted file mode 100644 index 386e0b0dca..0000000000 --- a/osu.Game/Overlays/News/Displays/FrontpageDisplay.cs +++ /dev/null @@ -1,119 +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.Threading; -using osu.Framework.Allocation; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Graphics.UserInterface; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests; -using osuTK; - -namespace osu.Game.Overlays.News.Displays -{ - public class FrontPageDisplay : CompositeDrawable - { - [Resolved] - private IAPIProvider api { get; set; } - - private readonly FillFlowContainer content; - private readonly ShowMoreButton showMore; - - private GetNewsRequest request; - private Cursor lastCursor; - - public FrontPageDisplay() - { - RelativeSizeAxes = Axes.X; - AutoSizeAxes = Axes.Y; - Padding = new MarginPadding - { - Vertical = 20, - Left = 30, - Right = 50 - }; - - InternalChild = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10), - Children = new Drawable[] - { - content = new FillFlowContainer - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10) - }, - showMore = new ShowMoreButton - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Margin = new MarginPadding - { - Top = 15 - }, - Action = fetchPage, - Alpha = 0 - } - } - }; - } - - [BackgroundDependencyLoader] - private void load() - { - fetchPage(); - } - - private void fetchPage() - { - request?.Cancel(); - - request = new GetNewsRequest(lastCursor); - request.Success += response => Schedule(() => createContent(response)); - api.PerformAsync(request); - } - - private CancellationTokenSource cancellationToken; - - private void createContent(GetNewsResponse response) - { - lastCursor = response.Cursor; - - var flow = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Spacing = new Vector2(0, 10) - }; - - response.NewsPosts.ForEach(p => - { - flow.Add(new NewsCard(p)); - }); - - LoadComponentAsync(flow, loaded => - { - content.Add(loaded); - showMore.IsLoading = false; - showMore.Show(); - }, (cancellationToken = new CancellationTokenSource()).Token); - } - - protected override void Dispose(bool isDisposing) - { - request?.Cancel(); - cancellationToken?.Cancel(); - base.Dispose(isDisposing); - } - } -} From f663dd18033e9cfc83dbbaa652f862a4e7fb8099 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 9 Jul 2020 04:02:14 +0300 Subject: [PATCH 027/185] Fix incorrect file name --- .../News/Displays/FrontPageDisplay.cs | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 osu.Game/Overlays/News/Displays/FrontPageDisplay.cs diff --git a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs new file mode 100644 index 0000000000..67b5edfafd --- /dev/null +++ b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs @@ -0,0 +1,115 @@ +// 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 osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osuTK; + +namespace osu.Game.Overlays.News.Displays +{ + public class FrontPageDisplay : CompositeDrawable + { + [Resolved] + private IAPIProvider api { get; set; } + + private readonly FillFlowContainer content; + private readonly ShowMoreButton showMore; + + private GetNewsRequest request; + private Cursor lastCursor; + + public FrontPageDisplay() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + Padding = new MarginPadding + { + Vertical = 20, + Left = 30, + Right = 50 + }; + + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + content = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10) + }, + showMore = new ShowMoreButton + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Margin = new MarginPadding + { + Top = 15 + }, + Action = fetchPage, + Alpha = 0 + } + } + }; + } + + [BackgroundDependencyLoader] + private void load() + { + fetchPage(); + } + + private void fetchPage() + { + request?.Cancel(); + + request = new GetNewsRequest(lastCursor); + request.Success += response => Schedule(() => createContent(response)); + api.PerformAsync(request); + } + + private CancellationTokenSource cancellationToken; + + private void createContent(GetNewsResponse response) + { + lastCursor = response.Cursor; + + var flow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = response.NewsPosts.Select(p => new NewsCard(p)).ToList() + }; + + LoadComponentAsync(flow, loaded => + { + content.Add(loaded); + showMore.IsLoading = false; + showMore.Show(); + }, (cancellationToken = new CancellationTokenSource()).Token); + } + + protected override void Dispose(bool isDisposing) + { + request?.Cancel(); + cancellationToken?.Cancel(); + base.Dispose(isDisposing); + } + } +} From 598e48678e2e143b6671a9cd2c9f55dad36f3bff Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 12 Jul 2020 15:45:48 +0300 Subject: [PATCH 028/185] Refactor NewsHeader --- osu.Game/Overlays/News/NewsHeader.cs | 54 ++++++++++++++-------------- osu.Game/Overlays/NewsOverlay.cs | 19 ++++++---- 2 files changed, 41 insertions(+), 32 deletions(-) diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index ee7991c0c6..ddada2bdaf 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.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.Bindables; using osu.Framework.Graphics; @@ -8,41 +9,42 @@ namespace osu.Game.Overlays.News { public class NewsHeader : BreadcrumbControlOverlayHeader { - public const string FRONT_PAGE_STRING = "frontpage"; + private const string front_page_string = "frontpage"; - public readonly Bindable Post = new Bindable(FRONT_PAGE_STRING); + public Action ShowFrontPage; + + private readonly Bindable article = new Bindable(null); public NewsHeader() { - TabControl.AddItem(FRONT_PAGE_STRING); - Current.Value = FRONT_PAGE_STRING; - Current.BindValueChanged(onCurrentChanged); - Post.BindValueChanged(onPostChanged, true); - } + TabControl.AddItem(front_page_string); - public void SetFrontPage() => Post.Value = FRONT_PAGE_STRING; - - public void SetArticle(string slug) => Post.Value = slug; - - private void onCurrentChanged(ValueChangedEvent current) - { - if (current.NewValue == FRONT_PAGE_STRING) - Post.Value = FRONT_PAGE_STRING; - } - - private void onPostChanged(ValueChangedEvent post) - { - if (post.OldValue != FRONT_PAGE_STRING) - TabControl.RemoveItem(post.OldValue); - - if (post.NewValue != FRONT_PAGE_STRING) + Current.BindValueChanged(e => { - TabControl.AddItem(post.NewValue); - Current.Value = post.NewValue; + if (e.NewValue == front_page_string) + ShowFrontPage?.Invoke(); + }); + + article.BindValueChanged(onArticleChanged, true); + } + + public void SetFrontPage() => article.Value = null; + + public void SetArticle(string slug) => article.Value = slug; + + private void onArticleChanged(ValueChangedEvent e) + { + if (e.OldValue != null) + TabControl.RemoveItem(e.OldValue); + + if (e.NewValue != null) + { + TabControl.AddItem(e.NewValue); + Current.Value = e.NewValue; } else { - Current.Value = FRONT_PAGE_STRING; + Current.Value = front_page_string; } } diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index db989e71bf..a5687b77e2 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -15,6 +15,8 @@ namespace osu.Game.Overlays { public class NewsOverlay : FullscreenOverlay { + private readonly Bindable article = new Bindable(null); + private Container content; private LoadingLayer loading; private NewsHeader header; @@ -46,7 +48,10 @@ namespace osu.Game.Overlays Direction = FillDirection.Vertical, Children = new Drawable[] { - header = new NewsHeader(), + header = new NewsHeader + { + ShowFrontPage = ShowFrontPage + }, content = new Container { RelativeSizeAxes = Axes.X, @@ -62,34 +67,36 @@ namespace osu.Game.Overlays protected override void LoadComplete() { base.LoadComplete(); - header.Post.BindValueChanged(onPostChanged, true); + article.BindValueChanged(onArticleChanged, true); } public void ShowFrontPage() { - header.SetFrontPage(); + article.Value = null; Show(); } public void ShowArticle(string slug) { - header.SetArticle(slug); + article.Value = slug; Show(); } private CancellationTokenSource cancellationToken; - private void onPostChanged(ValueChangedEvent post) + private void onArticleChanged(ValueChangedEvent e) { cancellationToken?.Cancel(); loading.Show(); - if (post.NewValue == NewsHeader.FRONT_PAGE_STRING) + if (e.NewValue == null) { + header.SetFrontPage(); LoadDisplay(new FrontPageDisplay()); return; } + header.SetArticle(e.NewValue); LoadDisplay(Empty()); } From a72bb932661faa2442ceb9160d61b0d21e803790 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 12 Jul 2020 15:57:18 +0300 Subject: [PATCH 029/185] Add test scene for NewsHeader --- .../Visual/Online/TestSceneNewsHeader.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs new file mode 100644 index 0000000000..65e86e925d --- /dev/null +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.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 NUnit.Framework; +using osu.Game.Overlays.News; +using osu.Framework.Graphics; +using osu.Game.Overlays; +using osu.Framework.Allocation; + +namespace osu.Game.Tests.Visual.Online +{ + public class TestSceneNewsHeader : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + private TestHeader header; + + [SetUp] + public void Setup() + { + Child = header = new TestHeader + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre + }; + } + + [Test] + public void TestControl() + { + AddAssert("Front page selected", () => header.Current.Value == "frontpage"); + AddAssert("1 tab total", () => header.TabCount == 1); + + AddStep("Set article 1", () => header.SetArticle("1")); + AddAssert("Article 1 selected", () => header.Current.Value == "1"); + AddAssert("2 tabs total", () => header.TabCount == 2); + + AddStep("Set article 2", () => header.SetArticle("2")); + AddAssert("Article 2 selected", () => header.Current.Value == "2"); + AddAssert("2 tabs total", () => header.TabCount == 2); + + AddStep("Set front page", () => header.SetFrontPage()); + AddAssert("Front page selected", () => header.Current.Value == "frontpage"); + AddAssert("1 tab total", () => header.TabCount == 1); + } + + private class TestHeader : NewsHeader + { + public int TabCount => TabControl.Items.Count; + } + } +} From 444701fdd0c0903346177900acef014755da297f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 12 Jul 2020 16:13:48 +0300 Subject: [PATCH 030/185] Use dummy api for tests --- .../Visual/Online/TestSceneNewsHeader.cs | 4 +- .../Visual/Online/TestSceneNewsOverlay.cs | 64 +++++++++++++++---- 2 files changed, 53 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs index 65e86e925d..78288bf6e4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsHeader.cs @@ -17,14 +17,14 @@ namespace osu.Game.Tests.Visual.Online private TestHeader header; [SetUp] - public void Setup() + public void Setup() => Schedule(() => { Child = header = new TestHeader { Anchor = Anchor.Centre, Origin = Anchor.Centre }; - } + }); [Test] public void TestControl() diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs index e35ef4916b..37d51c16d2 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsOverlay.cs @@ -1,27 +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 NUnit.Framework; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Online { public class TestSceneNewsOverlay : OsuTestScene { - protected override bool UseOnlineAPI => true; + private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; - protected override void LoadComplete() + private NewsOverlay news; + + [SetUp] + public void SetUp() => Schedule(() => Child = news = new NewsOverlay()); + + [Test] + public void TestRequest() { - base.LoadComplete(); - - NewsOverlay news; - Add(news = new NewsOverlay()); - - AddStep("Show", news.Show); - AddStep("Hide", news.Hide); - - AddStep("Show front page", () => news.ShowFrontPage()); - AddStep("Custom article", () => news.ShowArticle("Test Article 101")); - AddStep("Custom article", () => news.ShowArticle("Test Article 102")); + setUpNewsResponse(responseExample); + AddStep("Show", () => news.Show()); + AddStep("Show article", () => news.ShowArticle("article")); } + + private void setUpNewsResponse(GetNewsResponse r) + => AddStep("set up response", () => + { + dummyAPI.HandleRequest = request => + { + if (!(request is GetNewsRequest getNewsRequest)) + return; + + getNewsRequest.TriggerSuccess(r); + }; + }); + + private GetNewsResponse responseExample => new GetNewsResponse + { + NewsPosts = new[] + { + new APINewsPost + { + Title = "This post has an image which starts with \"/\" and has many authors!", + Preview = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", + Author = "someone, someone1, someone2, someone3, someone4", + FirstImage = "/help/wiki/shared/news/banners/monthly-beatmapping-contest.png", + PublishedAt = DateTimeOffset.Now + }, + new APINewsPost + { + Title = "This post has a full-url image! (HTML entity: &)", + Preview = "boom (HTML entity: &)", + Author = "user (HTML entity: &)", + FirstImage = "https://assets.ppy.sh/artists/88/header.jpg", + PublishedAt = DateTimeOffset.Now + } + } + }; } } From 68d2888a8c16cd5e16c1f50f12e5d22279dfd2c4 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Jul 2020 14:48:40 +0300 Subject: [PATCH 031/185] Add NewsOverlay to the game --- osu.Game.Tests/Visual/TestSceneOsuGame.cs | 1 + osu.Game/OsuGame.cs | 5 ++++- osu.Game/Overlays/Toolbar/Toolbar.cs | 1 + .../Overlays/Toolbar/ToolbarNewsButton.cs | 22 +++++++++++++++++++ 4 files changed, 28 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs diff --git a/osu.Game.Tests/Visual/TestSceneOsuGame.cs b/osu.Game.Tests/Visual/TestSceneOsuGame.cs index 22ae5257e7..b347c39c1e 100644 --- a/osu.Game.Tests/Visual/TestSceneOsuGame.cs +++ b/osu.Game.Tests/Visual/TestSceneOsuGame.cs @@ -44,6 +44,7 @@ namespace osu.Game.Tests.Visual typeof(NotificationOverlay), typeof(BeatmapListingOverlay), typeof(DashboardOverlay), + typeof(NewsOverlay), typeof(ChannelManager), typeof(ChatOverlay), typeof(SettingsOverlay), diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 618049e72c..84b32673d5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -71,6 +71,8 @@ namespace osu.Game private DashboardOverlay dashboard; + private NewsOverlay news; + private UserProfileOverlay userProfile; private BeatmapSetOverlay beatmapSetOverlay; @@ -630,6 +632,7 @@ namespace osu.Game // overlay elements loadComponentSingleFile(beatmapListing = new BeatmapListingOverlay(), overlayContent.Add, true); loadComponentSingleFile(dashboard = new DashboardOverlay(), overlayContent.Add, true); + loadComponentSingleFile(news = new NewsOverlay(), overlayContent.Add, true); var rankingsOverlay = loadComponentSingleFile(new RankingsOverlay(), overlayContent.Add, true); loadComponentSingleFile(channelManager = new ChannelManager(), AddInternal, true); loadComponentSingleFile(chatOverlay = new ChatOverlay(), overlayContent.Add, true); @@ -687,7 +690,7 @@ namespace osu.Game } // ensure only one of these overlays are open at once. - var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, dashboard, beatmapListing, changelogOverlay, rankingsOverlay }; + var singleDisplayOverlays = new OverlayContainer[] { chatOverlay, news, dashboard, beatmapListing, changelogOverlay, rankingsOverlay }; foreach (var overlay in singleDisplayOverlays) { diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index de08b79f57..5bdd86c671 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -69,6 +69,7 @@ namespace osu.Game.Overlays.Toolbar AutoSizeAxes = Axes.X, Children = new Drawable[] { + new ToolbarNewsButton(), new ToolbarChangelogButton(), new ToolbarRankingsButton(), new ToolbarBeatmapListingButton(), diff --git a/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs new file mode 100644 index 0000000000..e813a3f4cb --- /dev/null +++ b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs @@ -0,0 +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.Allocation; +using osu.Framework.Graphics.Sprites; + +namespace osu.Game.Overlays.Toolbar +{ + public class ToolbarNewsButton : ToolbarOverlayToggleButton + { + public ToolbarNewsButton() + { + Icon = FontAwesome.Solid.Newspaper; + } + + [BackgroundDependencyLoader(true)] + private void load(NewsOverlay news) + { + StateContainer = news; + } + } +} From dac98c8914c7c7f873fa26945bf2195fd72f271b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Jul 2020 14:55:02 +0300 Subject: [PATCH 032/185] Add shortcut for news overlay --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 4 ++++ osu.Game/OsuGame.cs | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 6ae420b162..9f59551b94 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -36,6 +36,7 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleDirect), new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications), + new KeyBinding(new[] { InputKey.Control, InputKey.A }, GlobalAction.ToggleNews), new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), @@ -165,5 +166,8 @@ namespace osu.Game.Input.Bindings [Description("Pause")] PauseGameplay, + + [Description("Toggle news overlay")] + ToggleNews } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 84b32673d5..fc904cb09c 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -872,6 +872,10 @@ namespace osu.Game dashboard.ToggleVisibility(); return true; + case GlobalAction.ToggleNews: + news.ToggleVisibility(); + return true; + case GlobalAction.ResetInputSettings: var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); From 2d9909cdd89b658411b450ad6e6ee86a8ba67193 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Jul 2020 15:18:01 +0300 Subject: [PATCH 033/185] Make news cards clickable --- .../Visual/Online/TestSceneNewsCard.cs | 7 +-- osu.Game/Overlays/News/NewsCard.cs | 50 ++++++++----------- 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNewsCard.cs b/osu.Game.Tests/Visual/Online/TestSceneNewsCard.cs index 0446cadac9..17675bfbc0 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNewsCard.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNewsCard.cs @@ -31,15 +31,16 @@ namespace osu.Game.Tests.Visual.Online { new NewsCard(new APINewsPost { - Title = "This post has an image which starts with \"/\" and has many authors!", + Title = "This post has an image which starts with \"/\" and has many authors! (clickable)", Preview = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", Author = "someone, someone1, someone2, someone3, someone4", FirstImage = "/help/wiki/shared/news/banners/monthly-beatmapping-contest.png", - PublishedAt = DateTimeOffset.Now + PublishedAt = DateTimeOffset.Now, + Slug = "2020-07-16-summer-theme-park-2020-voting-open" }), new NewsCard(new APINewsPost { - Title = "This post has a full-url image! (HTML entity: &)", + Title = "This post has a full-url image! (HTML entity: &) (non-clickable)", Preview = "boom (HTML entity: &)", Author = "user (HTML entity: &)", FirstImage = "https://assets.ppy.sh/artists/88/header.jpg", diff --git a/osu.Game/Overlays/News/NewsCard.cs b/osu.Game/Overlays/News/NewsCard.cs index 9c478a7c1d..38362038ae 100644 --- a/osu.Game/Overlays/News/NewsCard.cs +++ b/osu.Game/Overlays/News/NewsCard.cs @@ -2,6 +2,7 @@ // 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.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -10,18 +11,17 @@ 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.Framework.Platform; using osu.Game.Graphics; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Overlays.News { - public class NewsCard : CompositeDrawable + public class NewsCard : OsuHoverContainer { - [Resolved] - private OverlayColourProvider colourProvider { get; set; } + protected override IEnumerable EffectTargets => new[] { background }; private readonly APINewsPost post; @@ -31,24 +31,28 @@ namespace osu.Game.Overlays.News public NewsCard(APINewsPost post) { this.post = post; - } - [BackgroundDependencyLoader] - private void load() - { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; Masking = true; CornerRadius = 6; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider, GameHost host) + { + if (post.Slug != null) + { + TooltipText = "view in browser"; + Action = () => host.OpenUrlExternally("https://osu.ppy.sh/home/news/" + post.Slug); + } NewsBackground bg; - - InternalChildren = new Drawable[] + AddRange(new Drawable[] { background = new Box { - RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background4 + RelativeSizeAxes = Axes.Both }, new FillFlowContainer { @@ -104,9 +108,11 @@ namespace osu.Game.Overlays.News } } } - }, - new HoverClickSounds() - }; + } + }); + + IdleColour = colourProvider.Background4; + HoverColour = colourProvider.Background3; bg.OnLoadComplete += d => d.FadeIn(250, Easing.In); @@ -116,18 +122,6 @@ namespace osu.Game.Overlays.News main.AddText(post.Author, t => t.Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold)); } - protected override bool OnHover(HoverEvent e) - { - background.FadeColour(colourProvider.Background3, 200, Easing.OutQuint); - return true; - } - - protected override void OnHoverLost(HoverLostEvent e) - { - background.FadeColour(colourProvider.Background4, 200, Easing.OutQuint); - base.OnHoverLost(e); - } - [LongRunningLoad] private class NewsBackground : Sprite { From f67b93936ffeb85bb86b0d27e4cd5af94148ea3b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Jul 2020 23:11:55 +0300 Subject: [PATCH 034/185] Remove shortcut --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 4 ---- osu.Game/OsuGame.cs | 4 ---- 2 files changed, 8 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 9f59551b94..6ae420b162 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -36,7 +36,6 @@ namespace osu.Game.Input.Bindings new KeyBinding(new[] { InputKey.Control, InputKey.O }, GlobalAction.ToggleSettings), new KeyBinding(new[] { InputKey.Control, InputKey.D }, GlobalAction.ToggleDirect), new KeyBinding(new[] { InputKey.Control, InputKey.N }, GlobalAction.ToggleNotifications), - new KeyBinding(new[] { InputKey.Control, InputKey.A }, GlobalAction.ToggleNews), new KeyBinding(InputKey.Escape, GlobalAction.Back), new KeyBinding(InputKey.ExtraMouseButton1, GlobalAction.Back), @@ -166,8 +165,5 @@ namespace osu.Game.Input.Bindings [Description("Pause")] PauseGameplay, - - [Description("Toggle news overlay")] - ToggleNews } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index fc904cb09c..84b32673d5 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -872,10 +872,6 @@ namespace osu.Game dashboard.ToggleVisibility(); return true; - case GlobalAction.ToggleNews: - news.ToggleVisibility(); - return true; - case GlobalAction.ResetInputSettings: var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); From ab23e7dfd4faad3040a8e24eb114a16b6956c183 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 16 Jul 2020 23:14:51 +0300 Subject: [PATCH 035/185] Protect the NewsCard from clicks while hovering DateContainer --- osu.Game/Overlays/News/NewsCard.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/News/NewsCard.cs b/osu.Game/Overlays/News/NewsCard.cs index 38362038ae..201c3ce826 100644 --- a/osu.Game/Overlays/News/NewsCard.cs +++ b/osu.Game/Overlays/News/NewsCard.cs @@ -11,6 +11,7 @@ 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.Framework.Platform; using osu.Game.Graphics; using osu.Game.Graphics.Containers; @@ -187,6 +188,8 @@ namespace osu.Game.Overlays.News } }; } + + protected override bool OnClick(ClickEvent e) => true; // Protects the NewsCard from clicks while hovering DateContainer } } } From a8991bb8bfa69a62d9e9a753a7d8d8ba435d48bd Mon Sep 17 00:00:00 2001 From: Joehu Date: Mon, 20 Jul 2020 20:13:09 -0700 Subject: [PATCH 036/185] Fix keybind clear button always clearing first keybind regardless of target --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index eafb4572ca..d58acc1ac4 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -187,7 +187,7 @@ namespace osu.Game.Overlays.KeyBinding if (bindTarget.IsHovered) finalise(); - else + else if (buttons.Any(b => b.IsHovered)) updateBindTarget(); } From d9633fee64a270364f618fc415d5d48d93a808e5 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Jul 2020 18:47:09 +0900 Subject: [PATCH 037/185] Rename request --- .../Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs | 2 +- ...PlaylistScoresRequest.cs => IndexPlaylistScoresRequest.cs} | 4 ++-- osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Online/Multiplayer/{GetRoomPlaylistScoresRequest.cs => IndexPlaylistScoresRequest.cs} (82%) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 0023866124..37d31264b6 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Multiplayer { switch (request) { - case GetRoomPlaylistScoresRequest r: + case IndexPlaylistScoresRequest r: if (delay == 0) success(); else diff --git a/osu.Game/Online/Multiplayer/GetRoomPlaylistScoresRequest.cs b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs similarity index 82% rename from osu.Game/Online/Multiplayer/GetRoomPlaylistScoresRequest.cs rename to osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs index 3d3bd20ff3..c435dc6030 100644 --- a/osu.Game/Online/Multiplayer/GetRoomPlaylistScoresRequest.cs +++ b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs @@ -7,12 +7,12 @@ using osu.Game.Online.API; namespace osu.Game.Online.Multiplayer { - public class GetRoomPlaylistScoresRequest : APIRequest + public class IndexPlaylistScoresRequest : APIRequest { private readonly int roomId; private readonly int playlistItemId; - public GetRoomPlaylistScoresRequest(int roomId, int playlistItemId) + public IndexPlaylistScoresRequest(int roomId, int playlistItemId) { this.roomId = roomId; this.playlistItemId = playlistItemId; diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index f367d44347..b90c7252c4 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Multi.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { - var req = new GetRoomPlaylistScoresRequest(roomId, playlistItem.ID); + var req = new IndexPlaylistScoresRequest(roomId, playlistItem.ID); req.Success += r => { From ec33a6ea8791b6878912b26dd9209063a45f5719 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Jul 2020 18:47:40 +0900 Subject: [PATCH 038/185] Add additional responses --- .../Online/Multiplayer/MultiplayerScores.cs | 39 +++++++++++++++++++ .../Multiplayer/MultiplayerScoresAround.cs | 25 ++++++++++++ .../Multiplayer/MultiplayerScoresSort.cs | 14 +++++++ 3 files changed, 78 insertions(+) create mode 100644 osu.Game/Online/Multiplayer/MultiplayerScores.cs create mode 100644 osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs create mode 100644 osu.Game/Online/Multiplayer/MultiplayerScoresSort.cs diff --git a/osu.Game/Online/Multiplayer/MultiplayerScores.cs b/osu.Game/Online/Multiplayer/MultiplayerScores.cs new file mode 100644 index 0000000000..f944a8999c --- /dev/null +++ b/osu.Game/Online/Multiplayer/MultiplayerScores.cs @@ -0,0 +1,39 @@ +// 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; +using osu.Game.Online.API.Requests; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// An object which contains scores and related data for fetching next pages. + /// + public class MultiplayerScores + { + /// + /// To be used for fetching the next page. + /// + [JsonProperty("cursor")] + public Cursor Cursor { get; set; } + + /// + /// The scores. + /// + [JsonProperty("scores")] + public List Scores { get; set; } + + /// + /// The total scores in the playlist item. Only provided via . + /// + [JsonProperty("total")] + public int? TotalScores { get; set; } + + /// + /// The user's score, if any. Only provided via . + /// + [JsonProperty("user_score")] + public MultiplayerScore UserScore { get; set; } + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs b/osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs new file mode 100644 index 0000000000..e83cc1b753 --- /dev/null +++ b/osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs @@ -0,0 +1,25 @@ +// 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.Online.Multiplayer +{ + /// + /// An object which stores scores higher and lower than the user's score. + /// + public class MultiplayerScoresAround + { + /// + /// Scores sorted "higher" than the user's score, depending on the sorting order. + /// + [JsonProperty("higher")] + public MultiplayerScores Higher { get; set; } + + /// + /// Scores sorted "lower" than the user's score, depending on the sorting order. + /// + [JsonProperty("lower")] + public MultiplayerScores Lower { get; set; } + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerScoresSort.cs b/osu.Game/Online/Multiplayer/MultiplayerScoresSort.cs new file mode 100644 index 0000000000..decb1c4dfe --- /dev/null +++ b/osu.Game/Online/Multiplayer/MultiplayerScoresSort.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.Online.Multiplayer +{ + /// + /// Sorting option for indexing multiplayer scores. + /// + public enum MultiplayerScoresSort + { + Ascending, + Descending + } +} From 634efe31f843f53a8a86ae01319705b748398449 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Jul 2020 18:51:54 +0900 Subject: [PATCH 039/185] Inherit ResponseWithCursor --- osu.Game/Online/Multiplayer/MultiplayerScores.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game/Online/Multiplayer/MultiplayerScores.cs b/osu.Game/Online/Multiplayer/MultiplayerScores.cs index f944a8999c..6f74fc8984 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScores.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerScores.cs @@ -10,14 +10,8 @@ namespace osu.Game.Online.Multiplayer /// /// An object which contains scores and related data for fetching next pages. /// - public class MultiplayerScores + public class MultiplayerScores : ResponseWithCursor { - /// - /// To be used for fetching the next page. - /// - [JsonProperty("cursor")] - public Cursor Cursor { get; set; } - /// /// The scores. /// From c75955e3819f5b7c8ec3a77af1bce267f2008633 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Jul 2020 18:52:25 +0900 Subject: [PATCH 040/185] Add responses to MultiplayerScore --- osu.Game/Online/Multiplayer/MultiplayerScore.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Online/Multiplayer/MultiplayerScore.cs b/osu.Game/Online/Multiplayer/MultiplayerScore.cs index 3bbf19b11f..1793ba72ef 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScore.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerScore.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq; +using JetBrains.Annotations; using Newtonsoft.Json; using Newtonsoft.Json.Converters; using osu.Game.Online.API; @@ -47,6 +48,19 @@ namespace osu.Game.Online.Multiplayer [JsonProperty("ended_at")] public DateTimeOffset EndedAt { get; set; } + /// + /// The position of this score, starting at 1. + /// + [JsonProperty("position")] + public int? Position { get; set; } + + /// + /// Any scores in the room around this score. + /// + [JsonProperty("scores_around")] + [CanBeNull] + public MultiplayerScoresAround ScoresAround { get; set; } + public ScoreInfo CreateScoreInfo(PlaylistItem playlistItem) { var rulesetInstance = playlistItem.Ruleset.Value.CreateInstance(); From 334fb7d4753386c6d534efe27e80e421a3b8a94f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Jul 2020 18:54:41 +0900 Subject: [PATCH 041/185] Add additional params to index request --- .../Multiplayer/IndexPlaylistScoresRequest.cs | 32 ++++++++++++++++++- 1 file changed, 31 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs index c435dc6030..b43614bf6c 100644 --- a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs +++ b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs @@ -3,19 +3,49 @@ using System.Collections.Generic; using Newtonsoft.Json; +using osu.Framework.IO.Network; +using osu.Game.Extensions; using osu.Game.Online.API; +using osu.Game.Online.API.Requests; namespace osu.Game.Online.Multiplayer { + /// + /// Returns a list of scores for the specified playlist item. + /// public class IndexPlaylistScoresRequest : APIRequest { private readonly int roomId; private readonly int playlistItemId; + private readonly Cursor cursor; + private readonly MultiplayerScoresSort? sort; - public IndexPlaylistScoresRequest(int roomId, int playlistItemId) + public IndexPlaylistScoresRequest(int roomId, int playlistItemId, Cursor cursor = null, MultiplayerScoresSort? sort = null) { this.roomId = roomId; this.playlistItemId = playlistItemId; + this.cursor = cursor; + this.sort = sort; + } + + protected override WebRequest CreateWebRequest() + { + var req = base.CreateWebRequest(); + + req.AddCursor(cursor); + + switch (sort) + { + case MultiplayerScoresSort.Ascending: + req.AddParameter("sort", "scores_asc"); + break; + + case MultiplayerScoresSort.Descending: + req.AddParameter("sort", "scores_desc"); + break; + } + + return req; } protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores"; From f8401a76a25a59706226eee625dc479d13116c10 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Jul 2020 19:40:00 +0900 Subject: [PATCH 042/185] Use show/index requests in results screen --- .../ShowPlaylistUserScoreRequest.cs | 23 +++++++ .../Multi/Ranking/TimeshiftResultsScreen.cs | 63 +++++++++++++++++-- 2 files changed, 80 insertions(+), 6 deletions(-) create mode 100644 osu.Game/Online/Multiplayer/ShowPlaylistUserScoreRequest.cs diff --git a/osu.Game/Online/Multiplayer/ShowPlaylistUserScoreRequest.cs b/osu.Game/Online/Multiplayer/ShowPlaylistUserScoreRequest.cs new file mode 100644 index 0000000000..936b8bbe89 --- /dev/null +++ b/osu.Game/Online/Multiplayer/ShowPlaylistUserScoreRequest.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.Game.Online.API; + +namespace osu.Game.Online.Multiplayer +{ + public class ShowPlaylistUserScoreRequest : APIRequest + { + private readonly int roomId; + private readonly int playlistItemId; + private readonly long userId; + + public ShowPlaylistUserScoreRequest(int roomId, int playlistItemId, long userId) + { + this.roomId = roomId; + this.playlistItemId = playlistItemId; + this.userId = userId; + } + + protected override string Target => $"rooms/{roomId}/playlist/{playlistItemId}/scores/users/{userId}"; + } +} diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index b90c7252c4..47aab02b1a 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -21,6 +22,11 @@ namespace osu.Game.Screens.Multi.Ranking private readonly PlaylistItem playlistItem; private LoadingSpinner loadingLayer; + private Cursor higherScoresCursor; + private Cursor lowerScoresCursor; + + [Resolved] + private IAPIProvider api { get; set; } public TimeshiftResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry = true) : base(score, allowRetry) @@ -44,17 +50,62 @@ namespace osu.Game.Screens.Multi.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { - var req = new IndexPlaylistScoresRequest(roomId, playlistItem.ID); + // This performs two requests: + // 1. A request to show the user's score. + // 2. If (1) fails, a request to index the room. - req.Success += r => + var userScoreReq = new ShowPlaylistUserScoreRequest(roomId, playlistItem.ID, api.LocalUser.Value.Id); + + userScoreReq.Success += userScore => { - scoresCallback?.Invoke(r.Scores.Where(s => s.ID != Score?.OnlineScoreID).Select(s => s.CreateScoreInfo(playlistItem))); - loadingLayer.Hide(); + var allScores = new List { userScore }; + + if (userScore.ScoresAround?.Higher != null) + { + allScores.AddRange(userScore.ScoresAround.Higher.Scores); + higherScoresCursor = userScore.ScoresAround.Higher.Cursor; + } + + if (userScore.ScoresAround?.Lower != null) + { + allScores.AddRange(userScore.ScoresAround.Lower.Scores); + lowerScoresCursor = userScore.ScoresAround.Lower.Cursor; + } + + success(allScores); }; - req.Failure += _ => loadingLayer.Hide(); + userScoreReq.Failure += _ => + { + // Fallback to a normal index. + var indexReq = new IndexPlaylistScoresRequest(roomId, playlistItem.ID); + indexReq.Success += r => success(r.Scores); + indexReq.Failure += __ => loadingLayer.Hide(); + api.Queue(indexReq); + }; - return req; + return userScoreReq; + + void success(List scores) + { + var scoreInfos = new List(scores.Select(s => s.CreateScoreInfo(playlistItem))); + + // Select a score if we don't already have one selected. + // Note: This is done before the callback so that the panel list centres on the selected score before panels are added (eliminating initial scroll). + if (SelectedScore.Value == null) + { + Schedule(() => + { + // Prefer selecting the local user's score, or otherwise default to the first visible score. + SelectedScore.Value = scoreInfos.FirstOrDefault(s => s.User.Id == api.LocalUser.Value.Id) ?? scoreInfos.FirstOrDefault(); + }); + } + + // Invoke callback to add the scores. Exclude the user's current score which was added previously. + scoresCallback?.Invoke(scoreInfos.Where(s => s.ID != Score?.OnlineScoreID)); + + loadingLayer.Hide(); + } } } } From 568fb51ce239c8f2fbe26ab63d83ea1a508bb65e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Jul 2020 20:24:42 +0900 Subject: [PATCH 043/185] Remove RoomPlaylistScores intermediate class --- .../Multiplayer/TestSceneTimeshiftResultsScreen.cs | 2 +- .../Online/Multiplayer/IndexPlaylistScoresRequest.cs | 10 +--------- 2 files changed, 2 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 37d31264b6..44ca676c4f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -111,7 +111,7 @@ namespace osu.Game.Tests.Visual.Multiplayer void success() { - r.TriggerSuccess(new RoomPlaylistScores { Scores = roomScores }); + r.TriggerSuccess(new MultiplayerScores { Scores = roomScores }); roomsReceived = true; } diff --git a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs index b43614bf6c..d23208d338 100644 --- a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs +++ b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.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 Newtonsoft.Json; using osu.Framework.IO.Network; using osu.Game.Extensions; using osu.Game.Online.API; @@ -13,7 +11,7 @@ namespace osu.Game.Online.Multiplayer /// /// Returns a list of scores for the specified playlist item. /// - public class IndexPlaylistScoresRequest : APIRequest + public class IndexPlaylistScoresRequest : APIRequest { private readonly int roomId; private readonly int playlistItemId; @@ -50,10 +48,4 @@ namespace osu.Game.Online.Multiplayer protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores"; } - - public class RoomPlaylistScores - { - [JsonProperty("scores")] - public List Scores { get; set; } - } } From b7790de66fbc2d11126fb0c0dadf17090c647aa0 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Jul 2020 20:24:48 +0900 Subject: [PATCH 044/185] Fix incorrect sort param --- osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs index d23208d338..7273c0eea6 100644 --- a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs +++ b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs @@ -35,11 +35,11 @@ namespace osu.Game.Online.Multiplayer switch (sort) { case MultiplayerScoresSort.Ascending: - req.AddParameter("sort", "scores_asc"); + req.AddParameter("sort", "score_asc"); break; case MultiplayerScoresSort.Descending: - req.AddParameter("sort", "scores_desc"); + req.AddParameter("sort", "score_desc"); break; } From 46ea775cfb36d15a3dc7a13098bd62c18cbb7987 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 22 Jul 2020 20:24:55 +0900 Subject: [PATCH 045/185] Implement paging --- .../Multi/Ranking/TimeshiftResultsScreen.cs | 84 +++++++++++++++---- osu.Game/Screens/Ranking/ResultsScreen.cs | 41 +++++++-- osu.Game/Screens/Ranking/ScorePanelList.cs | 4 + 3 files changed, 109 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index 47aab02b1a..75a61b92ee 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -72,40 +73,93 @@ namespace osu.Game.Screens.Multi.Ranking lowerScoresCursor = userScore.ScoresAround.Lower.Cursor; } - success(allScores); + performSuccessCallback(scoresCallback, allScores); }; userScoreReq.Failure += _ => { // Fallback to a normal index. var indexReq = new IndexPlaylistScoresRequest(roomId, playlistItem.ID); - indexReq.Success += r => success(r.Scores); + + indexReq.Success += r => + { + performSuccessCallback(scoresCallback, r.Scores); + lowerScoresCursor = r.Cursor; + }; + indexReq.Failure += __ => loadingLayer.Hide(); + api.Queue(indexReq); }; return userScoreReq; + } - void success(List scores) + protected override APIRequest FetchNextPage(int direction, Action> scoresCallback) + { + Debug.Assert(direction == 1 || direction == -1); + + Cursor cursor; + MultiplayerScoresSort sort; + + switch (direction) { - var scoreInfos = new List(scores.Select(s => s.CreateScoreInfo(playlistItem))); + case -1: + cursor = higherScoresCursor; + sort = MultiplayerScoresSort.Ascending; + break; - // Select a score if we don't already have one selected. - // Note: This is done before the callback so that the panel list centres on the selected score before panels are added (eliminating initial scroll). - if (SelectedScore.Value == null) + default: + cursor = lowerScoresCursor; + sort = MultiplayerScoresSort.Descending; + break; + } + + if (cursor == null) + return null; + + var indexReq = new IndexPlaylistScoresRequest(roomId, playlistItem.ID, cursor, sort); + + indexReq.Success += r => + { + switch (direction) { - Schedule(() => - { - // Prefer selecting the local user's score, or otherwise default to the first visible score. - SelectedScore.Value = scoreInfos.FirstOrDefault(s => s.User.Id == api.LocalUser.Value.Id) ?? scoreInfos.FirstOrDefault(); - }); + case -1: + higherScoresCursor = r.Cursor; + break; + + default: + lowerScoresCursor = r.Cursor; + break; } - // Invoke callback to add the scores. Exclude the user's current score which was added previously. - scoresCallback?.Invoke(scoreInfos.Where(s => s.ID != Score?.OnlineScoreID)); + performSuccessCallback(scoresCallback, r.Scores); + }; - loadingLayer.Hide(); + indexReq.Failure += _ => loadingLayer.Hide(); + + return indexReq; + } + + private void performSuccessCallback(Action> callback, List scores) + { + var scoreInfos = new List(scores.Select(s => s.CreateScoreInfo(playlistItem))); + + // Select a score if we don't already have one selected. + // Note: This is done before the callback so that the panel list centres on the selected score before panels are added (eliminating initial scroll). + if (SelectedScore.Value == null) + { + Schedule(() => + { + // Prefer selecting the local user's score, or otherwise default to the first visible score. + SelectedScore.Value = scoreInfos.FirstOrDefault(s => s.User.Id == api.LocalUser.Value.Id) ?? scoreInfos.FirstOrDefault(); + }); } + + // Invoke callback to add the scores. Exclude the user's current score which was added previously. + callback?.Invoke(scoreInfos.Where(s => s.ID != Score?.OnlineScoreID)); + + loadingLayer.Hide(); } } } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 44458d8c8e..c5512822b2 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -164,11 +164,7 @@ namespace osu.Game.Screens.Ranking { base.LoadComplete(); - var req = FetchScores(scores => Schedule(() => - { - foreach (var s in scores) - addScore(s); - })); + var req = FetchScores(fetchScoresCallback); if (req != null) api.Queue(req); @@ -176,6 +172,29 @@ namespace osu.Game.Screens.Ranking statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true); } + private APIRequest nextPageRequest; + + protected override void Update() + { + base.Update(); + + if (hasAnyScores && nextPageRequest == null) + { + if (scorePanelList.IsScrolledToStart) + nextPageRequest = FetchNextPage(-1, fetchScoresCallback); + else if (scorePanelList.IsScrolledToEnd) + nextPageRequest = FetchNextPage(1, fetchScoresCallback); + + if (nextPageRequest != null) + { + nextPageRequest.Success += () => nextPageRequest = null; + nextPageRequest.Failure += _ => nextPageRequest = null; + + api.Queue(nextPageRequest); + } + } + } + /// /// Performs a fetch/refresh of scores to be displayed. /// @@ -183,6 +202,18 @@ namespace osu.Game.Screens.Ranking /// An responsible for the fetch operation. This will be queued and performed automatically. protected virtual APIRequest FetchScores(Action> scoresCallback) => null; + protected virtual APIRequest FetchNextPage(int direction, Action> scoresCallback) => null; + + private bool hasAnyScores; + + private void fetchScoresCallback(IEnumerable scores) => Schedule(() => + { + foreach (var s in scores) + addScore(s); + + hasAnyScores = true; + }); + public override void OnEntering(IScreen last) { base.OnEntering(last); diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 0f8bc82ac0..aba8314732 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -26,6 +26,10 @@ namespace osu.Game.Screens.Ranking /// private const float expanded_panel_spacing = 15; + public bool IsScrolledToStart => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.Current <= 100; + + public bool IsScrolledToEnd => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.IsScrolledToEnd(100); + /// /// An action to be invoked if a is clicked while in an expanded state. /// From 7e5147761fad18ea1bd904c0d718af15d8c2cc42 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 25 Jul 2020 09:26:29 +0300 Subject: [PATCH 046/185] Use OverlayView for FrontPageDisplay --- .../News/Displays/FrontPageDisplay.cs | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs index 67b5edfafd..2c228e2f78 100644 --- a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs +++ b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs @@ -13,18 +13,18 @@ using osuTK; namespace osu.Game.Overlays.News.Displays { - public class FrontPageDisplay : CompositeDrawable + public class FrontPageDisplay : OverlayView { - [Resolved] - private IAPIProvider api { get; set; } + protected override APIRequest CreateRequest() => new GetNewsRequest(); - private readonly FillFlowContainer content; - private readonly ShowMoreButton showMore; + private FillFlowContainer content; + private ShowMoreButton showMore; - private GetNewsRequest request; + private GetNewsRequest olderPostsRequest; private Cursor lastCursor; - public FrontPageDisplay() + [BackgroundDependencyLoader] + private void load() { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; @@ -60,32 +60,19 @@ namespace osu.Game.Overlays.News.Displays { Top = 15 }, - Action = fetchPage, + Action = fetchOlderPosts, Alpha = 0 } } }; } - [BackgroundDependencyLoader] - private void load() - { - fetchPage(); - } - - private void fetchPage() - { - request?.Cancel(); - - request = new GetNewsRequest(lastCursor); - request.Success += response => Schedule(() => createContent(response)); - api.PerformAsync(request); - } - private CancellationTokenSource cancellationToken; - private void createContent(GetNewsResponse response) + protected override void OnSuccess(GetNewsResponse response) { + cancellationToken?.Cancel(); + lastCursor = response.Cursor; var flow = new FillFlowContainer @@ -105,9 +92,18 @@ namespace osu.Game.Overlays.News.Displays }, (cancellationToken = new CancellationTokenSource()).Token); } + private void fetchOlderPosts() + { + olderPostsRequest?.Cancel(); + + olderPostsRequest = new GetNewsRequest(lastCursor); + olderPostsRequest.Success += response => Schedule(() => OnSuccess(response)); + API.PerformAsync(olderPostsRequest); + } + protected override void Dispose(bool isDisposing) { - request?.Cancel(); + olderPostsRequest?.Cancel(); cancellationToken?.Cancel(); base.Dispose(isDisposing); } From c6ae2f520e8a7c1ea2446acf401e8a352f57028b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 25 Jul 2020 10:39:10 +0300 Subject: [PATCH 047/185] Fix FrontPageDisplay is adding more news posts on api state change --- osu.Game/Overlays/News/Displays/FrontPageDisplay.cs | 2 ++ osu.Game/Overlays/OverlayView.cs | 12 +++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs index 2c228e2f78..fbacf53bf6 100644 --- a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs +++ b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs @@ -15,6 +15,8 @@ namespace osu.Game.Overlays.News.Displays { public class FrontPageDisplay : OverlayView { + protected override bool PerformFetchOnApiStateChange => false; + protected override APIRequest CreateRequest() => new GetNewsRequest(); private FillFlowContainer content; diff --git a/osu.Game/Overlays/OverlayView.cs b/osu.Game/Overlays/OverlayView.cs index 3e2c54c726..f73ca3aa6e 100644 --- a/osu.Game/Overlays/OverlayView.cs +++ b/osu.Game/Overlays/OverlayView.cs @@ -18,6 +18,11 @@ namespace osu.Game.Overlays public abstract class OverlayView : CompositeDrawable, IOnlineComponent where T : class { + /// + /// Whether we should perform fetch on api state change to online (true by default). + /// + protected virtual bool PerformFetchOnApiStateChange => true; + [Resolved] protected IAPIProvider API { get; private set; } @@ -33,6 +38,10 @@ namespace osu.Game.Overlays { base.LoadComplete(); API.Register(this); + + // If property is true - fetch will be triggered automatically by APIStateChanged and if not - we need to manually call it. + if (!PerformFetchOnApiStateChange) + PerformFetch(); } /// @@ -64,7 +73,8 @@ namespace osu.Game.Overlays switch (state) { case APIState.Online: - PerformFetch(); + if (PerformFetchOnApiStateChange) + PerformFetch(); break; } } From 788395f8bf24f1702697298c31a7435897c7e7f3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 25 Jul 2020 15:08:06 +0300 Subject: [PATCH 048/185] Revert changes regarding FrontPageDisplay and OverlayView --- .../News/Displays/FrontPageDisplay.cs | 35 ++++++++++--------- osu.Game/Overlays/OverlayView.cs | 12 +------ 2 files changed, 19 insertions(+), 28 deletions(-) diff --git a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs index fbacf53bf6..0f177f151a 100644 --- a/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs +++ b/osu.Game/Overlays/News/Displays/FrontPageDisplay.cs @@ -13,16 +13,15 @@ using osuTK; namespace osu.Game.Overlays.News.Displays { - public class FrontPageDisplay : OverlayView + public class FrontPageDisplay : CompositeDrawable { - protected override bool PerformFetchOnApiStateChange => false; - - protected override APIRequest CreateRequest() => new GetNewsRequest(); + [Resolved] + private IAPIProvider api { get; set; } private FillFlowContainer content; private ShowMoreButton showMore; - private GetNewsRequest olderPostsRequest; + private GetNewsRequest request; private Cursor lastCursor; [BackgroundDependencyLoader] @@ -62,16 +61,27 @@ namespace osu.Game.Overlays.News.Displays { Top = 15 }, - Action = fetchOlderPosts, + Action = performFetch, Alpha = 0 } } }; + + performFetch(); + } + + private void performFetch() + { + request?.Cancel(); + + request = new GetNewsRequest(lastCursor); + request.Success += response => Schedule(() => onSuccess(response)); + api.PerformAsync(request); } private CancellationTokenSource cancellationToken; - protected override void OnSuccess(GetNewsResponse response) + private void onSuccess(GetNewsResponse response) { cancellationToken?.Cancel(); @@ -94,18 +104,9 @@ namespace osu.Game.Overlays.News.Displays }, (cancellationToken = new CancellationTokenSource()).Token); } - private void fetchOlderPosts() - { - olderPostsRequest?.Cancel(); - - olderPostsRequest = new GetNewsRequest(lastCursor); - olderPostsRequest.Success += response => Schedule(() => OnSuccess(response)); - API.PerformAsync(olderPostsRequest); - } - protected override void Dispose(bool isDisposing) { - olderPostsRequest?.Cancel(); + request?.Cancel(); cancellationToken?.Cancel(); base.Dispose(isDisposing); } diff --git a/osu.Game/Overlays/OverlayView.cs b/osu.Game/Overlays/OverlayView.cs index f73ca3aa6e..3e2c54c726 100644 --- a/osu.Game/Overlays/OverlayView.cs +++ b/osu.Game/Overlays/OverlayView.cs @@ -18,11 +18,6 @@ namespace osu.Game.Overlays public abstract class OverlayView : CompositeDrawable, IOnlineComponent where T : class { - /// - /// Whether we should perform fetch on api state change to online (true by default). - /// - protected virtual bool PerformFetchOnApiStateChange => true; - [Resolved] protected IAPIProvider API { get; private set; } @@ -38,10 +33,6 @@ namespace osu.Game.Overlays { base.LoadComplete(); API.Register(this); - - // If property is true - fetch will be triggered automatically by APIStateChanged and if not - we need to manually call it. - if (!PerformFetchOnApiStateChange) - PerformFetch(); } /// @@ -73,8 +64,7 @@ namespace osu.Game.Overlays switch (state) { case APIState.Online: - if (PerformFetchOnApiStateChange) - PerformFetch(); + PerformFetch(); break; } } From ff3cb6487d2d474ff8d9ec9c6f164ee47d6efe62 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Jul 2020 16:52:07 +0900 Subject: [PATCH 049/185] Store all linked cancellation tokens --- osu.Game/Beatmaps/BeatmapDifficultyManager.cs | 33 ++++++++++++++++--- 1 file changed, 28 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs index 5e644fbf1c..e625f6f96e 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs @@ -109,25 +109,42 @@ namespace osu.Game.Beatmaps } private CancellationTokenSource trackedUpdateCancellationSource; + private readonly List linkedCancellationSources = new List(); /// /// Updates all tracked using the current ruleset and mods. /// private void updateTrackedBindables() { - trackedUpdateCancellationSource?.Cancel(); + cancelTrackedBindableUpdate(); trackedUpdateCancellationSource = new CancellationTokenSource(); foreach (var b in trackedBindables) { - if (trackedUpdateCancellationSource.IsCancellationRequested) - break; + var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(trackedUpdateCancellationSource.Token, b.CancellationToken); + linkedCancellationSources.Add(linkedSource); - using (var linkedSource = CancellationTokenSource.CreateLinkedTokenSource(trackedUpdateCancellationSource.Token, b.CancellationToken)) - updateBindable(b, currentRuleset.Value, currentMods.Value, linkedSource.Token); + updateBindable(b, currentRuleset.Value, currentMods.Value, linkedSource.Token); } } + /// + /// Cancels the existing update of all tracked via . + /// + private void cancelTrackedBindableUpdate() + { + trackedUpdateCancellationSource?.Cancel(); + trackedUpdateCancellationSource = null; + + foreach (var c in linkedCancellationSources) + { + c.Cancel(); + c.Dispose(); + } + + linkedCancellationSources.Clear(); + } + /// /// Updates the value of a with a given ruleset + mods. /// @@ -220,6 +237,12 @@ namespace osu.Game.Beatmaps return difficultyCache.TryGetValue(key, out existingDifficulty); } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + cancelTrackedBindableUpdate(); + } + private readonly struct DifficultyCacheLookup : IEquatable { public readonly int BeatmapId; From 96f68a32518422f90abbdce672089bd5b5ee50f3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Jul 2020 16:52:19 +0900 Subject: [PATCH 050/185] Reorder method --- osu.Game/Beatmaps/BeatmapDifficultyManager.cs | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs index e625f6f96e..98a1462d99 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs @@ -145,6 +145,22 @@ namespace osu.Game.Beatmaps linkedCancellationSources.Clear(); } + /// + /// Creates a new and triggers an initial value update. + /// + /// The that star difficulty should correspond to. + /// The initial to get the difficulty with. + /// The initial s to get the difficulty with. + /// An optional which stops updating the star difficulty for the given . + /// The . + private BindableStarDifficulty createBindable([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo initialRulesetInfo, [CanBeNull] IEnumerable initialMods, + CancellationToken cancellationToken) + { + var bindable = new BindableStarDifficulty(beatmapInfo, cancellationToken); + updateBindable(bindable, initialRulesetInfo, initialMods, cancellationToken); + return bindable; + } + /// /// Updates the value of a with a given ruleset + mods. /// @@ -165,22 +181,6 @@ namespace osu.Game.Beatmaps }, cancellationToken); } - /// - /// Creates a new and triggers an initial value update. - /// - /// The that star difficulty should correspond to. - /// The initial to get the difficulty with. - /// The initial s to get the difficulty with. - /// An optional which stops updating the star difficulty for the given . - /// The . - private BindableStarDifficulty createBindable([NotNull] BeatmapInfo beatmapInfo, [CanBeNull] RulesetInfo initialRulesetInfo, [CanBeNull] IEnumerable initialMods, - CancellationToken cancellationToken) - { - var bindable = new BindableStarDifficulty(beatmapInfo, cancellationToken); - updateBindable(bindable, initialRulesetInfo, initialMods, cancellationToken); - return bindable; - } - /// /// Computes the difficulty defined by a key, and stores it to the timed cache. /// From fa25f8aef9993c53f2ac72a9ed1ecd4446848b3b Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Jul 2020 17:23:35 +0900 Subject: [PATCH 051/185] Dispose update scheduler --- osu.Game/Beatmaps/BeatmapDifficultyManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs index 98a1462d99..b3afd1d4fd 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs @@ -240,7 +240,9 @@ namespace osu.Game.Beatmaps protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); + cancelTrackedBindableUpdate(); + updateScheduler.Dispose(); } private readonly struct DifficultyCacheLookup : IEquatable From 54d2f2c8cd2e837d7b0e046f4bf8df91317eb802 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Jul 2020 20:34:09 +0900 Subject: [PATCH 052/185] Delay loading of cover backgrounds in score panels --- .../Ranking/Contracted/ContractedPanelMiddleContent.cs | 5 ++++- osu.Game/Screens/Ranking/ScorePanel.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 8cd0e7025e..3ffb205d09 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -70,11 +70,14 @@ namespace osu.Game.Screens.Ranking.Contracted RelativeSizeAxes = Axes.Both, Colour = Color4Extensions.FromHex("444") }, - new UserCoverBackground + new DelayedLoadUnloadWrapper(() => new UserCoverBackground { RelativeSizeAxes = Axes.Both, User = score.User, Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.5f), Color4Extensions.FromHex("#444").Opacity(0)) + }, 300, 5000) + { + RelativeSizeAxes = Axes.Both }, new FillFlowContainer { diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 5da432d5b2..7ac98604f4 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -146,11 +146,14 @@ namespace osu.Game.Screens.Ranking Children = new[] { middleLayerBackground = new Box { RelativeSizeAxes = Axes.Both }, - new UserCoverBackground + new DelayedLoadUnloadWrapper(() => new UserCoverBackground { RelativeSizeAxes = Axes.Both, User = Score.User, Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.5f), Color4Extensions.FromHex("#444").Opacity(0)) + }, 300, 5000) + { + RelativeSizeAxes = Axes.Both }, } }, From 42e88c53d75a03f37fe8699ca2512c0477fb8c75 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Jul 2020 20:50:55 +0900 Subject: [PATCH 053/185] Embed behaviour into UserCoverBackground --- osu.Game/Overlays/Profile/ProfileHeader.cs | 7 ++++++- .../Contracted/ContractedPanelMiddleContent.cs | 5 +---- osu.Game/Screens/Ranking/ScorePanel.cs | 7 ++----- osu.Game/Users/UserCoverBackground.cs | 11 +++++++++++ osu.Game/Users/UserPanel.cs | 10 ++-------- 5 files changed, 22 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 2e5f1071f2..55474c9d3e 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Profile Masking = true, Children = new Drawable[] { - coverContainer = new UserCoverBackground + coverContainer = new ProfileCoverBackground { RelativeSizeAxes = Axes.Both, }, @@ -100,5 +100,10 @@ namespace osu.Game.Overlays.Profile IconTexture = "Icons/profile"; } } + + private class ProfileCoverBackground : UserCoverBackground + { + protected override double LoadDelay => 0; + } } } diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 3ffb205d09..8cd0e7025e 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -70,14 +70,11 @@ namespace osu.Game.Screens.Ranking.Contracted RelativeSizeAxes = Axes.Both, Colour = Color4Extensions.FromHex("444") }, - new DelayedLoadUnloadWrapper(() => new UserCoverBackground + new UserCoverBackground { RelativeSizeAxes = Axes.Both, User = score.User, Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.5f), Color4Extensions.FromHex("#444").Opacity(0)) - }, 300, 5000) - { - RelativeSizeAxes = Axes.Both }, new FillFlowContainer { diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 7ac98604f4..24d193e9a7 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -146,15 +146,12 @@ namespace osu.Game.Screens.Ranking Children = new[] { middleLayerBackground = new Box { RelativeSizeAxes = Axes.Both }, - new DelayedLoadUnloadWrapper(() => new UserCoverBackground + new UserCoverBackground { RelativeSizeAxes = Axes.Both, User = Score.User, Colour = ColourInfo.GradientVertical(Color4.White.Opacity(0.5f), Color4Extensions.FromHex("#444").Opacity(0)) - }, 300, 5000) - { - RelativeSizeAxes = Axes.Both - }, + } } }, middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both } diff --git a/osu.Game/Users/UserCoverBackground.cs b/osu.Game/Users/UserCoverBackground.cs index 748d9bd939..34bbf6892e 100644 --- a/osu.Game/Users/UserCoverBackground.cs +++ b/osu.Game/Users/UserCoverBackground.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; @@ -23,6 +24,16 @@ namespace osu.Game.Users protected override Drawable CreateDrawable(User user) => new Cover(user); + protected override double LoadDelay => 300; + + /// + /// Delay before the background is unloaded while off-screen. + /// + protected virtual double UnloadDelay => 5000; + + protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) + => new DelayedLoadUnloadWrapper(createContentFunc, timeBeforeLoad, UnloadDelay); + [LongRunningLoad] private class Cover : CompositeDrawable { diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 94c0c31cfc..57a87a713d 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -4,7 +4,6 @@ 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; @@ -25,7 +24,7 @@ namespace osu.Game.Users protected Action ViewProfile { get; private set; } - protected DelayedLoadUnloadWrapper Background { get; private set; } + protected Drawable Background { get; private set; } protected UserPanel(User user) { @@ -56,17 +55,12 @@ namespace osu.Game.Users RelativeSizeAxes = Axes.Both, Colour = ColourProvider?.Background5 ?? Colours.Gray1 }, - Background = new DelayedLoadUnloadWrapper(() => new UserCoverBackground + Background = new UserCoverBackground { RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, User = User, - }, 300, 5000) - { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - RelativeSizeAxes = Axes.Both, }, CreateLayout() }); From 9f6446d83685fb4d6052930129ae7b68f7781f50 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Jul 2020 20:58:13 +0900 Subject: [PATCH 054/185] Add xmldocs + refactoring --- osu.Game/Screens/Ranking/ResultsScreen.cs | 17 +++++++++++------ osu.Game/Screens/Ranking/ScorePanelList.cs | 15 +++++++++++++-- 2 files changed, 24 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index c5512822b2..254ab76f5b 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -50,6 +50,9 @@ namespace osu.Game.Screens.Ranking private ScorePanelList scorePanelList; private Container detachedPanelContainer; + private bool fetchedInitialScores; + private APIRequest nextPageRequest; + protected ResultsScreen(ScoreInfo score, bool allowRetry = true) { Score = score; @@ -172,13 +175,11 @@ namespace osu.Game.Screens.Ranking statisticsPanel.State.BindValueChanged(onStatisticsStateChanged, true); } - private APIRequest nextPageRequest; - protected override void Update() { base.Update(); - if (hasAnyScores && nextPageRequest == null) + if (fetchedInitialScores && nextPageRequest == null) { if (scorePanelList.IsScrolledToStart) nextPageRequest = FetchNextPage(-1, fetchScoresCallback); @@ -202,16 +203,20 @@ namespace osu.Game.Screens.Ranking /// An responsible for the fetch operation. This will be queued and performed automatically. protected virtual APIRequest FetchScores(Action> scoresCallback) => null; + /// + /// Performs a fetch of the next page of scores. This is invoked every frame until a non-null is returned. + /// + /// The fetch direction. -1 to fetch scores greater than the current start of the list, and 1 to fetch scores lower than the current end of the list. + /// A callback which should be called when fetching is completed. Scheduling is not required. + /// An responsible for the fetch operation. This will be queued and performed automatically. protected virtual APIRequest FetchNextPage(int direction, Action> scoresCallback) => null; - private bool hasAnyScores; - private void fetchScoresCallback(IEnumerable scores) => Schedule(() => { foreach (var s in scores) addScore(s); - hasAnyScores = true; + fetchedInitialScores = true; }); public override void OnEntering(IScreen last) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index aba8314732..b2e1e91831 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -26,9 +26,20 @@ namespace osu.Game.Screens.Ranking /// private const float expanded_panel_spacing = 15; - public bool IsScrolledToStart => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.Current <= 100; + /// + /// Minimum distance from either end point of the list that the list can be considered scrolled to the end point. + /// + private const float scroll_endpoint_distance = 100; - public bool IsScrolledToEnd => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.IsScrolledToEnd(100); + /// + /// Whether this can be scrolled and is currently scrolled to the start. + /// + public bool IsScrolledToStart => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.Current <= scroll_endpoint_distance; + + /// + /// Whether this can be scrolled and is currently scrolled to the end. + /// + public bool IsScrolledToEnd => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.IsScrolledToEnd(scroll_endpoint_distance); /// /// An action to be invoked if a is clicked while in an expanded state. From db91d1de50e9ab92f7df1ec45abbea93696766af Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Jul 2020 21:40:11 +0900 Subject: [PATCH 055/185] Use response params in next page request --- .../Multiplayer/IndexPlaylistScoresRequest.cs | 25 +++++----- .../Online/Multiplayer/IndexScoresParams.cs | 20 ++++++++ .../Online/Multiplayer/MultiplayerScores.cs | 6 +++ .../Multi/Ranking/TimeshiftResultsScreen.cs | 49 ++++++------------- 4 files changed, 53 insertions(+), 47 deletions(-) create mode 100644 osu.Game/Online/Multiplayer/IndexScoresParams.cs diff --git a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs index 7273c0eea6..67793df344 100644 --- a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs +++ b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.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.IO.Network; using osu.Game.Extensions; using osu.Game.Online.API; @@ -16,31 +17,31 @@ namespace osu.Game.Online.Multiplayer private readonly int roomId; private readonly int playlistItemId; private readonly Cursor cursor; - private readonly MultiplayerScoresSort? sort; + private readonly IndexScoresParams indexParams; - public IndexPlaylistScoresRequest(int roomId, int playlistItemId, Cursor cursor = null, MultiplayerScoresSort? sort = null) + public IndexPlaylistScoresRequest(int roomId, int playlistItemId) { this.roomId = roomId; this.playlistItemId = playlistItemId; + } + + public IndexPlaylistScoresRequest(int roomId, int playlistItemId, [NotNull] Cursor cursor, [NotNull] IndexScoresParams indexParams) + : this(roomId, playlistItemId) + { this.cursor = cursor; - this.sort = sort; + this.indexParams = indexParams; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); - req.AddCursor(cursor); - - switch (sort) + if (cursor != null) { - case MultiplayerScoresSort.Ascending: - req.AddParameter("sort", "score_asc"); - break; + req.AddCursor(cursor); - case MultiplayerScoresSort.Descending: - req.AddParameter("sort", "score_desc"); - break; + foreach (var (key, value) in indexParams.Properties) + req.AddParameter(key, value.ToString()); } return req; diff --git a/osu.Game/Online/Multiplayer/IndexScoresParams.cs b/osu.Game/Online/Multiplayer/IndexScoresParams.cs new file mode 100644 index 0000000000..8160dfefaf --- /dev/null +++ b/osu.Game/Online/Multiplayer/IndexScoresParams.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.Collections.Generic; +using JetBrains.Annotations; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// A collection of parameters which should be passed to the index endpoint to fetch the next page. + /// + public class IndexScoresParams + { + [UsedImplicitly] + [JsonExtensionData] + public IDictionary Properties; + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerScores.cs b/osu.Game/Online/Multiplayer/MultiplayerScores.cs index 6f74fc8984..2d0f98e032 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScores.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerScores.cs @@ -29,5 +29,11 @@ namespace osu.Game.Online.Multiplayer /// [JsonProperty("user_score")] public MultiplayerScore UserScore { get; set; } + + /// + /// The parameters to be used to fetch the next page. + /// + [JsonProperty("params")] + public IndexScoresParams Params { get; set; } } } diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index 75a61b92ee..648bee385c 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; -using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Scoring; using osu.Game.Screens.Ranking; @@ -23,8 +22,8 @@ namespace osu.Game.Screens.Multi.Ranking private readonly PlaylistItem playlistItem; private LoadingSpinner loadingLayer; - private Cursor higherScoresCursor; - private Cursor lowerScoresCursor; + private MultiplayerScores higherScores; + private MultiplayerScores lowerScores; [Resolved] private IAPIProvider api { get; set; } @@ -52,8 +51,8 @@ namespace osu.Game.Screens.Multi.Ranking protected override APIRequest FetchScores(Action> scoresCallback) { // This performs two requests: - // 1. A request to show the user's score. - // 2. If (1) fails, a request to index the room. + // 1. A request to show the user's score (and scores around). + // 2. If that fails, a request to index the room starting from the highest score. var userScoreReq = new ShowPlaylistUserScoreRequest(roomId, playlistItem.ID, api.LocalUser.Value.Id); @@ -64,13 +63,13 @@ namespace osu.Game.Screens.Multi.Ranking if (userScore.ScoresAround?.Higher != null) { allScores.AddRange(userScore.ScoresAround.Higher.Scores); - higherScoresCursor = userScore.ScoresAround.Higher.Cursor; + higherScores = userScore.ScoresAround.Higher; } if (userScore.ScoresAround?.Lower != null) { allScores.AddRange(userScore.ScoresAround.Lower.Scores); - lowerScoresCursor = userScore.ScoresAround.Lower.Cursor; + lowerScores = userScore.ScoresAround.Lower; } performSuccessCallback(scoresCallback, allScores); @@ -84,7 +83,7 @@ namespace osu.Game.Screens.Multi.Ranking indexReq.Success += r => { performSuccessCallback(scoresCallback, r.Scores); - lowerScoresCursor = r.Cursor; + lowerScores = r; }; indexReq.Failure += __ => loadingLayer.Hide(); @@ -99,39 +98,19 @@ namespace osu.Game.Screens.Multi.Ranking { Debug.Assert(direction == 1 || direction == -1); - Cursor cursor; - MultiplayerScoresSort sort; + MultiplayerScores pivot = direction == -1 ? higherScores : lowerScores; - switch (direction) - { - case -1: - cursor = higherScoresCursor; - sort = MultiplayerScoresSort.Ascending; - break; - - default: - cursor = lowerScoresCursor; - sort = MultiplayerScoresSort.Descending; - break; - } - - if (cursor == null) + if (pivot?.Cursor == null) return null; - var indexReq = new IndexPlaylistScoresRequest(roomId, playlistItem.ID, cursor, sort); + var indexReq = new IndexPlaylistScoresRequest(roomId, playlistItem.ID, pivot.Cursor, pivot.Params); indexReq.Success += r => { - switch (direction) - { - case -1: - higherScoresCursor = r.Cursor; - break; - - default: - lowerScoresCursor = r.Cursor; - break; - } + if (direction == -1) + higherScores = r; + else + lowerScores = r; performSuccessCallback(scoresCallback, r.Scores); }; From ccc377ae6af607215edc8756a1cd24881be427d1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Jul 2020 21:42:04 +0900 Subject: [PATCH 056/185] Remove unused enum --- .../Online/Multiplayer/MultiplayerScoresSort.cs | 14 -------------- 1 file changed, 14 deletions(-) delete mode 100644 osu.Game/Online/Multiplayer/MultiplayerScoresSort.cs diff --git a/osu.Game/Online/Multiplayer/MultiplayerScoresSort.cs b/osu.Game/Online/Multiplayer/MultiplayerScoresSort.cs deleted file mode 100644 index decb1c4dfe..0000000000 --- a/osu.Game/Online/Multiplayer/MultiplayerScoresSort.cs +++ /dev/null @@ -1,14 +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.Online.Multiplayer -{ - /// - /// Sorting option for indexing multiplayer scores. - /// - public enum MultiplayerScoresSort - { - Ascending, - Descending - } -} From a57b6bdc1817bec9274c92a3d879878707c355c1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Jul 2020 11:29:38 +0900 Subject: [PATCH 057/185] Remove cancellation of linked tokens --- osu.Game/Beatmaps/BeatmapDifficultyManager.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs index b3afd1d4fd..58b96b08b0 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs @@ -137,10 +137,7 @@ namespace osu.Game.Beatmaps trackedUpdateCancellationSource = null; foreach (var c in linkedCancellationSources) - { - c.Cancel(); c.Dispose(); - } linkedCancellationSources.Clear(); } From 46483622149904c9240f6f6e53ba4ef283edb07d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Jul 2020 11:30:25 +0900 Subject: [PATCH 058/185] Safeguard against potential finalise-before-initialised --- osu.Game/Beatmaps/BeatmapDifficultyManager.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs index 58b96b08b0..b80b4e45ed 100644 --- a/osu.Game/Beatmaps/BeatmapDifficultyManager.cs +++ b/osu.Game/Beatmaps/BeatmapDifficultyManager.cs @@ -136,10 +136,13 @@ namespace osu.Game.Beatmaps trackedUpdateCancellationSource?.Cancel(); trackedUpdateCancellationSource = null; - foreach (var c in linkedCancellationSources) - c.Dispose(); + if (linkedCancellationSources != null) + { + foreach (var c in linkedCancellationSources) + c.Dispose(); - linkedCancellationSources.Clear(); + linkedCancellationSources.Clear(); + } } /// @@ -239,7 +242,7 @@ namespace osu.Game.Beatmaps base.Dispose(isDisposing); cancelTrackedBindableUpdate(); - updateScheduler.Dispose(); + updateScheduler?.Dispose(); } private readonly struct DifficultyCacheLookup : IEquatable From 6c7e806eacecd71ad073d160ac95dbcadaad8199 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 12:39:18 +0900 Subject: [PATCH 059/185] Include executable hash when submitting multiplayer scores --- osu.Game/OsuGameBase.cs | 14 ++++++++++++++ osu.Game/Scoring/ScoreInfo.cs | 3 +++ osu.Game/Screens/Play/Player.cs | 1 + 3 files changed, 18 insertions(+) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index fe5c0704b7..964a7fdd35 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Development; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.IO.Stores; @@ -97,6 +98,11 @@ namespace osu.Game public virtual Version AssemblyVersion => Assembly.GetEntryAssembly()?.GetName().Version ?? new Version(); + /// + /// MD5 representation of the game executable. + /// + public string VersionHash { get; private set; } + public bool IsDeployedBuild => AssemblyVersion.Major > 0; public virtual string Version @@ -128,6 +134,14 @@ namespace osu.Game [BackgroundDependencyLoader] private void load() { + var assembly = Assembly.GetEntryAssembly(); + + if (assembly != null) + { + using (var str = File.OpenRead(assembly.Location)) + VersionHash = str.ComputeMD5Hash(); + } + Resources.AddStore(new DllResourceStore(OsuResources.ResourceAssembly)); dependencies.Cache(contextFactory = new DatabaseContextFactory(Storage)); diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 84c0d5b54e..2cc065b5ad 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -51,6 +51,9 @@ namespace osu.Game.Scoring [NotMapped] public bool Passed { get; set; } = true; + [JsonProperty("version_hash")] + public string VersionHash { get; set; } + [JsonIgnore] public virtual RulesetInfo Ruleset { get; set; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 541275cf55..5df6cf42cb 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -465,6 +465,7 @@ namespace osu.Game.Screens.Play { Beatmap = Beatmap.Value.BeatmapInfo, Ruleset = rulesetInfo, + VersionHash = Game.VersionHash, Mods = Mods.Value.ToArray(), }; From 9e6d562872effe4ab8c763dd0a6db0b7f0be1ffe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 13:18:40 +0900 Subject: [PATCH 060/185] Send in initial score request instead --- osu.Game/Online/Multiplayer/CreateRoomScoreRequest.cs | 5 ++++- osu.Game/Scoring/ScoreInfo.cs | 3 --- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 2 +- osu.Game/Screens/Play/Player.cs | 1 - 4 files changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Multiplayer/CreateRoomScoreRequest.cs b/osu.Game/Online/Multiplayer/CreateRoomScoreRequest.cs index f973f96b37..2d99b12519 100644 --- a/osu.Game/Online/Multiplayer/CreateRoomScoreRequest.cs +++ b/osu.Game/Online/Multiplayer/CreateRoomScoreRequest.cs @@ -11,17 +11,20 @@ namespace osu.Game.Online.Multiplayer { private readonly int roomId; private readonly int playlistItemId; + private readonly string versionHash; - public CreateRoomScoreRequest(int roomId, int playlistItemId) + public CreateRoomScoreRequest(int roomId, int playlistItemId, string versionHash) { this.roomId = roomId; this.playlistItemId = playlistItemId; + this.versionHash = versionHash; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); req.Method = HttpMethod.Post; + req.AddParameter("version_hash", versionHash); return req; } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 2cc065b5ad..84c0d5b54e 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -51,9 +51,6 @@ namespace osu.Game.Scoring [NotMapped] public bool Passed { get; set; } = true; - [JsonProperty("version_hash")] - public string VersionHash { get; set; } - [JsonIgnore] public virtual RulesetInfo Ruleset { get; set; } diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index c2381fe219..da082692d7 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Multi.Play if (!playlistItem.RequiredMods.All(m => Mods.Value.Any(m.Equals))) throw new InvalidOperationException("Current Mods do not match PlaylistItem's RequiredMods"); - var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItem.ID); + var req = new CreateRoomScoreRequest(roomId.Value ?? 0, playlistItem.ID, Game.VersionHash); req.Success += r => token = r.ID; req.Failure += e => { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 5df6cf42cb..541275cf55 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -465,7 +465,6 @@ namespace osu.Game.Screens.Play { Beatmap = Beatmap.Value.BeatmapInfo, Ruleset = rulesetInfo, - VersionHash = Game.VersionHash, Mods = Mods.Value.ToArray(), }; From c3c60334ec7b1806cd9cb7e99b39a0d9951c50a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 15:24:14 +0900 Subject: [PATCH 061/185] Add skinning support to spinner test scene --- .../TestSceneSpinner.cs | 40 +++++++------------ 1 file changed, 15 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 67afc45e32..8e3a22bfdc 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -4,37 +4,32 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] - public class TestSceneSpinner : OsuTestScene + public class TestSceneSpinner : OsuSkinnableTestScene { - private readonly Container content; - protected override Container Content => content; - private int depthIndex; public TestSceneSpinner() { - base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + // base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); - AddStep("Miss Big", () => testSingle(2)); - AddStep("Miss Medium", () => testSingle(5)); - AddStep("Miss Small", () => testSingle(7)); - AddStep("Hit Big", () => testSingle(2, true)); - AddStep("Hit Medium", () => testSingle(5, true)); - AddStep("Hit Small", () => testSingle(7, true)); + AddStep("Miss Big", () => SetContents(() => testSingle(2))); + AddStep("Miss Medium", () => SetContents(() => testSingle(5))); + AddStep("Miss Small", () => SetContents(() => testSingle(7))); + AddStep("Hit Big", () => SetContents(() => testSingle(2, true))); + AddStep("Hit Medium", () => SetContents(() => testSingle(5, true))); + AddStep("Hit Small", () => SetContents(() => testSingle(7, true))); } - private void testSingle(float circleSize, bool auto = false) + private Drawable testSingle(float circleSize, bool auto = false) { var spinner = new Spinner { StartTime = Time.Current + 2000, EndTime = Time.Current + 5000 }; @@ -49,12 +44,12 @@ namespace osu.Game.Rulesets.Osu.Tests foreach (var mod in SelectedMods.Value.OfType()) mod.ApplyToDrawableHitObjects(new[] { drawable }); - Add(drawable); + return drawable; } private class TestDrawableSpinner : DrawableSpinner { - private bool auto; + private readonly bool auto; public TestDrawableSpinner(Spinner s, bool auto) : base(s) @@ -62,16 +57,11 @@ namespace osu.Game.Rulesets.Osu.Tests this.auto = auto; } - protected override void CheckForResult(bool userTriggered, double timeOffset) + protected override void Update() { - if (auto && !userTriggered && Time.Current > Spinner.StartTime + Spinner.Duration / 2 && Progress < 1) - { - // force completion only once to not break human interaction - Disc.CumulativeRotation = Spinner.SpinsRequired * 360; - auto = false; - } - - base.CheckForResult(userTriggered, timeOffset); + base.Update(); + if (auto) + Disc.Rotate((float)(Clock.ElapsedFrameTime * 3)); } } } From 0f1f4b2b5c97656e80650b4560bfe4d1adb3b740 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Jul 2020 15:36:42 +0900 Subject: [PATCH 062/185] Add pooling for mania hit explosions --- .../Skinning/LegacyHitExplosion.cs | 7 ++- osu.Game.Rulesets.Mania/UI/Column.cs | 15 ++----- .../UI/DefaultHitExplosion.cs | 28 +++++------- osu.Game.Rulesets.Mania/UI/IHitExplosion.cs | 16 +++++++ .../UI/PoolableHitExplosion.cs | 44 +++++++++++++++++++ 5 files changed, 79 insertions(+), 31 deletions(-) create mode 100644 osu.Game.Rulesets.Mania/UI/IHitExplosion.cs create mode 100644 osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs index bc93bb2615..c2b39945c2 100644 --- a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs @@ -6,13 +6,14 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; +using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; using osuTK; namespace osu.Game.Rulesets.Mania.Skinning { - public class LegacyHitExplosion : LegacyManiaColumnElement + public class LegacyHitExplosion : LegacyManiaColumnElement, IHitExplosion { private readonly IBindable direction = new Bindable(); @@ -62,10 +63,8 @@ namespace osu.Game.Rulesets.Mania.Skinning explosion.Anchor = direction.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; } - protected override void LoadComplete() + public void Animate() { - base.LoadComplete(); - explosion?.FadeInFromZero(80) .Then().FadeOut(120); } diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 642353bd0b..7ddac759db 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -9,9 +9,9 @@ using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics.Pooling; using osu.Framework.Input.Bindings; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mania.Objects.Drawables; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; @@ -34,8 +34,8 @@ namespace osu.Game.Rulesets.Mania.UI public readonly Bindable Action = new Bindable(); public readonly ColumnHitObjectArea HitObjectArea; - internal readonly Container TopLevelContainer; + private readonly DrawablePool hitExplosionPool; public Container UnderlayElements => HitObjectArea.UnderlayElements; @@ -53,6 +53,7 @@ namespace osu.Game.Rulesets.Mania.UI InternalChildren = new[] { + hitExplosionPool = new DrawablePool(5), // For input purposes, the background is added at the highest depth, but is then proxied back below all other elements background.CreateProxy(), HitObjectArea = new ColumnHitObjectArea(Index, HitObjectContainer) { RelativeSizeAxes = Axes.Both }, @@ -108,15 +109,7 @@ namespace osu.Game.Rulesets.Mania.UI if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value) return; - var explosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, Index), _ => - new DefaultHitExplosion(judgedObject.AccentColour.Value, judgedObject is DrawableHoldNoteTick)) - { - RelativeSizeAxes = Axes.Both - }; - - HitObjectArea.Explosions.Add(explosion); - - explosion.Delay(200).Expire(true); + HitObjectArea.Explosions.Add(hitExplosionPool.Get()); } public bool OnPressed(ManiaAction action) diff --git a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs index 7a047ed121..bac77b134c 100644 --- a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs @@ -15,7 +15,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.UI { - public class DefaultHitExplosion : CompositeDrawable + public class DefaultHitExplosion : CompositeDrawable, IHitExplosion { public override bool RemoveWhenNotAlive => true; @@ -123,21 +123,6 @@ namespace osu.Game.Rulesets.Mania.UI direction.BindValueChanged(onDirectionChanged, true); } - protected override void LoadComplete() - { - const double duration = 200; - - base.LoadComplete(); - - 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); - } - private void onDirectionChanged(ValueChangedEvent direction) { if (direction.NewValue == ScrollingDirection.Up) @@ -151,5 +136,16 @@ namespace osu.Game.Rulesets.Mania.UI Y = -DefaultNotePiece.NOTE_HEIGHT / 2; } } + + public void Animate() + { + largeFaint + .ResizeTo(largeFaint.Size * new Vector2(5, 1), PoolableHitExplosion.DURATION, Easing.OutQuint) + .FadeOut(PoolableHitExplosion.DURATION * 2); + + mainGlow1.ScaleTo(1.4f, PoolableHitExplosion.DURATION, Easing.OutQuint); + + this.FadeOut(PoolableHitExplosion.DURATION, Easing.Out); + } } } diff --git a/osu.Game.Rulesets.Mania/UI/IHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/IHitExplosion.cs new file mode 100644 index 0000000000..da1742e48b --- /dev/null +++ b/osu.Game.Rulesets.Mania/UI/IHitExplosion.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.Mania.UI +{ + /// + /// Common interface for all hit explosion bodies. + /// + public interface IHitExplosion + { + /// + /// Begins animating this . + /// + void Animate(); + } +} diff --git a/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs new file mode 100644 index 0000000000..43808f99a8 --- /dev/null +++ b/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.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.Pooling; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Mania.UI +{ + public class PoolableHitExplosion : PoolableDrawable + { + public const double DURATION = 200; + + [Resolved] + private Column column { get; set; } + + private SkinnableDrawable skinnableExplosion; + + public PoolableHitExplosion() + { + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = skinnableExplosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, column.Index), + _ => new DefaultHitExplosion(column.AccentColour, false /*todo */)) + { + RelativeSizeAxes = Axes.Both + }; + } + + protected override void PrepareForUse() + { + base.PrepareForUse(); + + (skinnableExplosion?.Drawable as IHitExplosion)?.Animate(); + + this.Delay(DURATION).Then().Expire(); + } + } +} From 7f2e554ad47a6e2ac9f3d691d1d128838a49546f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Jul 2020 15:52:25 +0900 Subject: [PATCH 063/185] Fix animations not being reset --- .../Skinning/LegacyHitExplosion.cs | 2 ++ osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs | 15 +++++++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs index c2b39945c2..c4fcffbc22 100644 --- a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs @@ -65,6 +65,8 @@ namespace osu.Game.Rulesets.Mania.Skinning public void Animate() { + (explosion as IFramedAnimation)?.GotoFrame(0); + explosion?.FadeInFromZero(80) .Then().FadeOut(120); } diff --git a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs index bac77b134c..3007668117 100644 --- a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs @@ -17,6 +17,8 @@ namespace osu.Game.Rulesets.Mania.UI { public class DefaultHitExplosion : CompositeDrawable, IHitExplosion { + private const float default_large_faint_size = 0.8f; + public override bool RemoveWhenNotAlive => true; private readonly IBindable direction = new Bindable(); @@ -54,7 +56,7 @@ namespace osu.Game.Rulesets.Mania.UI RelativeSizeAxes = Axes.Both, Masking = true, // we want our size to be very small so the glow dominates it. - Size = new Vector2(0.8f), + Size = new Vector2(default_large_faint_size), Blending = BlendingParameters.Additive, EdgeEffect = new EdgeEffectParameters { @@ -140,12 +142,17 @@ namespace osu.Game.Rulesets.Mania.UI public void Animate() { largeFaint - .ResizeTo(largeFaint.Size * new Vector2(5, 1), PoolableHitExplosion.DURATION, Easing.OutQuint) + .ResizeTo(default_large_faint_size) + .Then() + .ResizeTo(default_large_faint_size * new Vector2(5, 1), PoolableHitExplosion.DURATION, Easing.OutQuint) .FadeOut(PoolableHitExplosion.DURATION * 2); - mainGlow1.ScaleTo(1.4f, PoolableHitExplosion.DURATION, Easing.OutQuint); + mainGlow1 + .ScaleTo(1) + .Then() + .ScaleTo(1.4f, PoolableHitExplosion.DURATION, Easing.OutQuint); - this.FadeOut(PoolableHitExplosion.DURATION, Easing.Out); + this.FadeOutFromOne(PoolableHitExplosion.DURATION, Easing.Out); } } } From 00821e7b653b58f55de16c9e9cfdb87b2846f85c Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Jul 2020 16:14:19 +0900 Subject: [PATCH 064/185] Re-implement support for small ticks --- .../Skinning/LegacyHitExplosion.cs | 3 +- osu.Game.Rulesets.Mania/UI/Column.cs | 2 +- .../UI/DefaultHitExplosion.cs | 43 +++++++++++-------- osu.Game.Rulesets.Mania/UI/IHitExplosion.cs | 5 ++- .../UI/PoolableHitExplosion.cs | 13 ++++-- 5 files changed, 41 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs index c4fcffbc22..12747924de 100644 --- a/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/Skinning/LegacyHitExplosion.cs @@ -6,6 +6,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Animations; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; @@ -63,7 +64,7 @@ namespace osu.Game.Rulesets.Mania.Skinning explosion.Anchor = direction.NewValue == ScrollingDirection.Up ? Anchor.TopCentre : Anchor.BottomCentre; } - public void Animate() + public void Animate(JudgementResult result) { (explosion as IFramedAnimation)?.GotoFrame(0); diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 7ddac759db..255ce4c064 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -109,7 +109,7 @@ namespace osu.Game.Rulesets.Mania.UI if (!result.IsHit || !judgedObject.DisplayResult || !DisplayJudgements.Value) return; - HitObjectArea.Explosions.Add(hitExplosionPool.Get()); + HitObjectArea.Explosions.Add(hitExplosionPool.Get(e => e.Apply(result))); } public bool OnPressed(ManiaAction action) diff --git a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs index 3007668117..225269cf48 100644 --- a/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/DefaultHitExplosion.cs @@ -8,6 +8,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Utils; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.UI.Scrolling; using osuTK; @@ -21,31 +23,30 @@ namespace osu.Game.Rulesets.Mania.UI public override bool RemoveWhenNotAlive => true; + [Resolved] + private Column column { get; set; } + private readonly IBindable direction = new Bindable(); - private readonly CircularContainer largeFaint; - private readonly CircularContainer mainGlow1; + private CircularContainer largeFaint; + private CircularContainer mainGlow1; - public DefaultHitExplosion(Color4 objectColour, bool isSmall = false) + public DefaultHitExplosion() { Origin = Anchor.Centre; RelativeSizeAxes = Axes.X; Height = DefaultNotePiece.NOTE_HEIGHT; + } - // scale roughly in-line with visual appearance of notes - Scale = new Vector2(1f, 0.6f); - - if (isSmall) - Scale *= 0.5f; - + [BackgroundDependencyLoader] + private void load(IScrollingInfo scrollingInfo) + { const float angle_variangle = 15; // should be less than 45 - const float roundness = 80; - const float initial_height = 10; - var colour = Interpolation.ValueAt(0.4f, objectColour, Color4.White, 0, 1); + var colour = Interpolation.ValueAt(0.4f, column.AccentColour, Color4.White, 0, 1); InternalChildren = new Drawable[] { @@ -61,7 +62,7 @@ namespace osu.Game.Rulesets.Mania.UI EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.1f, objectColour, Color4.White, 0, 1).Opacity(0.3f), + Colour = Interpolation.ValueAt(0.1f, column.AccentColour, Color4.White, 0, 1).Opacity(0.3f), Roundness = 160, Radius = 200, }, @@ -76,7 +77,7 @@ namespace osu.Game.Rulesets.Mania.UI EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Glow, - Colour = Interpolation.ValueAt(0.6f, objectColour, Color4.White, 0, 1), + Colour = Interpolation.ValueAt(0.6f, column.AccentColour, Color4.White, 0, 1), Roundness = 20, Radius = 50, }, @@ -116,11 +117,7 @@ namespace osu.Game.Rulesets.Mania.UI }, } }; - } - [BackgroundDependencyLoader] - private void load(IScrollingInfo scrollingInfo) - { direction.BindTo(scrollingInfo.Direction); direction.BindValueChanged(onDirectionChanged, true); } @@ -139,8 +136,16 @@ namespace osu.Game.Rulesets.Mania.UI } } - public void Animate() + public void Animate(JudgementResult result) { + // scale roughly in-line with visual appearance of notes + Vector2 scale = new Vector2(1, 0.6f); + + if (result.Judgement is HoldNoteTickJudgement) + scale *= 0.5f; + + this.ScaleTo(scale); + largeFaint .ResizeTo(default_large_faint_size) .Then() diff --git a/osu.Game.Rulesets.Mania/UI/IHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/IHitExplosion.cs index da1742e48b..3252dcc276 100644 --- a/osu.Game.Rulesets.Mania/UI/IHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/IHitExplosion.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.Judgements; + namespace osu.Game.Rulesets.Mania.UI { /// @@ -11,6 +13,7 @@ namespace osu.Game.Rulesets.Mania.UI /// /// Begins animating this . /// - void Animate(); + /// The type of that caused this explosion. + void Animate(JudgementResult result); } } diff --git a/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs b/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs index 43808f99a8..64b7d7d550 100644 --- a/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs +++ b/osu.Game.Rulesets.Mania/UI/PoolableHitExplosion.cs @@ -4,6 +4,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Judgements; using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania.UI @@ -12,6 +13,8 @@ namespace osu.Game.Rulesets.Mania.UI { public const double DURATION = 200; + public JudgementResult Result { get; private set; } + [Resolved] private Column column { get; set; } @@ -25,18 +28,22 @@ namespace osu.Game.Rulesets.Mania.UI [BackgroundDependencyLoader] private void load() { - InternalChild = skinnableExplosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, column.Index), - _ => new DefaultHitExplosion(column.AccentColour, false /*todo */)) + InternalChild = skinnableExplosion = new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, column.Index), _ => new DefaultHitExplosion()) { RelativeSizeAxes = Axes.Both }; } + public void Apply(JudgementResult result) + { + Result = result; + } + protected override void PrepareForUse() { base.PrepareForUse(); - (skinnableExplosion?.Drawable as IHitExplosion)?.Animate(); + (skinnableExplosion?.Drawable as IHitExplosion)?.Animate(Result); this.Delay(DURATION).Then().Expire(); } From d01d1ce3f1f6bd1b239eab98cf0ffb1a6abdb9bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 16:25:10 +0900 Subject: [PATCH 065/185] Add initial support for spinner disc skinning --- .../old-skin/spinner-approachcircle.png | Bin 0 -> 26350 bytes .../Resources/old-skin/spinner-background.png | Bin 0 -> 46103 bytes .../Resources/old-skin/spinner-circle.png | Bin 0 -> 166439 bytes .../Resources/old-skin/spinner-clear.png | Bin 0 -> 39074 bytes .../Resources/old-skin/spinner-metre.png | Bin 0 -> 14518 bytes .../Resources/old-skin/spinner-osu.png | Bin 0 -> 18585 bytes .../Resources/old-skin/spinner-spin.png | Bin 0 -> 21353 bytes .../Resources/old-skin/spinnerbonus.wav | Bin 0 -> 309536 bytes .../Resources/old-skin/spinnerspin.wav | Bin 0 -> 36868 bytes .../Objects/Drawables/DrawableSpinner.cs | 2 +- .../Objects/Drawables/Pieces/SpinnerDisc.cs | 30 ++++++++++++------ osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 1 + .../Skinning/OsuLegacySkinTransformer.cs | 9 ++++++ 13 files changed, 31 insertions(+), 11 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-approachcircle.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-background.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-circle.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-clear.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-metre.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-osu.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-spin.png create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinnerbonus.wav create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinnerspin.wav diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-approachcircle.png b/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-approachcircle.png new file mode 100644 index 0000000000000000000000000000000000000000..3811e5050f4cc0404a5713b1b875ab33ac32243a GIT binary patch literal 26350 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 S&tJL__^tB%W98pWj^KYrw90@0 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-background.png b/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-background.png new file mode 100644 index 0000000000000000000000000000000000000000..d84eab2f15eb844ef0c37f3a0fbda07f7f4b4d16 GIT binary patch literal 46103 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

Gk~*^-q;;x5EnoHUef!Db4# zt4PT0wW(>TM4^!1wr$%h_sq|ae^J4v0!;;(UXYo8=O^`m-TB+iXvb}B?4ST(efqYQtJ_t6k1Dm|x zCaqLAaKi$rVTA+=r-o6Yf1O}6ZNcW~)YR0E?w_9@`!5PM6=*8RTmzYJ|J0cI)FBHp zF@(_D4w+EIbM%dxb)?uuKW7~i_N%jmLgw-^p_S!)(GP^mp-=fnZY3EO{*whl+(!7s zZGOI$WkfYhM*;gT&MIvzaX+eur3hHbcI+V$-oIOU7Oj}~%deFA+z^VEqZy=7iaghf z+8Lzkg@wZTTP;M56v>s0+Us5xO8vs&A^@!+|CE3=uVHzcPAHukywlVv`xcqJx{0{1 zLWDk-7+MF|Oj2dDSR9$0-uA{OOln)#Si$uv>J)Hg1b??%CBJC_}IflwUFXjhRDj$t`i@;-_< zzwGkDdY8vxZtfL+j|$NupSi43*=ohN$c12-^^lA@83eR(SRaYPek&n#+4oF*AQK_E zP`ckmfaaxaA_puT*n}b>2%}>sZ_~CvB6h(LQ#P%?^?}$c(*1Ha3xy#IHeb1Setv?& zrm{X#ps65py&w~EE?H&yyYJeN37vlC=4{AZg`yuH6L!;#FDx$NmDvs;6wQlVZ?F{w z)OPk+7lo8404b|Xio7OD@s6kx7!|Zdp^6ADJ4#<{*&g9x%3v6%A_1t9E#?sgCK(GO zneaUpSidO9io&NToN6ds>GcChO<=$I?O(6&hUVmLLKq#EO}KvP4Q11?A=7la3hB_c zG8OCD*DH*UaVl9T<}KKK`L6l-$-h=Un+i0y3}k|Z5dh^4flN>=!H69^GNAzI0#ue? zX;zl=kl9MnkKYd|k@!Jv2Yj}pay?G)6y3f!!8I-=G6*)pie+TG2+4R}Mp5Wwju9fb z9Z~a9Mc_~r$YuQ%MJ@=ULRHw`GOr0UYxjDcCZS?t!TAXaw=BW|>ZL4PBNWZ3J?!`p z&TDn$Z4#$Qh;(=XnsU{itSmVr!AGQq-# zR+rm_KkEaTmoC}%MQ^@YM`zC%w9^l(EMJ07KPz_8534MfDf+n)2JP*zAPQ1D<5WS{ z0|HxJrymTF7yvj5%!&Y96d0BXDT*p_QMNrQFv}>AvhQ6cM2%vAnkZ^vtJ|#!f*L~6 ztc65?zZKP)_<**k+9|61`U0m%kr2z*B&@3t1jIh3-3P+u&`$ES)3cnllU!bM+DW=P{WL=5#9yIY z4@IyYUYBt|V%#8%V_II^>y4@sia=|tYG2f4v{HZ}vW_U~q^J@p3P5q}gSg-cOCD>Y z+b?TxskJ9O-e2Uzc0aB4;&&yz4lYmJi@!14HuWz9dz@<|p#4M1ot0J8knCmD+ z8tp5!tVM-gZHCR1qihaOZr}dmopW>B{)UH5Q#M+sh62rv51DVh2xjc-=975}JTi~k zka@;YGOu2>?TeO|2wDOAB7n@QqhuP_LuORi+UUyWdToygc!>I8#tEKLQwieq+-w)b zigq%Jx^1$GqwF$e(<@qq%~rrsBrj7W2T(>;i(&{L#aLdpsy$3CjC!$yk$f>07Tm{S z`&sCM$D@-F?UPP$BX1KsunB4==Das?I(Dy3Z`e#HS64^2&CLApj`{h?zj0e1si8n~ z(?e!HpGS}7Pdr!N{^^I@9e>&enQy&SN5_vD7G&0`N9KhK2F=G@VD?1?yOpF{$t0Vo zvOG#ic%wrxj`6+6k(Vk1crwK_QLDt`v}E$CG8b)O8TB&_^E3=n_y~$-!_-RnJ4#oZ zugeH@mfDSCWF_3IP?EVqiN3GOS=igi)&kK*4cYZDdr+4>bDX=E*U0%X#TLN5Cw zdcW?3)SbLdoW!(9sI9P>v|)24zis>W7Y@$OPJIa=li43B*i@josRkU}?%w5(=8u23 zy!|&G9bGTTJaM9qPM&BgnYMiqL5m9}Z70d>ivToP(N835hKTPoSixB^5&C!XQfCrf42!6w~!EHB#jDUMBMdMY|x0a#0}KAKvpZ2Aj+p zZw+W-8#ei3A>I1dzYfY~Cb_gUvVG^yAMBrud!nHx` ziK52Igv|YHk=(bMfJ!J6win5@)KD}vSLAb$0ZilznW(8~^7cp0E#z&I25dU;X}u|Z zpslc}>$cD4>gv$s&YjQgy>eySR~2k3(A+eTnaO0DkU4tvi?xXl|Ki}po@9UjKm#)2 zU~da#9ywx%%fW~pD$N^^xnQ@Gl=a9YA%NB@FrjsL&5Z)u^@dK_WrP7yE8E+uv3ro} z<>gfwZ|&MVphNX7tOi=d8* zUclgh3rj4q4>Tm-9ViOm`@Y`UcxLQQU&AWWdFV8_$+Q=;KCqcyUhW;+z5DsSmo80z zjlrg}JyM{#At4hET!75(?rt+|*pnaWn>v&o7#O6GnM$SF2br(G?hlzO z)FZPAnYEg~M`k+=&1G`iHY22p!cN?ZWxJtQW;<}eGU6@{l2Jc{J|7|pp)&rOQ2<<~ zB3-n*kxZ_ujN&MwfXRHmQSx9#+e8MbZeidl1Ew-n<1(tF7D1sD1>Gp`Nv4Za!)p_B z)<+C39q4SpCEqWd!RBCFVN=r*)=E0ED=VGDd-uMu`{KpvzgNnp0?iExnQ+hoWOj9R zp~1mH^z_)VFQEK=AM2WauxD^^h(cyMoo)|g!WZGlk(%|18|WPeGA~~?(VWw_eA$A` zatSPq2&w{Pavqr`kyFcLPW-JbXT70nqbZwlEDy)H91`>Uikh1#YZDXpo@6VVqrzbn zg;zAh?NzbfN=0~>Rg}!CAudR@2_boTp^etcEAd_|b3G>KY zZ{Wf=`2iAV2cvej&}>Jsk!&ZSR(p(#LNSQ|fg)janSF^vU@i<4MZiiFj6B%b-jsdZB_PdJcy?yM6o zXOOvKtC-~*tYn5k-Fg6+%-h9nT8CFLRTyN%`F?Q>`zEYfp9H*L6fj@V@jwC~(r`=gG zMFh4t?7KibmZu3`goYXDdu=ZcxTxT_fpDLb`}jgW7YbhUiM(I0&rlQm9wLw9-s^K& z1M~1Zq&c@WLSfV7UWKWUX?pJ0MroeDN1 z?qj8BM!OOMC6jM0`EdT7pO`bBxGg_2N~_Mn;s`*rP1WViaJd^YX}ElYLuNY_Ov2ufI>Uh7X}UO*l%&Qs>BhVi>umO_A!LSi!;K)=Rt)(e)~eEgN&%Mg+ktotZ}&P z_8=r4a+Wy{iS7#-(|8e^B_7BKlIvft34Y$P#{)vGd6fq4OYT7Di-fR4t&Llwsc_P+ zLRzfKCi{JER-)_HLrJ|{?#WM#zjV+1{P-97d`$(K(G*Punh_36=9LLbrftZ6;`_Ck zU%fv+YWFV(0A*g8u@9&&=W{YQ6J$nh9T)_a?drRZH_-CJRIGMar{i^8Zj{!8GLY#v zFNwH)BHNAwE{MW_ln8EP)C)IR`ytWZ0=4OD*IQ%!) zqm$2#?w=mjPje%0Z>zu=6(VITq>;eIORF7@!W2i5_Y$XhzRb3S!H>wk4{J3k`&bb0 zg>AokIV50tBH*kEfFMEMxt(F)8n%8>8!99LuR+@@3jf0SoE&U2b(5GJbn;WYrtSpf!t9j;Jc?2Zcyrn?znO3ZV$enU;&FkYO;31VA1kd1Wz58%@!UOn&Rtka*W4zKamz z3WTon*@VaAZC81{p*w}m)aq(?e)sP0@4j$h`Wvh`2qrZYXvRL=Hb&r;Nk$(2SnsyG zay(?V#VZqfmmfb~-<08Uk(0RS55A20wG}`!%KHf_8o_N?Pt=QnRyzoZytb9)iK=)} zj@zi}uZ%CHNIs&>J`}Mal2P2kAS+78C6P9YGUtS_s&cRS!q%@LF@;bB&SkqiAtY{R zq#=n)bAjIi`Zwkb-os#%PS!jLiwxTbn^28TNTy?THQl>!?{hoPo}2kLgH07iSD+c| zU^YeoleUe~i9gnc4tydv{n*gpP`*)N-WJFN74x{$yPPSRvyK@%SQu@#!k>1&HcEGv zt?coM*70+Z_0uGRpI2OkJu*sJl#WS65bXUf25^Ln2U%d3X&V^^IilY$nJ{oGxuT-S zvaE@^NI?)1R0N|K%OfOKup_g7!!Al>3WD74HxQ(+D_!Sc6QQQ%krPTM%;Y^~kzsFJ zVAK9h*Kxhx(N!pr%&oV)u;bLJnSbD6Q-NmqQ1(Uw2OYC9Vm-{0z13gVw>>@HKRDdp z){<|%RYxaJ)_u*`y?in^qLL|VF+?K1mPDARodrd$?5B+gxo%4oQIPcdJVmUL`~WQs zvSe&>f|ljBt|1}Y>{Z3@XG_FvhA;}Ynn6B~2W`KIIxT7w6jTi_Ykw5<*@xLwd2Mgd zXD#wxk;Hov{f>rx543<|8w-9%yw+j5R7Ut)M{BLD{Opg2&pzj?;KMAM2wH2M<=17% zy??L6L({G-r?)kN!2AgQH@NqMG{^eL`oXPhb$cP!l$g@v{M`pd69lBLu5Jr7N0PPL zJ^M?g<&~i!+xCcoCd=DYLn)dqLnc(1QyZh9w_Y)KeBwYpKSIG1ayHvyV|412fsP$B z(A#et=Ce` zKV)AcQ?%swxJ;3dti_I~8Yy#Z1nn!?O7}3cchM?#kq$jU&-sPuvEaN2h39O_bQ1FX zT%F2tdJb?Yn`^G%obWk?Qg}@kBdxnFrtWsWrmj~iU4zrp&)t6Y>eQE12wjEFV}ne- z_~)_w@o!gl{MuoFOt3Kmi=(#K7(u`Cqetr&WY#HUUcA`M$y{D;4Kig;m~rRrZO9MW zXP>nC-boDG5y!mk`aI+Q?Bi%*85KfBx2@G~N3Hfn4}&w2<60K@MPN&`HDwrdiiYfi zwjl`o5E2gohk=~vev#RB5wP?sut7*Z_to!s6RC!m*^i$$fGP>JViUPN2{wzVkUYSA5(ggOgsAB53--2M1Z&yiS5;YCosiMglyu+k*TXS~A z3Y*QG&4jh4WNKx#cf_tlpZYp)=&nFhfu`{As4)LL$v^PX-pM;V2L^}QS78oCK1Yu5 z;c^3Ay-FI@<;7w+WX55IvR$~Bd}7?8CT6Se;#NZjBYA@SlH(F?bz_D7;HEgCx3Jb)0PXf{Y;;OF;wc)eZ| z-2bBU1RWD$z0uvu=9&&d>{v3_hTfq#2xnr0O};n?!sw=%&J+rI*Z%!K*m35}^mn#u z7+ry83kMZzF9`4RbvZz$ zDFvDBf{ZP50BrRyKpS7%swZTt=>$>QShfl>K^K5B&wCa7$W)0FiR~pVf|w{c%H(v4 zC~u-_o5+QPtT$&ABXkjX)+E7C7TQI-{%A-n=TyZ0DD3-m-8!5Dk}!#pfhGe{UeTms zbxcc$v;pf!I@t!;v~7=SwQP5>SW6r{@cg!S-r4c&R;)x6n-sR1lOdB!_g(hUyYS%+1@j1B>iNB*NcAFKZ{3==F zUdLwJzodot8FryV1RwXaJ0b!gCt4*PRK1KsjtjpEc(2!a0i0j8chGjb;m`E=;SQ@z zCdfa8Zu%&WVHNyd>l1MX@Oq7GDnXdbLD2SuJx2nLF|Yl=S6hee57A?u&8MO3tGS8m z`LO+o1mBa_=is$ozRI1r-n-Xv6Lhg7vahhSLQ*F*gidPqGc0-zmFTSc93>2d0c|YE zOx>Q03v;foL?27mY7ZPJl@}Hu^2tCG6iro$t`yC{gNSc0`JUlp&s4X6=Arz^D2A$Y zDE^7P!rVF_Y5($*Ck+S2=bo{Q*1@@B!fN!g2YtiIQ&kvpwE!yY&!XxYx<-UN6B=B z)!{!qug7{ibkt3ZHEZFCM6uYD-?8Jn`z~GD_LZvjJH3@c=qhwBOWaOgCRCV%lKI5Q zvHw(?{O~UfkI(3ReRdCXfXvv7f8Kbb*7V9eZ=fqz05S<$TCyRtQfWeFtxhOlnr%9O zqZUMLHGsrzT5g=`BfiP@zlgbc+=W6>KW8lX9m0a!9^|hW*F~eB*QaRV5)xF=7d|E5ihGO~QcK1XF*n~>-<>j9IuAR^CIe%gD>j0Zur4n6% zro?b}{&{Nb#Gm5)eIM(cysN8!U~o-^d8}$Cgv*Z~H)z+NbLUKS)q>193Yn(e^{0a9 zWYjsxG+XNljRk+SmBM%9hK%QF)XykxkIiHr=7x|t5;o+Sw>XNw8Q01$n;VaL_=u= ziRa0wu`$%iux=-dl6r2|*l0WFTeSW!~$m7S?-K7Pi z;0{AUd6OpcFaGg62gu|QyL0e@6wV;aub^&0NN}5i@?V4YJBsi+*^4HAS*$aUNVlV) z<2fuWA9iu#mA4yotm2Sc#2s^5!1027)~kg)Cwv|%L|pL$?tJ zCvDI~8S66{izSTCKoi&?m+M6R{UdsOi{|!^-#*NB{;?(U7$FmEi=e{%t+xzx)+zpJ_Af`MT(TguN@)HjUtu1HA{C{Y zR4JP6h07==&~fBV;tlpWM^R;?xSfA;FGH zeC0Xj!{_ezft|3sb=woG$0h?#OxLgJrBdhM%=GteyL@@_Z?}S(y9%8PUXefrS+n!c zu`iWpKJ(s@k+DYSpV*6ku3h83GEH=WgUm*2N$cvY?y@mz)#$pFAO9QGQD!SCmolHl zdV*opu=tJgZj~`JmkpI8J6X3>i?uWmacVQa7aMNI-oH5A<0%+FL^xxV3x5zLR+u4(|J;H?O$;G9isl9ui*?#`~wy*QBsX%iJ4kmkpFDB$=K0S8y&&-kc zd}Lr^U#4xHf565Fy8eJyW;0|?(9&YlkR8J1RLL}K$V4Q*uy)iR;f5wA|k)ocEVbCcGY*Bl-`yB&8ML+Fo`6<#8QKk?l3?gN^2#JDI z5CG#4Kox;8zt39}SY{AdM=5X;QADF&NcgF_Oxa{gCuS`xUByGR5`9GXuCkJcOdd8d zrfn)yE34_=efz!-X71m0n?DUhPgG0jfo5xP*)9NEug_&Oh1Odw2#Nxr zQD9T#G0|{*oZolR^&%9lYm`WMngo0XqD4bU;5A6#yeZnZJIXO8+K$s5rVgtb*N}Gs zPtftEA-RhBu>IdEwEA^W^4<4b_aOJaZOrTRwbb&l76&z9Q^U1dx}#XE>IV=0VCwC+ zw|}20n_Cq$w_GvMTGLfq%}jRsDGRD)1Bs%OK-x*?sP{J`(XWjk?Y_K6jZu@g%~!MSFV_6kJ3RX4xZJH01ZZ zU(t_SNkk#rBXSIgn%TRbGX-GKufWAGRNu?%ul)*}yo#T2$T5y%I>!*y><&aA&CEd5&>cfjkVC1absy;_HF3u& z0~u-S`Krao2jMCztw&Z1kN9M6(wpZravnINHx#^V&73REGCU3|d2N<+dF$(&T5xKt+ zRkL<0mbgvxwz>^?9gdkOug5X%zn~vCnOr>?^^zbtn;RLW6GfaqiuxG_1TtpfLEVeO zLZ>8T`9b?bRDDE9%!((9-H^<&Ba&XBg(!Z!N*T1zyq2kj95WgUZZj!Tz!Wv@!B-*W zeuOmM0Ew(E3Suv$GGxx0x?;v;ufcMLM zdhCrqHS&i)Ixw+6%knZ~H)X$c$)u1;i+|1<^zZpOLVKH2$OIdsDr$tx{lew4-j}WZ z_($>SYbPWI*=_`ZpRB@!{(tt~{7JIwyb}BF%dCB`ZZv>Kqp>!z0|WtZ1;GU*KvFU( zIqV3>{0E9~_&0~co|&*mq83Xd&q%U0qir~zam3gW6Jv)LMMxa&9CIXUiIK+Ah#~PA*Xc6McDzI^L_=brC7=fL<%-ffQ8aqOj) zQh3Jsy(RZq+YI#7*Hd${0p!#bV3RDvNZfXH!96gZ$JOWOM+=g~ViH{bm5$&)L4CG_`O9{T%&(F}9Uy_M0YPW^}9>fimJJvlbNOa~sl z_{S^E-+tR3v@yEEdu1L48NW)}mn9VjK zBCWth!-i@n8QNqY2^^#{pW66Fj;S;W3?e|fr4oSN7c=F&0ML0LSIo9!X+9OmdFEPX z6%dBBN@Qd?rdFeL=hA})B&KIOgYr3y0BChfF*EnTU$X&|1AomEcir``AAbGy+y9l1 zsB~rY_Y0%>z5tqGU#3g`dG40K{MzpAU-^NXPT$#CU0oXwo9V|IEA^- zFn8jgeqRSNOM$4=T}F}#ALA_2QFQX`FFlk2PN~N_{V`vuP_g7jI;yg zvDvYS1HW{`Xsbkp%ijsz3{>v|N2JAL!b0$!hXrzvze|h zb7p1p3)StPy>)dY{&9aR%4ELzX1$kT{@QE(5tC_QW24?PWFIn_aKOe08?19Oq;r5< zZdNswx6S2nOWC+uOCRk|srTEA_0`%^Y4$hPQ!pN*?*PDi`YHzaStYv}eqRlE+6>q07=Di0?-h)%?_2|XwLP8Gb-^8Ti1tGTvL%z0 zZ%>WAm~|Zn$~5Ut()U|PvxkbmQ?pIs?YjzpQ2jIL)pU92m9sqR)=;}F z^ce&F+5SKjWEQGHRAnJXbLiQ<2&=0nyH~G1ao^6)TW>qk%k^vC$Y$<9%u#N3%=-fYIfjI{xDIYORX(N7LjWLqo~n?zVBqqfgICrsk@ zww;{V#J^d^zOo6|k0hA0#CKBbVHNMA{tlU+1;u$}6%TIzXsXFB0--2oGy>PX8zS2B5_vE^{H^aNl z^BWD|{C*$$&Aq$-*S&)~tsU5OZFxg|$f{XgJu?UJsgFMU?4SP1>gwvC(psGCI)*t`OCUt3!rkCoAW^3Q&}q!a(RzRc}y*o&8ReVNwk#XlOL zRI;RO3N9zN9Hs%dH(I}BYNARis4jI58RhXUZM{@NxRzrRMTvqWQ}_~PacY^G6128- zpCY2+_I1f~kpdJPyOwkPaNCT%o@BKHxDX3B$FHCR)KznCa$nRQ_P0s(Zb=IW( z#M=dl`}@%T2#~Mq*Bm~3umHY_K74k6aN?j-#r-v1zh=K*OF?PXUA#C<{&78-dl}{#3UjWM znwt(&sytn?%$@puITl2@Z3S&DgEZfpbKGo!eV$^!l(PShq7o#TfMe^VtpacfE;e!b z+;yxiky7ixwP0}REEJg2Fxpb-99usr$w#s;;%uFaa^Gvxg8+2)Q`q`e3FlNLoM+TF z+u*xG`ypwfLRlG^7S{)74FwECO0@!kux{E31?m$!+F7=_2M^@n&3)MkfouaRP1k~* zs=>}Qug^88Z=rc@$v}0swGth%JTh0VJaXriE3ba=#EJC-sX-2K-iRgi8?tnMxMcq4 z|L4`)|Kms3PMkEM_@@}qbSCW1kln>gzV=!@ikGx|@sc6P%q^FvfX{KUY)8c=NRl#FQ`)wV5kE{$X20Y|WWs(ecUcMEd)TvKM`)`KgRTjBqja<;Tx z+7fonY-gRYtU6;I;_u;(54F9TiKnJjL{rkXit~qBFX|lC^n`2Hohyasv`ARRK1jXh z(aJ_}0@>Z>Kdy}4+?#E4kG31li_JaTzEwm0T7&jpYM#H{JfAN@Glt~X;^LB7Tf23} zn5Uom>}S69t7~gR=Nlh=eIx$daHaFOWd8i=zx;>&)6f3UiQCT2xbz=q*zW#b3}m|A z%YXmxd+_aVJD^z)faW`+L~TS0KCGR^UsVE zgShsa3iRz6sh*`&3XxH~mpU({<)CVNv;XPg@1xWppBCi5+%cUUGxXOi0|3Jkdfo3# zUEG*iz5l-7zVpSG&i=;rm>TrHES-n524~FffM&IP*K;$sJhtRY=03-~=+C}a=CEYG zFa8-O|CC}gj~kn=RYqSc#!8bYVukk`hCNDQGjq3N+-Xo$xzH9~CdEkPc$!+^)xd2* zn^b<4%8XnYT2kL~$v#{umD(4Q8eX$JP`-ydhcK17RQ}hhjIsB|NjBm4bJw`$yva$w ziodf`?0b%dOjT&-0({AtfVv)HH9R$rQpdDP*w>PS1GeVL)c-1TK%y_zTfxn6&c>bgj^uF(vl z8ii=?HjgV=2{OK0vRo<+i0v58r&4{XIpt5OG{`Lj`&>>LSgY`@6PLxUtF&LJYl$lF zYqlxeKS1m&>b=rF(q7|ITSDr&B`vcCx}=_~($|)sOZAac+ooAZNv&_0v5o5cM91EG zs74?3YmQ3ju-!Z^HIEAo$b7qbI3@b`nn!bwx33h5@$vndqY^r-uAZ9ibUya@r$7B? z|1>P2-)JiI8>V!QmCWl~Kl_yvw|!v7smzNDfSh%aOaF1bnJ>S5(3IWvWqMO~ZSv3L zhX$0Tf!a8ehf==ROb&7OzA)ElxmrViZ9a91se7tL? zn;%_TSy>y<)J1d5T{-+a-|6j%f1D}%d+!}IWe*kRH?R@wSbs{C;Z4E}J9nn9RkW92 z;z|NLO*yor`M-3m%^2RVF-ND~pYq+9^4$vOVQSfW9Q&PH23H18T^{y(Y-TR4OKMp^ zHcgM&u?qA??%H#$BGyX+z>?aVYIg`#=Nf}`ZER<%KeGi~aZjo1jbeZ2w6WL-#99VW z@9jMI-+uGjuDiLzpoDHa&11TTdb+`x0~LCM3YVM5Q&T00F^5f7R#vMkSMEQvwe_K= z*4I}DCG^np=td}^-w>tq&^o|@%%|7?{Qua!{f8coOXkIXyjP}=mmGr3VP9rw%I-nt z1Zd`g!xHm?)N*i+(pu_ygo4{siZyHlyi2?npgm)qVg{d6{?#h4snTSw%3E75k4wfi zv+Nq^a(xZ@GCP3I09;$5mH@hRzH%k;I%EI0nG_m0Y&wpO4=E+>-10K_K41X0WZT7A z6IsB0PwSRr4R(xE`n#>>LU;X|t~!3TdA`zoexO8e{@>i&?VI(WbY8#EJigs41vC$q z&~Kius*gYZna}*muX-c*8>xhT-Ivb2^S-w-`oe90_0#pKr+;Yuw)^Lt%Dngid--Lb zV?ON797asm1EA?k=I^Jz%yEFAYqk7w*h3w~vaiK{Gh^I8<{F+$M^S1wiN8y)r7E>k zCP0$6CG}mJa&=1PcFsQ4wmES5-|@0i%-XTf#`KQn^U^wVDr^7$ia8G`^*TUWNuOG7 zrPx;PbuOnpWjW;gY`}2FndoLzK6j<_kqX@`S6~*4meAdAb={fT*nq`H9{&1m-~H}g zzjZxip}#Lm=cqCJ()Q=Nr=MC2joFL+xc~|n zRC^=U2uM=$+j9Yvfpl|u1LbnVO99()WT{KbYYRBrD9~-fxh8=vYF%>9Gp^^e2AvvS zlh3X_m$>z@ihV#G=ag)AZ9oFXEasTgQ|pndLQg$^c zD`dG9@TTjQ*#DL|CfGig0tQTp0muQ)-bjh=&}U}^k*1sfPuF1QoC>`^$U!%2Q_X8d zz^1RpuB@z??d^x}*xLTylTH}4y}fw_s30x95i?ba=If`o#wM z{q;Ch=w@Z*<{4|Be(H12{?RYpNGkN}7HGz7=4Vg+#gFyZ9(!iJIXLcj_j*wQkQ4t5 z`!apGe8^;mrtFix%yxh#B?~&25i&=(Jt;SzlHj+bs@}F(MyY+>1`ul3!$6)5PznIm zybhV~sS|-D^|u=MeopQ+5Y}JXHW0SeD2_v_Efpw_Z_0O>7U!wPW(q(~hSI!;W12Zu zm6|jd;Jq@Xb*P1Xo>FbFIb(xoHC@}=$nwZsY5u=CN)CFjZosCqI%=L@?16o?UucBe z!!j!?>t=23?9$fu$3OA|U;5@xIxI6gJ6jgZT!WwMwsh`h0WL~%adC0rnK`?7)2Hoi zpSpE*?L_&KxnnM!U#9ENbmE_P-l+%4KbJ4>=a>(Zej!T==dt!DB^H-H7V&QvgI9#^(|E~}L zLG?dc(B-}Rz2qk5ybK4;qpG&Ir z*qk0f-_D^`n`^Srr8wh5Nmu^f9hkZM61tsg{y)=z&$$L{E*h{)eK2dy7S6XqfQO9S zm)FeJ)??>(c3ytPm(aZm{dz5*Uzd!g7XS^d0aoDCC;sf0w@?4zN1ga*OeOPSj=Ag0 z4B1S~%4h;I^KR1GEO2efXPgY%QL&syu^!w_`&z6|j<`mv3U8zAP6gT$i~oigZe1Sw%SBb_VHWzz%IUe_vzIWr@Lb*nScA+!;<-{uhs*Tb_X(t zeVM~@`OuUdCVrV?W<4dAB9*mjQ`*WE?O0|uohuN@*`KK;sgmWvlE@rqwF0B@kFH={nK+j!I*S#^+JX_EBKYBpzQY z>>n=#;=C(u#5RVX<+9BI4!12~>?JpOtpFDUQR``heQO!^wNkRA=dt*txPHa9=`#M;_Q12?v=Yj4f# zj?oM~Gaaq`@wLDBzk9cR;oS1-7#e3?iQGp_I*>UO|I~XnMsdk}0yM|VdbVXWA#vH-fcP;UFV z^Ti}g2KV-xKcT(9<|v}lUUm_c&Es7=h^T}H8`!^T-f+3~i0Fkuj=7wlUx1aB6P>F1 z$RnS6=8yl$_3o{CT>(vhARLYCO6H$E`KLeCJMqy^tgjCRK(1%ASjpUhMkfGreVMLg z{`TATo|Vz0WIi4Sv(4vnm7YfhnsWg%)qj@@l(aVp4ZtpStaE@m_xW+;tGAi-Xo01r zi50Xw&4D}VnF5#G-zgxSUb*=_%&+sbe2^yark5qY^qeX7lQ>gl;c2FP?86E)V^+dQd{Qe^b1KzO=Mr zR#t9b+`jUur@#E=Kllk>Lcbn`LDyC3+}i-SK1|1Ec9(AZOy`ylu8t*Ua#%922PN~^ zl-(=LCss!303+9zQClWWDXZmr8Xo0O>VA>Rvf~gbr5^th(qO={laZWr2r8c;Km(i3wW#|0A4!gge}rG z8Ia4mTnho$h7<1_EUuFJ9zeFgV)ma=>~rnJH%0Ixfy&tHGlgn1eg@TC4ECx6Apd%@{gSz7GF@+`JIRo6hP-4X`#Jvk197ysY;w+h*Q2x%qutXw90G8 zCSLn7AQsE%b_6#2Lsru^_kMe2R6@V#O6c`oM5X;se~^=20P7r#r@UpR*Zbs0o_p?_ zzu>UX^~h>o7mOw*0D_edJhOJJucCpR&j2^5uAgdWJf7hE5641a99Xr5*`%0X(JXb zX4PuoZF&p=#yMtD!zxngLmaS@GQG9dN7&yh;8T{kh)j-6#r|GL-)BL#&#IXHNJUe| z+ff70L|fo<)B$8Ml!g#UVMOFka+H*a6LeEEqd*47qbb93{$69!!ujAj_q36q#(nbgOu;5G^tKC;+L+6HrM`v@g*#wk5YqRbC?O;y8g!$>o|cu7|cqRr~#^k)KBis!Q#UQp;{*Np#frg48HW zPI=Ci)HQyawea~=&cG4v{gDs!r2KCv>&18LlVlAAo! z7Rcn3O|{F@SevvJoq%w2p|mn+B34Nv-2!p^0gH}hvpod!HtB}=P zUS6xVwjMlt`SO2w)UleOx8`v#pC4;RGh{QJwExrVfBHY}oci2{R~o=sTma+%rYo7h z_S(?O=)LzWsxqIjnaBTzooa-v{d}%9kX5i1HsQN-%fn_&`@0P*IF3n$DR8bx0*RF6 z*_P*M$@4!3Mw(o6u3RqJAoU^5r&E)w&aq@EDMA!;37}&@YbA3TzE@Mt(ZnXUy)HGN zQ#!w-K6(JY6tD3mP4Qj@XkVEz(wHzhnAV-xzJxwfp}P{gb(TlpcUkEDL4r^L474A& zy=0b`Z=UJ*Km5d(zVwZsy`FN=k2Rwi=b4+OJ3hH|^3Iv1rRB1;u`^$H#_Yai?o{WO zF4btse8Oa=voA-z%i4;2N?0)E?u(SZ)YNsjRt#Kjl8Yn;8Q1aGg5);eZIAf;=gtK# zW8E+;r)MDhfcLe9tfgn0LRK?Q6w0+ik|t7IN{z`F53qeF<%`F{EQ>H^n#lvld}1OV zj%D>pLaCE+E}x#WN8*5Cynj+A*3!PzW}mAvtvjAm$Od8x9jEd zOJPb=7#7p=+%y6UM6rrgZzaW8YM4+B+bOY|ZF)OXCIuQ>Bdx`eCKN%NSFW8fAQQb9CR#O{o3|}qy7a^otE+R@h27DyWHdu(eAo2)@zsC#zuQ|sbC*v5 zDr#r+!VA5iWNrt_b00Ao8naJ8W*Gp$*;|++Uf-0w^Bgs~8Ph&I3g#{Ky%yw>m2`8v zUeEp9e$L~(O4dQ;a77pQ~PCyht^S=UC91*xwpYU8`i@IHt2r zfBY z@a(hS_(u*Sd0cc{SfGk|NFzn65 zjM*n8^YMeyHlm5PH`LUdjTU@K1L;wNMIcy>5&%hKwk`EpnlW$tQP@r$l~q*fbH@nh zy_BmifyNkQ_Jd~$yjlTlN@evr5eRancW%F=F3TodLs&1OQ2;x`#x@h zZWLoI<@>3PF6CN9azv5`mNz;%ZIBvd_Dbl@^E)GN&F?j5_v=NxHTM&PTn_q~g-e${ z{mc)2<#+#{w>v6^jouGH(;;(*+8t7_7H@sJd+Uc!6}K~Ttfd2*ufE!kjM-rz0NUN1 zfXtj5cdjzGEz>uye$X7Hc}oAJDX?!-0XoMp*aF8wx>=-d3lv(XocCYP*QDm$vH}3% z02v1WDNt!a?%l*bPXR_v-zEpBwC99XFo;}#A+@)?YITAp#u5HFO|AO%+>(yZ%`k-HPqXz5ZDV_4%v6?UROAJ1t-OvsD)9Zcc zgJ1gM@Bid+HgP{zKyzPNHDoip3#UFdck03ArR9~fJJ2c1zxTZ%o9T?%&!4wJ%;Z6p z`J^{99dwpH$H%d}60B+pNaUQ)oddI! z<&!iamex&TaG}LDCyp_rlhL>e=tvl3oww!)Y{J{k|zG(A%Hc1E9C`;(u$a`UrKEw8Tb<(U`Td11_C=$YwM=I#-)nUkON z3$XNV%K>z4Z|tVPzR^UzN8Tv=&4);JuEV$-Cyd=dL?YTyv6uNW<_NW?x|2z)cvf zzJ%oMt-#(jzMk zb^`S5(DG<^q(XPXpz|YP&}+>fUTU7-DpDr(ZH9}BE7iiny*F)d-~XuZ)jV#j=CJ^p zp_ld%w^86Fd&Zy{4a{`|iUvx2(-ZQfvfXvu4b7E)I{wAtzv_4AasZGyg zTQ*4oz;?|0NMcL0J$ktc_Z%Q?pgq{s08oIVVdqO(%CtF#>y*p0kf6cGW6Do%SYJ zs(c+9c$)$b?mMJ)9`~4STb^sF0;_Q_pOydsQt4dd&6$!y#4(*(wlcTxFU4jF^VQ_` zC&%Pbq92agq*8w*;B6V!GqvQd>Ft-Snj~v3ZoeAkwWf`y+!F>}9SVbt7Y4o4Jip!? z>6iP%M|-dtn7CJqi)U9hHy?ZaIQD8@bD$aaW)9fQl|TB&+qeF}hnAOD2d@<~XLn+s zp#W$bo;=#eI703<&GZ(xa5GL72c--mMOq9 zrA=U5;8X(|a+hnzc)mePc9Zhj)PPB@PgCmE1kfqSLHRd;bW?Zj#Df#jNZ^Uu4lLnF zI-e!b8K1j`crRvJFZpVY`&vu6j`n{1iPy7xi9vQ7h9aQzBUbaZKG+xgL!{W6uhatp(0lJ$*x0a# z1V9szIbPsbk`2{bxJZMDQGPb16w@a4W*ntr?Qt4szoiCPrh-q+cVZ=SEjTS9FtP=& zu~HK{Q+Ro8*iB7OW?unBC1z6#I04dC>e4pnn8-_ZOcX1abFLYN;lz#~H{MyG+fuA@ zQfC$Ct5vX~+Htsz;{(br2TjQ4svTybJ7G|RIh?m9yy3hx`y~>C;%Lgn#oLxHU3&cS ztvw|?d7t# ztyFJhihr9_rpXl|rFtQn7W>s?oD-?mMe%Eu@Ozx6ZJ>O)JqL7}F?PLQ762b-hBpn(eA#Z$i49Br2vwAuP<^sJ`0JF@WPW1s*0AN+&j zt%uhu&L@fJhgzdVi?bH06Pu1h*_lLJ`v3o z2~ogP`dwRU=hW#r$512oiT_>$Z4Z_jf6Jxbq)~es^kw!o*8!&(=Gf?K4K_guf+c{(0n8Lb_`U*E zQvU}f@Jxxl)GlB8ojYGD#d(5Vdl*pE_-#_mY0jk93hQfIj>oB9o>|}$1)3wfBgbm` z#Gs1}z;t#;ua87Q=UX#eF{@eEPaG>&vwN++IQGr7%Xfcxb#-OHZjOP?^a7y6c1Dwu z`FPk)E6m2R0VHR+;{sT=#d<=a&yE9Vw;7lvmL-KTFbTg)etaf#n=H_MWY|8fg=1Hab$Cfuvj%;sy=L~QB_>AL@z(TL0_glc zE%4^+0Jv|k_B`WaKe>B?EqJX+)wLCLNlIT%0W2L~JErTdlyPFez_ z<@7dU-zl+;5({dWWf|pII?iAUa$(j*Q#xUg85+57!w76PNBM0t=+%T5`(RF$W;GYL zwjLg&1zoyy39ekZa?M!HBWE<@6rg8T{^%F4-1^1Gi?f+7&)k`_5ABQ&3V=d8qlo}0 zjSXwl4{0+7EbhIMA+P4feVohMHuE^8^a)CAo2FbCFGCX8Xjn)SF^?Dn>6u6Fp?Qyg zOpP90Pw~J?=Tv=H@(3{!%$Ms&m^pcC3toKUht~b*GO34!xnm@Dpt)JgH_3Q_imX^yZ z&7C>B&olSt>_d<_u`?Pc+gTderS{j9DxpHb&z1msr9P`+FhL3W0B9is#$0j?WtG8H zPGvl%k^fH%?+ckt>~)Iuj2Tag^(@7BN=y^SQjOc^Q^TZz0Aw_MntjmsXZ8u^lIs1G z`Y-{~y)>m|$0q4DGYK+@*FCrIQzrG2;Efwcuyc@err_91Tku>>ogk^s`M~bT?84CQ z$g`Tx#QjaDKyRMEUyRjsUgS$lE-mOokACrszx`7_1m{|?nn%iLdNy+ym$VFK@yv%7 z*KV3xSXeBV0^}G=XJ_QtOlQvSq9#K&(*ey%UuHR$%^st5%N=am^m?W&UsB<>+2`ukbN*g!9eCD^u+J22xf!=!&DiMV$URTLwLgn1PE}6FEt$OiOWfVX>;9anD92& zAh&zYxa*T*`3%8C za-cRo<`uyz#@Ay7I;FdI!Y$>V=Zt~21!-~Mvw_`_?HMpTcE4A1VB&5ErE~ke1_Yfc z$Z9T|`T5(|E?s)~nkvwbjL|%}@r$lE^JlM~{KVtS%PV`CoJD@TosnlVFJG_pF0ZztB@sqH1L1g5hM+nEzl*hVRzJL%KpiE9~7t>0$qXSBVPntXGB z^!`n3!UGK3DxD*_;}|w!qb#=!F{W6^OH!jIBpR255-lqhGQ@PFlgA9Q&_}H1_I@;_ z9rS9B`ZY_jnyy80y<69hKk}t7e*LRnfnLl$@;EY@`%S7#&{@3sv8na5^PbHtLgzZ$ zBFARF{`!#3+}PNUnw;1fjR(k#i=jJe)-;D5lEh7=`bu+I)m(rn^;?Gjuaqn%Wrl7t zUzf^jQr{PK(=Y*;FbHGWfx}ib6ysDg3?^0*tptE!Kqm$%#m-A$ah#$H%3dctFKXdD z*D|%Wazc#(Ij*lzvrj4YIRx7ndyPu$uL0VKJ!cX@^-5CqNQPjPEyoA7SHYTn+O)%2q4`T3=4ZtjkoHn;A7$YHOIjg378`VGfu zIw0xeCO@_QCqHp@?WqqH@68O&+0UOJ+8H%F6n1xEVrO&>*+p}WzyGM&(Q6@G%85oa zz?Y#ls&S)WET=&3vBX}V2D#@F>$RL_*vct^L9K$M0 z3Vn&`)b?bi^yiJUk|Cwhq{L136B$h@H@${3N_2^}<$l*p_}heB7{xfJAsj3N7`1FB z2axN8{lQHlj-pq2@8og9porB3$7;ScVl`iGjRGCe915=&U}yTR-ZA z*u{j`$BEI5%jVtLwMXIP{cDShOJ#dAot=@3nsjXD#f$YMYO)}xN%fq}Eg6p^`KJww zXc7nS93^jw?d5u>B-S;@-;&E0rx;8MpkhE4;IIL@VHni#HO?^FA}p4q2Co4x7g~bx z3E8MB`h3VrNv9oX1|rTEkg0E$)XAe{3Q*s;Vh z1ISaNr=B=P`%e!4`?NQT$Ey{d+2O z7@4>aSxwu3P2=p2-sppUxdBCIi?EtQ&+;WRJ$3H3?d^N+a~RC~YSs-VI*MM+BLSKo zSvxk9EZNvAZQu$g56C6PBz)b+E4z9VJ( zwWY(k{I&x!z;hIt)8E}3J7*VIj;20T$EEMZy%&{^Upi`ltwK*=KD z6BB`02K-h8=mY_K;Vo!Don3JfRw;!==I#y-jcvj^DRJzq`7YK~0YU1HFs z=J)sPpjXrQUd?tTbdSB}=H|`f(uwKw=kGnYwA6(Q7cRi|_I3k{dPf2@$IhLGmPQUq ze{}gz|2H`G)NP*4EYh3lO5>i*bmr`y&2&trPXU^M%<;_fN&u}=tZRwwG}mxFa=uq< zYr@4rYFJ84sG_kOnhxGGjXYxOplQI%=l;D|NjfYy$K~$WQpq4pDaM?}K$_Z@6|qk% zfz|E6;aJOXUvf53@oN>s9Cv^(t*5kQL&3uu0In4X`Sd<(?iIW%c&m2 zO2IHu_EGT-vsN%#*m60G7fzHxl^^TqbMwt(djvA6vaFWT3fWl8ux{EI))W>~Ta8F% zN43?DSV#k-^V2fSC3P*-3D=Pk#tCZs3!}h8|e`A)60MKE4@=spYw5(MbNa{K`*~t?~Cp9gu}uhWWpNqqP~cEj}l)ej0Yt z67T6N*ij3(F}x(y0fFU{ERD1*DmMX1Nwne2(hcM%Bmu~+I+povts8>m&|sKjku%{+ zwo3}5DMzfP@73H_pci2^_p8dwm9>vQ{QUF3{r8WO)f_9M8OKe+;_c2f)VSZ>>qS&g zE^6}i=77X(W~?-y{2V^VOR);Tut-`~Lc{EpI*b}K>6{y8n+QJb=W_sND)5v5O$=^2 z2-As`bEzIojbOz;iV|al^>oJXrTXyVWn3Bk{}SLmrWG7E&&CYBF|%<1 zSyZ9{1-8)jmO7?r#xJ8_HnE9fmB4Vcl@)#A6b^TQz1oPtB0k+zop*odVqqSDWxyn|bL{4O?47 zCUe4O<`4eVtkWtlq8uY!n|hIEGPu;BV2)saxA|-ei)n;`8S5pi7#5SW6`F~d%bC#w zdv9pCj`db{2rx^P+qrG76yRC{UsUps75)~29m=u>2o|bVP6z<_e%hG1r0kJ2jBp7@ z1nOM1Ev^$uxy|)nN^zA^d}W_`FK6Tqkk_wineU1BK@GazN29zyP-#O_I*%oQE_HsV z+>1C6oO-`0p%pM~E{=%R97I!kR;^bF7S} z2bvB!oB5j`o?pFXCfu;rKi~hpQ=0b=_-1;g`Gn2PV+TzlTUV-&GgpD$mOVU*)SfoW zH<|C@fEcG7uL#gb2~xD>Tk3|TVF9$v16R6^S;U$+7c9u8?f`VT(nKJ@ME4T_zNE)7 zsnqf2N}re-nPYErqbf`FT(`ODa{?3%b4i)N8#vsvSre~Q2KyX{0|78|J)7}*bdY6H zwnAFV9zeFw5Z8fW7-uPOJw?&YwZ%wjL@DDCC>c}D7=;4!nAIHhYVNa|u2-`GGxsz{ z_CzaI(|eh_SjvryXV*73?|<;94BW@YXzo+B3-FoMZ~oog)khyIZqV*hfcBN<7FC*0 z*vzyGO3Pa2%sQn3M*DrH#rEg!mvOCAn`LkVrV-f7xF53%xYBzHVJxv{N;N(O2YW4J z=ClI3pD+f+(#?qL@6yEBVW6%?&S8qRvLj-`M!(kxNWl90vNXe|dPrRqk96xP~f~L<>>IdawE;*a5w&h}A7*uUalGevmGpWfj zUDxgplfYg zm9|T3wP6(uCIDYzjcs-?QBVvHJHQ*XOF&Ozd7<=TaAfwDkY;w_1e`8PynDy z?bOYhGUTp>b0zNM zH5O3j@F9~~fv!)~MvN!6@3j5h+@zNCro?<_0qCeo&LcAwoNAfVl)lYkHeZAS{UEE^ z9KCy*-;M0(7o@Dt?j!%;FE&JY2ZFKpy@?Gj+*T+f8ep@rNy$nnU2kLVDsH~ zEo^K!HWLOZKod4I?IziVnaBl-(#=0rsFPwnRML*9tT$FCa_q{VXXj(wGx2F<#T|;0!>Z0ckgdy{PrrrBdP;UKH|`p{R?JlqPi?Hjl+ zn1zLtvl|0B ztVfyVcAzw$u$j7x;y5s=Qn?}ZCMF3Ou4NBd3ZtyqJ^Ln@1hkLl?DRHk?r z=pIk5EQ|GVVm+js@jSI0tR&w7+0)YkE~W{YVTuW&t~pG6(g(VPj+JDojBQDPBBeKR zKB$tFR5p_{hU<HtoIt2%j)BHDN$YtE40xAX0JMhhTuEEg=A>1JTaXp)2IxLj ziBYYD^|Au{T>3ms@#&pwON-zQ+Kco7I5yM!W)9iR{Y-P8XFg#w%LAo0tV3?e zI(3_lOICWDK}$1J2Zp`G^1?ZFdHjFMyRyT86wTy8N*s|`OA6SI{wvQ9fR0^O7z#Ge_u!*@b zl2Us>?)ZM-k@L;86W`1+&B9WcM6JhW z?yOQPWV(?+CY9%F7&b0s?qa(l%1}LKFsBhlGiESj5K8%Ha{s5mDi-}nF_75u;)qAT z{U%s<|K4u&8U@5-Fdl<%h`>E|ZD8jprAC)b!nKSUXWA{Xm6hTVAFt9A-xBsSaVrDI zPI6X4T0?lvvC^`#TAQQzT#2_=V%4nTeu4F4a^tR1o`)bTo4aqNh;-VF$r4MOdLAFz zZ$7=S7qFUJ!?d7*)lq|a>?^GqG;eh@WHpzne*cjNzw(t||J#lX9G57lD5H6B0~Vm3 zKmEweiF=lv2&mWwc%?bUW;&qh8O_O0Iv`6mT2H+ZP(6y{U^TRZNa^oQ?JG5j1DyrB z2NU3Md6eq8jJ-0Y6do=`MrwiJn2j1?89`t#v1mJL5RVzbesnX70b-4OF21Sz*QX*@ zsRG>NFr3o&fi7hN=rzx%r5_4)4_aZX_ zo5p9LZ#Ta;7{?4)%^J)b%^#fI(U~H#l+M8Y;)OeJ-MDz)fYrQo>C#w4LB)aQkdmDn zl+Ejjb3@hvzbE?kY_U|ndW5%=(dbP&T>kz8rpb43waTnn(wtO2PR;c zUry{3vw0pEb_4c}1DINn3Cv)vNt2ft9JT=lD95z^2v{ zmMWpg>req5Bv1kG`HFoXmO`ZklmKaEa{xRbpn~-37d0`h7;v4#OtxV~#rr`xvu!Yq zQ=kvZ=Nm%>`oQX_0h%TsXbui-12l)E;GCIXSe@F~xa01{#rXkN+uGVHE($8fk%oq# zdm^BdpZI_i0lC+T}JC6WN5JcNPg4gsJm0!U*3 zJKoMPbKUQU4jj&Cr`TXBvmM)ivBAAbn82i_$Y3Q@spJX33(bt;G430=cA1<|hD!gT z+7-CcIOo~Ptpf;?A5a`;3~1JhDWxCGCosU_Ycfl8|wmn1cMDWXMF zVowq+g|mlU7%0u{mKpkL`dCWGYMyAFe?F{0FPgczQ;QcbKK7`?ZlTrDv12s%S9%UU zyZVj)uDANwLyIGW_9ASi1DCHhNZ!XyI*{4yj03)z6Ofq?1WR~}Nl}O?QC~S1zb&-R zkhq>E^Sh)BYs|U_WBR%Q>o&!(n3ND`ieWH03n0!?iSpQ_RP0#ZIc5$oF@+TZszX*1 z+umu0t;9r7{t??i#rr;dU-&!aO-e<4a@P)*&raDWNi72y@JTsejd0zMdjiX`YZ3!2 zWuEv*oU%0vf&1q8+Va_dg*XPYn{VnC4dzBP(ThEa^CMyUJ%18gB5@Vaa?_HpgenDU4rgB8t{Txzr7$En}Htu~gEyUTfrQg_}B7 zqQ;cvRJphdx_(Tl{K>Il8WZDKgoEX9OH2yq!>8#J<_yqnL@XGP;|eiZA8 zcXS|aKd?I5^a}K0uO__Q+Um$hg*#SrYU-ZbHa5=PH;$yBVx@D3jD6&!UAW_Z_v|f= zTK_mOxzA?SBc(Y^ERBR4iv+5)1GrWxUg{8}rut|Bb;)vF&fuXDU2q_i^26l7JuO{EU)(>P}zrrrNj`J_61M_fcIQh z409!=E~Q)>RN@K%-nx(rHf_ssD`m{YO6ZaZ2lHx;Rp6MSJ^sA{osiKgzpp~J$0F;C zU8flMg9vP5=T!xK>J-PaD6nHtN^X%WnI5TX?@xKn*KL0>~w43}A*p zGA@;KkGO2Cu`!a8hr*j>yq*{+*2G@Mx$7}Vo&vlFHQqj~ zHgVkn=tIvx|Med)8n9D>(TvsRcJAbT3rovY(G`95)nVME zE1OT^Cd;y(rNNf=J9AkHPSnsQdwpE{RT|W&(oRaLJZ3LBm9+*`rcMkD!`>Ru_rXf6 zgeH=XE7ftwOq1nJSs#U>p+3WMb`X}cA2FK!;5gR-yBT_PhKwgAy3w$U+>M@NoG8$c zYunT2`m~v27}pm`<%t9EwncI2oXaBTRM?t*NA=}WCI1i%V*Z+3n~NnT5t_JomuzC^xf6+1yzfz4lr?c;48kCvlU70Mt<`v?XwuYI3dt z3`YTaH3L+NRfm+*-9gyQ4jdM~aQ&DXkSPH_ZX2l2wOtmaz?Te|b0!6xB?}M?Qg;LZ z!{=V`dk8w?_r!p-4+l%>G02Qd?{Oa|<=L1rDU$pixz-u20@uQLTX3}C0N}6+y+#1L z5rC*BE)r9=SHMS=8lcV1DTcLDf?Dj{=E~uc2PS~7EUxkP1SKE zBz#vc6IlvmN~J_hq1{24%h>A?E4fYqF9hPu*s1ly%HlB)=G4?3pi_$|G1F88tf}pp zV%XmJ{hTi*wy&p{V-Oz;Us9ii!!e&!v?unSxLhB8Z}^(F=axJFQx(}#IbAyMH2|X2 z&0m^GXiDf(S(@|AOpOrb))y4VC^Zr@Wuic(KXDUhX?wIS4^j_1Frdwh;XE>9I$sam z@BBe>Aal>^2!^7d_lB(IAStLFS?H_Mb8`!owGZ6;`Op34PnV3O?3P$TuQs=HH=kQv z93}x3@y>K$(zBTtE)07!ou!dyGbc*(v_Tbs7R}^{0mcPDQ%mF5`V`RGad0l7>Aw4Y`P^Z=w&nBIK+}6?I+b~EdHsR4 z1-GIz<&uD$rI8l_ZEe9`3Q(^%F=*EesuDnx?24!+?Qtu$OP13#2|hX7Bb9lio+3&> zf%!>drST~OWVY$YlwLCma7o>qsNXrSMgTbiPm_gg1fW9!ni#BwCDyP<6JwG(0r>2} z!5+;{1RCq;ckKUt*e|vBV84VO0}1~b3OE4+u2Lt4lEZ^!PM+FJTdGna^`yoCo$JSx z`YeFZ^!q%q+!7wRkr}=##3TiOWyB3TS1ijTfPPL*Qy>rmQ!cynT_FZ=^EZDvBxl^CF z0h+CvNJA0OM;5;IKbsQ|-#kCRP|oc0wb%N42JN8;XreUFX9u~zbIZ_JC8dJ15;2yV z@3xBdO=U`qV(^T&wL+LnBY;e-d@Gg8v68ArRjyIIQ)+VTyEr3q%x(!Q18aRjr{HiK z0cTPlE5zsEpfY?G4wkQH0gvvC73A3OVL9EeA9q}*h|f^vcdS)HaurA!SGe-86tAdZ zu(-*@QH+_IHUUiux>o*GnN6P&u70jC4^$TMuuMVw_yn#Es)d%}(>+<=28{~rNbniX$+~98$bZ#_;c7cVT=&wHA#6_dN#-p?|q>A z;`+$ndl6F^dueunE}QowX0#W9$gqTt<#%&HCHESYL^eRM+_vx-Wm=907~9LmK2m;} zDavy!{*tPQNMa!DdqZZk1Bc7#1`e9I#|)=d?QIp+2P=#p7W4rQITMrAG`HOIKIQ$I z*X-zE8P94u1NW_f)qK5q!^5K+K_=_CS3X}bv$LlbH#Q!6*kL<|@!CdGcH6Ji9DH{9 z5B}bjwZHY?`ux7pXOXhGXES}S`NW{TEO;E3N}W?pmIRokS&h{FOQ|chGi{AeCI&94 zp3E3z#w?|VK{BBCK;*2MfcPG%3|}E2lVe&bpc!wwq&l?`pp3y~%otJskL`_m!3}&D z4jZ#$rE#pME<88l(zyqlA%N{6-(jM4rpma~1TMwd!6X1y$^bs6f7~hnE_*`{_C3XR z8Un0IWpN3RT4v&7gbB7rASCr_V#}|9cXpu01}jV;vHc!?A7#|eO<-fCyb;zhmzCuH zrm^O# z1OwnL3Py3hSOf^f;4F@Uq)LC(+fVc8?lyMIP9-kMbCa&A*0!W!+o37XJa2u zDwfkQe@m?xCsMvwtcZoGLpN~jG>^-pq#!2>daVy8A85vrl-=&#r!Q^Xea@H9 zcXoENMM2q&=6>Vo0@&GG?wy~XGmc^{l56e+KySU(hxgt)C<2;T8p$VZ8xf7fEaWhs zxu(o1>?bEu$`$$8M1aY8vq=Dp^2zK9AQLN>$7~;fPGG~O|8U!&Wkmtf`axrhClfu# z?~4H{1@5H;lDJYcfMO#ssv2)q z5Ux3H;!6pAG;y=3AWu_q8V3;NwnhA&aIhJPf`<9$4f=bx9*Tk-!?~2bw!Tt5J3D9Q z=2oV*w@;sOK+|EptprPuHL!QAmB2QlMYO}viZC3+JPX*mCZvD(8N1aH*jiw=Ss)DX};GeScEn= z8*UvWfUE(Q)Bsd5GkuEHlT6vAk~sx3b6Ly^HcjZ?LKWgb6lB0QK+{|p<)1g$$=sh0 zCyd#%t(=`*Fx~FiTP|L_=U(rP?jk9VgwYIbja=Ehp1tL6S2p)*^VUC}&2(k+%a`|) zfV>E3^3$F%Y`!4AeeQjBH= zI^{XnUm2IX4FVj)ZN`4D;IOy}1m+e%m%mJ%JDA;zl)U1`{b|4>C9x7ZrW)@cOl7>k zLjd_e2EQh)DK-ClY67X3gyE>h$*Z&A0iKAcyfBqnX;LnFTbxt&ywU z?<}9bzi`>yi-7C^XzuI`&Dkd+pz$!C(!ktyU@!G!m9M4j2h2d40+|xvtb~D+O8@B) z;Bq`76+dOzh^b zz#Kd^|8M_UfAvSomCapw{Eav2kqD>`%H|UhkOZ={C1GG@$R)vSTMwk%k$0Msl>wc_2q+jR zfWzwYxQsp(u$ghHPaS~Ge(<~-vA{7P^riIO05HbKrXr425AgN_iZSg2-bRpeU1=t_ zcdUQLKVC&pv~13)zvMw)$Y0q?0!0jSb3 zO&J$b#X)U^L)tR>AdYf$f!YQ$8CTBU_MVyz$aMMVk7W00hU)X#*+o-z&fN0svp@ec z|KeXf`CoeVd1{|#H+2QVG$1>B>)i_r^TvB;w*Gnb)p}4icix$f&733w=?7HI$X9C* z(iRk^_ClqC!CZi>Et5e4$kfCj0`pl;Va}Dxv68YR{E@7LBxbY208=V4CwDMxZcc zCF7n!Oc-Q@|M!J`Q@6M-+un$^rTS76Ta2~E`ZQxElVUPPOF(`IrbR%-eU$P7I-FZ-mRA+rfLwJM(v zorPu_e78QgvGLGDVfj4O<|r9x4r})_1E6_z`R4o9=jO|m&EI&VKS%=F*r;K169(Ry zj^&)F&C`HdY8kt2qkS7Yq!Mgy$?ehrHtj$O48ZFkK$6QY=Y&6+b>MJ{zlwO#m=MC2nkr?cd*ngWj5*;I+`Es1vZ2yAgBQ z83ECX0Gr{q^&@tZ>ebXb4D_ROCg*Qi+)T+e1Z{EICZEf9o$HGE?59>Mgc@l6|Mh5FYbx1iHiHO6@QI`~2v6Hu(Eow9n#WFhzj*m`$DrJX3ud=(MTuSWx}4 z)U~7;$5L~o+X_)q8PT|`%;lTc#A~(k984*zg8(eCv0BP+2cS#p6k?DR)f9-T0Pm5P z%G%m<46_?D$KXB-1rVBm`Te#ORr&S+lpXE zK{441RpcgO#6mAjzx}(>{=EwO7D&4RyJ?W)u?q(c<1t3tm(MlKCRIu|2*VloZl?Bs zOJzcvxolf-?8JIDIpugn_Ln63 z;jDZBbmd%1VNOL%YD7OB1$&tSGC6Qr!C@auGg`k8WOfj?lVaBV=MBgrCe5Ux53*Q; zV{~E$M`G$F<^_~D;!yeP6N09K_xfXA2m5R{0<9i|?na<6?8A%|{lQrmOM0ZVqGJ!&U3%|&T?qw={+3fdS&Qohj# zbE;LT*P<1h;c%j^C)`1ew67hL)j z*0zDM>s-$;H-XSzzo|LuZ6=zzV;EODrwsk6QhJ*Qx6e}8%o+|yk8+Q_Ui%0WRD;}7 zAx%iA8yZ!b$HZB_L>`_$9w_@`vP&?Pu_-%tKJ-8~?Z>X4ngC>V3)az$sS0BYkH?a# z8?!q~F>{w59B85#da6WD8O3v~eMJDliaGy{lF`(b$TefL)S5x5=Z}gc$|iV1=Y6L> zcjfcVpnUGqf=;$JZ+Cr~Gcz|WZd|_SoU3R%EV#YBT^48#?gfFZQ9XbAJ&S<|sDRqs zM@|mCGl%_|ld^e<6HywVQxbusEcDdxY;$wawgu#ZOih%dG>eZxTt@(yQcosV){M7F z9KeaK6BYkyfX+3q72wqpzf%fv>~(F7B{fb{f*`4PQ(M(abrVVXjvTP5 z-mcLB*;AjpNXiCi8dpAl$EnY2Fei(s&l~JlUAlDoKuSCN~&a;EmZSHdC@Nl42z# z0L=+Ju!No|1XOY!m~rV+9GX|Y;gk=mmH6juK*}PMp7CVN%=_r?RHen9=1)oKFxFYe)$J~@o)dy*FN#%Dc|DNq>x z-dDV@7crQ9*2N{>^3VgvYC zsg6B*5nx4$1OjZP0znPfgcje?hTY_>gJR)L1-yTy#B2i4!XS-90}$(|u~-7oWq@=2 zxfL81xa)4;r&$bW`i4+11ghswoo$|tqfU#Y0lBid1DejH-KotdW%Kc~oB(CY*Eyh9 zVo>cEFKEih96ew#5MiRK{MPK!AJ9dlX|g>xkj3 zL2rAcWu}e_Y_=UkQqzsX9$sw!VN52~{}?lj24oewmf4hC8L&wk=hulbiZ&VpQwe~S zv0VbV-0z&Xr^I}k7Uye;_mcZ8JbC97ayo1MkdGZ9XZR^MI`_W0?a)$H77x;s#y{U}_>I0rX{a zZg9c?9cBHaP5qHPGdZ@^5P)|Y4*Onqh4rOCCIvnyU&I5$8JE(rxJNAO8-!F8Tjb@H-2Gf^*=v7J3C*dZ0_PF9h>RY=9`<7vbi)MrhrVUF?<{hW*eYr zTYN8d!>4*DK>(G~jTPI!hG4Wh0*lEpnW?jXj(w^WfOm>mSN}W>ht=pEOcFZGGC8iSQZkbk4kvk4pl@5k)wp}?|AO>zwp(6{=Yo=cZvc{F9fpFt7p98sEDo6 ze%ag(vdt$Z?YcqFCZ5bk#e!uH3UE*B4p*#K#b#N$CUE|+78IZ}RkN?B53 zIk7lN${3!C9E{%^Gjm*nK9#9lBdnzdkRhn7;jj;7_}oKS${5UYY-O!vbAVxjxMLC@ z&rWn+gk|h_63`3&UyuGD_l{9&=CEJ12eeuL)#z`WSO^Gqin7m=ZoX~H=<#^QRbK27 zjL(Jvg(1+yY>-NTOR1C{pF5HW4^yRgfbY|^$b^XVwA5B(I3TIb2iKaWrwVm)7|I;R zGl|9bk(9fEdAsY+bmem=3c4d(xxKH`nKx5Y>kAvBKFuOP^N@e$-08CmvoqyvjhspQ z`SZibN!On_DVvWMsE%uwmhSh=0h!X@b27{U^bPM7!60HikGbV?4dBH3L#G2)avE8$ z88~PdPW59}2*3c4{VFjAGd5T61+RtAu_-&pTuKJn(v4LME~R8HNsNVM%6pLMTN^xE zS;N6L1&d6I`pEI<2EeWx0RW73?IGtq#&l}f%ou?70PjKM*h|xLEJ>ZL<|^5-F&2xT z><9as>%B|`n%w@!Z z3}st{34JWXScV|c5^Saa|1JVTW1tx>mr7!ZjoSfqVh9j!rrNx1>D&t^BO#-CH8>9a zVE=od*pC2bY4XOYyMQep0(%`D`$fQ8# zRKRF<5O#AKbp4|Mw0C6)5bX%?SOP?b0CSgMI;nn4jpwD*1D6XjQ*H{>dDufHC%Xvf z?+3>!ERkaqq7EEZqsQlsRj^N78C|Qs)%LK}0(jK)&K-3{h65UwVKphA%2XfCS~*tc z8iixu*J%NsEHf?xG3HXB)dKH9n{uxJNK>DWTSNpUmNP1!+g<}QUHSa-NPYg!$m-~( zEa2v_pvQyL)AP-LZ@c;V=YHm^&7V|#nq6+eVcEPtee>D5x!D2eY;Dqx0Zj)oCuMW} z;91fJ$R;wQ2f{M)HnkOXsDTeUBWC|Gai*3*Ch41R@Kc-jI_(p zoa)XsdZ!kxJQg%fK+JjAYJ4e~)W;ZIIJN3CMpWoCf zj&dXjGSky*bLTHS_yE8++B2F5`ZMRxoDI`}TK8v$YIEnE>B{B~Xim!J`2%UH;c&bR z$>Xvu$TgXt&nb&YmMoNSX2slCvGf+ooL%b2q)O(RK^ZHRhfHP&GG`)y(m?>G2QH!T z#~|w$-!v%(5(AeZkc`>P9^k#3n8zj;6DjqQaW`(w@0zLsbvn7N)yW(T$e+yJ`L#8i@*Kjn=3zZw=0_$>CbewM(*L# zfQGh4ld^fKS#-_`Tgop}yRI5=EY18yUNjWv%*g`kz9AKIXR%4YpmJ(EhEIOkE zG8%g%4Z|n7E^y1&f=7+ooE!hOVjnDRD8+sYgh`Xs|0?xzYSiy3U={5zbs*CY`!wBP zVRl?f5bUeZ-S0EmHb=fsb7p4F^!xYT^Vfg<|F+AQX-#SIIJv>L1s_55!XuQGMCk~iOUDYv5U`3 zt}Ita=V~AJg+M(7aP}jx*+GD2MF60#qUvCUYcAymiS6?a!@!m9J$BR`d4k-barMXJ_ZDPUq&;^Y7htC%_-KV>E{goQ3}MiQ8sp zX9ls8Mfx*c+1%M0Ikovv1Oy|XIbk%dK;w^#WjwB+Tl2dnzdU?&koQwUARunsF-wUt zo0K&WmhYRwX2vS_kjb147|m`3GX3wN5Gc;y#=u5D0)ssUJoe$Rz-JeDWjUrOr%L1+ zK&fRjC19xmsvO&<&84@<%|2wnVqm@-fX!aSSa$&LGwuXn)|cTS0yidNIKyq}LpdQK zXDd}JCc?ll#K&a}H^3{pYoG-}+;TbAF%`T}y_yy1d!V_@ax9{k8o#A}&X^6X7=TFu zx{5hxEXNWmCKb61WpU&7c;I#yx-)CP)I1u@HAo3+4>YHyreS7gp}V_#;wH`Kx-vyN z1O2%(w})!;R*a?tlMZNJyy$F=U}S4FDVu8sgAzco1Y)jgO^uqm5J$ey?T}AA)PZ6pD5E(M2VcA?Nt#V@z7K@o$-7o~m zlmO>(0h^d=J`{h|pce*d97LqB0nK=76~GqYaIdC8j#&kA#R)*}4F=Gs0%>|FwY~5u zCH6MyKpbl-wLFL_`(XBR$mZysVm3z&Hmt5(x#{L?py?Op7)-}#?k=6a)0fR#_h-6n z^H6Q>{WG1~eDX71c2E*9v~8L%1!8js32p$Z1mJ0iSvO`995a(K$c)85oEN3W_mhj2 zoJN?;>4@bFML@BAQ4C0;F^Xi<;-1SGNQQv)D#BiJ2JBLwSPDz2VKFtmXmtX}OkvzB zpj{E-8+h!$*dy3vFBFPXg<`hS|LnrS5_S!EYY2d@Y}zC4Dw0Wl3}~pn*3=Sq?zq_2 zlQ_<&w+zUv1(uR3YXj)i>YA}!<>(@ z`Oj4YwgH-EVBTI2Esqvk#Zfw-IXyjR>iYKEG@nCJqc58~p!t!x-~Y$mg;NXD(}nso z9nkb?Ku&G$%jS;FtXqTa_ZM(1#f-KQJaB_s8*^PyFn%19NdYv9#f;fTK-kX^oJnPK zO!!klnvWX*T`u=vGGsC5BET8;V$MXMGX}pHlNp1@s{yd|Aksgsj2=7D^KSHrmk|R_ z41i)cW$JH~X*_1XgfW9yViD!*N%fp!tp(U)m-t*?0GECr`hC3JJhn#9+oRX62FtIB z^B6mqE1&}iI>cvVQ5uzqMfu8Bz>DOxz)x}@NM-Y+G0Q5(TFwg-JFg)Eo{~M3#Aafw zuTij*?TfM8k_A%A{gD#-q@E*f?%OEg;GgM61@DKU_=LjbL~7n3U;0;F5HXnHNVo-?i4J1Cgdlw(RU>{Y}PVW!^@ z0e~q2YO%r`e?!GCk_gyyeXN%=BLDWGhC~ zF_*4v?)o#gww&60Vr!%s3=Qy}Wm?c_m>YEIbx!$Qn{s*FJDH+1$4siN0A?{JlX_I3 z_bQaY2q*rT3mD58pv~DUhQ-8`V=);abewQ$&gzZ2a7Eb@B{ZY!V@V#4=#7_d)`KQ#epN;66+ zlX)p}SIdHhEeR>&AkIsaV=rw2IFmBeF+fwpVNX^7++dv}3$XR66qO!MX|82DIpE8g zz^91MQq<*IpU|WdelV!p9Sf$xT=rt~{BAKn&Dq&mbNSLuw`4Jzp;7zp!s)ZKvvXzo zGv9vOmCfrx*?fC@C_d(4;#HFw1f)ReL4c4(*f&Q!Qlj<7?gV^G~Op229$IXDjS>{ zy8^slvkj}6I}t9KrOuVisa{QuVZDa2*8n2}x<1VsVXHZ^LlXQ{46usd%{65Rpk<1T@^=tG zW(+2^=|4s>I8xsmGnump$P7#8p?~I7w2tw2#=VgkfD8pdaS8ou@Ot?F@N-n(B^7n2 zeE{HP$UbuOj@JP*iN`FOuv8-qjC1{(RPSexS+<55rc#=&jwS~Fc_%nNp-oYIZpBGa z-3Xjg6E3XZQ^WjoJ)4zqeIAEuleTA6ilgKJruKVsD@_LUC3&sWf+lE#5?~sJ9N?JH z)OccP-7qHz&38%6WE=Mwtpkk>2KPbvd@wlJq0P~T9i#-=cMA1s&NW+j+bw_fmmmFa zlNimgbY6FtZl0N$wo_A6W%@IRWpkefH0jT5Gsv|a2um>oIqRtDGf6dNr}E4-y_!^s zgvv9gl;stqWtnS4OjKXyTmU$y2^O=;+q*{QAp zxyI|-$qU9(Rtn&06(H9n0Cl5aZVzp%-7iHA;wZN)mh%7sQqZrK#lsDeqfi zFsautF291qwiA--ToM9tjqn=K9D~f5%?!mqu}5aOZdk9GjhMY%hRwVh{Y?UxJ%+Ub z&{?ubg$4t3NORNqBUZ~eC0x&U#6F^p{b2nYd?-lDo!$dgLYaKYO0`CMYU{A5X z!$}Rsx?c_M68@a27d16tdumYXNh~9M#R%5bb#t99V*XV9Hkjv z%zJ&BBj7omeMb)ZG|kr5O{dgA^B_u{f!(D$?wFpQEyHFyTcZmX>b?HV&_DD24l=n$ zYMU|mwVjD`HW+PUFg2cMT17U1F8v4V_o4=;cqSeLKCVnImCTJ|<{bm!Zop=SKAEvs zX6%{SLH0!~a>05Jdjhb;z$n&#sOde+NspmQg?;9InE$2$+9n8)=M;ZNt2~cwbe>uT z*t80^Fa!pnph+qn#u20#3*3Rjs_3{4fC3{O;aF;n%*S0mH<@E+ia0a2G0H8UOXls= z->T@d6~jzI#HLf{Kh|<#g)y%p#z3nCnbP>GRfk()j7|l1IRuN%_rd90EDV?rXzES?iB zNq>{psYk3^4Y;E~Xi*ia*29TZEG(Ns&Uvm?g}Qd)ssJSnqCyW+ssrOD;rT7Wha& z%41WyQDF?cg@ZOlRRs8|XnQEIjd@$z;Pcq=htWjBBI^}1;TS0LBAp7kUMpsTiur6} z4g_7`6KU!~liZ`IMp59hV$Mm7*~HEPPP{{j*dz-eO{EJ<2+|69G?N9*L_!kGbgr2WoOqV`;k%rXq!|b?1HHXK%!BaQ`SZC%`+aHIt(D7z@)~? zNXuYiA|TAlXbxeTy21N9$V}TrtX3@kxr%_!t8ln<9s|wL!?6#v`7}2%(y$UCaEx!D zeZZB~p@mN$dA(*n6NBVB0<@smF3$8H5M~Gi&N0hV3lqw?mvNUkuWiJf?;;bhu$K}5 z7hm-eAQZQ1#Eg2ZMDHMAsEe?>9pt-8_KoK*Lo*@b000$1sqvYmNC}&`&W4!yQy`Md zJO_cv)DQ?7aHs{SMoDfM0l1X}tDpqr(lOGk%ib{=oKVd_{ZXIh_NaV*p|v$j-)qvx6{g9b{jJ=YRZt;j*<1T*-;LX8cXbW2gFwr9Mq8 z0uls9Q_H+l2Irda1dMH~nDJh+IHD%vZSJS3U@g_G9Ry~VtKg-8QJXl99yqzYpXTLZ z`Fzl;xsna=LYt$_%O_8YKyxtR3>4?P3wNDyK(mMt$j44@Zo(wrJaf?F1~NOQ?>C^! zbG8L8dX0HI6*ozNPEM3V4QO2Y52rAfVkRYq76X}{#q_i7bFv7gS*>6;Y;1m*=O&y z<{Ul8T66CGuYdiwG3MN-oUXM>tM=JzughHXGR8N)|Np+v68=~bcBKQi#@0pH7XVqw zJpryu#sIvxXQ!}D$;oI2X42^y#;F0m1V&5DlE?xO*o+Rq3viug_~h2TcCt2_4K!bVd1q@h<(p^!%)USNXO>RFp0Y=+3NAQ4uVetOx!A2$ zE;s61EmK_0^xbzR`u?-ydn@R#8N!c!`b0g@5|tM6T6I+H;*c!5#`y|*zq7}q6^l~$z-g>&beF|QOO%sOW?xtIMkFVe1jo>PKe zS|}mtEH=&C9{kSdKl1lfMiVCjb*rb|o@?%l^k?QG}j9n8IU=#nVibJVc1Myz!nBh6NC8} zF`66n81B#HA`vmT!Yp7vbMp2I-;(3;Bmlsk`5Q35Tcyih*@nQ6?FWJ%iM_!rbR#e$ zl%NQ3*q53FN`YQOEn+vRXKW;kpy|7u7~ncE*BZxa(sFvkFr`|*CKnGFW{zu1O-Cge z=-@c-G}atI77HbViIf;l?lS}E0w*xGOY5JEPDau@u1eo2f^C`PU8sZ^bDspBZ(^R^ zA21SxP1p_ekA{Z^Ocq{VL=d!EoxtkkZu`zs0Ge44v`Bwu_RXC7 zGmmBSWd^8kroqx0w7eIr*UCZ@0$Dk^QF$*;Y2JX|16tabsWGuQ-3Rw(N(ytzB2n;1 znXJdKZ&ifLKI z9u=mfheJ_KU_eR(hvFsa7@UyvZ^JPC-1SAv?_B!?kbOWE_MBo~^V6LAP%#jt@580J zyqRNla*TT%^RAu8)`F&HPLN_1HD5{!jHUJ}>Wirffk1hUMm}gPXbFh)E{01PTyTI? zxZ&4ay*3kj;~*F=NuTDt8^Vt9@#6|n#NavVU4a9CypIP6iRENEc9OUGPWnCagz?7CF;1_U-GbN*hqW-*xIQfO$VJI89` z>#i4AO=&D#8i1Dcc6uWtxtLTfx>d@wx7f>)@jD0$Knti@p3WX3K_ecl_qN)Wy!#+k z085VXOi*tuKgUMtfH|Eehl>$t4%iGbtNHW-f}qoIe*VleBGBB9Ib+#81I^8ycRfG( zXD-m6nak$=RoQ&XH$MW+5f%o*jSM6#pfX|R!D>-u_1__jM#>8CNkTa!jK=$Ue zR>@ug5KaKXRz|dkP0J(3eRtGw{el6Om!K#8aQ%v~5R>W2JeRCrs3AFLbdK5BBuv~~ z&!;yDs0Te3Fc?2+X<0jexaUfYQsr_8k)q~YxZC@!X@iHG%=2(Xb*5`IR?r(Y>9a0D9UR<*PO8` zC}2_prgJTl=BU&Gv6|Bau@wYOewr^24?bPDYezxQu6%wPHk)sGjs>F=BApcH-*@_% z|25uz_Fk^JF9b9vArNdC&0~M&e8Bi-u%&Y^a_!&N_YDSkq^ygUH!hV08v#f*pj#(- zl;+y@YIa7ah{@!(F))CvBLGq=mrs3}RPe(o%qeiApseeDDzTZj;4oTpMc7Ob0EHAMd4V3k069Vu|J3?16O@&7JoYS>ol4KJtQ!U_bqqjG z3b^$$A*CL&l)dZWupFMq znwim@>g=h#{a^x_vlZvroc*=e`aPreBhV}xrM)@Y3Q`i=Sm4r%o1~fG z0r--+1TZ7`)Q|vj>JVm!sGSk+%cP%8*$$48x&+-f6SIL_W_E36xz`6)nhd!fHw{S6$J(x(!7 z$FSd8Vi3S#8$iQ=EnGrx&_1I~s3Zbfszh$FO|?Kk>(A6Ijw}W%D%Y3AvU*Z{q-$6j z2cs~igR&4W0j1XQo11f98a!fM&z5R^NwHAip1i$a({4h@eVVT=mJqaRPr|0VbC+c_ zvEqDl^0sGhpDb85pYqMI5a`Ha`c}myD#{w>)AQa|B1SD3)gFt=DqB;QQ{Wht!Gi;) z9#2i~xz+&1F|X7&^9-??9MEilFPCG13oVUPut}ez^<;9Ofifu?!|f>p0qE&J6hzYq z&5rq-#;$Q?K#94r`qaRAyt3-pmH}g+W+}9zYic-Sk4dbR3k#VkLl+JHD1*r*y4yW0_`;RI{IJ zOW79FS|T1=YSA&5a0yRvyAA;9*tQrZLC~X}pC$|#4enHL*@Pk6RU0nX51u*znlyT{ zKYhou`QHwlKBKcCBUY|GJ4n?D~3`7Yn;G` z>n)^@Ye_}y$Ov-WDP?awz_|p3_GBTHguh9>aGHBV5I{$S!;E;&Lo>pDnLUi>3Kj&l zu-~uQjps5xe_G~y1%`#hk5QW@tUAZa^hkd1g>mjlSRI9h<^?H|k}IE=v;%mqMQQ#B z;A7rWa*nW5@|d z?h=CD{x+7;occ4jX6?71e{L}$&~<+%Y!iW|viY%eUbiBo?1sn7EjS0L686(W)z4)@ zC14l9*DT(E?O!PYRf)l|K&DqcPfO+#i%FALsKT5^8{%>??Y+E!!+y;*9Hzna4koiN zSkQ2S0SRbv)-0IC+`xW<4Nbe@O12!x^pu{Xz@`V~nvZwnj&gxzwt6 z4kKAJfJ#bP?;vNDm@N^I6*Etf}-<0YvvJ7zIC z-%Q+}DG6*O<7f^zaG(O)+;HzlCs68liN6mn|5K)x>p7JeSzrKE!~CQXpDp?w|6DPT z~>Gr}XX-l6tnqJ2xhS zU7g!|>18WJ>5CI$8gfF={65WVFZR>npKpKqi(mMjkMA*>*V6PAbnV^yQ@;7^viZbl z=6v%>2y|pK>ljRH#XP4(wA6bo*?%MbEgB$B>o?QHMBL=2soAY0AZhT!ljamPiF-3o z1dxK&wK(xcQki2$lLHAj?WYsKhs0)b%yu#+xg<{OUY}RxvBJjP?TnU{!pJ@4< z_H0hyvmrp3F3>viw;p)0%2J-h9)-ELdK3gE&D)zvU*|2=dcrh7VD`EF5`p)(wZIl9 z2m-MEH?{d=E}L0GxXe+4FTrXC;rjMCF9dwB)?@DZ!_l~X381+OZF}$RrI+9PE`a~* z8fZ=)ni*X7?SrSM{>-WFUVY0fW-gmgLZHlM9u?>FR@t5jzf?Swgx^cSq_;Go{aY)q zXbhobH547c)78Ajcs8Inp>yAj6Hib}qbbi^i$tyI@+jpNV7&lBsR&5AE|UT_ zZ+bkg86y(`m{D1rQKqwTf)=lJ7y#dD5d_u~glpJS@}|Hx8joAxGJ)*=;UNP6$`s~H zfyAX*&7$u#o=7RjQDQ;RyxXovbG22Tcm4f)2aM)aI`7+iPn{uaquFd`Hfqm+bG<$a zfl8T0>u+9jUhlw}7Q8^bxw4+##%PpBtg<->%&1l_56+0lr6+{Z#Chh_!;`9p zXcaQk3)$y>*27sl*^gM=OQ*;=N14``$vcVE-mb^x8{m4j*S4EBvBBJ z?Bv+Y77kVXT2f28f$_R_e3vY5ERZS1OO~)@Nnu!{@?6am(`%Uo3_$dd5LQ`T1DDA; z=hQNaR!^+wIW?fNwD0qvr}IjA4%_ni*5)YR?^f@btCXO&4eQ7E9vlG8D@OC=>1Qyb zITL8^`ZKdpdlmv6`!nY;m~RHa^$x0`@^v0I6M#2Z55gcBz@H?+CI4vZbV+CgWU`lR znx_80324&Z%n58#hLwUhuB3bk`#p`43A;!5qxoh^QGXm`(lM+VW<_y^I+y4}#Xpky zI)H9V#r0xh^)v>flG#7)T?+`Lati?*pNPRG2BXGzx)W%V-|U=5-+Nrry(2Dqo7Z)CM?E zR-_!u2yp1{M**Q{V2YrZS*3h)tv{1nrE`LY7BQNfF?$PqmN^w}Ofix;0f_c&a!e){ zD@oZl$up1k6D9&AB^7y3o`-u)dxpiNOl&8xnA&k`n0?A+cyn>2zEO=C@R{nW#?_y8f9xFU@vhGq)a^x1WDUwl-Qs43x{}$3&nv zj~Oj3#n-Z#bC^x5-_h#T;Na2y-hft&&a3@8jBm%zDFdvCCI)3&T(-)e~~_j9FkDK{PiOZu3^YzAeqk>F5` z8x^ogeL$(9JZ1@d1SUHM&~xl@3%1cbwVa8-Tc9LnvCuL5bwkeS4cU(!1AZF>2C?xv z1;reD+zYI9$FP|kgWWR=K#ym!#x6dIML&RU+}lVARs)#+I? zmCk41vJhx8YM+Ea$J;Uk*xdK7$ts@u0AEs%0>Es5SGIG^sq~%_ z_=yNWt`Ml4%H~tKoU<~bV*&;&n8mzgSjsoIvBX$hwIWRMDY{c&FWpbcFR?(igt_?_DvPR~hH=_ML5n(CD8zyLA5WVZQ~M*NXF_5a_=oz*{r8)&Nus zPy*=wl$>CwG$n`wmQ+9z89)lKZx;g$<3`yw&Y+!3|KZq7PKix(%Q*%4hG8vxkUcJG zPbIFjL;^-Rd!vp4O#$Rwc24-yGXPU#F^`(<(0KQL?i{7M3M4SqJL-rB=y<-+f96-c&MO<`b6dp^U_uS(5-U!uz zno3Ry7~pKXNuwI$>gCq z^Y6jmr-Y!(u}|~SY%k4gOFP-QIYkzw*yquaEti)*!Z|D5%EPBNbd=q2jw)tl}K7X)#is$Vsk@ zC-rJ_gH{p-IIM1-01+;qt^^PifbY%h;4l^El3_e)sd@v4@s3#W-NT_jCNN+Uj7bnb zZ-c`ChrW#~kV~zNlkK+7dHS5_xc(^MY=AdEj|SM}_y#H# z;uwC~?-|K8OBLam)ue{$)MATMq0{S*nylly1|%Y>7|=dVPf}2czz1fI!%Kio@4fW> zgYn(H0#}?bCYzOO3dGDDs|lFYBpTjb3NyqTXb}##!S0D&GXT? z{q^B~f0o73wokK-;qvm%?LDBGnar-edvCEspezKM5`m6DlfOty4c2QuqZHK6DVHyq zcwpIESW0p&9Vj|p$N4RC?<?Qs;?u(UuZ^!?qtS3vXHL-U!ZC!jeSY)-aD*+X+@)P4k+(twlRq|Ys7R7*gp zH`g4%&M7WoH6=ey0KQC4RpDCsTvEJK<7xW-SQK#r`(hMMWey-K&85ugjsa}k;~aA+ z2}3a3B~5gc`3eLK;n#CwAWrOq8MhWO!Yu*pG+R6hEG8%bb4l}N59mlBr&>`^ye9jN zD%P4 zJ$-qhU|z6#Gdc32rW%!;<9GtyUWwb51irpZfU>M|&5~&Wu8iGw&v~#^u*sbB(Fz6# z=$AB{Twx0|6QoB8!t1jIK|AvDBy`=qt8}1t5&(5)Z+~0%KU&1BeFrqJjoOclW;wVk z`Mahl1f#4`3wBE;SkFae4K5bI1TxV|ET)uW&ap3CY@wFe!ZA6Kux|L?!l42l;Lw7I zOUL0vKvJn3;IMQ~72;gEyazsR5wn>TXaexIEOgtbQaX}}f>Z2cfO$gpEmt0o4EXml zPd9bVYk*S%NgLqX7kXu4?#a0b%<+xDN6=DlP0odYx(r|zF%m{Pp=*kk;Vp|ZTspVd zV9x3+2rS}FGMXifp9M0(cuWIVL18kj?dKNd4cL9m9IKd|4B`?CKr0!}(~DWU9VNb* zrHhx6zkyJ-&`R}N^Y$)ny)@x`c>H0t#nB`pAKNiQN^gH^4`^l&&2;wMa;ZSqKrBhC0p429TpCIa?NLyR!n` z>IEO84e)kAR18F|iZ~BPE*U5RpA~M|;sgRpd-8h+hy8w?@G~h)@D$ZaOXDjz^c=O) zol5$!=1e9fy>k|;Kb@}ufM(3#^7~f5{J*VEZ!PDciK8bc z#rg3z4~%+Z%a;DT#r~9}9e@HRJ$-YQv34++%)qdcl3fZer2`zM#YlrE$F8&t6GzMD zpsFQA&T( zSmByp+@-=SN|I}UVU{hH6Dr1Etqg7DpnG!8OSUJ0-FxYYr%VpO=>e@Zb7fMFUs~wB z0O~^o)SloBGP^WRG$X6I6wo{kAwIbKr7!;JPi`5_EwF4>CvDqYv6-_N&0HqWZ06&~ z$LPs9t7NX^v-CF15&r=At%?Bbm@&_w5()-uX|=4GwsWT36udUf;6_0c6{S#HqZSU! zyuh%8*kiIoj3$=>F4-1wrs-N~j5ly3FF%~3B)z;ccPT}zFk-$PoM)8~LN?nynIl8u#QM^z%^nbVgV9nY=l zx%B{Rubom!yN~pqs=#QnIm(ku2AYo+>CxPQ&0Fm&ue|#m0RLeJG`o1~_9OyYq-;JZ z&X3WP`lM!+#-B7pTDw}Wc`vIGTmzUAdm%+ng1|m$TL%)m-UX>dyp_g(y87`ZD4h2ARMBPP|`R*ze<;KzW0HV*4WQ zm;uz4(Y&Qqi&3!pzpQ?1%L5V{rPCxH+E*E2Uwz)dz7U359#O!>W!H1A1rheu=hU`{ zE74laqa@&=o+OIE?L0V5hxBWB&yx z>;JUoAWPH~Dvt@wcEW`UPkr-~|)_qbYYwTIaRVLLrzvGn=cVqP;j z55l=FjqwTmy_lBml4UvMVIiNSAaKbj=Ouvh!DC}dELa8NefEN+DKzeJ96Bk{m<=1iz?bJuJWk6;W z7&R>moR+}>cvEd`^iCO1O#nh!%z)mHzK%|ov>%Y``2z+la;)V9G8+YCmHVpg_%8o!(Lo^+&H6f%4&AxICDP%WC2M4-PTE~d`PKX zR!N>R)-)UdZg7k(1%=4~ml9aCY`m;~NIHH;=lEVqMp$kTrTvN?fLg)?dK_7n@ZVk1 z5WWV?P8WaLBnX$2&C%>W&9QtQE-!9vV<>k$n$3d;6VRN^Xl6Du_h(*vXdY2!DJ$aj z#pL>E(!v);DBNxjVD&JMl88rQNGzt)%Z^Y66@V|#Q+7gAnoF7HoCt_o5@EoZ$WP8x zn<~*M^N9f{2NWnE#ij9%0U=?3ykg@LaW7XSEGEbLa!PWohf%t=JPS9iv|Y+za^i>5 z$%_M<6trEA`$zlU)YnvM!|8={Q39Vm@Paa$gN~U^06UF~yWTYOcFV}Uq$F>NzDi;r zJfns$E$fYlDNw zKNEyOM*^M^^h|RwCMC{lrBwhZ9Rz`}BlPnyBZtK-K>%o-z-D%EXm$<+U{ZDyGhUqV z2Y~Ojqu_x8P7YZ0!T_2G(?WY+X%8!9ep`_9yK4dFTmYdlrCQm%bX>sTe8ucr4ghjs zGm_&;ePKBfQ6n&!99xQkrNo9x-z|b23z|S~jq!lYXaH>uT!xwFhc@nc5_Y(1hvlJJ z(xY5sl~m#w0$@9hB;lCH!fhyZKq;N?N`YZ=yobhWCWVnr#-cTpY}=lfUV@bU^Ui0P zwr{7wZI9+E^y%&nXlC!sbn^85rdi0WePT4Te`fB{JOWMs0JkJqodYsG(P7qhTD?{t zkSdkMC39;}X+1J5Db1SZ%IlhXGYRk4fG%d?27U@OxxP)#ld?B{my&=u!4AN6HhBkp zY77TtI7SbH7YYEdas3JU$z_!<2{Hke2bfwwLakvuwbP>J`4m9*%y`oSFCuElIS|Ru z-~gEo$PuO;9IB0_lDQ<(OGZVcrhF~|$uQ$wDgk6AteFPdy~EG0H0V*fI|zTo&k^@@ODR10+1&3z|1EFoKk)?H_%Gu&S~GQ;IJQ4Dwi(- zGUr^s(qzw-s)772EqKh48-co({0)1Sa)4C&{GKeLFp~-7cWRl`3t+Ps&ZDS|D-y`G z*fQx_w)#I)A=tDQ_#SY!C=FoN^o&~IPKutiTt0wd_N_7B(`w~m@M@St z&$T~C&!El2b0aFfjI9UI2K1zWbB-?-GHqWC|Gd5R(Tv~U{2%|gZC9`uqnSbGE_(9F zXx7F=&1EW6#lTbY{;<(onGj*-PVo^a4 zW+m4QKuXCO_y28= zW0po;f ztI73nTSCE7=Bmd2%G*ZdDVbY~^OCVSG2>-W#!*d_St{i5w3T>$n`@8T-0+f`*DlfG zlb|ewMSQ>MBF>Y|7ky3oFE8;Oqysz^Q5s~Cbl%{ zfLDfdfJKU+!V|pa#kB8aB{f-TDoTkJadc0@}yJyCWhMbGEY|aUTBzE2M z(*y^TDW%g`V}_(m??JXX(prKnD>|thSp!lu(3PORRYdEfq~4gIfWQ)wPc3+=@zu0I zwYML(WIn8A8*3LdS_ni*PpBABu`Qcri=)hF&W1Y^(2Q}_zVz~Yo^4ZTPhwn5aekc$ zlo`z<(6m_>3tW2AO04|dnw6c`#5NfN6o9WZ5|qhR>cIlx_vl!0OROaaPNnU}i9nHI zoaj9|*vwu5kzDEz1&?&1r}^YuFJDKtg_^f>ObhN)rQjRyyA>Sv!Q$-l7Gygb?K<+5 z6}jhN(2;NLB$J%}o>@vfVvazQ-a6oWWUt~LhkEasTxM zyh`P2YDGlBC-yq!KxI_e%wAZa^a5zKqGwAOP08lW^3>E8=_QO)gz}`GWB>qg{@se0 z44@}Xacv3FvENJYRwYYgWj%ntmgTHfpx69e+ji?QPsD9)D%x(mf-B|umeuTq6QP{d z?EAK9`*6CN9-Muf{4sd-Aag|!q4N1wsm8kSEf6YsVgks$mtYKPno1r2 ztyLx}>7wS|3zyJa!j^IaJY^#{2(+w$SC7+jC={7G#i42-6F~RZg$h0-++IWt;=pzM8?g-J0?{L0K!71;k3`y;3&1l;!lyld14K zLFIGze&4e=f*sJzeVVtbnMdrUnflX{0cf_TajC}CU4Le}>d!oywcCTql96Xnd7)Wl z?~)auRazS1~(CH(}5;I(B&+kiNR({P1~k;c7l7^ zXEK^QTcgR^=*Va;4@^r3z><9DlGSpFscuPwx&=U`UQNlUT{CO91U`6>Z3I>c6h@Xa zDd*CFaQPM68+8Og(u5xhFo9tyJ2;FUr2U#3hFt{)B+xA5#DGky?8l(9fkU(W2J{Mz zC+nGGX{ps#KZ3@q?Vd{JBcR*@%@J&_#(!^Zsp1Z3o@NjU*UI_n7;Q!t^P=AY&dh4& z`(}842A>T%j(ZGeN(VsD)1!*PW(S7`>>V7I&?zvdX6@YLngU%C1>rJ&N6r;5D_JTE z@_RuJ(8d*iGPz2gQE~W1r@>d_J#F6Jg5G_a~XrY?cF>yZ+3hwNdE_ohqL1O@)w} z>C?=ME(j~(1iq9X2{>vcF^M&e%BtCuEX1i+ff;0hVLhmAQbSlh4%kxAgiGcdfwfEw zLy3C>0S-MlJ2+IO)6dmXWdP*(I|�r4vqggU&a{!Z;_c?}k>Xn?pcZ1606Q~R$ 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%0Adh;ycK}XpES)0?nv?hw6qc!^ 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 LNVHN!-~ayrzhB6! literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-clear.png b/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-clear.png new file mode 100644 index 0000000000000000000000000000000000000000..c66f1c9309cce5f54b6345d84a5bb4efbec30a82 GIT binary patch literal 39074 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%0Dwjh0N}037W8g|D4}N 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 kWRgiHc{?~s(fh($ literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-spin.png b/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinner-spin.png new file mode 100644 index 0000000000000000000000000000000000000000..98a9991c2f103308706ea3c52baf0b3e90ef5a11 GIT binary patch literal 21353 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

SjXN<|hNt%y;m$|%aMvI6 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 literal 0 HcmV?d00001 diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinnerbonus.wav b/osu.Game.Rulesets.Osu.Tests/Resources/old-skin/spinnerbonus.wav new file mode 100644 index 0000000000000000000000000000000000000000..5e583e77aa7441b968e4ebc8de47da1033206c29 GIT binary patch literal 309536 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

}JOkaT^af>To^nKaittEF$R(n9ODq-t4bDj? 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�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

942hnQeXd_w3}~ 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 background.AccentColour; - set => background.AccentColour = value; + get => accentColour; + set + { + accentColour = value; + if (background.Drawable is IHasAccentColour accent) + accent.AccentColour = value; + } } - private readonly SpinnerBackground background; + private readonly SkinnableDrawable background; private const float idle_alpha = 0.2f; private const float tracking_alpha = 0.4f; @@ -37,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Children = new Drawable[] { - background = new SpinnerBackground { Alpha = idle_alpha }, + background = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerDisc), _ => new SpinnerBackground { Alpha = idle_alpha }), }; } @@ -54,7 +62,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces tracking = value; - background.FadeTo(tracking ? tracking_alpha : idle_alpha, 100); + // todo: new default only + background.Drawable.FadeTo(tracking ? tracking_alpha : idle_alpha, 100); } } @@ -121,11 +130,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces if (Complete && updateCompleteTick()) { - background.FinishTransforms(false, nameof(Alpha)); - background - .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) - .Then() - .FadeTo(tracking_alpha, 250, Easing.OutQuint); + // todo: new default only + background.Drawable.FinishTransforms(false, nameof(Alpha)); + background.Drawable + .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) + .Then() + .FadeTo(tracking_alpha, 250, Easing.OutQuint); } Rotation = (float)Interpolation.Lerp(Rotation, currentRotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index b2cdc8ccbf..e25b4a5efc 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -17,5 +17,6 @@ namespace osu.Game.Rulesets.Osu SliderFollowCircle, SliderBall, SliderBody, + SpinnerDisc } } diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 95ef2d58b1..c5b5598be7 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -4,6 +4,7 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Game.Skinning; using osuTK; @@ -102,6 +103,14 @@ namespace osu.Game.Rulesets.Osu.Skinning Scale = new Vector2(0.8f), Spacing = new Vector2(-overlap, 0) }; + + case OsuSkinComponents.SpinnerDisc: + if (Source.GetTexture("spinner-background") != null) + return new Sprite { Texture = Source.GetTexture("spinner-circle") }; + else if (Source.GetTexture("spinner-top") != null) + return new Sprite { Texture = Source.GetTexture("spinner-top") }; + + return null; } return null; From e5ebd211569f8f0fe050142272c6e52ca64fb4b1 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Jul 2020 16:25:17 +0900 Subject: [PATCH 066/185] Fix test scene and add pooling support --- .../Skinning/TestSceneHitExplosion.cs | 50 ++++++++++++------- 1 file changed, 33 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs index a692c0b697..0c56f7bcf4 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneHitExplosion.cs @@ -1,23 +1,27 @@ // 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.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects.Drawables.Pieces; using osu.Game.Rulesets.Mania.UI; -using osu.Game.Skinning; +using osu.Game.Rulesets.Objects; using osuTK; -using osuTK.Graphics; namespace osu.Game.Rulesets.Mania.Tests.Skinning { [TestFixture] public class TestSceneHitExplosion : ManiaSkinnableTestScene { + private readonly List> hitExplosionPools = new List>(); + public TestSceneHitExplosion() { int runcount = 0; @@ -29,28 +33,40 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning if (runcount % 15 > 12) return; - CreatedDrawables.OfType().ForEach(c => + int poolIndex = 0; + + foreach (var c in CreatedDrawables.OfType()) { - c.Add(new SkinnableDrawable(new ManiaSkinComponent(ManiaSkinComponents.HitExplosion, 0), - _ => new DefaultHitExplosion((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, - })); - }); + c.Add(hitExplosionPools[poolIndex].Get(e => + { + e.Apply(new JudgementResult(new HitObject(), runcount % 6 == 0 ? new HoldNoteTickJudgement() : new ManiaJudgement())); + + e.Anchor = Anchor.Centre; + e.Origin = Anchor.Centre; + })); + + poolIndex++; + } }, 100); } [BackgroundDependencyLoader] private void load() { - SetContents(() => new ColumnTestContainer(0, ManiaAction.Key1) + SetContents(() => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - RelativePositionAxes = Axes.Y, - Y = -0.25f, - Size = new Vector2(Column.COLUMN_WIDTH, DefaultNotePiece.NOTE_HEIGHT), + var pool = new DrawablePool(5); + hitExplosionPools.Add(pool); + + return new ColumnTestContainer(0, ManiaAction.Key1) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativePositionAxes = Axes.Y, + Y = -0.25f, + Size = new Vector2(Column.COLUMN_WIDTH, DefaultNotePiece.NOTE_HEIGHT), + Child = pool + }; }); } } From 5439099b7cb23253179c270b9f4ddc2002163ded Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 29 Jul 2020 10:34:09 +0300 Subject: [PATCH 067/185] Merge GlobalSkinConfiguration settings into the LegacySetting enum --- .../Gameplay/TestSceneHitObjectSamples.cs | 8 ++++---- .../Objects/Legacy/ConvertHitObjectParser.cs | 2 +- osu.Game/Skinning/GlobalSkinConfiguration.cs | 11 ----------- osu.Game/Skinning/LegacySkin.cs | 14 ++++---------- osu.Game/Skinning/LegacySkinConfiguration.cs | 2 ++ osu.Game/Skinning/LegacySkinExtensions.cs | 3 ++- osu.Game/Skinning/LegacySkinTransformer.cs | 3 ++- 7 files changed, 15 insertions(+), 28 deletions(-) delete mode 100644 osu.Game/Skinning/GlobalSkinConfiguration.cs diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index 737946e1e0..583400f579 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -6,9 +6,9 @@ using osu.Framework.IO.Stores; using osu.Framework.Testing; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; -using osu.Game.Skinning; using osu.Game.Tests.Beatmaps; using osu.Game.Tests.Resources; +using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Tests.Gameplay { @@ -190,7 +190,7 @@ namespace osu.Game.Tests.Gameplay } ///

- /// Tests that when a custom sample bank is used, but is disabled, + /// Tests that when a custom sample bank is used, but is disabled, /// only the additional sound will be looked up. /// [Test] @@ -209,7 +209,7 @@ namespace osu.Game.Tests.Gameplay } /// - /// Tests that when a normal sample bank is used and is disabled, + /// Tests that when a normal sample bank is used and is disabled, /// the normal sound will be looked up anyway. /// [Test] @@ -226,6 +226,6 @@ namespace osu.Game.Tests.Gameplay } private void disableLayeredHitSounds() - => AddStep("set LayeredHitSounds to false", () => Skin.Configuration.ConfigDictionary[GlobalSkinConfiguration.LayeredHitSounds.ToString()] = "0"); + => AddStep("set LayeredHitSounds to false", () => Skin.Configuration.ConfigDictionary[LegacySetting.LayeredHitSounds.ToString()] = "0"); } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 77075b2abe..9afc0ecaf4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -434,7 +434,7 @@ namespace osu.Game.Rulesets.Objects.Legacy ///
/// /// Layered hit samples are automatically added in all modes (except osu!mania), but can be disabled - /// using the skin config option. + /// using the skin config option. /// public bool IsLayered { get; set; } } diff --git a/osu.Game/Skinning/GlobalSkinConfiguration.cs b/osu.Game/Skinning/GlobalSkinConfiguration.cs deleted file mode 100644 index d405702ea5..0000000000 --- a/osu.Game/Skinning/GlobalSkinConfiguration.cs +++ /dev/null @@ -1,11 +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.Skinning -{ - public enum GlobalSkinConfiguration - { - AnimationFramerate, - LayeredHitSounds, - } -} diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 3bbeff9918..5843cde94d 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -120,15 +120,6 @@ namespace osu.Game.Skinning break; - case LegacySkinConfiguration.LegacySetting legacy: - switch (legacy) - { - case LegacySkinConfiguration.LegacySetting.Version: - return SkinUtils.As(new Bindable(Configuration.LegacyVersion ?? LegacySkinConfiguration.LATEST_VERSION)); - } - - break; - case SkinCustomColourLookup customColour: return SkinUtils.As(getCustomColour(Configuration, customColour.Lookup.ToString())); @@ -142,8 +133,11 @@ namespace osu.Game.Skinning break; + case LegacySkinConfiguration.LegacySetting s when s == LegacySkinConfiguration.LegacySetting.Version: + return SkinUtils.As(new Bindable(Configuration.LegacyVersion ?? LegacySkinConfiguration.LATEST_VERSION)); + default: - // handles lookups like GlobalSkinConfiguration + // handles lookups like some in LegacySkinConfiguration.LegacySetting try { diff --git a/osu.Game/Skinning/LegacySkinConfiguration.cs b/osu.Game/Skinning/LegacySkinConfiguration.cs index 027f5b8883..41b7aea34b 100644 --- a/osu.Game/Skinning/LegacySkinConfiguration.cs +++ b/osu.Game/Skinning/LegacySkinConfiguration.cs @@ -15,6 +15,8 @@ namespace osu.Game.Skinning public enum LegacySetting { Version, + AnimationFramerate, + LayeredHitSounds, } } } diff --git a/osu.Game/Skinning/LegacySkinExtensions.cs b/osu.Game/Skinning/LegacySkinExtensions.cs index 7cf41ef3c1..bb46dc8b9f 100644 --- a/osu.Game/Skinning/LegacySkinExtensions.cs +++ b/osu.Game/Skinning/LegacySkinExtensions.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; +using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Skinning { @@ -89,7 +90,7 @@ namespace osu.Game.Skinning { if (applyConfigFrameRate) { - var iniRate = source.GetConfig(GlobalSkinConfiguration.AnimationFramerate); + var iniRate = source.GetConfig(LegacySetting.AnimationFramerate); if (iniRate?.Value > 0) return 1000f / iniRate.Value; diff --git a/osu.Game/Skinning/LegacySkinTransformer.cs b/osu.Game/Skinning/LegacySkinTransformer.cs index 786056b932..ebc4757e75 100644 --- a/osu.Game/Skinning/LegacySkinTransformer.cs +++ b/osu.Game/Skinning/LegacySkinTransformer.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.OpenGL.Textures; using osu.Framework.Graphics.Textures; using osu.Game.Audio; using osu.Game.Rulesets.Objects.Legacy; +using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Skinning { @@ -38,7 +39,7 @@ namespace osu.Game.Skinning if (!(sampleInfo is ConvertHitObjectParser.LegacyHitSampleInfo legacySample)) return Source.GetSample(sampleInfo); - var playLayeredHitSounds = GetConfig(GlobalSkinConfiguration.LayeredHitSounds); + var playLayeredHitSounds = GetConfig(LegacySetting.LayeredHitSounds); if (legacySample.IsLayered && playLayeredHitSounds?.Value == false) return new SampleChannelVirtual(); From e98154b43277b726e286216b139ab9923e7806da Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 16:37:23 +0900 Subject: [PATCH 068/185] Add initial support for spinner background layer --- .../Objects/Drawables/DrawableSpinner.cs | 11 ++++++----- .../Objects/Drawables/Pieces/SpinnerDisc.cs | 2 +- .../Pieces/{SpinnerBackground.cs => SpinnerFill.cs} | 4 ++-- osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 3 ++- .../Skinning/OsuLegacySkinTransformer.cs | 6 ++++++ 5 files changed, 17 insertions(+), 9 deletions(-) rename osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/{SpinnerBackground.cs => SpinnerFill.cs} (92%) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index ac0df0aef6..37601d9680 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Objects; using osu.Framework.Utils; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -34,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly Container mainContainer; - public readonly SpinnerBackground Background; + public readonly SkinnableDrawable Background; private readonly Container circleContainer; private readonly CirclePiece circle; private readonly GlowPiece glow; @@ -96,7 +97,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativeSizeAxes = Axes.Y, Children = new[] { - Background = new SpinnerBackground + Background = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBackground), _ => new SpinnerFill { Disc = { @@ -104,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }, Anchor = Anchor.Centre, Origin = Anchor.Centre, - }, + }), Disc = new SpinnerDisc(Spinner) { Scale = Vector2.Zero, @@ -173,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables normalColour = baseColour; completeColour = colours.YellowLight; - Background.AccentColour = normalColour; + if (Background.Drawable is IHasAccentColour accent) accent.AccentColour = normalColour; Ticks.AccentColour = normalColour; Disc.AccentColour = fillColour; @@ -328,7 +329,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { Disc.FadeAccent(colour, duration); - Background.FadeAccent(colour.Darken(1), duration); + (Background.Drawable as IHasAccentColour)?.FadeAccent(colour.Darken(1), duration); Ticks.FadeAccent(colour, duration); circle.FadeColour(colour, duration); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs index 86588a8a9f..22a6fc391a 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces Children = new Drawable[] { - background = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerDisc), _ => new SpinnerBackground { Alpha = idle_alpha }), + background = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerDisc), _ => new SpinnerFill { Alpha = idle_alpha }), }; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs similarity index 92% rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs rename to osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs index 944354abca..dbba1044ca 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerBackground.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs @@ -10,7 +10,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class SpinnerBackground : CircularContainer, IHasAccentColour + public class SpinnerFill : CircularContainer, IHasAccentColour { public readonly Box Disc; @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - public SpinnerBackground() + public SpinnerFill() { RelativeSizeAxes = Axes.Both; Masking = true; diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index e25b4a5efc..d72673f9ee 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -17,6 +17,7 @@ namespace osu.Game.Rulesets.Osu SliderFollowCircle, SliderBall, SliderBody, - SpinnerDisc + SpinnerDisc, + SpinnerBackground } } diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index c5b5598be7..28eef603f4 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -111,6 +111,12 @@ namespace osu.Game.Rulesets.Osu.Skinning return new Sprite { Texture = Source.GetTexture("spinner-top") }; return null; + + case OsuSkinComponents.SpinnerBackground: + if (Source.GetTexture("spinner-background") != null) + return new Sprite { Texture = Source.GetTexture("spinner-background") }; + + return null; } return null; From 5df406a0352285c871ed8606e54fae1c03e07a11 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 29 Jul 2020 16:41:10 +0900 Subject: [PATCH 069/185] Add pooling for mania judgements --- .../UI/DrawableManiaJudgement.cs | 4 ++++ osu.Game.Rulesets.Mania/UI/Stage.cs | 16 ++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs index 8797f014df..d99f6cb8d3 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaJudgement.cs @@ -15,6 +15,10 @@ namespace osu.Game.Rulesets.Mania.UI { } + public DrawableManiaJudgement() + { + } + [BackgroundDependencyLoader] private void load() { diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index faa04dea97..36780b0f80 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; @@ -33,8 +34,8 @@ namespace osu.Game.Rulesets.Mania.UI public IReadOnlyList Columns => columnFlow.Children; private readonly FillFlowContainer columnFlow; - public Container Judgements => judgements; private readonly JudgementContainer judgements; + private readonly DrawablePool judgementPool; private readonly Drawable barLineContainer; private readonly Container topLevelContainer; @@ -63,6 +64,7 @@ namespace osu.Game.Rulesets.Mania.UI InternalChildren = new Drawable[] { + judgementPool = new DrawablePool(2), new Container { Anchor = Anchor.TopCentre, @@ -208,12 +210,14 @@ namespace osu.Game.Rulesets.Mania.UI if (!judgedObject.DisplayResult || !DisplayJudgements.Value) return; - judgements.Clear(); - judgements.Add(new DrawableManiaJudgement(result, judgedObject) + judgements.Clear(false); + judgements.Add(judgementPool.Get(j => { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }); + j.Apply(result, judgedObject); + + j.Anchor = Anchor.Centre; + j.Origin = Anchor.Centre; + })); } protected override void Update() From 1c00cf95d5f7cb838bd72f2dbf09fd4a845c1422 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 16:55:42 +0900 Subject: [PATCH 070/185] Add initial support for spinner middle skinning --- .../Objects/Drawables/DrawableSpinner.cs | 35 ++++++++++++------- osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 3 +- .../Skinning/OsuLegacySkinTransformer.cs | 8 +++++ 3 files changed, 32 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 37601d9680..d6914c7a69 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Objects; using osu.Framework.Utils; @@ -36,11 +37,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly Container mainContainer; public readonly SkinnableDrawable Background; - private readonly Container circleContainer; - private readonly CirclePiece circle; - private readonly GlowPiece glow; + private readonly SkinnableDrawable circleContainer; + private CirclePiece circle; + private GlowPiece glow; - private readonly SpriteIcon symbol; + private SpriteIcon symbol; private readonly Color4 baseColour = Color4Extensions.FromHex(@"002c3c"); private readonly Color4 fillColour = Color4Extensions.FromHex(@"005b7c"); @@ -66,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables InternalChildren = new Drawable[] { ticks = new Container(), - circleContainer = new Container + circleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerCentre), _ => new Container { AutoSizeAxes = Axes.Both, Anchor = Anchor.Centre, @@ -89,6 +90,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Shadow = false, }, } + }) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }, mainContainer = new AspectContainer { @@ -175,11 +180,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables completeColour = colours.YellowLight; if (Background.Drawable is IHasAccentColour accent) accent.AccentColour = normalColour; + Ticks.AccentColour = normalColour; Disc.AccentColour = fillColour; - circle.Colour = colours.BlueDark; - glow.Colour = colours.BlueDark; + if (circle != null) circle.Colour = colours.BlueDark; + if (glow != null) glow.Colour = colours.BlueDark; positionBindable.BindValueChanged(pos => Position = pos.NewValue); positionBindable.BindTo(HitObject.PositionBindable); @@ -224,6 +230,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false; } + private float relativeHeight => ToScreenSpace(new RectangleF(0, 0, OsuHitObject.OBJECT_RADIUS, OsuHitObject.OBJECT_RADIUS)).Height / mainContainer.DrawHeight; + protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); @@ -231,18 +239,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!SpmCounter.IsPresent && Disc.Tracking) SpmCounter.FadeIn(HitObject.TimeFadeIn); - circle.Rotation = Disc.Rotation; + if (circle != null) circle.Rotation = Disc.Rotation; Ticks.Rotation = Disc.Rotation; SpmCounter.SetRotation(Disc.CumulativeRotation); updateBonusScore(); - float relativeCircleScale = Spinner.Scale * circle.DrawHeight / mainContainer.DrawHeight; + float relativeCircleScale = Spinner.Scale * relativeHeight; float targetScale = relativeCircleScale + (1 - relativeCircleScale) * Progress; Disc.Scale = new Vector2((float)Interpolation.Lerp(Disc.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); - symbol.Rotation = (float)Interpolation.Lerp(symbol.Rotation, Disc.Rotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); + if (symbol != null) + symbol.Rotation = (float)Interpolation.Lerp(symbol.Rotation, Disc.Rotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); } private int wholeSpins; @@ -291,7 +300,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables circleContainer.ScaleTo(phaseOneScale, HitObject.TimePreempt / 4, Easing.OutQuint); mainContainer - .ScaleTo(phaseOneScale * circle.DrawHeight / DrawHeight * 1.6f, HitObject.TimePreempt / 4, Easing.OutQuint) + .ScaleTo(phaseOneScale * relativeHeight * 1.6f, HitObject.TimePreempt / 4, Easing.OutQuint) .RotateTo((float)(25 * Spinner.Duration / 2000), HitObject.TimePreempt + Spinner.Duration); using (BeginDelayedSequence(HitObject.TimePreempt / 2, true)) @@ -332,8 +341,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables (Background.Drawable as IHasAccentColour)?.FadeAccent(colour.Darken(1), duration); Ticks.FadeAccent(colour, duration); - circle.FadeColour(colour, duration); - glow.FadeColour(colour, duration); + circle?.FadeColour(colour, duration); + glow?.FadeColour(colour, duration); } } } diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index d72673f9ee..c05dbf6b16 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Osu SliderBall, SliderBody, SpinnerDisc, - SpinnerBackground + SpinnerBackground, + SpinnerCentre } } diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 28eef603f4..a5198d3448 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -117,6 +117,14 @@ namespace osu.Game.Rulesets.Osu.Skinning return new Sprite { Texture = Source.GetTexture("spinner-background") }; return null; + + case OsuSkinComponents.SpinnerCentre: + if (Source.GetTexture("spinner-background") != null) + return Drawable.Empty(); + else if (Source.GetTexture("spinner-top") != null) + return new Sprite { Texture = Source.GetTexture("spinner-middle2") }; + + return null; } return null; From 2cd6e89cb05ba3cea8854dad77c614c4fd95cb63 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 18:02:12 +0900 Subject: [PATCH 071/185] Move default centre implementation out of DrawableSpinner --- .../Objects/Drawables/DefaultSpinnerCentre.cs | 83 +++++++++++++++++++ .../Objects/Drawables/DrawableSpinner.cs | 40 +-------- 2 files changed, 84 insertions(+), 39 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerCentre.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerCentre.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerCentre.cs new file mode 100644 index 0000000000..d07829fbac --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerCentre.cs @@ -0,0 +1,83 @@ +// 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.Sprites; +using osu.Framework.Utils; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DefaultSpinnerCentre : CompositeDrawable + { + private DrawableSpinner spinner; + + private CirclePiece circle; + private GlowPiece glow; + private SpriteIcon symbol; + + [BackgroundDependencyLoader] + private void load(OsuColour colours, DrawableHitObject drawableHitObject) + { + spinner = (DrawableSpinner)drawableHitObject; + + AutoSizeAxes = Axes.Both; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + InternalChildren = new Drawable[] + { + glow = new GlowPiece(), + circle = new CirclePiece + { + Position = Vector2.Zero, + Anchor = Anchor.Centre, + }, + new RingPiece(), + symbol = new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(48), + Icon = FontAwesome.Solid.Asterisk, + Shadow = false, + }, + }; + + drawableHitObject.State.BindValueChanged(val => + { + Color4 colour; + + switch (val.NewValue) + { + default: + colour = colours.BlueDark; + break; + + case ArmedState.Hit: + colour = colours.YellowLight; + break; + } + + circle.FadeColour(colour, 200); + glow.FadeColour(colour, 200); + }, true); + + FinishTransforms(true); + } + + protected override void Update() + { + base.Update(); + + circle.Rotation = spinner.Disc.Rotation; + symbol.Rotation = (float)Interpolation.Lerp(symbol.Rotation, spinner.Disc.Rotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index d6914c7a69..b2844c79a2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -14,7 +14,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics.Primitives; -using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Objects; using osu.Framework.Utils; using osu.Game.Rulesets.Scoring; @@ -38,10 +37,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public readonly SkinnableDrawable Background; private readonly SkinnableDrawable circleContainer; - private CirclePiece circle; - private GlowPiece glow; - - private SpriteIcon symbol; private readonly Color4 baseColour = Color4Extensions.FromHex(@"002c3c"); private readonly Color4 fillColour = Color4Extensions.FromHex(@"005b7c"); @@ -67,30 +62,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables InternalChildren = new Drawable[] { ticks = new Container(), - circleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerCentre), _ => new Container - { - AutoSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Children = new Drawable[] - { - glow = new GlowPiece(), - circle = new CirclePiece - { - Position = Vector2.Zero, - Anchor = Anchor.Centre, - }, - new RingPiece(), - symbol = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Size = new Vector2(48), - Icon = FontAwesome.Solid.Asterisk, - Shadow = false, - }, - } - }) + circleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerCentre), _ => new DefaultSpinnerCentre()) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -184,9 +156,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Ticks.AccentColour = normalColour; Disc.AccentColour = fillColour; - if (circle != null) circle.Colour = colours.BlueDark; - if (glow != null) glow.Colour = colours.BlueDark; - positionBindable.BindValueChanged(pos => Position = pos.NewValue); positionBindable.BindTo(HitObject.PositionBindable); } @@ -239,7 +208,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!SpmCounter.IsPresent && Disc.Tracking) SpmCounter.FadeIn(HitObject.TimeFadeIn); - if (circle != null) circle.Rotation = Disc.Rotation; Ticks.Rotation = Disc.Rotation; SpmCounter.SetRotation(Disc.CumulativeRotation); @@ -249,9 +217,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables float relativeCircleScale = Spinner.Scale * relativeHeight; float targetScale = relativeCircleScale + (1 - relativeCircleScale) * Progress; Disc.Scale = new Vector2((float)Interpolation.Lerp(Disc.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); - - if (symbol != null) - symbol.Rotation = (float)Interpolation.Lerp(symbol.Rotation, Disc.Rotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); } private int wholeSpins; @@ -340,9 +305,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables (Background.Drawable as IHasAccentColour)?.FadeAccent(colour.Darken(1), duration); Ticks.FadeAccent(colour, duration); - - circle?.FadeColour(colour, duration); - glow?.FadeColour(colour, duration); } } } From 2a5e9fed4d3f8d3a1cb3ea44f34e30a739459df2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 18:15:19 +0900 Subject: [PATCH 072/185] Move default background implementation out of DrawableSpinner --- .../Drawables/DefaultSpinnerBackground.cs | 44 +++++++++++++++++++ .../Objects/Drawables/DrawableSpinner.cs | 14 +----- 2 files changed, 45 insertions(+), 13 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerBackground.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerBackground.cs new file mode 100644 index 0000000000..be864b8c16 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerBackground.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.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class DefaultSpinnerBackground : SpinnerFill + { + [BackgroundDependencyLoader] + private void load(OsuColour colours, DrawableHitObject drawableHitObject) + { + Disc.Alpha = 0; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + + drawableHitObject.State.BindValueChanged(val => + { + Color4 colour; + + switch (val.NewValue) + { + default: + colour = colours.BlueDark; + break; + + case ArmedState.Hit: + colour = colours.YellowLight; + break; + } + + this.FadeAccent(colour.Darken(1), 200); + }, true); + + FinishTransforms(true); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index b2844c79a2..a0c2a06ff8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -74,15 +74,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativeSizeAxes = Axes.Y, Children = new[] { - Background = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBackground), _ => new SpinnerFill - { - Disc = - { - Alpha = 0f, - }, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }), + Background = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBackground), _ => new DefaultSpinnerBackground()), Disc = new SpinnerDisc(Spinner) { Scale = Vector2.Zero, @@ -151,8 +143,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables normalColour = baseColour; completeColour = colours.YellowLight; - if (Background.Drawable is IHasAccentColour accent) accent.AccentColour = normalColour; - Ticks.AccentColour = normalColour; Disc.AccentColour = fillColour; @@ -302,8 +292,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private void transformFillColour(Colour4 colour, double duration) { Disc.FadeAccent(colour, duration); - - (Background.Drawable as IHasAccentColour)?.FadeAccent(colour.Darken(1), duration); Ticks.FadeAccent(colour, duration); } } From 023feaf438ca028ca0ee44d27d38d86408ed4c29 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 20:01:01 +0900 Subject: [PATCH 073/185] Refactor to centralise implementation into a single component Turns out this is a better way forward. --- .../TestSceneSpinner.cs | 2 +- .../TestSceneSpinnerRotation.cs | 22 +-- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 4 +- osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | 4 +- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 3 +- .../Drawables/DefaultSpinnerBackground.cs | 44 ----- .../Objects/Drawables/DrawableSpinner.cs | 144 +++------------ .../Drawables/Pieces/DefaultSpinnerDisc.cs | 170 ++++++++++++++++++ .../Objects/Drawables/Pieces/SpinnerFill.cs | 3 + ...innerDisc.cs => SpinnerRotationTracker.cs} | 85 ++------- .../Drawables/SpinnerBackgroundLayer.cs | 22 +++ ...SpinnerCentre.cs => SpinnerCentreLayer.cs} | 41 ++--- 12 files changed, 265 insertions(+), 279 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerBackground.cs create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs rename osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/{SpinnerDisc.cs => SpinnerRotationTracker.cs} (59%) create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerBackgroundLayer.cs rename osu.Game.Rulesets.Osu/Objects/Drawables/{DefaultSpinnerCentre.cs => SpinnerCentreLayer.cs} (67%) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 8e3a22bfdc..65b338882e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -61,7 +61,7 @@ namespace osu.Game.Rulesets.Osu.Tests { base.Update(); if (auto) - Disc.Rotate((float)(Clock.ElapsedFrameTime * 3)); + RotationTracker.AddRotation((float)(Clock.ElapsedFrameTime * 3)); } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index c36bec391f..319d326a01 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -61,12 +61,12 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestSpinnerRewindingRotation() { addSeekStep(5000); - AddAssert("is disc rotation not almost 0", () => !Precision.AlmostEquals(drawableSpinner.Disc.Rotation, 0, 100)); - AddAssert("is disc rotation absolute not almost 0", () => !Precision.AlmostEquals(drawableSpinner.Disc.CumulativeRotation, 0, 100)); + AddAssert("is disc rotation not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, 0, 100)); + AddAssert("is disc rotation absolute not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, 0, 100)); addSeekStep(0); - AddAssert("is disc rotation almost 0", () => Precision.AlmostEquals(drawableSpinner.Disc.Rotation, 0, 100)); - AddAssert("is disc rotation absolute almost 0", () => Precision.AlmostEquals(drawableSpinner.Disc.CumulativeRotation, 0, 100)); + AddAssert("is disc rotation almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, 0, 100)); + AddAssert("is disc rotation absolute almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, 0, 100)); } [Test] @@ -75,24 +75,24 @@ namespace osu.Game.Rulesets.Osu.Tests double finalAbsoluteDiscRotation = 0, finalRelativeDiscRotation = 0, finalSpinnerSymbolRotation = 0; addSeekStep(5000); - AddStep("retrieve disc relative rotation", () => finalRelativeDiscRotation = drawableSpinner.Disc.Rotation); - AddStep("retrieve disc absolute rotation", () => finalAbsoluteDiscRotation = drawableSpinner.Disc.CumulativeRotation); + AddStep("retrieve disc relative rotation", () => finalRelativeDiscRotation = drawableSpinner.RotationTracker.Rotation); + AddStep("retrieve disc absolute rotation", () => finalAbsoluteDiscRotation = drawableSpinner.RotationTracker.CumulativeRotation); AddStep("retrieve spinner symbol rotation", () => finalSpinnerSymbolRotation = spinnerSymbol.Rotation); addSeekStep(2500); AddUntilStep("disc rotation rewound", // we want to make sure that the rotation at time 2500 is in the same direction as at time 5000, but about half-way in. - () => Precision.AlmostEquals(drawableSpinner.Disc.Rotation, finalRelativeDiscRotation / 2, 100)); + () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, finalRelativeDiscRotation / 2, 100)); AddUntilStep("symbol rotation rewound", () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation / 2, 100)); addSeekStep(5000); AddAssert("is disc rotation almost same", - () => Precision.AlmostEquals(drawableSpinner.Disc.Rotation, finalRelativeDiscRotation, 100)); + () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, finalRelativeDiscRotation, 100)); AddAssert("is symbol rotation almost same", () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation, 100)); AddAssert("is disc rotation absolute almost same", - () => Precision.AlmostEquals(drawableSpinner.Disc.CumulativeRotation, finalAbsoluteDiscRotation, 100)); + () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, finalAbsoluteDiscRotation, 100)); } [Test] @@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Tests addSeekStep(5000); - AddAssert("disc spin direction correct", () => clockwise ? drawableSpinner.Disc.Rotation > 0 : drawableSpinner.Disc.Rotation < 0); + AddAssert("disc spin direction correct", () => clockwise ? drawableSpinner.RotationTracker.Rotation > 0 : drawableSpinner.RotationTracker.Rotation < 0); AddAssert("spinner symbol direction correct", () => clockwise ? spinnerSymbol.Rotation > 0 : spinnerSymbol.Rotation < 0); } @@ -142,7 +142,7 @@ namespace osu.Game.Rulesets.Osu.Tests { // multipled by 2 to nullify the score multiplier. (autoplay mod selected) var totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2; - return totalScore == (int)(drawableSpinner.Disc.CumulativeRotation / 360) * SpinnerTick.SCORE_PER_TICK; + return totalScore == (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360) * SpinnerTick.SCORE_PER_TICK; }); addSeekStep(0); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index fdba03f260..08fd13915d 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -82,9 +82,7 @@ namespace osu.Game.Rulesets.Osu.Mods case DrawableSpinner spinner: // hide elements we don't care about. - spinner.Disc.Hide(); - spinner.Ticks.Hide(); - spinner.Background.Hide(); + // todo: hide background using (spinner.BeginAbsoluteSequence(fadeOutStartTime + longFadeDuration, true)) spinner.FadeOut(fadeOutDuration); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index 7b54baa99b..47d765fecd 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs @@ -40,8 +40,8 @@ namespace osu.Game.Rulesets.Osu.Mods { var spinner = (DrawableSpinner)drawable; - spinner.Disc.Tracking = true; - spinner.Disc.Rotate(MathUtils.RadiansToDegrees((float)spinner.Clock.ElapsedFrameTime * 0.03f)); + spinner.RotationTracker.Tracking = true; + spinner.RotationTracker.AddRotation(MathUtils.RadiansToDegrees((float)spinner.Clock.ElapsedFrameTime * 0.03f)); } } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 774f9cf58b..f209b315af 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -58,8 +58,7 @@ namespace osu.Game.Rulesets.Osu.Mods break; case DrawableSpinner spinner: - spinner.Disc.Hide(); - spinner.Background.Hide(); + //todo: hide background break; } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerBackground.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerBackground.cs deleted file mode 100644 index be864b8c16..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerBackground.cs +++ /dev/null @@ -1,44 +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.Extensions.Color4Extensions; -using osu.Framework.Graphics; -using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using osuTK.Graphics; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public class DefaultSpinnerBackground : SpinnerFill - { - [BackgroundDependencyLoader] - private void load(OsuColour colours, DrawableHitObject drawableHitObject) - { - Disc.Alpha = 0; - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - - drawableHitObject.State.BindValueChanged(val => - { - Color4 colour; - - switch (val.NewValue) - { - default: - colour = colours.BlueDark; - break; - - case ArmedState.Hit: - colour = colours.YellowLight; - break; - } - - this.FadeAccent(colour.Darken(1), 200); - }, true); - - FinishTransforms(true); - } - } -} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index a0c2a06ff8..11f1afafba 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -3,22 +3,19 @@ using System; using System.Linq; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Objects.Drawables; -using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; -using osuTK; -using osuTK.Graphics; -using osu.Game.Graphics; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; +using osu.Game.Graphics; using osu.Game.Rulesets.Objects; -using osu.Framework.Utils; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -28,24 +25,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private readonly Container ticks; - public readonly SpinnerDisc Disc; - public readonly SpinnerTicks Ticks; + public readonly SpinnerRotationTracker RotationTracker; public readonly SpinnerSpmCounter SpmCounter; private readonly SpinnerBonusDisplay bonusDisplay; - private readonly Container mainContainer; - - public readonly SkinnableDrawable Background; - private readonly SkinnableDrawable circleContainer; - - private readonly Color4 baseColour = Color4Extensions.FromHex(@"002c3c"); - private readonly Color4 fillColour = Color4Extensions.FromHex(@"005b7c"); - private readonly IBindable positionBindable = new Bindable(); - private Color4 normalColour; - private Color4 completeColour; - public DrawableSpinner(Spinner s) : base(s) { @@ -62,27 +47,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables InternalChildren = new Drawable[] { ticks = new Container(), - circleContainer = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerCentre), _ => new DefaultSpinnerCentre()) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - mainContainer = new AspectContainer + RotationTracker = new SpinnerRotationTracker(Spinner), + new AspectContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, - Children = new[] + Scale = new Vector2(Spinner.Scale), + Children = new Drawable[] { - Background = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBackground), _ => new DefaultSpinnerBackground()), - Disc = new SpinnerDisc(Spinner) - { - Scale = Vector2.Zero, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, - circleContainer.CreateProxy(), - Ticks = new SpinnerTicks + new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerDisc), _ => new DefaultSpinnerDisc()), + RotationTracker = new SpinnerRotationTracker(Spinner) { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -117,6 +92,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + protected override void UpdateStateTransforms(ArmedState state) + { + base.UpdateStateTransforms(state); + + using (BeginDelayedSequence(Spinner.Duration, true)) + this.FadeOut(160); + } + protected override void ClearNestedHitObjects() { base.ClearNestedHitObjects(); @@ -140,27 +123,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables [BackgroundDependencyLoader] private void load(OsuColour colours) { - normalColour = baseColour; - completeColour = colours.YellowLight; - - Ticks.AccentColour = normalColour; - Disc.AccentColour = fillColour; - positionBindable.BindValueChanged(pos => Position = pos.NewValue); positionBindable.BindTo(HitObject.PositionBindable); } - public float Progress => Math.Clamp(Disc.CumulativeRotation / 360 / Spinner.SpinsRequired, 0, 1); + public float Progress => Math.Clamp(RotationTracker.CumulativeRotation / 360 / Spinner.SpinsRequired, 0, 1); protected override void CheckForResult(bool userTriggered, double timeOffset) { if (Time.Current < HitObject.StartTime) return; - if (Progress >= 1 && !Disc.Complete) - { - Disc.Complete = true; - transformFillColour(completeColour, 200); - } + RotationTracker.Complete.Value = Progress >= 1; if (userTriggered || Time.Current < Spinner.EndTime) return; @@ -186,27 +159,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.Update(); if (HandleUserInput) - Disc.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false; + RotationTracker.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false; } - private float relativeHeight => ToScreenSpace(new RectangleF(0, 0, OsuHitObject.OBJECT_RADIUS, OsuHitObject.OBJECT_RADIUS)).Height / mainContainer.DrawHeight; + public float RelativeHeight => ToScreenSpace(new RectangleF(0, 0, 0, OsuHitObject.OBJECT_RADIUS * 2)).Height / DrawHeight; protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); - if (!SpmCounter.IsPresent && Disc.Tracking) + if (!SpmCounter.IsPresent && RotationTracker.Tracking) SpmCounter.FadeIn(HitObject.TimeFadeIn); - - Ticks.Rotation = Disc.Rotation; - - SpmCounter.SetRotation(Disc.CumulativeRotation); + SpmCounter.SetRotation(RotationTracker.CumulativeRotation); updateBonusScore(); - - float relativeCircleScale = Spinner.Scale * relativeHeight; - float targetScale = relativeCircleScale + (1 - relativeCircleScale) * Progress; - Disc.Scale = new Vector2((float)Interpolation.Lerp(Disc.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); } private int wholeSpins; @@ -216,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (ticks.Count == 0) return; - int spins = (int)(Disc.CumulativeRotation / 360); + int spins = (int)(RotationTracker.CumulativeRotation / 360); if (spins < wholeSpins) { @@ -240,59 +206,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables wholeSpins++; } } - - protected override void UpdateInitialTransforms() - { - base.UpdateInitialTransforms(); - - circleContainer.ScaleTo(0); - mainContainer.ScaleTo(0); - - using (BeginDelayedSequence(HitObject.TimePreempt / 2, true)) - { - float phaseOneScale = Spinner.Scale * 0.7f; - - circleContainer.ScaleTo(phaseOneScale, HitObject.TimePreempt / 4, Easing.OutQuint); - - mainContainer - .ScaleTo(phaseOneScale * relativeHeight * 1.6f, HitObject.TimePreempt / 4, Easing.OutQuint) - .RotateTo((float)(25 * Spinner.Duration / 2000), HitObject.TimePreempt + Spinner.Duration); - - using (BeginDelayedSequence(HitObject.TimePreempt / 2, true)) - { - circleContainer.ScaleTo(Spinner.Scale, 400, Easing.OutQuint); - mainContainer.ScaleTo(1, 400, Easing.OutQuint); - } - } - } - - protected override void UpdateStateTransforms(ArmedState state) - { - base.UpdateStateTransforms(state); - - using (BeginDelayedSequence(Spinner.Duration, true)) - { - this.FadeOut(160); - - switch (state) - { - case ArmedState.Hit: - transformFillColour(completeColour, 0); - this.ScaleTo(Scale * 1.2f, 320, Easing.Out); - mainContainer.RotateTo(mainContainer.Rotation + 180, 320); - break; - - case ArmedState.Miss: - this.ScaleTo(Scale * 0.8f, 320, Easing.In); - break; - } - } - } - - private void transformFillColour(Colour4 colour, double duration) - { - Disc.FadeAccent(colour, duration); - Ticks.FadeAccent(colour, duration); - } } } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs new file mode 100644 index 0000000000..11cd73b995 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -0,0 +1,170 @@ +// 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; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces +{ + public class DefaultSpinnerDisc : CompositeDrawable + { + private DrawableSpinner drawableSpinner; + + private Spinner spinner; + + private const float idle_alpha = 0.2f; + private const float tracking_alpha = 0.4f; + + private Color4 normalColour; + private Color4 completeColour; + + private SpinnerTicks ticks; + + private int completeTick; + private SpinnerFill fill; + private Container mainContainer; + private SpinnerCentreLayer centre; + private SpinnerBackgroundLayer background; + + public DefaultSpinnerDisc() + { + RelativeSizeAxes = Axes.Both; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + + [BackgroundDependencyLoader] + private void load(OsuColour colours, DrawableHitObject drawableHitObject) + { + drawableSpinner = (DrawableSpinner)drawableHitObject; + spinner = (Spinner)drawableSpinner.HitObject; + + normalColour = colours.BlueDark; + completeColour = colours.YellowLight; + + InternalChildren = new Drawable[] + { + mainContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Children = new Drawable[] + { + background = new SpinnerBackgroundLayer(), + fill = new SpinnerFill + { + Alpha = idle_alpha, + AccentColour = normalColour + }, + ticks = new SpinnerTicks + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AccentColour = normalColour + }, + } + }, + centre = new SpinnerCentreLayer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + centre.ScaleTo(0); + mainContainer.ScaleTo(0); + this.ScaleTo(1); + + drawableSpinner.RotationTracker.Complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200)); + drawableSpinner.State.BindValueChanged(updateStateTransforms, true); + } + + private void updateStateTransforms(ValueChangedEvent state) + { + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt / 2, true)) + { + float phaseOneScale = spinner.Scale * 0.7f; + + centre.ScaleTo(phaseOneScale, spinner.TimePreempt / 4, Easing.OutQuint); + + mainContainer + .ScaleTo(phaseOneScale * drawableSpinner.RelativeHeight * 1.6f, spinner.TimePreempt / 4, Easing.OutQuint); + + this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration); + + using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) + { + centre.ScaleTo(spinner.Scale, spinner.TimePreempt / 2, Easing.OutQuint); + mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint); + } + } + + // transforms we have from completing the spinner will be rolled back, so reapply immediately. + updateComplete(state.NewValue == ArmedState.Hit, 0); + + using (BeginDelayedSequence(spinner.Duration, true)) + { + switch (state.NewValue) + { + case ArmedState.Hit: + this.ScaleTo(Scale * 1.2f, 320, Easing.Out); + this.RotateTo(mainContainer.Rotation + 180, 320); + break; + + case ArmedState.Miss: + this.ScaleTo(Scale * 0.8f, 320, Easing.In); + break; + } + } + } + + private void updateComplete(bool complete, double duration) + { + var colour = complete ? completeColour : normalColour; + + ticks.FadeAccent(colour.Darken(1), duration); + fill.FadeAccent(colour.Darken(1), duration); + + background.FadeAccent(colour, duration); + centre.FadeAccent(colour, duration); + } + + private bool updateCompleteTick() => completeTick != (completeTick = (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360)); + + protected override void Update() + { + base.Update(); + + if (drawableSpinner.RotationTracker.Complete.Value && updateCompleteTick()) + { + fill.FinishTransforms(false, nameof(Alpha)); + fill + .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) + .Then() + .FadeTo(tracking_alpha, 250, Easing.OutQuint); + } + + float relativeCircleScale = spinner.Scale * drawableSpinner.RelativeHeight; + float targetScale = relativeCircleScale + (1 - relativeCircleScale) * drawableSpinner.Progress; + + fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); + mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs index dbba1044ca..043bc5618c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerFill.cs @@ -36,6 +36,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces RelativeSizeAxes = Axes.Both; Masking = true; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + Children = new Drawable[] { Disc = new Box diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs similarity index 59% rename from osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs rename to osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs index 22a6fc391a..968c2a6df5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs @@ -2,85 +2,33 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Game.Graphics; -using osuTK; -using osuTK.Graphics; using osu.Framework.Utils; -using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { - public class SpinnerDisc : CircularContainer, IHasAccentColour + public class SpinnerRotationTracker : CircularContainer { private readonly Spinner spinner; - private Color4 accentColour; - - public Color4 AccentColour - { - get => accentColour; - set - { - accentColour = value; - if (background.Drawable is IHasAccentColour accent) - accent.AccentColour = value; - } - } - - private readonly SkinnableDrawable background; - - private const float idle_alpha = 0.2f; - private const float tracking_alpha = 0.4f; - public override bool IsPresent => true; // handle input when hidden - public SpinnerDisc(Spinner s) + public SpinnerRotationTracker(Spinner s) { spinner = s; RelativeSizeAxes = Axes.Both; - - Children = new Drawable[] - { - background = new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerDisc), _ => new SpinnerFill { Alpha = idle_alpha }), - }; } public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - private bool tracking; + public bool Tracking { get; set; } - public bool Tracking - { - get => tracking; - set - { - if (value == tracking) return; - - tracking = value; - - // todo: new default only - background.Drawable.FadeTo(tracking ? tracking_alpha : idle_alpha, 100); - } - } - - private bool complete; - - public bool Complete - { - get => complete; - set - { - if (value == complete) return; - - complete = value; - - updateCompleteTick(); - } - } + public readonly BindableBool Complete = new BindableBool(); /// /// The total rotation performed on the spinner disc, disregarding the spin direction. @@ -93,7 +41,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces /// If the spinner is spun 360 degrees clockwise and then 360 degrees counter-clockwise, /// this property will return the value of 720 (as opposed to 0 for ). /// - public float CumulativeRotation; + public float CumulativeRotation { get; private set; } /// /// Whether currently in the correct time range to allow spinning. @@ -110,9 +58,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private float lastAngle; private float currentRotation; - private int completeTick; - - private bool updateCompleteTick() => completeTick != (completeTick = (int)(CumulativeRotation / 360)); private bool rotationTransferred; @@ -123,21 +68,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces var delta = thisAngle - lastAngle; - if (tracking) - Rotate(delta); + if (Tracking) + AddRotation(delta); lastAngle = thisAngle; - if (Complete && updateCompleteTick()) - { - // todo: new default only - background.Drawable.FinishTransforms(false, nameof(Alpha)); - background.Drawable - .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) - .Then() - .FadeTo(tracking_alpha, 250, Easing.OutQuint); - } - Rotation = (float)Interpolation.Lerp(Rotation, currentRotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); } @@ -148,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces /// Will be a no-op if not a valid time to spin. /// /// The delta angle. - public void Rotate(float angle) + public void AddRotation(float angle) { if (!isSpinnableTime) return; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerBackgroundLayer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerBackgroundLayer.cs new file mode 100644 index 0000000000..3cd2454706 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerBackgroundLayer.cs @@ -0,0 +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.Allocation; +using osu.Framework.Graphics; +using osu.Game.Graphics; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public class SpinnerBackgroundLayer : SpinnerFill + { + [BackgroundDependencyLoader] + private void load(OsuColour colours, DrawableHitObject drawableHitObject) + { + Disc.Alpha = 0; + Anchor = Anchor.Centre; + Origin = Anchor.Centre; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerCentre.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs similarity index 67% rename from osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerCentre.cs rename to osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs index d07829fbac..8803b92815 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DefaultSpinnerCentre.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs @@ -15,7 +15,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public class DefaultSpinnerCentre : CompositeDrawable + public class SpinnerCentreLayer : CompositeDrawable, IHasAccentColour { private DrawableSpinner spinner; @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private SpriteIcon symbol; [BackgroundDependencyLoader] - private void load(OsuColour colours, DrawableHitObject drawableHitObject) + private void load(DrawableHitObject drawableHitObject) { spinner = (DrawableSpinner)drawableHitObject; @@ -49,35 +49,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Shadow = false, }, }; - - drawableHitObject.State.BindValueChanged(val => - { - Color4 colour; - - switch (val.NewValue) - { - default: - colour = colours.BlueDark; - break; - - case ArmedState.Hit: - colour = colours.YellowLight; - break; - } - - circle.FadeColour(colour, 200); - glow.FadeColour(colour, 200); - }, true); - - FinishTransforms(true); } protected override void Update() { base.Update(); + symbol.Rotation = (float)Interpolation.Lerp(symbol.Rotation, spinner.RotationTracker.Rotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); + } - circle.Rotation = spinner.Disc.Rotation; - symbol.Rotation = (float)Interpolation.Lerp(symbol.Rotation, spinner.Disc.Rotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); + private Color4 accentColour; + + public Color4 AccentColour + { + get => accentColour; + set + { + accentColour = value; + + circle.Colour = accentColour; + glow.Colour = accentColour; + } } } } From 2b71ffa2ed9a45ec87b38fcd63cb053f244f800e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Jul 2020 22:31:18 +0900 Subject: [PATCH 074/185] Add back legacy implementations --- .../Objects/Drawables/DrawableSpinner.cs | 2 +- osu.Game.Rulesets.Osu/OsuSkinComponents.cs | 4 +- .../Skinning/LegacyNewStyleSpinner.cs | 80 +++++++++++++++++++ .../Skinning/LegacyOldStyleSpinner.cs | 64 +++++++++++++++ .../Skinning/OsuLegacySkinTransformer.cs | 25 ++---- 5 files changed, 151 insertions(+), 24 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs create mode 100644 osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 11f1afafba..f36ecd9dc4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Scale = new Vector2(Spinner.Scale), Children = new Drawable[] { - new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerDisc), _ => new DefaultSpinnerDisc()), + new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBody), _ => new DefaultSpinnerDisc()), RotationTracker = new SpinnerRotationTracker(Spinner) { Anchor = Anchor.Centre, diff --git a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs index c05dbf6b16..5468764692 100644 --- a/osu.Game.Rulesets.Osu/OsuSkinComponents.cs +++ b/osu.Game.Rulesets.Osu/OsuSkinComponents.cs @@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Osu SliderFollowCircle, SliderBall, SliderBody, - SpinnerDisc, - SpinnerBackground, - SpinnerCentre + SpinnerBody } } diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs new file mode 100644 index 0000000000..65b8b979fc --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.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.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public class LegacyNewStyleSpinner : CompositeDrawable + { + private Sprite discBottom; + private Sprite discTop; + private Sprite spinningMiddle; + + private DrawableSpinner drawableSpinner; + + [BackgroundDependencyLoader] + private void load(ISkinSource source, DrawableHitObject drawableObject) + { + drawableSpinner = (DrawableSpinner)drawableObject; + + InternalChildren = new Drawable[] + { + discBottom = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-bottom") + }, + discTop = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-top") + }, + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-middle") + }, + spinningMiddle = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-middle2") + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.FadeOut(); + drawableSpinner.State.BindValueChanged(updateStateTransforms, true); + } + + private void updateStateTransforms(ValueChangedEvent state) + { + var spinner = drawableSpinner.HitObject; + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt / 2, true)) + this.FadeInFromZero(spinner.TimePreempt / 2); + } + + protected override void Update() + { + base.Update(); + spinningMiddle.Rotation = discTop.Rotation = drawableSpinner.RotationTracker.Rotation; + discBottom.Rotation = discTop.Rotation / 3; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs new file mode 100644 index 0000000000..16d8664e66 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.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.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Skinning; + +namespace osu.Game.Rulesets.Osu.Skinning +{ + public class LegacyOldStyleSpinner : CompositeDrawable + { + private DrawableSpinner drawableSpinner; + private Sprite disc; + + [BackgroundDependencyLoader] + private void load(ISkinSource source, DrawableHitObject drawableObject) + { + drawableSpinner = (DrawableSpinner)drawableObject; + + InternalChildren = new Drawable[] + { + new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-background") + }, + disc = new Sprite + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Texture = source.GetTexture("spinner-circle") + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + this.FadeOut(); + drawableSpinner.State.BindValueChanged(updateStateTransforms, true); + } + + private void updateStateTransforms(ValueChangedEvent state) + { + var spinner = drawableSpinner.HitObject; + + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt / 2, true)) + this.FadeInFromZero(spinner.TimePreempt / 2); + } + + protected override void Update() + { + base.Update(); + disc.Rotation = drawableSpinner.RotationTracker.Rotation; + } + } +} diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index a5198d3448..44f431d6f7 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -4,7 +4,6 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Sprites; using osu.Game.Skinning; using osuTK; @@ -104,25 +103,11 @@ namespace osu.Game.Rulesets.Osu.Skinning Spacing = new Vector2(-overlap, 0) }; - case OsuSkinComponents.SpinnerDisc: - if (Source.GetTexture("spinner-background") != null) - return new Sprite { Texture = Source.GetTexture("spinner-circle") }; - else if (Source.GetTexture("spinner-top") != null) - return new Sprite { Texture = Source.GetTexture("spinner-top") }; - - return null; - - case OsuSkinComponents.SpinnerBackground: - if (Source.GetTexture("spinner-background") != null) - return new Sprite { Texture = Source.GetTexture("spinner-background") }; - - return null; - - case OsuSkinComponents.SpinnerCentre: - if (Source.GetTexture("spinner-background") != null) - return Drawable.Empty(); - else if (Source.GetTexture("spinner-top") != null) - return new Sprite { Texture = Source.GetTexture("spinner-middle2") }; + case OsuSkinComponents.SpinnerBody: + if (Source.GetTexture("spinner-top") != null) + return new LegacyNewStyleSpinner(); + else if (Source.GetTexture("spinner-background") != null) + return new LegacyOldStyleSpinner(); return null; } From ca21f038e0c8bf8f2f2a619562d88af7feac50cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 10:35:48 +0900 Subject: [PATCH 075/185] Add xmldoc for legacy classes --- osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs | 4 ++++ osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs | 3 +++ 2 files changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 65b8b979fc..2521bb1d76 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -12,6 +12,10 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Skinning { + /// + /// Legacy skinned spinner with two main spinning layers, one fixed overlay and one final spinning overlay. + /// No background layer. + /// public class LegacyNewStyleSpinner : CompositeDrawable { private Sprite discBottom; diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 16d8664e66..e6a4064129 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -12,6 +12,9 @@ using osu.Game.Skinning; namespace osu.Game.Rulesets.Osu.Skinning { + /// + /// Legacy skinned spinner with one main spinning layer and a background layer. + /// public class LegacyOldStyleSpinner : CompositeDrawable { private DrawableSpinner drawableSpinner; From d4496eb9824ad8bbc14da66d00b14c037fc54c69 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 30 Jul 2020 04:51:09 +0300 Subject: [PATCH 076/185] Update ShowMoreButton in line with web --- .../Visual/Online/TestSceneShowMoreButton.cs | 20 ++--- .../Graphics/UserInterface/ShowMoreButton.cs | 84 ++++++++++++++----- .../Comments/Buttons/CommentRepliesButton.cs | 15 ++-- .../Comments/CommentsShowMoreButton.cs | 11 --- .../Profile/Sections/PaginatedContainer.cs | 5 +- .../Profile/Sections/ProfileShowMoreButton.cs | 19 ----- 6 files changed, 76 insertions(+), 78 deletions(-) delete mode 100644 osu.Game/Overlays/Profile/Sections/ProfileShowMoreButton.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs index 273f593c32..18ac415126 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneShowMoreButton.cs @@ -4,19 +4,22 @@ using osu.Framework.Graphics; using osu.Game.Graphics.UserInterface; using osu.Framework.Allocation; -using osu.Game.Graphics; +using osu.Game.Overlays; namespace osu.Game.Tests.Visual.Online { public class TestSceneShowMoreButton : OsuTestScene { + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Green); + public TestSceneShowMoreButton() { - TestButton button = null; + ShowMoreButton button = null; int fireCount = 0; - Add(button = new TestButton + Add(button = new ShowMoreButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -46,16 +49,5 @@ namespace osu.Game.Tests.Visual.Online AddAssert("action fired twice", () => fireCount == 2); AddAssert("is in loading state", () => button.IsLoading); } - - private class TestButton : ShowMoreButton - { - [BackgroundDependencyLoader] - private void load(OsuColour colors) - { - IdleColour = colors.YellowDark; - HoverColour = colors.Yellow; - ChevronIconColour = colors.Red; - } - } } } diff --git a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs index c9cd9f1158..f4ab53d305 100644 --- a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs +++ b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs @@ -1,13 +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.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.Sprites; +using osu.Game.Overlays; using osuTK; -using osuTK.Graphics; using System.Collections.Generic; namespace osu.Game.Graphics.UserInterface @@ -16,14 +18,6 @@ namespace osu.Game.Graphics.UserInterface { private const int duration = 200; - private Color4 chevronIconColour; - - protected Color4 ChevronIconColour - { - get => chevronIconColour; - set => chevronIconColour = leftChevron.Colour = rightChevron.Colour = value; - } - public string Text { get => text.Text; @@ -32,22 +26,28 @@ namespace osu.Game.Graphics.UserInterface protected override IEnumerable EffectTargets => new[] { background }; - private ChevronIcon leftChevron; - private ChevronIcon rightChevron; + private ChevronIcon leftIcon; + private ChevronIcon rightIcon; private SpriteText text; private Box background; private FillFlowContainer textContainer; public ShowMoreButton() { - Height = 30; - Width = 140; + AutoSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + IdleColour = colourProvider.Background2; + HoverColour = colourProvider.Background1; } protected override Drawable CreateContent() => new CircularContainer { Masking = true, - RelativeSizeAxes = Axes.Both, + AutoSizeAxes = Axes.Both, Children = new Drawable[] { background = new Box @@ -56,22 +56,36 @@ namespace osu.Game.Graphics.UserInterface }, textContainer = new FillFlowContainer { + AlwaysPresent = true, Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(7), + Spacing = new Vector2(10), + Margin = new MarginPadding + { + Horizontal = 20, + Vertical = 5 + }, Children = new Drawable[] { - leftChevron = new ChevronIcon(), + leftIcon = new ChevronIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, text = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Text = "show more".ToUpper(), }, - rightChevron = new ChevronIcon(), + rightIcon = new ChevronIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } } } } @@ -81,17 +95,41 @@ namespace osu.Game.Graphics.UserInterface protected override void OnLoadFinished() => textContainer.FadeIn(duration, Easing.OutQuint); - private class ChevronIcon : SpriteIcon + protected override bool OnHover(HoverEvent e) { - private const int icon_size = 8; + base.OnHover(e); + leftIcon.FadeHoverColour(); + rightIcon.FadeHoverColour(); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + leftIcon.FadeIdleColour(); + rightIcon.FadeIdleColour(); + } + + public class ChevronIcon : SpriteIcon + { + [Resolved] + private OverlayColourProvider colourProvider { get; set; } public ChevronIcon() { - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - Size = new Vector2(icon_size); + Size = new Vector2(7.5f); Icon = FontAwesome.Solid.ChevronDown; } + + [BackgroundDependencyLoader] + private void load() + { + Colour = colourProvider.Foreground1; + } + + public void FadeHoverColour() => this.FadeColour(colourProvider.Light1, 200, Easing.OutQuint); + + public void FadeIdleColour() => this.FadeColour(colourProvider.Foreground1, 200, Easing.OutQuint); } } } diff --git a/osu.Game/Overlays/Comments/Buttons/CommentRepliesButton.cs b/osu.Game/Overlays/Comments/Buttons/CommentRepliesButton.cs index 53438ca421..202f3ddd7b 100644 --- a/osu.Game/Overlays/Comments/Buttons/CommentRepliesButton.cs +++ b/osu.Game/Overlays/Comments/Buttons/CommentRepliesButton.cs @@ -5,12 +5,12 @@ 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.Sprites; using osu.Game.Graphics.UserInterface; using osuTK; +using static osu.Game.Graphics.UserInterface.ShowMoreButton; namespace osu.Game.Overlays.Comments.Buttons { @@ -25,7 +25,7 @@ namespace osu.Game.Overlays.Comments.Buttons [Resolved] private OverlayColourProvider colourProvider { get; set; } - private readonly SpriteIcon icon; + private readonly ChevronIcon icon; private readonly Box background; private readonly OsuSpriteText text; @@ -68,12 +68,10 @@ namespace osu.Game.Overlays.Comments.Buttons AlwaysPresent = true, Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold) }, - icon = new SpriteIcon + icon = new ChevronIcon { Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(7.5f), - Icon = FontAwesome.Solid.ChevronDown + Origin = Anchor.CentreLeft } } } @@ -88,7 +86,6 @@ namespace osu.Game.Overlays.Comments.Buttons private void load() { background.Colour = colourProvider.Background2; - icon.Colour = colourProvider.Foreground1; } protected void SetIconDirection(bool upwards) => icon.ScaleTo(new Vector2(1, upwards ? -1 : 1)); @@ -99,7 +96,7 @@ namespace osu.Game.Overlays.Comments.Buttons { base.OnHover(e); background.FadeColour(colourProvider.Background1, 200, Easing.OutQuint); - icon.FadeColour(colourProvider.Light1, 200, Easing.OutQuint); + icon.FadeHoverColour(); return true; } @@ -107,7 +104,7 @@ namespace osu.Game.Overlays.Comments.Buttons { base.OnHoverLost(e); background.FadeColour(colourProvider.Background2, 200, Easing.OutQuint); - icon.FadeColour(colourProvider.Foreground1, 200, Easing.OutQuint); + icon.FadeIdleColour(); } } } diff --git a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs b/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs index d2ff7ecb1f..adf64eabb1 100644 --- a/osu.Game/Overlays/Comments/CommentsShowMoreButton.cs +++ b/osu.Game/Overlays/Comments/CommentsShowMoreButton.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.Bindables; using osu.Game.Graphics.UserInterface; @@ -11,16 +10,6 @@ namespace osu.Game.Overlays.Comments { public readonly BindableInt Current = new BindableInt(); - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - Height = 20; - - IdleColour = colourProvider.Background2; - HoverColour = colourProvider.Background1; - ChevronIconColour = colourProvider.Foreground1; - } - protected override void LoadComplete() { Current.BindValueChanged(onCurrentChanged, true); diff --git a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs index a30ff786fb..9720469548 100644 --- a/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/PaginatedContainer.cs @@ -14,12 +14,13 @@ using osu.Game.Users; using System.Collections.Generic; using System.Linq; using System.Threading; +using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Profile.Sections { public abstract class PaginatedContainer : FillFlowContainer { - private readonly ProfileShowMoreButton moreButton; + private readonly ShowMoreButton moreButton; private readonly OsuSpriteText missingText; private APIRequest> retrievalRequest; private CancellationTokenSource loadCancellation; @@ -74,7 +75,7 @@ namespace osu.Game.Overlays.Profile.Sections RelativeSizeAxes = Axes.X, Spacing = new Vector2(0, 2), }, - moreButton = new ProfileShowMoreButton + moreButton = new ShowMoreButton { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, diff --git a/osu.Game/Overlays/Profile/Sections/ProfileShowMoreButton.cs b/osu.Game/Overlays/Profile/Sections/ProfileShowMoreButton.cs deleted file mode 100644 index 426ebeebe6..0000000000 --- a/osu.Game/Overlays/Profile/Sections/ProfileShowMoreButton.cs +++ /dev/null @@ -1,19 +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.Game.Graphics.UserInterface; - -namespace osu.Game.Overlays.Profile.Sections -{ - public class ProfileShowMoreButton : ShowMoreButton - { - [BackgroundDependencyLoader] - private void load(OverlayColourProvider colourProvider) - { - IdleColour = colourProvider.Background2; - HoverColour = colourProvider.Background1; - ChevronIconColour = colourProvider.Foreground1; - } - } -} From 45ddc7a2e9f9d1b05404831c172ff479920031a5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 30 Jul 2020 05:02:01 +0300 Subject: [PATCH 077/185] Rename ShowMoreButton in comments namespace to ShowMoreRepliesButton --- .../Buttons/{ShowMoreButton.cs => ShowMoreRepliesButton.cs} | 4 ++-- osu.Game/Overlays/Comments/DrawableComment.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) rename osu.Game/Overlays/Comments/Buttons/{ShowMoreButton.cs => ShowMoreRepliesButton.cs} (93%) diff --git a/osu.Game/Overlays/Comments/Buttons/ShowMoreButton.cs b/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs similarity index 93% rename from osu.Game/Overlays/Comments/Buttons/ShowMoreButton.cs rename to osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs index 2c363564d2..c115a8bb8f 100644 --- a/osu.Game/Overlays/Comments/Buttons/ShowMoreButton.cs +++ b/osu.Game/Overlays/Comments/Buttons/ShowMoreRepliesButton.cs @@ -12,13 +12,13 @@ using osu.Framework.Allocation; namespace osu.Game.Overlays.Comments.Buttons { - public class ShowMoreButton : LoadingButton + public class ShowMoreRepliesButton : LoadingButton { protected override IEnumerable EffectTargets => new[] { text }; private OsuSpriteText text; - public ShowMoreButton() + public ShowMoreRepliesButton() { AutoSizeAxes = Axes.Both; LoadingAnimationSize = new Vector2(8); diff --git a/osu.Game/Overlays/Comments/DrawableComment.cs b/osu.Game/Overlays/Comments/DrawableComment.cs index 9c0a48ec29..31aa41e967 100644 --- a/osu.Game/Overlays/Comments/DrawableComment.cs +++ b/osu.Game/Overlays/Comments/DrawableComment.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Comments private FillFlowContainer childCommentsVisibilityContainer; private FillFlowContainer childCommentsContainer; private LoadRepliesButton loadRepliesButton; - private ShowMoreButton showMoreButton; + private ShowMoreRepliesButton showMoreButton; private ShowRepliesButton showRepliesButton; private ChevronButton chevronButton; private DeletedCommentsCounter deletedCommentsCounter; @@ -213,7 +213,7 @@ namespace osu.Game.Overlays.Comments Top = 10 } }, - showMoreButton = new ShowMoreButton + showMoreButton = new ShowMoreRepliesButton { Action = () => RepliesRequested(this, ++currentPage) } From 64c7ae768641e22a51fffb0e2a828ebcba00eab9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 11:25:49 +0900 Subject: [PATCH 078/185] Fix hit transforms not playing out correctly --- .../Objects/Drawables/Pieces/DefaultSpinnerDisc.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 11cd73b995..6a40069c5d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -87,16 +87,16 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { base.LoadComplete(); - centre.ScaleTo(0); - mainContainer.ScaleTo(0); - this.ScaleTo(1); - drawableSpinner.RotationTracker.Complete.BindValueChanged(complete => updateComplete(complete.NewValue, 200)); drawableSpinner.State.BindValueChanged(updateStateTransforms, true); } private void updateStateTransforms(ValueChangedEvent state) { + centre.ScaleTo(0); + mainContainer.ScaleTo(0); + this.ScaleTo(1); + using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt / 2, true)) { float phaseOneScale = spinner.Scale * 0.7f; From 54fee7e7160ec6931937f37c31898a5e554bc247 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 11:50:13 +0900 Subject: [PATCH 079/185] Simplify and standardise scale for default display --- .../Objects/Drawables/DrawableSpinner.cs | 12 ---------- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 22 +++++++++---------- .../Objects/Drawables/SpinnerCentreLayer.cs | 3 --- 3 files changed, 11 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index f36ecd9dc4..52a1b4679b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Primitives; using osu.Game.Graphics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; @@ -39,29 +38,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RelativeSizeAxes = Axes.Both; - // we are slightly bigger than our parent, to clip the top and bottom of the circle - Height = 1.3f; - Spinner = s; InternalChildren = new Drawable[] { ticks = new Container(), - RotationTracker = new SpinnerRotationTracker(Spinner), new AspectContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Y, - Scale = new Vector2(Spinner.Scale), Children = new Drawable[] { new SkinnableDrawable(new OsuSkinComponent(OsuSkinComponents.SpinnerBody), _ => new DefaultSpinnerDisc()), RotationTracker = new SpinnerRotationTracker(Spinner) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - }, } }, SpmCounter = new SpinnerSpmCounter @@ -162,8 +152,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables RotationTracker.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false; } - public float RelativeHeight => ToScreenSpace(new RectangleF(0, 0, 0, OsuHitObject.OBJECT_RADIUS * 2)).Height / DrawHeight; - protected override void UpdateAfterChildren() { base.UpdateAfterChildren(); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 6a40069c5d..2644f425a0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -39,6 +39,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { RelativeSizeAxes = Axes.Both; + // we are slightly bigger than our parent, to clip the top and bottom of the circle + // this should probably be revisited when scaled spinners are a thing. + Scale = new Vector2(1.3f); + Anchor = Anchor.Centre; Origin = Anchor.Centre; } @@ -95,22 +99,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { centre.ScaleTo(0); mainContainer.ScaleTo(0); - this.ScaleTo(1); using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt / 2, true)) { - float phaseOneScale = spinner.Scale * 0.7f; - - centre.ScaleTo(phaseOneScale, spinner.TimePreempt / 4, Easing.OutQuint); - - mainContainer - .ScaleTo(phaseOneScale * drawableSpinner.RelativeHeight * 1.6f, spinner.TimePreempt / 4, Easing.OutQuint); - + // constant ambient rotation to give the spinner "spinning" character. this.RotateTo((float)(25 * spinner.Duration / 2000), spinner.TimePreempt + spinner.Duration); + centre.ScaleTo(0.3f, spinner.TimePreempt / 4, Easing.OutQuint); + mainContainer.ScaleTo(0.2f, spinner.TimePreempt / 4, Easing.OutQuint); + using (BeginDelayedSequence(spinner.TimePreempt / 2, true)) { - centre.ScaleTo(spinner.Scale, spinner.TimePreempt / 2, Easing.OutQuint); + centre.ScaleTo(0.5f, spinner.TimePreempt / 2, Easing.OutQuint); mainContainer.ScaleTo(1, spinner.TimePreempt / 2, Easing.OutQuint); } } @@ -160,8 +160,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces .FadeTo(tracking_alpha, 250, Easing.OutQuint); } - float relativeCircleScale = spinner.Scale * drawableSpinner.RelativeHeight; - float targetScale = relativeCircleScale + (1 - relativeCircleScale) * drawableSpinner.Progress; + const float initial_scale = 0.2f; + float targetScale = initial_scale + (1 - initial_scale) * drawableSpinner.Progress; fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs index 8803b92815..b62ce822f0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SpinnerCentreLayer.cs @@ -28,9 +28,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { spinner = (DrawableSpinner)drawableHitObject; - AutoSizeAxes = Axes.Both; - Anchor = Anchor.Centre; - Origin = Anchor.Centre; InternalChildren = new Drawable[] { glow = new GlowPiece(), From 4d822742e83a8892838b7ddf105f5f4c6c472858 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 12:05:19 +0900 Subject: [PATCH 080/185] Add scale and tint to new legacy style spinner --- .../Skinning/LegacyNewStyleSpinner.cs | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 2521bb1d76..618cb0e9ad 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -6,9 +6,13 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; +using osuTK; +using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning { @@ -21,14 +25,19 @@ namespace osu.Game.Rulesets.Osu.Skinning private Sprite discBottom; private Sprite discTop; private Sprite spinningMiddle; + private Sprite fixedMiddle; private DrawableSpinner drawableSpinner; + private const float final_scale = 0.625f; + [BackgroundDependencyLoader] private void load(ISkinSource source, DrawableHitObject drawableObject) { drawableSpinner = (DrawableSpinner)drawableObject; + Scale = new Vector2(final_scale); + InternalChildren = new Drawable[] { discBottom = new Sprite @@ -43,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Origin = Anchor.Centre, Texture = source.GetTexture("spinner-top") }, - new Sprite + fixedMiddle = new Sprite { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -68,10 +77,14 @@ namespace osu.Game.Rulesets.Osu.Skinning private void updateStateTransforms(ValueChangedEvent state) { - var spinner = drawableSpinner.HitObject; + var spinner = (Spinner)drawableSpinner.HitObject; using (BeginAbsoluteSequence(spinner.StartTime - spinner.TimePreempt / 2, true)) this.FadeInFromZero(spinner.TimePreempt / 2); + + fixedMiddle.FadeColour(Color4.White); + using (BeginAbsoluteSequence(spinner.StartTime, true)) + fixedMiddle.FadeColour(Color4.Red, spinner.Duration); } protected override void Update() @@ -79,6 +92,9 @@ namespace osu.Game.Rulesets.Osu.Skinning base.Update(); spinningMiddle.Rotation = discTop.Rotation = drawableSpinner.RotationTracker.Rotation; discBottom.Rotation = discTop.Rotation / 3; + + Scale = new Vector2(final_scale * 0.8f + + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * (final_scale * 0.2f)); } } } From d2b3fe1e7b3412dd82082c3ffe818972a52f47e9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 12:08:04 +0900 Subject: [PATCH 081/185] Add scale to old legacy spinner --- osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index e6a4064129..02b8c7eef1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Osu.Skinning { @@ -25,6 +26,8 @@ namespace osu.Game.Rulesets.Osu.Skinning { drawableSpinner = (DrawableSpinner)drawableObject; + Scale = new Vector2(0.625f); + InternalChildren = new Drawable[] { new Sprite From 743d165319cba9784ee2700524999c59298e1880 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 12:32:19 +0900 Subject: [PATCH 082/185] Add old style spin metre --- .../Skinning/LegacyOldStyleSpinner.cs | 50 +++++++++++++++++-- 1 file changed, 45 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 02b8c7eef1..9b1f2b31e3 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.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.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Skinning; @@ -20,37 +22,59 @@ namespace osu.Game.Rulesets.Osu.Skinning { private DrawableSpinner drawableSpinner; private Sprite disc; + private Container metre; [BackgroundDependencyLoader] private void load(ISkinSource source, DrawableHitObject drawableObject) { drawableSpinner = (DrawableSpinner)drawableObject; - Scale = new Vector2(0.625f); + RelativeSizeAxes = Axes.Both; InternalChildren = new Drawable[] { new Sprite { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Texture = source.GetTexture("spinner-background") + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Texture = source.GetTexture("spinner-background"), + Y = 20, + Scale = new Vector2(0.625f) }, disc = new Sprite { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Texture = source.GetTexture("spinner-circle") + Texture = source.GetTexture("spinner-circle"), + Scale = new Vector2(0.625f) + }, + metre = new Container + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Y = 20, + Masking = true, + Child = new Sprite + { + Texture = source.GetTexture("spinner-metre"), + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + }, + Scale = new Vector2(0.625f) } }; } + private Vector2 metreFinalSize; + protected override void LoadComplete() { base.LoadComplete(); this.FadeOut(); drawableSpinner.State.BindValueChanged(updateStateTransforms, true); + + metreFinalSize = metre.Size = metre.Child.Size; } private void updateStateTransforms(ValueChangedEvent state) @@ -65,6 +89,22 @@ namespace osu.Game.Rulesets.Osu.Skinning { base.Update(); disc.Rotation = drawableSpinner.RotationTracker.Rotation; + metre.Height = getMetreHeight(drawableSpinner.Progress); + } + + private const int total_bars = 10; + + private float getMetreHeight(float progress) + { + progress = Math.Min(99, progress * 100); + + int barCount = (int)progress / 10; + + // todo: add SpinnerNoBlink support + if (RNG.NextBool(((int)progress % 10) / 10f)) + barCount++; + + return (float)barCount / total_bars * metreFinalSize.Y; } } } From 19fb350cd81d1ed3567f967a4a3d1f1d00943454 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 12:50:27 +0900 Subject: [PATCH 083/185] Move offset and scale to constant --- .../Skinning/LegacyOldStyleSpinner.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs index 9b1f2b31e3..0ae1d8f683 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyOldStyleSpinner.cs @@ -24,6 +24,10 @@ namespace osu.Game.Rulesets.Osu.Skinning private Sprite disc; private Container metre; + private const float background_y_offset = 20; + + private const float sprite_scale = 1 / 1.6f; + [BackgroundDependencyLoader] private void load(ISkinSource source, DrawableHitObject drawableObject) { @@ -38,21 +42,21 @@ namespace osu.Game.Rulesets.Osu.Skinning Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, Texture = source.GetTexture("spinner-background"), - Y = 20, - Scale = new Vector2(0.625f) + Y = background_y_offset, + Scale = new Vector2(sprite_scale) }, disc = new Sprite { Anchor = Anchor.Centre, Origin = Anchor.Centre, Texture = source.GetTexture("spinner-circle"), - Scale = new Vector2(0.625f) + Scale = new Vector2(sprite_scale) }, metre = new Container { Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - Y = 20, + Y = background_y_offset, Masking = true, Child = new Sprite { From c1085d49d3c41b88233cf530e41774065161f527 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 12:55:34 +0900 Subject: [PATCH 084/185] Add more xmldoc --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 52a1b4679b..f65570a3d5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -117,6 +117,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables positionBindable.BindTo(HitObject.PositionBindable); } + /// + /// The completion progress of this spinner from 0..1 (clamped). + /// public float Progress => Math.Clamp(RotationTracker.CumulativeRotation / 360 / Spinner.SpinsRequired, 0, 1); protected override void CheckForResult(bool userTriggered, double timeOffset) From 41c0f7557ac2a3bcc46174d811ead37d432a17fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 12:56:30 +0900 Subject: [PATCH 085/185] Remove traceable spinner reference for now --- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index f209b315af..977485ba66 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -57,9 +57,7 @@ namespace osu.Game.Rulesets.Osu.Mods applySliderState(slider); break; - case DrawableSpinner spinner: - //todo: hide background - break; + //todo: hide spinner background somehow } } From ec0d7760afeca015e7ed8ce488a27bedcb6cc20f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 13:06:53 +0900 Subject: [PATCH 086/185] Move todo? --- 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 977485ba66..d7582f3196 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -43,6 +43,8 @@ namespace osu.Game.Rulesets.Osu.Mods var h = drawableOsu.HitObject; + //todo: expose and hide spinner background somehow + switch (drawable) { case DrawableHitCircle circle: @@ -56,8 +58,6 @@ namespace osu.Game.Rulesets.Osu.Mods slider.Body.OnSkinChanged += () => applySliderState(slider); applySliderState(slider); break; - - //todo: hide spinner background somehow } } From 6473bf503b91bdc6d8b8acb474530e7125862152 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 30 Jul 2020 07:09:40 +0300 Subject: [PATCH 087/185] Remove use of `case when` --- osu.Game/Skinning/LegacySkin.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 5843cde94d..d98f8aba83 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -133,8 +133,14 @@ namespace osu.Game.Skinning break; - case LegacySkinConfiguration.LegacySetting s when s == LegacySkinConfiguration.LegacySetting.Version: - return SkinUtils.As(new Bindable(Configuration.LegacyVersion ?? LegacySkinConfiguration.LATEST_VERSION)); + case LegacySkinConfiguration.LegacySetting legacy: + switch (legacy) + { + case LegacySkinConfiguration.LegacySetting.Version: + return SkinUtils.As(new Bindable(Configuration.LegacyVersion ?? LegacySkinConfiguration.LATEST_VERSION)); + } + + goto default; default: // handles lookups like some in LegacySkinConfiguration.LegacySetting From e5991d6e1487cd3f9a3596494fc732a0f36a5155 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 13:49:04 +0900 Subject: [PATCH 088/185] Change method structure for hover/unhover state setting (shouldn't be called "Fade") --- osu.Game/Graphics/UserInterface/ShowMoreButton.cs | 13 ++++++------- .../Comments/Buttons/CommentRepliesButton.cs | 4 ++-- 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs index f4ab53d305..924c7913f3 100644 --- a/osu.Game/Graphics/UserInterface/ShowMoreButton.cs +++ b/osu.Game/Graphics/UserInterface/ShowMoreButton.cs @@ -98,16 +98,16 @@ namespace osu.Game.Graphics.UserInterface protected override bool OnHover(HoverEvent e) { base.OnHover(e); - leftIcon.FadeHoverColour(); - rightIcon.FadeHoverColour(); + leftIcon.SetHoveredState(true); + rightIcon.SetHoveredState(true); return true; } protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - leftIcon.FadeIdleColour(); - rightIcon.FadeIdleColour(); + leftIcon.SetHoveredState(false); + rightIcon.SetHoveredState(false); } public class ChevronIcon : SpriteIcon @@ -127,9 +127,8 @@ namespace osu.Game.Graphics.UserInterface Colour = colourProvider.Foreground1; } - public void FadeHoverColour() => this.FadeColour(colourProvider.Light1, 200, Easing.OutQuint); - - public void FadeIdleColour() => this.FadeColour(colourProvider.Foreground1, 200, Easing.OutQuint); + public void SetHoveredState(bool hovered) => + this.FadeColour(hovered ? colourProvider.Light1 : colourProvider.Foreground1, 200, Easing.OutQuint); } } } diff --git a/osu.Game/Overlays/Comments/Buttons/CommentRepliesButton.cs b/osu.Game/Overlays/Comments/Buttons/CommentRepliesButton.cs index 202f3ddd7b..57bf2af4d2 100644 --- a/osu.Game/Overlays/Comments/Buttons/CommentRepliesButton.cs +++ b/osu.Game/Overlays/Comments/Buttons/CommentRepliesButton.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Comments.Buttons { base.OnHover(e); background.FadeColour(colourProvider.Background1, 200, Easing.OutQuint); - icon.FadeHoverColour(); + icon.SetHoveredState(true); return true; } @@ -104,7 +104,7 @@ namespace osu.Game.Overlays.Comments.Buttons { base.OnHoverLost(e); background.FadeColour(colourProvider.Background2, 200, Easing.OutQuint); - icon.FadeIdleColour(); + icon.SetHoveredState(false); } } } From 7071f9a3af6fd281cf76c5aacef45959df5c3e87 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 14:24:21 +0900 Subject: [PATCH 089/185] 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 7e6f1469f5..61314bdc10 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 5ac54a853f..d6aeca1f53 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 8b2d1346be..9b8d70ab6d 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From d8bb52800fe5e351dd730cd2d37f707b1a447c3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 14:31:05 +0900 Subject: [PATCH 090/185] Update framework again (github deploy failed) --- 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 61314bdc10..0d951f58e6 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index d6aeca1f53..92e7080fed 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 9b8d70ab6d..973c1d5b89 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 1dfd2112c60d2f468a74675f383b4125334d11cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 15:32:08 +0900 Subject: [PATCH 091/185] Source hash from osu.Game.dll rather than executable --- osu.Game/OsuGameBase.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 964a7fdd35..278f2d849f 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -134,13 +134,8 @@ namespace osu.Game [BackgroundDependencyLoader] private void load() { - var assembly = Assembly.GetEntryAssembly(); - - if (assembly != null) - { - using (var str = File.OpenRead(assembly.Location)) - VersionHash = str.ComputeMD5Hash(); - } + using (var str = File.OpenRead(typeof(OsuGameBase).Assembly.Location)) + VersionHash = str.ComputeMD5Hash(); Resources.AddStore(new DllResourceStore(OsuResources.ResourceAssembly)); From cf697cc276e7aa466d068566b4250009291efdd7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 15:32:40 +0900 Subject: [PATCH 092/185] Update framework again (fix audio component disposal issue) --- 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 0d951f58e6..0ac766926c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 92e7080fed..1ececa448c 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index 973c1d5b89..ef5ba10d17 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From 6b9102b2a43665b82c92bcb3a3a643e8d23acce9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 17:58:49 +0900 Subject: [PATCH 093/185] Add osu!catch banana catching sounds --- osu.Game.Rulesets.Catch/Objects/Banana.cs | 21 +++++++++++++++++++ .../Objects/BananaShower.cs | 12 ++++++++--- .../Objects/Drawables/DrawableBanana.cs | 7 +++++++ 3 files changed, 37 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Banana.cs b/osu.Game.Rulesets.Catch/Objects/Banana.cs index 0b3d1d23e0..4ecfb7b16d 100644 --- a/osu.Game.Rulesets.Catch/Objects/Banana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Banana.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.Collections.Generic; +using osu.Game.Audio; using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Judgements; @@ -8,8 +10,27 @@ namespace osu.Game.Rulesets.Catch.Objects { public class Banana : Fruit { + /// + /// Index of banana in current shower. + /// + public int BananaIndex; + public override FruitVisualRepresentation VisualRepresentation => FruitVisualRepresentation.Banana; public override Judgement CreateJudgement() => new CatchBananaJudgement(); + + private static readonly List samples = new List { new BananaHitSampleInfo() }; + + public Banana() + { + Samples = samples; + } + + private class BananaHitSampleInfo : HitSampleInfo + { + private static string[] lookupNames { get; } = { "metronomelow", "catch-banana" }; + + public override IEnumerable LookupNames => lookupNames; + } } } diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index 04a995c77e..89c51459a6 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -30,15 +30,21 @@ namespace osu.Game.Rulesets.Catch.Objects if (spacing <= 0) return; - for (double i = StartTime; i <= EndTime; i += spacing) + double time = StartTime; + int i = 0; + + while (time <= EndTime) { cancellationToken.ThrowIfCancellationRequested(); AddNested(new Banana { - Samples = Samples, - StartTime = i + StartTime = time, + BananaIndex = i, }); + + time += spacing; + i++; } } diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs index 01b76ceed9..a865984d45 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBanana.cs @@ -40,6 +40,13 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables float getRandomAngle() => 180 * (RNG.NextSingle() * 2 - 1); } + public override void PlaySamples() + { + base.PlaySamples(); + if (Samples != null) + Samples.Frequency.Value = 0.77f + ((Banana)HitObject).BananaIndex * 0.006f; + } + private Color4 getBananaColour() { switch (RNG.Next(0, 3)) From 38a4bdf068c7b727e046a5b6d6119d6b3339f51b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 30 Jul 2020 19:34:59 +0900 Subject: [PATCH 094/185] Add spinner spin sample support --- .../Objects/Drawables/DrawableSpinner.cs | 62 ++++++++++++++++++- .../Pieces/SpinnerRotationTracker.cs | 9 ++- 2 files changed, 69 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index f65570a3d5..68516bedf8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -70,6 +70,58 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }; } + private Bindable isSpinning; + + protected override void LoadComplete() + { + base.LoadComplete(); + + isSpinning = RotationTracker.IsSpinning.GetBoundCopy(); + isSpinning.BindValueChanged(updateSpinningSample); + } + + private SkinnableSound spinningSample; + + private const float minimum_volume = 0.0001f; + + protected override void LoadSamples() + { + base.LoadSamples(); + + spinningSample?.Expire(); + spinningSample = null; + + var firstSample = HitObject.Samples.FirstOrDefault(); + + if (firstSample != null) + { + var clone = HitObject.SampleControlPoint.ApplyTo(firstSample); + clone.Name = "spinnerspin"; + + AddInternal(spinningSample = new SkinnableSound(clone) + { + Volume = { Value = minimum_volume }, + Looping = true, + }); + } + } + + private void updateSpinningSample(ValueChangedEvent tracking) + { + // note that samples will not start playing if exiting a seek operation in the middle of a spinner. + // may be something we want to address at a later point, but not so easy to make happen right now + // (SkinnableSound would need to expose whether the sample is already playing and this logic would need to run in Update). + if (tracking.NewValue && ShouldPlaySamples) + { + spinningSample?.Play(); + spinningSample?.VolumeTo(1, 200); + } + else + { + spinningSample?.VolumeTo(minimum_volume, 200).Finally(_ => spinningSample.Stop()); + } + } + protected override void AddNestedHitObject(DrawableHitObject hitObject) { base.AddNestedHitObject(hitObject); @@ -88,6 +140,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables using (BeginDelayedSequence(Spinner.Duration, true)) this.FadeOut(160); + + // skin change does a rewind of transforms, which will stop the spinning sound from playing if it's currently in playback. + isSpinning?.TriggerChange(); } protected override void ClearNestedHitObjects() @@ -151,8 +206,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void Update() { base.Update(); + if (HandleUserInput) - RotationTracker.Tracking = OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false; + RotationTracker.Tracking = !Result.HasResult && (OsuActionInputManager?.PressedActions.Any(x => x == OsuAction.LeftButton || x == OsuAction.RightButton) ?? false); + + if (spinningSample != null) + // todo: implement SpinnerFrequencyModulate + spinningSample.Frequency.Value = 0.5f + Progress; } protected override void UpdateAfterChildren() diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs index 968c2a6df5..0cc6c842f4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs @@ -43,6 +43,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces /// public float CumulativeRotation { get; private set; } + /// + /// Whether the spinning is spinning at a reasonable speed to be considered visually spinning. + /// + public readonly BindableBool IsSpinning = new BindableBool(); + /// /// Whether currently in the correct time range to allow spinning. /// @@ -73,7 +78,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces lastAngle = thisAngle; - Rotation = (float)Interpolation.Lerp(Rotation, currentRotation / 2, Math.Clamp(Math.Abs(Time.Elapsed) / 40, 0, 1)); + IsSpinning.Value = isSpinnableTime && Math.Abs(currentRotation / 2 - Rotation) > 5f; + + Rotation = (float)Interpolation.Damp(Rotation, currentRotation / 2, 0.99, Math.Abs(Time.Elapsed)); } /// From 23ab6f8f946739bb788cc480f894b57611e78647 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 30 Jul 2020 21:10:13 +0900 Subject: [PATCH 095/185] Fix dynamic compilation loading wrong ruleset versions --- osu.Game/Rulesets/RulesetStore.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index 58a2ba056e..837796287a 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -65,11 +65,15 @@ namespace osu.Game.Rulesets // the requesting assembly may be located out of the executable's base directory, thus requiring manual resolving of its dependencies. // this attempts resolving the ruleset dependencies on game core and framework assemblies by returning assemblies with the same assembly name // already loaded in the AppDomain. - foreach (var curAsm in AppDomain.CurrentDomain.GetAssemblies()) - { - if (asm.Name.Equals(curAsm.GetName().Name, StringComparison.Ordinal)) - return curAsm; - } + var domainAssembly = AppDomain.CurrentDomain.GetAssemblies() + // Given name is always going to be equally-or-more qualified than the assembly name. + .Where(a => args.Name.Contains(a.GetName().Name, StringComparison.Ordinal)) + // Pick the greatest assembly version. + .OrderBy(a => a.GetName().Version) + .LastOrDefault(); + + if (domainAssembly != null) + return domainAssembly; return loadedAssemblies.Keys.FirstOrDefault(a => a.FullName == asm.FullName); } From 5af45bcdcc008b8bc61d3919037b6cd150532893 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Jul 2020 20:10:41 +0200 Subject: [PATCH 096/185] Expand tests to cover non-bank sample lookups --- osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index 737946e1e0..a70b08a0d3 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -67,9 +67,11 @@ namespace osu.Game.Tests.Gameplay /// Tests that a hitobject which provides a custom sample set of 2 retrieves the following samples from the beatmap skin: /// normal-hitnormal2 /// normal-hitnormal + /// hitnormal /// [TestCase("normal-hitnormal2")] [TestCase("normal-hitnormal")] + [TestCase("hitnormal")] public void TestDefaultCustomSampleFromBeatmap(string expectedSample) { SetupSkins(expectedSample, expectedSample); @@ -83,9 +85,11 @@ namespace osu.Game.Tests.Gameplay /// Tests that a hitobject which provides a custom sample set of 2 retrieves the following samples from the user skin when the beatmap does not contain the sample: /// normal-hitnormal2 /// normal-hitnormal + /// hitnormal /// [TestCase("normal-hitnormal2")] [TestCase("normal-hitnormal")] + [TestCase("hitnormal")] public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample) { SetupSkins(string.Empty, expectedSample); @@ -145,6 +149,7 @@ namespace osu.Game.Tests.Gameplay /// [TestCase("normal-hitnormal2")] [TestCase("normal-hitnormal")] + [TestCase("hitnormal")] public void TestControlPointCustomSampleFromBeatmap(string sampleName) { SetupSkins(sampleName, sampleName); From 566c5310bf954c1b7b5b71dceb63d4a904d81162 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Jul 2020 21:34:57 +0200 Subject: [PATCH 097/185] Add test coverage for taiko sample lookups --- ...o-hitobject-beatmap-custom-sample-bank.osu | 10 ++++ .../TestSceneTaikoHitObjectSamples.cs | 52 +++++++++++++++++++ 2 files changed, 62 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Resources/SampleLookups/taiko-hitobject-beatmap-custom-sample-bank.osu create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Resources/SampleLookups/taiko-hitobject-beatmap-custom-sample-bank.osu b/osu.Game.Rulesets.Taiko.Tests/Resources/SampleLookups/taiko-hitobject-beatmap-custom-sample-bank.osu new file mode 100644 index 0000000000..f9755782c2 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Resources/SampleLookups/taiko-hitobject-beatmap-custom-sample-bank.osu @@ -0,0 +1,10 @@ +osu file format v14 + +[General] +Mode: 1 + +[TimingPoints] +0,300,4,1,2,100,1,0 + +[HitObjects] +444,320,1000,5,0,0:0:0:0: diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.cs new file mode 100644 index 0000000000..7089ea6619 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoHitObjectSamples.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.Reflection; +using NUnit.Framework; +using osu.Framework.IO.Stores; +using osu.Game.Tests.Beatmaps; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + public class TestSceneTaikoHitObjectSamples : HitObjectSampleTest + { + protected override Ruleset CreatePlayerRuleset() => new TaikoRuleset(); + + protected override IResourceStore Resources => new DllResourceStore(Assembly.GetAssembly(typeof(TestSceneTaikoHitObjectSamples))); + + [TestCase("taiko-normal-hitnormal")] + [TestCase("normal-hitnormal")] + [TestCase("hitnormal")] + public void TestDefaultCustomSampleFromBeatmap(string expectedSample) + { + SetupSkins(expectedSample, expectedSample); + + CreateTestWithBeatmap("taiko-hitobject-beatmap-custom-sample-bank.osu"); + + AssertBeatmapLookup(expectedSample); + } + + [TestCase("taiko-normal-hitnormal")] + [TestCase("normal-hitnormal")] + [TestCase("hitnormal")] + public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample) + { + SetupSkins(string.Empty, expectedSample); + + CreateTestWithBeatmap("taiko-hitobject-beatmap-custom-sample-bank.osu"); + + AssertUserLookup(expectedSample); + } + + [TestCase("taiko-normal-hitnormal2")] + [TestCase("normal-hitnormal2")] + public void TestUserSkinLookupIgnoresSampleBank(string unwantedSample) + { + SetupSkins(string.Empty, unwantedSample); + + CreateTestWithBeatmap("taiko-hitobject-beatmap-custom-sample-bank.osu"); + + AssertNoLookup(unwantedSample); + } + } +} From 2df5fafea0ed7a537eb51e2a830f851702f90dc3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Jul 2020 21:39:45 +0200 Subject: [PATCH 098/185] Add failing test case --- .../Gameplay/TestSceneHitObjectSamples.cs | 24 +++++++++++++++---- 1 file changed, 20 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs index a70b08a0d3..c3acc2ebe7 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectSamples.cs @@ -82,12 +82,11 @@ namespace osu.Game.Tests.Gameplay } /// - /// Tests that a hitobject which provides a custom sample set of 2 retrieves the following samples from the user skin when the beatmap does not contain the sample: - /// normal-hitnormal2 + /// Tests that a hitobject which provides a custom sample set of 2 retrieves the following samples from the user skin + /// (ignoring the custom sample set index) when the beatmap skin does not contain the sample: /// normal-hitnormal /// hitnormal /// - [TestCase("normal-hitnormal2")] [TestCase("normal-hitnormal")] [TestCase("hitnormal")] public void TestDefaultCustomSampleFromUserSkinFallback(string expectedSample) @@ -99,6 +98,23 @@ namespace osu.Game.Tests.Gameplay AssertUserLookup(expectedSample); } + /// + /// Tests that a hitobject which provides a custom sample set of 2 does not retrieve a normal-hitnormal2 sample from the user skin + /// if the beatmap skin does not contain the sample. + /// User skins in stable ignore the custom sample set index when performing lookups. + /// + [Test] + public void TestUserSkinLookupIgnoresSampleBank() + { + const string unwanted_sample = "normal-hitnormal2"; + + SetupSkins(string.Empty, unwanted_sample); + + CreateTestWithBeatmap("hitobject-beatmap-custom-sample.osu"); + + AssertNoLookup(unwanted_sample); + } + /// /// Tests that a hitobject which provides a sample file retrieves the sample file from the beatmap skin. /// @@ -183,7 +199,7 @@ namespace osu.Game.Tests.Gameplay string[] expectedSamples = { "normal-hitnormal2", - "normal-hitwhistle2" + "normal-hitwhistle" // user skin lookups ignore custom sample set index }; SetupSkins(expectedSamples[0], expectedSamples[1]); From 2bb436fd3c6dd6a6d172c462ed264ae8bc963faf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 28 Jul 2020 23:52:09 +0200 Subject: [PATCH 099/185] Do not use custom sample banks outside of beatmap skin --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 1 + osu.Game/Skinning/LegacySkin.cs | 26 +++++++++++++++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 87bca856a3..d647bc4a2d 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -14,6 +14,7 @@ namespace osu.Game.Skinning public class LegacyBeatmapSkin : LegacySkin { protected override bool AllowManiaSkin => false; + protected override bool UseCustomSampleBanks => true; public LegacyBeatmapSkin(BeatmapInfo beatmap, IResourceStore storage, AudioManager audioManager) : base(createSkinInfo(beatmap), new LegacySkinResourceStore(beatmap.BeatmapSet, storage), audioManager, beatmap.Path) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 3bbeff9918..187d601812 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -38,6 +38,12 @@ namespace osu.Game.Skinning protected virtual bool AllowManiaSkin => hasKeyTexture.Value; + /// + /// Whether this skin can use samples with a custom bank (custom sample set in stable terminology). + /// Added in order to match sample lookup logic from stable (in stable, only the beatmap skin could use samples with a custom sample bank). + /// + protected virtual bool UseCustomSampleBanks => false; + public new LegacySkinConfiguration Configuration { get => base.Configuration as LegacySkinConfiguration; @@ -337,7 +343,12 @@ namespace osu.Game.Skinning public override SampleChannel GetSample(ISampleInfo sampleInfo) { - foreach (var lookup in sampleInfo.LookupNames) + var lookupNames = sampleInfo.LookupNames; + + if (sampleInfo is HitSampleInfo hitSample) + lookupNames = getLegacyLookupNames(hitSample); + + foreach (var lookup in lookupNames) { var sample = Samples?.Get(lookup); @@ -361,5 +372,18 @@ namespace osu.Game.Skinning string lastPiece = componentName.Split('/').Last(); yield return componentName.StartsWith("Gameplay/taiko/") ? "taiko-" + lastPiece : lastPiece; } + + private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) + { + var lookupNames = hitSample.LookupNames; + + if (!UseCustomSampleBanks && !string.IsNullOrEmpty(hitSample.Suffix)) + // for compatibility with stable, exclude the lookup names with the custom sample bank suffix, if they are not valid for use in this skin. + // using .EndsWith() is intentional as it ensures parity in all edge cases + // (see LegacyTaikoSampleInfo for an example of one - prioritising the taiko prefix should still apply, but the sample bank should not). + lookupNames = hitSample.LookupNames.Where(name => !name.EndsWith(hitSample.Suffix)); + + return lookupNames; + } } } From 971eafde2b05e51231208eff01f326664f6a7a4f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 30 Jul 2020 22:07:07 +0200 Subject: [PATCH 100/185] Move fallback to non-bank samples to centralise hackery --- osu.Game/Skinning/LegacySkin.cs | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 187d601812..fc04383a64 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -356,10 +356,6 @@ namespace osu.Game.Skinning return sample; } - if (sampleInfo is HitSampleInfo hsi) - // Try fallback to non-bank samples. - return Samples?.Get(hsi.Name); - return null; } @@ -383,6 +379,11 @@ namespace osu.Game.Skinning // (see LegacyTaikoSampleInfo for an example of one - prioritising the taiko prefix should still apply, but the sample bank should not). lookupNames = hitSample.LookupNames.Where(name => !name.EndsWith(hitSample.Suffix)); + // also for compatibility, try falling back to non-bank samples (so-called "universal" samples) as the last resort. + // going forward specifying banks shall always be required, even for elements that wouldn't require it on stable, + // which is why this is done locally here. + lookupNames = lookupNames.Append(hitSample.Name); + return lookupNames; } } From 8e49256a5c28bac5d93d765c2efc4833e62bc165 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Jul 2020 09:03:29 +0900 Subject: [PATCH 101/185] Rename and split up statement to make more legible --- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 2644f425a0..7f65a8c022 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -29,7 +29,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces private SpinnerTicks ticks; - private int completeTick; + private int wholeRotationCount; + private SpinnerFill fill; private Container mainContainer; private SpinnerCentreLayer centre; @@ -145,13 +146,24 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces centre.FadeAccent(colour, duration); } - private bool updateCompleteTick() => completeTick != (completeTick = (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360)); + private bool checkNewRotationCount + { + get + { + int rotations = (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360); + + if (wholeRotationCount == rotations) return false; + + wholeRotationCount = rotations; + return true; + } + } protected override void Update() { base.Update(); - if (drawableSpinner.RotationTracker.Complete.Value && updateCompleteTick()) + if (drawableSpinner.RotationTracker.Complete.Value && checkNewRotationCount) { fill.FinishTransforms(false, nameof(Alpha)); fill From cd570433f4aa1779d7bed92aac36e5fd368b6546 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Jul 2020 09:04:20 +0900 Subject: [PATCH 102/185] Move private methods to bottom of class --- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 39 ++++++++++--------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 7f65a8c022..81dea8d1c1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -96,6 +96,26 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces drawableSpinner.State.BindValueChanged(updateStateTransforms, true); } + protected override void Update() + { + base.Update(); + + if (drawableSpinner.RotationTracker.Complete.Value && checkNewRotationCount) + { + fill.FinishTransforms(false, nameof(Alpha)); + fill + .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) + .Then() + .FadeTo(tracking_alpha, 250, Easing.OutQuint); + } + + const float initial_scale = 0.2f; + float targetScale = initial_scale + (1 - initial_scale) * drawableSpinner.Progress; + + fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); + mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation; + } + private void updateStateTransforms(ValueChangedEvent state) { centre.ScaleTo(0); @@ -159,24 +179,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - protected override void Update() - { - base.Update(); - - if (drawableSpinner.RotationTracker.Complete.Value && checkNewRotationCount) - { - fill.FinishTransforms(false, nameof(Alpha)); - fill - .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) - .Then() - .FadeTo(tracking_alpha, 250, Easing.OutQuint); - } - - const float initial_scale = 0.2f; - float targetScale = initial_scale + (1 - initial_scale) * drawableSpinner.Progress; - - fill.Scale = new Vector2((float)Interpolation.Lerp(fill.Scale.X, targetScale, Math.Clamp(Math.Abs(Time.Elapsed) / 100, 0, 1))); - mainContainer.Rotation = drawableSpinner.RotationTracker.Rotation; - } } } From 86784e30ad4cea26f205370fc5c2781ce9802e6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Jul 2020 09:54:30 +0900 Subject: [PATCH 103/185] Fix spacing --- .../Objects/Drawables/Pieces/DefaultSpinnerDisc.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 81dea8d1c1..79bfeedd07 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -178,6 +178,5 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return true; } } - } } From fb74195d83be6b7361740e3584732a52c8795622 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Jul 2020 10:43:54 +0900 Subject: [PATCH 104/185] Move InputManager implementation to base skinnable test scene class --- .../OsuSkinnableTestScene.cs | 15 +++++++++++++++ osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs | 14 -------------- osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs | 2 -- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs index a0a38fc47b..cad98185ce 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuSkinnableTestScene.cs @@ -1,12 +1,27 @@ // 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.Tests.Visual; namespace osu.Game.Rulesets.Osu.Tests { public abstract class OsuSkinnableTestScene : SkinnableTestScene { + private Container content; + + protected override Container Content + { + get + { + if (content == null) + base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); + + return content; + } + } + protected override Ruleset CreateRulesetForSkinProvider() => new OsuRuleset(); } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index a9404f665a..6a689a1f80 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -26,19 +25,6 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class TestSceneSlider : OsuSkinnableTestScene { - 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() diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index 65b338882e..b57561f3e1 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -19,8 +19,6 @@ namespace osu.Game.Rulesets.Osu.Tests public TestSceneSpinner() { - // base.Content.Add(content = new OsuInputManager(new RulesetInfo { ID = 0 })); - AddStep("Miss Big", () => SetContents(() => testSingle(2))); AddStep("Miss Medium", () => SetContents(() => testSingle(5))); AddStep("Miss Small", () => SetContents(() => testSingle(7))); From 6452d62249f1252034fd7c8d85c3929e692c1336 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Jul 2020 12:52:03 +0900 Subject: [PATCH 105/185] 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 0ac766926c..13b4b6ebbb 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -51,7 +51,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 1ececa448c..745555e0e2 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 ef5ba10d17..f1080f0c8b 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -71,7 +71,7 @@ - + From 186b452331437ba41ef07d2559b1b28314d89933 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Jul 2020 14:48:56 +0900 Subject: [PATCH 106/185] Apply common multiplication refactor --- osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs index 618cb0e9ad..72bc3ddc9a 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyNewStyleSpinner.cs @@ -93,8 +93,7 @@ namespace osu.Game.Rulesets.Osu.Skinning spinningMiddle.Rotation = discTop.Rotation = drawableSpinner.RotationTracker.Rotation; discBottom.Rotation = discTop.Rotation / 3; - Scale = new Vector2(final_scale * 0.8f - + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * (final_scale * 0.2f)); + Scale = new Vector2(final_scale * (0.8f + (float)Interpolation.ApplyEasing(Easing.Out, drawableSpinner.Progress) * 0.2f)); } } } From 62ba214dad3d9cd99b760a1bed13e04ea78bd97a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Jul 2020 16:21:47 +0900 Subject: [PATCH 107/185] Use OrderByDescending --- 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 837796287a..dd43092c0d 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -69,8 +69,8 @@ namespace osu.Game.Rulesets // Given name is always going to be equally-or-more qualified than the assembly name. .Where(a => args.Name.Contains(a.GetName().Name, StringComparison.Ordinal)) // Pick the greatest assembly version. - .OrderBy(a => a.GetName().Version) - .LastOrDefault(); + .OrderByDescending(a => a.GetName().Version) + .FirstOrDefault(); if (domainAssembly != null) return domainAssembly; From 88e179d8aa684a1ef8b9971f74cad059f20e6ce3 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 17:40:58 +0900 Subject: [PATCH 108/185] Split out index-only response --- .../TestSceneTimeshiftResultsScreen.cs | 2 +- .../Multiplayer/IndexPlaylistScoresRequest.cs | 2 +- .../Multiplayer/IndexedMultiplayerScores.cs | 27 +++++++++++++++++++ .../Online/Multiplayer/MultiplayerScores.cs | 12 --------- 4 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 osu.Game/Online/Multiplayer/IndexedMultiplayerScores.cs diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 44ca676c4f..8b6d6694c3 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -111,7 +111,7 @@ namespace osu.Game.Tests.Visual.Multiplayer void success() { - r.TriggerSuccess(new MultiplayerScores { Scores = roomScores }); + r.TriggerSuccess(new IndexedMultiplayerScores { Scores = roomScores }); roomsReceived = true; } diff --git a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs index 67793df344..91f24933e1 100644 --- a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs +++ b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs @@ -12,7 +12,7 @@ namespace osu.Game.Online.Multiplayer /// /// Returns a list of scores for the specified playlist item. /// - public class IndexPlaylistScoresRequest : APIRequest + public class IndexPlaylistScoresRequest : APIRequest { private readonly int roomId; private readonly int playlistItemId; diff --git a/osu.Game/Online/Multiplayer/IndexedMultiplayerScores.cs b/osu.Game/Online/Multiplayer/IndexedMultiplayerScores.cs new file mode 100644 index 0000000000..e237b7e3fb --- /dev/null +++ b/osu.Game/Online/Multiplayer/IndexedMultiplayerScores.cs @@ -0,0 +1,27 @@ +// 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 Newtonsoft.Json; + +namespace osu.Game.Online.Multiplayer +{ + /// + /// A object returned via a . + /// + public class IndexedMultiplayerScores : MultiplayerScores + { + /// + /// The total scores in the playlist item. + /// + [JsonProperty("total")] + public int? TotalScores { get; set; } + + /// + /// The user's score, if any. + /// + [JsonProperty("user_score")] + [CanBeNull] + public MultiplayerScore UserScore { get; set; } + } +} diff --git a/osu.Game/Online/Multiplayer/MultiplayerScores.cs b/osu.Game/Online/Multiplayer/MultiplayerScores.cs index 2d0f98e032..8b8dd9e48d 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScores.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerScores.cs @@ -18,18 +18,6 @@ namespace osu.Game.Online.Multiplayer [JsonProperty("scores")] public List Scores { get; set; } - /// - /// The total scores in the playlist item. Only provided via . - /// - [JsonProperty("total")] - public int? TotalScores { get; set; } - - /// - /// The user's score, if any. Only provided via . - /// - [JsonProperty("user_score")] - public MultiplayerScore UserScore { get; set; } - /// /// The parameters to be used to fetch the next page. /// From eadef53e68cd1612f8bb0b0a6d7260f5631ad07f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 17:43:40 +0900 Subject: [PATCH 109/185] Add more annotations --- osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs b/osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs index e83cc1b753..2ac62d0300 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScoresAround.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerScoresAround.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 Newtonsoft.Json; namespace osu.Game.Online.Multiplayer @@ -14,12 +15,14 @@ namespace osu.Game.Online.Multiplayer /// Scores sorted "higher" than the user's score, depending on the sorting order. ///
[JsonProperty("higher")] + [CanBeNull] public MultiplayerScores Higher { get; set; } /// /// Scores sorted "lower" than the user's score, depending on the sorting order. /// [JsonProperty("lower")] + [CanBeNull] public MultiplayerScores Lower { get; set; } } } From 6d728d27fc7d150c313ec442b810772f174995cf Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 17:58:48 +0900 Subject: [PATCH 110/185] Cleanup/consolidate indexing request --- .../Multi/Ranking/TimeshiftResultsScreen.cs | 52 +++++++++++-------- 1 file changed, 30 insertions(+), 22 deletions(-) diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index 648bee385c..0ef4953e83 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -75,21 +76,8 @@ namespace osu.Game.Screens.Multi.Ranking performSuccessCallback(scoresCallback, allScores); }; - userScoreReq.Failure += _ => - { - // Fallback to a normal index. - var indexReq = new IndexPlaylistScoresRequest(roomId, playlistItem.ID); - - indexReq.Success += r => - { - performSuccessCallback(scoresCallback, r.Scores); - lowerScores = r; - }; - - indexReq.Failure += __ => loadingLayer.Hide(); - - api.Queue(indexReq); - }; + // On failure, fallback to a normal index. + userScoreReq.Failure += _ => api.Queue(createIndexRequest(scoresCallback)); return userScoreReq; } @@ -103,16 +91,30 @@ namespace osu.Game.Screens.Multi.Ranking if (pivot?.Cursor == null) return null; - var indexReq = new IndexPlaylistScoresRequest(roomId, playlistItem.ID, pivot.Cursor, pivot.Params); + return createIndexRequest(scoresCallback, pivot); + } + + /// + /// Creates a with an optional score pivot. + /// + /// Does not queue the request. + /// The callback to perform with the resulting scores. + /// An optional score pivot to retrieve scores around. Can be null to retrieve scores from the highest score. + /// The indexing . + private APIRequest createIndexRequest(Action> scoresCallback, [CanBeNull] MultiplayerScores pivot = null) + { + var indexReq = pivot != null + ? new IndexPlaylistScoresRequest(roomId, playlistItem.ID, pivot.Cursor, pivot.Params) + : new IndexPlaylistScoresRequest(roomId, playlistItem.ID); indexReq.Success += r => { - if (direction == -1) - higherScores = r; - else + if (pivot == lowerScores) lowerScores = r; + else + higherScores = r; - performSuccessCallback(scoresCallback, r.Scores); + performSuccessCallback(scoresCallback, r.Scores, pivot); }; indexReq.Failure += _ => loadingLayer.Hide(); @@ -120,7 +122,13 @@ namespace osu.Game.Screens.Multi.Ranking return indexReq; } - private void performSuccessCallback(Action> callback, List scores) + /// + /// Transforms returned into s, ensure the is put into a sane state, and invokes a given success callback. + /// + /// The callback to invoke with the final s. + /// The s that were retrieved from s. + /// An optional pivot around which the scores were retrieved. + private void performSuccessCallback([NotNull] Action> callback, [NotNull] List scores, [CanBeNull] MultiplayerScores pivot = null) { var scoreInfos = new List(scores.Select(s => s.CreateScoreInfo(playlistItem))); @@ -136,7 +144,7 @@ namespace osu.Game.Screens.Multi.Ranking } // Invoke callback to add the scores. Exclude the user's current score which was added previously. - callback?.Invoke(scoreInfos.Where(s => s.ID != Score?.OnlineScoreID)); + callback.Invoke(scoreInfos.Where(s => s.ID != Score?.OnlineScoreID)); loadingLayer.Hide(); } From 9966d4f3b3b5da3c364157ab38dbadd0343152ae Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 19:57:05 +0900 Subject: [PATCH 111/185] Add more loading spinners --- .../Multi/Ranking/TimeshiftResultsScreen.cs | 81 ++++++++++++++++--- osu.Game/Screens/Ranking/ResultsScreen.cs | 30 +++---- osu.Game/Screens/Ranking/ScorePanelList.cs | 10 +++ 3 files changed, 97 insertions(+), 24 deletions(-) diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index 0ef4953e83..87de9fd72a 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -22,7 +22,9 @@ namespace osu.Game.Screens.Multi.Ranking private readonly int roomId; private readonly PlaylistItem playlistItem; - private LoadingSpinner loadingLayer; + private LoadingSpinner leftLoadingLayer; + private LoadingSpinner centreLoadingLayer; + private LoadingSpinner rightLoadingLayer; private MultiplayerScores higherScores; private MultiplayerScores lowerScores; @@ -39,13 +41,29 @@ namespace osu.Game.Screens.Multi.Ranking [BackgroundDependencyLoader] private void load() { - AddInternal(loadingLayer = new LoadingLayer + AddInternal(new Container { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - X = -10, - State = { Value = Score == null ? Visibility.Visible : Visibility.Hidden }, - Padding = new MarginPadding { Bottom = TwoLayerButton.SIZE_EXTENDED.Y } + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Bottom = TwoLayerButton.SIZE_EXTENDED.Y }, + Children = new Drawable[] + { + leftLoadingLayer = new PanelListLoadingSpinner(ScorePanelList) + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.Centre, + }, + centreLoadingLayer = new PanelListLoadingSpinner(ScorePanelList) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + State = { Value = Score == null ? Visibility.Visible : Visibility.Hidden }, + }, + rightLoadingLayer = new PanelListLoadingSpinner(ScorePanelList) + { + Anchor = Anchor.CentreRight, + Origin = Anchor.Centre, + }, + } }); } @@ -91,6 +109,11 @@ namespace osu.Game.Screens.Multi.Ranking if (pivot?.Cursor == null) return null; + if (pivot == higherScores) + leftLoadingLayer.Show(); + else + rightLoadingLayer.Show(); + return createIndexRequest(scoresCallback, pivot); } @@ -114,10 +137,10 @@ namespace osu.Game.Screens.Multi.Ranking else higherScores = r; - performSuccessCallback(scoresCallback, r.Scores, pivot); + performSuccessCallback(scoresCallback, r.Scores, r); }; - indexReq.Failure += _ => loadingLayer.Hide(); + indexReq.Failure += _ => hideLoadingSpinners(pivot); return indexReq; } @@ -146,7 +169,45 @@ namespace osu.Game.Screens.Multi.Ranking // Invoke callback to add the scores. Exclude the user's current score which was added previously. callback.Invoke(scoreInfos.Where(s => s.ID != Score?.OnlineScoreID)); - loadingLayer.Hide(); + hideLoadingSpinners(pivot); + } + + private void hideLoadingSpinners([CanBeNull] MultiplayerScores pivot = null) + { + centreLoadingLayer.Hide(); + + if (pivot == lowerScores) + rightLoadingLayer.Hide(); + else if (pivot == higherScores) + leftLoadingLayer.Hide(); + } + + private class PanelListLoadingSpinner : LoadingSpinner + { + private readonly ScorePanelList list; + + /// + /// Creates a new . + /// + /// The list to track. + /// Whether the spinner should have a surrounding black box for visibility. + public PanelListLoadingSpinner(ScorePanelList list, bool withBox = true) + : base(withBox) + { + this.list = list; + } + + protected override void Update() + { + base.Update(); + + float panelOffset = list.DrawWidth / 2 - ScorePanel.EXPANDED_WIDTH; + + if ((Anchor & Anchor.x0) > 0) + X = (float)(panelOffset - list.Current); + else if ((Anchor & Anchor.x2) > 0) + X = (float)(list.ScrollableExtent - list.Current - panelOffset); + } } } } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 254ab76f5b..2506a63cc0 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -37,7 +37,8 @@ namespace osu.Game.Screens.Ranking public readonly Bindable SelectedScore = new Bindable(); public readonly ScoreInfo Score; - private readonly bool allowRetry; + + protected ScorePanelList ScorePanelList { get; private set; } [Resolved(CanBeNull = true)] private Player player { get; set; } @@ -47,12 +48,13 @@ namespace osu.Game.Screens.Ranking private StatisticsPanel statisticsPanel; private Drawable bottomPanel; - private ScorePanelList scorePanelList; private Container detachedPanelContainer; private bool fetchedInitialScores; private APIRequest nextPageRequest; + private readonly bool allowRetry; + protected ResultsScreen(ScoreInfo score, bool allowRetry = true) { Score = score; @@ -87,7 +89,7 @@ namespace osu.Game.Screens.Ranking RelativeSizeAxes = Axes.Both, Score = { BindTarget = SelectedScore } }, - scorePanelList = new ScorePanelList + ScorePanelList = new ScorePanelList { RelativeSizeAxes = Axes.Both, SelectedScore = { BindTarget = SelectedScore }, @@ -145,7 +147,7 @@ namespace osu.Game.Screens.Ranking }; if (Score != null) - scorePanelList.AddScore(Score); + ScorePanelList.AddScore(Score); if (player != null && allowRetry) { @@ -181,9 +183,9 @@ namespace osu.Game.Screens.Ranking if (fetchedInitialScores && nextPageRequest == null) { - if (scorePanelList.IsScrolledToStart) + if (ScorePanelList.IsScrolledToStart) nextPageRequest = FetchNextPage(-1, fetchScoresCallback); - else if (scorePanelList.IsScrolledToEnd) + else if (ScorePanelList.IsScrolledToEnd) nextPageRequest = FetchNextPage(1, fetchScoresCallback); if (nextPageRequest != null) @@ -249,7 +251,7 @@ namespace osu.Game.Screens.Ranking private void addScore(ScoreInfo score) { - var panel = scorePanelList.AddScore(score); + var panel = ScorePanelList.AddScore(score); if (detachedPanel != null) panel.Alpha = 0; @@ -262,11 +264,11 @@ namespace osu.Game.Screens.Ranking if (state.NewValue == Visibility.Visible) { // Detach the panel in its original location, and move into the desired location in the local container. - var expandedPanel = scorePanelList.GetPanelForScore(SelectedScore.Value); + var expandedPanel = ScorePanelList.GetPanelForScore(SelectedScore.Value); var screenSpacePos = expandedPanel.ScreenSpaceDrawQuad.TopLeft; // Detach and move into the local container. - scorePanelList.Detach(expandedPanel); + ScorePanelList.Detach(expandedPanel); detachedPanelContainer.Add(expandedPanel); // Move into its original location in the local container first, then to the final location. @@ -276,9 +278,9 @@ namespace osu.Game.Screens.Ranking .MoveTo(new Vector2(StatisticsPanel.SIDE_PADDING, origLocation.Y), 150, Easing.OutQuint); // Hide contracted panels. - foreach (var contracted in scorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted)) + foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted)) contracted.FadeOut(150, Easing.OutQuint); - scorePanelList.HandleInput = false; + ScorePanelList.HandleInput = false; // Dim background. Background.FadeTo(0.1f, 150); @@ -291,7 +293,7 @@ namespace osu.Game.Screens.Ranking // Remove from the local container and re-attach. detachedPanelContainer.Remove(detachedPanel); - scorePanelList.Attach(detachedPanel); + ScorePanelList.Attach(detachedPanel); // Move into its original location in the attached container first, then to the final location. var origLocation = detachedPanel.Parent.ToLocalSpace(screenSpacePos); @@ -300,9 +302,9 @@ namespace osu.Game.Screens.Ranking .MoveTo(new Vector2(0, origLocation.Y), 150, Easing.OutQuint); // Show contracted panels. - foreach (var contracted in scorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted)) + foreach (var contracted in ScorePanelList.GetScorePanels().Where(p => p.State == PanelState.Contracted)) contracted.FadeIn(150, Easing.OutQuint); - scorePanelList.HandleInput = true; + ScorePanelList.HandleInput = true; // Un-dim background. Background.FadeTo(0.5f, 150); diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index b2e1e91831..10bd99c8ce 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -41,6 +41,16 @@ namespace osu.Game.Screens.Ranking ///
public bool IsScrolledToEnd => flow.Count > 0 && scroll.ScrollableExtent > 0 && scroll.IsScrolledToEnd(scroll_endpoint_distance); + /// + /// The current scroll position. + /// + public double Current => scroll.Current; + + /// + /// The scrollable extent. + /// + public double ScrollableExtent => scroll.ScrollableExtent; + /// /// An action to be invoked if a is clicked while in an expanded state. /// From 4d2a677080beaed2a64c6ece28c663cf4d9f3aff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Jul 2020 20:33:18 +0900 Subject: [PATCH 112/185] Fix next track starting before previous one is paused Closes #9651. --- osu.Game/Overlays/MusicController.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 546f7a1ec4..212d4d4850 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -262,7 +262,11 @@ namespace osu.Game.Overlays { if (beatmap is Bindable working) working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); - beatmap.Value.Track.Restart(); + + // if not scheduled, the previously track will be stopped one frame later (see ScheduleAfterChildren logic in GameBase). + // we probably want to move this to a central method for switching to a new working beatmap in the future. + Schedule(() => beatmap.Value.Track.Restart()); + return true; } From 8e8a11bb72f558c0fb8144390bdab7e5d928ea73 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 20:55:26 +0900 Subject: [PATCH 113/185] Add APIRequest.TriggerFailure() for testing --- osu.Game/Online/API/APIRequest.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIRequest.cs b/osu.Game/Online/API/APIRequest.cs index 2115326cc2..6912d9b629 100644 --- a/osu.Game/Online/API/APIRequest.cs +++ b/osu.Game/Online/API/APIRequest.cs @@ -136,6 +136,11 @@ namespace osu.Game.Online.API Success?.Invoke(); } + internal void TriggerFailure(Exception e) + { + Failure?.Invoke(e); + } + public void Cancel() => Fail(new OperationCanceledException(@"Request cancelled")); public void Fail(Exception e) @@ -166,7 +171,7 @@ namespace osu.Game.Online.API } Logger.Log($@"Failing request {this} ({e})", LoggingTarget.Network); - pendingFailure = () => Failure?.Invoke(e); + pendingFailure = () => TriggerFailure(e); checkAndScheduleFailure(); } From 2b77f99f56af81771980d77e8e5e57f02ff60f59 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 20:55:44 +0900 Subject: [PATCH 114/185] Initialise some response parameters --- osu.Game/Online/API/Requests/Cursor.cs | 2 +- osu.Game/Online/Multiplayer/IndexScoresParams.cs | 2 +- osu.Game/Online/Multiplayer/MultiplayerScores.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/API/Requests/Cursor.cs b/osu.Game/Online/API/Requests/Cursor.cs index f21445ca32..3de8db770c 100644 --- a/osu.Game/Online/API/Requests/Cursor.cs +++ b/osu.Game/Online/API/Requests/Cursor.cs @@ -15,6 +15,6 @@ namespace osu.Game.Online.API.Requests { [UsedImplicitly] [JsonExtensionData] - public IDictionary Properties; + public IDictionary Properties { get; set; } = new Dictionary(); } } diff --git a/osu.Game/Online/Multiplayer/IndexScoresParams.cs b/osu.Game/Online/Multiplayer/IndexScoresParams.cs index 8160dfefaf..a511e9a780 100644 --- a/osu.Game/Online/Multiplayer/IndexScoresParams.cs +++ b/osu.Game/Online/Multiplayer/IndexScoresParams.cs @@ -15,6 +15,6 @@ namespace osu.Game.Online.Multiplayer { [UsedImplicitly] [JsonExtensionData] - public IDictionary Properties; + public IDictionary Properties { get; set; } = new Dictionary(); } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerScores.cs b/osu.Game/Online/Multiplayer/MultiplayerScores.cs index 8b8dd9e48d..7b9dcff828 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScores.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerScores.cs @@ -16,12 +16,12 @@ namespace osu.Game.Online.Multiplayer /// The scores. /// [JsonProperty("scores")] - public List Scores { get; set; } + public List Scores { get; set; } = new List(); /// /// The parameters to be used to fetch the next page. /// [JsonProperty("params")] - public IndexScoresParams Params { get; set; } + public IndexScoresParams Params { get; set; } = new IndexScoresParams(); } } From a4a4c8761241eabdf82a35fe02686aa5453c1d6e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 21:21:48 +0900 Subject: [PATCH 115/185] Fix incorrect score id being used --- osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index 87de9fd72a..b0cf63a7a9 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -167,7 +167,7 @@ namespace osu.Game.Screens.Multi.Ranking } // Invoke callback to add the scores. Exclude the user's current score which was added previously. - callback.Invoke(scoreInfos.Where(s => s.ID != Score?.OnlineScoreID)); + callback.Invoke(scoreInfos.Where(s => s.OnlineScoreID != Score?.OnlineScoreID)); hideLoadingSpinners(pivot); } From 17018ffa8b616fe8da85c7acce17ad3744c9c018 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 21:33:04 +0900 Subject: [PATCH 116/185] Fix potentially triggering new requests too early --- osu.Game/Screens/Ranking/ResultsScreen.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 2506a63cc0..c95cf1066e 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -190,8 +190,9 @@ namespace osu.Game.Screens.Ranking if (nextPageRequest != null) { - nextPageRequest.Success += () => nextPageRequest = null; - nextPageRequest.Failure += _ => nextPageRequest = null; + // Scheduled after children to give the list a chance to update its scroll position and not potentially trigger a second request too early. + nextPageRequest.Success += () => ScheduleAfterChildren(() => nextPageRequest = null); + nextPageRequest.Failure += _ => ScheduleAfterChildren(() => nextPageRequest = null); api.Queue(nextPageRequest); } From f1e721e396988d5410b32fa3402b612f59dca23d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 21:39:50 +0900 Subject: [PATCH 117/185] Rewrite test scene and add more tests --- .../TestSceneTimeshiftResultsScreen.cs | 344 +++++++++++++++--- .../Multiplayer/IndexPlaylistScoresRequest.cs | 31 +- .../Multi/Ranking/TimeshiftResultsScreen.cs | 23 +- 3 files changed, 329 insertions(+), 69 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 8b6d6694c3..628d08a314 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -3,14 +3,24 @@ using System; using System.Collections.Generic; +using System.Linq; +using System.Net; using System.Threading.Tasks; +using JetBrains.Annotations; +using Newtonsoft.Json.Linq; using NUnit.Framework; +using osu.Framework.Graphics.Containers; +using osu.Framework.Testing; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; +using osu.Game.Online.API.Requests; using osu.Game.Online.Multiplayer; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Multi.Ranking; +using osu.Game.Screens.Ranking; using osu.Game.Tests.Beatmaps; using osu.Game.Users; @@ -18,43 +28,134 @@ namespace osu.Game.Tests.Visual.Multiplayer { public class TestSceneTimeshiftResultsScreen : ScreenTestScene { - private bool roomsReceived; + private const int scores_per_result = 10; + + private TestResultsScreen resultsScreen; + private int currentScoreId; + private bool requestComplete; [SetUp] public void Setup() => Schedule(() => { - roomsReceived = false; + currentScoreId = 0; + requestComplete = false; bindHandler(); }); [Test] - public void TestShowResultsWithScore() + public void TestShowWithUserScore() { - createResults(new TestScoreInfo(new OsuRuleset().RulesetInfo)); - AddWaitStep("wait for display", 5); + ScoreInfo userScore = null; + + AddStep("bind user score info handler", () => + { + userScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { OnlineScoreID = currentScoreId++ }; + bindHandler(userScore: userScore); + }); + + createResults(() => userScore); + waitForDisplay(); + + AddAssert("user score selected", () => this.ChildrenOfType().Single(p => p.Score.OnlineScoreID == userScore.OnlineScoreID).State == PanelState.Expanded); } [Test] - public void TestShowResultsNullScore() + public void TestShowNullUserScore() { - createResults(null); - AddWaitStep("wait for display", 5); + createResults(); + waitForDisplay(); + + AddAssert("top score selected", () => this.ChildrenOfType().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded); } [Test] - public void TestShowResultsNullScoreWithDelay() + public void TestShowUserScoreWithDelay() + { + ScoreInfo userScore = null; + + AddStep("bind user score info handler", () => + { + userScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { OnlineScoreID = currentScoreId++ }; + bindHandler(3000, userScore); + }); + + createResults(() => userScore); + waitForDisplay(); + + AddAssert("more than 1 panel displayed", () => this.ChildrenOfType().Count() > 1); + AddAssert("user score selected", () => this.ChildrenOfType().Single(p => p.Score.OnlineScoreID == userScore.OnlineScoreID).State == PanelState.Expanded); + } + + [Test] + public void TestShowNullUserScoreWithDelay() { AddStep("bind delayed handler", () => bindHandler(3000)); - createResults(null); - AddUntilStep("wait for rooms to be received", () => roomsReceived); - AddWaitStep("wait for display", 5); + + createResults(); + waitForDisplay(); + + AddAssert("top score selected", () => this.ChildrenOfType().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded); } - private void createResults(ScoreInfo score) + [Test] + public void TestFetchWhenScrolledToTheRight() + { + createResults(); + waitForDisplay(); + + AddStep("bind delayed handler", () => bindHandler(3000)); + + for (int i = 0; i < 2; i++) + { + int beforePanelCount = 0; + + AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType().Count()); + AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType().Single().ScrollToEnd(false)); + + AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible); + waitForDisplay(); + + AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType().Count() == beforePanelCount + scores_per_result); + AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden); + } + } + + [Test] + public void TestFetchWhenScrolledToTheLeft() + { + ScoreInfo userScore = null; + + AddStep("bind user score info handler", () => + { + userScore = new TestScoreInfo(new OsuRuleset().RulesetInfo) { OnlineScoreID = currentScoreId++ }; + bindHandler(userScore: userScore); + }); + + createResults(() => userScore); + waitForDisplay(); + + AddStep("bind delayed handler", () => bindHandler(3000)); + + for (int i = 0; i < 2; i++) + { + int beforePanelCount = 0; + + AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType().Count()); + AddStep("scroll to left", () => resultsScreen.ScorePanelList.ChildrenOfType().Single().ScrollToStart(false)); + + AddAssert("left loading spinner shown", () => resultsScreen.LeftSpinner.State.Value == Visibility.Visible); + waitForDisplay(); + + AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType().Count() == beforePanelCount + scores_per_result); + AddAssert("left loading spinner hidden", () => resultsScreen.LeftSpinner.State.Value == Visibility.Hidden); + } + } + + private void createResults(Func getScore = null) { AddStep("load results", () => { - LoadScreen(new TimeshiftResultsScreen(score, 1, new PlaylistItem + LoadScreen(resultsScreen = new TestResultsScreen(getScore?.Invoke(), 1, new PlaylistItem { Beatmap = { Value = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo }, Ruleset = { Value = new OsuRuleset().RulesetInfo } @@ -62,62 +163,213 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } - private void bindHandler(double delay = 0) + private void waitForDisplay() { - var roomScores = new List(); + AddUntilStep("wait for request to complete", () => requestComplete); + AddWaitStep("wait for display", 5); + } - for (int i = 0; i < 10; i++) + private void bindHandler(double delay = 0, ScoreInfo userScore = null, bool failRequests = false) => ((DummyAPIAccess)API).HandleRequest = request => + { + requestComplete = false; + + if (failRequests) { - roomScores.Add(new MultiplayerScore + triggerFail(request, delay); + return; + } + + switch (request) + { + case ShowPlaylistUserScoreRequest s: + if (userScore == null) + triggerFail(s, delay); + else + triggerSuccess(s, createUserResponse(userScore), delay); + break; + + case IndexPlaylistScoresRequest i: + triggerSuccess(i, createIndexResponse(i), delay); + break; + } + }; + + private void triggerSuccess(APIRequest req, T result, double delay) + where T : class + { + if (delay == 0) + success(); + else + { + Task.Run(async () => { - ID = i, - Accuracy = 0.9 - 0.01 * i, - EndedAt = DateTimeOffset.Now.Subtract(TimeSpan.FromHours(i)), + await Task.Delay(TimeSpan.FromMilliseconds(delay)); + Schedule(success); + }); + } + + void success() + { + requestComplete = true; + req.TriggerSuccess(result); + } + } + + private void triggerFail(APIRequest req, double delay) + { + if (delay == 0) + fail(); + else + { + Task.Run(async () => + { + await Task.Delay(TimeSpan.FromMilliseconds(delay)); + Schedule(fail); + }); + } + + void fail() + { + requestComplete = true; + req.TriggerFailure(new WebException("Failed.")); + } + } + + private MultiplayerScore createUserResponse([NotNull] ScoreInfo userScore) + { + var multiplayerUserScore = new MultiplayerScore + { + ID = (int)(userScore.OnlineScoreID ?? currentScoreId++), + Accuracy = userScore.Accuracy, + EndedAt = userScore.Date, + Passed = userScore.Passed, + Rank = userScore.Rank, + MaxCombo = userScore.MaxCombo, + TotalScore = userScore.TotalScore, + User = userScore.User, + Statistics = userScore.Statistics, + ScoresAround = new MultiplayerScoresAround + { + Higher = new MultiplayerScores(), + Lower = new MultiplayerScores() + } + }; + + for (int i = 1; i <= scores_per_result; i++) + { + multiplayerUserScore.ScoresAround.Lower.Scores.Add(new MultiplayerScore + { + ID = currentScoreId++, + Accuracy = userScore.Accuracy, + EndedAt = userScore.Date, Passed = true, - Rank = ScoreRank.B, - MaxCombo = 999, - TotalScore = 999999 - i * 1000, + Rank = userScore.Rank, + MaxCombo = userScore.MaxCombo, + TotalScore = userScore.TotalScore - i, User = new User { Id = 2, Username = $"peppy{i}", CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", }, - Statistics = + Statistics = userScore.Statistics + }); + + multiplayerUserScore.ScoresAround.Higher.Scores.Add(new MultiplayerScore + { + ID = currentScoreId++, + Accuracy = userScore.Accuracy, + EndedAt = userScore.Date, + Passed = true, + Rank = userScore.Rank, + MaxCombo = userScore.MaxCombo, + TotalScore = userScore.TotalScore + i, + User = new User + { + Id = 2, + Username = $"peppy{i}", + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }, + Statistics = userScore.Statistics + }); + } + + addCursor(multiplayerUserScore.ScoresAround.Lower); + addCursor(multiplayerUserScore.ScoresAround.Higher); + + return multiplayerUserScore; + } + + private IndexedMultiplayerScores createIndexResponse(IndexPlaylistScoresRequest req) + { + var result = new IndexedMultiplayerScores(); + + long startTotalScore = req.Cursor?.Properties["total_score"].ToObject() ?? 1000000; + string sort = req.IndexParams?.Properties["sort"].ToObject() ?? "score_desc"; + + for (int i = 1; i <= scores_per_result; i++) + { + result.Scores.Add(new MultiplayerScore + { + ID = currentScoreId++, + Accuracy = 1, + EndedAt = DateTimeOffset.Now, + Passed = true, + Rank = ScoreRank.X, + MaxCombo = 1000, + TotalScore = startTotalScore + (sort == "score_asc" ? i : -i), + User = new User + { + Id = 2, + Username = $"peppy{i}", + CoverUrl = "https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + }, + Statistics = new Dictionary { { HitResult.Miss, 1 }, { HitResult.Meh, 50 }, { HitResult.Good, 100 }, - { HitResult.Great, 300 }, + { HitResult.Great, 300 } } }); } - ((DummyAPIAccess)API).HandleRequest = request => + addCursor(result); + + return result; + } + + private void addCursor(MultiplayerScores scores) + { + scores.Cursor = new Cursor { - switch (request) + Properties = new Dictionary { - case IndexPlaylistScoresRequest r: - if (delay == 0) - success(); - else - { - Task.Run(async () => - { - await Task.Delay(TimeSpan.FromMilliseconds(delay)); - Schedule(success); - }); - } + { "total_score", JToken.FromObject(scores.Scores[^1].TotalScore) }, + { "score_id", JToken.FromObject(scores.Scores[^1].ID) }, + } + }; - void success() - { - r.TriggerSuccess(new IndexedMultiplayerScores { Scores = roomScores }); - roomsReceived = true; - } - - break; + scores.Params = new IndexScoresParams + { + Properties = new Dictionary + { + { "sort", JToken.FromObject(scores.Scores[^1].TotalScore > scores.Scores[^2].TotalScore ? "score_asc" : "score_desc") } } }; } + + private class TestResultsScreen : TimeshiftResultsScreen + { + public new LoadingSpinner LeftSpinner => base.LeftSpinner; + public new LoadingSpinner CentreSpinner => base.CentreSpinner; + public new LoadingSpinner RightSpinner => base.RightSpinner; + public new ScorePanelList ScorePanelList => base.ScorePanelList; + + public TestResultsScreen(ScoreInfo score, int roomId, PlaylistItem playlistItem, bool allowRetry = true) + : base(score, roomId, playlistItem, allowRetry) + { + } + } } } diff --git a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs index 91f24933e1..684d0aecd8 100644 --- a/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.cs +++ b/osu.Game/Online/Multiplayer/IndexPlaylistScoresRequest.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 JetBrains.Annotations; using osu.Framework.IO.Network; using osu.Game.Extensions; @@ -14,39 +15,45 @@ namespace osu.Game.Online.Multiplayer /// public class IndexPlaylistScoresRequest : APIRequest { - private readonly int roomId; - private readonly int playlistItemId; - private readonly Cursor cursor; - private readonly IndexScoresParams indexParams; + public readonly int RoomId; + public readonly int PlaylistItemId; + + [CanBeNull] + public readonly Cursor Cursor; + + [CanBeNull] + public readonly IndexScoresParams IndexParams; public IndexPlaylistScoresRequest(int roomId, int playlistItemId) { - this.roomId = roomId; - this.playlistItemId = playlistItemId; + RoomId = roomId; + PlaylistItemId = playlistItemId; } public IndexPlaylistScoresRequest(int roomId, int playlistItemId, [NotNull] Cursor cursor, [NotNull] IndexScoresParams indexParams) : this(roomId, playlistItemId) { - this.cursor = cursor; - this.indexParams = indexParams; + Cursor = cursor; + IndexParams = indexParams; } protected override WebRequest CreateWebRequest() { var req = base.CreateWebRequest(); - if (cursor != null) + if (Cursor != null) { - req.AddCursor(cursor); + Debug.Assert(IndexParams != null); - foreach (var (key, value) in indexParams.Properties) + req.AddCursor(Cursor); + + foreach (var (key, value) in IndexParams.Properties) req.AddParameter(key, value.ToString()); } return req; } - protected override string Target => $@"rooms/{roomId}/playlist/{playlistItemId}/scores"; + protected override string Target => $@"rooms/{RoomId}/playlist/{PlaylistItemId}/scores"; } } diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index b0cf63a7a9..e212bd4a82 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -22,9 +22,10 @@ namespace osu.Game.Screens.Multi.Ranking private readonly int roomId; private readonly PlaylistItem playlistItem; - private LoadingSpinner leftLoadingLayer; - private LoadingSpinner centreLoadingLayer; - private LoadingSpinner rightLoadingLayer; + protected LoadingSpinner LeftSpinner { get; private set; } + protected LoadingSpinner CentreSpinner { get; private set; } + protected LoadingSpinner RightSpinner { get; private set; } + private MultiplayerScores higherScores; private MultiplayerScores lowerScores; @@ -47,18 +48,18 @@ namespace osu.Game.Screens.Multi.Ranking Padding = new MarginPadding { Bottom = TwoLayerButton.SIZE_EXTENDED.Y }, Children = new Drawable[] { - leftLoadingLayer = new PanelListLoadingSpinner(ScorePanelList) + LeftSpinner = new PanelListLoadingSpinner(ScorePanelList) { Anchor = Anchor.CentreLeft, Origin = Anchor.Centre, }, - centreLoadingLayer = new PanelListLoadingSpinner(ScorePanelList) + CentreSpinner = new PanelListLoadingSpinner(ScorePanelList) { Anchor = Anchor.Centre, Origin = Anchor.Centre, State = { Value = Score == null ? Visibility.Visible : Visibility.Hidden }, }, - rightLoadingLayer = new PanelListLoadingSpinner(ScorePanelList) + RightSpinner = new PanelListLoadingSpinner(ScorePanelList) { Anchor = Anchor.CentreRight, Origin = Anchor.Centre, @@ -110,9 +111,9 @@ namespace osu.Game.Screens.Multi.Ranking return null; if (pivot == higherScores) - leftLoadingLayer.Show(); + LeftSpinner.Show(); else - rightLoadingLayer.Show(); + RightSpinner.Show(); return createIndexRequest(scoresCallback, pivot); } @@ -174,12 +175,12 @@ namespace osu.Game.Screens.Multi.Ranking private void hideLoadingSpinners([CanBeNull] MultiplayerScores pivot = null) { - centreLoadingLayer.Hide(); + CentreSpinner.Hide(); if (pivot == lowerScores) - rightLoadingLayer.Hide(); + RightSpinner.Hide(); else if (pivot == higherScores) - leftLoadingLayer.Hide(); + LeftSpinner.Hide(); } private class PanelListLoadingSpinner : LoadingSpinner From e8f75a78e8ce4d820d72d05efacc6cec70f51264 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 31 Jul 2020 22:02:12 +0900 Subject: [PATCH 118/185] Also fix second instance of same execution --- osu.Game/Overlays/MusicController.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 212d4d4850..a990f9a6ab 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -236,8 +236,8 @@ namespace osu.Game.Overlays { if (beatmap is Bindable working) working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); - beatmap.Value.Track.Restart(); + restartTrack(); return PreviousTrackResult.Previous; } @@ -263,16 +263,20 @@ namespace osu.Game.Overlays if (beatmap is Bindable working) working.Value = beatmaps.GetWorkingBeatmap(playable.Beatmaps.First(), beatmap.Value); - // if not scheduled, the previously track will be stopped one frame later (see ScheduleAfterChildren logic in GameBase). - // we probably want to move this to a central method for switching to a new working beatmap in the future. - Schedule(() => beatmap.Value.Track.Restart()); - + restartTrack(); return true; } return false; } + private void restartTrack() + { + // if not scheduled, the previously track will be stopped one frame later (see ScheduleAfterChildren logic in GameBase). + // we probably want to move this to a central method for switching to a new working beatmap in the future. + Schedule(() => beatmap.Value.Track.Restart()); + } + private WorkingBeatmap current; private TrackChangeDirection? queuedDirection; From b361761d869950fd802df2ae551e6819037233da Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Tue, 28 Jul 2020 22:08:10 +0900 Subject: [PATCH 119/185] Add position display in contracted score panels --- .../Online/Multiplayer/MultiplayerScore.cs | 3 +- osu.Game/Scoring/ScoreInfo.cs | 7 ++++ .../Contracted/ContractedPanelTopContent.cs | 37 +++++++++++++++++++ osu.Game/Screens/Ranking/ScorePanel.cs | 1 + osu.Game/Tests/TestScoreInfo.cs | 2 + 5 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs diff --git a/osu.Game/Online/Multiplayer/MultiplayerScore.cs b/osu.Game/Online/Multiplayer/MultiplayerScore.cs index 1793ba72ef..8191003aad 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerScore.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerScore.cs @@ -80,7 +80,8 @@ namespace osu.Game.Online.Multiplayer Date = EndedAt, Hash = string.Empty, // todo: temporary? Rank = Rank, - Mods = Mods?.Select(m => m.ToMod(rulesetInstance)).ToArray() ?? Array.Empty() + Mods = Mods?.Select(m => m.ToMod(rulesetInstance)).ToArray() ?? Array.Empty(), + Position = Position, }; return scoreInfo; diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 84c0d5b54e..efcf1737c9 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -179,6 +179,13 @@ namespace osu.Game.Scoring [JsonIgnore] public bool DeletePending { get; set; } + /// + /// The position of this score, starting at 1. + /// + [NotMapped] + [JsonProperty("position")] + public int? Position { get; set; } + [Serializable] protected class DeserializedMod : IMod { diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.cs new file mode 100644 index 0000000000..0935ee7fb2 --- /dev/null +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelTopContent.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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osu.Game.Scoring; + +namespace osu.Game.Screens.Ranking.Contracted +{ + public class ContractedPanelTopContent : CompositeDrawable + { + private readonly ScoreInfo score; + + public ContractedPanelTopContent(ScoreInfo score) + { + this.score = score; + + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load() + { + InternalChild = new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Y = 6, + Text = score.Position != null ? $"#{score.Position}" : string.Empty, + Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold) + }; + } + } +} diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 5da432d5b2..b32da805e4 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -213,6 +213,7 @@ namespace osu.Game.Screens.Ranking topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint); + topLayerContentContainer.Add(middleLayerContent = new ContractedPanelTopContent(Score).With(d => d.Alpha = 0)); middleLayerContentContainer.Add(topLayerContent = new ContractedPanelMiddleContent(Score).With(d => d.Alpha = 0)); break; } diff --git a/osu.Game/Tests/TestScoreInfo.cs b/osu.Game/Tests/TestScoreInfo.cs index 1193a29d70..31cced6ce4 100644 --- a/osu.Game/Tests/TestScoreInfo.cs +++ b/osu.Game/Tests/TestScoreInfo.cs @@ -37,6 +37,8 @@ namespace osu.Game.Tests Statistics[HitResult.Meh] = 50; Statistics[HitResult.Good] = 100; Statistics[HitResult.Great] = 300; + + Position = 1; } private class TestModHardRock : ModHardRock From 4f3795486d34f0db6e97a04b53ba23d5e1be8048 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 22:36:37 +0900 Subject: [PATCH 120/185] Post-process responses to populate positions --- .../TestSceneTimeshiftResultsScreen.cs | 1 + .../Multi/Ranking/TimeshiftResultsScreen.cs | 36 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs index 628d08a314..03fd2b968c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneTimeshiftResultsScreen.cs @@ -244,6 +244,7 @@ namespace osu.Game.Tests.Visual.Multiplayer EndedAt = userScore.Date, Passed = userScore.Passed, Rank = userScore.Rank, + Position = 200, MaxCombo = userScore.MaxCombo, TotalScore = userScore.TotalScore, User = userScore.User, diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index e212bd4a82..232f368fb3 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -84,12 +84,18 @@ namespace osu.Game.Screens.Multi.Ranking { allScores.AddRange(userScore.ScoresAround.Higher.Scores); higherScores = userScore.ScoresAround.Higher; + + Debug.Assert(userScore.Position != null); + setPositions(higherScores, userScore.Position.Value, -1); } if (userScore.ScoresAround?.Lower != null) { allScores.AddRange(userScore.ScoresAround.Lower.Scores); lowerScores = userScore.ScoresAround.Lower; + + Debug.Assert(userScore.Position != null); + setPositions(lowerScores, userScore.Position.Value, 1); } performSuccessCallback(scoresCallback, allScores); @@ -134,9 +140,15 @@ namespace osu.Game.Screens.Multi.Ranking indexReq.Success += r => { if (pivot == lowerScores) + { lowerScores = r; + setPositions(r, pivot, 1); + } else + { higherScores = r; + setPositions(r, pivot, -1); + } performSuccessCallback(scoresCallback, r.Scores, r); }; @@ -183,6 +195,30 @@ namespace osu.Game.Screens.Multi.Ranking LeftSpinner.Hide(); } + /// + /// Applies positions to all s from a given pivot. + /// + /// The to set positions on. + /// The pivot. + /// The amount to increment the pivot position by for each in . + private void setPositions([NotNull] MultiplayerScores scores, [CanBeNull] MultiplayerScores pivot, int increment) + => setPositions(scores, pivot?.Scores[^1].Position ?? 0, increment); + + /// + /// Applies positions to all s from a given pivot. + /// + /// The to set positions on. + /// The pivot position. + /// The amount to increment the pivot position by for each in . + private void setPositions([NotNull] MultiplayerScores scores, int pivotPosition, int increment) + { + foreach (var s in scores.Scores) + { + pivotPosition += increment; + s.Position = pivotPosition; + } + } + private class PanelListLoadingSpinner : LoadingSpinner { private readonly ScorePanelList list; From 308f8bf9bf1b5eb7a929d8f1a19c0746d908bbed Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 23:11:42 +0900 Subject: [PATCH 121/185] Fix inverted naming --- osu.Game/Screens/Ranking/ScorePanel.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index ae55f6e0ae..1904da7094 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -203,8 +203,8 @@ namespace osu.Game.Screens.Ranking topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint); - topLayerContentContainer.Add(middleLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0)); - middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(Score).With(d => d.Alpha = 0)); + topLayerContentContainer.Add(topLayerContent = new ExpandedPanelTopContent(Score.User).With(d => d.Alpha = 0)); + middleLayerContentContainer.Add(middleLayerContent = new ExpandedPanelMiddleContent(Score).With(d => d.Alpha = 0)); break; case PanelState.Contracted: @@ -213,8 +213,8 @@ namespace osu.Game.Screens.Ranking topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint); middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint); - topLayerContentContainer.Add(middleLayerContent = new ContractedPanelTopContent(Score).With(d => d.Alpha = 0)); - middleLayerContentContainer.Add(topLayerContent = new ContractedPanelMiddleContent(Score).With(d => d.Alpha = 0)); + topLayerContentContainer.Add(topLayerContent = new ContractedPanelTopContent(Score).With(d => d.Alpha = 0)); + middleLayerContentContainer.Add(middleLayerContent = new ContractedPanelMiddleContent(Score).With(d => d.Alpha = 0)); break; } From 04b71a0c7c3fb0420dccfb645944d4687dd4588d Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 31 Jul 2020 23:16:55 +0900 Subject: [PATCH 122/185] Adjust xmldoc --- osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs index 232f368fb3..8da6a530a8 100644 --- a/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs +++ b/osu.Game/Screens/Multi/Ranking/TimeshiftResultsScreen.cs @@ -196,7 +196,7 @@ namespace osu.Game.Screens.Multi.Ranking } /// - /// Applies positions to all s from a given pivot. + /// Applies positions to all s referenced to a given pivot. /// /// The to set positions on. /// The pivot. @@ -205,7 +205,7 @@ namespace osu.Game.Screens.Multi.Ranking => setPositions(scores, pivot?.Scores[^1].Position ?? 0, increment); /// - /// Applies positions to all s from a given pivot. + /// Applies positions to all s referenced to a given pivot. /// /// The to set positions on. /// The pivot position. From 9e244be4890aacc1ff0c1534fcd860817e507eae Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 1 Aug 2020 00:05:04 +0900 Subject: [PATCH 123/185] Use better conditional for choosing which spinner type to use Co-authored-by: Dan Balasescu --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 44f431d6f7..81d1d05b66 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -104,9 +104,11 @@ namespace osu.Game.Rulesets.Osu.Skinning }; case OsuSkinComponents.SpinnerBody: - if (Source.GetTexture("spinner-top") != null) + bool hasBackground = Source.GetTexture("spinner-background") != null; + + if (Source.GetTexture("spinner-top") != null && !hasBackground) return new LegacyNewStyleSpinner(); - else if (Source.GetTexture("spinner-background") != null) + else if (hasBackground) return new LegacyOldStyleSpinner(); return null; From bb01ee5be953d6a241830f8a08e3a029298f9e25 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 1 Aug 2020 00:27:00 +0900 Subject: [PATCH 124/185] Fix trackign alpha not being applied --- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index 79bfeedd07..eba5d869c0 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -100,13 +100,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { base.Update(); - if (drawableSpinner.RotationTracker.Complete.Value && checkNewRotationCount) + if (drawableSpinner.RotationTracker.Complete.Value) { - fill.FinishTransforms(false, nameof(Alpha)); - fill - .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) - .Then() - .FadeTo(tracking_alpha, 250, Easing.OutQuint); + if (checkNewRotationCount) + { + fill.FinishTransforms(false, nameof(Alpha)); + fill + .FadeTo(tracking_alpha + 0.2f, 60, Easing.OutExpo) + .Then() + .FadeTo(tracking_alpha, 250, Easing.OutQuint); + } + } + else + { + fill.Alpha = (float)Interpolation.Damp(fill.Alpha, drawableSpinner.RotationTracker.Tracking ? tracking_alpha : idle_alpha, 0.98f, (float)Clock.ElapsedFrameTime); } const float initial_scale = 0.2f; From 180afff80515bc09a0833d1acb45a3bac243a958 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 1 Aug 2020 00:39:04 +0900 Subject: [PATCH 125/185] Ensure damp is always positive exponent --- .../Objects/Drawables/Pieces/DefaultSpinnerDisc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index eba5d869c0..dfb692eba9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -113,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } else { - fill.Alpha = (float)Interpolation.Damp(fill.Alpha, drawableSpinner.RotationTracker.Tracking ? tracking_alpha : idle_alpha, 0.98f, (float)Clock.ElapsedFrameTime); + fill.Alpha = (float)Interpolation.Damp(fill.Alpha, drawableSpinner.RotationTracker.Tracking ? tracking_alpha : idle_alpha, 0.98f, (float)Math.Abs(Clock.ElapsedFrameTime)); } const float initial_scale = 0.2f; From 74f70136fdf80faa47d53772c7c4f386d5e6c3e1 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 1 Aug 2020 06:00:24 +0300 Subject: [PATCH 126/185] Implement DashboardBeatmapPanel component --- .../TestSceneDashboardBeatmapPanel.cs | 52 ++++++ .../Dashboard/DashboardBeatmapPanel.cs | 159 ++++++++++++++++++ 2 files changed, 211 insertions(+) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs create mode 100644 osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs new file mode 100644 index 0000000000..a61cd37513 --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.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 osu.Framework.Graphics.Containers; +using osu.Framework.Graphics; +using osu.Game.Overlays.Dashboard.Dashboard; +using osu.Game.Beatmaps; +using osu.Game.Overlays; +using osu.Framework.Allocation; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneDashboardBeatmapPanel : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + public TestSceneDashboardBeatmapPanel() + { + Add(new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + Width = 300, + Child = new DashboardBeatmapPanel(beatmap_set) + }); + } + + private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is it possible", + Author = new User + { + Username = "author", + Id = 100 + } + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + } + } + }; + } +} diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs new file mode 100644 index 0000000000..84cb5ae46a --- /dev/null +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs @@ -0,0 +1,159 @@ +// 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.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.Dashboard.Dashboard +{ + public class DashboardBeatmapPanel : OsuClickableContainer + { + [Resolved] + private OverlayColourProvider colourProvider { get; set; } + + [Resolved(canBeNull: true)] + private BeatmapSetOverlay beatmapOverlay { get; set; } + + private readonly BeatmapSetInfo setInfo; + + private Box background; + private SpriteIcon chevron; + + public DashboardBeatmapPanel(BeatmapSetInfo setInfo) + { + this.setInfo = setInfo; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.X; + Height = 60; + Children = new Drawable[] + { + background = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background3, + Alpha = 0 + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 10 }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, 70), + new Dimension(), + new Dimension(GridSizeMode.AutoSize) + }, + RowDimensions = new[] + { + new Dimension() + }, + Content = new[] + { + new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 6, + Child = new UpdateableBeatmapSetCover + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + BeatmapSet = setInfo + } + }, + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Horizontal = 10 }, + Child = new FillFlowContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + RelativeSizeAxes = Axes.X, + Truncate = true, + Font = OsuFont.GetFont(weight: FontWeight.SemiBold), + Text = setInfo.Metadata.Title + }, + new OsuSpriteText + { + RelativeSizeAxes = Axes.X, + Truncate = true, + Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), + Text = setInfo.Metadata.Artist + }, + new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold)) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y + }.With(c => + { + c.AddText("by "); + c.AddUserLink(setInfo.Metadata.Author); + }) + } + } + }, + chevron = new SpriteIcon + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + Size = new Vector2(16), + Icon = FontAwesome.Solid.ChevronRight, + Colour = colourProvider.Foreground1 + } + } + } + } + } + }; + + Action = () => + { + if (setInfo.OnlineBeatmapSetID.HasValue) + beatmapOverlay?.FetchAndShowBeatmapSet(setInfo.OnlineBeatmapSetID.Value); + }; + } + + protected override bool OnHover(HoverEvent e) + { + base.OnHover(e); + background.FadeIn(200, Easing.OutQuint); + chevron.FadeColour(colourProvider.Light1, 200, Easing.OutQuint); + return true; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + base.OnHoverLost(e); + background.FadeOut(200, Easing.OutQuint); + chevron.FadeColour(colourProvider.Foreground1, 200, Easing.OutQuint); + } + } +} From ce47a34991ce574265aeaf37134c3d9f6de02cee Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 1 Aug 2020 06:14:24 +0300 Subject: [PATCH 127/185] Implement DashboardNewBeatmapPanel component --- .../TestSceneDashboardBeatmapPanel.cs | 8 ++- .../Dashboard/DashboardBeatmapPanel.cs | 56 +++++++++++-------- .../Dashboard/DashboardNewBeatmapPanel.cs | 22 ++++++++ 3 files changed, 61 insertions(+), 25 deletions(-) create mode 100644 osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs index a61cd37513..5f1af012db 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs @@ -8,6 +8,7 @@ using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Framework.Allocation; using osu.Game.Users; +using System; namespace osu.Game.Tests.Visual.UserInterface { @@ -24,7 +25,7 @@ namespace osu.Game.Tests.Visual.UserInterface Origin = Anchor.Centre, AutoSizeAxes = Axes.Y, Width = 300, - Child = new DashboardBeatmapPanel(beatmap_set) + Child = new DashboardNewBeatmapPanel(beatmap_set) }); } @@ -33,7 +34,7 @@ namespace osu.Game.Tests.Visual.UserInterface Metadata = new BeatmapMetadata { Title = "Very Long Title (TV size) [TATOE]", - Artist = "This artist has a really long name how is it possible", + Artist = "This artist has a really long name how is this possible", Author = new User { Username = "author", @@ -45,7 +46,8 @@ namespace osu.Game.Tests.Visual.UserInterface Covers = new BeatmapSetOnlineCovers { Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", - } + }, + Ranked = DateTimeOffset.Now } }; } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs index 84cb5ae46a..30b0086b8a 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs @@ -16,22 +16,22 @@ using osuTK; namespace osu.Game.Overlays.Dashboard.Dashboard { - public class DashboardBeatmapPanel : OsuClickableContainer + public abstract class DashboardBeatmapPanel : OsuClickableContainer { [Resolved] - private OverlayColourProvider colourProvider { get; set; } + protected OverlayColourProvider ColourProvider { get; private set; } [Resolved(canBeNull: true)] private BeatmapSetOverlay beatmapOverlay { get; set; } - private readonly BeatmapSetInfo setInfo; + protected readonly BeatmapSetInfo SetInfo; private Box background; private SpriteIcon chevron; - public DashboardBeatmapPanel(BeatmapSetInfo setInfo) + protected DashboardBeatmapPanel(BeatmapSetInfo setInfo) { - this.setInfo = setInfo; + SetInfo = setInfo; } [BackgroundDependencyLoader] @@ -44,7 +44,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard background = new Box { RelativeSizeAxes = Axes.Both, - Colour = colourProvider.Background3, + Colour = ColourProvider.Background3, Alpha = 0 }, new Container @@ -78,7 +78,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard RelativeSizeAxes = Axes.Both, Anchor = Anchor.Centre, Origin = Anchor.Centre, - BeatmapSet = setInfo + BeatmapSet = SetInfo } }, new Container @@ -99,24 +99,34 @@ namespace osu.Game.Overlays.Dashboard.Dashboard RelativeSizeAxes = Axes.X, Truncate = true, Font = OsuFont.GetFont(weight: FontWeight.SemiBold), - Text = setInfo.Metadata.Title + Text = SetInfo.Metadata.Title }, new OsuSpriteText { RelativeSizeAxes = Axes.X, Truncate = true, Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), - Text = setInfo.Metadata.Artist + Text = SetInfo.Metadata.Artist }, - new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold)) + new FillFlowContainer { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y - }.With(c => - { - c.AddText("by "); - c.AddUserLink(setInfo.Metadata.Author); - }) + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5, 0), + Margin = new MarginPadding { Top = 2 }, + Children = new Drawable[] + { + new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold)) + { + AutoSizeAxes = Axes.Both + }.With(c => + { + c.AddText("by "); + c.AddUserLink(SetInfo.Metadata.Author); + }), + CreateInfo() + } + } } } }, @@ -126,7 +136,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard Origin = Anchor.CentreRight, Size = new Vector2(16), Icon = FontAwesome.Solid.ChevronRight, - Colour = colourProvider.Foreground1 + Colour = ColourProvider.Foreground1 } } } @@ -136,16 +146,18 @@ namespace osu.Game.Overlays.Dashboard.Dashboard Action = () => { - if (setInfo.OnlineBeatmapSetID.HasValue) - beatmapOverlay?.FetchAndShowBeatmapSet(setInfo.OnlineBeatmapSetID.Value); + if (SetInfo.OnlineBeatmapSetID.HasValue) + beatmapOverlay?.FetchAndShowBeatmapSet(SetInfo.OnlineBeatmapSetID.Value); }; } + protected abstract Drawable CreateInfo(); + protected override bool OnHover(HoverEvent e) { base.OnHover(e); background.FadeIn(200, Easing.OutQuint); - chevron.FadeColour(colourProvider.Light1, 200, Easing.OutQuint); + chevron.FadeColour(ColourProvider.Light1, 200, Easing.OutQuint); return true; } @@ -153,7 +165,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard { base.OnHoverLost(e); background.FadeOut(200, Easing.OutQuint); - chevron.FadeColour(colourProvider.Foreground1, 200, Easing.OutQuint); + chevron.FadeColour(ColourProvider.Foreground1, 200, Easing.OutQuint); } } } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs new file mode 100644 index 0000000000..6d2ec7ae37 --- /dev/null +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs @@ -0,0 +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.Game.Beatmaps; +using osu.Game.Graphics; + +namespace osu.Game.Overlays.Dashboard.Dashboard +{ + public class DashboardNewBeatmapPanel : DashboardBeatmapPanel + { + public DashboardNewBeatmapPanel(BeatmapSetInfo setInfo) + : base(setInfo) + { + } + + protected override Drawable CreateInfo() => new DrawableDate(SetInfo.OnlineInfo.Ranked.Value, 10, false) + { + Colour = ColourProvider.Foreground1 + }; + } +} From 7624804edfacb185dc456c18eb32648287613665 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 1 Aug 2020 06:23:06 +0300 Subject: [PATCH 128/185] Implement DashboardPopularBeatmapPanel component --- .../TestSceneDashboardBeatmapPanel.cs | 35 ++++++++++++++-- .../Dashboard/DashboardBeatmapPanel.cs | 2 +- .../Dashboard/DashboardPopularBeatmapPanel.cs | 42 +++++++++++++++++++ 3 files changed, 75 insertions(+), 4 deletions(-) create mode 100644 osu.Game/Overlays/Dashboard/Dashboard/DashboardPopularBeatmapPanel.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs index 5f1af012db..10682490ae 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs @@ -9,6 +9,7 @@ using osu.Game.Overlays; using osu.Framework.Allocation; using osu.Game.Users; using System; +using osuTK; namespace osu.Game.Tests.Visual.UserInterface { @@ -19,17 +20,23 @@ namespace osu.Game.Tests.Visual.UserInterface public TestSceneDashboardBeatmapPanel() { - Add(new Container + Add(new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, AutoSizeAxes = Axes.Y, Width = 300, - Child = new DashboardNewBeatmapPanel(beatmap_set) + Spacing = new Vector2(0, 10), + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new DashboardNewBeatmapPanel(new_beatmap_set), + new DashboardPopularBeatmapPanel(popular_beatmap_set) + } }); } - private static readonly BeatmapSetInfo beatmap_set = new BeatmapSetInfo + private static readonly BeatmapSetInfo new_beatmap_set = new BeatmapSetInfo { Metadata = new BeatmapMetadata { @@ -50,5 +57,27 @@ namespace osu.Game.Tests.Visual.UserInterface Ranked = DateTimeOffset.Now } }; + + private static readonly BeatmapSetInfo popular_beatmap_set = new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = "Title", + Artist = "Artist", + Author = new User + { + Username = "author", + Id = 100 + } + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + FavouriteCount = 100 + } + }; } } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs index 30b0086b8a..fc70d9d40a 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard { AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, - Spacing = new Vector2(5, 0), + Spacing = new Vector2(3, 0), Margin = new MarginPadding { Top = 2 }, Children = new Drawable[] { diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardPopularBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardPopularBeatmapPanel.cs new file mode 100644 index 0000000000..04bb261dce --- /dev/null +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardPopularBeatmapPanel.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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Overlays.Dashboard.Dashboard +{ + public class DashboardPopularBeatmapPanel : DashboardBeatmapPanel + { + public DashboardPopularBeatmapPanel(BeatmapSetInfo setInfo) + : base(setInfo) + { + } + + protected override Drawable CreateInfo() => new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(3, 0), + Colour = ColourProvider.Foreground1, + Children = new Drawable[] + { + new SpriteIcon + { + Size = new Vector2(10), + Icon = FontAwesome.Solid.Heart + }, + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 10), + Text = SetInfo.OnlineInfo.FavouriteCount.ToString() + } + } + }; + } +} From b5f688e63aeb9168cb4d73c651889dab270871a3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 1 Aug 2020 07:04:39 +0300 Subject: [PATCH 129/185] Implement DashboardBeatmapListing component --- .../TestSceneDashboardBeatmapListing.cs | 144 ++++++++++++++++++ .../TestSceneDashboardBeatmapPanel.cs | 83 ---------- .../Dashboard/DashboardBeatmapListing.cs | 43 ++++++ .../Dashboard/DashboardBeatmapPanel.cs | 2 +- .../Dashboard/DashboardNewBeatmapPanel.cs | 3 +- .../Dashboard/DrawableBeatmapsList.cs | 57 +++++++ .../Dashboard/DrawableNewBeatmapsList.cs | 20 +++ .../Dashboard/DrawablePopularBeatmapsList.cs | 20 +++ 8 files changed, 287 insertions(+), 85 deletions(-) create mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs delete mode 100644 osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs create mode 100644 osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs create mode 100644 osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapsList.cs create mode 100644 osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapsList.cs create mode 100644 osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapsList.cs diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs new file mode 100644 index 0000000000..be0cc5187e --- /dev/null +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs @@ -0,0 +1,144 @@ +// 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.Overlays.Dashboard.Dashboard; +using osu.Game.Beatmaps; +using osu.Game.Overlays; +using osu.Framework.Allocation; +using osu.Game.Users; +using System; +using osu.Framework.Graphics.Shapes; +using System.Collections.Generic; + +namespace osu.Game.Tests.Visual.UserInterface +{ + public class TestSceneDashboardBeatmapListing : OsuTestScene + { + [Cached] + private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + + private readonly Container content; + + public TestSceneDashboardBeatmapListing() + { + Add(content = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + AutoSizeAxes = Axes.Y, + Width = 300, + Children = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colourProvider.Background4 + }, + new DashboardBeatmapListing(new_beatmaps, popular_beatmaps) + } + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + AddStep("Set width to 500", () => content.ResizeWidthTo(500, 500)); + AddStep("Set width to 300", () => content.ResizeWidthTo(300, 500)); + } + + private static readonly List new_beatmaps = new List + { + new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User + { + Username = "author", + Id = 100 + } + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.Now + } + }, + new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = "Very Long Title (TV size) [TATOE]", + Artist = "This artist has a really long name how is this possible", + Author = new User + { + Username = "author", + Id = 100 + } + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", + }, + Ranked = DateTimeOffset.MinValue + } + } + }; + + private static readonly List popular_beatmaps = new List + { + new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = "Title", + Artist = "Artist", + Author = new User + { + Username = "author", + Id = 100 + } + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = "https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg?1595295586", + }, + FavouriteCount = 100 + } + }, + new BeatmapSetInfo + { + Metadata = new BeatmapMetadata + { + Title = "Title 2", + Artist = "Artist 2", + Author = new User + { + Username = "someone", + Id = 100 + } + }, + OnlineInfo = new BeatmapSetOnlineInfo + { + Covers = new BeatmapSetOnlineCovers + { + Cover = "https://assets.ppy.sh/beatmaps/1079428/covers/cover.jpg?1595295586", + }, + FavouriteCount = 10 + } + } + }; + } +} diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.cs deleted file mode 100644 index 10682490ae..0000000000 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapPanel.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.Containers; -using osu.Framework.Graphics; -using osu.Game.Overlays.Dashboard.Dashboard; -using osu.Game.Beatmaps; -using osu.Game.Overlays; -using osu.Framework.Allocation; -using osu.Game.Users; -using System; -using osuTK; - -namespace osu.Game.Tests.Visual.UserInterface -{ - public class TestSceneDashboardBeatmapPanel : OsuTestScene - { - [Cached] - private readonly OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - - public TestSceneDashboardBeatmapPanel() - { - Add(new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - AutoSizeAxes = Axes.Y, - Width = 300, - Spacing = new Vector2(0, 10), - Direction = FillDirection.Vertical, - Children = new Drawable[] - { - new DashboardNewBeatmapPanel(new_beatmap_set), - new DashboardPopularBeatmapPanel(popular_beatmap_set) - } - }); - } - - private static readonly BeatmapSetInfo new_beatmap_set = new BeatmapSetInfo - { - Metadata = new BeatmapMetadata - { - Title = "Very Long Title (TV size) [TATOE]", - Artist = "This artist has a really long name how is this possible", - Author = new User - { - Username = "author", - Id = 100 - } - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", - }, - Ranked = DateTimeOffset.Now - } - }; - - private static readonly BeatmapSetInfo popular_beatmap_set = new BeatmapSetInfo - { - Metadata = new BeatmapMetadata - { - Title = "Title", - Artist = "Artist", - Author = new User - { - Username = "author", - Id = 100 - } - }, - OnlineInfo = new BeatmapSetOnlineInfo - { - Covers = new BeatmapSetOnlineCovers - { - Cover = "https://assets.ppy.sh/beatmaps/1189904/covers/cover.jpg?1595456608", - }, - FavouriteCount = 100 - } - }; - } -} diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs new file mode 100644 index 0000000000..26c892174d --- /dev/null +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs @@ -0,0 +1,43 @@ +// 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.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osuTK; + +namespace osu.Game.Overlays.Dashboard.Dashboard +{ + public class DashboardBeatmapListing : CompositeDrawable + { + private readonly List newBeatmaps; + private readonly List popularBeatmaps; + + public DashboardBeatmapListing(List newBeatmaps, List popularBeatmaps) + { + this.newBeatmaps = newBeatmaps; + this.popularBeatmaps = popularBeatmaps; + } + + [BackgroundDependencyLoader] + private void load() + { + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new DrawableBeatmapsList[] + { + new DrawableNewBeatmapsList(newBeatmaps), + new DrawablePopularBeatmapsList(popularBeatmaps) + } + }; + } + } +} diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs index fc70d9d40a..bb35ddd07c 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs @@ -114,7 +114,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard Direction = FillDirection.Horizontal, Spacing = new Vector2(3, 0), Margin = new MarginPadding { Top = 2 }, - Children = new Drawable[] + Children = new[] { new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold)) { diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs index 6d2ec7ae37..c4782b733d 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.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.Game.Beatmaps; using osu.Game.Graphics; @@ -14,7 +15,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard { } - protected override Drawable CreateInfo() => new DrawableDate(SetInfo.OnlineInfo.Ranked.Value, 10, false) + protected override Drawable CreateInfo() => new DrawableDate(SetInfo.OnlineInfo.Ranked ?? DateTimeOffset.Now, 10, false) { Colour = ColourProvider.Foreground1 }; diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapsList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapsList.cs new file mode 100644 index 0000000000..dfe483a962 --- /dev/null +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapsList.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 System.Collections.Generic; +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Overlays.Dashboard.Dashboard +{ + public abstract class DrawableBeatmapsList : CompositeDrawable + { + private readonly List beatmaps; + + protected DrawableBeatmapsList(List beatmaps) + { + this.beatmaps = beatmaps; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider colourProvider) + { + FillFlowContainer flow; + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = flow = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new OsuSpriteText + { + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), + Colour = colourProvider.Light1, + Text = CreateTitle(), + Padding = new MarginPadding { Left = 10 } + } + } + }; + + flow.AddRange(beatmaps.Select(CreateBeatmapPanel)); + } + + protected abstract string CreateTitle(); + + protected abstract DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo); + } +} diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapsList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapsList.cs new file mode 100644 index 0000000000..e89495dc99 --- /dev/null +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapsList.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.Collections.Generic; +using osu.Game.Beatmaps; + +namespace osu.Game.Overlays.Dashboard.Dashboard +{ + public class DrawableNewBeatmapsList : DrawableBeatmapsList + { + public DrawableNewBeatmapsList(List beatmaps) + : base(beatmaps) + { + } + + protected override DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo) => new DashboardNewBeatmapPanel(setInfo); + + protected override string CreateTitle() => "New Ranked Beatmaps"; + } +} diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapsList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapsList.cs new file mode 100644 index 0000000000..8076e86ed0 --- /dev/null +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapsList.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.Collections.Generic; +using osu.Game.Beatmaps; + +namespace osu.Game.Overlays.Dashboard.Dashboard +{ + public class DrawablePopularBeatmapsList : DrawableBeatmapsList + { + public DrawablePopularBeatmapsList(List beatmaps) + : base(beatmaps) + { + } + + protected override DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo) => new DashboardPopularBeatmapPanel(setInfo); + + protected override string CreateTitle() => "Popular Beatmaps"; + } +} From 5b1e3e86220e7ba3b8024fa9ff6cfc035e34a82c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 1 Aug 2020 09:11:53 +0300 Subject: [PATCH 130/185] Remove redundant FillFlowContainer from DashboardBeatmapPanel --- .../Dashboard/DashboardBeatmapPanel.cs | 29 +++++++------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs index bb35ddd07c..61324fdc7f 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs @@ -108,25 +108,18 @@ namespace osu.Game.Overlays.Dashboard.Dashboard Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), Text = SetInfo.Metadata.Artist }, - new FillFlowContainer + new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold)) { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(3, 0), - Margin = new MarginPadding { Top = 2 }, - Children = new[] - { - new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold)) - { - AutoSizeAxes = Axes.Both - }.With(c => - { - c.AddText("by "); - c.AddUserLink(SetInfo.Metadata.Author); - }), - CreateInfo() - } - } + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + Spacing = new Vector2(3), + Margin = new MarginPadding { Top = 2 } + }.With(c => + { + c.AddText("by"); + c.AddUserLink(SetInfo.Metadata.Author); + c.AddArbitraryDrawable(CreateInfo()); + }) } } }, From 2190e6443a0265ba4d4db20a44b207b3a63a4760 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 1 Aug 2020 10:02:46 +0300 Subject: [PATCH 131/185] Apply height constraints to all settings dropdown --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 -- osu.Game/Overlays/Settings/SettingsDropdown.cs | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 04390a1193..596d3a9801 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -116,8 +116,6 @@ 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); } } diff --git a/osu.Game/Overlays/Settings/SettingsDropdown.cs b/osu.Game/Overlays/Settings/SettingsDropdown.cs index 167061f485..1175ddaab8 100644 --- a/osu.Game/Overlays/Settings/SettingsDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsDropdown.cs @@ -38,6 +38,8 @@ namespace osu.Game.Overlays.Settings Margin = new MarginPadding { Top = 5 }; RelativeSizeAxes = Axes.X; } + + protected override DropdownMenu CreateMenu() => base.CreateMenu().With(m => m.MaxHeight = 200); } } } From 5f52701273a8b41205bcd7a1c24fd02bb658560c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 1 Aug 2020 10:11:34 +0300 Subject: [PATCH 132/185] Remove no longer necessary custom dropdown --- .../Ladder/Components/LadderEditorSettings.cs | 2 +- .../Components/LadderSettingsDropdown.cs | 26 ------------------- .../Ladder/Components/SettingsTeamDropdown.cs | 3 ++- 3 files changed, 3 insertions(+), 28 deletions(-) delete mode 100644 osu.Game.Tournament/Screens/Ladder/Components/LadderSettingsDropdown.cs diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs index 4aea7ff4c0..fa530ea2c4 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/LadderEditorSettings.cs @@ -81,7 +81,7 @@ namespace osu.Game.Tournament.Screens.Ladder.Components { } - private class SettingsRoundDropdown : LadderSettingsDropdown + private class SettingsRoundDropdown : SettingsDropdown { public SettingsRoundDropdown(BindableList rounds) { diff --git a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettingsDropdown.cs b/osu.Game.Tournament/Screens/Ladder/Components/LadderSettingsDropdown.cs deleted file mode 100644 index 347e4d91e0..0000000000 --- a/osu.Game.Tournament/Screens/Ladder/Components/LadderSettingsDropdown.cs +++ /dev/null @@ -1,26 +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.Graphics.UserInterface; -using osu.Game.Overlays.Settings; - -namespace osu.Game.Tournament.Screens.Ladder.Components -{ - public class LadderSettingsDropdown : SettingsDropdown - { - protected override OsuDropdown CreateDropdown() => new DropdownControl(); - - private new class DropdownControl : SettingsDropdown.DropdownControl - { - protected override DropdownMenu CreateMenu() => new Menu(); - - private new class Menu : OsuDropdownMenu - { - public Menu() - { - MaxHeight = 200; - } - } - } - } -} diff --git a/osu.Game.Tournament/Screens/Ladder/Components/SettingsTeamDropdown.cs b/osu.Game.Tournament/Screens/Ladder/Components/SettingsTeamDropdown.cs index a630e51e44..6604e3a313 100644 --- a/osu.Game.Tournament/Screens/Ladder/Components/SettingsTeamDropdown.cs +++ b/osu.Game.Tournament/Screens/Ladder/Components/SettingsTeamDropdown.cs @@ -6,11 +6,12 @@ using System.Collections.Specialized; using System.Linq; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Overlays.Settings; using osu.Game.Tournament.Models; namespace osu.Game.Tournament.Screens.Ladder.Components { - public class SettingsTeamDropdown : LadderSettingsDropdown + public class SettingsTeamDropdown : SettingsDropdown { public SettingsTeamDropdown(BindableList teams) { From f4128083312843d9594c1b96d212914a43fee60b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 2 Aug 2020 12:57:15 +0200 Subject: [PATCH 133/185] Check rotation with bigger tolerance to account for damp --- .../TestSceneSpinnerRotation.cs | 44 ++++++++++++++----- 1 file changed, 33 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index 319d326a01..e4d89fb402 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.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; @@ -60,39 +61,60 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestSpinnerRewindingRotation() { + double trackerRotationTolerance = 0; + addSeekStep(5000); + AddStep("calculate rotation tolerance", () => + { + trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f); + }); AddAssert("is disc rotation not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, 0, 100)); AddAssert("is disc rotation absolute not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, 0, 100)); addSeekStep(0); - AddAssert("is disc rotation almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, 0, 100)); + AddAssert("is disc rotation almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, 0, trackerRotationTolerance)); AddAssert("is disc rotation absolute almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, 0, 100)); } [Test] public void TestSpinnerMiddleRewindingRotation() { - double finalAbsoluteDiscRotation = 0, finalRelativeDiscRotation = 0, finalSpinnerSymbolRotation = 0; + double finalCumulativeTrackerRotation = 0; + double finalTrackerRotation = 0, trackerRotationTolerance = 0; + double finalSpinnerSymbolRotation = 0, spinnerSymbolRotationTolerance = 0; addSeekStep(5000); - AddStep("retrieve disc relative rotation", () => finalRelativeDiscRotation = drawableSpinner.RotationTracker.Rotation); - AddStep("retrieve disc absolute rotation", () => finalAbsoluteDiscRotation = drawableSpinner.RotationTracker.CumulativeRotation); - AddStep("retrieve spinner symbol rotation", () => finalSpinnerSymbolRotation = spinnerSymbol.Rotation); + AddStep("retrieve disc rotation", () => + { + finalTrackerRotation = drawableSpinner.RotationTracker.Rotation; + trackerRotationTolerance = Math.Abs(finalTrackerRotation * 0.05f); + }); + AddStep("retrieve spinner symbol rotation", () => + { + finalSpinnerSymbolRotation = spinnerSymbol.Rotation; + spinnerSymbolRotationTolerance = Math.Abs(finalSpinnerSymbolRotation * 0.05f); + }); + AddStep("retrieve cumulative disc rotation", () => finalCumulativeTrackerRotation = drawableSpinner.RotationTracker.CumulativeRotation); addSeekStep(2500); AddUntilStep("disc rotation rewound", // we want to make sure that the rotation at time 2500 is in the same direction as at time 5000, but about half-way in. - () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, finalRelativeDiscRotation / 2, 100)); + // due to the exponential damping applied we're allowing a larger margin of error of about 10% + // (5% relative to the final rotation value, but we're half-way through the spin). + () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, finalTrackerRotation / 2, trackerRotationTolerance)); AddUntilStep("symbol rotation rewound", - () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation / 2, 100)); + () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation / 2, spinnerSymbolRotationTolerance)); + AddAssert("is cumulative rotation rewound", + // cumulative rotation is not damped, so we're treating it as the "ground truth" and allowing a comparatively smaller margin of error. + () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, finalCumulativeTrackerRotation / 2, 100)); addSeekStep(5000); AddAssert("is disc rotation almost same", - () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, finalRelativeDiscRotation, 100)); + () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, finalTrackerRotation, trackerRotationTolerance)); AddAssert("is symbol rotation almost same", - () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation, 100)); - AddAssert("is disc rotation absolute almost same", - () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, finalAbsoluteDiscRotation, 100)); + () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation, spinnerSymbolRotationTolerance)); + AddAssert("is cumulative rotation almost same", + () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, finalCumulativeTrackerRotation, 100)); } [Test] From efb08aeed32b1787e79b7ebd0e7501a874cf559c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 2 Aug 2020 14:54:41 +0200 Subject: [PATCH 134/185] Switch unnecessary wait steps to asserts --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index e4d89fb402..b46964e8b7 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -97,12 +97,12 @@ namespace osu.Game.Rulesets.Osu.Tests AddStep("retrieve cumulative disc rotation", () => finalCumulativeTrackerRotation = drawableSpinner.RotationTracker.CumulativeRotation); addSeekStep(2500); - AddUntilStep("disc rotation rewound", + AddAssert("disc rotation rewound", // we want to make sure that the rotation at time 2500 is in the same direction as at time 5000, but about half-way in. // due to the exponential damping applied we're allowing a larger margin of error of about 10% // (5% relative to the final rotation value, but we're half-way through the spin). () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, finalTrackerRotation / 2, trackerRotationTolerance)); - AddUntilStep("symbol rotation rewound", + AddAssert("symbol rotation rewound", () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation / 2, spinnerSymbolRotationTolerance)); AddAssert("is cumulative rotation rewound", // cumulative rotation is not damped, so we're treating it as the "ground truth" and allowing a comparatively smaller margin of error. From 3e5c3e256d3f0747afd8f89f05e622eba1d035ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 2 Aug 2020 19:46:29 +0200 Subject: [PATCH 135/185] Extract method for performing generic config lookup --- osu.Game/Skinning/LegacySkin.cs | 46 ++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 21 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d98f8aba83..d1acc51bed 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -143,27 +143,7 @@ namespace osu.Game.Skinning goto default; default: - // handles lookups like some in LegacySkinConfiguration.LegacySetting - - try - { - 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(); - if (val != null) - bindable.Parse(val); - return bindable; - } - } - catch - { - } - - break; + return genericLookup(lookup); } return null; @@ -286,6 +266,30 @@ namespace osu.Game.Skinning private IBindable getManiaImage(LegacyManiaSkinConfiguration source, string lookup) => source.ImageLookups.TryGetValue(lookup, out var image) ? new Bindable(image) : null; + [CanBeNull] + private IBindable genericLookup(TLookup lookup) + { + try + { + 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(); + if (val != null) + bindable.Parse(val); + return bindable; + } + } + catch + { + } + + return null; + } + public override Drawable GetDrawableComponent(ISkinComponent component) { switch (component) From ca7545917c85d5b5cf58ab54ec49aaab4c17d52f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 2 Aug 2020 19:50:17 +0200 Subject: [PATCH 136/185] Extract method for performing legacy lookups --- osu.Game/Skinning/LegacySkin.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index d1acc51bed..4e470e13b5 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -134,13 +134,7 @@ namespace osu.Game.Skinning break; case LegacySkinConfiguration.LegacySetting legacy: - switch (legacy) - { - case LegacySkinConfiguration.LegacySetting.Version: - return SkinUtils.As(new Bindable(Configuration.LegacyVersion ?? LegacySkinConfiguration.LATEST_VERSION)); - } - - goto default; + return legacySettingLookup(legacy); default: return genericLookup(lookup); @@ -266,6 +260,19 @@ namespace osu.Game.Skinning private IBindable getManiaImage(LegacyManiaSkinConfiguration source, string lookup) => source.ImageLookups.TryGetValue(lookup, out var image) ? new Bindable(image) : null; + [CanBeNull] + private IBindable legacySettingLookup(LegacySkinConfiguration.LegacySetting legacySetting) + { + switch (legacySetting) + { + case LegacySkinConfiguration.LegacySetting.Version: + return SkinUtils.As(new Bindable(Configuration.LegacyVersion ?? LegacySkinConfiguration.LATEST_VERSION)); + + default: + return genericLookup(legacySetting); + } + } + [CanBeNull] private IBindable genericLookup(TLookup lookup) { From ca57c70961e51cceed04404706c59a8601fa29b9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 2 Aug 2020 21:33:14 +0300 Subject: [PATCH 137/185] Naming adjustments --- .../Dashboard/Dashboard/DashboardBeatmapListing.cs | 8 ++++---- .../Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs | 8 ++++---- .../{DrawableBeatmapsList.cs => DrawableBeatmapList.cs} | 4 ++-- ...awableNewBeatmapsList.cs => DrawableNewBeatmapList.cs} | 4 ++-- ...pularBeatmapsList.cs => DrawablePopularBeatmapList.cs} | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) rename osu.Game/Overlays/Dashboard/Dashboard/{DrawableBeatmapsList.cs => DrawableBeatmapList.cs} (92%) rename osu.Game/Overlays/Dashboard/Dashboard/{DrawableNewBeatmapsList.cs => DrawableNewBeatmapList.cs} (80%) rename osu.Game/Overlays/Dashboard/Dashboard/{DrawablePopularBeatmapsList.cs => DrawablePopularBeatmapList.cs} (79%) diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs index 26c892174d..a8c6ab7ba2 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs @@ -26,16 +26,16 @@ namespace osu.Game.Overlays.Dashboard.Dashboard { RelativeSizeAxes = Axes.X; AutoSizeAxes = Axes.Y; - InternalChild = new FillFlowContainer + InternalChild = new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Spacing = new Vector2(0, 10), - Children = new DrawableBeatmapsList[] + Children = new DrawableBeatmapList[] { - new DrawableNewBeatmapsList(newBeatmaps), - new DrawablePopularBeatmapsList(popularBeatmaps) + new DrawableNewBeatmapList(newBeatmaps), + new DrawablePopularBeatmapList(popularBeatmaps) } }; } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs index 61324fdc7f..45fa56a177 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs @@ -26,7 +26,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard protected readonly BeatmapSetInfo SetInfo; - private Box background; + private Box hoverBackground; private SpriteIcon chevron; protected DashboardBeatmapPanel(BeatmapSetInfo setInfo) @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard Height = 60; Children = new Drawable[] { - background = new Box + hoverBackground = new Box { RelativeSizeAxes = Axes.Both, Colour = ColourProvider.Background3, @@ -149,7 +149,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard protected override bool OnHover(HoverEvent e) { base.OnHover(e); - background.FadeIn(200, Easing.OutQuint); + hoverBackground.FadeIn(200, Easing.OutQuint); chevron.FadeColour(ColourProvider.Light1, 200, Easing.OutQuint); return true; } @@ -157,7 +157,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard protected override void OnHoverLost(HoverLostEvent e) { base.OnHoverLost(e); - background.FadeOut(200, Easing.OutQuint); + hoverBackground.FadeOut(200, Easing.OutQuint); chevron.FadeColour(ColourProvider.Foreground1, 200, Easing.OutQuint); } } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapsList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs similarity index 92% rename from osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapsList.cs rename to osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs index dfe483a962..7dd969863a 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapsList.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs @@ -13,11 +13,11 @@ using osuTK; namespace osu.Game.Overlays.Dashboard.Dashboard { - public abstract class DrawableBeatmapsList : CompositeDrawable + public abstract class DrawableBeatmapList : CompositeDrawable { private readonly List beatmaps; - protected DrawableBeatmapsList(List beatmaps) + protected DrawableBeatmapList(List beatmaps) { this.beatmaps = beatmaps; } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapsList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs similarity index 80% rename from osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapsList.cs rename to osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs index e89495dc99..3ed42cc4bb 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapsList.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs @@ -6,9 +6,9 @@ using osu.Game.Beatmaps; namespace osu.Game.Overlays.Dashboard.Dashboard { - public class DrawableNewBeatmapsList : DrawableBeatmapsList + public class DrawableNewBeatmapList : DrawableBeatmapList { - public DrawableNewBeatmapsList(List beatmaps) + public DrawableNewBeatmapList(List beatmaps) : base(beatmaps) { } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapsList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs similarity index 79% rename from osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapsList.cs rename to osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs index 8076e86ed0..ee6c3e6d77 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapsList.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs @@ -6,9 +6,9 @@ using osu.Game.Beatmaps; namespace osu.Game.Overlays.Dashboard.Dashboard { - public class DrawablePopularBeatmapsList : DrawableBeatmapsList + public class DrawablePopularBeatmapList : DrawableBeatmapList { - public DrawablePopularBeatmapsList(List beatmaps) + public DrawablePopularBeatmapList(List beatmaps) : base(beatmaps) { } From 7d83cdbf1c18fdaa9d25d866ac62a3d292a5e795 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 2 Aug 2020 21:35:24 +0300 Subject: [PATCH 138/185] Make title in DrawableBeatmapList a property --- osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs | 4 ++-- .../Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs | 2 +- .../Dashboard/Dashboard/DrawablePopularBeatmapList.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs index 7dd969863a..4837d587fd 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard { Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), Colour = colourProvider.Light1, - Text = CreateTitle(), + Text = Title, Padding = new MarginPadding { Left = 10 } } } @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard flow.AddRange(beatmaps.Select(CreateBeatmapPanel)); } - protected abstract string CreateTitle(); + protected abstract string Title { get; } protected abstract DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo); } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs index 3ed42cc4bb..9856f6ae3e 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs @@ -15,6 +15,6 @@ namespace osu.Game.Overlays.Dashboard.Dashboard protected override DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo) => new DashboardNewBeatmapPanel(setInfo); - protected override string CreateTitle() => "New Ranked Beatmaps"; + protected override string Title => "New Ranked Beatmaps"; } } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs index ee6c3e6d77..294a75c48a 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs @@ -15,6 +15,6 @@ namespace osu.Game.Overlays.Dashboard.Dashboard protected override DashboardBeatmapPanel CreateBeatmapPanel(BeatmapSetInfo setInfo) => new DashboardPopularBeatmapPanel(setInfo); - protected override string CreateTitle() => "Popular Beatmaps"; + protected override string Title => "Popular Beatmaps"; } } From bddc61756a3a40f5885b449cd72178b4fd8c2c7b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 2 Aug 2020 21:44:34 +0300 Subject: [PATCH 139/185] Rework padding --- .../TestSceneDashboardBeatmapListing.cs | 8 +++++++- .../Dashboard/Dashboard/DashboardBeatmapPanel.cs | 12 ++++++++---- .../Dashboard/Dashboard/DrawableBeatmapList.cs | 3 +-- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs index be0cc5187e..c5714dae46 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs @@ -36,7 +36,13 @@ namespace osu.Game.Tests.Visual.UserInterface RelativeSizeAxes = Axes.Both, Colour = colourProvider.Background4 }, - new DashboardBeatmapListing(new_beatmaps, popular_beatmaps) + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 10 }, + Child = new DashboardBeatmapListing(new_beatmaps, popular_beatmaps) + } } }); } diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs index 45fa56a177..d74cdf4414 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs @@ -41,16 +41,20 @@ namespace osu.Game.Overlays.Dashboard.Dashboard Height = 60; Children = new Drawable[] { - hoverBackground = new Box + new Container { RelativeSizeAxes = Axes.Both, - Colour = ColourProvider.Background3, - Alpha = 0 + Padding = new MarginPadding { Horizontal = -10 }, + Child = hoverBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = ColourProvider.Background3, + Alpha = 0 + } }, new Container { RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Horizontal = 10 }, Child = new GridContainer { RelativeSizeAxes = Axes.Both, diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs b/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs index 4837d587fd..6e42881de7 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs @@ -41,8 +41,7 @@ namespace osu.Game.Overlays.Dashboard.Dashboard { Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), Colour = colourProvider.Light1, - Text = Title, - Padding = new MarginPadding { Left = 10 } + Text = Title } } }; From dc559093cdc519e600d7304e6f14bdcc06ea91aa Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 2 Aug 2020 21:47:09 +0300 Subject: [PATCH 140/185] Rename namespace from Dashboard to Home --- .../Visual/UserInterface/TestSceneDashboardBeatmapListing.cs | 2 +- .../Dashboard/{Dashboard => Home}/DashboardBeatmapListing.cs | 2 +- .../Dashboard/{Dashboard => Home}/DashboardBeatmapPanel.cs | 2 +- .../Dashboard/{Dashboard => Home}/DashboardNewBeatmapPanel.cs | 2 +- .../{Dashboard => Home}/DashboardPopularBeatmapPanel.cs | 2 +- .../Dashboard/{Dashboard => Home}/DrawableBeatmapList.cs | 2 +- .../Dashboard/{Dashboard => Home}/DrawableNewBeatmapList.cs | 2 +- .../Dashboard/{Dashboard => Home}/DrawablePopularBeatmapList.cs | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) rename osu.Game/Overlays/Dashboard/{Dashboard => Home}/DashboardBeatmapListing.cs (96%) rename osu.Game/Overlays/Dashboard/{Dashboard => Home}/DashboardBeatmapPanel.cs (99%) rename osu.Game/Overlays/Dashboard/{Dashboard => Home}/DashboardNewBeatmapPanel.cs (93%) rename osu.Game/Overlays/Dashboard/{Dashboard => Home}/DashboardPopularBeatmapPanel.cs (96%) rename osu.Game/Overlays/Dashboard/{Dashboard => Home}/DrawableBeatmapList.cs (97%) rename osu.Game/Overlays/Dashboard/{Dashboard => Home}/DrawableNewBeatmapList.cs (92%) rename osu.Game/Overlays/Dashboard/{Dashboard => Home}/DrawablePopularBeatmapList.cs (92%) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs index c5714dae46..c51204eaba 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDashboardBeatmapListing.cs @@ -3,7 +3,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics; -using osu.Game.Overlays.Dashboard.Dashboard; +using osu.Game.Overlays.Dashboard.Home; using osu.Game.Beatmaps; using osu.Game.Overlays; using osu.Framework.Allocation; diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs similarity index 96% rename from osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs rename to osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs index a8c6ab7ba2..4d96825353 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapListing.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapListing.cs @@ -8,7 +8,7 @@ using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osuTK; -namespace osu.Game.Overlays.Dashboard.Dashboard +namespace osu.Game.Overlays.Dashboard.Home { public class DashboardBeatmapListing : CompositeDrawable { diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs similarity index 99% rename from osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs rename to osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs index d74cdf4414..0b660c16af 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs @@ -14,7 +14,7 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osuTK; -namespace osu.Game.Overlays.Dashboard.Dashboard +namespace osu.Game.Overlays.Dashboard.Home { public abstract class DashboardBeatmapPanel : OsuClickableContainer { diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs similarity index 93% rename from osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs rename to osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs index c4782b733d..b212eaf20a 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardNewBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardNewBeatmapPanel.cs @@ -6,7 +6,7 @@ using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Graphics; -namespace osu.Game.Overlays.Dashboard.Dashboard +namespace osu.Game.Overlays.Dashboard.Home { public class DashboardNewBeatmapPanel : DashboardBeatmapPanel { diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DashboardPopularBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs similarity index 96% rename from osu.Game/Overlays/Dashboard/Dashboard/DashboardPopularBeatmapPanel.cs rename to osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs index 04bb261dce..2fb5617796 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DashboardPopularBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs @@ -9,7 +9,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; -namespace osu.Game.Overlays.Dashboard.Dashboard +namespace osu.Game.Overlays.Dashboard.Home { public class DashboardPopularBeatmapPanel : DashboardBeatmapPanel { diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs similarity index 97% rename from osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs rename to osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs index 6e42881de7..f6535b7db3 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawableBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableBeatmapList.cs @@ -11,7 +11,7 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osuTK; -namespace osu.Game.Overlays.Dashboard.Dashboard +namespace osu.Game.Overlays.Dashboard.Home { public abstract class DrawableBeatmapList : CompositeDrawable { diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs similarity index 92% rename from osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs rename to osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs index 9856f6ae3e..75e8ca336d 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawableNewBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawableNewBeatmapList.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps; -namespace osu.Game.Overlays.Dashboard.Dashboard +namespace osu.Game.Overlays.Dashboard.Home { public class DrawableNewBeatmapList : DrawableBeatmapList { diff --git a/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs similarity index 92% rename from osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs rename to osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs index 294a75c48a..90bd00008c 100644 --- a/osu.Game/Overlays/Dashboard/Dashboard/DrawablePopularBeatmapList.cs +++ b/osu.Game/Overlays/Dashboard/Home/DrawablePopularBeatmapList.cs @@ -4,7 +4,7 @@ using System.Collections.Generic; using osu.Game.Beatmaps; -namespace osu.Game.Overlays.Dashboard.Dashboard +namespace osu.Game.Overlays.Dashboard.Home { public class DrawablePopularBeatmapList : DrawableBeatmapList { From b96e32b0bbd741373689ad7755b54d576d87dd56 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 2 Aug 2020 12:26:09 -0700 Subject: [PATCH 141/185] Add xmldoc for updateBindTarget --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index d58acc1ac4..e5b246807c 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -320,6 +320,9 @@ namespace osu.Game.Overlays.KeyBinding base.OnFocusLost(e); } + /// + /// Updates the bind target to the currently hovered key button or the first if clicked anywhere else. + /// private void updateBindTarget() { if (bindTarget != null) bindTarget.IsBinding = false; From ba77fa2945475ab3d6eb8092f384c4500f1a6f51 Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 2 Aug 2020 12:41:35 -0700 Subject: [PATCH 142/185] Add test for clear button --- .../Settings/TestSceneKeyBindingPanel.cs | 40 +++++++++++++++++++ osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 14 +++---- 2 files changed, 47 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 3d335995ac..e7a1cab8eb 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -64,5 +64,45 @@ namespace osu.Game.Tests.Visual.Settings }, 0, true); }); } + + [Test] + public void TestClearButtonOnBindings() + { + KeyBindingRow backBindingRow = null; + + AddStep("click back binding row", () => + { + backBindingRow = panel.ChildrenOfType().ElementAt(10); + InputManager.MoveMouseTo(backBindingRow); + InputManager.Click(MouseButton.Left); + }); + + clickClearButton(); + + AddAssert("first binding cleared", () => string.IsNullOrEmpty(backBindingRow.Buttons.First().Text.Text)); + + AddStep("click second binding", () => + { + var target = backBindingRow.Buttons.ElementAt(1); + + InputManager.MoveMouseTo(target); + InputManager.Click(MouseButton.Left); + }); + + clickClearButton(); + + AddAssert("second binding cleared", () => string.IsNullOrEmpty(backBindingRow.Buttons.ElementAt(1).Text.Text)); + + void clickClearButton() + { + AddStep("click clear button", () => + { + var clearButton = backBindingRow.ChildrenOfType().Single(); + + InputManager.MoveMouseTo(clearButton); + InputManager.Click(MouseButton.Left); + }); + } + } } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index e5b246807c..44b5fe09f4 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -50,7 +50,7 @@ namespace osu.Game.Overlays.KeyBinding private OsuSpriteText text; private Drawable pressAKey; - private FillFlowContainer buttons; + public FillFlowContainer Buttons; public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend((string)text.Text); @@ -93,7 +93,7 @@ namespace osu.Game.Overlays.KeyBinding Text = action.GetDescription(), Margin = new MarginPadding(padding), }, - buttons = new FillFlowContainer + Buttons = new FillFlowContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.TopRight, @@ -116,7 +116,7 @@ namespace osu.Game.Overlays.KeyBinding }; foreach (var b in bindings) - buttons.Add(new KeyButton(b)); + Buttons.Add(new KeyButton(b)); } public void RestoreDefaults() @@ -125,7 +125,7 @@ namespace osu.Game.Overlays.KeyBinding foreach (var d in Defaults) { - var button = buttons[i++]; + var button = Buttons[i++]; button.UpdateKeyCombination(d); store.Update(button.KeyBinding); } @@ -187,7 +187,7 @@ namespace osu.Game.Overlays.KeyBinding if (bindTarget.IsHovered) finalise(); - else if (buttons.Any(b => b.IsHovered)) + else if (Buttons.Any(b => b.IsHovered)) updateBindTarget(); } @@ -326,7 +326,7 @@ namespace osu.Game.Overlays.KeyBinding private void updateBindTarget() { if (bindTarget != null) bindTarget.IsBinding = false; - bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault(); + bindTarget = Buttons.FirstOrDefault(b => b.IsHovered) ?? Buttons.FirstOrDefault(); if (bindTarget != null) bindTarget.IsBinding = true; } @@ -357,7 +357,7 @@ namespace osu.Game.Overlays.KeyBinding } } - private class KeyButton : Container + public class KeyButton : Container { public readonly Framework.Input.Bindings.KeyBinding KeyBinding; From d49e54deb60b3cfee754697c446b9b4ea21cfb2a Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 2 Aug 2020 12:47:23 -0700 Subject: [PATCH 143/185] Add failing test for another regressing behavior --- .../Settings/TestSceneKeyBindingPanel.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index e7a1cab8eb..96075d56d1 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -104,5 +104,37 @@ namespace osu.Game.Tests.Visual.Settings }); } } + + [Test] + public void TestClickRowSelectsFirstBinding() + { + KeyBindingRow backBindingRow = null; + + AddStep("click back binding row", () => + { + backBindingRow = panel.ChildrenOfType().ElementAt(10); + InputManager.MoveMouseTo(backBindingRow); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("first binding selected", () => backBindingRow.Buttons.First().IsBinding); + + AddStep("click second binding", () => + { + var target = backBindingRow.Buttons.ElementAt(1); + + InputManager.MoveMouseTo(target); + InputManager.Click(MouseButton.Left); + }); + + AddStep("click back binding row", () => + { + backBindingRow = panel.ChildrenOfType().ElementAt(10); + InputManager.MoveMouseTo(backBindingRow); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("first binding selected", () => backBindingRow.Buttons.First().IsBinding); + } } } From 7aafc018ad384f5e1d10437519df539a93f4a91b Mon Sep 17 00:00:00 2001 From: Joehu Date: Sun, 2 Aug 2020 12:52:12 -0700 Subject: [PATCH 144/185] Prevent updating bind target when hovering cancel and clear buttons instead --- osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index 44b5fe09f4..a7394579bd 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.KeyBinding public bool FilteringActive { get; set; } private OsuSpriteText text; - private Drawable pressAKey; + private FillFlowContainer cancelAndClearButtons; public FillFlowContainer Buttons; @@ -80,7 +80,7 @@ namespace osu.Game.Overlays.KeyBinding Hollow = true, }; - Children = new[] + Children = new Drawable[] { new Box { @@ -99,7 +99,7 @@ namespace osu.Game.Overlays.KeyBinding Anchor = Anchor.TopRight, Origin = Anchor.TopRight }, - pressAKey = new FillFlowContainer + cancelAndClearButtons = new FillFlowContainer { AutoSizeAxes = Axes.Both, Padding = new MarginPadding(padding) { Top = height + padding * 2 }, @@ -187,7 +187,8 @@ namespace osu.Game.Overlays.KeyBinding if (bindTarget.IsHovered) finalise(); - else if (Buttons.Any(b => b.IsHovered)) + // prevent updating bind target before clear button's action + else if (!cancelAndClearButtons.Any(b => b.IsHovered)) updateBindTarget(); } @@ -298,8 +299,8 @@ namespace osu.Game.Overlays.KeyBinding if (HasFocus) GetContainingInputManager().ChangeFocus(null); - pressAKey.FadeOut(300, Easing.OutQuint); - pressAKey.BypassAutoSizeAxes |= Axes.Y; + cancelAndClearButtons.FadeOut(300, Easing.OutQuint); + cancelAndClearButtons.BypassAutoSizeAxes |= Axes.Y; } protected override void OnFocus(FocusEvent e) @@ -307,8 +308,8 @@ namespace osu.Game.Overlays.KeyBinding AutoSizeDuration = 500; AutoSizeEasing = Easing.OutQuint; - pressAKey.FadeIn(300, Easing.OutQuint); - pressAKey.BypassAutoSizeAxes &= ~Axes.Y; + cancelAndClearButtons.FadeIn(300, Easing.OutQuint); + cancelAndClearButtons.BypassAutoSizeAxes &= ~Axes.Y; updateBindTarget(); base.OnFocus(e); From 435c9de8b9d500890fb649809f38d6263fca2a89 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Mon, 3 Aug 2020 15:25:23 +0900 Subject: [PATCH 145/185] Re-privatise buttons --- .../Visual/Settings/TestSceneKeyBindingPanel.cs | 12 ++++++------ osu.Game/Overlays/KeyBinding/KeyBindingRow.cs | 11 +++++------ 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 96075d56d1..e06b3a8a7e 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -79,11 +79,11 @@ namespace osu.Game.Tests.Visual.Settings clickClearButton(); - AddAssert("first binding cleared", () => string.IsNullOrEmpty(backBindingRow.Buttons.First().Text.Text)); + AddAssert("first binding cleared", () => string.IsNullOrEmpty(backBindingRow.ChildrenOfType().First().Text.Text)); AddStep("click second binding", () => { - var target = backBindingRow.Buttons.ElementAt(1); + var target = backBindingRow.ChildrenOfType().ElementAt(1); InputManager.MoveMouseTo(target); InputManager.Click(MouseButton.Left); @@ -91,7 +91,7 @@ namespace osu.Game.Tests.Visual.Settings clickClearButton(); - AddAssert("second binding cleared", () => string.IsNullOrEmpty(backBindingRow.Buttons.ElementAt(1).Text.Text)); + AddAssert("second binding cleared", () => string.IsNullOrEmpty(backBindingRow.ChildrenOfType().ElementAt(1).Text.Text)); void clickClearButton() { @@ -117,11 +117,11 @@ namespace osu.Game.Tests.Visual.Settings InputManager.Click(MouseButton.Left); }); - AddAssert("first binding selected", () => backBindingRow.Buttons.First().IsBinding); + AddAssert("first binding selected", () => backBindingRow.ChildrenOfType().First().IsBinding); AddStep("click second binding", () => { - var target = backBindingRow.Buttons.ElementAt(1); + var target = backBindingRow.ChildrenOfType().ElementAt(1); InputManager.MoveMouseTo(target); InputManager.Click(MouseButton.Left); @@ -134,7 +134,7 @@ namespace osu.Game.Tests.Visual.Settings InputManager.Click(MouseButton.Left); }); - AddAssert("first binding selected", () => backBindingRow.Buttons.First().IsBinding); + AddAssert("first binding selected", () => backBindingRow.ChildrenOfType().First().IsBinding); } } } diff --git a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs index a7394579bd..b808d49fa2 100644 --- a/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs +++ b/osu.Game/Overlays/KeyBinding/KeyBindingRow.cs @@ -49,8 +49,7 @@ namespace osu.Game.Overlays.KeyBinding private OsuSpriteText text; private FillFlowContainer cancelAndClearButtons; - - public FillFlowContainer Buttons; + private FillFlowContainer buttons; public IEnumerable FilterTerms => bindings.Select(b => b.KeyCombination.ReadableString()).Prepend((string)text.Text); @@ -93,7 +92,7 @@ namespace osu.Game.Overlays.KeyBinding Text = action.GetDescription(), Margin = new MarginPadding(padding), }, - Buttons = new FillFlowContainer + buttons = new FillFlowContainer { AutoSizeAxes = Axes.Both, Anchor = Anchor.TopRight, @@ -116,7 +115,7 @@ namespace osu.Game.Overlays.KeyBinding }; foreach (var b in bindings) - Buttons.Add(new KeyButton(b)); + buttons.Add(new KeyButton(b)); } public void RestoreDefaults() @@ -125,7 +124,7 @@ namespace osu.Game.Overlays.KeyBinding foreach (var d in Defaults) { - var button = Buttons[i++]; + var button = buttons[i++]; button.UpdateKeyCombination(d); store.Update(button.KeyBinding); } @@ -327,7 +326,7 @@ namespace osu.Game.Overlays.KeyBinding private void updateBindTarget() { if (bindTarget != null) bindTarget.IsBinding = false; - bindTarget = Buttons.FirstOrDefault(b => b.IsHovered) ?? Buttons.FirstOrDefault(); + bindTarget = buttons.FirstOrDefault(b => b.IsHovered) ?? buttons.FirstOrDefault(); if (bindTarget != null) bindTarget.IsBinding = true; } From 630322ff85a5c08a1b71e2a35404386fabd483e7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 3 Aug 2020 09:55:06 +0300 Subject: [PATCH 146/185] Adjust font weights in line with web --- osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs | 6 +++--- .../Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs index 0b660c16af..3badea155d 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardBeatmapPanel.cs @@ -102,17 +102,17 @@ namespace osu.Game.Overlays.Dashboard.Home { RelativeSizeAxes = Axes.X, Truncate = true, - Font = OsuFont.GetFont(weight: FontWeight.SemiBold), + Font = OsuFont.GetFont(weight: FontWeight.Regular), Text = SetInfo.Metadata.Title }, new OsuSpriteText { RelativeSizeAxes = Axes.X, Truncate = true, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.SemiBold), + Font = OsuFont.GetFont(size: 12, weight: FontWeight.Regular), Text = SetInfo.Metadata.Artist }, - new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold)) + new LinkFlowContainer(f => f.Font = OsuFont.GetFont(size: 10, weight: FontWeight.Regular)) { AutoSizeAxes = Axes.Y, RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs b/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs index 2fb5617796..e9066c0657 100644 --- a/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs +++ b/osu.Game/Overlays/Dashboard/Home/DashboardPopularBeatmapPanel.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays.Dashboard.Home }, new OsuSpriteText { - Font = OsuFont.GetFont(size: 10), + Font = OsuFont.GetFont(size: 10, weight: FontWeight.Regular), Text = SetInfo.OnlineInfo.FavouriteCount.ToString() } } From af320e4a619784cd979c1eba9af93a9e29ee97d8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 3 Aug 2020 10:03:42 +0300 Subject: [PATCH 147/185] Fix NewsOverlay running request on startup --- osu.Game/Overlays/NewsOverlay.cs | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index a5687b77e2..f8666e22c5 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -67,7 +67,26 @@ namespace osu.Game.Overlays protected override void LoadComplete() { base.LoadComplete(); - article.BindValueChanged(onArticleChanged, true); + article.BindValueChanged(onArticleChanged); + } + + private bool displayUpdateRequired = true; + + protected override void PopIn() + { + base.PopIn(); + + if (displayUpdateRequired) + { + article.TriggerChange(); + displayUpdateRequired = false; + } + } + + protected override void PopOutComplete() + { + base.PopOutComplete(); + displayUpdateRequired = true; } public void ShowFrontPage() From f812767c95428a679d6e5646fc9c6cc5b608ed17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Aug 2020 18:48:10 +0900 Subject: [PATCH 148/185] Add fallback hash generation to fix android startup crash --- osu.Game/OsuGameBase.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 278f2d849f..98f60d52d3 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -36,6 +36,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Skinning; using osuTK.Input; +using RuntimeInfo = osu.Framework.RuntimeInfo; namespace osu.Game { @@ -134,8 +135,17 @@ namespace osu.Game [BackgroundDependencyLoader] private void load() { - using (var str = File.OpenRead(typeof(OsuGameBase).Assembly.Location)) - VersionHash = str.ComputeMD5Hash(); + try + { + using (var str = File.OpenRead(typeof(OsuGameBase).Assembly.Location)) + VersionHash = str.ComputeMD5Hash(); + } + catch + { + // special case for android builds, which can't read DLLs from a packed apk. + // should eventually be handled in a better way. + VersionHash = $"{Version}-{RuntimeInfo.OS}".ComputeMD5Hash(); + } Resources.AddStore(new DllResourceStore(OsuResources.ResourceAssembly)); From c48648ea2ac014f40281f7b0f313949b7d454fda Mon Sep 17 00:00:00 2001 From: Sebastian Krajewski Date: Mon, 3 Aug 2020 12:59:15 +0200 Subject: [PATCH 149/185] Add playfield shift like in osu-stable --- 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 9c8be868b0..93d88f3690 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs @@ -55,6 +55,7 @@ namespace osu.Game.Rulesets.Osu.UI // Scale = 819.2 / 512 // Scale = 1.6 Scale = new Vector2(Parent.ChildSize.X / OsuPlayfield.BASE_SIZE.X); + Position = new Vector2(0, 8 * Parent.ChildSize.Y / OsuPlayfield.BASE_SIZE.Y); // Size = 0.625 Size = Vector2.Divide(Vector2.One, Scale); } From 675f618b288cc1e75fed0a152c7908e61e77e9d4 Mon Sep 17 00:00:00 2001 From: Sebastian Krajewski Date: Mon, 3 Aug 2020 14:18:45 +0200 Subject: [PATCH 150/185] Apply playfield's offset only in play mode --- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 2 +- .../UI/OsuPlayfieldAdjustmentContainer.cs | 12 +++++++++++- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index b4d51d11c9..e6e37fd58e 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); - public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer(); + public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { PlayfieldShift = true }; protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay(); diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs index 93d88f3690..700601dbfd 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs @@ -15,6 +15,14 @@ namespace osu.Game.Rulesets.Osu.UI private const float playfield_size_adjust = 0.8f; + /// + /// Whether an osu-stable playfield offset should be applied (8 osu!pixels) + /// + public bool PlayfieldShift + { + set => ((ScalingContainer)content).PlayfieldShift = value; + } + public OsuPlayfieldAdjustmentContainer() { Anchor = Anchor.Centre; @@ -39,6 +47,8 @@ namespace osu.Game.Rulesets.Osu.UI /// private class ScalingContainer : Container { + internal bool PlayfieldShift { get; set; } + protected override void Update() { base.Update(); @@ -55,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.UI // Scale = 819.2 / 512 // Scale = 1.6 Scale = new Vector2(Parent.ChildSize.X / OsuPlayfield.BASE_SIZE.X); - Position = new Vector2(0, 8 * Parent.ChildSize.Y / OsuPlayfield.BASE_SIZE.Y); + Position = new Vector2(0, (PlayfieldShift ? 8f : 0f) * Parent.ChildSize.Y / OsuPlayfield.BASE_SIZE.Y); // Size = 0.625 Size = Vector2.Divide(Vector2.One, Scale); } From 4d6f60edaf8b49a95ede7b5a97fdcca16dfd13ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 3 Aug 2020 22:41:22 +0900 Subject: [PATCH 151/185] Fix multiplayer match select forcing playback even when user paused --- osu.Game/Screens/Multi/Multiplayer.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Multi/Multiplayer.cs b/osu.Game/Screens/Multi/Multiplayer.cs index 269eab5772..4912df17b1 100644 --- a/osu.Game/Screens/Multi/Multiplayer.cs +++ b/osu.Game/Screens/Multi/Multiplayer.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Input; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; +using osu.Game.Overlays; using osu.Game.Screens.Menu; using osu.Game.Screens.Multi.Components; using osu.Game.Screens.Multi.Lounge; @@ -50,6 +51,9 @@ namespace osu.Game.Screens.Multi [Cached] private readonly Bindable currentFilter = new Bindable(new FilterCriteria()); + [Resolved] + private MusicController music { get; set; } + [Cached(Type = typeof(IRoomManager))] private RoomManager roomManager; @@ -346,8 +350,7 @@ namespace osu.Game.Screens.Multi track.RestartPoint = Beatmap.Value.Metadata.PreviewTime; track.Looping = true; - if (!track.IsRunning) - track.Restart(); + music.EnsurePlayingSomething(); } } else From 22b52d63c7767f4cc9d75c4ff249cf5d5e6828c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Aug 2020 20:51:59 +0900 Subject: [PATCH 152/185] 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 13b4b6ebbb..924e9c4a16 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 745555e0e2..627c2f3d33 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index f1080f0c8b..f443937017 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From d7e82efb671016649e13a1afcaec640691567392 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 4 Aug 2020 21:16:59 +0900 Subject: [PATCH 153/185] Fix tests --- .../Visual/UserInterface/TestSceneLogoTrackingContainer.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs index 010e4330d7..5582cc6826 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLogoTrackingContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using NUnit.Framework; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -263,7 +264,7 @@ namespace osu.Game.Tests.Visual.UserInterface private void moveLogoFacade() { - if (logoFacade?.Transforms.Count == 0 && transferContainer?.Transforms.Count == 0) + if (!(logoFacade?.Transforms).Any() && !(transferContainer?.Transforms).Any()) { Random random = new Random(); trackingContainer.Delay(500).MoveTo(new Vector2(random.Next(0, (int)logo.Parent.DrawWidth), random.Next(0, (int)logo.Parent.DrawHeight)), 300); From 9fb7b8f3d8b6e1c83c0de13c3d4eb1af8d4dc7dd Mon Sep 17 00:00:00 2001 From: Sebastian Krajewski Date: Tue, 4 Aug 2020 15:43:33 +0200 Subject: [PATCH 154/185] Rename the property to "AlignWithStoryboard" --- osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs | 2 +- .../UI/OsuPlayfieldAdjustmentContainer.cs | 11 ++++++----- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs index e6e37fd58e..b2299398e1 100644 --- a/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/UI/DrawableOsuRuleset.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Osu.UI protected override PassThroughInputManager CreateInputManager() => new OsuInputManager(Ruleset.RulesetInfo); - public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { PlayfieldShift = true }; + public override PlayfieldAdjustmentContainer CreatePlayfieldAdjustmentContainer() => new OsuPlayfieldAdjustmentContainer { AlignWithStoryboard = true }; protected override ResumeOverlay CreateResumeOverlay() => new OsuResumeOverlay(); diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs index 700601dbfd..0d1a5a8304 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfieldAdjustmentContainer.cs @@ -11,16 +11,17 @@ namespace osu.Game.Rulesets.Osu.UI public class OsuPlayfieldAdjustmentContainer : PlayfieldAdjustmentContainer { protected override Container Content => content; - private readonly Container content; + private readonly ScalingContainer content; private const float playfield_size_adjust = 0.8f; /// - /// Whether an osu-stable playfield offset should be applied (8 osu!pixels) + /// When true, an offset is applied to allow alignment with historical storyboards displayed in the same parent space. + /// This will shift the playfield downwards slightly. /// - public bool PlayfieldShift + public bool AlignWithStoryboard { - set => ((ScalingContainer)content).PlayfieldShift = value; + set => content.PlayfieldShift = value; } public OsuPlayfieldAdjustmentContainer() @@ -65,7 +66,7 @@ namespace osu.Game.Rulesets.Osu.UI // Scale = 819.2 / 512 // Scale = 1.6 Scale = new Vector2(Parent.ChildSize.X / OsuPlayfield.BASE_SIZE.X); - Position = new Vector2(0, (PlayfieldShift ? 8f : 0f) * Parent.ChildSize.Y / OsuPlayfield.BASE_SIZE.Y); + Position = new Vector2(0, (PlayfieldShift ? 8f : 0f) * Scale.X); // Size = 0.625 Size = Vector2.Divide(Vector2.One, Scale); } From 24bc9b33b1437cf845834743c7019a5429773b46 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 5 Aug 2020 15:40:45 +0900 Subject: [PATCH 155/185] Always place spinners behind hitcircles/sliders --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 600efefca3..4ef9bbe091 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -23,7 +23,8 @@ namespace osu.Game.Rulesets.Osu.UI { public class OsuPlayfield : Playfield { - private readonly ApproachCircleProxyContainer approachCircles; + private readonly ProxyContainer approachCircles; + private readonly ProxyContainer spinnerProxies; private readonly JudgementContainer judgementLayer; private readonly FollowPointRenderer followPoints; private readonly OrderedHitPolicy hitPolicy; @@ -38,6 +39,10 @@ namespace osu.Game.Rulesets.Osu.UI { InternalChildren = new Drawable[] { + spinnerProxies = new ProxyContainer + { + RelativeSizeAxes = Axes.Both + }, followPoints = new FollowPointRenderer { RelativeSizeAxes = Axes.Both, @@ -54,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.UI { Child = HitObjectContainer, }, - approachCircles = new ApproachCircleProxyContainer + approachCircles = new ProxyContainer { RelativeSizeAxes = Axes.Both, Depth = -1, @@ -76,6 +81,9 @@ namespace osu.Game.Rulesets.Osu.UI h.OnNewResult += onNewResult; h.OnLoadComplete += d => { + if (d is DrawableSpinner) + spinnerProxies.Add(d.CreateProxy()); + if (d is IDrawableHitObjectWithProxiedApproach c) approachCircles.Add(c.ProxiedLayer.CreateProxy()); }; @@ -113,9 +121,9 @@ namespace osu.Game.Rulesets.Osu.UI public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => HitObjectContainer.ReceivePositionalInputAt(screenSpacePos); - private class ApproachCircleProxyContainer : LifetimeManagementContainer + private class ProxyContainer : LifetimeManagementContainer { - public void Add(Drawable approachCircleProxy) => AddInternal(approachCircleProxy); + public void Add(Drawable proxy) => AddInternal(proxy); } private class DrawableJudgementPool : DrawablePool From bb73489ae5947c4be39ecaca7a3634f4b49c6db1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Aug 2020 18:44:34 +0900 Subject: [PATCH 156/185] Fix very short spinners being impossible to complete --- .../TestSceneSpinner.cs | 40 ++++++++++++++----- .../Objects/Drawables/DrawableSpinner.cs | 13 +++++- osu.Game.Rulesets.Osu/Objects/Spinner.cs | 3 +- 3 files changed, 44 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index b57561f3e1..be92a25dbe 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -17,32 +17,54 @@ namespace osu.Game.Rulesets.Osu.Tests { private int depthIndex; - public TestSceneSpinner() + private TestDrawableSpinner drawableSpinner; + + [TestCase(false)] + [TestCase(true)] + public void TestVariousSpinners(bool autoplay) { AddStep("Miss Big", () => SetContents(() => testSingle(2))); AddStep("Miss Medium", () => SetContents(() => testSingle(5))); AddStep("Miss Small", () => SetContents(() => testSingle(7))); - AddStep("Hit Big", () => SetContents(() => testSingle(2, true))); - AddStep("Hit Medium", () => SetContents(() => testSingle(5, true))); - AddStep("Hit Small", () => SetContents(() => testSingle(7, true))); + AddStep("Hit Big", () => SetContents(() => testSingle(2, autoplay))); + AddStep("Hit Medium", () => SetContents(() => testSingle(5, autoplay))); + AddStep("Hit Small", () => SetContents(() => testSingle(7, autoplay))); } - private Drawable testSingle(float circleSize, bool auto = false) + [TestCase(false)] + [TestCase(true)] + public void TestLongSpinner(bool autoplay) { - var spinner = new Spinner { StartTime = Time.Current + 2000, EndTime = Time.Current + 5000 }; + AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 2000))); + AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult); + AddUntilStep("Check correct progress", () => drawableSpinner.Progress == (autoplay ? 1 : 0)); + } + + [TestCase(false)] + [TestCase(true)] + public void TestSuperShortSpinner(bool autoplay) + { + AddStep("Very short spinner", () => SetContents(() => testSingle(5, autoplay, 200))); + AddUntilStep("Wait for completion", () => drawableSpinner.Result.HasResult); + AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1); + } + + private Drawable testSingle(float circleSize, bool auto = false, double length = 3000) + { + var spinner = new Spinner { StartTime = Time.Current + 2000, EndTime = Time.Current + +2000 + length }; spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); - var drawable = new TestDrawableSpinner(spinner, auto) + drawableSpinner = new TestDrawableSpinner(spinner, auto) { Anchor = Anchor.Centre, Depth = depthIndex++ }; foreach (var mod in SelectedMods.Value.OfType()) - mod.ApplyToDrawableHitObjects(new[] { drawable }); + mod.ApplyToDrawableHitObjects(new[] { drawableSpinner }); - return drawable; + return drawableSpinner; } private class TestDrawableSpinner : DrawableSpinner diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 68516bedf8..7363da0de8 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -175,7 +175,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// /// The completion progress of this spinner from 0..1 (clamped). /// - public float Progress => Math.Clamp(RotationTracker.CumulativeRotation / 360 / Spinner.SpinsRequired, 0, 1); + public float Progress + { + get + { + if (Spinner.SpinsRequired == 0) + // some spinners are so short they can't require an integer spin count. + // these become implicitly hit. + return 1; + + return Math.Clamp(RotationTracker.CumulativeRotation / 360 / Spinner.SpinsRequired, 0, 1); + } + } protected override void CheckForResult(bool userTriggered, double timeOffset) { diff --git a/osu.Game.Rulesets.Osu/Objects/Spinner.cs b/osu.Game.Rulesets.Osu/Objects/Spinner.cs index 619b49926e..1658a4e7c2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Spinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Spinner.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.Beatmaps.ControlPoints; using osu.Game.Rulesets.Judgements; @@ -45,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Objects double minimumRotationsPerSecond = stable_matching_fudge * BeatmapDifficulty.DifficultyRange(difficulty.OverallDifficulty, 3, 5, 7.5); - SpinsRequired = (int)Math.Max(1, (secondsDuration * minimumRotationsPerSecond)); + SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond); MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration); } From 3916d98e5292db1546b4b9ced3bcea8c27ae887f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Aug 2020 18:50:37 +0900 Subject: [PATCH 157/185] Add comment for clarity --- osu.Game/Overlays/NewsOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/NewsOverlay.cs b/osu.Game/Overlays/NewsOverlay.cs index f8666e22c5..09fb445b1f 100644 --- a/osu.Game/Overlays/NewsOverlay.cs +++ b/osu.Game/Overlays/NewsOverlay.cs @@ -67,6 +67,8 @@ namespace osu.Game.Overlays protected override void LoadComplete() { base.LoadComplete(); + + // should not be run until first pop-in to avoid requesting data before user views. article.BindValueChanged(onArticleChanged); } From bf1bb3267420e64400d8991c34bb2b65f026c9c5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 5 Aug 2020 19:06:58 +0900 Subject: [PATCH 158/185] Add missing toolbar tooltips for right-hand icons --- osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs | 2 ++ osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs | 2 ++ osu.Game/Overlays/Toolbar/ToolbarChatButton.cs | 2 ++ osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs | 2 ++ osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs | 2 ++ osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs | 2 ++ osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs | 2 ++ 7 files changed, 14 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index eecb368ee9..3c38fdd207 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -11,6 +11,8 @@ namespace osu.Game.Overlays.Toolbar public ToolbarBeatmapListingButton() { SetIcon(OsuIcon.ChevronDownCircle); + TooltipMain = "Beatmap Listing"; + TooltipSub = "Browse for new beatmaps"; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs index 84210e27a4..c88b418853 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChangelogButton.cs @@ -11,6 +11,8 @@ namespace osu.Game.Overlays.Toolbar public ToolbarChangelogButton() { SetIcon(FontAwesome.Solid.Bullhorn); + TooltipMain = "Changelog"; + TooltipSub = "Track recent dev updates in the osu! ecosystem"; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs index ad0e5be551..ec7da54571 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs @@ -11,6 +11,8 @@ namespace osu.Game.Overlays.Toolbar public ToolbarChatButton() { SetIcon(FontAwesome.Solid.Comments); + TooltipMain = "Chat"; + TooltipSub = "Join the real-time discussion"; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index b29aec5842..712da12208 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs @@ -11,6 +11,8 @@ namespace osu.Game.Overlays.Toolbar public ToolbarMusicButton() { Icon = FontAwesome.Solid.Music; + TooltipMain = "Now playing"; + TooltipSub = "Manage the currently playing track"; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs index e813a3f4cb..106c67a041 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNewsButton.cs @@ -11,6 +11,8 @@ namespace osu.Game.Overlays.Toolbar public ToolbarNewsButton() { Icon = FontAwesome.Solid.Newspaper; + TooltipMain = "News"; + TooltipSub = "Get up-to-date on community happenings"; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs index cbd097696d..c026ce99fe 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRankingsButton.cs @@ -11,6 +11,8 @@ namespace osu.Game.Overlays.Toolbar public ToolbarRankingsButton() { SetIcon(FontAwesome.Regular.ChartBar); + TooltipMain = "Ranking"; + TooltipSub = "Find out who's the best right now"; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs index f6646eb81d..0dbb552c15 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -11,6 +11,8 @@ namespace osu.Game.Overlays.Toolbar public ToolbarSocialButton() { Icon = FontAwesome.Solid.Users; + TooltipMain = "Friends"; + TooltipSub = "Interact with those close to you"; } [BackgroundDependencyLoader(true)] From 84f6b7608cf83a76db915d148d6c7f2090290742 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 Aug 2020 20:05:53 +0300 Subject: [PATCH 159/185] Remove misleading ExpandNumberPiece lookup --- osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs | 5 ----- osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs | 1 - 2 files changed, 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs index 80602dfa40..81d1d05b66 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuLegacySkinTransformer.cs @@ -6,7 +6,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Skinning; using osuTK; -using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Osu.Skinning { @@ -139,10 +138,6 @@ namespace osu.Game.Rulesets.Osu.Skinning // HitCircleOverlayAboveNumer (with typo) should still be supported for now. return Source.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber) ?? Source.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumer); - - case OsuSkinConfiguration.ExpandNumberPiece: - bool expand = !(source.GetConfig(LegacySetting.Version)?.Value >= 2.0m); - return SkinUtils.As(new BindableBool(expand)); } break; diff --git a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs index c04312a2c3..154160fdb5 100644 --- a/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs +++ b/osu.Game.Rulesets.Osu/Skinning/OsuSkinConfiguration.cs @@ -7,7 +7,6 @@ namespace osu.Game.Rulesets.Osu.Skinning { HitCirclePrefix, HitCircleOverlap, - ExpandNumberPiece, SliderBorderSize, SliderPathRadius, AllowSliderBallTint, From 1ab6110c05e7fc4e0bbc18ec3847636e847780b2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 Aug 2020 20:07:05 +0300 Subject: [PATCH 160/185] Apply fade out to the number piece with quarter the pieces duration --- .../Skinning/LegacyMainCirclePiece.cs | 67 ++++++++++++------- 1 file changed, 43 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index fe28e69ff2..41fe170ae0 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -14,6 +14,7 @@ using osu.Game.Rulesets.Osu.Objects; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; +using static osu.Game.Skinning.LegacySkinConfiguration; namespace osu.Game.Rulesets.Osu.Skinning { @@ -28,7 +29,9 @@ namespace osu.Game.Rulesets.Osu.Skinning Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } + private Container circlePieces; private Sprite hitCircleSprite, hitCircleOverlay; + private SkinnableSpriteText hitCircleText; private readonly IBindable state = new Bindable(); @@ -45,12 +48,27 @@ namespace osu.Game.Rulesets.Osu.Skinning InternalChildren = new Drawable[] { - hitCircleSprite = new Sprite + circlePieces = new Container { - Texture = getTextureWithFallback(string.Empty), - Colour = drawableObject.AccentColour.Value, Anchor = Anchor.Centre, Origin = Anchor.Centre, + RelativeSizeAxes = Axes.Both, + Children = new[] + { + hitCircleSprite = new Sprite + { + Texture = getTextureWithFallback(string.Empty), + Colour = drawableObject.AccentColour.Value, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + hitCircleOverlay = new Sprite + { + Texture = getTextureWithFallback("overlay"), + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + } }, hitCircleText = new SkinnableSpriteText(new OsuSkinComponent(OsuSkinComponents.HitCircleText), _ => new OsuSpriteText { @@ -61,31 +79,16 @@ namespace osu.Game.Rulesets.Osu.Skinning Anchor = Anchor.Centre, Origin = Anchor.Centre, }, - hitCircleOverlay = new Sprite - { - Texture = getTextureWithFallback("overlay"), - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - } }; bool overlayAboveNumber = skin.GetConfig(OsuSkinConfiguration.HitCircleOverlayAboveNumber)?.Value ?? true; - if (!overlayAboveNumber) - ChangeInternalChildDepth(hitCircleText, -float.MaxValue); + if (overlayAboveNumber) + AddInternal(hitCircleOverlay.CreateProxy()); state.BindTo(drawableObject.State); accentColour.BindTo(drawableObject.AccentColour); indexInCurrentCombo.BindTo(osuObject.IndexInCurrentComboBindable); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - - state.BindValueChanged(updateState, true); - accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); - indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); Texture getTextureWithFallback(string name) { @@ -98,6 +101,15 @@ namespace osu.Game.Rulesets.Osu.Skinning } } + protected override void LoadComplete() + { + base.LoadComplete(); + + state.BindValueChanged(updateState, true); + accentColour.BindValueChanged(colour => hitCircleSprite.Colour = colour.NewValue, true); + indexInCurrentCombo.BindValueChanged(index => hitCircleText.Text = (index.NewValue + 1).ToString(), true); + } + private void updateState(ValueChangedEvent state) { const double legacy_fade_duration = 240; @@ -105,13 +117,20 @@ namespace osu.Game.Rulesets.Osu.Skinning switch (state.NewValue) { case ArmedState.Hit: - this.FadeOut(legacy_fade_duration, Easing.Out); + circlePieces.FadeOut(legacy_fade_duration, Easing.Out); + circlePieces.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - hitCircleSprite.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); - hitCircleOverlay.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + var legacyVersion = skin.GetConfig(LegacySetting.Version)?.Value; - if (skin.GetConfig(OsuSkinConfiguration.ExpandNumberPiece)?.Value ?? true) + if (legacyVersion >= 2.0m) + // legacy skins of version 2.0 and newer apply immediate fade out to the number piece and only that. + hitCircleText.FadeOut(legacy_fade_duration / 4, Easing.Out); + else + { + // old skins scale and fade it normally along other pieces. + hitCircleText.FadeOut(legacy_fade_duration, Easing.Out); hitCircleText.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + } break; } From 43161697f88983bc271715e792c7a89eed9631d0 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 Aug 2020 23:41:57 +0300 Subject: [PATCH 161/185] Fix wrong english --- osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 41fe170ae0..74a2cb7dc5 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -123,7 +123,7 @@ namespace osu.Game.Rulesets.Osu.Skinning var legacyVersion = skin.GetConfig(LegacySetting.Version)?.Value; if (legacyVersion >= 2.0m) - // legacy skins of version 2.0 and newer apply immediate fade out to the number piece and only that. + // legacy skins of version 2.0 and newer only apply very short fade out to the number piece. hitCircleText.FadeOut(legacy_fade_duration / 4, Easing.Out); else { From 9465e7abe10e22482d598f9529d43a9b7468706f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 5 Aug 2020 23:45:00 +0300 Subject: [PATCH 162/185] Rename sprites container to "circleSprites" --- osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 74a2cb7dc5..9fbd9f50b4 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Skinning Size = new Vector2(OsuHitObject.OBJECT_RADIUS * 2); } - private Container circlePieces; + private Container circleSprites; private Sprite hitCircleSprite, hitCircleOverlay; private SkinnableSpriteText hitCircleText; @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Skinning InternalChildren = new Drawable[] { - circlePieces = new Container + circleSprites = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -117,8 +117,8 @@ namespace osu.Game.Rulesets.Osu.Skinning switch (state.NewValue) { case ArmedState.Hit: - circlePieces.FadeOut(legacy_fade_duration, Easing.Out); - circlePieces.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); + circleSprites.FadeOut(legacy_fade_duration, Easing.Out); + circleSprites.ScaleTo(1.4f, legacy_fade_duration, Easing.Out); var legacyVersion = skin.GetConfig(LegacySetting.Version)?.Value; From e3f314349abc74ebc3ef6e1774045325fd763a75 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Aug 2020 12:27:30 +0900 Subject: [PATCH 163/185] Don't use title case Co-authored-by: Joseph Madamba --- osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index 3c38fdd207..64430c77ac 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Toolbar public ToolbarBeatmapListingButton() { SetIcon(OsuIcon.ChevronDownCircle); - TooltipMain = "Beatmap Listing"; + TooltipMain = "Beatmap listing"; TooltipSub = "Browse for new beatmaps"; } From d5324be07d00ff9276831f2263ac326efa020f35 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Aug 2020 12:33:40 +0900 Subject: [PATCH 164/185] Fix malformed testcase --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index be92a25dbe..ed89a4c991 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -23,12 +23,10 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase(true)] public void TestVariousSpinners(bool autoplay) { - AddStep("Miss Big", () => SetContents(() => testSingle(2))); - AddStep("Miss Medium", () => SetContents(() => testSingle(5))); - AddStep("Miss Small", () => SetContents(() => testSingle(7))); - AddStep("Hit Big", () => SetContents(() => testSingle(2, autoplay))); - AddStep("Hit Medium", () => SetContents(() => testSingle(5, autoplay))); - AddStep("Hit Small", () => SetContents(() => testSingle(7, autoplay))); + string term = autoplay ? "Hit" : "Miss"; + AddStep($"{term} Big", () => SetContents(() => testSingle(2, autoplay))); + AddStep($"{term} Medium", () => SetContents(() => testSingle(5, autoplay))); + AddStep($"{term} Small", () => SetContents(() => testSingle(7, autoplay))); } [TestCase(false)] From 3b15a50f0d343fbf985abc0167d86f04972958df Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Aug 2020 12:34:42 +0900 Subject: [PATCH 165/185] Fix unnecessary + character --- osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs index ed89a4c991..47b3926ceb 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinner.cs @@ -49,7 +49,13 @@ namespace osu.Game.Rulesets.Osu.Tests private Drawable testSingle(float circleSize, bool auto = false, double length = 3000) { - var spinner = new Spinner { StartTime = Time.Current + 2000, EndTime = Time.Current + +2000 + length }; + const double delay = 2000; + + var spinner = new Spinner + { + StartTime = Time.Current + delay, + EndTime = Time.Current + delay + length + }; spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { CircleSize = circleSize }); From 9a00ad48c617a4d490ab5420698b2656c3679a45 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 6 Aug 2020 14:43:39 +0900 Subject: [PATCH 166/185] Update components to use extension methods --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 1 + osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs | 2 ++ osu.Game/Screens/Play/PauseOverlay.cs | 1 + 3 files changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 7363da0de8..a2a49b5c42 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index 168e937256..3f7dc957fb 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -117,6 +117,8 @@ namespace osu.Game.Rulesets.UI public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotSupportedException(); + public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotImplementedException(); + public BindableNumber Volume => throw new NotSupportedException(); public BindableNumber Balance => throw new NotSupportedException(); diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index fa917cda32..97f1d1c91d 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -4,6 +4,7 @@ using System; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Game.Audio; using osu.Game.Graphics; From 641279ec3e8397f23b2f21c200cf99af313d9253 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 6 Aug 2020 14:43:48 +0900 Subject: [PATCH 167/185] Make SkinnableSound an IAdjustableAudioComponent --- osu.Game/Skinning/SkinnableSound.cs | 42 ++++++++--------------------- 1 file changed, 11 insertions(+), 31 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 27f6c37895..11856fa581 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -4,19 +4,18 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; using osu.Framework.Graphics.Audio; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Transforms; using osu.Game.Audio; using osu.Game.Screens.Play; namespace osu.Game.Skinning { - public class SkinnableSound : SkinReloadableDrawable + public class SkinnableSound : SkinReloadableDrawable, IAdjustableAudioComponent { private readonly ISampleInfo[] hitSamples; @@ -143,36 +142,17 @@ namespace osu.Game.Skinning public BindableNumber Tempo => samplesContainer.Tempo; + public void AddAdjustment(AdjustableProperty type, BindableNumber adjustBindable) + => samplesContainer.AddAdjustment(type, adjustBindable); + + public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) + => samplesContainer.RemoveAdjustment(type, adjustBindable); + + public void RemoveAllAdjustments(AdjustableProperty type) + => samplesContainer.RemoveAllAdjustments(type); + public bool IsPlaying => samplesContainer.Any(s => s.Playing); - /// - /// Smoothly adjusts over time. - /// - /// A to which further transforms can be added. - public TransformSequence VolumeTo(double newVolume, double duration = 0, Easing easing = Easing.None) => - samplesContainer.VolumeTo(newVolume, duration, easing); - - /// - /// Smoothly adjusts over time. - /// - /// A to which further transforms can be added. - public TransformSequence BalanceTo(double newBalance, double duration = 0, Easing easing = Easing.None) => - samplesContainer.BalanceTo(newBalance, duration, easing); - - /// - /// Smoothly adjusts over time. - /// - /// A to which further transforms can be added. - public TransformSequence FrequencyTo(double newFrequency, double duration = 0, Easing easing = Easing.None) => - samplesContainer.FrequencyTo(newFrequency, duration, easing); - - /// - /// Smoothly adjusts over time. - /// - /// A to which further transforms can be added. - public TransformSequence TempoTo(double newTempo, double duration = 0, Easing easing = Easing.None) => - samplesContainer.TempoTo(newTempo, duration, easing); - #endregion } } From ad959ce5238dbd8ca4465305d703efac065a3dab Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 6 Aug 2020 01:06:51 -0700 Subject: [PATCH 168/185] Make toolbar button abstract --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 86a3f5d8aa..a03ea64eb2 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -18,7 +18,7 @@ using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Toolbar { - public class ToolbarButton : OsuClickableContainer + public abstract class ToolbarButton : OsuClickableContainer { public const float WIDTH = Toolbar.HEIGHT * 1.4f; @@ -68,7 +68,7 @@ namespace osu.Game.Overlays.Toolbar private readonly SpriteText tooltip2; protected FillFlowContainer Flow; - public ToolbarButton() + protected ToolbarButton() : base(HoverSampleSet.Loud) { Width = WIDTH; From 7bcb68ffacd76d2f7b5d743781598b733f2c2731 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 6 Aug 2020 01:17:24 -0700 Subject: [PATCH 169/185] Handle overlay toggling with toolbar buttons instead --- osu.Game/OsuGame.cs | 28 +----------------- .../Toolbar/ToolbarBeatmapListingButton.cs | 3 ++ osu.Game/Overlays/Toolbar/ToolbarButton.cs | 29 +++++++++++++++---- .../Overlays/Toolbar/ToolbarChatButton.cs | 3 ++ .../Overlays/Toolbar/ToolbarHomeButton.cs | 18 ++---------- .../Overlays/Toolbar/ToolbarMusicButton.cs | 3 ++ .../Toolbar/ToolbarNotificationButton.cs | 3 ++ .../Overlays/Toolbar/ToolbarSettingsButton.cs | 3 ++ .../Overlays/Toolbar/ToolbarSocialButton.cs | 3 ++ 9 files changed, 45 insertions(+), 48 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 26f7c3b93b..b5752214bd 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -67,8 +67,6 @@ namespace osu.Game [NotNull] private readonly NotificationOverlay notifications = new NotificationOverlay(); - private NowPlayingOverlay nowPlaying; - private BeatmapListingOverlay beatmapListing; private DashboardOverlay dashboard; @@ -650,7 +648,7 @@ namespace osu.Game Origin = Anchor.TopRight, }, rightFloatingOverlayContent.Add, true); - loadComponentSingleFile(nowPlaying = new NowPlayingOverlay + loadComponentSingleFile(new NowPlayingOverlay { GetToolbarHeight = () => ToolbarOffset, Anchor = Anchor.TopRight, @@ -862,18 +860,6 @@ namespace osu.Game switch (action) { - case GlobalAction.ToggleNowPlaying: - nowPlaying.ToggleVisibility(); - return true; - - case GlobalAction.ToggleChat: - chatOverlay.ToggleVisibility(); - return true; - - case GlobalAction.ToggleSocial: - dashboard.ToggleVisibility(); - return true; - case GlobalAction.ResetInputSettings: var sensitivity = frameworkConfig.GetBindable(FrameworkSetting.CursorSensitivity); @@ -889,18 +875,6 @@ namespace osu.Game Toolbar.ToggleVisibility(); return true; - case GlobalAction.ToggleSettings: - Settings.ToggleVisibility(); - return true; - - case GlobalAction.ToggleDirect: - beatmapListing.ToggleVisibility(); - return true; - - case GlobalAction.ToggleNotifications: - notifications.ToggleVisibility(); - return true; - case GlobalAction.ToggleGameplayMouseButtons: LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons)); return true; diff --git a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs index 64430c77ac..cde305fffd 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarBeatmapListingButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Game.Graphics; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { @@ -13,6 +14,8 @@ namespace osu.Game.Overlays.Toolbar SetIcon(OsuIcon.ChevronDownCircle); TooltipMain = "Beatmap listing"; TooltipSub = "Browse for new beatmaps"; + + Hotkey = GlobalAction.ToggleDirect; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index a03ea64eb2..3f1dccc45a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -5,23 +5,27 @@ 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.Bindings; +using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; +using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Framework.Input.Events; -using osu.Game.Graphics.Containers; -using osu.Game.Graphics.UserInterface; namespace osu.Game.Overlays.Toolbar { - public abstract class ToolbarButton : OsuClickableContainer + public abstract class ToolbarButton : OsuClickableContainer, IKeyBindingHandler { public const float WIDTH = Toolbar.HEIGHT * 1.4f; + protected GlobalAction? Hotkey { get; set; } + public void SetIcon(Drawable icon) { IconContainer.Icon = icon; @@ -164,6 +168,21 @@ namespace osu.Game.Overlays.Toolbar HoverBackground.FadeOut(200); tooltipContainer.FadeOut(100); } + + public bool OnPressed(GlobalAction action) + { + if (action == Hotkey) + { + Click(); + return true; + } + + return false; + } + + public void OnReleased(GlobalAction action) + { + } } public class OpaqueBackground : Container diff --git a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs index ec7da54571..dee4be0c1f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarChatButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { @@ -13,6 +14,8 @@ namespace osu.Game.Overlays.Toolbar SetIcon(FontAwesome.Solid.Comments); TooltipMain = "Chat"; TooltipSub = "Join the real-time discussion"; + + Hotkey = GlobalAction.ToggleChat; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs index e642f0c453..4845c9a99f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs @@ -2,33 +2,19 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics.Sprites; -using osu.Framework.Input.Bindings; using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { - public class ToolbarHomeButton : ToolbarButton, IKeyBindingHandler + public class ToolbarHomeButton : ToolbarButton { public ToolbarHomeButton() { Icon = FontAwesome.Solid.Home; TooltipMain = "Home"; TooltipSub = "Return to the main menu"; - } - public bool OnPressed(GlobalAction action) - { - if (action == GlobalAction.Home) - { - Click(); - return true; - } - - return false; - } - - public void OnReleased(GlobalAction action) - { + Hotkey = GlobalAction.Home; } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index 712da12208..59276a5943 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { @@ -13,6 +14,8 @@ namespace osu.Game.Overlays.Toolbar Icon = FontAwesome.Solid.Music; TooltipMain = "Now playing"; TooltipSub = "Manage the currently playing track"; + + Hotkey = GlobalAction.ToggleNowPlaying; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs index dbd6c557d3..a699fd907f 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarNotificationButton.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; @@ -28,6 +29,8 @@ namespace osu.Game.Overlays.Toolbar TooltipMain = "Notifications"; TooltipSub = "Waiting for 'ya"; + Hotkey = GlobalAction.ToggleNotifications; + Add(countDisplay = new CountCircle { Alpha = 0, diff --git a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs index 79942012f9..ed2a23ec2a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { @@ -13,6 +14,8 @@ namespace osu.Game.Overlays.Toolbar Icon = FontAwesome.Solid.Cog; TooltipMain = "Settings"; TooltipSub = "Change your settings"; + + Hotkey = GlobalAction.ToggleSettings; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs index 0dbb552c15..6faa58c559 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSocialButton.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; +using osu.Game.Input.Bindings; namespace osu.Game.Overlays.Toolbar { @@ -13,6 +14,8 @@ namespace osu.Game.Overlays.Toolbar Icon = FontAwesome.Solid.Users; TooltipMain = "Friends"; TooltipSub = "Interact with those close to you"; + + Hotkey = GlobalAction.ToggleSocial; } [BackgroundDependencyLoader(true)] From d574cac702612dc4c36748588cebf4991d3ab64f Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 6 Aug 2020 01:18:45 -0700 Subject: [PATCH 170/185] Add keybinding to toolbar button's tooltip --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 35 ++++++++++++++++++++-- 1 file changed, 32 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 3f1dccc45a..9fd47fd150 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.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.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -14,6 +15,7 @@ using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Input; using osu.Game.Input.Bindings; using osuTK; using osuTK.Graphics; @@ -70,8 +72,12 @@ namespace osu.Game.Overlays.Toolbar private readonly FillFlowContainer tooltipContainer; private readonly SpriteText tooltip1; private readonly SpriteText tooltip2; + private readonly SpriteText keyBindingTooltip; protected FillFlowContainer Flow; + [Resolved] + private KeyBindingStore store { get; set; } + protected ToolbarButton() : base(HoverSampleSet.Loud) { @@ -127,7 +133,7 @@ namespace osu.Game.Overlays.Toolbar Origin = TooltipAnchor, Position = new Vector2(TooltipAnchor.HasFlag(Anchor.x0) ? 5 : -5, 5), Alpha = 0, - Children = new[] + Children = new Drawable[] { tooltip1 = new OsuSpriteText { @@ -136,17 +142,40 @@ namespace osu.Game.Overlays.Toolbar Shadow = true, Font = OsuFont.GetFont(size: 22, weight: FontWeight.Bold), }, - tooltip2 = new OsuSpriteText + new FillFlowContainer { + AutoSizeAxes = Axes.Both, Anchor = TooltipAnchor, Origin = TooltipAnchor, - Shadow = true, + Direction = FillDirection.Horizontal, + Children = new[] + { + tooltip2 = new OsuSpriteText { Shadow = true }, + keyBindingTooltip = new OsuSpriteText { Shadow = true } + } } } } }; } + [BackgroundDependencyLoader] + private void load() + { + updateTooltip(); + + store.KeyBindingChanged += updateTooltip; + } + + private void updateTooltip() + { + var binding = store.Query().Find(b => (GlobalAction)b.Action == Hotkey); + + var keyBindingString = binding?.KeyCombination.ReadableString(); + + keyBindingTooltip.Text = !string.IsNullOrEmpty(keyBindingString) ? $" ({keyBindingString})" : string.Empty; + } + protected override bool OnMouseDown(MouseDownEvent e) => true; protected override bool OnClick(ClickEvent e) From f9c369b23cff9b0bdc1b8b7907ed74d8919bf123 Mon Sep 17 00:00:00 2001 From: Joehu Date: Thu, 6 Aug 2020 01:20:03 -0700 Subject: [PATCH 171/185] Fix toolbar music button tooltip overflowing off-screen --- osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index 59276a5943..f9aa2de497 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.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.Sprites; using osu.Game.Input.Bindings; @@ -9,6 +10,8 @@ namespace osu.Game.Overlays.Toolbar { public class ToolbarMusicButton : ToolbarOverlayToggleButton { + protected override Anchor TooltipAnchor => Anchor.TopRight; + public ToolbarMusicButton() { Icon = FontAwesome.Solid.Music; From 9685df0ecac3c264b6379a240c073473c4d410ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Aug 2020 18:22:17 +0900 Subject: [PATCH 172/185] Only update key binding on next usage to avoid large blocking calls --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 9fd47fd150..0afc6642b2 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Caching; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -76,7 +77,7 @@ namespace osu.Game.Overlays.Toolbar protected FillFlowContainer Flow; [Resolved] - private KeyBindingStore store { get; set; } + private KeyBindingStore keyBindings { get; set; } protected ToolbarButton() : base(HoverSampleSet.Loud) @@ -159,21 +160,25 @@ namespace osu.Game.Overlays.Toolbar }; } + private readonly Cached tooltipKeyBinding = new Cached(); + [BackgroundDependencyLoader] private void load() { - updateTooltip(); - - store.KeyBindingChanged += updateTooltip; + keyBindings.KeyBindingChanged += () => tooltipKeyBinding.Invalidate(); + updateKeyBindingTooltip(); } - private void updateTooltip() + private void updateKeyBindingTooltip() { - var binding = store.Query().Find(b => (GlobalAction)b.Action == Hotkey); + if (tooltipKeyBinding.IsValid) + return; + var binding = keyBindings.Query().Find(b => (GlobalAction)b.Action == Hotkey); var keyBindingString = binding?.KeyCombination.ReadableString(); - keyBindingTooltip.Text = !string.IsNullOrEmpty(keyBindingString) ? $" ({keyBindingString})" : string.Empty; + + tooltipKeyBinding.Validate(); } protected override bool OnMouseDown(MouseDownEvent e) => true; @@ -187,6 +192,8 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnHover(HoverEvent e) { + updateKeyBindingTooltip(); + HoverBackground.FadeIn(200); tooltipContainer.FadeIn(100); return base.OnHover(e); From f994bf28884b66d7efc4654c50ebbb961640ecb0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Aug 2020 21:34:48 +0900 Subject: [PATCH 173/185] 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 924e9c4a16..e5fed09c07 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 627c2f3d33..18c3052ca3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index f443937017..b034253d88 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From c84452cfbfdb8e72424702ee1e25cea82aa39606 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 6 Aug 2020 21:53:20 +0900 Subject: [PATCH 174/185] Update usages --- osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs | 2 ++ osu.Game/Skinning/SkinnableSound.cs | 9 +++++---- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs index 168e937256..83a1077d70 100644 --- a/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs +++ b/osu.Game/Rulesets/UI/DrawableRulesetDependencies.cs @@ -117,6 +117,8 @@ namespace osu.Game.Rulesets.UI public void RemoveAdjustment(AdjustableProperty type, BindableNumber adjustBindable) => throw new NotSupportedException(); + public void RemoveAllAdjustments(AdjustableProperty type) => throw new NotSupportedException(); + public BindableNumber Volume => throw new NotSupportedException(); public BindableNumber Balance => throw new NotSupportedException(); diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index 27f6c37895..f19aaee821 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; @@ -149,28 +150,28 @@ namespace osu.Game.Skinning /// Smoothly adjusts over time. /// /// A to which further transforms can be added. - public TransformSequence VolumeTo(double newVolume, double duration = 0, Easing easing = Easing.None) => + public TransformSequence> VolumeTo(double newVolume, double duration = 0, Easing easing = Easing.None) => samplesContainer.VolumeTo(newVolume, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. - public TransformSequence BalanceTo(double newBalance, double duration = 0, Easing easing = Easing.None) => + public TransformSequence> BalanceTo(double newBalance, double duration = 0, Easing easing = Easing.None) => samplesContainer.BalanceTo(newBalance, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. - public TransformSequence FrequencyTo(double newFrequency, double duration = 0, Easing easing = Easing.None) => + public TransformSequence> FrequencyTo(double newFrequency, double duration = 0, Easing easing = Easing.None) => samplesContainer.FrequencyTo(newFrequency, duration, easing); /// /// Smoothly adjusts over time. /// /// A to which further transforms can be added. - public TransformSequence TempoTo(double newTempo, double duration = 0, Easing easing = Easing.None) => + public TransformSequence> TempoTo(double newTempo, double duration = 0, Easing easing = Easing.None) => samplesContainer.TempoTo(newTempo, duration, easing); #endregion From bce3f3952fbc1154b74d190b2947624b1558c574 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Fri, 7 Aug 2020 16:36:40 +0900 Subject: [PATCH 175/185] Split out variable declaration --- osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs index 9fbd9f50b4..0ab3e8825b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/LegacyMainCirclePiece.cs @@ -30,7 +30,8 @@ namespace osu.Game.Rulesets.Osu.Skinning } private Container circleSprites; - private Sprite hitCircleSprite, hitCircleOverlay; + private Sprite hitCircleSprite; + private Sprite hitCircleOverlay; private SkinnableSpriteText hitCircleText; From 1f84e541518a29d0b514f99b9ce8830b68daf68e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Aug 2020 20:16:16 +0900 Subject: [PATCH 176/185] Improve messaging when timeshift token retrieval fails Obviously not a final solution, but should better help self-compiling (or unofficial package) users better understand why this is happening. --- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index da082692d7..79b4b04722 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Multi.Play { failed = true; - Logger.Error(e, "Failed to retrieve a score submission token."); + Logger.Error(e, "Failed to retrieve a score submission token.\n\nThis may happen if you are not running an official release of osu! (ie. you are self-compiling)."); Schedule(() => { From 730d13fda6f08a6976c30139ab32d908b78eadc8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 10 Aug 2020 20:48:04 +0900 Subject: [PATCH 177/185] Always show newly presented overlay at front This feels much better. Does not change order if the overlay to be shown is not yet completely hidden. - Closes #9815. --- osu.Game/OsuGame.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index b5752214bd..623c677991 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -702,6 +702,9 @@ namespace osu.Game if (state.NewValue == Visibility.Hidden) return; singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); + + if (!overlay.IsPresent) + overlayContent.ChangeChildDepth(overlay, (float)-Clock.CurrentTime); }; } From d7de8b2916af35ebd843b08c6f2a3c0d8ad788a6 Mon Sep 17 00:00:00 2001 From: "dependabot-preview[bot]" <27856297+dependabot-preview[bot]@users.noreply.github.com> Date: Mon, 10 Aug 2020 17:17:07 +0000 Subject: [PATCH 178/185] Bump Microsoft.NET.Test.Sdk from 16.6.1 to 16.7.0 Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 16.6.1 to 16.7.0. - [Release notes](https://github.com/microsoft/vstest/releases) - [Commits](https://github.com/microsoft/vstest/compare/v16.6.1...v16.7.0) 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 7c0b73e8c3..f9d56dfa78 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 972cbec4a2..ed00ed0b4c 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 d6a68abaf2..f3837ea6b1 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 ada7ac5d74..e896606ee8 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 4b0506d818..d767973528 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 f256b8e4e9..95f5deb2cc 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 61f1c4fe62d5d4c52fa98ee2895af00229c9748d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 10 Aug 2020 19:51:00 +0200 Subject: [PATCH 179/185] Extract replay-transforming helper test method --- .../TestSceneSpinnerRotation.cs | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index b46964e8b7..816c0c38d9 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -121,19 +121,7 @@ namespace osu.Game.Rulesets.Osu.Tests public void TestRotationDirection([Values(true, false)] bool clockwise) { if (clockwise) - { - AddStep("flip replay", () => - { - var drawableRuleset = this.ChildrenOfType().Single(); - var score = drawableRuleset.ReplayScore; - var scoreWithFlippedReplay = new Score - { - ScoreInfo = score.ScoreInfo, - Replay = flipReplay(score.Replay) - }; - drawableRuleset.SetReplayScore(scoreWithFlippedReplay); - }); - } + transformReplay(flip); addSeekStep(5000); @@ -141,7 +129,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("spinner symbol direction correct", () => clockwise ? spinnerSymbol.Rotation > 0 : spinnerSymbol.Rotation < 0); } - private Replay flipReplay(Replay scoreReplay) => new Replay + private Replay flip(Replay scoreReplay) => new Replay { Frames = scoreReplay .Frames @@ -203,6 +191,18 @@ namespace osu.Game.Rulesets.Osu.Tests AddUntilStep("wait for seek to finish", () => Precision.AlmostEquals(time, Player.DrawableRuleset.FrameStableClock.CurrentTime, 100)); } + private void transformReplay(Func replayTransformation) => AddStep("set replay", () => + { + var drawableRuleset = this.ChildrenOfType().Single(); + var score = drawableRuleset.ReplayScore; + var transformedScore = new Score + { + ScoreInfo = score.ScoreInfo, + Replay = replayTransformation.Invoke(score.Replay) + }; + drawableRuleset.SetReplayScore(transformedScore); + }); + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap { HitObjects = new List From 5d63b5f6a5be7657a75024448b44c6ed214bf7b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 10 Aug 2020 20:04:14 +0200 Subject: [PATCH 180/185] Add failing test cases --- .../TestSceneSpinnerRotation.cs | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index 816c0c38d9..b6f4efc24c 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -7,6 +7,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Framework.Timing; @@ -184,6 +185,49 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("spm still valid", () => Precision.AlmostEquals(drawableSpinner.SpmCounter.SpinsPerMinute, estimatedSpm, 1.0)); } + [TestCase(0.5)] + [TestCase(2.0)] + public void TestSpinUnaffectedByClockRate(double rate) + { + double expectedProgress = 0; + double expectedSpm = 0; + + addSeekStep(1000); + AddStep("retrieve spinner state", () => + { + expectedProgress = drawableSpinner.Progress; + expectedSpm = drawableSpinner.SpmCounter.SpinsPerMinute; + }); + + addSeekStep(0); + + AddStep("adjust track rate", () => track.AddAdjustment(AdjustableProperty.Tempo, new BindableDouble(rate))); + // autoplay replay frames use track time; + // if a spin takes 1000ms in track time and we're playing with a 2x rate adjustment, the spin will take 500ms of *real* time. + // therefore we need to apply the rate adjustment to the replay itself to change from track time to real time, + // as real time is what we care about for spinners + // (so we're making the spin take 1000ms in real time *always*, regardless of the track clock's rate). + transformReplay(replay => applyRateAdjustment(replay, rate)); + + addSeekStep(1000); + AddAssert("progress almost same", () => Precision.AlmostEquals(expectedProgress, drawableSpinner.Progress, 0.05)); + AddAssert("spm almost same", () => Precision.AlmostEquals(expectedSpm, drawableSpinner.SpmCounter.SpinsPerMinute, 2.0)); + } + + private Replay applyRateAdjustment(Replay scoreReplay, double rate) => new Replay + { + Frames = scoreReplay + .Frames + .Cast() + .Select(replayFrame => + { + var adjustedTime = replayFrame.Time * rate; + return new OsuReplayFrame(adjustedTime, replayFrame.Position, replayFrame.Actions.ToArray()); + }) + .Cast() + .ToList() + }; + private void addSeekStep(double time) { AddStep($"seek to {time}", () => track.Seek(time)); From cca78235d5e9c0028852563329802f1d026da6c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 10 Aug 2020 22:17:47 +0200 Subject: [PATCH 181/185] Replace CumulativeRotation with RateAdjustedRotation --- .../TestSceneSpinnerRotation.cs | 12 +++++------ .../Objects/Drawables/DrawableSpinner.cs | 6 +++--- .../Drawables/Pieces/DefaultSpinnerDisc.cs | 2 +- .../Pieces/SpinnerRotationTracker.cs | 21 +++++++++++++++---- 4 files changed, 27 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index b6f4efc24c..69857f8ef9 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -70,11 +70,11 @@ namespace osu.Game.Rulesets.Osu.Tests trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f); }); AddAssert("is disc rotation not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, 0, 100)); - AddAssert("is disc rotation absolute not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, 0, 100)); + AddAssert("is disc rotation absolute not almost 0", () => !Precision.AlmostEquals(drawableSpinner.RotationTracker.RateAdjustedRotation, 0, 100)); addSeekStep(0); AddAssert("is disc rotation almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.Rotation, 0, trackerRotationTolerance)); - AddAssert("is disc rotation absolute almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, 0, 100)); + AddAssert("is disc rotation absolute almost 0", () => Precision.AlmostEquals(drawableSpinner.RotationTracker.RateAdjustedRotation, 0, 100)); } [Test] @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Tests finalSpinnerSymbolRotation = spinnerSymbol.Rotation; spinnerSymbolRotationTolerance = Math.Abs(finalSpinnerSymbolRotation * 0.05f); }); - AddStep("retrieve cumulative disc rotation", () => finalCumulativeTrackerRotation = drawableSpinner.RotationTracker.CumulativeRotation); + AddStep("retrieve cumulative disc rotation", () => finalCumulativeTrackerRotation = drawableSpinner.RotationTracker.RateAdjustedRotation); addSeekStep(2500); AddAssert("disc rotation rewound", @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Tests () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation / 2, spinnerSymbolRotationTolerance)); AddAssert("is cumulative rotation rewound", // cumulative rotation is not damped, so we're treating it as the "ground truth" and allowing a comparatively smaller margin of error. - () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, finalCumulativeTrackerRotation / 2, 100)); + () => Precision.AlmostEquals(drawableSpinner.RotationTracker.RateAdjustedRotation, finalCumulativeTrackerRotation / 2, 100)); addSeekStep(5000); AddAssert("is disc rotation almost same", @@ -115,7 +115,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("is symbol rotation almost same", () => Precision.AlmostEquals(spinnerSymbol.Rotation, finalSpinnerSymbolRotation, spinnerSymbolRotationTolerance)); AddAssert("is cumulative rotation almost same", - () => Precision.AlmostEquals(drawableSpinner.RotationTracker.CumulativeRotation, finalCumulativeTrackerRotation, 100)); + () => Precision.AlmostEquals(drawableSpinner.RotationTracker.RateAdjustedRotation, finalCumulativeTrackerRotation, 100)); } [Test] @@ -153,7 +153,7 @@ namespace osu.Game.Rulesets.Osu.Tests { // multipled by 2 to nullify the score multiplier. (autoplay mod selected) var totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2; - return totalScore == (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360) * SpinnerTick.SCORE_PER_TICK; + return totalScore == (int)(drawableSpinner.RotationTracker.RateAdjustedRotation / 360) * SpinnerTick.SCORE_PER_TICK; }); addSeekStep(0); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index a2a49b5c42..f10d11827b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // these become implicitly hit. return 1; - return Math.Clamp(RotationTracker.CumulativeRotation / 360 / Spinner.SpinsRequired, 0, 1); + return Math.Clamp(RotationTracker.RateAdjustedRotation / 360 / Spinner.SpinsRequired, 0, 1); } } @@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!SpmCounter.IsPresent && RotationTracker.Tracking) SpmCounter.FadeIn(HitObject.TimeFadeIn); - SpmCounter.SetRotation(RotationTracker.CumulativeRotation); + SpmCounter.SetRotation(RotationTracker.RateAdjustedRotation); updateBonusScore(); } @@ -245,7 +245,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (ticks.Count == 0) return; - int spins = (int)(RotationTracker.CumulativeRotation / 360); + int spins = (int)(RotationTracker.RateAdjustedRotation / 360); if (spins < wholeSpins) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs index dfb692eba9..1476fe6010 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/DefaultSpinnerDisc.cs @@ -177,7 +177,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { get { - int rotations = (int)(drawableSpinner.RotationTracker.CumulativeRotation / 360); + int rotations = (int)(drawableSpinner.RotationTracker.RateAdjustedRotation / 360); if (wholeRotationCount == rotations) return false; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs index 0cc6c842f4..f1a782cbb5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SpinnerRotationTracker.cs @@ -31,17 +31,28 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces public readonly BindableBool Complete = new BindableBool(); /// - /// The total rotation performed on the spinner disc, disregarding the spin direction. + /// The total rotation performed on the spinner disc, disregarding the spin direction, + /// adjusted for the track's playback rate. /// /// + /// /// This value is always non-negative and is monotonically increasing with time /// (i.e. will only increase if time is passing forward, but can decrease during rewind). + /// + /// + /// The rotation from each frame is multiplied by the clock's current playback rate. + /// The reason this is done is to ensure that spinners give the same score and require the same number of spins + /// regardless of whether speed-modifying mods are applied. + /// /// /// - /// If the spinner is spun 360 degrees clockwise and then 360 degrees counter-clockwise, + /// Assuming no speed-modifying mods are active, + /// if the spinner is spun 360 degrees clockwise and then 360 degrees counter-clockwise, /// this property will return the value of 720 (as opposed to 0 for ). + /// If Double Time is active instead (with a speed multiplier of 1.5x), + /// in the same scenario the property will return 720 * 1.5 = 1080. /// - public float CumulativeRotation { get; private set; } + public float RateAdjustedRotation { get; private set; } /// /// Whether the spinning is spinning at a reasonable speed to be considered visually spinning. @@ -113,7 +124,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } currentRotation += angle; - CumulativeRotation += Math.Abs(angle) * Math.Sign(Clock.ElapsedFrameTime); + // rate has to be applied each frame, because it's not guaranteed to be constant throughout playback + // (see: ModTimeRamp) + RateAdjustedRotation += (float)(Math.Abs(angle) * Clock.Rate); } } } From ecb4826e1974ce4752020732d78d6631863d9e9f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Aug 2020 06:54:26 +0900 Subject: [PATCH 182/185] 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 e5fed09c07..a384ad4c34 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 18c3052ca3..b38ef38ec2 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -24,7 +24,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index b034253d88..00ddd94d53 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -80,7 +80,7 @@ - + From d1b106a3b557c0c8dd13746679a7e68193863eff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Aug 2020 10:59:28 +0900 Subject: [PATCH 183/185] Include mention of old releases in error message --- osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs index 79b4b04722..04da943a10 100644 --- a/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs +++ b/osu.Game/Screens/Multi/Play/TimeshiftPlayer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Multi.Play { failed = true; - Logger.Error(e, "Failed to retrieve a score submission token.\n\nThis may happen if you are not running an official release of osu! (ie. you are self-compiling)."); + Logger.Error(e, "Failed to retrieve a score submission token.\n\nThis may happen if you are running an old or non-official release of osu! (ie. you are self-compiling)."); Schedule(() => { From 471ed968e3d7a81b566a137078b5e3bef9e35d10 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Aug 2020 11:09:02 +0900 Subject: [PATCH 184/185] Fix crash when same ruleset loaded more than once If the same ruleset assembly was present more than once in the current AppDomain, the game would crash. We recently saw this in Rider EAP9. While this behaviour may change going forward, this is a good safety measure regardless. --- osu.Game/Rulesets/RulesetStore.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Rulesets/RulesetStore.cs b/osu.Game/Rulesets/RulesetStore.cs index dd43092c0d..5d93f5186b 100644 --- a/osu.Game/Rulesets/RulesetStore.cs +++ b/osu.Game/Rulesets/RulesetStore.cs @@ -191,6 +191,11 @@ namespace osu.Game.Rulesets if (loadedAssemblies.ContainsKey(assembly)) return; + // the same assembly may be loaded twice in the same AppDomain (currently a thing in certain Rider versions https://youtrack.jetbrains.com/issue/RIDER-48799). + // as a failsafe, also compare by FullName. + if (loadedAssemblies.Any(a => a.Key.FullName == assembly.FullName)) + return; + try { loadedAssemblies[assembly] = assembly.GetTypes().First(t => t.IsPublic && t.IsSubclassOf(typeof(Ruleset))); From 8bfe6ba27c275208f665e6c125b73fade6c6fdd7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 11 Aug 2020 23:04:00 +0900 Subject: [PATCH 185/185] Fix informational overlays not hiding each other correctly --- osu.Game/OsuGame.cs | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 623c677991..053eb01dcd 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -683,9 +683,8 @@ namespace osu.Game { overlay.State.ValueChanged += state => { - if (state.NewValue == Visibility.Hidden) return; - - informationalOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); + if (state.NewValue != Visibility.Hidden) + showOverlayAboveOthers(overlay, informationalOverlays); }; } @@ -699,12 +698,8 @@ namespace osu.Game // informational overlays should be dismissed on a show or hide of a full overlay. informationalOverlays.ForEach(o => o.Hide()); - if (state.NewValue == Visibility.Hidden) return; - - singleDisplayOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); - - if (!overlay.IsPresent) - overlayContent.ChangeChildDepth(overlay, (float)-Clock.CurrentTime); + if (state.NewValue != Visibility.Hidden) + showOverlayAboveOthers(overlay, singleDisplayOverlays); }; } @@ -729,6 +724,15 @@ namespace osu.Game notifications.State.ValueChanged += _ => updateScreenOffset(); } + private void showOverlayAboveOthers(OverlayContainer overlay, OverlayContainer[] otherOverlays) + { + otherOverlays.Where(o => o != overlay).ForEach(o => o.Hide()); + + // show above others if not visible at all, else leave at current depth. + if (!overlay.IsPresent) + overlayContent.ChangeChildDepth(overlay, (float)-Clock.CurrentTime); + } + public class GameIdleTracker : IdleTracker { private InputManager inputManager;