diff --git a/osu.Android.props b/osu.Android.props
index a41c1a5864..f62ba48953 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -51,7 +51,7 @@
-
-
+
+
diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs
index 18cc300ff9..f009c10a9c 100644
--- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs
+++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Catch.Objects;
@@ -23,19 +22,19 @@ namespace osu.Game.Rulesets.Catch.Beatmaps
{
Name = @"Fruit Count",
Content = fruits.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles),
},
new BeatmapStatistic
{
Name = @"Juice Stream Count",
Content = juiceStreams.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
},
new BeatmapStatistic
{
Name = @"Banana Shower Count",
Content = bananaShowers.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners),
}
};
}
diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs
index dc24a344e9..d1d5adea75 100644
--- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs
+++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmap.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Mania.Objects;
using osu.Game.Rulesets.Mania.UI;
@@ -41,14 +40,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
new BeatmapStatistic
{
Name = @"Note Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles),
Content = notes.ToString(),
- Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Hold Note Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
Content = holdnotes.ToString(),
- Icon = FontAwesome.Regular.Circle
},
};
}
diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs
index 491d82b89e..2d3cc3c103 100644
--- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs
+++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmap.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu.Objects;
@@ -23,19 +22,19 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
{
Name = @"Circle Count",
Content = circles.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles),
},
new BeatmapStatistic
{
Name = @"Slider Count",
Content = sliders.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
},
new BeatmapStatistic
{
Name = @"Spinner Count",
Content = spinners.ToString(),
- Icon = FontAwesome.Regular.Circle
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners),
}
};
}
diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs
index b595f43fbb..16a0726c8c 100644
--- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs
+++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmap.cs
@@ -3,7 +3,6 @@
using System.Collections.Generic;
using System.Linq;
-using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Taiko.Objects;
@@ -22,20 +21,20 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
new BeatmapStatistic
{
Name = @"Hit Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Circles),
Content = hits.ToString(),
- Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Drumroll Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Sliders),
Content = drumrolls.ToString(),
- Icon = FontAwesome.Regular.Circle
},
new BeatmapStatistic
{
Name = @"Swell Count",
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Spinners),
Content = swells.ToString(),
- Icon = FontAwesome.Regular.Circle
}
};
}
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs
index 3d225aa0a9..faea32f90f 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchSongSelect.cs
@@ -162,6 +162,28 @@ namespace osu.Game.Tests.Visual.Multiplayer
AddAssert("item 2 has rate 2", () => Precision.AlmostEquals(2, ((OsuModDoubleTime)Room.Playlist.Last().RequiredMods[0]).SpeedChange.Value));
}
+ ///
+ /// Tests that the global mod instances are not retained by the rooms, as global mod instances are retained and re-used by the mod select overlay.
+ ///
+ [Test]
+ public void TestGlobalModInstancesNotRetained()
+ {
+ OsuModDoubleTime mod = null;
+
+ AddStep("set dt mod and store", () =>
+ {
+ SelectedMods.Value = new[] { new OsuModDoubleTime() };
+
+ // Mod select overlay replaces our mod.
+ mod = (OsuModDoubleTime)SelectedMods.Value[0];
+ });
+
+ AddStep("create item", () => songSelect.BeatmapDetails.CreateNewItem());
+
+ AddStep("change stored mod rate", () => mod.SpeedChange.Value = 2);
+ AddAssert("item has rate 1.5", () => Precision.AlmostEquals(1.5, ((OsuModDoubleTime)Room.Playlist.First().RequiredMods[0]).SpeedChange.Value));
+ }
+
private class TestMatchSongSelect : MatchSongSelect
{
public new MatchBeatmapDetailArea BeatmapDetails => (MatchBeatmapDetailArea)base.BeatmapDetails;
diff --git a/osu.Game/Beatmaps/BeatmapStatistic.cs b/osu.Game/Beatmaps/BeatmapStatistic.cs
index 0745ec5222..9d87a20d60 100644
--- a/osu.Game/Beatmaps/BeatmapStatistic.cs
+++ b/osu.Game/Beatmaps/BeatmapStatistic.cs
@@ -1,14 +1,31 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
+using osu.Framework.Graphics;
using osu.Framework.Graphics.Sprites;
+using osuTK;
namespace osu.Game.Beatmaps
{
public class BeatmapStatistic
{
- public IconUsage Icon;
+ [Obsolete("Use CreateIcon instead")] // can be removed 20210203
+ public IconUsage Icon = FontAwesome.Regular.QuestionCircle;
+
+ ///
+ /// A function to create the icon for display purposes. Use default icons available via whenever possible for conformity.
+ ///
+ public Func CreateIcon;
+
public string Content;
public string Name;
+
+ public BeatmapStatistic()
+ {
+#pragma warning disable 618
+ CreateIcon = () => new SpriteIcon { Icon = Icon, Scale = new Vector2(0.7f) };
+#pragma warning restore 618
+ }
}
}
diff --git a/osu.Game/Beatmaps/BeatmapStatisticIcon.cs b/osu.Game/Beatmaps/BeatmapStatisticIcon.cs
new file mode 100644
index 0000000000..181fb540df
--- /dev/null
+++ b/osu.Game/Beatmaps/BeatmapStatisticIcon.cs
@@ -0,0 +1,43 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using Humanizer;
+using osu.Framework.Allocation;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Graphics.Textures;
+
+namespace osu.Game.Beatmaps
+{
+ ///
+ /// A default implementation of an icon used to represent beatmap statistics.
+ ///
+ public class BeatmapStatisticIcon : Sprite
+ {
+ private readonly BeatmapStatisticsIconType iconType;
+
+ public BeatmapStatisticIcon(BeatmapStatisticsIconType iconType)
+ {
+ this.iconType = iconType;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Texture = textures.Get($"Icons/BeatmapDetails/{iconType.ToString().Kebaberize()}");
+ }
+ }
+
+ public enum BeatmapStatisticsIconType
+ {
+ Accuracy,
+ ApproachRate,
+ Bpm,
+ Circles,
+ HpDrain,
+ Length,
+ OverallDifficulty,
+ Size,
+ Sliders,
+ Spinners,
+ }
+}
diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs
index 17877a69a5..31bd80d6f3 100644
--- a/osu.Game/Overlays/MusicController.cs
+++ b/osu.Game/Overlays/MusicController.cs
@@ -279,6 +279,11 @@ namespace osu.Game.Overlays
private void changeBeatmap(WorkingBeatmap newWorking)
{
+ // This method can potentially be triggered multiple times as it is eagerly fired in next() / prev() to ensure correct execution order
+ // (changeBeatmap must be called before consumers receive the bindable changed event, which is not the case when the local beatmap bindable is updated directly).
+ if (newWorking == current)
+ return;
+
var lastWorking = current;
TrackChangeDirection direction = TrackChangeDirection.None;
diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs
index aca507f20a..e5cebd28e2 100644
--- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs
+++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs
@@ -1,12 +1,14 @@
// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
+using System;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Overlays.Settings.Sections.Gameplay;
using osu.Game.Rulesets;
using System.Linq;
using osu.Framework.Graphics.Sprites;
+using osu.Framework.Logging;
namespace osu.Game.Overlays.Settings.Sections
{
@@ -34,9 +36,17 @@ namespace osu.Game.Overlays.Settings.Sections
{
foreach (Ruleset ruleset in rulesets.AvailableRulesets.Select(info => info.CreateInstance()))
{
- SettingsSubsection section = ruleset.CreateSettings();
- if (section != null)
- Add(section);
+ try
+ {
+ SettingsSubsection section = ruleset.CreateSettings();
+
+ if (section != null)
+ Add(section);
+ }
+ catch (Exception e)
+ {
+ Logger.Error(e, "Failed to load ruleset settings");
+ }
}
}
}
diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
index ad977c70b5..2a3eb8c67a 100644
--- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs
+++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs
@@ -318,14 +318,14 @@ namespace osu.Game.Screens.Select
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "Length",
- Icon = FontAwesome.Regular.Clock,
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length),
Content = TimeSpan.FromMilliseconds(b.BeatmapInfo.Length).ToString(@"m\:ss"),
}));
labels.Add(new InfoLabel(new BeatmapStatistic
{
Name = "BPM",
- Icon = FontAwesome.Regular.Circle,
+ CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Bpm),
Content = getBPMRange(b),
}));
@@ -418,10 +418,18 @@ namespace osu.Game.Screens.Select
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
- Scale = new Vector2(0.8f),
Colour = Color4Extensions.FromHex(@"f7dd55"),
- Icon = statistic.Icon,
+ Icon = FontAwesome.Regular.Circle,
+ Size = new Vector2(0.8f)
},
+ statistic.CreateIcon().With(i =>
+ {
+ i.Anchor = Anchor.Centre;
+ i.Origin = Anchor.Centre;
+ i.RelativeSizeAxes = Axes.Both;
+ i.Colour = Color4Extensions.FromHex(@"f7dd55");
+ i.Size = new Vector2(0.64f);
+ }),
}
},
new OsuSpriteText
diff --git a/osu.Game/Screens/Select/MatchSongSelect.cs b/osu.Game/Screens/Select/MatchSongSelect.cs
index 96a48fa3ac..8692833a21 100644
--- a/osu.Game/Screens/Select/MatchSongSelect.cs
+++ b/osu.Game/Screens/Select/MatchSongSelect.cs
@@ -76,9 +76,7 @@ namespace osu.Game.Screens.Select
item.Ruleset.Value = Ruleset.Value;
item.RequiredMods.Clear();
- item.RequiredMods.AddRange(Mods.Value);
-
- Mods.Value = Mods.Value.Select(m => m.CreateCopy()).ToArray();
+ item.RequiredMods.AddRange(Mods.Value.Select(m => m.CreateCopy()));
}
}
}
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index d79806883e..526dca421a 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -24,8 +24,8 @@
-
-
+
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 16a8a1acb7..0cbbba70b9 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -70,8 +70,8 @@
-
-
+
+
@@ -80,7 +80,7 @@
-
+