diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
index edc1696456..f420ad976b 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs
@@ -21,6 +21,8 @@ namespace osu.Game.Tests.Visual.Online
protected override bool UseOnlineAPI => true;
+ private int nextBeatmapSetId = 1;
+
public TestSceneBeatmapSetOverlay()
{
Add(overlay = new TestBeatmapSetOverlay());
@@ -240,12 +242,23 @@ namespace osu.Game.Tests.Visual.Online
{
AddStep("show explicit map", () =>
{
- var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
+ var beatmapSet = getBeatmapSet();
beatmapSet.OnlineInfo.HasExplicitContent = true;
overlay.ShowBeatmapSet(beatmapSet);
});
}
+ [Test]
+ public void TestFeaturedBeatmap()
+ {
+ AddStep("show featured map", () =>
+ {
+ var beatmapSet = getBeatmapSet();
+ beatmapSet.OnlineInfo.TrackId = 1;
+ overlay.ShowBeatmapSet(beatmapSet);
+ });
+ }
+
[Test]
public void TestHide()
{
@@ -308,6 +321,14 @@ namespace osu.Game.Tests.Visual.Online
};
}
+ private BeatmapSetInfo getBeatmapSet()
+ {
+ var beatmapSet = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
+ // Make sure the overlay is reloaded (see `BeatmapSetInfo.Equals`).
+ beatmapSet.OnlineBeatmapSetID = nextBeatmapSetId++;
+ return beatmapSet;
+ }
+
private void downloadAssert(bool shown)
{
AddAssert($"is download button {(shown ? "shown" : "hidden")}", () => overlay.Header.HeaderContent.DownloadButtonsVisible == shown);
diff --git a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs
index fd5f306e07..722010ace2 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneDirectPanel.cs
@@ -99,16 +99,23 @@ namespace osu.Game.Tests.Visual.Online
[BackgroundDependencyLoader]
private void load(RulesetStore rulesets)
{
- var normal = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
+ var normal = getBeatmapSet();
normal.OnlineInfo.HasVideo = true;
normal.OnlineInfo.HasStoryboard = true;
var undownloadable = getUndownloadableBeatmapSet();
var manyDifficulties = getManyDifficultiesBeatmapSet(rulesets);
- var explicitMap = CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
+ var explicitMap = getBeatmapSet();
explicitMap.OnlineInfo.HasExplicitContent = true;
+ var featuredMap = getBeatmapSet();
+ featuredMap.OnlineInfo.TrackId = 1;
+
+ var explicitFeaturedMap = getBeatmapSet();
+ explicitFeaturedMap.OnlineInfo.HasExplicitContent = true;
+ explicitFeaturedMap.OnlineInfo.TrackId = 2;
+
Child = new BasicScrollContainer
{
RelativeSizeAxes = Axes.Both,
@@ -125,13 +132,19 @@ namespace osu.Game.Tests.Visual.Online
new GridBeatmapPanel(undownloadable),
new GridBeatmapPanel(manyDifficulties),
new GridBeatmapPanel(explicitMap),
+ new GridBeatmapPanel(featuredMap),
+ new GridBeatmapPanel(explicitFeaturedMap),
new ListBeatmapPanel(normal),
new ListBeatmapPanel(undownloadable),
new ListBeatmapPanel(manyDifficulties),
- new ListBeatmapPanel(explicitMap)
+ new ListBeatmapPanel(explicitMap),
+ new ListBeatmapPanel(featuredMap),
+ new ListBeatmapPanel(explicitFeaturedMap)
},
},
};
+
+ BeatmapSetInfo getBeatmapSet() => CreateBeatmap(Ruleset.Value).BeatmapInfo.BeatmapSet;
}
}
}
diff --git a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
index 48f1f0ce68..3658dbab83 100644
--- a/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
+++ b/osu.Game/Beatmaps/BeatmapSetOnlineInfo.cs
@@ -90,6 +90,12 @@ namespace osu.Game.Beatmaps
/// The song language of this beatmap set.
///
public BeatmapSetOnlineLanguage Language { get; set; }
+
+ ///
+ /// The track ID of this beatmap set.
+ /// Non-null only if the track is linked to a featured artist track entry.
+ ///
+ public int? TrackId { get; set; }
}
public class BeatmapSetOnlineGenre
diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs
index 45d9c9405f..f653a654ca 100644
--- a/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs
+++ b/osu.Game/Online/API/Requests/Responses/APIBeatmapSet.cs
@@ -63,6 +63,9 @@ namespace osu.Game.Online.API.Requests.Responses
[JsonProperty(@"ratings")]
private int[] ratings { get; set; }
+ [JsonProperty(@"track_id")]
+ private int? trackId { get; set; }
+
[JsonProperty(@"user_id")]
private int creatorId
{
@@ -106,7 +109,8 @@ namespace osu.Game.Online.API.Requests.Responses
Availability = availability,
HasFavourited = hasFavourited,
Genre = genre,
- Language = language
+ Language = language,
+ TrackId = trackId
},
};
diff --git a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs
index 4d5c387c4a..c078127353 100644
--- a/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs
+++ b/osu.Game/Overlays/BeatmapListing/Panels/GridBeatmapPanel.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
private const float horizontal_padding = 10;
private const float vertical_padding = 5;
- private FillFlowContainer bottomPanel, statusContainer, titleContainer;
+ private FillFlowContainer bottomPanel, statusContainer, titleContainer, artistContainer;
private PlayButton playButton;
private Box progressBar;
@@ -89,11 +89,19 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
},
}
},
- new OsuSpriteText
+ artistContainer = new FillFlowContainer
{
- Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
- Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
- },
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Children = new Drawable[]
+ {
+ new OsuSpriteText
+ {
+ Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
+ Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
+ }
+ }
+ }
},
},
new Container
@@ -213,6 +221,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
});
}
+ if (SetInfo.OnlineInfo?.TrackId != null)
+ {
+ artistContainer.Add(new FeaturedArtistBeatmapPill
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Margin = new MarginPadding { Left = 10f, Top = 2f },
+ });
+ }
+
if (SetInfo.OnlineInfo?.HasVideo ?? false)
{
statusContainer.Add(new IconPill(FontAwesome.Solid.Film));
diff --git a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs
index 00ffd168c1..5011749c5f 100644
--- a/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs
+++ b/osu.Game/Overlays/BeatmapListing/Panels/ListBeatmapPanel.cs
@@ -27,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
private const float vertical_padding = 5;
private const float height = 70;
- private FillFlowContainer statusContainer, titleContainer;
+ private FillFlowContainer statusContainer, titleContainer, artistContainer;
protected BeatmapPanelDownloadButton DownloadButton;
private PlayButton playButton;
private Box progressBar;
@@ -112,10 +112,18 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
},
}
},
- new OsuSpriteText
+ artistContainer = new FillFlowContainer
{
- Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
- Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Children = new[]
+ {
+ new OsuSpriteText
+ {
+ Text = new RomanisableString(SetInfo.Metadata.ArtistUnicode, SetInfo.Metadata.Artist),
+ Font = OsuFont.GetFont(weight: FontWeight.Bold, italics: true)
+ },
+ },
},
}
},
@@ -227,6 +235,16 @@ namespace osu.Game.Overlays.BeatmapListing.Panels
});
}
+ if (SetInfo.OnlineInfo?.TrackId != null)
+ {
+ artistContainer.Add(new FeaturedArtistBeatmapPill
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Margin = new MarginPadding { Left = 10f, Top = 2f },
+ });
+ }
+
if (SetInfo.OnlineInfo?.HasVideo ?? false)
{
statusContainer.Add(new IconPill(FontAwesome.Solid.Film) { IconSize = new Vector2(20) });
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs
index a61640a02e..c3b6444a24 100644
--- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeaderContent.cs
@@ -37,6 +37,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly OsuSpriteText title, artist;
private readonly AuthorInfo author;
private readonly ExplicitContentBeatmapPill explicitContentPill;
+ private readonly FeaturedArtistBeatmapPill featuredArtistPill;
private readonly FillFlowContainer downloadButtonsContainer;
private readonly BeatmapAvailability beatmapAvailability;
private readonly BeatmapSetOnlineStatusPill onlineStatusPill;
@@ -129,10 +130,25 @@ namespace osu.Game.Overlays.BeatmapSet
}
}
},
- artist = new OsuSpriteText
+ new FillFlowContainer
{
- Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true),
- Margin = new MarginPadding { Bottom = 20 }
+ Direction = FillDirection.Horizontal,
+ AutoSizeAxes = Axes.Both,
+ Margin = new MarginPadding { Bottom = 20 },
+ Children = new Drawable[]
+ {
+ artist = new OsuSpriteText
+ {
+ Font = OsuFont.GetFont(size: 20, weight: FontWeight.Medium, italics: true),
+ },
+ featuredArtistPill = new FeaturedArtistBeatmapPill
+ {
+ Alpha = 0f,
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Margin = new MarginPadding { Left = 10 }
+ }
+ }
},
new Container
{
@@ -233,6 +249,7 @@ namespace osu.Game.Overlays.BeatmapSet
artist.Text = new RomanisableString(setInfo.NewValue.Metadata.ArtistUnicode, setInfo.NewValue.Metadata.Artist);
explicitContentPill.Alpha = setInfo.NewValue.OnlineInfo.HasExplicitContent ? 1 : 0;
+ featuredArtistPill.Alpha = setInfo.NewValue.OnlineInfo.TrackId != null ? 1 : 0;
onlineStatusPill.FadeIn(500, Easing.OutQuint);
onlineStatusPill.Status = setInfo.NewValue.OnlineInfo.Status;
diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs
new file mode 100644
index 0000000000..fdee0799ff
--- /dev/null
+++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.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.Extensions.LocalisationExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Resources.Localisation.Web;
+
+namespace osu.Game.Overlays.BeatmapSet
+{
+ public class FeaturedArtistBeatmapPill : CompositeDrawable
+ {
+ public FeaturedArtistBeatmapPill()
+ {
+ AutoSizeAxes = Axes.Both;
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuColour colours, OverlayColourProvider colourProvider)
+ {
+ InternalChild = new CircularContainer
+ {
+ Masking = true,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colourProvider?.Background5 ?? colours.Gray2,
+ },
+ new OsuSpriteText
+ {
+ Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f },
+ Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(),
+ Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold),
+ Colour = OverlayColourProvider.Blue.Colour1,
+ }
+ }
+ };
+ }
+ }
+}