From da827f0cd61806f8a33760c8c247e0e8139f7cc4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Apr 2025 16:37:55 +0900 Subject: [PATCH 1/8] Adjust mod icon test scene to show overlapping versions too --- .../Visual/UserInterface/TestSceneModIcon.cs | 106 +++++++++++++----- 1 file changed, 75 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs index 11cd122c99..b6d4836316 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs @@ -12,22 +12,66 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.UI; +using osu.Game.Screens.Play.HUD; namespace osu.Game.Tests.Visual.UserInterface { public partial class TestSceneModIcon : OsuTestScene { + private FillFlowContainer spreadOutFlow = null!; + private ModDisplay modDisplay = null!; + + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create flows", () => + { + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Relative, 0.5f), + new Dimension(GridSizeMode.Relative, 0.5f), + }, + Content = new[] + { + new Drawable[] + { + modDisplay = new ModDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + } + }, + new Drawable[] + { + spreadOutFlow = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Full, + } + } + } + }; + }); + } + + private void addRange(IEnumerable mods) + { + spreadOutFlow.AddRange(mods.Select(m => new ModIcon(m))); + modDisplay.Current.Value = modDisplay.Current.Value.Concat(mods.OfType()).ToList(); + } + [Test] public void TestShowAllMods() { AddStep("create mod icons", () => { - Child = new FillFlowContainer - { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Full, - ChildrenEnumerable = Ruleset.Value.CreateInstance().CreateAllMods().Select(m => new ModIcon(m)), - }; + addRange(Ruleset.Value.CreateInstance().CreateAllMods()); }); AddStep("toggle selected", () => @@ -42,26 +86,22 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create mod icons", () => { - Child = new FillFlowContainer + var rateAdjustMods = Ruleset.Value.CreateInstance().CreateAllMods() + .OfType(); + + addRange(rateAdjustMods.SelectMany(m => { - RelativeSizeAxes = Axes.Both, - Direction = FillDirection.Full, - ChildrenEnumerable = Ruleset.Value.CreateInstance().CreateAllMods() - .OfType() - .SelectMany(m => - { - List icons = new List { new ModIcon(m) }; + List mods = new List { m }; - for (double i = m.SpeedChange.MinValue; i < m.SpeedChange.MaxValue; i += m.SpeedChange.Precision * 10) - { - m = (ModRateAdjust)m.DeepClone(); - m.SpeedChange.Value = i; - icons.Add(new ModIcon(m)); - } + for (double i = m.SpeedChange.MinValue; i < m.SpeedChange.MaxValue; i += m.SpeedChange.Precision * 10) + { + m = (ModRateAdjust)m.DeepClone(); + m.SpeedChange.Value = i; + mods.Add(m); + } - return icons; - }), - }; + return mods; + })); }); AddStep("adjust rates", () => @@ -81,21 +121,25 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestChangeModType() { - ModIcon icon = null!; - - AddStep("create mod icon", () => Child = icon = new ModIcon(new OsuModDoubleTime())); - AddStep("change mod", () => icon.Mod = new OsuModEasy()); + AddStep("create mod icon", () => addRange([new OsuModDoubleTime()])); + AddStep("change mod", () => + { + foreach (var modIcon in this.ChildrenOfType()) + modIcon.Mod = new OsuModEasy(); + }); } [Test] public void TestInterfaceModType() { - ModIcon icon = null!; - var ruleset = new OsuRuleset(); - AddStep("create mod icon", () => Child = icon = new ModIcon(ruleset.AllMods.First(m => m.Acronym == "DT"))); - AddStep("change mod", () => icon.Mod = ruleset.AllMods.First(m => m.Acronym == "EZ")); + AddStep("create mod icon", () => addRange([ruleset.AllMods.First(m => m.Acronym == "DT")])); + AddStep("change mod", () => + { + foreach (var modIcon in this.ChildrenOfType()) + modIcon.Mod = ruleset.AllMods.First(m => m.Acronym == "EZ"); + }); } } } From c16514274d7f3bcfb02d79793280291613539e70 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Apr 2025 16:58:16 +0900 Subject: [PATCH 2/8] Show singular difficulty adjust modifications inline in mod icons --- .../Mods/CatchModDifficultyAdjust.cs | 30 +++++++++++++++++++ .../Mods/OsuModDifficultyAdjust.cs | 30 +++++++++++++++++++ .../Mods/TaikoModDifficultyAdjust.cs | 28 +++++++++++++++++ .../Visual/UserInterface/TestSceneModIcon.cs | 25 ++++++++++++++++ .../Extensions/NumberFormattingExtensions.cs | 2 +- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 28 +++++++++++++++++ 6 files changed, 142 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index 1312f45cdc..856989a685 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Mods; @@ -36,6 +37,35 @@ namespace osu.Game.Rulesets.Catch.Mods [SettingSource("Spicy Patterns", "Adjust the patterns as if Hard Rock is enabled.")] public BindableBool HardRockOffsets { get; } = new BindableBool(); + public override int AdjustedSettingsCount + { + get + { + int count = base.AdjustedSettingsCount; + if (!ApproachRate.IsDefault) count++; + if (!CircleSize.IsDefault) count++; + return count; + } + } + + public override string ExtendedIconInformation + { + get + { + if (AdjustedSettingsCount != 1) + return string.Empty; + + if (!CircleSize.IsDefault) return format("CS", CircleSize); + if (!ApproachRate.IsDefault) return format("AR", ApproachRate); + if (!OverallDifficulty.IsDefault) return format("OD", OverallDifficulty); + if (!DrainRate.IsDefault) return format("HP", DrainRate); + + return string.Empty; + + string format(string acronym, DifficultyBindable bindable) => $"{acronym}{bindable.Value!.Value.ToStandardFormattedString(1)}"; + } + } + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription { get diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 77e9aeb123..357a971c0f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -7,6 +7,7 @@ using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Objects; @@ -36,6 +37,35 @@ namespace osu.Game.Rulesets.Osu.Mods ReadCurrentFromDifficulty = diff => diff.ApproachRate, }; + public override int AdjustedSettingsCount + { + get + { + int count = base.AdjustedSettingsCount; + if (!ApproachRate.IsDefault) count++; + if (!CircleSize.IsDefault) count++; + return count; + } + } + + public override string ExtendedIconInformation + { + get + { + if (AdjustedSettingsCount != 1) + return string.Empty; + + if (!CircleSize.IsDefault) return format("CS", CircleSize); + if (!ApproachRate.IsDefault) return format("AR", ApproachRate); + if (!OverallDifficulty.IsDefault) return format("OD", OverallDifficulty); + if (!DrainRate.IsDefault) return format("HP", DrainRate); + + return string.Empty; + + string format(string acronym, DifficultyBindable bindable) => $"{acronym}{bindable.Value!.Value.ToStandardFormattedString(1)}"; + } + } + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription { get diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 000736e9f7..628592fe51 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Extensions; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Taiko.Mods @@ -20,6 +21,33 @@ namespace osu.Game.Rulesets.Taiko.Mods ReadCurrentFromDifficulty = _ => 1, }; + public override int AdjustedSettingsCount + { + get + { + int count = base.AdjustedSettingsCount; + if (!ScrollSpeed.IsDefault) count++; + return count; + } + } + + public override string ExtendedIconInformation + { + get + { + if (AdjustedSettingsCount != 1) + return string.Empty; + + if (!ScrollSpeed.IsDefault) return format("SC", ScrollSpeed); + if (!OverallDifficulty.IsDefault) return format("OD", OverallDifficulty); + if (!DrainRate.IsDefault) return format("HP", DrainRate); + + return string.Empty; + + string format(string acronym, DifficultyBindable bindable) => $"{acronym}{bindable.Value!.Value.ToStandardFormattedString(1)}"; + } + } + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription { get diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs index b6d4836316..c47a6fd610 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs @@ -141,5 +141,30 @@ namespace osu.Game.Tests.Visual.UserInterface modIcon.Mod = ruleset.AllMods.First(m => m.Acronym == "EZ"); }); } + + [Test] + public void TestDifficultyAdjust() + { + AddStep("create icons", () => + { + addRange([ + new OsuModDifficultyAdjust + { + CircleSize = { Value = 8 } + }, + new OsuModDifficultyAdjust + { + CircleSize = { Value = 5.5f } + }, + new OsuModDifficultyAdjust + { + CircleSize = { Value = 8 }, + ApproachRate = { Value = 8 }, + OverallDifficulty = { Value = 8 }, + DrainRate = { Value = 8 }, + } + ]); + }); + } } } diff --git a/osu.Game/Extensions/NumberFormattingExtensions.cs b/osu.Game/Extensions/NumberFormattingExtensions.cs index 618b086a5b..33252448fc 100644 --- a/osu.Game/Extensions/NumberFormattingExtensions.cs +++ b/osu.Game/Extensions/NumberFormattingExtensions.cs @@ -17,7 +17,7 @@ namespace osu.Game.Extensions /// The maximum number of decimals to be considered in the original value. /// Whether the output should be a percentage. For integer types, 0-100 is mapped to 0-100%; for other types 0-1 is mapped to 0-100%. /// The formatted output. - public static string ToStandardFormattedString(this T value, int maxDecimalDigits, bool asPercentage) where T : struct, INumber, IMinMaxValue + public static string ToStandardFormattedString(this T value, int maxDecimalDigits, bool asPercentage = false) where T : struct, INumber, IMinMaxValue { double floatValue = double.CreateTruncating(value); diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 79fc918487..857527062f 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -8,6 +8,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Configuration; +using osu.Game.Extensions; namespace osu.Game.Rulesets.Mods { @@ -67,6 +68,33 @@ namespace osu.Game.Rulesets.Mods } } + public virtual int AdjustedSettingsCount + { + get + { + int count = 0; + if (!DrainRate.IsDefault) count++; + if (!OverallDifficulty.IsDefault) count++; + return count; + } + } + + public override string ExtendedIconInformation + { + get + { + if (AdjustedSettingsCount != 1) + return string.Empty; + + if (!OverallDifficulty.IsDefault) return format("OD", OverallDifficulty); + if (!DrainRate.IsDefault) return format("HP", DrainRate); + + return string.Empty; + + string format(string acronym, DifficultyBindable bindable) => $"{acronym}{bindable.Value!.Value.ToStandardFormattedString(1)}"; + } + } + public override IEnumerable<(LocalisableString setting, LocalisableString value)> SettingDescription { get From 1e8d9b3482e585207d2d5806bdf12f7f2f5892c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 28 Apr 2025 17:24:24 +0900 Subject: [PATCH 3/8] Show marker when settings are adjusted --- .../Visual/UserInterface/TestSceneModIcon.cs | 17 ++++++++- osu.Game/Rulesets/Mods/IMod.cs | 26 ++++++++++++++ osu.Game/Rulesets/UI/ModIcon.cs | 35 ++++++++++++++++++- 3 files changed, 76 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs index c47a6fd610..c8283d0956 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModIcon.cs @@ -71,7 +71,22 @@ namespace osu.Game.Tests.Visual.UserInterface { AddStep("create mod icons", () => { - addRange(Ruleset.Value.CreateInstance().CreateAllMods()); + addRange(Ruleset.Value.CreateInstance().CreateAllMods().Select(m => + { + if (m is OsuModFlashlight fl) + fl.FollowDelay.Value = 1245; + + if (m is OsuModDaycore dc) + dc.SpeedChange.Value = 0.74f; + + if (m is OsuModDifficultyAdjust da) + da.CircleSize.Value = 8.2f; + + if (m is ModAdaptiveSpeed ad) + ad.AdjustPitch.Value = false; + + return m; + })); }); AddStep("toggle selected", () => diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index 5d4cc5fd12..d4c51b1dfb 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -2,8 +2,10 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Configuration; namespace osu.Game.Rulesets.Mods { @@ -81,5 +83,29 @@ namespace osu.Game.Rulesets.Mods /// Create a fresh instance based on this mod. /// Mod CreateInstance() => (Mod)Activator.CreateInstance(GetType())!; + + /// + /// Whether any user adjustable setting attached to this mod has a non-default value. + /// + bool HasNonDefaultSettings + { + get + { + bool hasAdjustments = false; + + foreach (var (_, property) in this.GetSettingsSourceProperties()) + { + var bindable = (IBindable)property.GetValue(this)!; + + if (!bindable.IsDefault) + { + hasAdjustments = true; + break; + } + } + + return hasAdjustments; + } + } } } diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index ee0103a8e5..d42e185784 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -8,6 +8,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Utils; @@ -81,6 +82,8 @@ namespace osu.Game.Rulesets.UI private Container extendedContent = null!; + private Drawable adjustmentMarker = null!; + private ModSettingChangeTracker? modSettingsChangeTracker; /// @@ -139,7 +142,7 @@ namespace osu.Game.Rulesets.UI Origin = Anchor.CentreLeft, Name = "main content", Size = MOD_ICON_SIZE, - Children = new Drawable[] + Children = new[] { background = new Sprite { @@ -165,6 +168,31 @@ namespace osu.Game.Rulesets.UI Size = new Vector2(45), Icon = FontAwesome.Solid.Question }, + adjustmentMarker = new Container + { + Size = new Vector2(20), + Origin = Anchor.Centre, + Position = new Vector2(64, 14), + Children = new Drawable[] + { + new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colours.YellowLight, + RelativeSizeAxes = Axes.Both, + }, + new SpriteIcon + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.Cog, + Colour = colours.YellowDark, + RelativeSizeAxes = Axes.Both, + Size = new Vector2(0.7f), + } + } + }, } }, }; @@ -207,6 +235,11 @@ namespace osu.Game.Rulesets.UI backgroundColour = colours.ForModType(value.Type); updateColour(); + if (mod.HasNonDefaultSettings) + adjustmentMarker.Show(); + else + adjustmentMarker.Hide(); + updateExtendedInformation(); } From 713fbfb2c89cb6919d72d9ed9dc8768ef117c4fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Apr 2025 12:48:07 +0900 Subject: [PATCH 4/8] Adjust colours to match icon better --- osu.Game/Rulesets/UI/ModIcon.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index d42e185784..bfd5d63268 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -84,6 +84,9 @@ namespace osu.Game.Rulesets.UI private Drawable adjustmentMarker = null!; + private Circle cogBackground = null!; + private SpriteIcon cog = null!; + private ModSettingChangeTracker? modSettingsChangeTracker; /// @@ -175,21 +178,19 @@ namespace osu.Game.Rulesets.UI Position = new Vector2(64, 14), Children = new Drawable[] { - new Circle + cogBackground = new Circle { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Colour = colours.YellowLight, RelativeSizeAxes = Axes.Both, }, - new SpriteIcon + cog = new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.Cog, - Colour = colours.YellowDark, RelativeSizeAxes = Axes.Both, - Size = new Vector2(0.7f), + Size = new Vector2(0.6f), } } }, @@ -254,6 +255,8 @@ namespace osu.Game.Rulesets.UI private void updateColour() { modAcronym.Colour = modIcon.Colour = Interpolation.ValueAt(0.1f, Colour4.Black, backgroundColour, 0, 1); + cogBackground.Colour = Interpolation.ValueAt(0.1f, Colour4.Black, backgroundColour, 0, 1); + cog.Colour = backgroundColour; extendedText.Colour = background.Colour = Selected.Value ? backgroundColour.Lighten(0.2f) : backgroundColour; extendedBackground.Colour = Selected.Value ? backgroundColour.Darken(2.4f) : backgroundColour.Darken(2.8f); From 14d0565194003999e1a40bc135f868c9727765b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Apr 2025 12:49:39 +0900 Subject: [PATCH 5/8] Add xmldoc note explaining new flag is instantaneous state --- osu.Game/Rulesets/Mods/IMod.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Mods/IMod.cs b/osu.Game/Rulesets/Mods/IMod.cs index d4c51b1dfb..08e64c4aa9 100644 --- a/osu.Game/Rulesets/Mods/IMod.cs +++ b/osu.Game/Rulesets/Mods/IMod.cs @@ -87,6 +87,10 @@ namespace osu.Game.Rulesets.Mods /// /// Whether any user adjustable setting attached to this mod has a non-default value. /// + /// + /// This returns the instantaneous state of this mod. It may change over time. + /// For tracking changes on a dynamic display, make sure to setup a . + /// bool HasNonDefaultSettings { get From babccca2bbe102328142349268db3777ee9df6d9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Apr 2025 12:58:40 +0900 Subject: [PATCH 6/8] Don't bother with localised implementation of `AdjustedSettingsCount` I was avoiding using reflection to save on overheads, but it's probably not worth it. --- .../Mods/CatchModDifficultyAdjust.cs | 13 +----------- .../Mods/OsuModDifficultyAdjust.cs | 13 +----------- .../Mods/TaikoModDifficultyAdjust.cs | 12 +---------- osu.Game/Rulesets/Mods/Mod.cs | 21 +++++++++++++++++++ osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 13 +----------- 5 files changed, 25 insertions(+), 47 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs index 856989a685..c300afa79f 100644 --- a/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Catch/Mods/CatchModDifficultyAdjust.cs @@ -37,22 +37,11 @@ namespace osu.Game.Rulesets.Catch.Mods [SettingSource("Spicy Patterns", "Adjust the patterns as if Hard Rock is enabled.")] public BindableBool HardRockOffsets { get; } = new BindableBool(); - public override int AdjustedSettingsCount - { - get - { - int count = base.AdjustedSettingsCount; - if (!ApproachRate.IsDefault) count++; - if (!CircleSize.IsDefault) count++; - return count; - } - } - public override string ExtendedIconInformation { get { - if (AdjustedSettingsCount != 1) + if (UserAdjustedSettingsCount != 1) return string.Empty; if (!CircleSize.IsDefault) return format("CS", CircleSize); diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs index 357a971c0f..1d94ac6335 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDifficultyAdjust.cs @@ -37,22 +37,11 @@ namespace osu.Game.Rulesets.Osu.Mods ReadCurrentFromDifficulty = diff => diff.ApproachRate, }; - public override int AdjustedSettingsCount - { - get - { - int count = base.AdjustedSettingsCount; - if (!ApproachRate.IsDefault) count++; - if (!CircleSize.IsDefault) count++; - return count; - } - } - public override string ExtendedIconInformation { get { - if (AdjustedSettingsCount != 1) + if (UserAdjustedSettingsCount != 1) return string.Empty; if (!CircleSize.IsDefault) return format("CS", CircleSize); diff --git a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs index 628592fe51..57b57555c2 100644 --- a/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs +++ b/osu.Game.Rulesets.Taiko/Mods/TaikoModDifficultyAdjust.cs @@ -21,21 +21,11 @@ namespace osu.Game.Rulesets.Taiko.Mods ReadCurrentFromDifficulty = _ => 1, }; - public override int AdjustedSettingsCount - { - get - { - int count = base.AdjustedSettingsCount; - if (!ScrollSpeed.IsDefault) count++; - return count; - } - } - public override string ExtendedIconInformation { get { - if (AdjustedSettingsCount != 1) + if (UserAdjustedSettingsCount != 1) return string.Empty; if (!ScrollSpeed.IsDefault) return format("SC", ScrollSpeed); diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 727db913e2..56a4aa7a50 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -75,6 +75,27 @@ namespace osu.Game.Rulesets.Mods } } + /// + /// The number of settings on this mod instance which have been adjusted by the user from their default values. + /// + public int UserAdjustedSettingsCount + { + get + { + int count = 0; + + foreach (var (_, property) in this.GetSettingsSourceProperties()) + { + var bindable = (IBindable)property.GetValue(this)!; + + if (!bindable.IsDefault) + count++; + } + + return count; + } + } + /// /// The score multiplier of this mod. /// diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 857527062f..0c1a4ab589 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -68,22 +68,11 @@ namespace osu.Game.Rulesets.Mods } } - public virtual int AdjustedSettingsCount - { - get - { - int count = 0; - if (!DrainRate.IsDefault) count++; - if (!OverallDifficulty.IsDefault) count++; - return count; - } - } - public override string ExtendedIconInformation { get { - if (AdjustedSettingsCount != 1) + if (UserAdjustedSettingsCount != 1) return string.Empty; if (!OverallDifficulty.IsDefault) return format("OD", OverallDifficulty); From 0c5193ba59fda2099f9b30908bc756aaa5747168 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Apr 2025 13:04:50 +0900 Subject: [PATCH 7/8] Fix adjustment marker not updating when settings' states change --- osu.Game/Rulesets/UI/ModIcon.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Rulesets/UI/ModIcon.cs b/osu.Game/Rulesets/UI/ModIcon.cs index bfd5d63268..d3f04e7e74 100644 --- a/osu.Game/Rulesets/UI/ModIcon.cs +++ b/osu.Game/Rulesets/UI/ModIcon.cs @@ -236,11 +236,6 @@ namespace osu.Game.Rulesets.UI backgroundColour = colours.ForModType(value.Type); updateColour(); - if (mod.HasNonDefaultSettings) - adjustmentMarker.Show(); - else - adjustmentMarker.Hide(); - updateExtendedInformation(); } @@ -250,6 +245,11 @@ namespace osu.Game.Rulesets.UI extendedContent.Alpha = showExtended ? 1 : 0; extendedText.Text = mod.ExtendedIconInformation; + + if (mod.HasNonDefaultSettings) + adjustmentMarker.Show(); + else + adjustmentMarker.Hide(); } private void updateColour() From a39773747653936aad2e8d441de96871e8aed268 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 30 Apr 2025 16:04:26 +0900 Subject: [PATCH 8/8] Move `UserAdjustedSettingsCount` local to `ModDifficultyAdjust` in absence of other usages --- osu.Game/Rulesets/Mods/Mod.cs | 21 ------------------- osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs | 21 +++++++++++++++++++ 2 files changed, 21 insertions(+), 21 deletions(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 56a4aa7a50..727db913e2 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -75,27 +75,6 @@ namespace osu.Game.Rulesets.Mods } } - /// - /// The number of settings on this mod instance which have been adjusted by the user from their default values. - /// - public int UserAdjustedSettingsCount - { - get - { - int count = 0; - - foreach (var (_, property) in this.GetSettingsSourceProperties()) - { - var bindable = (IBindable)property.GetValue(this)!; - - if (!bindable.IsDefault) - count++; - } - - return count; - } - } - /// /// The score multiplier of this mod. /// diff --git a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs index 0c1a4ab589..15ce583413 100644 --- a/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs +++ b/osu.Game/Rulesets/Mods/ModDifficultyAdjust.cs @@ -111,5 +111,26 @@ namespace osu.Game.Rulesets.Mods if (DrainRate.Value != null) difficulty.DrainRate = DrainRate.Value.Value; if (OverallDifficulty.Value != null) difficulty.OverallDifficulty = OverallDifficulty.Value.Value; } + + /// + /// The number of settings on this mod instance which have been adjusted by the user from their default values. + /// + protected int UserAdjustedSettingsCount + { + get + { + int count = 0; + + foreach (var (_, property) in this.GetSettingsSourceProperties()) + { + var bindable = (IBindable)property.GetValue(this)!; + + if (!bindable.IsDefault) + count++; + } + + return count; + } + } } }