diff --git a/osu.Android.props b/osu.Android.props
index 24a0d20874..526ce959a6 100644
--- a/osu.Android.props
+++ b/osu.Android.props
@@ -52,7 +52,7 @@
-
+
diff --git a/osu.Game.Tests/Chat/MessageFormatterTests.cs b/osu.Game.Tests/Chat/MessageFormatterTests.cs
index 8def8005f1..cea4d510c1 100644
--- a/osu.Game.Tests/Chat/MessageFormatterTests.cs
+++ b/osu.Game.Tests/Chat/MessageFormatterTests.cs
@@ -409,26 +409,26 @@ namespace osu.Game.Tests.Chat
Assert.AreEqual(result.Content, result.DisplayContent);
Assert.AreEqual(2, result.Links.Count);
- Assert.AreEqual("osu://chan/#english", result.Links[0].Url);
- Assert.AreEqual("osu://chan/#japanese", result.Links[1].Url);
+ Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#english", result.Links[0].Url);
+ Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#japanese", result.Links[1].Url);
}
[Test]
public void TestOsuProtocol()
{
- Message result = MessageFormatter.FormatMessage(new Message { Content = "This is a custom protocol osu://chan/#english." });
+ Message result = MessageFormatter.FormatMessage(new Message { Content = $"This is a custom protocol {OsuGameBase.OSU_PROTOCOL}chan/#english." });
Assert.AreEqual(result.Content, result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
- Assert.AreEqual("osu://chan/#english", result.Links[0].Url);
+ Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#english", result.Links[0].Url);
Assert.AreEqual(26, result.Links[0].Index);
Assert.AreEqual(19, result.Links[0].Length);
- result = MessageFormatter.FormatMessage(new Message { Content = "This is a [custom protocol](osu://chan/#english)." });
+ result = MessageFormatter.FormatMessage(new Message { Content = $"This is a [custom protocol]({OsuGameBase.OSU_PROTOCOL}chan/#english)." });
Assert.AreEqual("This is a custom protocol.", result.DisplayContent);
Assert.AreEqual(1, result.Links.Count);
- Assert.AreEqual("osu://chan/#english", result.Links[0].Url);
+ Assert.AreEqual($"{OsuGameBase.OSU_PROTOCOL}chan/#english", result.Links[0].Url);
Assert.AreEqual("#english", result.Links[0].Argument);
Assert.AreEqual(10, result.Links[0].Index);
Assert.AreEqual(15, result.Links[0].Length);
diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
index f0d9ece06f..c6e7988543 100644
--- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
+++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs
@@ -36,9 +36,9 @@ namespace osu.Game.Tests.Rulesets.Scoring
[TestCase(ScoringMode.Standardised, HitResult.Meh, 750_000)]
[TestCase(ScoringMode.Standardised, HitResult.Ok, 800_000)]
[TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)]
- [TestCase(ScoringMode.Classic, HitResult.Meh, 41)]
- [TestCase(ScoringMode.Classic, HitResult.Ok, 46)]
- [TestCase(ScoringMode.Classic, HitResult.Great, 72)]
+ [TestCase(ScoringMode.Classic, HitResult.Meh, 20)]
+ [TestCase(ScoringMode.Classic, HitResult.Ok, 23)]
+ [TestCase(ScoringMode.Classic, HitResult.Great, 36)]
public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
scoreProcessor.Mode.Value = scoringMode;
@@ -86,17 +86,17 @@ namespace osu.Game.Tests.Rulesets.Scoring
[TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 10 (bonus points)
[TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] // 1 * 300_000 + 700_000 (max combo 0) + 3 * 50 (bonus points)
[TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)]
- [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 68)]
- [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 81)]
- [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 109)]
- [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 149)]
- [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 149)]
- [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 9)]
- [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 15)]
+ [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 86)]
+ [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 104)]
+ [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 140)]
+ [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 190)]
+ [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 190)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 18)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 31)]
[TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)]
- [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 149)]
- [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 18)]
- [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 18)]
+ [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 12)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 36)]
+ [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 36)]
public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore)
{
var minResult = new TestJudgement(hitResult).MinResult;
@@ -128,8 +128,8 @@ namespace osu.Game.Tests.Rulesets.Scoring
///
[TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, 978_571)] // (3 * 10 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000
[TestCase(ScoringMode.Standardised, HitResult.SmallTickMiss, 914_286)] // (3 * 0 + 100) / (4 * 10 + 100) * 300_000 + (1 / 1) * 700_000
- [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 69)] // (((3 * 10 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
- [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 60)] // (((3 * 0 + 100) / (4 * 10 + 100)) * 1 * 300) * (1 + 0 / 25)
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, 34)]
+ [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, 30)]
public void TestSmallTicksAccuracy(ScoringMode scoringMode, HitResult hitResult, int expectedScore)
{
IEnumerable hitObjects = Enumerable
diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs
index adaa24d542..d1c1558003 100644
--- a/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs
+++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorSaving.cs
@@ -49,6 +49,8 @@ namespace osu.Game.Tests.Visual.Editing
double originalTimelineZoom = 0;
double changedTimelineZoom = 0;
+ AddUntilStep("wait for timeline load", () => Editor.ChildrenOfType().SingleOrDefault()?.IsLoaded == true);
+
AddStep("Set beat divisor", () => Editor.Dependencies.Get().Value = 16);
AddStep("Set timeline zoom", () =>
{
diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
index c3d5f7ec23..93cd281bc5 100644
--- a/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
+++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneLoungeRoomsContainer.cs
@@ -44,15 +44,20 @@ namespace osu.Game.Tests.Visual.Multiplayer
[Test]
public void TestBasicListChanges()
{
- AddStep("add rooms", () => RoomManager.AddRooms(3));
+ AddStep("add rooms", () => RoomManager.AddRooms(5, withSpotlightRooms: true));
- AddAssert("has 3 rooms", () => container.Rooms.Count == 3);
- AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.FirstOrDefault()));
- AddAssert("has 2 rooms", () => container.Rooms.Count == 2);
+ AddAssert("has 5 rooms", () => container.Rooms.Count == 5);
+
+ AddAssert("all spotlights at top", () => container.Rooms
+ .SkipWhile(r => r.Room.Category.Value == RoomCategory.Spotlight)
+ .All(r => r.Room.Category.Value == RoomCategory.Normal));
+
+ AddStep("remove first room", () => RoomManager.RemoveRoom(RoomManager.Rooms.FirstOrDefault(r => r.RoomID.Value == 0)));
+ AddAssert("has 4 rooms", () => container.Rooms.Count == 4);
AddAssert("first room removed", () => container.Rooms.All(r => r.Room.RoomID.Value != 0));
AddStep("select first room", () => container.Rooms.First().TriggerClick());
- AddAssert("first room selected", () => checkRoomSelected(RoomManager.Rooms.First()));
+ AddAssert("first spotlight selected", () => checkRoomSelected(RoomManager.Rooms.First(r => r.Category.Value == RoomCategory.Spotlight)));
}
[Test]
diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneStartupBeatmapDisplay.cs b/osu.Game.Tests/Visual/Navigation/TestSceneStartupBeatmapDisplay.cs
new file mode 100644
index 0000000000..961b7dedc3
--- /dev/null
+++ b/osu.Game.Tests/Visual/Navigation/TestSceneStartupBeatmapDisplay.cs
@@ -0,0 +1,54 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Overlays;
+using osu.Game.Overlays.BeatmapSet;
+
+namespace osu.Game.Tests.Visual.Navigation
+{
+ public class TestSceneStartupBeatmapDisplay : OsuGameTestScene
+ {
+ private const int requested_beatmap_id = 75;
+ private const int requested_beatmap_set_id = 1;
+
+ protected override TestOsuGame CreateTestGame() => new TestOsuGame(LocalStorage, API, new[] { $"osu://b/{requested_beatmap_id}" });
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ ((DummyAPIAccess)API).HandleRequest = request =>
+ {
+ switch (request)
+ {
+ case GetBeatmapSetRequest gbr:
+ var apiBeatmapSet = CreateAPIBeatmapSet();
+ apiBeatmapSet.OnlineID = requested_beatmap_set_id;
+ apiBeatmapSet.Beatmaps = apiBeatmapSet.Beatmaps.Append(new APIBeatmap
+ {
+ DifficultyName = "Target difficulty",
+ OnlineID = requested_beatmap_id,
+ }).ToArray();
+
+ gbr.TriggerSuccess(apiBeatmapSet);
+ return true;
+ }
+
+ return false;
+ };
+ });
+
+ [Test]
+ public void TestBeatmapLink()
+ {
+ AddUntilStep("Beatmap overlay displayed", () => Game.ChildrenOfType().FirstOrDefault()?.State.Value == Visibility.Visible);
+ AddUntilStep("Beatmap overlay showing content", () => Game.ChildrenOfType().FirstOrDefault()?.Beatmap.Value.OnlineID == requested_beatmap_id);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneStartupBeatmapSetDisplay.cs b/osu.Game.Tests/Visual/Navigation/TestSceneStartupBeatmapSetDisplay.cs
new file mode 100644
index 0000000000..1aa56896d3
--- /dev/null
+++ b/osu.Game.Tests/Visual/Navigation/TestSceneStartupBeatmapSetDisplay.cs
@@ -0,0 +1,52 @@
+// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence.
+// See the LICENCE file in the repository root for full licence text.
+
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
+using osu.Game.Online.API;
+using osu.Game.Online.API.Requests;
+using osu.Game.Online.API.Requests.Responses;
+using osu.Game.Overlays;
+
+namespace osu.Game.Tests.Visual.Navigation
+{
+ public class TestSceneStartupBeatmapSetDisplay : OsuGameTestScene
+ {
+ private const int requested_beatmap_set_id = 1;
+
+ protected override TestOsuGame CreateTestGame() => new TestOsuGame(LocalStorage, API, new[] { $"osu://s/{requested_beatmap_set_id}" });
+
+ [SetUp]
+ public void Setup() => Schedule(() =>
+ {
+ ((DummyAPIAccess)API).HandleRequest = request =>
+ {
+ switch (request)
+ {
+ case GetBeatmapSetRequest gbr:
+
+ var apiBeatmapSet = CreateAPIBeatmapSet();
+ apiBeatmapSet.OnlineID = requested_beatmap_set_id;
+ apiBeatmapSet.Beatmaps = apiBeatmapSet.Beatmaps.Append(new APIBeatmap
+ {
+ DifficultyName = "Target difficulty",
+ OnlineID = 75,
+ }).ToArray();
+ gbr.TriggerSuccess(apiBeatmapSet);
+ return true;
+ }
+
+ return false;
+ };
+ });
+
+ [Test]
+ public void TestBeatmapSetLink()
+ {
+ AddUntilStep("Beatmap overlay displayed", () => Game.ChildrenOfType().FirstOrDefault()?.State.Value == Visibility.Visible);
+ AddUntilStep("Beatmap overlay showing content", () => Game.ChildrenOfType().FirstOrDefault()?.Header.BeatmapSet.Value.OnlineID == requested_beatmap_set_id);
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs
index 12b5f64559..d077868175 100644
--- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs
+++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs
@@ -87,8 +87,8 @@ namespace osu.Game.Tests.Visual.Online
addMessageWithChecks("likes to post this [https://dev.ppy.sh/home link].", 1, true, true, expectedActions: LinkAction.External);
addMessageWithChecks("Join my multiplayer game osump://12346.", 1, expectedActions: LinkAction.JoinMultiplayerMatch);
addMessageWithChecks("Join my [multiplayer game](osump://12346).", 1, expectedActions: LinkAction.JoinMultiplayerMatch);
- addMessageWithChecks("Join my [#english](osu://chan/#english).", 1, expectedActions: LinkAction.OpenChannel);
- addMessageWithChecks("Join my osu://chan/#english.", 1, expectedActions: LinkAction.OpenChannel);
+ addMessageWithChecks($"Join my [#english]({OsuGameBase.OSU_PROTOCOL}chan/#english).", 1, expectedActions: LinkAction.OpenChannel);
+ addMessageWithChecks($"Join my {OsuGameBase.OSU_PROTOCOL}chan/#english.", 1, expectedActions: LinkAction.OpenChannel);
addMessageWithChecks("Join my #english or #japanese channels.", 2, expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel });
addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", 1, expectedActions: LinkAction.OpenChannel);
diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
index 667fd08084..c4178143f8 100644
--- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
+++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapLeaderboard.cs
@@ -197,6 +197,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now,
Mods = new Mod[]
{
new OsuModHidden(),
@@ -234,6 +235,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddSeconds(-30),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -254,6 +256,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddSeconds(-70),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -275,6 +278,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddMinutes(-40),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -296,6 +300,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 1,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddHours(-2),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -317,6 +322,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.9826,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddHours(-25),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -338,6 +344,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.9654,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddHours(-50),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -359,6 +366,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.6025,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddHours(-72),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -380,6 +388,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.5140,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddMonths(-3),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
@@ -401,6 +410,7 @@ namespace osu.Game.Tests.Visual.SongSelect
Accuracy = 0.4222,
MaxCombo = 244,
TotalScore = 1707827,
+ Date = DateTime.Now.AddYears(-2),
Mods = new Mod[] { new OsuModHidden(), new OsuModHardRock(), },
BeatmapInfo = beatmapInfo,
Ruleset = new OsuRuleset().RulesetInfo,
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSwitchSmall.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSwitchSmall.cs
new file mode 100644
index 0000000000..447352b7a6
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSwitchSmall.cs
@@ -0,0 +1,85 @@
+// 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.Linq;
+using NUnit.Framework;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
+using osu.Game.Overlays;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Catch;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Taiko;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ [TestFixture]
+ public class TestSceneModSwitchSmall : OsuTestScene
+ {
+ [Test]
+ public void TestOsu() => createSwitchTestFor(new OsuRuleset());
+
+ [Test]
+ public void TestTaiko() => createSwitchTestFor(new TaikoRuleset());
+
+ [Test]
+ public void TestCatch() => createSwitchTestFor(new CatchRuleset());
+
+ [Test]
+ public void TestMania() => createSwitchTestFor(new ManiaRuleset());
+
+ private void createSwitchTestFor(Ruleset ruleset)
+ {
+ AddStep("no colour scheme", () => Child = createContent(ruleset, null));
+
+ foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast())
+ {
+ AddStep($"{scheme} colour scheme", () => Child = createContent(ruleset, scheme));
+ }
+
+ AddToggleStep("toggle active", active => this.ChildrenOfType().ForEach(s => s.Active.Value = active));
+ }
+
+ private static Drawable createContent(Ruleset ruleset, OverlayColourScheme? colourScheme)
+ {
+ var switchFlow = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(10),
+ Padding = new MarginPadding(20),
+ ChildrenEnumerable = ruleset.CreateAllMods()
+ .GroupBy(mod => mod.Type)
+ .Select(group => new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Full,
+ Spacing = new Vector2(5),
+ ChildrenEnumerable = group.Select(mod => new ModSwitchSmall(mod))
+ })
+ };
+
+ if (colourScheme != null)
+ {
+ return new DependencyProvidingContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ CachedDependencies = new (Type, object)[]
+ {
+ (typeof(OverlayColourProvider), new OverlayColourProvider(colourScheme.Value))
+ },
+ Child = switchFlow
+ };
+ }
+
+ return switchFlow;
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSwitchTiny.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSwitchTiny.cs
new file mode 100644
index 0000000000..dbde7ce425
--- /dev/null
+++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSwitchTiny.cs
@@ -0,0 +1,85 @@
+// 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.Linq;
+using NUnit.Framework;
+using osu.Framework.Extensions.IEnumerableExtensions;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Testing;
+using osu.Game.Overlays;
+using osu.Game.Rulesets;
+using osu.Game.Rulesets.Catch;
+using osu.Game.Rulesets.Mania;
+using osu.Game.Rulesets.Osu;
+using osu.Game.Rulesets.Taiko;
+using osu.Game.Rulesets.UI;
+using osuTK;
+
+namespace osu.Game.Tests.Visual.UserInterface
+{
+ [TestFixture]
+ public class TestSceneModSwitchTiny : OsuTestScene
+ {
+ [Test]
+ public void TestOsu() => createSwitchTestFor(new OsuRuleset());
+
+ [Test]
+ public void TestTaiko() => createSwitchTestFor(new TaikoRuleset());
+
+ [Test]
+ public void TestCatch() => createSwitchTestFor(new CatchRuleset());
+
+ [Test]
+ public void TestMania() => createSwitchTestFor(new ManiaRuleset());
+
+ private void createSwitchTestFor(Ruleset ruleset)
+ {
+ AddStep("no colour scheme", () => Child = createContent(ruleset, null));
+
+ foreach (var scheme in Enum.GetValues(typeof(OverlayColourScheme)).Cast())
+ {
+ AddStep($"{scheme} colour scheme", () => Child = createContent(ruleset, scheme));
+ }
+
+ AddToggleStep("toggle active", active => this.ChildrenOfType().ForEach(s => s.Active.Value = active));
+ }
+
+ private static Drawable createContent(Ruleset ruleset, OverlayColourScheme? colourScheme)
+ {
+ var switchFlow = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Direction = FillDirection.Vertical,
+ Spacing = new Vector2(10),
+ Padding = new MarginPadding(20),
+ ChildrenEnumerable = ruleset.CreateAllMods()
+ .GroupBy(mod => mod.Type)
+ .Select(group => new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Direction = FillDirection.Full,
+ Spacing = new Vector2(5),
+ ChildrenEnumerable = group.Select(mod => new ModSwitchTiny(mod))
+ })
+ };
+
+ if (colourScheme != null)
+ {
+ return new DependencyProvidingContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ CachedDependencies = new (Type, object)[]
+ {
+ (typeof(OverlayColourProvider), new OverlayColourProvider(colourScheme.Value))
+ },
+ Child = switchFlow
+ };
+ }
+
+ return switchFlow;
+ }
+ }
+}
diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs
index dc05482a05..54af6a5942 100644
--- a/osu.Game/Extensions/TimeDisplayExtensions.cs
+++ b/osu.Game/Extensions/TimeDisplayExtensions.cs
@@ -2,7 +2,9 @@
// See the LICENCE file in the repository root for full licence text.
using System;
+using Humanizer;
using osu.Framework.Localisation;
+using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Extensions
{
@@ -47,5 +49,57 @@ namespace osu.Game.Extensions
return new LocalisableFormattableString(timeSpan, @"mm\:ss");
}
+
+ ///
+ /// Formats a provided date to a short relative string version for compact display.
+ ///
+ /// The time to be displayed.
+ /// A timespan denoting the time length beneath which "now" should be displayed.
+ /// A short relative string representing the input time.
+ public static string ToShortRelativeTime(this DateTimeOffset time, TimeSpan lowerCutoff)
+ {
+ if (time == default)
+ return "-";
+
+ var now = DateTime.Now;
+ var difference = now - time;
+
+ // web uses momentjs's custom locales to format the date for the purposes of the scoreboard.
+ // this is intended to be a best-effort, more legible approximation of that.
+ // compare:
+ // * https://github.com/ppy/osu-web/blob/a8f5a68fb435cb19a4faa4c7c4bce08c4f096933/resources/assets/lib/scoreboard-time.tsx
+ // * https://momentjs.com/docs/#/customization/ (reference for the customisation format)
+
+ // TODO: support localisation (probably via `CommonStrings.CountHours()` etc.)
+ // requires pluralisable string support framework-side
+
+ if (difference < lowerCutoff)
+ return CommonStrings.TimeNow.ToString();
+
+ if (difference.TotalMinutes < 1)
+ return "sec".ToQuantity((int)difference.TotalSeconds);
+ if (difference.TotalHours < 1)
+ return "min".ToQuantity((int)difference.TotalMinutes);
+ if (difference.TotalDays < 1)
+ return "hr".ToQuantity((int)difference.TotalHours);
+
+ // this is where this gets more complicated because of how the calendar works.
+ // since there's no `TotalMonths` / `TotalYears`, we have to iteratively add months/years
+ // and test against cutoff dates to determine how many months/years to show.
+
+ if (time > now.AddMonths(-1))
+ return difference.TotalDays < 2 ? "1dy" : $"{(int)difference.TotalDays}dys";
+
+ for (int months = 1; months <= 11; ++months)
+ {
+ if (time > now.AddMonths(-(months + 1)))
+ return months == 1 ? "1mo" : $"{months}mos";
+ }
+
+ int years = 1;
+ while (time <= now.AddYears(-(years + 1)))
+ years += 1;
+ return years == 1 ? "1yr" : $"{years}yrs";
+ }
}
}
diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs
index f63bd53549..afedf36cad 100644
--- a/osu.Game/Graphics/OsuColour.cs
+++ b/osu.Game/Graphics/OsuColour.cs
@@ -5,6 +5,7 @@ using System;
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
+using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Utils;
@@ -157,6 +158,36 @@ namespace osu.Game.Graphics
}
}
+ ///
+ /// Retrieves the main accent colour for a .
+ ///
+ public Color4 ForModType(ModType modType)
+ {
+ switch (modType)
+ {
+ case ModType.Automation:
+ return Blue1;
+
+ case ModType.DifficultyIncrease:
+ return Red1;
+
+ case ModType.DifficultyReduction:
+ return Lime1;
+
+ case ModType.Conversion:
+ return Purple1;
+
+ case ModType.Fun:
+ return Pink1;
+
+ case ModType.System:
+ return Gray7;
+
+ default:
+ throw new ArgumentOutOfRangeException(nameof(modType), modType, "Unknown mod type");
+ }
+ }
+
///
/// Returns a foreground text colour that is supposed to contrast well with
/// the supplied .
@@ -264,32 +295,58 @@ namespace osu.Game.Graphics
public readonly Color4 GrayE = Color4Extensions.FromHex(@"eee");
public readonly Color4 GrayF = Color4Extensions.FromHex(@"fff");
- ///
- /// Equivalent to 's .
- ///
- public readonly Color4 Pink3 = Color4Extensions.FromHex(@"cc3378");
+ #region "Basic" colour theme
- ///
- /// Equivalent to 's .
- ///
+ // Reference: https://www.figma.com/file/VIkXMYNPMtQem2RJg9k2iQ/Asset%2FColours?node-id=1838%3A3
+
+ // Note that the colours in this region are also defined in `OverlayColourProvider` as `Colour{0,1,2,3,4}`.
+ // The difference as to which should be used where comes down to context.
+ // If the colour in question is supposed to always match the view in which it is displayed theme-wise, use `OverlayColourProvider`.
+ // If the colour usage is special and in general differs from the surrounding view in choice of hue, use the `OsuColour` constants.
+
+ public readonly Color4 Pink0 = Color4Extensions.FromHex(@"ff99c7");
+ public readonly Color4 Pink1 = Color4Extensions.FromHex(@"ff66ab");
+ public readonly Color4 Pink2 = Color4Extensions.FromHex(@"eb4791");
+ public readonly Color4 Pink3 = Color4Extensions.FromHex(@"cc3378");
+ public readonly Color4 Pink4 = Color4Extensions.FromHex(@"6b2e49");
+
+ public readonly Color4 Purple0 = Color4Extensions.FromHex(@"b299ff");
+ public readonly Color4 Purple1 = Color4Extensions.FromHex(@"8c66ff");
+ public readonly Color4 Purple2 = Color4Extensions.FromHex(@"7047eb");
+ public readonly Color4 Purple3 = Color4Extensions.FromHex(@"5933cc");
+ public readonly Color4 Purple4 = Color4Extensions.FromHex(@"3d2e6b");
+
+ public readonly Color4 Blue0 = Color4Extensions.FromHex(@"99ddff");
+ public readonly Color4 Blue1 = Color4Extensions.FromHex(@"66ccff");
+ public readonly Color4 Blue2 = Color4Extensions.FromHex(@"47b4eb");
public readonly Color4 Blue3 = Color4Extensions.FromHex(@"3399cc");
+ public readonly Color4 Blue4 = Color4Extensions.FromHex(@"2e576b");
+
+ public readonly Color4 Green0 = Color4Extensions.FromHex(@"99ffa2");
+ public readonly Color4 Green1 = Color4Extensions.FromHex(@"66ff73");
+ public readonly Color4 Green2 = Color4Extensions.FromHex(@"47eb55");
+ public readonly Color4 Green3 = Color4Extensions.FromHex(@"33cc40");
+ public readonly Color4 Green4 = Color4Extensions.FromHex(@"2e6b33");
public readonly Color4 Lime0 = Color4Extensions.FromHex(@"ccff99");
-
- ///
- /// Equivalent to 's .
- ///
public readonly Color4 Lime1 = Color4Extensions.FromHex(@"b2ff66");
-
- ///
- /// Equivalent to 's .
- ///
+ public readonly Color4 Lime2 = Color4Extensions.FromHex(@"99eb47");
public readonly Color4 Lime3 = Color4Extensions.FromHex(@"7fcc33");
+ public readonly Color4 Lime4 = Color4Extensions.FromHex(@"4c6b2e");
- ///
- /// Equivalent to 's .
- ///
+ public readonly Color4 Orange0 = Color4Extensions.FromHex(@"ffe699");
public readonly Color4 Orange1 = Color4Extensions.FromHex(@"ffd966");
+ public readonly Color4 Orange2 = Color4Extensions.FromHex(@"ebc247");
+ public readonly Color4 Orange3 = Color4Extensions.FromHex(@"cca633");
+ public readonly Color4 Orange4 = Color4Extensions.FromHex(@"6b5c2e");
+
+ public readonly Color4 Red0 = Color4Extensions.FromHex(@"ff9b9b");
+ public readonly Color4 Red1 = Color4Extensions.FromHex(@"ff6666");
+ public readonly Color4 Red2 = Color4Extensions.FromHex(@"eb4747");
+ public readonly Color4 Red3 = Color4Extensions.FromHex(@"cc3333");
+ public readonly Color4 Red4 = Color4Extensions.FromHex(@"6b2e2e");
+
+ #endregion
// Content Background
public readonly Color4 B5 = Color4Extensions.FromHex(@"222a28");
diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs
index d7974004b1..b18daea453 100644
--- a/osu.Game/Online/Chat/MessageFormatter.cs
+++ b/osu.Game/Online/Chat/MessageFormatter.cs
@@ -236,8 +236,7 @@ namespace osu.Game.Online.Chat
break;
default:
- linkType = LinkAction.External;
- break;
+ return new LinkDetails(LinkAction.External, url);
}
return new LinkDetails(linkType, args[2]);
@@ -269,10 +268,10 @@ namespace osu.Game.Online.Chat
handleAdvanced(advanced_link_regex, result, startIndex);
// handle editor times
- handleMatches(time_regex, "{0}", "osu://edit/{0}", result, startIndex, LinkAction.OpenEditorTimestamp);
+ handleMatches(time_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}edit/{{0}}", result, startIndex, LinkAction.OpenEditorTimestamp);
// handle channels
- handleMatches(channel_regex, "{0}", "osu://chan/{0}", result, startIndex, LinkAction.OpenChannel);
+ handleMatches(channel_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}chan/{{0}}", result, startIndex, LinkAction.OpenChannel);
string empty = "";
while (space-- > 0)
diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs
index c2393a5de5..ddd9d9a2b2 100644
--- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs
+++ b/osu.Game/Online/Leaderboards/LeaderboardScore.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 osu.Framework.Allocation;
@@ -16,6 +17,7 @@ using osu.Framework.Input.Events;
using osu.Framework.Localisation;
using osu.Framework.Platform;
using osu.Game.Database;
+using osu.Game.Extensions;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
@@ -56,7 +58,7 @@ namespace osu.Game.Online.Leaderboards
public GlowingSpriteText ScoreText { get; private set; }
- private Container flagBadgeContainer;
+ private FillFlowContainer flagBadgeAndDateContainer;
private FillFlowContainer modsContainer;
private List statisticsLabels;
@@ -103,7 +105,7 @@ namespace osu.Game.Online.Leaderboards
content = new Container
{
RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Left = rank_width, },
+ Padding = new MarginPadding { Left = rank_width },
Children = new Drawable[]
{
new Container
@@ -158,32 +160,41 @@ namespace osu.Game.Online.Leaderboards
},
new FillFlowContainer
{
- Origin = Anchor.BottomLeft,
Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10f, 0f),
Children = new Drawable[]
{
- flagBadgeContainer = new Container
+ flagBadgeAndDateContainer = new FillFlowContainer
{
- Origin = Anchor.BottomLeft,
- Anchor = Anchor.BottomLeft,
- Size = new Vector2(87f, 20f),
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ RelativeSizeAxes = Axes.Y,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(5f, 0f),
+ Width = 87f,
Masking = true,
Children = new Drawable[]
{
new UpdateableFlag(user.Country)
{
- Width = 30,
- RelativeSizeAxes = Axes.Y,
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Size = new Vector2(30f, 20f),
+ },
+ new DateLabel(Score.Date)
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
},
},
},
new FillFlowContainer
{
- Origin = Anchor.BottomLeft,
- Anchor = Anchor.BottomLeft,
+ Origin = Anchor.CentreLeft,
+ Anchor = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Margin = new MarginPadding { Left = edge_margin },
@@ -243,7 +254,7 @@ namespace osu.Game.Online.Leaderboards
public override void Show()
{
- foreach (var d in new[] { avatar, nameLabel, ScoreText, scoreRank, flagBadgeContainer, modsContainer }.Concat(statisticsLabels))
+ foreach (var d in new[] { avatar, nameLabel, ScoreText, scoreRank, flagBadgeAndDateContainer, modsContainer }.Concat(statisticsLabels))
d.FadeOut();
Alpha = 0;
@@ -270,7 +281,7 @@ namespace osu.Game.Online.Leaderboards
using (BeginDelayedSequence(50))
{
- var drawables = new Drawable[] { flagBadgeContainer, modsContainer }.Concat(statisticsLabels).ToArray();
+ var drawables = new Drawable[] { flagBadgeAndDateContainer, modsContainer }.Concat(statisticsLabels).ToArray();
for (int i = 0; i < drawables.Length; i++)
drawables[i].FadeIn(100 + i * 50);
}
@@ -377,6 +388,17 @@ namespace osu.Game.Online.Leaderboards
public LocalisableString TooltipText { get; }
}
+ private class DateLabel : DrawableDate
+ {
+ public DateLabel(DateTimeOffset date)
+ : base(date)
+ {
+ Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold, italics: true);
+ }
+
+ protected override string Format() => Date.ToShortRelativeTime(TimeSpan.FromSeconds(30));
+ }
+
public class LeaderboardScoreStatistic
{
public IconUsage Icon;
diff --git a/osu.Game/Online/Rooms/Room.cs b/osu.Game/Online/Rooms/Room.cs
index bbe854f2dd..a328f8e8c0 100644
--- a/osu.Game/Online/Rooms/Room.cs
+++ b/osu.Game/Online/Rooms/Room.cs
@@ -168,8 +168,7 @@ namespace osu.Game.Online.Rooms
RoomID.Value = other.RoomID.Value;
Name.Value = other.Name.Value;
- if (other.Category.Value != RoomCategory.Spotlight)
- Category.Value = other.Category.Value;
+ Category.Value = other.Category.Value;
if (other.Host.Value != null && Host.Value?.Id != other.Host.Value.Id)
Host.Value = other.Host.Value;
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 5b58dec0c3..fa5a336b7c 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -150,6 +150,7 @@ namespace osu.Game
protected SettingsOverlay Settings;
private VolumeOverlay volume;
+
private OsuLogo osuLogo;
private MainMenu menuScreen;
@@ -898,8 +899,20 @@ namespace osu.Game
if (args?.Length > 0)
{
string[] paths = args.Where(a => !a.StartsWith('-')).ToArray();
+
if (paths.Length > 0)
- Task.Run(() => Import(paths));
+ {
+ string firstPath = paths.First();
+
+ if (firstPath.StartsWith(OSU_PROTOCOL, StringComparison.Ordinal))
+ {
+ HandleLink(firstPath);
+ }
+ else
+ {
+ Task.Run(() => Import(paths));
+ }
+ }
}
}
diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs
index 0b2644d5ba..86390e7630 100644
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -52,6 +52,8 @@ namespace osu.Game
///
public partial class OsuGameBase : Framework.Game, ICanAcceptFiles
{
+ public const string OSU_PROTOCOL = "osu://";
+
public const string CLIENT_STREAM_NAME = @"lazer";
public const int SAMPLE_CONCURRENCY = 6;
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
index 157753c09f..0f87f04270 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingFilterControl.cs
@@ -67,6 +67,8 @@ namespace osu.Game.Overlays.BeatmapListing
[Resolved]
private IAPIProvider api { get; set; }
+ private IBindable apiUser;
+
public BeatmapListingFilterControl()
{
RelativeSizeAxes = Axes.X;
@@ -127,7 +129,7 @@ namespace osu.Game.Overlays.BeatmapListing
}
[BackgroundDependencyLoader]
- private void load(OverlayColourProvider colourProvider)
+ private void load(OverlayColourProvider colourProvider, IAPIProvider api)
{
sortControlBackground.Colour = colourProvider.Background4;
}
@@ -161,6 +163,9 @@ namespace osu.Game.Overlays.BeatmapListing
sortCriteria.BindValueChanged(_ => queueUpdateSearch());
sortDirection.BindValueChanged(_ => queueUpdateSearch());
+
+ apiUser = api.LocalUser.GetBoundCopy();
+ apiUser.BindValueChanged(_ => queueUpdateSearch());
}
public void TakeFocus() => searchControl.TakeFocus();
@@ -190,6 +195,9 @@ namespace osu.Game.Overlays.BeatmapListing
resetSearch();
+ if (!api.IsLoggedIn)
+ return;
+
queryChangedDebounce = Scheduler.AddDelayed(() =>
{
resetSearch();
diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs b/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs
index fb9e1c0420..51dad100c2 100644
--- a/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs
+++ b/osu.Game/Overlays/BeatmapListing/BeatmapSearchGeneralFilterRow.cs
@@ -1,6 +1,8 @@
// 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.Game.Graphics;
using osu.Game.Resources.Localisation.Web;
using osuTK.Graphics;
@@ -33,7 +35,10 @@ namespace osu.Game.Overlays.BeatmapListing
{
}
- protected override Color4 GetStateColour() => OverlayColourProvider.Orange.Colour1;
+ [Resolved]
+ private OsuColour colours { get; set; }
+
+ protected override Color4 GetStateColour() => colours.Orange1;
}
}
}
diff --git a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs
index 9274cf20aa..52dfcad2cc 100644
--- a/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs
+++ b/osu.Game/Overlays/BeatmapListing/FilterTabItem.cs
@@ -44,7 +44,14 @@ namespace osu.Game.Overlays.BeatmapListing
});
Enabled.Value = true;
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
updateState();
+ FinishTransforms(true);
}
protected override bool OnHover(HoverEvent e)
diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs
index fbed234cc7..3476968ded 100644
--- a/osu.Game/Overlays/BeatmapListingOverlay.cs
+++ b/osu.Game/Overlays/BeatmapListingOverlay.cs
@@ -6,6 +6,7 @@ using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using osu.Framework.Allocation;
+using osu.Framework.Bindables;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Localisation;
using osu.Framework.Graphics;
@@ -19,6 +20,7 @@ using osu.Game.Beatmaps.Drawables.Cards;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.Containers;
+using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Overlays.BeatmapListing;
using osu.Game.Resources.Localisation.Web;
@@ -32,6 +34,11 @@ namespace osu.Game.Overlays
[Resolved]
private PreviewTrackManager previewTrackManager { get; set; }
+ [Resolved]
+ private IAPIProvider api { get; set; }
+
+ private IBindable apiUser;
+
private Drawable currentContent;
private Container panelTarget;
private FillFlowContainer foundContent;
@@ -93,6 +100,13 @@ namespace osu.Game.Overlays
{
base.LoadComplete();
filterControl.CardSize.BindValueChanged(_ => onCardSizeChanged());
+
+ apiUser = api.LocalUser.GetBoundCopy();
+ apiUser.BindValueChanged(_ =>
+ {
+ if (api.IsLoggedIn)
+ addContentToResultsArea(Drawable.Empty());
+ });
}
public void ShowWithSearch(string query)
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
index 59e8e8db3c..031442814d 100644
--- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
@@ -183,7 +183,14 @@ namespace osu.Game.Overlays.BeatmapSet
}
starRatingContainer.FadeOut(100);
- Beatmap.Value = Difficulties.FirstOrDefault()?.Beatmap;
+
+ // If a selection is already made, try and maintain it.
+ if (Beatmap.Value != null)
+ Beatmap.Value = Difficulties.FirstOrDefault(b => b.Beatmap.OnlineID == Beatmap.Value.OnlineID)?.Beatmap;
+
+ // Else just choose the first available difficulty for now.
+ Beatmap.Value ??= Difficulties.FirstOrDefault()?.Beatmap;
+
plays.Value = BeatmapSet?.PlayCount ?? 0;
favourites.Value = BeatmapSet?.FavouriteCount ?? 0;
diff --git a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs
index ba78592ed2..21d1d1172c 100644
--- a/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs
+++ b/osu.Game/Overlays/BeatmapSet/ExplicitContentBeatmapPill.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f },
Text = BeatmapsetsStrings.NsfwBadgeLabel.ToUpper(),
Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold),
- Colour = OverlayColourProvider.Orange.Colour2,
+ Colour = colours.Orange2
}
}
};
diff --git a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs
index fdee0799ff..1be987cde2 100644
--- a/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs
+++ b/osu.Game/Overlays/BeatmapSet/FeaturedArtistBeatmapPill.cs
@@ -38,7 +38,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Horizontal = 10f, Vertical = 2f },
Text = BeatmapsetsStrings.FeaturedArtistBadgeLabel.ToUpper(),
Font = OsuFont.GetFont(size: 10, weight: FontWeight.SemiBold),
- Colour = OverlayColourProvider.Blue.Colour1,
+ Colour = colours.Blue1
}
}
};
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs
index ff1d3490b4..5018fb8c70 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreboardTime.cs
@@ -2,9 +2,8 @@
// See the LICENCE file in the repository root for full licence text.
using System;
-using Humanizer;
+using osu.Game.Extensions;
using osu.Game.Graphics;
-using osu.Game.Resources.Localisation.Web;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
@@ -16,41 +15,6 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
}
protected override string Format()
- {
- var now = DateTime.Now;
- var difference = now - Date;
-
- // web uses momentjs's custom locales to format the date for the purposes of the scoreboard.
- // this is intended to be a best-effort, more legible approximation of that.
- // compare:
- // * https://github.com/ppy/osu-web/blob/a8f5a68fb435cb19a4faa4c7c4bce08c4f096933/resources/assets/lib/scoreboard-time.tsx
- // * https://momentjs.com/docs/#/customization/ (reference for the customisation format)
-
- // TODO: support localisation (probably via `CommonStrings.CountHours()` etc.)
- // requires pluralisable string support framework-side
-
- if (difference.TotalHours < 1)
- return CommonStrings.TimeNow.ToString();
- if (difference.TotalDays < 1)
- return "hr".ToQuantity((int)difference.TotalHours);
-
- // this is where this gets more complicated because of how the calendar works.
- // since there's no `TotalMonths` / `TotalYears`, we have to iteratively add months/years
- // and test against cutoff dates to determine how many months/years to show.
-
- if (Date > now.AddMonths(-1))
- return difference.TotalDays < 2 ? "1dy" : $"{(int)difference.TotalDays}dys";
-
- for (int months = 1; months <= 11; ++months)
- {
- if (Date > now.AddMonths(-(months + 1)))
- return months == 1 ? "1mo" : $"{months}mos";
- }
-
- int years = 1;
- while (Date <= now.AddYears(-(years + 1)))
- years += 1;
- return years == 1 ? "1yr" : $"{years}yrs";
- }
+ => Date.ToShortRelativeTime(TimeSpan.FromHours(1));
}
}
diff --git a/osu.Game/Overlays/OverlayColourProvider.cs b/osu.Game/Overlays/OverlayColourProvider.cs
index e7b3e6d873..7bddb924a0 100644
--- a/osu.Game/Overlays/OverlayColourProvider.cs
+++ b/osu.Game/Overlays/OverlayColourProvider.cs
@@ -11,20 +11,16 @@ namespace osu.Game.Overlays
{
private readonly OverlayColourScheme colourScheme;
- public static OverlayColourProvider Red { get; } = new OverlayColourProvider(OverlayColourScheme.Red);
- public static OverlayColourProvider Pink { get; } = new OverlayColourProvider(OverlayColourScheme.Pink);
- public static OverlayColourProvider Orange { get; } = new OverlayColourProvider(OverlayColourScheme.Orange);
- public static OverlayColourProvider Lime { get; } = new OverlayColourProvider(OverlayColourScheme.Lime);
- public static OverlayColourProvider Green { get; } = new OverlayColourProvider(OverlayColourScheme.Green);
- public static OverlayColourProvider Purple { get; } = new OverlayColourProvider(OverlayColourScheme.Purple);
- public static OverlayColourProvider Blue { get; } = new OverlayColourProvider(OverlayColourScheme.Blue);
- public static OverlayColourProvider Plum { get; } = new OverlayColourProvider(OverlayColourScheme.Plum);
-
public OverlayColourProvider(OverlayColourScheme colourScheme)
{
this.colourScheme = colourScheme;
}
+ // Note that the following five colours are also defined in `OsuColour` as `{colourScheme}{0,1,2,3,4}`.
+ // The difference as to which should be used where comes down to context.
+ // If the colour in question is supposed to always match the view in which it is displayed theme-wise, use `OverlayColourProvider`.
+ // If the colour usage is special and in general differs from the surrounding view in choice of hue, use the `OsuColour` constants.
+ public Color4 Colour0 => getColour(1, 0.8f);
public Color4 Colour1 => getColour(1, 0.7f);
public Color4 Colour2 => getColour(0.8f, 0.6f);
public Color4 Colour3 => getColour(0.6f, 0.5f);
diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
index 79861c0ecc..d5a5aa4592 100644
--- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
+++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs
@@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Scoring
private double getScore(ScoringMode mode)
{
- return GetScore(mode, maxAchievableCombo,
+ return GetScore(mode,
calculateAccuracyRatio(baseScore),
calculateComboRatio(HighestCombo.Value),
scoreResultCounts);
@@ -222,12 +222,11 @@ namespace osu.Game.Rulesets.Scoring
/// Computes the total score.
///
/// The to compute the total score in.
- /// The maximum combo achievable in the beatmap.
/// The accuracy percentage achieved by the player.
- /// The proportion of achieved by the player.
+ /// The proportion of the max combo achieved by the player.
/// Any statistics to be factored in.
/// The total score.
- public double GetScore(ScoringMode mode, int maxCombo, double accuracyRatio, double comboRatio, Dictionary statistics)
+ public double GetScore(ScoringMode mode, double accuracyRatio, double comboRatio, Dictionary statistics)
{
switch (mode)
{
@@ -238,10 +237,16 @@ namespace osu.Game.Rulesets.Scoring
return (max_score * (accuracyScore + comboScore) + getBonusScore(statistics)) * scoreMultiplier;
case ScoringMode.Classic:
+ int totalHitObjects = statistics.Where(k => k.Key >= HitResult.Miss && k.Key <= HitResult.Perfect).Sum(k => k.Value);
+
+ // If there are no hitobjects then the beatmap can be composed of only ticks or spinners, so ensure we don't multiply by 0 at all times.
+ if (totalHitObjects == 0)
+ totalHitObjects = 1;
+
// This gives a similar feeling to osu!stable scoring (ScoreV1) while keeping classic scoring as only a constant multiple of standardised scoring.
// The invariant is important to ensure that scores don't get re-ordered on leaderboards between the two scoring modes.
- double scaledStandardised = GetScore(ScoringMode.Standardised, maxCombo, accuracyRatio, comboRatio, statistics) / max_score;
- return Math.Pow(scaledStandardised * (maxCombo + 1), 2) * 18;
+ double scaledStandardised = GetScore(ScoringMode.Standardised, accuracyRatio, comboRatio, statistics) / max_score;
+ return Math.Pow(scaledStandardised * totalHitObjects, 2) * 36;
}
}
@@ -265,7 +270,7 @@ namespace osu.Game.Rulesets.Scoring
computedBaseScore += Judgement.ToNumericResult(pair.Key) * pair.Value;
}
- return GetScore(mode, maxAchievableCombo, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), statistics);
+ return GetScore(mode, calculateAccuracyRatio(computedBaseScore), calculateComboRatio(maxCombo), statistics);
}
///
diff --git a/osu.Game/Rulesets/UI/ModSwitchSmall.cs b/osu.Game/Rulesets/UI/ModSwitchSmall.cs
new file mode 100644
index 0000000000..676bbac95c
--- /dev/null
+++ b/osu.Game/Rulesets/UI/ModSwitchSmall.cs
@@ -0,0 +1,109 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Sprites;
+using osu.Framework.Utils;
+using osu.Game.Graphics;
+using osu.Game.Overlays;
+using osu.Game.Rulesets.Mods;
+using osuTK;
+using osuTK.Graphics;
+
+#nullable enable
+
+namespace osu.Game.Rulesets.UI
+{
+ public class ModSwitchSmall : CompositeDrawable
+ {
+ public BindableBool Active { get; } = new BindableBool();
+
+ public const float DEFAULT_SIZE = 60;
+
+ private readonly IMod mod;
+
+ private readonly SpriteIcon background;
+ private readonly SpriteIcon? modIcon;
+
+ private Color4 activeForegroundColour;
+ private Color4 inactiveForegroundColour;
+
+ private Color4 activeBackgroundColour;
+ private Color4 inactiveBackgroundColour;
+
+ public ModSwitchSmall(IMod mod)
+ {
+ this.mod = mod;
+
+ AutoSizeAxes = Axes.Both;
+
+ FillFlowContainer contentFlow;
+ ModSwitchTiny tinySwitch;
+
+ InternalChildren = new Drawable[]
+ {
+ background = new SpriteIcon
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Size = new Vector2(DEFAULT_SIZE),
+ Icon = OsuIcon.ModBg
+ },
+ contentFlow = new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Spacing = new Vector2(0, 4),
+ Direction = FillDirection.Vertical,
+ Child = tinySwitch = new ModSwitchTiny(mod)
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Scale = new Vector2(0.6f),
+ Active = { BindTarget = Active }
+ }
+ }
+ };
+
+ if (mod.Icon != null)
+ {
+ contentFlow.Insert(-1, modIcon = new SpriteIcon
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ Size = new Vector2(21),
+ Icon = mod.Icon.Value
+ });
+ tinySwitch.Scale = new Vector2(0.3f);
+ }
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuColour colours, OverlayColourProvider? colourProvider)
+ {
+ inactiveForegroundColour = colourProvider?.Background5 ?? colours.Gray3;
+ activeForegroundColour = colours.ForModType(mod.Type);
+
+ inactiveBackgroundColour = colourProvider?.Background2 ?? colours.Gray5;
+ activeBackgroundColour = Interpolation.ValueAt(0.1f, Colour4.Black, activeForegroundColour, 0, 1);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Active.BindValueChanged(_ => updateState(), true);
+ FinishTransforms(true);
+ }
+
+ private void updateState()
+ {
+ modIcon?.FadeColour(Active.Value ? activeForegroundColour : inactiveForegroundColour, 200, Easing.OutQuint);
+ background.FadeColour(Active.Value ? activeBackgroundColour : inactiveBackgroundColour, 200, Easing.OutQuint);
+ }
+ }
+}
diff --git a/osu.Game/Rulesets/UI/ModSwitchTiny.cs b/osu.Game/Rulesets/UI/ModSwitchTiny.cs
new file mode 100644
index 0000000000..b1d453f588
--- /dev/null
+++ b/osu.Game/Rulesets/UI/ModSwitchTiny.cs
@@ -0,0 +1,93 @@
+// 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.Bindables;
+using osu.Framework.Graphics;
+using osu.Framework.Graphics.Containers;
+using osu.Framework.Graphics.Shapes;
+using osu.Framework.Utils;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays;
+using osu.Game.Rulesets.Mods;
+using osuTK;
+using osuTK.Graphics;
+
+#nullable enable
+
+namespace osu.Game.Rulesets.UI
+{
+ public class ModSwitchTiny : CompositeDrawable
+ {
+ public BindableBool Active { get; } = new BindableBool();
+
+ public const float DEFAULT_HEIGHT = 30;
+
+ private readonly IMod mod;
+
+ private readonly Box background;
+ private readonly OsuSpriteText acronymText;
+
+ private Color4 activeForegroundColour;
+ private Color4 inactiveForegroundColour;
+
+ private Color4 activeBackgroundColour;
+ private Color4 inactiveBackgroundColour;
+
+ public ModSwitchTiny(IMod mod)
+ {
+ this.mod = mod;
+ Size = new Vector2(73, DEFAULT_HEIGHT);
+
+ InternalChild = new CircularContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Masking = true,
+ Children = new Drawable[]
+ {
+ background = new Box
+ {
+ RelativeSizeAxes = Axes.Both
+ },
+ acronymText = new OsuSpriteText
+ {
+ Anchor = Anchor.Centre,
+ Origin = Anchor.Centre,
+ Shadow = false,
+ Font = OsuFont.Numeric.With(size: 24, weight: FontWeight.Black),
+ Text = mod.Acronym,
+ Margin = new MarginPadding
+ {
+ Top = 4
+ }
+ }
+ }
+ };
+ }
+
+ [BackgroundDependencyLoader(true)]
+ private void load(OsuColour colours, OverlayColourProvider? colourProvider)
+ {
+ inactiveBackgroundColour = colourProvider?.Background5 ?? colours.Gray3;
+ activeBackgroundColour = colours.ForModType(mod.Type);
+
+ inactiveForegroundColour = colourProvider?.Background2 ?? colours.Gray5;
+ activeForegroundColour = Interpolation.ValueAt(0.1f, Colour4.Black, activeForegroundColour, 0, 1);
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+
+ Active.BindValueChanged(_ => updateState(), true);
+ FinishTransforms(true);
+ }
+
+ private void updateState()
+ {
+ acronymText.FadeColour(Active.Value ? activeForegroundColour : inactiveForegroundColour, 200, Easing.OutQuint);
+ background.FadeColour(Active.Value ? activeBackgroundColour : inactiveBackgroundColour, 200, Easing.OutQuint);
+ }
+ }
+}
diff --git a/osu.Game/Scoring/ScoreManager.cs b/osu.Game/Scoring/ScoreManager.cs
index 532c6b42a3..963c4a77ca 100644
--- a/osu.Game/Scoring/ScoreManager.cs
+++ b/osu.Game/Scoring/ScoreManager.cs
@@ -184,7 +184,7 @@ namespace osu.Game.Scoring
var scoreProcessor = ruleset.CreateScoreProcessor();
scoreProcessor.Mods.Value = score.Mods;
- return (long)Math.Round(scoreProcessor.GetScore(mode, beatmapMaxCombo, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics));
+ return (long)Math.Round(scoreProcessor.GetScore(mode, accuracy, (double)score.MaxCombo / beatmapMaxCombo, score.Statistics));
}
///
diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs
index 98c4b15f7f..afe75c5ef7 100644
--- a/osu.Game/Screens/Menu/IntroScreen.cs
+++ b/osu.Game/Screens/Menu/IntroScreen.cs
@@ -19,6 +19,7 @@ using osu.Game.Database;
using osu.Game.IO.Archives;
using osu.Game.Overlays;
using osu.Game.Overlays.Notifications;
+using osu.Game.Rulesets;
using osu.Game.Screens.Backgrounds;
using osuTK;
using osuTK.Graphics;
@@ -71,6 +72,9 @@ namespace osu.Game.Screens.Menu
[CanBeNull]
private readonly Func createNextScreen;
+ [Resolved]
+ private RulesetStore rulesets { get; set; }
+
///
/// Whether the is provided by osu! resources, rather than a user beatmap.
/// Only valid during or after .
@@ -117,7 +121,11 @@ namespace osu.Game.Screens.Menu
// we generally want a song to be playing on startup, so use the intro music even if a user has specified not to if no other track is available.
if (initialBeatmap == null)
{
- if (!loadThemedIntro())
+ // Intro beatmaps are generally made using the osu! ruleset.
+ // It might not be present in test projects for other rulesets.
+ bool osuRulesetPresent = rulesets.GetRuleset(0) != null;
+
+ if (!loadThemedIntro() && osuRulesetPresent)
{
// if we detect that the theme track or beatmap is unavailable this is either first startup or things are in a bad state.
// this could happen if a user has nuked their files store. for now, reimport to repair this.
diff --git a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
index 9f917c978c..3260427192 100644
--- a/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
+++ b/osu.Game/Screens/OnlinePlay/Lounge/Components/RoomsContainer.cs
@@ -124,7 +124,12 @@ namespace osu.Game.Screens.OnlinePlay.Lounge.Components
private void updateSorting()
{
foreach (var room in roomFlow)
- roomFlow.SetLayoutPosition(room, -(room.Room.RoomID.Value ?? 0));
+ {
+ roomFlow.SetLayoutPosition(room, room.Room.Category.Value == RoomCategory.Spotlight
+ // Always show spotlight playlists at the top of the listing.
+ ? float.MinValue
+ : -(room.Room.RoomID.Value ?? 0));
+ }
}
protected override bool OnClick(ClickEvent e)
diff --git a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
index 542851cb0f..338a9c856f 100644
--- a/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
+++ b/osu.Game/Screens/OnlinePlay/Playlists/PlaylistsRoomSubScreen.cs
@@ -69,132 +69,155 @@ namespace osu.Game.Screens.OnlinePlay.Playlists
Room.MaxAttempts.BindValueChanged(attempts => progressSection.Alpha = Room.MaxAttempts.Value != null ? 1 : 0, true);
}
- protected override Drawable CreateMainContent() => new GridContainer
+ protected override Drawable CreateMainContent() => new Container
{
RelativeSizeAxes = Axes.Both,
- Content = new[]
+ Padding = new MarginPadding { Horizontal = 5, Vertical = 10 },
+ Child = new GridContainer
{
- new Drawable[]
+ RelativeSizeAxes = Axes.Both,
+ ColumnDimensions = new[]
{
- new Container
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 10),
+ new Dimension(),
+ new Dimension(GridSizeMode.Absolute, 10),
+ new Dimension(),
+ },
+ Content = new[]
+ {
+ new Drawable[]
{
- RelativeSizeAxes = Axes.Both,
- Padding = new MarginPadding { Right = 5 },
- Child = new GridContainer
+ // Playlist items column
+ new Container
+ {
+ RelativeSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Right = 5 },
+ Child = new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[] { new OverlinedPlaylistHeader(), },
+ new Drawable[]
+ {
+ new DrawableRoomPlaylist
+ {
+ RelativeSizeAxes = Axes.Both,
+ Items = { BindTarget = Room.Playlist },
+ SelectedItem = { BindTarget = SelectedItem },
+ AllowSelection = true,
+ AllowShowingResults = true,
+ RequestResults = item =>
+ {
+ Debug.Assert(RoomId.Value != null);
+ ParentScreen?.Push(new PlaylistsResultsScreen(null, RoomId.Value.Value, item, false));
+ }
+ }
+ },
+ },
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(),
+ }
+ }
+ },
+ // Spacer
+ null,
+ // Middle column (mods and leaderboard)
+ new GridContainer
{
RelativeSizeAxes = Axes.Both,
Content = new[]
{
- new Drawable[] { new OverlinedPlaylistHeader(), },
+ new[]
+ {
+ UserModsSection = new FillFlowContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Alpha = 0,
+ Margin = new MarginPadding { Bottom = 10 },
+ Children = new Drawable[]
+ {
+ new OverlinedHeader("Extra mods"),
+ new FillFlowContainer
+ {
+ AutoSizeAxes = Axes.Both,
+ Direction = FillDirection.Horizontal,
+ Spacing = new Vector2(10, 0),
+ Children = new Drawable[]
+ {
+ new UserModSelectButton
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Width = 90,
+ Text = "Select",
+ Action = ShowUserModSelect,
+ },
+ new ModDisplay
+ {
+ Anchor = Anchor.CentreLeft,
+ Origin = Anchor.CentreLeft,
+ Current = UserMods,
+ Scale = new Vector2(0.8f),
+ },
+ }
+ }
+ }
+ },
+ },
new Drawable[]
{
- new DrawableRoomPlaylist
+ progressSection = new FillFlowContainer
{
- RelativeSizeAxes = Axes.Both,
- Items = { BindTarget = Room.Playlist },
- SelectedItem = { BindTarget = SelectedItem },
- AllowSelection = true,
- AllowShowingResults = true,
- RequestResults = item =>
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Alpha = 0,
+ Margin = new MarginPadding { Bottom = 10 },
+ Direction = FillDirection.Vertical,
+ Children = new Drawable[]
{
- Debug.Assert(RoomId.Value != null);
- ParentScreen?.Push(new PlaylistsResultsScreen(null, RoomId.Value.Value, item, false));
+ new OverlinedHeader("Progress"),
+ new RoomLocalUserInfo(),
}
- }
+ },
},
+ new Drawable[]
+ {
+ new OverlinedHeader("Leaderboard")
+ },
+ new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
+ },
+ RowDimensions = new[]
+ {
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(GridSizeMode.AutoSize),
+ new Dimension(),
+ }
+ },
+ // Spacer
+ null,
+ // Main right column
+ new GridContainer
+ {
+ RelativeSizeAxes = Axes.Both,
+ Content = new[]
+ {
+ new Drawable[] { new OverlinedHeader("Chat") },
+ new Drawable[] { new MatchChatDisplay(Room) { RelativeSizeAxes = Axes.Both } }
},
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
new Dimension(),
}
- }
- },
- null,
- new GridContainer
- {
- RelativeSizeAxes = Axes.Both,
- Content = new[]
- {
- new[]
- {
- UserModsSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Alpha = 0,
- Margin = new MarginPadding { Bottom = 10 },
- Children = new Drawable[]
- {
- new OverlinedHeader("Extra mods"),
- new FillFlowContainer
- {
- AutoSizeAxes = Axes.Both,
- Direction = FillDirection.Horizontal,
- Spacing = new Vector2(10, 0),
- Children = new Drawable[]
- {
- new UserModSelectButton
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Width = 90,
- Text = "Select",
- Action = ShowUserModSelect,
- },
- new ModDisplay
- {
- Anchor = Anchor.CentreLeft,
- Origin = Anchor.CentreLeft,
- Current = UserMods,
- Scale = new Vector2(0.8f),
- },
- }
- }
- }
- },
- },
- new Drawable[]
- {
- progressSection = new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Alpha = 0,
- Margin = new MarginPadding { Bottom = 10 },
- Direction = FillDirection.Vertical,
- Children = new Drawable[]
- {
- new OverlinedHeader("Progress"),
- new RoomLocalUserInfo(),
- }
- },
- },
- new Drawable[]
- {
- new OverlinedHeader("Leaderboard")
- },
- new Drawable[] { leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }, },
- new Drawable[] { new OverlinedHeader("Chat"), },
- new Drawable[] { new MatchChatDisplay(Room) { RelativeSizeAxes = Axes.Both } }
},
- RowDimensions = new[]
- {
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(),
- new Dimension(GridSizeMode.AutoSize),
- new Dimension(GridSizeMode.Relative, size: 0.4f, minSize: 120),
- }
},
},
- },
- ColumnDimensions = new[]
- {
- new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 400),
- new Dimension(),
- new Dimension(GridSizeMode.Relative, size: 0.5f, maxSize: 600),
}
};
diff --git a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
index 8dfd969c51..6abcb2924c 100644
--- a/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
+++ b/osu.Game/Tests/Visual/OnlinePlay/TestRoomManager.cs
@@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
base.JoinRoom(room, password, onSuccess, onError);
}
- public void AddRooms(int count, RulesetInfo ruleset = null, bool withPassword = false)
+ public void AddRooms(int count, RulesetInfo ruleset = null, bool withPassword = false, bool withSpotlightRooms = false)
{
for (int i = 0; i < count; i++)
{
@@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.OnlinePlay
Name = { Value = $@"Room {currentRoomId}" },
Host = { Value = new APIUser { Username = @"Host" } },
EndDate = { Value = DateTimeOffset.Now + TimeSpan.FromSeconds(10) },
- Category = { Value = i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal },
+ Category = { Value = withSpotlightRooms && i % 2 == 0 ? RoomCategory.Spotlight : RoomCategory.Normal },
};
if (withPassword)
diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs
index cd675e467b..1107089a46 100644
--- a/osu.Game/Tests/Visual/SkinnableTestScene.cs
+++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs
@@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual
[BackgroundDependencyLoader]
private void load()
{
- var dllStore = new DllResourceStore(DynamicCompilationOriginal.GetType().Assembly);
+ var dllStore = new DllResourceStore(GetType().Assembly);
metricsSkin = new TestLegacySkin(new SkinInfo { Name = "metrics-skin" }, new NamespacedResourceStore(dllStore, "Resources/metrics_skin"), this, true);
defaultSkin = new DefaultLegacySkin(this);
diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj
index 4c6f81defa..7dfd099df1 100644
--- a/osu.Game/osu.Game.csproj
+++ b/osu.Game/osu.Game.csproj
@@ -36,7 +36,7 @@
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/osu.iOS.props b/osu.iOS.props
index 99b9de3fe2..9d0e1790f0 100644
--- a/osu.iOS.props
+++ b/osu.iOS.props
@@ -60,7 +60,7 @@
-
+
@@ -83,7 +83,7 @@
-
+