From 6e28c1b29a440299d87f81c35dab27a25fc44aa4 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Jun 2021 17:54:57 +0900 Subject: [PATCH 01/26] Move default catcher sprite to its own file --- .../Skinning/Default/DefaultCatcherSprite.cs | 26 +++++++++++++++++++ osu.Game.Rulesets.Catch/UI/CatcherSprite.cs | 24 ++++------------- 2 files changed, 31 insertions(+), 19 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcherSprite.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcherSprite.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcherSprite.cs new file mode 100644 index 0000000000..f366adf134 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcherSprite.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets.Catch.UI; + +namespace osu.Game.Rulesets.Catch.Skinning.Default +{ + public class DefaultCatcherSprite : Sprite + { + private readonly CatcherAnimationState state; + + public DefaultCatcherSprite(CatcherAnimationState state) + { + this.state = state; + } + + [BackgroundDependencyLoader] + private void load(TextureStore textures) + { + Texture = textures.Get($"Gameplay/catch/fruit-catcher-{state.ToString().ToLower()}"); + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs index ef69e3d2d1..a840792597 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherSprite.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.Sprites; -using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Skinning; using osuTK; +namespace osu.Game.Rulesets.Catch.Skinning.Default +{ +} + namespace osu.Game.Rulesets.Catch.UI { public class CatcherSprite : SkinnableDrawable @@ -39,21 +41,5 @@ namespace osu.Game.Rulesets.Catch.UI return CatchSkinComponents.CatcherIdle; } } - - private class DefaultCatcherSprite : Sprite - { - private readonly CatcherAnimationState state; - - public DefaultCatcherSprite(CatcherAnimationState state) - { - this.state = state; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - Texture = textures.Get($"Gameplay/catch/fruit-catcher-{state.ToString().ToLower()}"); - } - } } } From 0192549d6cea54375398d89d564dc75a36161863 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Jun 2021 19:51:40 +0900 Subject: [PATCH 02/26] Refactor catcher sprite to use skinned piece pattern --- .../CatchSkinComponents.cs | 4 +- .../Skinning/Default/DefaultCatcher.cs | 54 +++++++++++++ .../Skinning/Default/DefaultCatcherSprite.cs | 26 ------- .../Skinning/ICatcherPiece.cs | 12 +++ .../Legacy/CatchLegacySkinTransformer.cs | 15 ++-- .../Skinning/Legacy/LegacyCatcher.cs | 76 +++++++++++++++++++ osu.Game.Rulesets.Catch/UI/Catcher.cs | 63 ++++----------- osu.Game.Rulesets.Catch/UI/CatcherSprite.cs | 45 ----------- .../UI/CatcherTrailDisplay.cs | 6 +- 9 files changed, 163 insertions(+), 138 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs delete mode 100644 osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcherSprite.cs create mode 100644 osu.Game.Rulesets.Catch/Skinning/ICatcherPiece.cs create mode 100644 osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcher.cs delete mode 100644 osu.Game.Rulesets.Catch/UI/CatcherSprite.cs diff --git a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs index 668f7197be..e736d68740 100644 --- a/osu.Game.Rulesets.Catch/CatchSkinComponents.cs +++ b/osu.Game.Rulesets.Catch/CatchSkinComponents.cs @@ -8,9 +8,7 @@ namespace osu.Game.Rulesets.Catch Fruit, Banana, Droplet, - CatcherIdle, - CatcherFail, - CatcherKiai, + Catcher, CatchComboCounter } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs new file mode 100644 index 0000000000..655e557705 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs @@ -0,0 +1,54 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +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.Graphics.Textures; +using osu.Game.Rulesets.Catch.UI; + +namespace osu.Game.Rulesets.Catch.Skinning.Default +{ + public class DefaultCatcher : CompositeDrawable, ICatcherPiece + { + public Bindable CurrentState { get; } = new Bindable(); + + public Texture CurrentTexture => sprite.Texture; + + private readonly Sprite sprite; + + private readonly Dictionary textures = new Dictionary(); + + public DefaultCatcher() + { + RelativeSizeAxes = Axes.Both; + InternalChild = sprite = new Sprite + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.Both, + FillMode = FillMode.Fit + }; + } + + [BackgroundDependencyLoader] + private void load(TextureStore store, Bindable currentState) + { + CurrentState.BindTo(currentState); + + textures[CatcherAnimationState.Idle] = store.Get(@"Gameplay/catch/fruit-catcher-idle"); + textures[CatcherAnimationState.Fail] = store.Get(@"Gameplay/catch/fruit-catcher-fail"); + textures[CatcherAnimationState.Kiai] = store.Get(@"Gameplay/catch/fruit-catcher-kiai"); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + CurrentState.BindValueChanged(state => sprite.Texture = textures[state.NewValue], true); + } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcherSprite.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcherSprite.cs deleted file mode 100644 index f366adf134..0000000000 --- a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcherSprite.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.Framework.Allocation; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Game.Rulesets.Catch.UI; - -namespace osu.Game.Rulesets.Catch.Skinning.Default -{ - public class DefaultCatcherSprite : Sprite - { - private readonly CatcherAnimationState state; - - public DefaultCatcherSprite(CatcherAnimationState state) - { - this.state = state; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - Texture = textures.Get($"Gameplay/catch/fruit-catcher-{state.ToString().ToLower()}"); - } - } -} diff --git a/osu.Game.Rulesets.Catch/Skinning/ICatcherPiece.cs b/osu.Game.Rulesets.Catch/Skinning/ICatcherPiece.cs new file mode 100644 index 0000000000..0b51846eda --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/ICatcherPiece.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Rulesets.Catch.Skinning +{ + public interface ICatcherPiece + { + Texture CurrentTexture { get; } + } +} diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 8c9e602cd4..2713f17789 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -65,17 +65,12 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy return null; - case CatchSkinComponents.CatcherIdle: - return this.GetAnimation("fruit-catcher-idle", true, true, true) ?? - this.GetAnimation("fruit-ryuuta", true, true, true); + case CatchSkinComponents.Catcher: + if (this.GetAnimation(@"fruit-ryuuta", true, true) != null || + this.GetAnimation(@"fruit-catcher-idle", true, true) != null) + return new LegacyCatcher(); - case CatchSkinComponents.CatcherFail: - return this.GetAnimation("fruit-catcher-fail", true, true, true) ?? - this.GetAnimation("fruit-ryuuta", true, true, true); - - case CatchSkinComponents.CatcherKiai: - return this.GetAnimation("fruit-catcher-kiai", true, true, true) ?? - this.GetAnimation("fruit-ryuuta", true, true, true); + return null; case CatchSkinComponents.CatchComboCounter: if (providesComboCounter) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcher.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcher.cs new file mode 100644 index 0000000000..4bf5dd9dd8 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcher.cs @@ -0,0 +1,76 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Animations; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Skinning.Legacy +{ + public class LegacyCatcher : CompositeDrawable, ICatcherPiece + { + public Bindable CurrentState { get; } = new Bindable(); + + public Texture CurrentTexture => (currentDrawable as TextureAnimation)?.CurrentFrame ?? (currentDrawable as Sprite)?.Texture; + + private readonly Dictionary drawables = new Dictionary(); + + private Drawable currentDrawable; + + public LegacyCatcher() + { + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin, Bindable currentState) + { + CurrentState.BindTo(currentState); + + AddRangeInternal(new[] + { + drawables[CatcherAnimationState.Idle] = getDrawableFor(@"fruit-catcher-idle"), + drawables[CatcherAnimationState.Fail] = getDrawableFor(@"fruit-catcher-fail"), + drawables[CatcherAnimationState.Kiai] = getDrawableFor(@"fruit-catcher-kiai"), + }); + currentDrawable = drawables[CatcherAnimationState.Idle]; + + foreach (var d in drawables.Values) + { + d.Anchor = Anchor.TopCentre; + d.Origin = Anchor.TopCentre; + d.RelativeSizeAxes = Axes.Both; + d.Size = Vector2.One; + d.FillMode = FillMode.Fit; + d.Alpha = 0; + } + + Drawable getDrawableFor(string name) => + skin.GetAnimation(name, true, true, true) ?? + skin.GetAnimation(@"fruit-ryuuta", true, true, true) ?? + skin.GetAnimation(@"fruit-catcher-idle", true, true, true); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + CurrentState.BindValueChanged(state => + { + currentDrawable.Alpha = 0; + currentDrawable = drawables[state.NewValue]; + currentDrawable.Alpha = 1; + + (currentDrawable as IFramedAnimation)?.GotoFrame(0); + }, true); + } + } +} diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 1c29e8b20c..9daf6e23ab 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -7,9 +7,9 @@ using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; +using osu.Framework.Graphics.Textures; using osu.Framework.Input.Bindings; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -18,6 +18,7 @@ using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Objects.Drawables; using osu.Game.Rulesets.Catch.Skinning; +using osu.Game.Rulesets.Catch.Skinning.Default; using osu.Game.Rulesets.Judgements; using osu.Game.Skinning; using osuTK; @@ -78,17 +79,17 @@ namespace osu.Game.Rulesets.Catch.UI /// private readonly Container droppedObjectTarget; - public CatcherAnimationState CurrentState { get; private set; } + [Cached] + public readonly Bindable CurrentStateBindable = new Bindable(); + + public CatcherAnimationState CurrentState => CurrentStateBindable.Value; /// /// The width of the catcher which can receive fruit. Equivalent to "catchMargin" in osu-stable. /// public const float ALLOWED_CATCH_RANGE = 0.8f; - /// - /// The drawable catcher for . - /// - internal Drawable CurrentDrawableCatcher => currentCatcher.Drawable; + internal Texture CurrentTexture => currentCatcherPiece.CurrentTexture; private bool dashing; @@ -110,11 +111,9 @@ namespace osu.Game.Rulesets.Catch.UI /// private readonly float catchWidth; - private readonly CatcherSprite catcherIdle; - private readonly CatcherSprite catcherKiai; - private readonly CatcherSprite catcherFail; + private readonly SkinnableDrawable currentCatcher; - private CatcherSprite currentCatcher; + private ICatcherPiece currentCatcherPiece => (ICatcherPiece)currentCatcher.Drawable; private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR; @@ -156,20 +155,12 @@ namespace osu.Game.Rulesets.Catch.UI Anchor = Anchor.TopCentre, Origin = Anchor.BottomCentre, }, - catcherIdle = new CatcherSprite(CatcherAnimationState.Idle) + currentCatcher = new SkinnableDrawable( + new CatchSkinComponent(CatchSkinComponents.Catcher), + _ => new DefaultCatcher()) { Anchor = Anchor.TopCentre, - Alpha = 0, - }, - catcherKiai = new CatcherSprite(CatcherAnimationState.Kiai) - { - Anchor = Anchor.TopCentre, - Alpha = 0, - }, - catcherFail = new CatcherSprite(CatcherAnimationState.Fail) - { - Anchor = Anchor.TopCentre, - Alpha = 0, + OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE }, hitExplosionContainer = new HitExplosionContainer { @@ -184,8 +175,6 @@ namespace osu.Game.Rulesets.Catch.UI { hitLighting = config.GetBindable(OsuSetting.HitLighting); trails = new CatcherTrailDisplay(this); - - updateCatcher(); } protected override void LoadComplete() @@ -436,36 +425,12 @@ namespace osu.Game.Rulesets.Catch.UI } } - private void updateCatcher() - { - currentCatcher?.Hide(); - - switch (CurrentState) - { - default: - currentCatcher = catcherIdle; - break; - - case CatcherAnimationState.Fail: - currentCatcher = catcherFail; - break; - - case CatcherAnimationState.Kiai: - currentCatcher = catcherKiai; - break; - } - - currentCatcher.Show(); - (currentCatcher.Drawable as IFramedAnimation)?.GotoFrame(0); - } - private void updateState(CatcherAnimationState state) { if (CurrentState == state) return; - CurrentState = state; - updateCatcher(); + CurrentStateBindable.Value = state; } private void placeCaughtObject(DrawablePalpableCatchHitObject drawableObject, Vector2 position) diff --git a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs b/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs deleted file mode 100644 index a840792597..0000000000 --- a/osu.Game.Rulesets.Catch/UI/CatcherSprite.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Game.Rulesets.Catch.Skinning.Default; -using osu.Game.Skinning; -using osuTK; - -namespace osu.Game.Rulesets.Catch.Skinning.Default -{ -} - -namespace osu.Game.Rulesets.Catch.UI -{ - public class CatcherSprite : SkinnableDrawable - { - protected override bool ApplySizeRestrictionsToDefault => true; - - public CatcherSprite(CatcherAnimationState state) - : base(new CatchSkinComponent(componentFromState(state)), _ => - new DefaultCatcherSprite(state), confineMode: ConfineMode.ScaleToFit) - { - RelativeSizeAxes = Axes.None; - Size = new Vector2(CatcherArea.CATCHER_SIZE); - - // Sets the origin roughly to the centre of the catcher's plate to allow for correct scaling. - OriginPosition = new Vector2(0.5f, 0.06f) * CatcherArea.CATCHER_SIZE; - } - - private static CatchSkinComponents componentFromState(CatcherAnimationState state) - { - switch (state) - { - case CatcherAnimationState.Fail: - return CatchSkinComponents.CatcherFail; - - case CatcherAnimationState.Kiai: - return CatchSkinComponents.CatcherKiai; - - default: - return CatchSkinComponents.CatcherIdle; - } - } - } -} diff --git a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs index fa65190032..0aef215797 100644 --- a/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs +++ b/osu.Game.Rulesets.Catch/UI/CatcherTrailDisplay.cs @@ -4,10 +4,8 @@ using System; using JetBrains.Annotations; using osu.Framework.Graphics; -using osu.Framework.Graphics.Animations; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Pooling; -using osu.Framework.Graphics.Sprites; using osuTK; using osuTK.Graphics; @@ -120,11 +118,9 @@ namespace osu.Game.Rulesets.Catch.UI private CatcherTrailSprite createTrailSprite(Container target) { - var texture = (catcher.CurrentDrawableCatcher as TextureAnimation)?.CurrentFrame ?? ((Sprite)catcher.CurrentDrawableCatcher).Texture; - CatcherTrailSprite sprite = trailPool.Get(); - sprite.Texture = texture; + sprite.Texture = catcher.CurrentTexture; sprite.Anchor = catcher.Anchor; sprite.Scale = catcher.Scale; sprite.Blending = BlendingParameters.Additive; From 109a366722bb6f46a5d3304da7a6742faa988fb8 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Jun 2021 21:58:39 +0900 Subject: [PATCH 03/26] Use separate classes for old and new catcher legacy skin element - Fix catcher texture animation is reset for legacy old catcher skin --- .../Legacy/CatchLegacySkinTransformer.cs | 11 ++++-- .../{LegacyCatcher.cs => LegacyCatcherNew.cs} | 25 ++++++------- .../Skinning/Legacy/LegacyCatcherOld.cs | 37 +++++++++++++++++++ 3 files changed, 56 insertions(+), 17 deletions(-) rename osu.Game.Rulesets.Catch/Skinning/Legacy/{LegacyCatcher.cs => LegacyCatcherNew.cs} (75%) create mode 100644 osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 2713f17789..6a9a3c43a2 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -66,9 +66,14 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy return null; case CatchSkinComponents.Catcher: - if (this.GetAnimation(@"fruit-ryuuta", true, true) != null || - this.GetAnimation(@"fruit-catcher-idle", true, true) != null) - return new LegacyCatcher(); + // New elements will be ignored when the old element exists. + if (GetTexture(@"fruit-ryuuta") != null || + GetTexture(@"fruit-ryuuta-0") != null) + return new LegacyCatcherOld(); + + if (GetTexture(@"fruit-catcher-idle") != null || + GetTexture(@"fruit-catcher-idle-0") != null) + return new LegacyCatcherNew(); return null; diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcher.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs similarity index 75% rename from osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcher.cs rename to osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs index 4bf5dd9dd8..4e662869c9 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcher.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -15,7 +17,7 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Skinning.Legacy { - public class LegacyCatcher : CompositeDrawable, ICatcherPiece + public class LegacyCatcherNew : CompositeDrawable, ICatcherPiece { public Bindable CurrentState { get; } = new Bindable(); @@ -25,7 +27,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy private Drawable currentDrawable; - public LegacyCatcher() + public LegacyCatcherNew() { RelativeSizeAxes = Axes.Both; } @@ -35,27 +37,22 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { CurrentState.BindTo(currentState); - AddRangeInternal(new[] - { - drawables[CatcherAnimationState.Idle] = getDrawableFor(@"fruit-catcher-idle"), - drawables[CatcherAnimationState.Fail] = getDrawableFor(@"fruit-catcher-fail"), - drawables[CatcherAnimationState.Kiai] = getDrawableFor(@"fruit-catcher-kiai"), - }); - currentDrawable = drawables[CatcherAnimationState.Idle]; - - foreach (var d in drawables.Values) + foreach (var state in Enum.GetValues(typeof(CatcherAnimationState)).Cast()) { + var d = getDrawableFor(state); d.Anchor = Anchor.TopCentre; d.Origin = Anchor.TopCentre; d.RelativeSizeAxes = Axes.Both; d.Size = Vector2.One; d.FillMode = FillMode.Fit; d.Alpha = 0; + AddInternal(drawables[state] = d); } - Drawable getDrawableFor(string name) => - skin.GetAnimation(name, true, true, true) ?? - skin.GetAnimation(@"fruit-ryuuta", true, true, true) ?? + currentDrawable = drawables[CatcherAnimationState.Idle]; + + Drawable getDrawableFor(CatcherAnimationState state) => + skin.GetAnimation(@$"fruit-catcher-{state.ToString().ToLowerInvariant()}", true, true, true) ?? skin.GetAnimation(@"fruit-catcher-idle", true, true, true); } diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs new file mode 100644 index 0000000000..c426856c27 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.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.Animations; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Skinning.Legacy +{ + public class LegacyCatcherOld : CompositeDrawable, ICatcherPiece + { + public Texture CurrentTexture => (InternalChild as TextureAnimation)?.CurrentFrame ?? (InternalChild as Sprite)?.Texture; + + public LegacyCatcherOld() + { + RelativeSizeAxes = Axes.Both; + } + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + InternalChild = skin.GetAnimation(@"fruit-ryuuta", true, true, true).With(d => + { + d.Anchor = Anchor.TopCentre; + d.Origin = Anchor.TopCentre; + d.RelativeSizeAxes = Axes.Both; + d.Size = Vector2.One; + d.FillMode = FillMode.Fit; + }); + } + } +} From 194c78f67aab618f6fb48b1c4983279c192105b5 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Jun 2021 22:08:54 +0900 Subject: [PATCH 04/26] Make current state bindable protected --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 9daf6e23ab..a9be2cde1c 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Catch.UI private readonly Container droppedObjectTarget; [Cached] - public readonly Bindable CurrentStateBindable = new Bindable(); + protected readonly Bindable CurrentStateBindable = new Bindable(); public CatcherAnimationState CurrentState => CurrentStateBindable.Value; From 7df971a9709f98e061434d86c4fecb02a992a508 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Tue, 8 Jun 2021 22:10:13 +0900 Subject: [PATCH 05/26] `ICatcherPiece` -> `ICatcherSprite` --- osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs | 2 +- .../Skinning/{ICatcherPiece.cs => ICatcherSprite.cs} | 2 +- osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs | 2 +- osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs | 2 +- osu.Game.Rulesets.Catch/UI/Catcher.cs | 4 +--- 5 files changed, 5 insertions(+), 7 deletions(-) rename osu.Game.Rulesets.Catch/Skinning/{ICatcherPiece.cs => ICatcherSprite.cs} (88%) diff --git a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs index 655e557705..364fc211a0 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Default/DefaultCatcher.cs @@ -12,7 +12,7 @@ using osu.Game.Rulesets.Catch.UI; namespace osu.Game.Rulesets.Catch.Skinning.Default { - public class DefaultCatcher : CompositeDrawable, ICatcherPiece + public class DefaultCatcher : CompositeDrawable, ICatcherSprite { public Bindable CurrentState { get; } = new Bindable(); diff --git a/osu.Game.Rulesets.Catch/Skinning/ICatcherPiece.cs b/osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs similarity index 88% rename from osu.Game.Rulesets.Catch/Skinning/ICatcherPiece.cs rename to osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs index 0b51846eda..073868e947 100644 --- a/osu.Game.Rulesets.Catch/Skinning/ICatcherPiece.cs +++ b/osu.Game.Rulesets.Catch/Skinning/ICatcherSprite.cs @@ -5,7 +5,7 @@ using osu.Framework.Graphics.Textures; namespace osu.Game.Rulesets.Catch.Skinning { - public interface ICatcherPiece + public interface ICatcherSprite { Texture CurrentTexture { get; } } diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs index 4e662869c9..5987e9e393 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs @@ -17,7 +17,7 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Skinning.Legacy { - public class LegacyCatcherNew : CompositeDrawable, ICatcherPiece + public class LegacyCatcherNew : CompositeDrawable, ICatcherSprite { public Bindable CurrentState { get; } = new Bindable(); diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs index c426856c27..a8948d2ed0 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherOld.cs @@ -12,7 +12,7 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Skinning.Legacy { - public class LegacyCatcherOld : CompositeDrawable, ICatcherPiece + public class LegacyCatcherOld : CompositeDrawable, ICatcherSprite { public Texture CurrentTexture => (InternalChild as TextureAnimation)?.CurrentFrame ?? (InternalChild as Sprite)?.Texture; diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index a9be2cde1c..799743ae6e 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -89,7 +89,7 @@ namespace osu.Game.Rulesets.Catch.UI /// public const float ALLOWED_CATCH_RANGE = 0.8f; - internal Texture CurrentTexture => currentCatcherPiece.CurrentTexture; + internal Texture CurrentTexture => ((ICatcherSprite)currentCatcher.Drawable).CurrentTexture; private bool dashing; @@ -113,8 +113,6 @@ namespace osu.Game.Rulesets.Catch.UI private readonly SkinnableDrawable currentCatcher; - private ICatcherPiece currentCatcherPiece => (ICatcherPiece)currentCatcher.Drawable; - private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; private Color4 hyperDashEndGlowColour = DEFAULT_HYPER_DASH_COLOUR; From 88266eac63b54be330837c4b2c77bca522012aa1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Jun 2021 16:33:56 +0200 Subject: [PATCH 06/26] Add option to fix label width of a `LabelledDrawable` --- .../TestSceneLabelledDrawable.cs | 42 +++++++++++++++ .../UserInterfaceV2/LabelledDrawable.cs | 51 +++++++++++++++++-- 2 files changed, 90 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs index 8179f92ffc..fe312ccc8f 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneLabelledDrawable.cs @@ -2,11 +2,14 @@ // See the LICENCE file in the repository root for full licence text. using NUnit.Framework; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Testing; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterfaceV2; +using osuTK; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface @@ -21,6 +24,45 @@ namespace osu.Game.Tests.Visual.UserInterface [TestCase(true)] public void TestNonPadded(bool hasDescription) => createPaddedComponent(hasDescription, false); + [Test] + public void TestFixedWidth() + { + const float label_width = 200; + + AddStep("create components", () => Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, 10), + Children = new Drawable[] + { + new NonPaddedLabelledDrawable + { + Label = "short", + FixedLabelWidth = label_width + }, + new NonPaddedLabelledDrawable + { + Label = "very very very very very very very very very very very long", + FixedLabelWidth = label_width + }, + new PaddedLabelledDrawable + { + Label = "short", + FixedLabelWidth = label_width + }, + new PaddedLabelledDrawable + { + Label = "very very very very very very very very very very very long", + FixedLabelWidth = label_width + } + } + }); + + AddStep("unset label width", () => this.ChildrenOfType>().ForEach(d => d.FixedLabelWidth = null)); + AddStep("reset label width", () => this.ChildrenOfType>().ForEach(d => d.FixedLabelWidth = label_width)); + } + private void createPaddedComponent(bool hasDescription = false, bool padded = true) { AddStep("create component", () => diff --git a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs index ec68223a3d..5a697623c9 100644 --- a/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs +++ b/osu.Game/Graphics/UserInterfaceV2/LabelledDrawable.cs @@ -14,6 +14,27 @@ namespace osu.Game.Graphics.UserInterfaceV2 public abstract class LabelledDrawable : CompositeDrawable where T : Drawable { + private float? fixedLabelWidth; + + /// + /// The fixed width of the label of this . + /// If null, the label portion will auto-size to its content. + /// Can be used in layout scenarios where several labels must match in length for the components to be aligned properly. + /// + public float? FixedLabelWidth + { + get => fixedLabelWidth; + set + { + if (fixedLabelWidth == value) + return; + + fixedLabelWidth = value; + + updateLabelWidth(); + } + } + protected const float CONTENT_PADDING_VERTICAL = 10; protected const float CONTENT_PADDING_HORIZONTAL = 15; protected const float CORNER_RADIUS = 15; @@ -23,6 +44,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 /// protected readonly T Component; + private readonly GridContainer grid; private readonly OsuTextFlowContainer labelText; private readonly OsuTextFlowContainer descriptionText; @@ -56,7 +78,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 Spacing = new Vector2(0, 12), Children = new Drawable[] { - new GridContainer + grid = new GridContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -69,7 +91,13 @@ namespace osu.Game.Graphics.UserInterfaceV2 Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Right = 20 } + Padding = new MarginPadding + { + Right = 20, + // ensure that the label is always vertically padded even if the component itself isn't. + // this may become an issue if the label is taller than the component. + Vertical = padded ? 0 : CONTENT_PADDING_VERTICAL + } }, new Container { @@ -87,7 +115,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 }, }, RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, - ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) } }, descriptionText = new OsuTextFlowContainer(s => s.Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold, italics: true)) { @@ -99,6 +126,24 @@ namespace osu.Game.Graphics.UserInterfaceV2 } } }; + + updateLabelWidth(); + } + + private void updateLabelWidth() + { + if (fixedLabelWidth == null) + { + grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }; + labelText.RelativeSizeAxes = Axes.None; + labelText.AutoSizeAxes = Axes.Both; + } + else + { + grid.ColumnDimensions = new[] { new Dimension(GridSizeMode.Absolute, fixedLabelWidth.Value) }; + labelText.AutoSizeAxes = Axes.Y; + labelText.RelativeSizeAxes = Axes.X; + } } [BackgroundDependencyLoader] From 410cb16340fc7212070a456ff7412cfb9a2204af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 8 Jun 2021 17:14:26 +0200 Subject: [PATCH 07/26] Apply fixed label width to setup screen items --- osu.Game/Screens/Edit/Setup/ColoursSection.cs | 1 + osu.Game/Screens/Edit/Setup/DifficultySection.cs | 4 ++++ osu.Game/Screens/Edit/Setup/MetadataSection.cs | 4 ++++ osu.Game/Screens/Edit/Setup/ResourcesSection.cs | 2 ++ osu.Game/Screens/Edit/Setup/SetupSection.cs | 6 ++++++ 5 files changed, 17 insertions(+) diff --git a/osu.Game/Screens/Edit/Setup/ColoursSection.cs b/osu.Game/Screens/Edit/Setup/ColoursSection.cs index cb7deadcb7..4a81959a54 100644 --- a/osu.Game/Screens/Edit/Setup/ColoursSection.cs +++ b/osu.Game/Screens/Edit/Setup/ColoursSection.cs @@ -25,6 +25,7 @@ namespace osu.Game.Screens.Edit.Setup comboColours = new LabelledColourPalette { Label = "Hitcircle / Slider Combos", + FixedLabelWidth = LABEL_WIDTH, ColourNamePrefix = "Combo" } }; diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 493d3ed20c..a8800d524f 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -28,6 +28,7 @@ namespace osu.Game.Screens.Edit.Setup circleSizeSlider = new LabelledSliderBar { Label = "Object Size", + FixedLabelWidth = LABEL_WIDTH, Description = "The size of all hit objects", Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.CircleSize) { @@ -40,6 +41,7 @@ namespace osu.Game.Screens.Edit.Setup healthDrainSlider = new LabelledSliderBar { Label = "Health Drain", + FixedLabelWidth = LABEL_WIDTH, Description = "The rate of passive health drain throughout playable time", Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.DrainRate) { @@ -52,6 +54,7 @@ namespace osu.Game.Screens.Edit.Setup approachRateSlider = new LabelledSliderBar { Label = "Approach Rate", + FixedLabelWidth = LABEL_WIDTH, Description = "The speed at which objects are presented to the player", Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.ApproachRate) { @@ -64,6 +67,7 @@ namespace osu.Game.Screens.Edit.Setup overallDifficultySlider = new LabelledSliderBar { Label = "Overall Difficulty", + FixedLabelWidth = LABEL_WIDTH, Description = "The harshness of hit windows and difficulty of special objects (ie. spinners)", Current = new BindableFloat(Beatmap.BeatmapInfo.BaseDifficulty.OverallDifficulty) { diff --git a/osu.Game/Screens/Edit/Setup/MetadataSection.cs b/osu.Game/Screens/Edit/Setup/MetadataSection.cs index 889a5eab5e..c79888b9d1 100644 --- a/osu.Game/Screens/Edit/Setup/MetadataSection.cs +++ b/osu.Game/Screens/Edit/Setup/MetadataSection.cs @@ -27,24 +27,28 @@ namespace osu.Game.Screens.Edit.Setup artistTextBox = new LabelledTextBox { Label = "Artist", + FixedLabelWidth = LABEL_WIDTH, Current = { Value = Beatmap.Metadata.Artist }, TabbableContentContainer = this }, titleTextBox = new LabelledTextBox { Label = "Title", + FixedLabelWidth = LABEL_WIDTH, Current = { Value = Beatmap.Metadata.Title }, TabbableContentContainer = this }, creatorTextBox = new LabelledTextBox { Label = "Creator", + FixedLabelWidth = LABEL_WIDTH, Current = { Value = Beatmap.Metadata.AuthorString }, TabbableContentContainer = this }, difficultyTextBox = new LabelledTextBox { Label = "Difficulty Name", + FixedLabelWidth = LABEL_WIDTH, Current = { Value = Beatmap.BeatmapInfo.Version }, TabbableContentContainer = this }, diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index 12270f2aa4..ba22c82ecc 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -54,6 +54,7 @@ namespace osu.Game.Screens.Edit.Setup backgroundTextBox = new FileChooserLabelledTextBox(".jpg", ".jpeg", ".png") { Label = "Background", + FixedLabelWidth = LABEL_WIDTH, PlaceholderText = "Click to select a background image", Current = { Value = working.Value.Metadata.BackgroundFile }, Target = backgroundFileChooserContainer, @@ -72,6 +73,7 @@ namespace osu.Game.Screens.Edit.Setup audioTrackTextBox = new FileChooserLabelledTextBox(".mp3", ".ogg") { Label = "Audio Track", + FixedLabelWidth = LABEL_WIDTH, PlaceholderText = "Click to select a track", Current = { Value = working.Value.Metadata.AudioFile }, Target = audioTrackFileChooserContainer, diff --git a/osu.Game/Screens/Edit/Setup/SetupSection.cs b/osu.Game/Screens/Edit/Setup/SetupSection.cs index 8964e651df..560e6fff67 100644 --- a/osu.Game/Screens/Edit/Setup/SetupSection.cs +++ b/osu.Game/Screens/Edit/Setup/SetupSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterfaceV2; using osuTK; namespace osu.Game.Screens.Edit.Setup @@ -15,6 +16,11 @@ namespace osu.Game.Screens.Edit.Setup { private readonly FillFlowContainer flow; + /// + /// Used to align some of the child s together to achieve a grid-like look. + /// + protected const float LABEL_WIDTH = 160; + [Resolved] protected OsuColour Colours { get; private set; } From 249a8f259bb0ef55777d92531dfd5f32f3903317 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 13:44:26 +0900 Subject: [PATCH 08/26] Reword "unranked" to "not ranked" on beatmap overlay This will be replaced anyway once we start to consume osu-web translation strings. --- osu.Game/Overlays/BeatmapSet/Info.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs index bac658b76e..dbe01ad27f 100644 --- a/osu.Game/Overlays/BeatmapSet/Info.cs +++ b/osu.Game/Overlays/BeatmapSet/Info.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.BeatmapSet public Info() { MetadataSection source, tags, genre, language; - OsuSpriteText unrankedPlaceholder; + OsuSpriteText notRankedPlaceholder; RelativeSizeAxes = Axes.X; Height = base_height; @@ -102,12 +102,12 @@ namespace osu.Game.Overlays.BeatmapSet RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Top = 20, Horizontal = 15 }, }, - unrankedPlaceholder = new OsuSpriteText + notRankedPlaceholder = new OsuSpriteText { Anchor = Anchor.Centre, Origin = Anchor.Centre, Alpha = 0, - Text = "Unranked beatmap", + Text = "This beatmap is not ranked", Font = OsuFont.GetFont(size: 12) }, }, @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.BeatmapSet language.Text = b.NewValue?.OnlineInfo?.Language?.Name ?? string.Empty; var setHasLeaderboard = b.NewValue?.OnlineInfo?.Status > 0; successRate.Alpha = setHasLeaderboard ? 1 : 0; - unrankedPlaceholder.Alpha = setHasLeaderboard ? 0 : 1; + notRankedPlaceholder.Alpha = setHasLeaderboard ? 0 : 1; Height = setHasLeaderboard ? 270 : base_height; }; } From 7774344f0e40390c9c8c521fd1b721885d7ceb6e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 13:45:09 +0900 Subject: [PATCH 09/26] Remove "Unranked" text from `ModDisplay` --- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 1 - .../OnlinePlay/FooterButtonFreeMods.cs | 1 - .../Multiplayer/MultiplayerMatchSubScreen.cs | 1 - .../Participants/ParticipantPanel.cs | 1 - .../Playlists/PlaylistsRoomSubScreen.cs | 1 - osu.Game/Screens/Play/HUD/ModDisplay.cs | 22 ++----------------- .../ContractedPanelMiddleContent.cs | 1 - .../Expanded/ExpandedPanelMiddleContent.cs | 1 - osu.Game/Screens/Select/FooterButtonMods.cs | 1 - 9 files changed, 2 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 38a9ace619..a3a61ccc36 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -202,7 +202,6 @@ namespace osu.Game.Screens.OnlinePlay Child = modDisplay = new ModDisplay { Scale = new Vector2(0.4f), - DisplayUnrankedText = false, ExpansionMode = ExpansionMode.AlwaysExpanded } } diff --git a/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs index a3cc383b67..834e82fcfd 100644 --- a/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs +++ b/osu.Game/Screens/OnlinePlay/FooterButtonFreeMods.cs @@ -31,7 +31,6 @@ namespace osu.Game.Screens.OnlinePlay { Anchor = Anchor.Centre, Origin = Anchor.Centre, - DisplayUnrankedText = false, Scale = new Vector2(0.8f), ExpansionMode = ExpansionMode.AlwaysContracted, }); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 62ef70ed68..f9b3549f3c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -185,7 +185,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - DisplayUnrankedText = false, Current = UserMods, Scale = new Vector2(0.8f), }, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 5bef934e6a..f4a334e9d3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -139,7 +139,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants { Scale = new Vector2(0.5f), ExpansionMode = ExpansionMode.AlwaysContracted, - DisplayUnrankedText = false, } }, userStateDisplay = new StateDisplay diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs index 26ee21a2c3..092394446b 100644 --- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs @@ -175,7 +175,6 @@ namespace osu.Game.Screens.OnlinePlay.Playlists { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - DisplayUnrankedText = false, Current = UserMods, Scale = new Vector2(0.8f), }, diff --git a/osu.Game/Screens/Play/HUD/ModDisplay.cs b/osu.Game/Screens/Play/HUD/ModDisplay.cs index cffdb21fb8..2f7ca74372 100644 --- a/osu.Game/Screens/Play/HUD/ModDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ModDisplay.cs @@ -3,18 +3,15 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; -using osu.Game.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.UI; using osuTK; -using osu.Game.Graphics.Containers; -using osu.Framework.Input.Events; -using osu.Game.Graphics; namespace osu.Game.Screens.Play.HUD { @@ -22,8 +19,6 @@ namespace osu.Game.Screens.Play.HUD { private const int fade_duration = 1000; - public bool DisplayUnrankedText = true; - public ExpansionMode ExpansionMode = ExpansionMode.ExpandOnHover; private readonly Bindable> current = new Bindable>(); @@ -42,7 +37,6 @@ namespace osu.Game.Screens.Play.HUD } private readonly FillFlowContainer iconsContainer; - private readonly OsuSpriteText unrankedText; public ModDisplay() { @@ -63,13 +57,6 @@ namespace osu.Game.Screens.Play.HUD AutoSizeAxes = Axes.Both, Direction = FillDirection.Horizontal, }, - unrankedText = new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Text = @"/ UNRANKED /", - Font = OsuFont.Numeric.With(size: 12) - } }, }; } @@ -102,11 +89,6 @@ namespace osu.Game.Screens.Play.HUD private void appearTransform() { - if (DisplayUnrankedText && Current.Value.Any(m => !m.Ranked)) - unrankedText.FadeInFromZero(fade_duration, Easing.OutQuint); - else - unrankedText.Hide(); - expand(); using (iconsContainer.BeginDelayedSequence(1200)) diff --git a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs index 24f1116d0e..7e8dcdcfe0 100644 --- a/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Contracted/ContractedPanelMiddleContent.cs @@ -137,7 +137,6 @@ namespace osu.Game.Screens.Ranking.Contracted Origin = Anchor.TopCentre, AutoSizeAxes = Axes.Both, ExpansionMode = ExpansionMode.AlwaysExpanded, - DisplayUnrankedText = false, Current = { Value = score.Mods }, Scale = new Vector2(0.5f), } diff --git a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs index 4895240314..f65aed3037 100644 --- a/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs +++ b/osu.Game/Screens/Ranking/Expanded/ExpandedPanelMiddleContent.cs @@ -242,7 +242,6 @@ namespace osu.Game.Screens.Ranking.Expanded { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - DisplayUnrankedText = false, ExpansionMode = ExpansionMode.AlwaysExpanded, Scale = new Vector2(0.5f), Current = { Value = score.Mods } diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs index b98b48a0c0..5bbca5ca1a 100644 --- a/osu.Game/Screens/Select/FooterButtonMods.cs +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -37,7 +37,6 @@ namespace osu.Game.Screens.Select { Anchor = Anchor.Centre, Origin = Anchor.Centre, - DisplayUnrankedText = false, Scale = new Vector2(0.8f), ExpansionMode = ExpansionMode.AlwaysContracted, }); From d0e9f8ef90b93ab1116f7a6fa91571defec2090e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 14:17:01 +0900 Subject: [PATCH 10/26] Replace and obsolete `Ranked` flag with `IsUserPlayable` --- .../Difficulty/CatchPerformanceCalculator.cs | 2 +- osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs | 1 - .../Difficulty/ManiaPerformanceCalculator.cs | 2 +- osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs | 1 - osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs | 2 -- osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs | 1 - .../Difficulty/OsuPerformanceCalculator.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs | 2 -- osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs | 1 - osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs | 1 - .../Difficulty/TaikoPerformanceCalculator.cs | 2 +- osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs | 1 - osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 8 ++++++-- osu.Game/Rulesets/Mods/ModAutoplay.cs | 2 ++ osu.Game/Rulesets/Mods/ModClassic.cs | 2 -- osu.Game/Rulesets/Mods/ModDoubleTime.cs | 1 - osu.Game/Rulesets/Mods/ModEasy.cs | 1 - osu.Game/Rulesets/Mods/ModFlashlight.cs | 1 - osu.Game/Rulesets/Mods/ModHalfTime.cs | 1 - osu.Game/Rulesets/Mods/ModHidden.cs | 1 - osu.Game/Rulesets/Mods/ModNoFail.cs | 1 - osu.Game/Rulesets/Mods/ModPerfect.cs | 1 - osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 1 - 26 files changed, 13 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 6a3a16ed33..165dfb85e9 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty misses = Score.Statistics.GetOrDefault(HitResult.Miss); // Don't count scores made with supposedly unranked mods - if (mods.Any(m => !m.Ranked)) + if (mods.Any(m => !m.UserPlayable)) return 0; // We are heavily relying on aim in catch the beat diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs index ced1900ba9..0dde6aa06e 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs @@ -10,7 +10,6 @@ namespace osu.Game.Rulesets.Catch.Mods public class CatchModHardRock : ModHardRock, IApplicableToBeatmap { public override double ScoreMultiplier => 1.12; - public override bool Ranked => true; public void ApplyToBeatmap(IBeatmap beatmap) => CatchBeatmapProcessor.ApplyPositionOffsets(beatmap, this); } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index 00bec18a45..d630f6b4a8 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); - if (mods.Any(m => !m.Ranked)) + if (mods.Any(m => !m.UserPlayable)) return 0; IEnumerable scoreIncreaseMods = Ruleset.GetModsFor(ModType.DifficultyIncrease); diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs b/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs index 8fd5950dfb..050b302bd8 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaKeyMod.cs @@ -15,7 +15,6 @@ namespace osu.Game.Rulesets.Mania.Mods public abstract int KeyCount { get; } public override ModType Type => ModType.Conversion; public override double ScoreMultiplier => 1; // TODO: Implement the mania key mod score multiplier - public override bool Ranked => true; public void ApplyToBeatmapConverter(IBeatmapConverter beatmapConverter) { diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs index 078394b1d8..614ef76a3b 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModConstantSpeed.cs @@ -24,8 +24,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override ModType Type => ModType.Conversion; - public override bool Ranked => false; - public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) { var maniaRuleset = (DrawableManiaRuleset)drawableRuleset; diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs index 12f379bddb..cf404cc98e 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModMirror.cs @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Mania.Mods public override ModType Type => ModType.Conversion; public override string Description => "Notes are flipped horizontally."; public override double ScoreMultiplier => 1; - public override bool Ranked => true; public void ApplyToBeatmap(IBeatmap beatmap) { diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 44a9dd2f1f..71908e6693 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -42,7 +42,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); // Don't count scores made with supposedly unranked mods - if (mods.Any(m => !m.Ranked)) + if (mods.Any(m => !m.UserPlayable)) return 0; // Custom multipliers for NoFail and SpunOut. diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs index 6841ecd23c..ebf6f9dda7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModBlinds.cs @@ -27,8 +27,6 @@ namespace osu.Game.Rulesets.Osu.Mods public override IconUsage? Icon => FontAwesome.Solid.Adjust; public override ModType Type => ModType.DifficultyIncrease; - public override bool Ranked => false; - public override double ScoreMultiplier => 1.12; private DrawableOsuBlinds blinds; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs index e0577dd464..16c166257a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHardRock.cs @@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModHardRock : ModHardRock, IApplicableToHitObject { public override double ScoreMultiplier => 1.06; - public override bool Ranked => true; public void ApplyToHitObject(HitObject hitObject) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs index c282a919ea..97e3d82664 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRandom.cs @@ -22,7 +22,6 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModRandom : ModRandom, IApplicableToBeatmap { public override string Description => "It never gets boring!"; - public override bool Ranked => false; // The relative distance to the edge of the playfield before objects' positions should start to "turn around" and curve towards the middle. // The closer the hit objects draw to the border, the sharper the turn diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs index f080e11933..b12d735474 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpunOut.cs @@ -21,7 +21,6 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Automation; public override string Description => @"Spinners will be automatically completed."; public override double ScoreMultiplier => 0.9; - public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(OsuModAutopilot) }; public void ApplyToDrawableHitObjects(IEnumerable drawables) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs index 3b16e9d2b7..789b06ddbe 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs @@ -14,6 +14,5 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.System; - public override bool Ranked => true; } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 2d9b95ae88..5d82bd1f09 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); // Don't count scores made with supposedly unranked mods - if (mods.Any(m => !m.Ranked)) + if (mods.Any(m => !m.UserPlayable)) return 0; // Custom multipliers for NoFail and SpunOut. diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs index a5a8b75f80..8437dfe52e 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModHardRock.cs @@ -9,7 +9,6 @@ namespace osu.Game.Rulesets.Taiko.Mods public class TaikoModHardRock : ModHardRock { public override double ScoreMultiplier => 1.06; - public override bool Ranked => true; /// /// Multiplier factor added to the scrolling speed. diff --git a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs index 60fd520681..98662e5dea 100644 --- a/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs +++ b/osu.Game/Overlays/BeatmapSet/LeaderboardModSelector.cs @@ -52,7 +52,7 @@ namespace osu.Game.Overlays.BeatmapSet return; modsContainer.Add(new ModButton(new ModNoMod())); - modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.Ranked).Select(m => new ModButton(m))); + modsContainer.AddRange(ruleset.NewValue.CreateInstance().GetAllMods().Where(m => m.UserPlayable).Select(m => new ModButton(m))); modsContainer.ForEach(button => button.OnSelectionChanged = selectionChanged); } diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 7f48888abe..79d16013e3 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -108,10 +108,14 @@ namespace osu.Game.Rulesets.Mods public virtual bool HasImplementation => this is IApplicableMod; /// - /// Returns if this mod is ranked. + /// Whether this mod is playable by an end user. + /// Should be false for cases where the user is not interacting with the game (so it can be excluded from mutliplayer selection, for example). /// [JsonIgnore] - public virtual bool Ranked => false; + public virtual bool UserPlayable => true; + + [Obsolete("Going forward, the concept of \"ranked\" doesn't exist. The only exceptions are automation mods, which should now override and set UserPlayable to true.")] // Can be removed 20211009 + public virtual bool IsRanked => false; /// /// Whether this mod requires configuration to apply changes to the game. diff --git a/osu.Game/Rulesets/Mods/ModAutoplay.cs b/osu.Game/Rulesets/Mods/ModAutoplay.cs index b84b5671e1..4849d6ea36 100644 --- a/osu.Game/Rulesets/Mods/ModAutoplay.cs +++ b/osu.Game/Rulesets/Mods/ModAutoplay.cs @@ -24,6 +24,8 @@ namespace osu.Game.Rulesets.Mods public bool RestartOnFail => false; + public override bool UserPlayable => false; + public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModNoFail) }; public override bool HasImplementation => GetType().GenericTypeArguments.Length == 0; diff --git a/osu.Game/Rulesets/Mods/ModClassic.cs b/osu.Game/Rulesets/Mods/ModClassic.cs index f1207ec188..1159955e11 100644 --- a/osu.Game/Rulesets/Mods/ModClassic.cs +++ b/osu.Game/Rulesets/Mods/ModClassic.cs @@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Mods public override string Description => "Feeling nostalgic?"; - public override bool Ranked => false; - public override ModType Type => ModType.Conversion; } } diff --git a/osu.Game/Rulesets/Mods/ModDoubleTime.cs b/osu.Game/Rulesets/Mods/ModDoubleTime.cs index 152657da33..d12f48e973 100644 --- a/osu.Game/Rulesets/Mods/ModDoubleTime.cs +++ b/osu.Game/Rulesets/Mods/ModDoubleTime.cs @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => OsuIcon.ModDoubletime; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Zoooooooooom..."; - public override bool Ranked => true; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModHalfTime)).ToArray(); diff --git a/osu.Game/Rulesets/Mods/ModEasy.cs b/osu.Game/Rulesets/Mods/ModEasy.cs index 1290e8136c..0f51e2a6d5 100644 --- a/osu.Game/Rulesets/Mods/ModEasy.cs +++ b/osu.Game/Rulesets/Mods/ModEasy.cs @@ -15,7 +15,6 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => OsuIcon.ModEasy; public override ModType Type => ModType.DifficultyReduction; public override double ScoreMultiplier => 0.5; - public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModHardRock), typeof(ModDifficultyAdjust) }; public virtual void ReadFromDifficulty(BeatmapDifficulty difficulty) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 08f2ccb75c..7abae71273 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -31,7 +31,6 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => OsuIcon.ModFlashlight; public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Restricted view area."; - public override bool Ranked => true; internal ModFlashlight() { diff --git a/osu.Game/Rulesets/Mods/ModHalfTime.cs b/osu.Game/Rulesets/Mods/ModHalfTime.cs index 203b88951c..c240cdbe6e 100644 --- a/osu.Game/Rulesets/Mods/ModHalfTime.cs +++ b/osu.Game/Rulesets/Mods/ModHalfTime.cs @@ -17,7 +17,6 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => OsuIcon.ModHalftime; public override ModType Type => ModType.DifficultyReduction; public override string Description => "Less zoom..."; - public override bool Ranked => true; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModDoubleTime)).ToArray(); diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 238612b3d2..5a8226115f 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -14,7 +14,6 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "HD"; public override IconUsage? Icon => OsuIcon.ModHidden; public override ModType Type => ModType.DifficultyIncrease; - public override bool Ranked => true; public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index c0f24e116a..abf67c2e2d 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -15,7 +15,6 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyReduction; public override string Description => "You can't fail, no matter what."; public override double ScoreMultiplier => 0.5; - public override bool Ranked => true; public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition), typeof(ModAutoplay) }; } } diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index d0b09b50f2..187a4d8e23 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -16,7 +16,6 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "PF"; public override IconUsage? Icon => OsuIcon.ModPerfect; public override ModType Type => ModType.DifficultyIncrease; - public override bool Ranked => true; public override double ScoreMultiplier => 1; public override string Description => "SS or quit."; diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 617ae38feb..1abd353d20 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -18,7 +18,6 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyIncrease; public override string Description => "Miss and fail."; public override double ScoreMultiplier => 1; - public override bool Ranked => true; public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModPerfect)).ToArray(); From b754c5239253341758b166c3872d983e7e115290 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 14:32:48 +0900 Subject: [PATCH 11/26] Update `ModAutoplay` matching to use new `UserPlayable` flag instead --- osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs | 2 +- osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs | 2 +- osu.Game/Screens/Play/SubmittingPlayer.cs | 4 ++-- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs index 66262e7dc4..5e2e9fd087 100644 --- a/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs +++ b/osu.Game/Screens/OnlinePlay/FreeModSelectOverlay.cs @@ -25,7 +25,7 @@ namespace osu.Game.Screens.OnlinePlay public new Func IsValidMod { get => base.IsValidMod; - set => base.IsValidMod = m => m.HasImplementation && !(m is ModAutoplay) && value(m); + set => base.IsValidMod = m => m.HasImplementation && m.UserPlayable && value(m); } public FreeModSelectOverlay() diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs index 3e7e557aad..2c46f76737 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySongSelect.cs @@ -161,7 +161,7 @@ namespace osu.Game.Screens.OnlinePlay /// /// The to check. /// Whether is a valid mod for online play. - protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && !ModUtils.FlattenMod(mod).Any(m => m is ModAutoplay); + protected virtual bool IsValidMod(Mod mod) => mod.HasImplementation && ModUtils.FlattenMod(mod).All(m => m.UserPlayable); /// /// Checks whether a given is valid for per-player free-mod selection. diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 23b9037244..ea53d03fcb 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -44,9 +44,9 @@ namespace osu.Game.Screens.Play // Token request construction should happen post-load to allow derived classes to potentially prepare DI backings that are used to create the request. var tcs = new TaskCompletionSource(); - if (Mods.Value.Any(m => m is ModAutoplay)) + if (Mods.Value.Any(m => !m.UserPlayable)) { - handleTokenFailure(new InvalidOperationException("Autoplay loaded.")); + handleTokenFailure(new InvalidOperationException("Non-user playable mod selected.")); return false; } diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index a0ea27b640..3ea764ed58 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -153,7 +153,7 @@ namespace osu.Game.Screens.Ranking if (Score != null) { // only show flair / animation when arriving after watching a play that isn't autoplay. - bool shouldFlair = player != null && !Score.Mods.Any(m => m is ModAutoplay); + bool shouldFlair = player != null && Score.Mods.All(m => m.UserPlayable); ScorePanelList.AddScore(Score, shouldFlair); From 85abee5fc703914621aab0d25611cff9c3f95446 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 14:33:34 +0900 Subject: [PATCH 12/26] Remove difficulty calculator exceptions I don't think there's any reason difficulty calculators shouldn't be able to calculate for autoplays. --- .../Difficulty/CatchPerformanceCalculator.cs | 4 ---- .../Difficulty/ManiaPerformanceCalculator.cs | 3 --- osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs | 4 ---- .../Difficulty/TaikoPerformanceCalculator.cs | 4 ---- 4 files changed, 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs index 165dfb85e9..fdd6ac0857 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchPerformanceCalculator.cs @@ -39,10 +39,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty tinyTicksMissed = Score.Statistics.GetOrDefault(HitResult.SmallTickMiss); misses = Score.Statistics.GetOrDefault(HitResult.Miss); - // Don't count scores made with supposedly unranked mods - if (mods.Any(m => !m.UserPlayable)) - return 0; - // We are heavily relying on aim in catch the beat double value = Math.Pow(5.0 * Math.Max(1.0, Attributes.StarRating / 0.0049) - 4.0, 2.0) / 100000.0; diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs index d630f6b4a8..405ac56e94 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaPerformanceCalculator.cs @@ -44,9 +44,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); - if (mods.Any(m => !m.UserPlayable)) - return 0; - IEnumerable scoreIncreaseMods = Ruleset.GetModsFor(ModType.DifficultyIncrease); double scoreMultiplier = 1.0; diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs index 71908e6693..749d7d1b41 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuPerformanceCalculator.cs @@ -41,10 +41,6 @@ namespace osu.Game.Rulesets.Osu.Difficulty countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); - // Don't count scores made with supposedly unranked mods - if (mods.Any(m => !m.UserPlayable)) - return 0; - // Custom multipliers for NoFail and SpunOut. double multiplier = 1.12; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs index 5d82bd1f09..6117ed1673 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoPerformanceCalculator.cs @@ -36,10 +36,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty countMeh = Score.Statistics.GetOrDefault(HitResult.Meh); countMiss = Score.Statistics.GetOrDefault(HitResult.Miss); - // Don't count scores made with supposedly unranked mods - if (mods.Any(m => !m.UserPlayable)) - return 0; - // Custom multipliers for NoFail and SpunOut. double multiplier = 1.1; // This is being adjusted to keep the final pp value scaled around what it used to be when changing things From e5deecf459b279ef3e16137a5d4984793e0c08fe Mon Sep 17 00:00:00 2001 From: ekrctb Date: Wed, 9 Jun 2021 15:47:23 +0900 Subject: [PATCH 13/26] Check skin version for legacy catcher sprite --- .../Skinning/Legacy/CatchLegacySkinTransformer.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs index 6a9a3c43a2..b23011f1a3 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/CatchLegacySkinTransformer.cs @@ -66,10 +66,14 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy return null; case CatchSkinComponents.Catcher: - // New elements will be ignored when the old element exists. - if (GetTexture(@"fruit-ryuuta") != null || - GetTexture(@"fruit-ryuuta-0") != null) - return new LegacyCatcherOld(); + var version = Source.GetConfig(LegacySkinConfiguration.LegacySetting.Version)?.Value ?? 1; + + if (version < 2.3m) + { + if (GetTexture(@"fruit-ryuuta") != null || + GetTexture(@"fruit-ryuuta-0") != null) + return new LegacyCatcherOld(); + } if (GetTexture(@"fruit-catcher-idle") != null || GetTexture(@"fruit-catcher-idle-0") != null) From 38fc9347bec98496ff03a8b6f72fb0f5d18c17d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 16:13:00 +0900 Subject: [PATCH 14/26] Add failing test coverage for beatmap skin disable --- .../TestSceneBeatmapSkinLookupDisables.cs | 116 ++++++++++++++++++ 1 file changed, 116 insertions(+) create mode 100644 osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs new file mode 100644 index 0000000000..4f3924602f --- /dev/null +++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs @@ -0,0 +1,116 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Audio.Sample; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.OpenGL.Textures; +using osu.Framework.Graphics.Textures; +using osu.Framework.Testing; +using osu.Game.Audio; +using osu.Game.Configuration; +using osu.Game.Rulesets.Osu; +using osu.Game.Skinning; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Visual; + +namespace osu.Game.Tests.Skins +{ + [TestFixture] + [HeadlessTest] + public class TestSceneBeatmapSkinLookupDisables : OsuTestScene + { + private UserSkinSource userSource; + private BeatmapSkinSource beatmapSource; + private SkinRequester requester; + + [Resolved] + private OsuConfigManager config { get; set; } + + [SetUp] + public void SetUp() => Schedule(() => + { + Add(new SkinProvidingContainer(userSource = new UserSkinSource()) + .WithChild(new BeatmapSkinProvidingContainer(beatmapSource = new BeatmapSkinSource()) + .WithChild(requester = new SkinRequester()))); + }); + + [TestCase(false)] + [TestCase(true)] + public void TestDrawableLookup(bool allowBeatmapLookups) + { + AddStep($"Set beatmap skin enabled to {allowBeatmapLookups}", () => config.SetValue(OsuSetting.BeatmapSkins, allowBeatmapLookups)); + + string expected = allowBeatmapLookups ? "beatmap" : "user"; + + AddAssert($"Check lookup is from {expected}", () => requester.GetDrawableComponent(new TestSkinComponent())?.Name == expected); + } + + [TestCase(false)] + [TestCase(true)] + public void TestProviderLookup(bool allowBeatmapLookups) + { + AddStep($"Set beatmap skin enabled to {allowBeatmapLookups}", () => config.SetValue(OsuSetting.BeatmapSkins, allowBeatmapLookups)); + + ISkin expected() => allowBeatmapLookups ? (ISkin)beatmapSource : userSource; + + AddAssert($"Check lookup is from correct source", () => requester.FindProvider(s => s.GetDrawableComponent(new TestSkinComponent()) != null) == expected()); + } + + public class UserSkinSource : LegacySkin + { + public UserSkinSource() + : base(new SkinInfo(), null, null, string.Empty) + { + } + + public override Drawable GetDrawableComponent(ISkinComponent component) + { + return new Container { Name = "user" }; + } + } + + public class BeatmapSkinSource : LegacyBeatmapSkin + { + public BeatmapSkinSource() + : base(new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, null, null) + { + } + + public override Drawable GetDrawableComponent(ISkinComponent component) + { + return new Container { Name = "beatmap" }; + } + } + + public class SkinRequester : Drawable, ISkin + { + private ISkinSource skin; + + [BackgroundDependencyLoader] + private void load(ISkinSource skin) + { + this.skin = skin; + } + + public Drawable GetDrawableComponent(ISkinComponent component) => skin.GetDrawableComponent(component); + + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) => skin.GetTexture(componentName, wrapModeS, wrapModeT); + + public ISample GetSample(ISampleInfo sampleInfo) => skin.GetSample(sampleInfo); + + public IBindable GetConfig(TLookup lookup) => skin.GetConfig(lookup); + + public ISkin FindProvider(Func lookupFunction) => skin.FindProvider(lookupFunction); + } + + private class TestSkinComponent : ISkinComponent + { + public string LookupName => string.Empty; + } + } +} From 448e4e7ee53fea46c6bf28a77d3ae13db07dcef5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 15:25:42 +0900 Subject: [PATCH 15/26] Fix `FindProvider` calls on `SkinProvidingContainer` not considering disable flags Closes #13394. --- osu.Game/Skinning/SkinProvidingContainer.cs | 71 +++++++++++++++++++-- 1 file changed, 67 insertions(+), 4 deletions(-) diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 0e16cf43ee..5e91a280e7 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -27,6 +27,8 @@ namespace osu.Game.Skinning [CanBeNull] private ISkinSource fallbackSource; + private readonly NoFallbackProxy noFallbackLookupProxy; + protected virtual bool AllowDrawableLookup(ISkinComponent component) => true; protected virtual bool AllowTextureLookup(string componentName) => true; @@ -42,6 +44,8 @@ namespace osu.Game.Skinning this.skin = skin; RelativeSizeAxes = Axes.Both; + + noFallbackLookupProxy = new NoFallbackProxy(this); } public ISkin FindProvider(Func lookupFunction) @@ -53,7 +57,8 @@ namespace osu.Game.Skinning } else if (skin != null) { - if (lookupFunction(skin)) + // a proxy must be used here to correctly pass through the "Allow" checks without implicitly falling back to the fallbackSource. + if (lookupFunction(noFallbackLookupProxy)) return skin; } @@ -61,46 +66,70 @@ namespace osu.Game.Skinning } public Drawable GetDrawableComponent(ISkinComponent component) + => GetDrawableComponent(component, true); + + public Drawable GetDrawableComponent(ISkinComponent component, bool fallback) { Drawable sourceDrawable; if (AllowDrawableLookup(component) && (sourceDrawable = skin?.GetDrawableComponent(component)) != null) return sourceDrawable; + if (!fallback) + return null; + return fallbackSource?.GetDrawableComponent(component); } public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) + => GetTexture(componentName, wrapModeS, wrapModeT, true); + + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT, bool fallback) { Texture sourceTexture; if (AllowTextureLookup(componentName) && (sourceTexture = skin?.GetTexture(componentName, wrapModeS, wrapModeT)) != null) return sourceTexture; + if (!fallback) + return null; + return fallbackSource?.GetTexture(componentName, wrapModeS, wrapModeT); } public ISample GetSample(ISampleInfo sampleInfo) + => GetSample(sampleInfo, true); + + public ISample GetSample(ISampleInfo sampleInfo, bool fallback) { ISample sourceChannel; if (AllowSampleLookup(sampleInfo) && (sourceChannel = skin?.GetSample(sampleInfo)) != null) return sourceChannel; + if (!fallback) + return null; + return fallbackSource?.GetSample(sampleInfo); } public IBindable GetConfig(TLookup lookup) + => GetConfig(lookup, true); + + public IBindable GetConfig(TLookup lookup, bool fallback) { if (skin != null) { if (lookup is GlobalSkinColours || lookup is SkinCustomColourLookup) - return lookupWithFallback(lookup, AllowColourLookup); + return lookupWithFallback(lookup, AllowColourLookup, fallback); - return lookupWithFallback(lookup, AllowConfigurationLookup); + return lookupWithFallback(lookup, AllowConfigurationLookup, fallback); } + if (!fallback) + return null; + return fallbackSource?.GetConfig(lookup); } - private IBindable lookupWithFallback(TLookup lookup, bool canUseSkinLookup) + private IBindable lookupWithFallback(TLookup lookup, bool canUseSkinLookup, bool canUseFallback) { if (canUseSkinLookup) { @@ -109,6 +138,9 @@ namespace osu.Game.Skinning return bindable; } + if (!canUseFallback) + return null; + return fallbackSource?.GetConfig(lookup); } @@ -137,5 +169,36 @@ namespace osu.Game.Skinning if (fallbackSource != null) fallbackSource.SourceChanged -= TriggerSourceChanged; } + + private class NoFallbackProxy : ISkinSource + { + private readonly SkinProvidingContainer provider; + + public NoFallbackProxy(SkinProvidingContainer provider) + { + this.provider = provider; + } + + public Drawable GetDrawableComponent(ISkinComponent component) + => provider.GetDrawableComponent(component, false); + + public Texture GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) + => provider.GetTexture(componentName, wrapModeS, wrapModeT, false); + + public ISample GetSample(ISampleInfo sampleInfo) + => provider.GetSample(sampleInfo, false); + + public IBindable GetConfig(TLookup lookup) + => provider.GetConfig(lookup, false); + + public event Action SourceChanged + { + add => provider.SourceChanged += value; + remove => provider.SourceChanged -= value; + } + + public ISkin FindProvider(Func lookupFunction) => + provider.FindProvider(lookupFunction); + } } } From 020c63017ece8c1d804fff9268b8d9ef41575b1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 9 Jun 2021 09:21:02 +0200 Subject: [PATCH 16/26] Fix inspectcode issues --- osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs | 1 - osu.Game/Screens/Play/SubmittingPlayer.cs | 1 - osu.Game/Screens/Ranking/ResultsScreen.cs | 1 - 3 files changed, 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs index 789b06ddbe..7276cc753c 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTouchDevice.cs @@ -13,6 +13,5 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; public override ModType Type => ModType.System; - } } diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index ea53d03fcb..b843915a7c 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -10,7 +10,6 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Online.API; using osu.Game.Online.Rooms; -using osu.Game.Rulesets.Mods; using osu.Game.Scoring; namespace osu.Game.Screens.Play diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 3ea764ed58..3f9a74d5d0 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -17,7 +17,6 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Online.API; -using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking.Expanded.Accuracy; From 47eeab34e182def8eac2dd5b14bf3ea1060e2184 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 17:34:47 +0900 Subject: [PATCH 17/26] Remove redundant string interpolation --- osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs b/osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs index 4f3924602f..71544e94f3 100644 --- a/osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs +++ b/osu.Game.Tests/Skins/TestSceneBeatmapSkinLookupDisables.cs @@ -58,7 +58,7 @@ namespace osu.Game.Tests.Skins ISkin expected() => allowBeatmapLookups ? (ISkin)beatmapSource : userSource; - AddAssert($"Check lookup is from correct source", () => requester.FindProvider(s => s.GetDrawableComponent(new TestSkinComponent()) != null) == expected()); + AddAssert("Check lookup is from correct source", () => requester.FindProvider(s => s.GetDrawableComponent(new TestSkinComponent()) != null) == expected()); } public class UserSkinSource : LegacySkin From 2438c20d63e8aa9ad3eb0d6a09a99a11e03bbc13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 17:56:07 +0900 Subject: [PATCH 18/26] Fix `SourceChanged` not being correctly forwarded through `LegacySkinTransformer` --- osu.Game/Skinning/LegacySkinTransformer.cs | 4 ++-- osu.Game/Skinning/SkinProvidingContainer.cs | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySkinTransformer.cs b/osu.Game/Skinning/LegacySkinTransformer.cs index 651fdddb1b..613b0218f2 100644 --- a/osu.Game/Skinning/LegacySkinTransformer.cs +++ b/osu.Game/Skinning/LegacySkinTransformer.cs @@ -53,8 +53,8 @@ namespace osu.Game.Skinning public event Action SourceChanged { - add { throw new NotSupportedException(); } - remove { } + add => Source.SourceChanged += value; + remove => Source.SourceChanged -= value; } } } diff --git a/osu.Game/Skinning/SkinProvidingContainer.cs b/osu.Game/Skinning/SkinProvidingContainer.cs index 5e91a280e7..b1929aac6f 100644 --- a/osu.Game/Skinning/SkinProvidingContainer.cs +++ b/osu.Game/Skinning/SkinProvidingContainer.cs @@ -46,6 +46,9 @@ namespace osu.Game.Skinning RelativeSizeAxes = Axes.Both; noFallbackLookupProxy = new NoFallbackProxy(this); + + if (skin is ISkinSource source) + source.SourceChanged += TriggerSourceChanged; } public ISkin FindProvider(Func lookupFunction) @@ -168,6 +171,9 @@ namespace osu.Game.Skinning if (fallbackSource != null) fallbackSource.SourceChanged -= TriggerSourceChanged; + + if (skin is ISkinSource source) + source.SourceChanged -= TriggerSourceChanged; } private class NoFallbackProxy : ISkinSource From 0cf7c56e7ee51ed68310208cd8470e38eaf2f751 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 9 Jun 2021 18:51:34 +0900 Subject: [PATCH 19/26] Add fallback lookup support for `DefaultSkin` --- osu.Game/Skinning/SkinManager.cs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 48d6b9254f..9e274227a2 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -50,6 +50,8 @@ namespace osu.Game.Skinning private readonly Skin defaultLegacySkin; + private readonly Skin defaultSkin; + public SkinManager(Storage storage, DatabaseContextFactory contextFactory, GameHost host, IResourceStore resources, AudioManager audio) : base(storage, contextFactory, new SkinStore(contextFactory, storage), host) { @@ -58,10 +60,11 @@ namespace osu.Game.Skinning this.resources = resources; defaultLegacySkin = new DefaultLegacySkin(this); + defaultSkin = new DefaultSkin(this); CurrentSkinInfo.ValueChanged += skin => CurrentSkin.Value = GetSkin(skin.NewValue); - CurrentSkin.Value = new DefaultSkin(this); + CurrentSkin.Value = defaultSkin; CurrentSkin.ValueChanged += skin => { if (skin.NewValue.SkinInfo != CurrentSkinInfo.Value) @@ -226,24 +229,26 @@ namespace osu.Game.Skinning if (CurrentSkin.Value is LegacySkin && lookupFunction(defaultLegacySkin)) return defaultLegacySkin; + if (lookupFunction(defaultSkin)) + return defaultSkin; + return null; } - private T lookupWithFallback(Func func) + private T lookupWithFallback(Func lookupFunction) where T : class { - var selectedSkin = func(CurrentSkin.Value); - - if (selectedSkin != null) - return selectedSkin; + if (lookupFunction(CurrentSkin.Value) is T skinSourced) + return skinSourced; // TODO: we also want to return a DefaultLegacySkin here if the current *beatmap* is providing any skinned elements. // When attempting to address this, we may want to move the full DefaultLegacySkin fallback logic to within Player itself (to better allow // for beatmap skin visibility). - if (CurrentSkin.Value is LegacySkin) - return func(defaultLegacySkin); + if (CurrentSkin.Value is LegacySkin && lookupFunction(defaultLegacySkin) is T legacySourced) + return legacySourced; - return null; + // Finally fall back to the (non-legacy) default. + return lookupFunction(defaultSkin); } #region IResourceStorageProvider From e3f3c379535d078d4c7e0003c555a20afe64b5b1 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 9 Jun 2021 17:03:46 -0700 Subject: [PATCH 20/26] Add ability to navigate score panels with left/right arrows --- osu.Game/Screens/Ranking/ScorePanelList.cs | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 441c9e048a..e170241ede 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -8,9 +8,11 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osu.Game.Scoring; using osuTK; +using osuTK.Input; namespace osu.Game.Screens.Ranking { @@ -263,6 +265,26 @@ namespace osu.Game.Screens.Ranking container.Attach(); } + protected override bool OnKeyDown(KeyDownEvent e) + { + var expandedPanelIndex = flow.GetPanelIndex(expandedPanel.Score); + + switch (e.Key) + { + case Key.Left: + if (expandedPanelIndex > 0) + SelectedScore.Value = flow.Children[expandedPanelIndex - 1].Panel.Score; + return true; + + case Key.Right: + if (expandedPanelIndex < flow.Count - 1) + SelectedScore.Value = flow.Children[expandedPanelIndex + 1].Panel.Score; + return true; + } + + return base.OnKeyDown(e); + } + private class Flow : FillFlowContainer { public override IEnumerable FlowingChildren => applySorting(AliveInternalChildren); From 39e1f77d537c0fc3df62dbde6b5bc9be13a1c9a5 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 10 Jun 2021 09:37:33 +0700 Subject: [PATCH 21/26] add image table with image content test --- .../Online/TestSceneWikiMarkdownContainer.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 9d8f07969c..b6dce2c398 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -143,6 +143,25 @@ Line after image"; }); } + [Test] + public void TestTableWithImageContent() + { + AddStep("Add Table", () => + { + markdownContainer.DocumentUrl = "https://dev.ppy.sh"; + markdownContainer.Text = @" +| Image | Name | Effect | +| :-: | :-: | :-- | +| ![](/wiki/Skinning/Interface/img/hit300.png ""300"") | 300 | A possible score when tapping a hit circle precisely on time, completing a Slider and keeping the cursor over every tick, or completing a Spinner with the Spinner Metre full. A score of 300 appears in an blue score by default. Scoring nothing except 300s in a beatmap will award the player with the SS or SSH grade. | +| ![](/wiki/Skinning/Interface/img/hit300g.png ""Geki"") | (激) Geki | A term from Ouendan, called Elite Beat! in EBA. Appears when playing the last element in a combo in which the player has scored only 300s. Getting a Geki will give a sizable boost to the Life Bar. By default, it is blue. | +| ![](/wiki/Skinning/Interface/img/hit100.png ""100"") | 100 | A possible score one can get when tapping a Hit Object slightly late or early, completing a Slider and missing a number of ticks, or completing a Spinner with the Spinner Meter almost full. A score of 100 appears in a green score by default. When very skilled players test a beatmap and they get a lot of 100s, this may mean that the beatmap does not have correct timing. | +| ![](/wiki/Skinning/Interface/img/hit300k.png ""300 Katu"") ![](/wiki/Skinning/Interface/img/hit100k.png ""100 Katu"") | (喝) Katu or Katsu | A term from Ouendan, called Beat! in EBA. Appears when playing the last element in a combo in which the player has scored at least one 100, but no 50s or misses. Getting a Katu will give a small boost to the Life Bar. By default, it is coloured green or blue depending on whether the Katu itself is a 100 or a 300. | +| ![](/wiki/Skinning/Interface/img/hit50.png ""50"") | 50 | A possible score one can get when tapping a hit circle rather early or late but not early or late enough to cause a miss, completing a Slider and missing a lot of ticks, or completing a Spinner with the Spinner Metre close to full. A score of 50 appears in a orange score by default. Scoring a 50 in a combo will prevent the appearance of a Katu or a Geki at the combo's end. | +| ![](/wiki/Skinning/Interface/img/hit0.png ""Miss"") | Miss | A possible score one can get when not tapping a hit circle or too early (based on OD and AR, it may *shake* instead), not tapping or holding the Slider at least once, or completing a Spinner with low Spinner Metre fill. Scoring a Miss will reset the current combo to 0 and will prevent the appearance of a Katu or a Geki at the combo's end. | +"; + }); + } + private class TestMarkdownContainer : WikiMarkdownContainer { public LinkInline Link; From 28d7b069085aa9721fc9d55b20963a34753606a1 Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 10 Jun 2021 09:38:07 +0700 Subject: [PATCH 22/26] create OsuMarkdownImage --- .../Containers/Markdown/OsuMarkdownImage.cs | 20 +++++++++++++++++++ .../Markdown/OsuMarkdownTextFlowContainer.cs | 2 ++ 2 files changed, 22 insertions(+) create mode 100644 osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.cs new file mode 100644 index 0000000000..75c73af0ce --- /dev/null +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownImage.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 Markdig.Syntax.Inlines; +using osu.Framework.Graphics.Containers.Markdown; +using osu.Framework.Graphics.Cursor; + +namespace osu.Game.Graphics.Containers.Markdown +{ + public class OsuMarkdownImage : MarkdownImage, IHasTooltip + { + public string TooltipText { get; } + + public OsuMarkdownImage(LinkInline linkInline) + : base(linkInline.Url) + { + TooltipText = linkInline.Title; + } + } +} diff --git a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs index f3308019ce..36b48b7769 100644 --- a/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs +++ b/osu.Game/Graphics/Containers/Markdown/OsuMarkdownTextFlowContainer.cs @@ -17,6 +17,8 @@ namespace osu.Game.Graphics.Containers.Markdown protected override void AddLinkText(string text, LinkInline linkInline) => AddDrawable(new OsuMarkdownLinkText(text, linkInline)); + protected override void AddImage(LinkInline linkInline) => AddDrawable(new OsuMarkdownImage(linkInline)); + // TODO : Change font to monospace protected override void AddCodeInLine(CodeInline codeInline) => AddDrawable(new OsuMarkdownInlineCode { From 05cb935a940315ad6ff3bfdbed05c010ee09388c Mon Sep 17 00:00:00 2001 From: Gagah Pangeran Rosfatiputra Date: Thu, 10 Jun 2021 09:38:48 +0700 Subject: [PATCH 23/26] change WikiMarkdownImage to extend OsuMarkdownImage --- osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs index c2115efeb5..27d1fe9b2f 100644 --- a/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs +++ b/osu.Game/Overlays/Wiki/Markdown/WikiMarkdownImage.cs @@ -2,19 +2,15 @@ // See the LICENCE file in the repository root for full licence text. using Markdig.Syntax.Inlines; -using osu.Framework.Graphics.Containers.Markdown; -using osu.Framework.Graphics.Cursor; +using osu.Game.Graphics.Containers.Markdown; namespace osu.Game.Overlays.Wiki.Markdown { - public class WikiMarkdownImage : MarkdownImage, IHasTooltip + public class WikiMarkdownImage : OsuMarkdownImage { - public string TooltipText { get; } - public WikiMarkdownImage(LinkInline linkInline) - : base(linkInline.Url) + : base(linkInline) { - TooltipText = linkInline.Title; } protected override ImageContainer CreateImageContainer(string url) From cc38556f619c652d4b9bbc84d4948a059fe9aaf1 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 9 Jun 2021 23:26:57 -0700 Subject: [PATCH 24/26] Fix background being dimmed forever after toggling statistics in results screen --- osu.Game/Screens/Ranking/ResultsScreen.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index c44ce63ccb..9f7d0935d5 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -318,7 +318,7 @@ namespace osu.Game.Screens.Ranking ScorePanelList.HandleInput = false; // Dim background. - ApplyToBackground(b => b.FadeTo(0.1f, 150)); + ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.1f), 150)); detachedPanel = expandedPanel; } @@ -342,7 +342,7 @@ namespace osu.Game.Screens.Ranking ScorePanelList.HandleInput = true; // Un-dim background. - ApplyToBackground(b => b.FadeTo(0.5f, 150)); + ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.5f), 150)); detachedPanel = null; } From 699594536048ec2f4519793d5184ad23ab9e0f27 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Jun 2021 15:45:49 +0900 Subject: [PATCH 25/26] Use `With` to simplify drawable construction --- .../Skinning/Legacy/LegacyCatcherNew.cs | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs index 5987e9e393..0a7d877627 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs @@ -35,18 +35,17 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy [BackgroundDependencyLoader] private void load(ISkinSource skin, Bindable currentState) { - CurrentState.BindTo(currentState); - foreach (var state in Enum.GetValues(typeof(CatcherAnimationState)).Cast()) { - var d = getDrawableFor(state); - d.Anchor = Anchor.TopCentre; - d.Origin = Anchor.TopCentre; - d.RelativeSizeAxes = Axes.Both; - d.Size = Vector2.One; - d.FillMode = FillMode.Fit; - d.Alpha = 0; - AddInternal(drawables[state] = d); + AddInternal(drawables[state] = getDrawableFor(state).With(d => + { + d.Anchor = Anchor.TopCentre; + d.Origin = Anchor.TopCentre; + d.RelativeSizeAxes = Axes.Both; + d.Size = Vector2.One; + d.FillMode = FillMode.Fit; + d.Alpha = 0; + })); } currentDrawable = drawables[CatcherAnimationState.Idle]; @@ -54,6 +53,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy Drawable getDrawableFor(CatcherAnimationState state) => skin.GetAnimation(@$"fruit-catcher-{state.ToString().ToLowerInvariant()}", true, true, true) ?? skin.GetAnimation(@"fruit-catcher-idle", true, true, true); + + CurrentState.BindTo(currentState); } protected override void LoadComplete() From 865c5c06766d76110aeb118dee47d74d1cb3637b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 10 Jun 2021 15:47:03 +0900 Subject: [PATCH 26/26] Use `[Resolved]` to simplify bindable resolution --- .../Skinning/Legacy/LegacyCatcherNew.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs index 0a7d877627..2bf8b28aa2 100644 --- a/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs +++ b/osu.Game.Rulesets.Catch/Skinning/Legacy/LegacyCatcherNew.cs @@ -19,7 +19,8 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy { public class LegacyCatcherNew : CompositeDrawable, ICatcherSprite { - public Bindable CurrentState { get; } = new Bindable(); + [Resolved] + private Bindable currentState { get; set; } public Texture CurrentTexture => (currentDrawable as TextureAnimation)?.CurrentFrame ?? (currentDrawable as Sprite)?.Texture; @@ -33,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy } [BackgroundDependencyLoader] - private void load(ISkinSource skin, Bindable currentState) + private void load(ISkinSource skin) { foreach (var state in Enum.GetValues(typeof(CatcherAnimationState)).Cast()) { @@ -53,15 +54,13 @@ namespace osu.Game.Rulesets.Catch.Skinning.Legacy Drawable getDrawableFor(CatcherAnimationState state) => skin.GetAnimation(@$"fruit-catcher-{state.ToString().ToLowerInvariant()}", true, true, true) ?? skin.GetAnimation(@"fruit-catcher-idle", true, true, true); - - CurrentState.BindTo(currentState); } protected override void LoadComplete() { base.LoadComplete(); - CurrentState.BindValueChanged(state => + currentState.BindValueChanged(state => { currentDrawable.Alpha = 0; currentDrawable = drawables[state.NewValue];