From f38b68a590d53c41ad0a2550d048e808da3bc127 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 25 Nov 2018 03:50:26 +0100 Subject: [PATCH 01/30] Add action to pick random skin --- .../Overlays/Settings/Sections/SkinSection.cs | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 938e2ca2c3..9b190c6862 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -1,6 +1,8 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE +using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; @@ -24,7 +26,12 @@ namespace osu.Game.Overlays.Settings.Sections private readonly Bindable dropdownBindable = new Bindable(); private readonly Bindable configBindable = new Bindable(); + private static readonly SkinInfo randomSkinInfo = new RandomSkinInfo(); + private SkinManager skins; + private SkinInfo[] usableSkins; + + private Random random = new Random(); [BackgroundDependencyLoader] private void load(OsuConfigManager config, SkinManager skins) @@ -59,15 +66,32 @@ namespace osu.Game.Overlays.Settings.Sections config.BindWith(OsuSetting.Skin, configBindable); + usableSkins = skins.GetAllUsableSkins().ToArray(); + skinDropdown.Bindable = dropdownBindable; - skinDropdown.Items = skins.GetAllUsableSkins().ToArray(); + skinDropdown.Items = usableSkins.Concat(new[] { randomSkinInfo }); // Todo: This should not be necessary when OsuConfigManager is databased if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) configBindable.Value = 0; configBindable.BindValueChanged(v => dropdownBindable.Value = skinDropdown.Items.Single(s => s.ID == v), true); - dropdownBindable.BindValueChanged(v => configBindable.Value = v.ID); + dropdownBindable.BindValueChanged(v => + { + if (v == randomSkinInfo) + randomizeSkin(); + else + configBindable.Value = v.ID; + }); + } + + private void randomizeSkin() + { + int n = usableSkins.Count(); + if (n > 1) + configBindable.Value = (configBindable.Value + random.Next(n - 1) + 1) % n; // make sure it's always a different one + else + configBindable.Value = 0; } private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray()); @@ -98,5 +122,16 @@ namespace osu.Game.Overlays.Settings.Sections protected override string GenerateItemText(SkinInfo item) => item.ToString(); } } + + private class RandomSkinInfo : SkinInfo + { + public RandomSkinInfo() + { + Name = ""; + ID = -1; + } + + public override string ToString() => Name; + } } } From 6a9187ece061aebac08d949c19b8f95feeb477e5 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 25 Nov 2018 04:01:30 +0100 Subject: [PATCH 02/30] Fixed style warnings --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 9b190c6862..0802db821e 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; -using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; @@ -26,12 +25,12 @@ namespace osu.Game.Overlays.Settings.Sections private readonly Bindable dropdownBindable = new Bindable(); private readonly Bindable configBindable = new Bindable(); - private static readonly SkinInfo randomSkinInfo = new RandomSkinInfo(); + private static readonly SkinInfo random_skin_info = new RandomSkinInfo(); private SkinManager skins; private SkinInfo[] usableSkins; - private Random random = new Random(); + private readonly Random random = new Random(); [BackgroundDependencyLoader] private void load(OsuConfigManager config, SkinManager skins) @@ -69,7 +68,7 @@ namespace osu.Game.Overlays.Settings.Sections usableSkins = skins.GetAllUsableSkins().ToArray(); skinDropdown.Bindable = dropdownBindable; - skinDropdown.Items = usableSkins.Concat(new[] { randomSkinInfo }); + skinDropdown.Items = usableSkins.Concat(new[] { random_skin_info }); // Todo: This should not be necessary when OsuConfigManager is databased if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) @@ -78,7 +77,7 @@ namespace osu.Game.Overlays.Settings.Sections configBindable.BindValueChanged(v => dropdownBindable.Value = skinDropdown.Items.Single(s => s.ID == v), true); dropdownBindable.BindValueChanged(v => { - if (v == randomSkinInfo) + if (v == random_skin_info) randomizeSkin(); else configBindable.Value = v.ID; @@ -87,7 +86,7 @@ namespace osu.Game.Overlays.Settings.Sections private void randomizeSkin() { - int n = usableSkins.Count(); + int n = usableSkins.Length; if (n > 1) configBindable.Value = (configBindable.Value + random.Next(n - 1) + 1) % n; // make sure it's always a different one else From edb45e4e47ff8d6f76969425ceefd80366223d99 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 25 Nov 2018 14:23:53 +0100 Subject: [PATCH 03/30] Only show random skin button with more than one skin --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 0802db821e..cd109706cc 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -68,7 +68,10 @@ namespace osu.Game.Overlays.Settings.Sections usableSkins = skins.GetAllUsableSkins().ToArray(); skinDropdown.Bindable = dropdownBindable; - skinDropdown.Items = usableSkins.Concat(new[] { random_skin_info }); + if (usableSkins.Length > 1) + skinDropdown.Items = usableSkins.Concat(new[] { random_skin_info }); + else + skinDropdown.Items = usableSkins; // Todo: This should not be necessary when OsuConfigManager is databased if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) From 2b05a618066b4bdb50104011a2944173ef256d5f Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 25 Nov 2018 14:24:20 +0100 Subject: [PATCH 04/30] Fix crash when reseting skin while in dropdown --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index cd109706cc..46810184d7 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -83,7 +83,7 @@ namespace osu.Game.Overlays.Settings.Sections if (v == random_skin_info) randomizeSkin(); else - configBindable.Value = v.ID; + configBindable.Value = v?.ID ?? 0; }); } From 17a11212e802d0659d3a5e4904609624be19f3b3 Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Sun, 25 Nov 2018 14:41:39 +0100 Subject: [PATCH 05/30] Style fixes --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 46810184d7..56f61efa2a 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -68,10 +68,7 @@ namespace osu.Game.Overlays.Settings.Sections usableSkins = skins.GetAllUsableSkins().ToArray(); skinDropdown.Bindable = dropdownBindable; - if (usableSkins.Length > 1) - skinDropdown.Items = usableSkins.Concat(new[] { random_skin_info }); - else - skinDropdown.Items = usableSkins; + skinDropdown.Items = usableSkins.Length > 1 ? usableSkins.Concat(new[] { random_skin_info }) : usableSkins; // Todo: This should not be necessary when OsuConfigManager is databased if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) From c4c2191500b81bed98918ed00b0536373253dc1f Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Wed, 28 Nov 2018 12:36:21 +0100 Subject: [PATCH 06/30] Apply requested changes --- .../Overlays/Settings/Sections/SkinSection.cs | 32 +++++++++++++------ osu.Game/Skinning/SkinManager.cs | 4 +-- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 56f61efa2a..225e8024e3 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -2,10 +2,12 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using System; +using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Graphics; +using osu.Framework.MathUtils; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; @@ -28,9 +30,7 @@ namespace osu.Game.Overlays.Settings.Sections private static readonly SkinInfo random_skin_info = new RandomSkinInfo(); private SkinManager skins; - private SkinInfo[] usableSkins; - - private readonly Random random = new Random(); + private List usableSkins; [BackgroundDependencyLoader] private void load(OsuConfigManager config, SkinManager skins) @@ -65,10 +65,10 @@ namespace osu.Game.Overlays.Settings.Sections config.BindWith(OsuSetting.Skin, configBindable); - usableSkins = skins.GetAllUsableSkins().ToArray(); + usableSkins = skins.GetAllUsableSkins(); skinDropdown.Bindable = dropdownBindable; - skinDropdown.Items = usableSkins.Length > 1 ? usableSkins.Concat(new[] { random_skin_info }) : usableSkins; + resetSkinButtons(); // Todo: This should not be necessary when OsuConfigManager is databased if (skinDropdown.Items.All(s => s.ID != configBindable.Value)) @@ -86,15 +86,29 @@ namespace osu.Game.Overlays.Settings.Sections private void randomizeSkin() { - int n = usableSkins.Length; + int n = usableSkins.Count(); if (n > 1) - configBindable.Value = (configBindable.Value + random.Next(n - 1) + 1) % n; // make sure it's always a different one + configBindable.Value = (configBindable.Value + RNG.Next(n - 1) + 1) % n; // make sure it's always a different one else configBindable.Value = 0; } - private void itemRemoved(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Where(i => i.ID != s.ID).ToArray()); - private void itemAdded(SkinInfo s) => Schedule(() => skinDropdown.Items = skinDropdown.Items.Append(s).ToArray()); + private void itemRemoved(SkinInfo s) => Schedule(() => + { + usableSkins.RemoveAll(i => i.ID == s.ID); + resetSkinButtons(); + }); + + private void itemAdded(SkinInfo s) => Schedule(() => + { + usableSkins.Add(s); + resetSkinButtons(); + }); + + private void resetSkinButtons() + { + skinDropdown.Items = usableSkins.Count() > 1 ? usableSkins.Concat(new[] { random_skin_info }) : usableSkins; + } protected override void Dispose(bool isDisposing) { diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index bd694e443a..5ea205d2f3 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -31,7 +31,7 @@ namespace osu.Game.Skinning /// /// Returns a list of all usable s. Includes the special default skin plus all skins from . /// - /// A list of available . + /// A newly allocated list of available . public List GetAllUsableSkins() { var userSkins = GetAllUserSkins(); @@ -42,7 +42,7 @@ namespace osu.Game.Skinning /// /// Returns a list of all usable s that have been loaded by the user. /// - /// A list of available . + /// A newly allocated list of available . public List GetAllUserSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo From 89ded824b3a6554d30b14149947f0a28969d010e Mon Sep 17 00:00:00 2001 From: WebFreak001 Date: Wed, 28 Nov 2018 12:49:17 +0100 Subject: [PATCH 07/30] Style fixes --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 225e8024e3..3073e2067e 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -1,7 +1,6 @@ // Copyright (c) 2007-2018 ppy Pty Ltd . // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE -using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -86,7 +85,7 @@ namespace osu.Game.Overlays.Settings.Sections private void randomizeSkin() { - int n = usableSkins.Count(); + int n = usableSkins.Count; if (n > 1) configBindable.Value = (configBindable.Value + RNG.Next(n - 1) + 1) % n; // make sure it's always a different one else @@ -107,7 +106,7 @@ namespace osu.Game.Overlays.Settings.Sections private void resetSkinButtons() { - skinDropdown.Items = usableSkins.Count() > 1 ? usableSkins.Concat(new[] { random_skin_info }) : usableSkins; + skinDropdown.Items = usableSkins.Count > 1 ? usableSkins.Concat(new[] { random_skin_info }) : usableSkins; } protected override void Dispose(bool isDisposing) From f24569694716cbbbd92f2470680c4469f5b326bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Nov 2020 13:24:32 +0100 Subject: [PATCH 08/30] Move {-> Default}KiaiHitExplosion --- .../UI/{KiaiHitExplosion.cs => DefaultKiaiHitExplosion.cs} | 4 ++-- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) rename osu.Game.Rulesets.Taiko/UI/{KiaiHitExplosion.cs => DefaultKiaiHitExplosion.cs} (92%) diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs similarity index 92% rename from osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs rename to osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs index 067d390894..32c9f3ec4f 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs @@ -13,14 +13,14 @@ using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.UI { - public class KiaiHitExplosion : CircularContainer + public class DefaultKiaiHitExplosion : CircularContainer { public override bool RemoveWhenNotAlive => true; public readonly DrawableHitObject JudgedObject; private readonly HitType type; - public KiaiHitExplosion(DrawableHitObject judgedObject, HitType type) + public DefaultKiaiHitExplosion(DrawableHitObject judgedObject, HitType type) { JudgedObject = judgedObject; this.type = type; diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 120cf264c3..03895dfd68 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.UI public const float DEFAULT_HEIGHT = 178; private Container hitExplosionContainer; - private Container kiaiExplosionContainer; + private Container kiaiExplosionContainer; private JudgementContainer judgementContainer; private ScrollingHitObjectContainer drumRollHitContainer; internal Drawable HitTarget; @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.UI drumRollHitContainer = new DrumRollHitContainer() } }, - kiaiExplosionContainer = new Container + kiaiExplosionContainer = new Container { Name = "Kiai hit explosions", RelativeSizeAxes = Axes.Both, @@ -219,7 +219,7 @@ namespace osu.Game.Rulesets.Taiko.UI { hitExplosionContainer.Add(new HitExplosion(drawableObject, result)); if (drawableObject.HitObject.Kiai) - kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type)); + kiaiExplosionContainer.Add(new DefaultKiaiHitExplosion(drawableObject, type)); } private class ProxyContainer : LifetimeManagementContainer From ed01d37966a9249bb4f81d81f03a2c01746ec374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Nov 2020 13:35:49 +0100 Subject: [PATCH 09/30] Reintroduce KiaiHitExplosion as skinnable --- .../TaikoSkinComponents.cs | 1 + .../UI/DefaultKiaiHitExplosion.cs | 9 +--- .../UI/KiaiHitExplosion.cs | 47 +++++++++++++++++++ osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 6 +-- 4 files changed, 52 insertions(+), 11 deletions(-) create mode 100644 osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs diff --git a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs index 132d8f8868..bf48898dd2 100644 --- a/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs +++ b/osu.Game.Rulesets.Taiko/TaikoSkinComponents.cs @@ -18,6 +18,7 @@ namespace osu.Game.Rulesets.Taiko TaikoExplosionMiss, TaikoExplosionOk, TaikoExplosionGreat, + TaikoExplosionKiai, Scroller, Mascot, } diff --git a/osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs index 32c9f3ec4f..7ce8b016d5 100644 --- a/osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/DefaultKiaiHitExplosion.cs @@ -8,7 +8,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Graphics; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Taiko.Objects; namespace osu.Game.Rulesets.Taiko.UI @@ -17,19 +16,13 @@ namespace osu.Game.Rulesets.Taiko.UI { public override bool RemoveWhenNotAlive => true; - public readonly DrawableHitObject JudgedObject; private readonly HitType type; - public DefaultKiaiHitExplosion(DrawableHitObject judgedObject, HitType type) + public DefaultKiaiHitExplosion(HitType type) { - JudgedObject = judgedObject; this.type = type; - Anchor = Anchor.CentreLeft; - Origin = Anchor.Centre; - RelativeSizeAxes = Axes.Both; - Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1); Blending = BlendingParameters.Additive; diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs new file mode 100644 index 0000000000..20900a9352 --- /dev/null +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Skinning; +using osuTK; + +namespace osu.Game.Rulesets.Taiko.UI +{ + public class KiaiHitExplosion : Container + { + public override bool RemoveWhenNotAlive => true; + + [Cached(typeof(DrawableHitObject))] + public readonly DrawableHitObject JudgedObject; + + private readonly HitType hitType; + + private SkinnableDrawable skinnable; + + public override double LifetimeStart => skinnable.Drawable.LifetimeStart; + + public override double LifetimeEnd => skinnable.Drawable.LifetimeEnd; + + public KiaiHitExplosion(DrawableHitObject judgedObject, HitType hitType) + { + JudgedObject = judgedObject; + this.hitType = hitType; + + Anchor = Anchor.CentreLeft; + Origin = Anchor.Centre; + + RelativeSizeAxes = Axes.Both; + Size = new Vector2(TaikoHitObject.DEFAULT_SIZE, 1); + } + + [BackgroundDependencyLoader] + private void load() + { + Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoExplosionKiai), _ => new DefaultKiaiHitExplosion(hitType)); + } + } +} diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 03895dfd68..120cf264c3 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Taiko.UI public const float DEFAULT_HEIGHT = 178; private Container hitExplosionContainer; - private Container kiaiExplosionContainer; + private Container kiaiExplosionContainer; private JudgementContainer judgementContainer; private ScrollingHitObjectContainer drumRollHitContainer; internal Drawable HitTarget; @@ -97,7 +97,7 @@ namespace osu.Game.Rulesets.Taiko.UI drumRollHitContainer = new DrumRollHitContainer() } }, - kiaiExplosionContainer = new Container + kiaiExplosionContainer = new Container { Name = "Kiai hit explosions", RelativeSizeAxes = Axes.Both, @@ -219,7 +219,7 @@ namespace osu.Game.Rulesets.Taiko.UI { hitExplosionContainer.Add(new HitExplosion(drawableObject, result)); if (drawableObject.HitObject.Kiai) - kiaiExplosionContainer.Add(new DefaultKiaiHitExplosion(drawableObject, type)); + kiaiExplosionContainer.Add(new KiaiHitExplosion(drawableObject, type)); } private class ProxyContainer : LifetimeManagementContainer From 35763a74fd75c18897d7236b4f58f2218c80135e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Nov 2020 14:07:39 +0100 Subject: [PATCH 10/30] Add test scene --- .../Skinning/TestSceneKiaiHitExplosion.cs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.cs new file mode 100644 index 0000000000..b558709592 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Skinning/TestSceneKiaiHitExplosion.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 NUnit.Framework; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.UI; + +namespace osu.Game.Rulesets.Taiko.Tests.Skinning +{ + [TestFixture] + public class TestSceneKiaiHitExplosion : TaikoSkinnableTestScene + { + [Test] + public void TestKiaiHits() + { + AddStep("rim hit", () => SetContents(() => getContentFor(createHit(HitType.Rim)))); + AddStep("centre hit", () => SetContents(() => getContentFor(createHit(HitType.Centre)))); + } + + private Drawable getContentFor(DrawableTestHit hit) + { + return new Container + { + RelativeSizeAxes = Axes.Both, + Child = new KiaiHitExplosion(hit, hit.HitObject.Type) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }; + } + + private DrawableTestHit createHit(HitType type) => new DrawableTestHit(new Hit { StartTime = Time.Current, Type = type }); + } +} From 0387d994bdc6136259d32c3b2181842a6e91f8ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Nov 2020 14:08:27 +0100 Subject: [PATCH 11/30] Do not lookup default kiai explosion if skin has own --- .../Skinning/TaikoLegacySkinTransformer.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index c88480d18f..ddbf20b827 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -114,6 +114,13 @@ namespace osu.Game.Rulesets.Taiko.Skinning return null; + case TaikoSkinComponents.TaikoExplosionKiai: + // suppress the default kiai explosion if the skin brings its own sprites. + if (hasExplosion.Value) + return Drawable.Empty(); + + return null; + case TaikoSkinComponents.Scroller: if (GetTexture("taiko-slider") != null) return new LegacyTaikoScroller(); From be4735cd2ba8f37e853d9a09796d1248cbddc8d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 10 Nov 2020 14:50:19 +0100 Subject: [PATCH 12/30] Explicitly set lifetime to ensure empty drawables are cleaned up --- .../Skinning/TaikoLegacySkinTransformer.cs | 3 ++- osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs | 6 ++++++ 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index ddbf20b827..880af3fbd8 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -116,8 +116,9 @@ namespace osu.Game.Rulesets.Taiko.Skinning case TaikoSkinComponents.TaikoExplosionKiai: // suppress the default kiai explosion if the skin brings its own sprites. + // the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield. if (hasExplosion.Value) - return Drawable.Empty(); + return KiaiHitExplosion.EmptyExplosion(); return null; diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index 20900a9352..326cb23897 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -43,5 +43,11 @@ namespace osu.Game.Rulesets.Taiko.UI { Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoExplosionKiai), _ => new DefaultKiaiHitExplosion(hitType)); } + + /// + /// Helper function to use when an explosion is not desired. + /// Lifetime is set to avoid accumulating empty drawables in the parent container. + /// + public static Drawable EmptyExplosion() => Empty().With(d => d.LifetimeEnd = double.MinValue); } } From 6b548ef5e4a6517e8ac5ae0689d7827c87cbfab4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Nov 2020 11:54:40 +0900 Subject: [PATCH 13/30] Split out SkinInfo ID constants and fix random logic --- .../Overlays/Settings/Sections/SkinSection.cs | 49 ++++++++++--------- osu.Game/Skinning/DefaultLegacySkin.cs | 2 +- osu.Game/Skinning/SkinInfo.cs | 5 ++ 3 files changed, 31 insertions(+), 25 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 412103af38..b21de26dde 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -31,9 +31,13 @@ namespace osu.Game.Overlays.Settings.Sections private readonly Bindable dropdownBindable = new Bindable { Default = SkinInfo.Default }; private readonly Bindable configBindable = new Bindable(); - private static readonly SkinInfo random_skin_info = new RandomSkinInfo(); + private static readonly SkinInfo random_skin_info = new SkinInfo + { + ID = SkinInfo.RANDOM_SKIN, + Name = "", + }; - private List usableSkins; + private List skinItems; [Resolved] private SkinManager skins { get; set; } @@ -98,29 +102,37 @@ namespace osu.Game.Overlays.Settings.Sections dropdownBindable.BindValueChanged(skin => { if (skin.NewValue == random_skin_info) + { randomizeSkin(); - else - configBindable.Value = skin.NewValue.ID; + return; + } + + configBindable.Value = skin.NewValue.ID; }); } private void randomizeSkin() { - int n = usableSkins.Count; - if (n > 1) - configBindable.Value = (configBindable.Value + RNG.Next(n - 1) + 1) % n; // make sure it's always a different one - else - configBindable.Value = 0; + int count = skinItems.Count - 1; // exclude "random" item. + + if (count <= 1) + { + configBindable.Value = SkinInfo.Default.ID; + return; + } + + // ensure the random selection is never the same as the previous. + configBindable.Value = skinItems.Where(s => s.ID != configBindable.Value).ElementAt(RNG.Next(0, count)).ID; } private void updateItems() { - usableSkins = skins.GetAllUsableSkins(); + skinItems = skins.GetAllUsableSkins(); - if (usableSkins.Count > 1) - usableSkins.Add(random_skin_info); + if (skinItems.Count > 1) + skinItems.Add(random_skin_info); - skinDropdown.Items = usableSkins; + skinDropdown.Items = skinItems; } private void itemUpdated(ValueChangedEvent> weakItem) @@ -150,17 +162,6 @@ namespace osu.Game.Overlays.Settings.Sections } } - private class RandomSkinInfo : SkinInfo - { - public RandomSkinInfo() - { - Name = ""; - ID = -1; - } - - public override string ToString() => Name; - } - private class ExportSkinButton : SettingsButton { [Resolved] diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index 78d3a37f7c..2758a4cbba 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -25,7 +25,7 @@ namespace osu.Game.Skinning public static SkinInfo Info { get; } = new SkinInfo { - ID = -1, // this is temporary until database storage is decided upon. + ID = SkinInfo.CLASSIC_SKIN, // this is temporary until database storage is decided upon. Name = "osu!classic", Creator = "team osu!" }; diff --git a/osu.Game/Skinning/SkinInfo.cs b/osu.Game/Skinning/SkinInfo.cs index b9fe44ef3b..aaccbefb3d 100644 --- a/osu.Game/Skinning/SkinInfo.cs +++ b/osu.Game/Skinning/SkinInfo.cs @@ -10,6 +10,10 @@ namespace osu.Game.Skinning { public class SkinInfo : IHasFiles, IEquatable, IHasPrimaryKey, ISoftDelete { + internal const int DEFAULT_SKIN = 0; + internal const int CLASSIC_SKIN = -1; + internal const int RANDOM_SKIN = -2; + public int ID { get; set; } public string Name { get; set; } @@ -26,6 +30,7 @@ namespace osu.Game.Skinning public static SkinInfo Default { get; } = new SkinInfo { + ID = DEFAULT_SKIN, Name = "osu!lazer", Creator = "team osu!" }; From 6674628bc7383c1ae896afbce945237666a421d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Nov 2020 12:03:38 +0900 Subject: [PATCH 14/30] Only include user skins in random choices --- .../Overlays/Settings/Sections/SkinSection.cs | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index b21de26dde..8297b56db8 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -113,24 +113,28 @@ namespace osu.Game.Overlays.Settings.Sections private void randomizeSkin() { - int count = skinItems.Count - 1; // exclude "random" item. + // choose from only user skins, removing the current selection to ensure a new one is chosen. + var randomChoices = skinItems.Where(s => s.ID > 0 && s.ID != configBindable.Value).ToArray(); - if (count <= 1) + if (randomChoices.Length == 0) { configBindable.Value = SkinInfo.Default.ID; return; } - // ensure the random selection is never the same as the previous. - configBindable.Value = skinItems.Where(s => s.ID != configBindable.Value).ElementAt(RNG.Next(0, count)).ID; + configBindable.Value = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length)).ID; } private void updateItems() { skinItems = skins.GetAllUsableSkins(); - if (skinItems.Count > 1) - skinItems.Add(random_skin_info); + // insert after lazer built-in skins + int firstNonDefault = skinItems.FindIndex(s => s.ID > 0); + if (firstNonDefault < 0) + firstNonDefault = skinItems.Count; + + skinItems.Insert(firstNonDefault, random_skin_info); skinDropdown.Items = skinItems; } From 9caa56c64fcac23824932389327cf9945ff6c098 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Nov 2020 12:19:01 +0900 Subject: [PATCH 15/30] Display skin changes via on-screen display overlay --- osu.Game/Configuration/OsuConfigManager.cs | 8 ++++++++ osu.Game/OsuGame.cs | 4 ++++ 2 files changed, 12 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 8206a92a54..b79e99781b 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; @@ -177,7 +178,14 @@ namespace osu.Game.Configuration new TrackedSetting(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled")), new TrackedSetting(OsuSetting.HUDVisibilityMode, m => new SettingDescription(m, "HUD Visibility", m.GetDescription())), new TrackedSetting(OsuSetting.Scaling, m => new SettingDescription(m, "scaling", m.GetDescription())), + new TrackedSetting(OsuSetting.Skin, m => + { + string skinName = LookupSkinName?.Invoke(m) ?? string.Empty; + return new SettingDescription(skinName, "skin", skinName); + }) }; + + public Func LookupSkinName { get; set; } } public enum OsuSetting diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5119f262d5..a34272f5f0 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -548,6 +548,10 @@ namespace osu.Game ScoreManager.GetStableStorage = GetStorageForStableInstall; ScoreManager.PresentImport = items => PresentScore(items.First()); + // make config aware of how to lookup skins for on-screen display purposes. + // if this becomes a more common thing, tracked settings should be reconsidered to allow local DI. + LocalConfig.LookupSkinName = id => SkinManager.GetAllUsableSkins().FirstOrDefault(s => s.ID == id)?.ToString() ?? "Unknown"; + Container logoContainer; BackButton.Receptor receptor; From 1173ef089099e2468b80cf33eeb8f9bf79bbcc80 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Nov 2020 12:37:00 +0900 Subject: [PATCH 16/30] Fix mania notelock crashing with overlapping hitwindows --- .../TestSceneHoldNoteInput.cs | 2 +- .../TestSceneOutOfOrderHits.cs | 23 +++++++++++++++++++ .../Objects/Drawables/DrawableHoldNote.cs | 2 +- .../UI/OrderedHitPolicy.cs | 2 +- 4 files changed, 26 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 5cb1519196..6c9f184c2c 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Tests assertHeadJudgement(HitResult.Miss); assertTickJudgement(HitResult.LargeTickMiss); assertTailJudgement(HitResult.Miss); - assertNoteJudgement(HitResult.IgnoreHit); + assertNoteJudgement(HitResult.IgnoreMiss); } /// diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs index e8c2472c3b..d699921307 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs @@ -54,6 +54,29 @@ namespace osu.Game.Rulesets.Mania.Tests } } + [Test] + public void TestMissAfterNextObjectStartTime() + { + var objects = new List + { + new HoldNote + { + StartTime = 1000, + EndTime = 1200, + }, + new HoldNote + { + StartTime = 1220, + EndTime = 1420 + } + }; + + performTest(objects, new List()); + + addJudgementAssert(objects[0], HitResult.IgnoreMiss); + addJudgementAssert(objects[1], HitResult.IgnoreMiss); + } + private void addJudgementAssert(ManiaHitObject hitObject, HitResult result) { AddAssert($"({hitObject.GetType().ReadableName()} @ {hitObject.StartTime}) judgement is {result}", diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index d9d740c145..3b3f72157a 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { if (Tail.AllJudged) { - ApplyResult(r => r.Type = r.Judgement.MaxResult); + ApplyResult(r => r.Type = Tail.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); endHold(); } diff --git a/osu.Game.Rulesets.Mania/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Mania/UI/OrderedHitPolicy.cs index 0f9cd48dd8..9bc577a81e 100644 --- a/osu.Game.Rulesets.Mania/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Mania/UI/OrderedHitPolicy.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mania.UI /// The that was hit. public void HandleHit(DrawableHitObject hitObject) { - if (!IsHittable(hitObject, hitObject.HitObject.StartTime + hitObject.Result.TimeOffset)) + if (hitObject.IsHit && !IsHittable(hitObject, hitObject.HitObject.StartTime + hitObject.Result.TimeOffset)) throw new InvalidOperationException($"A {hitObject} was hit before it became hittable!"); foreach (var obj in enumerateHitObjectsUpTo(hitObject.HitObject.StartTime)) From 626231d90626790a063f9b93256276e2b4d8135e Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Nov 2020 12:53:32 +0900 Subject: [PATCH 17/30] Completely remove check as it can occur for hits too --- .../TestSceneOutOfOrderHits.cs | 51 +++++++++++++++++-- .../UI/OrderedHitPolicy.cs | 4 -- 2 files changed, 47 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs index d699921307..86a142f2f6 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -55,19 +56,19 @@ namespace osu.Game.Rulesets.Mania.Tests } [Test] - public void TestMissAfterNextObjectStartTime() + public void TestHoldNoteMissAfterNextObjectStartTime() { var objects = new List { new HoldNote { StartTime = 1000, - EndTime = 1200, + EndTime = 1010, }, new HoldNote { - StartTime = 1220, - EndTime = 1420 + StartTime = 1020, + EndTime = 1030 } }; @@ -77,12 +78,54 @@ namespace osu.Game.Rulesets.Mania.Tests addJudgementAssert(objects[1], HitResult.IgnoreMiss); } + [Test] + public void TestHoldNoteReleasedHitAfterNextObjectStartTime() + { + var objects = new List + { + new HoldNote + { + StartTime = 1000, + EndTime = 1010, + }, + new HoldNote + { + StartTime = 1020, + EndTime = 1030 + } + }; + + var frames = new List + { + new ManiaReplayFrame(1000, ManiaAction.Key1), + new ManiaReplayFrame(1030), + new ManiaReplayFrame(1040, ManiaAction.Key1), + new ManiaReplayFrame(1050) + }; + + performTest(objects, frames); + + addJudgementAssert(objects[0], HitResult.IgnoreHit); + addJudgementAssert("first head", () => ((HoldNote)objects[0]).Head, HitResult.Perfect); + addJudgementAssert("first tail", () => ((HoldNote)objects[0]).Tail, HitResult.Perfect); + + addJudgementAssert(objects[1], HitResult.IgnoreHit); + addJudgementAssert("second head", () => ((HoldNote)objects[1]).Head, HitResult.Great); + addJudgementAssert("second tail", () => ((HoldNote)objects[1]).Tail, HitResult.Perfect); + } + private void addJudgementAssert(ManiaHitObject hitObject, HitResult result) { AddAssert($"({hitObject.GetType().ReadableName()} @ {hitObject.StartTime}) judgement is {result}", () => judgementResults.Single(r => r.HitObject == hitObject).Type == result); } + private void addJudgementAssert(string name, Func hitObject, HitResult result) + { + AddAssert($"{name} judgement is {result}", + () => judgementResults.Single(r => r.HitObject == hitObject()).Type == result); + } + private void addJudgementOffsetAssert(ManiaHitObject hitObject, double offset) { AddAssert($"({hitObject.GetType().ReadableName()} @ {hitObject.StartTime}) judged at {offset}", diff --git a/osu.Game.Rulesets.Mania/UI/OrderedHitPolicy.cs b/osu.Game.Rulesets.Mania/UI/OrderedHitPolicy.cs index 9bc577a81e..961858b62b 100644 --- a/osu.Game.Rulesets.Mania/UI/OrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Mania/UI/OrderedHitPolicy.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using osu.Framework.Extensions.IEnumerableExtensions; using osu.Game.Rulesets.Mania.Objects.Drawables; @@ -44,9 +43,6 @@ namespace osu.Game.Rulesets.Mania.UI /// The that was hit. public void HandleHit(DrawableHitObject hitObject) { - if (hitObject.IsHit && !IsHittable(hitObject, hitObject.HitObject.StartTime + hitObject.Result.TimeOffset)) - throw new InvalidOperationException($"A {hitObject} was hit before it became hittable!"); - foreach (var obj in enumerateHitObjectsUpTo(hitObject.HitObject.StartTime)) { if (obj.Judged) From 508ae91a978e3d706d654457cbfaff4206aea266 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Nov 2020 12:53:53 +0900 Subject: [PATCH 18/30] Revert unnecessary change --- osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs | 2 +- osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs | 4 ++-- osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 6c9f184c2c..5cb1519196 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Mania.Tests assertHeadJudgement(HitResult.Miss); assertTickJudgement(HitResult.LargeTickMiss); assertTailJudgement(HitResult.Miss); - assertNoteJudgement(HitResult.IgnoreMiss); + assertNoteJudgement(HitResult.IgnoreHit); } /// diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs index 86a142f2f6..cecac38f70 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneOutOfOrderHits.cs @@ -74,8 +74,8 @@ namespace osu.Game.Rulesets.Mania.Tests performTest(objects, new List()); - addJudgementAssert(objects[0], HitResult.IgnoreMiss); - addJudgementAssert(objects[1], HitResult.IgnoreMiss); + addJudgementAssert(objects[0], HitResult.IgnoreHit); + addJudgementAssert(objects[1], HitResult.IgnoreHit); } [Test] diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs index 3b3f72157a..d9d740c145 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableHoldNote.cs @@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { if (Tail.AllJudged) { - ApplyResult(r => r.Type = Tail.IsHit ? r.Judgement.MaxResult : r.Judgement.MinResult); + ApplyResult(r => r.Type = r.Judgement.MaxResult); endHold(); } From 6014751e29f4cd3835d75af104ddeaa50c68b954 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Nov 2020 12:54:39 +0900 Subject: [PATCH 19/30] Add the ability for the game OSD to display user bindings Adds binding display for mouse button toggle / HUD toggle keys. - [ ] Depends on #10786 for ease-of-merge --- osu.Game/Configuration/OsuConfigManager.cs | 33 ++++++++++++++-------- osu.Game/Input/KeyBindingStore.cs | 17 +++++++++++ osu.Game/OsuGame.cs | 10 +++++++ 3 files changed, 49 insertions(+), 11 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index b79e99781b..795ad96170 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using osu.Framework.Bindables; using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; @@ -9,6 +10,7 @@ using osu.Framework.Extensions; using osu.Framework.Platform; using osu.Framework.Testing; using osu.Game.Input; +using osu.Game.Input.Bindings; using osu.Game.Overlays; using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select; @@ -173,19 +175,28 @@ namespace osu.Game.Configuration } } - public override TrackedSettings CreateTrackedSettings() => new TrackedSettings + public override TrackedSettings CreateTrackedSettings() { - new TrackedSetting(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled")), - new TrackedSetting(OsuSetting.HUDVisibilityMode, m => new SettingDescription(m, "HUD Visibility", m.GetDescription())), - new TrackedSetting(OsuSetting.Scaling, m => new SettingDescription(m, "scaling", m.GetDescription())), - new TrackedSetting(OsuSetting.Skin, m => - { - string skinName = LookupSkinName?.Invoke(m) ?? string.Empty; - return new SettingDescription(skinName, "skin", skinName); - }) - }; + // these need to be assigned in normal game startup scenarios. + Debug.Assert(LookupKeyBindings != null); + Debug.Assert(LookupSkinName != null); - public Func LookupSkinName { get; set; } + return new TrackedSettings + { + new TrackedSetting(OsuSetting.MouseDisableButtons, v => new SettingDescription(!v, "gameplay mouse buttons", v ? "disabled" : "enabled", LookupKeyBindings(GlobalAction.ToggleGameplayMouseButtons))), + new TrackedSetting(OsuSetting.HUDVisibilityMode, m => new SettingDescription(m, "HUD Visibility", m.GetDescription(), $"cycle: shift-tab quick view: {LookupKeyBindings(GlobalAction.HoldForHUD)}")), + new TrackedSetting(OsuSetting.Scaling, m => new SettingDescription(m, "scaling", m.GetDescription())), + new TrackedSetting(OsuSetting.Skin, m => + { + string skinName = LookupSkinName(m) ?? string.Empty; + return new SettingDescription(skinName, "skin", skinName); + }) + }; + } + + public Func LookupSkinName { private get; set; } + + public Func LookupKeyBindings { private get; set; } } public enum OsuSetting diff --git a/osu.Game/Input/KeyBindingStore.cs b/osu.Game/Input/KeyBindingStore.cs index 198ab6883d..bc73d74d74 100644 --- a/osu.Game/Input/KeyBindingStore.cs +++ b/osu.Game/Input/KeyBindingStore.cs @@ -32,6 +32,23 @@ namespace osu.Game.Input public void Register(KeyBindingContainer manager) => insertDefaults(manager.DefaultKeyBindings); + /// + /// Retrieve all user-defined key combinations (in a format that can be displayed) for a specific action. + /// + /// The action to lookup. + /// A set of display strings for all the user's key configuration for the action. + public IEnumerable GetReadableKeyCombinationsFor(GlobalAction globalAction) + { + foreach (var action in Query().Where(b => (GlobalAction)b.Action == globalAction)) + { + string str = action.KeyCombination.ReadableString(); + + // even if found, the readable string may be empty for an unbound action. + if (str.Length > 0) + yield return str; + } + } + private void insertDefaults(IEnumerable defaults, int? rulesetId = null, int? variant = null) { using (var usage = ContextFactory.GetForWrite()) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a34272f5f0..7f0465604b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -552,6 +552,16 @@ namespace osu.Game // if this becomes a more common thing, tracked settings should be reconsidered to allow local DI. LocalConfig.LookupSkinName = id => SkinManager.GetAllUsableSkins().FirstOrDefault(s => s.ID == id)?.ToString() ?? "Unknown"; + LocalConfig.LookupKeyBindings = l => + { + var combinations = KeyBindingStore.GetReadableKeyCombinationsFor(l).ToArray(); + + if (combinations.Length == 0) + return "none"; + + return string.Join(" or ", combinations); + }; + Container logoContainer; BackButton.Receptor receptor; From 8d38d9cc93497a0f32d253afc15e14769670b456 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Nov 2020 13:05:03 +0900 Subject: [PATCH 20/30] Add hotkey to select random skin --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- .../Input/Bindings/GlobalActionContainer.cs | 5 +++++ osu.Game/OsuGame.cs | 4 ++++ .../Overlays/Settings/Sections/SkinSection.cs | 17 +---------------- osu.Game/Skinning/SkinManager.cs | 15 +++++++++++++++ 5 files changed, 26 insertions(+), 17 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 795ad96170..a4b99bb6e6 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -189,7 +189,7 @@ namespace osu.Game.Configuration new TrackedSetting(OsuSetting.Skin, m => { string skinName = LookupSkinName(m) ?? string.Empty; - return new SettingDescription(skinName, "skin", skinName); + return new SettingDescription(skinName, "skin", skinName, $"random: {LookupKeyBindings(GlobalAction.RandomSkin)}"); }) }; } diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 3de4bb1f9d..e5d3a89a88 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -48,6 +48,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.Space, GlobalAction.Select), new KeyBinding(InputKey.Enter, GlobalAction.Select), new KeyBinding(InputKey.KeypadEnter, GlobalAction.Select), + + new KeyBinding(new[] { InputKey.Control, InputKey.Shift, InputKey.R }, GlobalAction.RandomSkin), }; public IEnumerable EditorKeyBindings => new[] @@ -191,5 +193,8 @@ namespace osu.Game.Input.Bindings [Description("Hold for HUD")] HoldForHUD, + + [Description("Random Skin")] + RandomSkin, } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7f0465604b..1e94becb98 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -887,6 +887,10 @@ namespace osu.Game case GlobalAction.ToggleGameplayMouseButtons: LocalConfig.Set(OsuSetting.MouseDisableButtons, !LocalConfig.Get(OsuSetting.MouseDisableButtons)); return true; + + case GlobalAction.RandomSkin: + SkinManager.SelectRandomSkin(); + return true; } return false; diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 8297b56db8..3e7068f1ff 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -9,7 +9,6 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Logging; -using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Skinning; @@ -103,7 +102,7 @@ namespace osu.Game.Overlays.Settings.Sections { if (skin.NewValue == random_skin_info) { - randomizeSkin(); + skins.SelectRandomSkin(); return; } @@ -111,20 +110,6 @@ namespace osu.Game.Overlays.Settings.Sections }); } - private void randomizeSkin() - { - // choose from only user skins, removing the current selection to ensure a new one is chosen. - var randomChoices = skinItems.Where(s => s.ID > 0 && s.ID != configBindable.Value).ToArray(); - - if (randomChoices.Length == 0) - { - configBindable.Value = SkinInfo.Default.ID; - return; - } - - configBindable.Value = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length)).ID; - } - private void updateItems() { skinItems = skins.GetAllUsableSkins(); diff --git a/osu.Game/Skinning/SkinManager.cs b/osu.Game/Skinning/SkinManager.cs index 2e4c24a89e..bef3e86a4d 100644 --- a/osu.Game/Skinning/SkinManager.cs +++ b/osu.Game/Skinning/SkinManager.cs @@ -19,6 +19,7 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Framework.Platform; using osu.Framework.Testing; +using osu.Framework.Utils; using osu.Game.Audio; using osu.Game.Database; using osu.Game.IO.Archives; @@ -87,6 +88,20 @@ namespace osu.Game.Skinning /// A newly allocated list of available . public List GetAllUserSkins() => ModelStore.ConsumableItems.Where(s => !s.DeletePending).ToList(); + public void SelectRandomSkin() + { + // choose from only user skins, removing the current selection to ensure a new one is chosen. + var randomChoices = GetAllUsableSkins().Where(s => s.ID > 0 && s.ID != CurrentSkinInfo.Value.ID).ToArray(); + + if (randomChoices.Length == 0) + { + CurrentSkinInfo.Value = SkinInfo.Default; + return; + } + + CurrentSkinInfo.Value = randomChoices.ElementAt(RNG.Next(0, randomChoices.Length)); + } + protected override SkinInfo CreateModel(ArchiveReader archive) => new SkinInfo { Name = archive.Name }; private const string unknown_creator_string = "Unknown"; From 11cf04eed171f3dac856929f917febc62b8d9636 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Nov 2020 13:39:42 +0900 Subject: [PATCH 21/30] Fix frames potentially getting added to spectator replay in wrong format The way spectator currently works, the `Spectator` screen is responsible for adding new frames to the replay, even when it has a child (`SpectatorPlayer`) present. There was a possibility that a new play had already started, and on returning to the Spectator screen (to initialise the new play) there would be a brief period where the Player instance is still reading from the replay, the `userBeganPlaying` call had not yet finished initialising the new target replay, and `userSentFrames` is run (asynchronously), writing frames to the previous replay using the incorrect ruleset instance). To make this work, it doesn't `Schedule` frame addition to the replay (making things a bit unsafe). Changing this itself isn't such a simple one to do, so I instead opted to fix this via locking. Closes https://github.com/ppy/osu/issues/10777. --- osu.Game/Screens/Play/Spectator.cs | 80 ++++++++++++++++++------------ 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/osu.Game/Screens/Play/Spectator.cs b/osu.Game/Screens/Play/Spectator.cs index 0f593db277..6f51771c12 100644 --- a/osu.Game/Screens/Play/Spectator.cs +++ b/osu.Game/Screens/Play/Spectator.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -61,7 +62,9 @@ namespace osu.Game.Screens.Play [Resolved] private RulesetStore rulesets { get; set; } - private Replay replay; + private Score score; + + private readonly object scoreLock = new object(); private Container beatmapPanelContainer; @@ -198,23 +201,32 @@ namespace osu.Game.Screens.Play private void userSentFrames(int userId, FrameDataBundle data) { + // this is not scheduled as it handles propagation of frames even when in a child screen (at which point we are not alive). + // probably not the safest way to handle this. + if (userId != targetUser.Id) return; - // this should never happen as the server sends the user's state on watching, - // but is here as a safety measure. - if (replay == null) - return; - - foreach (var frame in data.Frames) + lock (scoreLock) { - IConvertibleReplayFrame convertibleFrame = rulesetInstance.CreateConvertibleReplayFrame(); - convertibleFrame.FromLegacy(frame, beatmap.Value.Beatmap); + // this should never happen as the server sends the user's state on watching, + // but is here as a safety measure. + if (score == null) + return; - var convertedFrame = (ReplayFrame)convertibleFrame; - convertedFrame.Time = frame.Time; + // rulesetInstance should be guaranteed to be in sync with the score via scoreLock. + Debug.Assert(rulesetInstance != null && rulesetInstance.RulesetInfo.Equals(score.ScoreInfo.Ruleset)); - replay.Frames.Add(convertedFrame); + foreach (var frame in data.Frames) + { + IConvertibleReplayFrame convertibleFrame = rulesetInstance.CreateConvertibleReplayFrame(); + convertibleFrame.FromLegacy(frame, beatmap.Value.Beatmap); + + var convertedFrame = (ReplayFrame)convertibleFrame; + convertedFrame.Time = frame.Time; + + score.Replay.Frames.Add(convertedFrame); + } } } @@ -247,10 +259,13 @@ namespace osu.Game.Screens.Play if (userId != targetUser.Id) return; - if (replay != null) + lock (scoreLock) { - replay.HasReceivedAllFrames = true; - replay = null; + if (score != null) + { + score.Replay.HasReceivedAllFrames = true; + score = null; + } } Schedule(clearDisplay); @@ -283,27 +298,28 @@ namespace osu.Game.Screens.Play return; } - replay ??= new Replay { HasReceivedAllFrames = false }; - - var scoreInfo = new ScoreInfo + lock (scoreLock) { - Beatmap = resolvedBeatmap, - User = targetUser, - Mods = state.Mods.Select(m => m.ToMod(resolvedRuleset)).ToArray(), - Ruleset = resolvedRuleset.RulesetInfo, - }; + score = new Score + { + ScoreInfo = new ScoreInfo + { + Beatmap = resolvedBeatmap, + User = targetUser, + Mods = state.Mods.Select(m => m.ToMod(resolvedRuleset)).ToArray(), + Ruleset = resolvedRuleset.RulesetInfo, + }, + Replay = new Replay { HasReceivedAllFrames = false }, + }; - ruleset.Value = resolvedRuleset.RulesetInfo; - rulesetInstance = resolvedRuleset; + ruleset.Value = resolvedRuleset.RulesetInfo; + rulesetInstance = resolvedRuleset; - beatmap.Value = beatmaps.GetWorkingBeatmap(resolvedBeatmap); - watchButton.Enabled.Value = true; + beatmap.Value = beatmaps.GetWorkingBeatmap(resolvedBeatmap); + watchButton.Enabled.Value = true; - this.Push(new SpectatorPlayerLoader(new Score - { - ScoreInfo = scoreInfo, - Replay = replay, - })); + this.Push(new SpectatorPlayerLoader(score)); + } } private void showBeatmapPanel(SpectatorState state) From 324626e0978cb1d401440e516b6c2f74b0408e26 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Nov 2020 13:51:20 +0900 Subject: [PATCH 22/30] Move default config tracking calls out of OnScreenDisplay itself --- osu.Game/OsuGame.cs | 7 ++++++- osu.Game/Overlays/OnScreenDisplay.cs | 11 +---------- 2 files changed, 7 insertions(+), 11 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7f0465604b..ba856a6e6b 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -627,7 +627,12 @@ namespace osu.Game loadComponentSingleFile(volume = new VolumeOverlay(), leftFloatingOverlayContent.Add, true); - loadComponentSingleFile(new OnScreenDisplay(), Add, true); + var onScreenDisplay = new OnScreenDisplay(); + + onScreenDisplay.BeginTracking(this, frameworkConfig); + onScreenDisplay.BeginTracking(this, LocalConfig); + + loadComponentSingleFile(onScreenDisplay, Add, true); loadComponentSingleFile(notifications.With(d => { diff --git a/osu.Game/Overlays/OnScreenDisplay.cs b/osu.Game/Overlays/OnScreenDisplay.cs index e6708093c4..af6d24fc65 100644 --- a/osu.Game/Overlays/OnScreenDisplay.cs +++ b/osu.Game/Overlays/OnScreenDisplay.cs @@ -3,16 +3,14 @@ using System; using System.Collections.Generic; -using osu.Framework.Allocation; using osu.Framework.Configuration; using osu.Framework.Configuration.Tracking; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osuTK; using osu.Framework.Graphics.Transforms; using osu.Framework.Threading; -using osu.Game.Configuration; using osu.Game.Overlays.OSD; +using osuTK; namespace osu.Game.Overlays { @@ -47,13 +45,6 @@ namespace osu.Game.Overlays }; } - [BackgroundDependencyLoader] - private void load(FrameworkConfigManager frameworkConfig, OsuConfigManager osuConfig) - { - BeginTracking(this, frameworkConfig); - BeginTracking(this, osuConfig); - } - private readonly Dictionary<(object, IConfigManager), TrackedSettings> trackedConfigManagers = new Dictionary<(object, IConfigManager), TrackedSettings>(); /// From 804450e707300b51abfc13014d354f9a9a1123ce Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Nov 2020 15:49:45 +0900 Subject: [PATCH 23/30] Remove duplicate instantiation of externalLinkOpener --- osu.Game/OsuGame.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5119f262d5..a4e7214d32 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -662,7 +662,6 @@ namespace osu.Game loadComponentSingleFile(new AccountCreationOverlay(), topMostOverlayContent.Add, true); loadComponentSingleFile(new DialogOverlay(), topMostOverlayContent.Add, true); - loadComponentSingleFile(externalLinkOpener = new ExternalLinkOpener(), topMostOverlayContent.Add); chatOverlay.State.ValueChanged += state => channelManager.HighPollRate.Value = state.NewValue == Visibility.Visible; From 5d55af58182449a73da8aea2c5da743fb86fc589 Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Nov 2020 16:35:48 +0900 Subject: [PATCH 24/30] Fix hitobjects sometimes not fading in completely with HD mod --- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 10 ++++++++-- .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 9 +++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index 025e202666..cf7faca9b9 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -27,17 +27,23 @@ namespace osu.Game.Rulesets.Osu.Mods public override void ApplyToDrawableHitObjects(IEnumerable drawables) { foreach (var d in drawables) - d.ApplyCustomUpdateState += applyFadeInAdjustment; + { + d.HitObjectApplied += applyFadeInAdjustment; + applyFadeInAdjustment(d); + } base.ApplyToDrawableHitObjects(drawables); } - private void applyFadeInAdjustment(DrawableHitObject hitObject, ArmedState state) + private void applyFadeInAdjustment(DrawableHitObject hitObject) { if (!(hitObject is DrawableOsuHitObject d)) return; d.HitObject.TimeFadeIn = d.HitObject.TimePreempt * fade_in_duration_multiplier; + + foreach (var nested in d.NestedHitObjects) + applyFadeInAdjustment(nested); } private double lastSliderHeadFadeOutStartTime; diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 7a4e136553..62709b2900 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -26,8 +26,16 @@ namespace osu.Game.Rulesets.Objects.Drawables [Cached(typeof(DrawableHitObject))] public abstract class DrawableHitObject : SkinReloadableDrawable { + /// + /// Invoked after this 's applied has had its defaults applied. + /// public event Action DefaultsApplied; + /// + /// Invoked after a has been applied to this . + /// + public event Action HitObjectApplied; + /// /// The currently represented by this . /// @@ -192,6 +200,7 @@ namespace osu.Game.Rulesets.Objects.Drawables HitObject.DefaultsApplied += onDefaultsApplied; OnApply(hitObject); + HitObjectApplied?.Invoke(this); // If not loaded, the state update happens in LoadComplete(). Otherwise, the update is scheduled to allow for lifetime updates. if (IsLoaded) From 891218ec6b754ef13165c9e95dc88b64d66d9c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 11 Nov 2020 09:11:33 +0100 Subject: [PATCH 25/30] Inline empty explosion in legacy transformer --- .../Skinning/TaikoLegacySkinTransformer.cs | 2 +- osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs | 6 ------ 2 files changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs index 880af3fbd8..96fb065e79 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/TaikoLegacySkinTransformer.cs @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Taiko.Skinning // suppress the default kiai explosion if the skin brings its own sprites. // the drawable needs to expire as soon as possible to avoid accumulating empty drawables on the playfield. if (hasExplosion.Value) - return KiaiHitExplosion.EmptyExplosion(); + return Drawable.Empty().With(d => d.LifetimeEnd = double.MinValue); return null; diff --git a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs index 326cb23897..20900a9352 100644 --- a/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs +++ b/osu.Game.Rulesets.Taiko/UI/KiaiHitExplosion.cs @@ -43,11 +43,5 @@ namespace osu.Game.Rulesets.Taiko.UI { Child = skinnable = new SkinnableDrawable(new TaikoSkinComponent(TaikoSkinComponents.TaikoExplosionKiai), _ => new DefaultKiaiHitExplosion(hitType)); } - - /// - /// Helper function to use when an explosion is not desired. - /// Lifetime is set to avoid accumulating empty drawables in the parent container. - /// - public static Drawable EmptyExplosion() => Empty().With(d => d.LifetimeEnd = double.MinValue); } } From a08833f3b3cd3b7581be8e95a0b56d03611c8567 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 11 Nov 2020 18:03:04 +0900 Subject: [PATCH 26/30] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index bbe8426316..5078fee1cf 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -52,6 +52,6 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 8f0cc58594..405fb1a6ca 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -26,7 +26,7 @@ - + diff --git a/osu.iOS.props b/osu.iOS.props index f766e0ec03..099ecd8319 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -70,7 +70,7 @@ - + @@ -88,7 +88,7 @@ - + From 90499329bd10e75c81ff946f5195039f196ebdab Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Wed, 11 Nov 2020 18:50:38 +0900 Subject: [PATCH 27/30] Fix frame stable playback not being set correctly --- osu.Game/Rulesets/UI/DrawableRuleset.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/DrawableRuleset.cs b/osu.Game/Rulesets/UI/DrawableRuleset.cs index f6cf836fe7..a36b66d62b 100644 --- a/osu.Game/Rulesets/UI/DrawableRuleset.cs +++ b/osu.Game/Rulesets/UI/DrawableRuleset.cs @@ -77,7 +77,7 @@ namespace osu.Game.Rulesets.UI get => frameStablePlayback; set { - frameStablePlayback = false; + frameStablePlayback = value; if (frameStabilityContainer != null) frameStabilityContainer.FrameStablePlayback = value; } From f753f138c6bb3df154e109d30ee6bbbaaa841257 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 12 Nov 2020 03:11:29 +0300 Subject: [PATCH 28/30] Add counter to most played beatmaps section in user overlay --- .../Historical/PaginatedMostPlayedBeatmapContainer.cs | 4 +++- osu.Game/Users/User.cs | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs index 8f19cd900c..556f3139dd 100644 --- a/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs +++ b/osu.Game/Overlays/Profile/Sections/Historical/PaginatedMostPlayedBeatmapContainer.cs @@ -16,7 +16,7 @@ namespace osu.Game.Overlays.Profile.Sections.Historical public class PaginatedMostPlayedBeatmapContainer : PaginatedContainer { public PaginatedMostPlayedBeatmapContainer(Bindable user) - : base(user, "Most Played Beatmaps", "No records. :(") + : base(user, "Most Played Beatmaps", "No records. :(", CounterVisibilityState.AlwaysVisible) { ItemsPerPage = 5; } @@ -27,6 +27,8 @@ namespace osu.Game.Overlays.Profile.Sections.Historical ItemsContainer.Direction = FillDirection.Vertical; } + protected override int GetCount(User user) => user.BeatmapPlaycountsCount; + protected override APIRequest> CreateRequest() => new GetUserMostPlayedBeatmapsRequest(User.Value.Id, VisiblePages++, ItemsPerPage); diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs index 2a76a963e1..d7e78d5b35 100644 --- a/osu.Game/Users/User.cs +++ b/osu.Game/Users/User.cs @@ -144,6 +144,9 @@ namespace osu.Game.Users [JsonProperty(@"scores_first_count")] public int ScoresFirstCount; + [JsonProperty(@"beatmap_playcounts_count")] + public int BeatmapPlaycountsCount; + [JsonProperty] private string[] playstyle { From 25af091409142d702041f5a5bd15bea093c78897 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 12 Nov 2020 17:03:42 +0900 Subject: [PATCH 29/30] Fix storyboard animations of very old beatmaps playing too slow Closes https://github.com/ppy/osu/issues/10772. --- osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs | 2 -- osu.Game/Beatmaps/Formats/LegacyDecoder.cs | 2 ++ osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs | 12 +++++++++--- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 442be6e837..37ab489da5 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -16,8 +16,6 @@ namespace osu.Game.Beatmaps.Formats { public class LegacyBeatmapDecoder : LegacyDecoder { - public const int LATEST_VERSION = 14; - private Beatmap beatmap; private ConvertHitObjectParser parser; diff --git a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs index 7b377e481f..de4dc8cdc8 100644 --- a/osu.Game/Beatmaps/Formats/LegacyDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyDecoder.cs @@ -16,6 +16,8 @@ namespace osu.Game.Beatmaps.Formats public abstract class LegacyDecoder : Decoder where T : new() { + public const int LATEST_VERSION = 14; + protected readonly int FormatVersion; protected LegacyDecoder(int version) diff --git a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs index 8d8ca523d5..9a244c8bb2 100644 --- a/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyStoryboardDecoder.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using osu.Framework.Graphics; using osu.Framework.Utils; using osu.Game.Beatmaps.Legacy; @@ -23,15 +24,15 @@ namespace osu.Game.Beatmaps.Formats private readonly Dictionary variables = new Dictionary(); - public LegacyStoryboardDecoder() - : base(0) + public LegacyStoryboardDecoder(int version = LATEST_VERSION) + : base(version) { } public static void Register() { // note that this isn't completely correct - AddDecoder(@"osu file format v", m => new LegacyStoryboardDecoder()); + AddDecoder(@"osu file format v", m => new LegacyStoryboardDecoder(Parsing.ParseInt(m.Split('v').Last()))); AddDecoder(@"[Events]", m => new LegacyStoryboardDecoder()); SetFallbackDecoder(() => new LegacyStoryboardDecoder()); } @@ -133,6 +134,11 @@ namespace osu.Game.Beatmaps.Formats var y = Parsing.ParseFloat(split[5], Parsing.MAX_COORDINATE_VALUE); var frameCount = Parsing.ParseInt(split[6]); var frameDelay = Parsing.ParseDouble(split[7]); + + if (FormatVersion < 6) + // this is random as hell but taken straight from osu-stable. + frameDelay = Math.Round(0.015 * frameDelay) * 1.186 * (1000 / 60f); + var loopType = split.Length > 8 ? (AnimationLoopType)Enum.Parse(typeof(AnimationLoopType), split[8]) : AnimationLoopType.LoopForever; storyboardSprite = new StoryboardAnimation(path, origin, new Vector2(x, y), frameCount, frameDelay, loopType); storyboard.GetLayer(layer).Add(storyboardSprite); From de31c1ea0cd3df825d65fae00dc6b644d3f16a9f Mon Sep 17 00:00:00 2001 From: smoogipoo Date: Thu, 12 Nov 2020 17:58:39 +0900 Subject: [PATCH 30/30] Fix skinfallback test crashing on repeats --- osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs index 075bf314bc..856bfd7e80 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSkinFallbacks.cs @@ -125,6 +125,9 @@ namespace osu.Game.Rulesets.Osu.Tests { if (!enabled) return null; + if (component is OsuSkinComponent osuComponent && osuComponent.Component == OsuSkinComponents.SliderBody) + return null; + return new OsuSpriteText { Text = identifier,