diff --git a/.gitattributes b/.gitattributes
index baf69b41d1..e3ffd343db 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -14,6 +14,7 @@ App.config text eol=crlf
*.bat text eol=crlf
*.cmd text eol=crlf
*.snippet text eol=crlf
+*.manifest text eol=crlf
# Check out with lf (UNIX) line endings
*.sh text eol=lf
diff --git a/app.manifest b/app.manifest
new file mode 100644
index 0000000000..533c6ff208
--- /dev/null
+++ b/app.manifest
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+
+
+
+
+
+
+
+
diff --git a/appveyor_deploy.yml b/appveyor_deploy.yml
new file mode 100644
index 0000000000..cd241eb88f
--- /dev/null
+++ b/appveyor_deploy.yml
@@ -0,0 +1,30 @@
+# 2017-09-14
+clone_depth: 1
+version: '{branch}-{build}'
+image: Visual Studio 2017
+configuration: Debug
+cache:
+ - packages -> **\packages.config
+install:
+ - cmd: git submodule update --init --recursive --depth=5
+before_build:
+ - cmd: nuget restore -verbosity quiet
+build:
+ project: osu.Desktop.Deploy/osu.Desktop.Deploy.csproj
+ verbosity: minimal
+after_build:
+ - ps: iex ((New-Object Net.WebClient).DownloadString('https://raw.githubusercontent.com/appveyor/secure-file/master/install.ps1'))
+ - appveyor DownloadFile https://puu.sh/A6g5K/4d08705438.enc # signing certificate
+ - cmd: appveyor-tools\secure-file -decrypt 4d08705438.enc -secret %decode_secret% -out %HOMEPATH%\deanherbert.pfx
+ - appveyor DownloadFile https://puu.sh/A6g75/fdc6f19b04.enc # deploy configuration
+ - cmd: appveyor-tools\secure-file -decrypt fdc6f19b04.enc -secret %decode_secret% -out osu.Desktop.Deploy\bin\Debug\net461\osu.Desktop.Deploy.exe.config
+ - cd osu.Desktop.Deploy\bin\Debug\net461\
+ - osu.Desktop.Deploy.exe %code_signing_password%
+environment:
+ TargetFramework: net461
+ decode_secret:
+ secure: i67IC2xj6DjjxmA6Oj2jing3+MwzLkq6CbGsjfZ7rdY=
+ code_signing_password:
+ secure: 34tLNqvjmmZEi97MLKfrnQ==
+artifacts:
+ - path: 'Releases\*'
\ No newline at end of file
diff --git a/osu-framework b/osu-framework
index 7e8788e601..16e6a453db 160000
--- a/osu-framework
+++ b/osu-framework
@@ -1 +1 @@
-Subproject commit 7e8788e601b62577e51197a29e24f56eeeac0286
+Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a
diff --git a/osu.Desktop.Deploy/Program.cs b/osu.Desktop.Deploy/Program.cs
index 6095ce062d..8c460f03cf 100644
--- a/osu.Desktop.Deploy/Program.cs
+++ b/osu.Desktop.Deploy/Program.cs
@@ -57,8 +57,12 @@ namespace osu.Desktop.Deploy
private static string codeSigningPassword;
+ private static bool interactive;
+
public static void Main(string[] args)
{
+ interactive = args.Length == 0;
+
displayHeader();
findSolutionPath();
@@ -82,15 +86,15 @@ namespace osu.Desktop.Deploy
string version = $"{verBase}{increment}";
Console.ForegroundColor = ConsoleColor.White;
- Console.Write($"Ready to deploy {version}: ");
- Console.ReadLine();
+ Console.Write($"Ready to deploy {version}!");
+ pauseIfInteractive();
sw.Start();
if (!string.IsNullOrEmpty(CodeSigningCertificate))
{
Console.Write("Enter code signing password: ");
- codeSigningPassword = readLineMasked();
+ codeSigningPassword = args.Length > 0 ? args[0] : readLineMasked();
}
write("Updating AssemblyInfo...");
@@ -124,7 +128,7 @@ namespace osu.Desktop.Deploy
updateCsprojVersion("0.0.0");
write("Done!", ConsoleColor.White);
- Console.ReadLine();
+ pauseIfInteractive();
}
private static void displayHeader()
@@ -388,10 +392,18 @@ namespace osu.Desktop.Deploy
Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"FATAL ERROR: {message}");
- Console.ReadLine();
+ pauseIfInteractive();
Environment.Exit(-1);
}
+ private static void pauseIfInteractive()
+ {
+ if (interactive)
+ Console.ReadLine();
+ else
+ Console.WriteLine();
+ }
+
private static void write(string message, ConsoleColor col = ConsoleColor.Gray)
{
if (sw.ElapsedMilliseconds > 0)
diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs
index 8930b09089..cfe0fc5cec 100644
--- a/osu.Game.Rulesets.Catch/CatchRuleset.cs
+++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Replays.Types;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Catch
{
@@ -29,6 +30,44 @@ namespace osu.Game.Rulesets.Catch
new KeyBinding(InputKey.Shift, CatchAction.Dash),
};
+ public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ {
+ if (mods.HasFlag(LegacyMods.Nightcore))
+ yield return new CatchModNightcore();
+ else if (mods.HasFlag(LegacyMods.DoubleTime))
+ yield return new CatchModDoubleTime();
+
+ if (mods.HasFlag(LegacyMods.Autoplay))
+ yield return new CatchModAutoplay();
+
+ if (mods.HasFlag(LegacyMods.Easy))
+ yield return new CatchModEasy();
+
+ if (mods.HasFlag(LegacyMods.Flashlight))
+ yield return new CatchModFlashlight();
+
+ if (mods.HasFlag(LegacyMods.HalfTime))
+ yield return new CatchModHalfTime();
+
+ if (mods.HasFlag(LegacyMods.HardRock))
+ yield return new CatchModHardRock();
+
+ if (mods.HasFlag(LegacyMods.Hidden))
+ yield return new CatchModHidden();
+
+ if (mods.HasFlag(LegacyMods.NoFail))
+ yield return new CatchModNoFail();
+
+ if (mods.HasFlag(LegacyMods.Perfect))
+ yield return new CatchModPerfect();
+
+ if (mods.HasFlag(LegacyMods.Relax))
+ yield return new CatchModRelax();
+
+ if (mods.HasFlag(LegacyMods.SuddenDeath))
+ yield return new CatchModSuddenDeath();
+ }
+
public override IEnumerable GetModsFor(ModType type)
{
switch (type)
diff --git a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs
index 9e48e8de74..df7578799f 100644
--- a/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs
+++ b/osu.Game.Rulesets.Catch/Mods/CatchModHardRock.cs
@@ -1,13 +1,88 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.MathUtils;
+using osu.Game.Rulesets.Catch.Objects;
+using osu.Game.Rulesets.Catch.UI;
using osu.Game.Rulesets.Mods;
+using System;
namespace osu.Game.Rulesets.Catch.Mods
{
- public class CatchModHardRock : ModHardRock
+ public class CatchModHardRock : ModHardRock, IApplicableToHitObject
{
public override double ScoreMultiplier => 1.12;
public override bool Ranked => true;
+
+ private float lastStartX;
+ private int lastStartTime;
+
+ public void ApplyToHitObject(CatchHitObject hitObject)
+ {
+ float position = hitObject.X;
+ int startTime = (int)hitObject.StartTime;
+
+ if (lastStartX == 0)
+ {
+ lastStartX = position;
+ lastStartTime = startTime;
+ return;
+ }
+
+ float diff = lastStartX - position;
+ int timeDiff = startTime - lastStartTime;
+
+ if (timeDiff > 1000)
+ {
+ lastStartX = position;
+ lastStartTime = startTime;
+ return;
+ }
+
+ if (diff == 0)
+ {
+ bool right = RNG.NextBool();
+
+ float rand = Math.Min(20, (float)RNG.NextDouble(0, timeDiff / 4d)) / CatchPlayfield.BASE_WIDTH;
+
+ if (right)
+ {
+ if (position + rand <= 1)
+ position += rand;
+ else
+ position -= rand;
+ }
+ else
+ {
+ if (position - rand >= 0)
+ position -= rand;
+ else
+ position += rand;
+ }
+
+ hitObject.X = position;
+
+ return;
+ }
+
+ if (Math.Abs(diff) < timeDiff / 3d)
+ {
+ if (diff > 0)
+ {
+ if (position - diff > 0)
+ position -= diff;
+ }
+ else
+ {
+ if (position - diff < 1)
+ position -= diff;
+ }
+ }
+
+ hitObject.X = position;
+
+ lastStartX = position;
+ lastStartTime = startTime;
+ }
}
}
diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
index 7f37f55d14..0546cbc174 100644
--- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs
+++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs
@@ -14,6 +14,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays.Types;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Mania
{
@@ -21,6 +22,74 @@ namespace osu.Game.Rulesets.Mania
{
public override RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset);
+ public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ {
+ if (mods.HasFlag(LegacyMods.Nightcore))
+ yield return new ManiaModNightcore();
+ else if (mods.HasFlag(LegacyMods.DoubleTime))
+ yield return new ManiaModDoubleTime();
+
+ if (mods.HasFlag(LegacyMods.Autoplay))
+ yield return new ManiaModAutoplay();
+
+ if (mods.HasFlag(LegacyMods.Easy))
+ yield return new ManiaModEasy();
+
+ if (mods.HasFlag(LegacyMods.FadeIn))
+ yield return new ManiaModFadeIn();
+
+ if (mods.HasFlag(LegacyMods.Flashlight))
+ yield return new ManiaModFlashlight();
+
+ if (mods.HasFlag(LegacyMods.HalfTime))
+ yield return new ManiaModHalfTime();
+
+ if (mods.HasFlag(LegacyMods.HardRock))
+ yield return new ManiaModHardRock();
+
+ if (mods.HasFlag(LegacyMods.Hidden))
+ yield return new ManiaModHidden();
+
+ if (mods.HasFlag(LegacyMods.Key1))
+ yield return new ManiaModKey1();
+
+ if (mods.HasFlag(LegacyMods.Key2))
+ yield return new ManiaModKey2();
+
+ if (mods.HasFlag(LegacyMods.Key3))
+ yield return new ManiaModKey3();
+
+ if (mods.HasFlag(LegacyMods.Key4))
+ yield return new ManiaModKey4();
+
+ if (mods.HasFlag(LegacyMods.Key5))
+ yield return new ManiaModKey5();
+
+ if (mods.HasFlag(LegacyMods.Key6))
+ yield return new ManiaModKey6();
+
+ if (mods.HasFlag(LegacyMods.Key7))
+ yield return new ManiaModKey7();
+
+ if (mods.HasFlag(LegacyMods.Key8))
+ yield return new ManiaModKey8();
+
+ if (mods.HasFlag(LegacyMods.Key9))
+ yield return new ManiaModKey9();
+
+ if (mods.HasFlag(LegacyMods.NoFail))
+ yield return new ManiaModNoFail();
+
+ if (mods.HasFlag(LegacyMods.Perfect))
+ yield return new ManiaModPerfect();
+
+ if (mods.HasFlag(LegacyMods.Random))
+ yield return new ManiaModRandom();
+
+ if (mods.HasFlag(LegacyMods.SuddenDeath))
+ yield return new ManiaModSuddenDeath();
+ }
+
public override IEnumerable GetModsFor(ModType type)
{
switch (type)
diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs
index 37e6ec3817..e0ecee97a3 100644
--- a/osu.Game.Rulesets.Osu/OsuRuleset.cs
+++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs
@@ -21,6 +21,7 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Replays.Types;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Osu
{
@@ -66,6 +67,53 @@ namespace osu.Game.Rulesets.Osu
};
}
+ public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ {
+ if (mods.HasFlag(LegacyMods.Nightcore))
+ yield return new OsuModNightcore();
+ else if (mods.HasFlag(LegacyMods.DoubleTime))
+ yield return new OsuModDoubleTime();
+
+ if (mods.HasFlag(LegacyMods.Autopilot))
+ yield return new OsuModAutopilot();
+
+ if (mods.HasFlag(LegacyMods.Autoplay))
+ yield return new OsuModAutoplay();
+
+ if (mods.HasFlag(LegacyMods.Easy))
+ yield return new OsuModEasy();
+
+ if (mods.HasFlag(LegacyMods.Flashlight))
+ yield return new OsuModFlashlight();
+
+ if (mods.HasFlag(LegacyMods.HalfTime))
+ yield return new OsuModHalfTime();
+
+ if (mods.HasFlag(LegacyMods.HardRock))
+ yield return new OsuModHardRock();
+
+ if (mods.HasFlag(LegacyMods.Hidden))
+ yield return new OsuModHidden();
+
+ if (mods.HasFlag(LegacyMods.NoFail))
+ yield return new OsuModNoFail();
+
+ if (mods.HasFlag(LegacyMods.Perfect))
+ yield return new OsuModPerfect();
+
+ if (mods.HasFlag(LegacyMods.Relax))
+ yield return new OsuModRelax();
+
+ if (mods.HasFlag(LegacyMods.SpunOut))
+ yield return new OsuModSpunOut();
+
+ if (mods.HasFlag(LegacyMods.SuddenDeath))
+ yield return new OsuModSuddenDeath();
+
+ if (mods.HasFlag(LegacyMods.Target))
+ yield return new OsuModTarget();
+ }
+
public override IEnumerable GetModsFor(ModType type)
{
switch (type)
diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
index cb5e020601..06a8e44a14 100644
--- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
+++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs
@@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Taiko.Replays;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Taiko
{
@@ -31,6 +32,44 @@ namespace osu.Game.Rulesets.Taiko
new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim),
};
+ public override IEnumerable ConvertLegacyMods(LegacyMods mods)
+ {
+ if (mods.HasFlag(LegacyMods.Nightcore))
+ yield return new TaikoModNightcore();
+ else if (mods.HasFlag(LegacyMods.DoubleTime))
+ yield return new TaikoModDoubleTime();
+
+ if (mods.HasFlag(LegacyMods.Autoplay))
+ yield return new TaikoModAutoplay();
+
+ if (mods.HasFlag(LegacyMods.Easy))
+ yield return new TaikoModEasy();
+
+ if (mods.HasFlag(LegacyMods.Flashlight))
+ yield return new TaikoModFlashlight();
+
+ if (mods.HasFlag(LegacyMods.HalfTime))
+ yield return new TaikoModHalfTime();
+
+ if (mods.HasFlag(LegacyMods.HardRock))
+ yield return new TaikoModHardRock();
+
+ if (mods.HasFlag(LegacyMods.Hidden))
+ yield return new TaikoModHidden();
+
+ if (mods.HasFlag(LegacyMods.NoFail))
+ yield return new TaikoModNoFail();
+
+ if (mods.HasFlag(LegacyMods.Perfect))
+ yield return new TaikoModPerfect();
+
+ if (mods.HasFlag(LegacyMods.Relax))
+ yield return new TaikoModRelax();
+
+ if (mods.HasFlag(LegacyMods.SuddenDeath))
+ yield return new TaikoModSuddenDeath();
+ }
+
public override IEnumerable GetModsFor(ModType type)
{
switch (type)
diff --git a/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs b/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs
new file mode 100644
index 0000000000..8177e2e272
--- /dev/null
+++ b/osu.Game.Tests/Visual/TestCaseBadgeContainer.cs
@@ -0,0 +1,62 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using NUnit.Framework;
+using osu.Framework.Graphics;
+using osu.Game.Overlays.Profile.Header;
+using osu.Game.Users;
+
+namespace osu.Game.Tests.Visual
+{
+ [TestFixture]
+ public class TestCaseBadgeContainer : OsuTestCase
+ {
+ public override IReadOnlyList RequiredTypes => new[] { typeof(BadgeContainer) };
+
+ public TestCaseBadgeContainer()
+ {
+ BadgeContainer badgeContainer;
+
+ Child = badgeContainer = new BadgeContainer
+ {
+ RelativeSizeAxes = Axes.Both
+ };
+
+ AddStep("Show 1 badge", () => badgeContainer.ShowBadges(new[]
+ {
+ new Badge
+ {
+ AwardedAt = DateTimeOffset.Now,
+ Description = "Appreciates compasses",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png",
+ }
+ }));
+
+ AddStep("Show 2 badges", () => badgeContainer.ShowBadges(new[]
+ {
+ new Badge
+ {
+ AwardedAt = DateTimeOffset.Now,
+ Description = "Contributed to osu!lazer testing",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.png",
+ },
+ new Badge
+ {
+ AwardedAt = DateTimeOffset.Now,
+ Description = "Appreciates compasses",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/mg2018-1star.png",
+ }
+ }));
+
+ AddStep("Show many badges", () => badgeContainer.ShowBadges(Enumerable.Range(1, 20).Select(i => new Badge
+ {
+ AwardedAt = DateTimeOffset.Now,
+ Description = $"Contributed to osu!lazer testing {i} times",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg",
+ }).ToArray()));
+ }
+ }
+}
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
index 6cb6a342a8..5be7386238 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapScoresContainer.cs
@@ -14,6 +14,8 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Users;
using System.Collections.Generic;
using osu.Framework.Graphics.Containers;
+using osu.Game.Beatmaps;
+using osu.Game.Rulesets.Osu;
namespace osu.Game.Tests.Visual
{
@@ -47,11 +49,11 @@ namespace osu.Game.Tests.Visual
AddStep("scores pack 1", () => scoresContainer.Scores = scores);
AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
AddStep("only top score", () => scoresContainer.Scores = new[] { topScore });
- AddStep("remove scores", scoresContainer.CleanAllScores);
- AddStep("turn on loading", () => scoresContainer.IsLoading = true);
- AddStep("turn off loading", () => scoresContainer.IsLoading = false);
+ AddStep("remove scores", () => scoresContainer.Scores = null);
AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 300));
+ AddStep("online scores", () => scoresContainer.Beatmap = new BeatmapInfo { OnlineBeatmapSetID = 1, OnlineBeatmapID = 75, Ruleset = new OsuRuleset().RulesetInfo });
+
scores = new[]
{
diff --git a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs
index 69955a90c4..025562f75f 100644
--- a/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs
+++ b/osu.Game.Tests/Visual/TestCaseBeatmapSetOverlay.cs
@@ -8,6 +8,9 @@ using NUnit.Framework;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Overlays;
+using osu.Game.Overlays.BeatmapSet;
+using osu.Game.Overlays.BeatmapSet.Buttons;
+using osu.Game.Overlays.BeatmapSet.Scores;
using osu.Game.Rulesets;
using osu.Game.Users;
@@ -18,6 +21,26 @@ namespace osu.Game.Tests.Visual
{
private readonly BeatmapSetOverlay overlay;
+ public override IReadOnlyList RequiredTypes => new[]
+ {
+ typeof(Header),
+ typeof(ClickableUsername),
+ typeof(DrawableScore),
+ typeof(DrawableTopScore),
+ typeof(ScoresContainer),
+ typeof(AuthorInfo),
+ typeof(BasicStats),
+ typeof(BeatmapPicker),
+ typeof(Details),
+ typeof(DownloadButton),
+ typeof(FavouriteButton),
+ typeof(Header),
+ typeof(HeaderButton),
+ typeof(Info),
+ typeof(PreviewButton),
+ typeof(SuccessRate),
+ };
+
public TestCaseBeatmapSetOverlay()
{
Add(overlay = new BeatmapSetOverlay());
@@ -29,6 +52,10 @@ namespace osu.Game.Tests.Visual
var mania = rulesets.GetRuleset(3);
var taiko = rulesets.GetRuleset(1);
+ AddStep(@"show loading", () => overlay.ShowBeatmapSet(null));
+
+ AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55));
+
AddStep(@"show first", () =>
{
overlay.ShowBeatmapSet(new BeatmapSetInfo
diff --git a/osu.Game.Tests/Visual/TestCaseRankGraph.cs b/osu.Game.Tests/Visual/TestCaseRankGraph.cs
index 45f6651537..f5558620ad 100644
--- a/osu.Game.Tests/Visual/TestCaseRankGraph.cs
+++ b/osu.Game.Tests/Visual/TestCaseRankGraph.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using osu.Game.Overlays.Profile;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using OpenTK;
@@ -11,6 +10,7 @@ using System.Collections.Generic;
using System;
using NUnit.Framework;
using osu.Game.Graphics.UserInterface;
+using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
diff --git a/osu.Game.Tests/Visual/TestCaseUserProfile.cs b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
index b060b9f2f8..0fdc01a974 100644
--- a/osu.Game.Tests/Visual/TestCaseUserProfile.cs
+++ b/osu.Game.Tests/Visual/TestCaseUserProfile.cs
@@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays;
using osu.Game.Overlays.Profile;
+using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
namespace osu.Game.Tests.Visual
@@ -23,6 +24,7 @@ namespace osu.Game.Tests.Visual
typeof(UserProfileOverlay),
typeof(RankGraph),
typeof(LineGraph),
+ typeof(BadgeContainer)
};
public TestCaseUserProfile()
@@ -53,6 +55,15 @@ namespace osu.Game.Tests.Visual
{
Mode = @"osu",
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray()
+ },
+ Badges = new[]
+ {
+ new Badge
+ {
+ AwardedAt = DateTimeOffset.FromUnixTimeSeconds(1505741569),
+ Description = "Outstanding help by being a voluntary test subject.",
+ ImageUrl = "https://assets.ppy.sh/profile-badges/contributor.jpg"
+ }
}
}, false));
diff --git a/osu.Game.props b/osu.Game.props
index 87edafb97f..ec859e64a5 100644
--- a/osu.Game.props
+++ b/osu.Game.props
@@ -3,6 +3,9 @@
7
+
+ ..\app.manifest
+
osu.licenseheader
diff --git a/osu.Game/Beatmaps/Legacy/LegacyMods.cs b/osu.Game/Beatmaps/Legacy/LegacyMods.cs
new file mode 100644
index 0000000000..0983610ba0
--- /dev/null
+++ b/osu.Game/Beatmaps/Legacy/LegacyMods.cs
@@ -0,0 +1,42 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+
+namespace osu.Game.Beatmaps.Legacy
+{
+ [Flags]
+ public enum LegacyMods
+ {
+ None = 0,
+ NoFail = 1 << 0,
+ Easy = 1 << 1,
+ TouchDevice = 1 << 2,
+ Hidden = 1 << 3,
+ HardRock = 1 << 4,
+ SuddenDeath = 1 << 5,
+ DoubleTime = 1 << 6,
+ Relax = 1 << 7,
+ HalfTime = 1 << 8,
+ Nightcore = 1 << 9,
+ Flashlight = 1 << 10,
+ Autoplay = 1 << 11,
+ SpunOut = 1 << 12,
+ Autopilot = 1 << 13,
+ Perfect = 1 << 14,
+ Key4 = 1 << 15,
+ Key5 = 1 << 16,
+ Key6 = 1 << 17,
+ Key7 = 1 << 18,
+ Key8 = 1 << 19,
+ FadeIn = 1 << 20,
+ Random = 1 << 21,
+ Cinema = 1 << 22,
+ Target = 1 << 23,
+ Key9 = 1 << 24,
+ KeyCoop = 1 << 25,
+ Key1 = 1 << 26,
+ Key3 = 1 << 27,
+ Key2 = 1 << 28,
+ }
+}
diff --git a/osu.Game/Beatmaps/RankStatus.cs b/osu.Game/Beatmaps/RankStatus.cs
deleted file mode 100644
index dce4f494f1..0000000000
--- a/osu.Game/Beatmaps/RankStatus.cs
+++ /dev/null
@@ -1,23 +0,0 @@
-// Copyright (c) 2007-2018 ppy Pty Ltd .
-// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-
-using System.ComponentModel;
-
-namespace osu.Game.Beatmaps
-{
- public enum RankStatus
- {
- Any = 7,
- [Description("Ranked & Approved")]
- RankedApproved = 0,
- Approved = 1,
- Loved = 8,
- Favourites = 2,
- [Description("Mod Requests")]
- ModRequests = 3,
- Pending = 4,
- Graveyard = 5,
- [Description("My Maps")]
- MyMaps = 6,
- }
-}
diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs
index c7f555ff0f..3efaa02a31 100644
--- a/osu.Game/Configuration/OsuConfigManager.cs
+++ b/osu.Game/Configuration/OsuConfigManager.cs
@@ -85,6 +85,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg);
Set(OsuSetting.ScreenshotCaptureMenuCursor, false);
+
+ Set(OsuSetting.SongSelectRightMouseScroll, false);
}
public OsuConfigManager(Storage storage) : base(storage)
@@ -130,6 +132,7 @@ namespace osu.Game.Configuration
SpeedChangeVisualisation,
Skin,
ScreenshotFormat,
- ScreenshotCaptureMenuCursor
+ ScreenshotCaptureMenuCursor,
+ SongSelectRightMouseScroll
}
}
diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
index 8e18dbd2f5..c39e9abf8b 100644
--- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs
+++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs
@@ -61,9 +61,9 @@ namespace osu.Game.Graphics.Containers
AddText(text.Substring(previousLinkEnd));
}
- public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null)
+ public void AddLink(string text, string url, LinkAction linkType = LinkAction.External, string linkArgument = null, string tooltipText = null, Action creationParameters = null)
{
- AddInternal(new DrawableLinkCompiler(AddText(text).ToList())
+ AddInternal(new DrawableLinkCompiler(AddText(text, creationParameters).ToList())
{
TooltipText = tooltipText ?? (url != text ? url : string.Empty),
Action = () =>
diff --git a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
index e961dd9a9e..d68314f8fc 100644
--- a/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
+++ b/osu.Game/Online/API/Requests/SearchBeatmapSetsRequest.cs
@@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic;
-using osu.Game.Beatmaps;
+using System.ComponentModel;
using osu.Game.Overlays;
using osu.Game.Overlays.Direct;
using osu.Game.Rulesets;
@@ -13,21 +13,36 @@ namespace osu.Game.Online.API.Requests
{
private readonly string query;
private readonly RulesetInfo ruleset;
- private readonly RankStatus rankStatus;
+ private readonly BeatmapSearchCategory searchCategory;
private readonly DirectSortCriteria sortCriteria;
private readonly SortDirection direction;
private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc";
- public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, RankStatus rankStatus = RankStatus.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending)
+ public SearchBeatmapSetsRequest(string query, RulesetInfo ruleset, BeatmapSearchCategory searchCategory = BeatmapSearchCategory.Any, DirectSortCriteria sortCriteria = DirectSortCriteria.Ranked, SortDirection direction = SortDirection.Descending)
{
this.query = System.Uri.EscapeDataString(query);
this.ruleset = ruleset;
- this.rankStatus = rankStatus;
+ this.searchCategory = searchCategory;
this.sortCriteria = sortCriteria;
this.direction = direction;
}
// ReSharper disable once ImpureMethodCallOnReadonlyValueField
- protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)rankStatus}&sort={sortCriteria.ToString().ToLower()}_{directionString}";
+ protected override string Target => $@"beatmapsets/search?q={query}&m={ruleset.ID ?? 0}&s={(int)searchCategory}&sort={sortCriteria.ToString().ToLower()}_{directionString}";
+ }
+
+ public enum BeatmapSearchCategory
+ {
+ Any = 7,
+ [Description("Ranked & Approved")]
+ RankedApproved = 0,
+ Approved = 1,
+ Loved = 8,
+ Favourites = 2,
+ Qualified = 3,
+ Pending = 4,
+ Graveyard = 5,
+ [Description("My Maps")]
+ MyMaps = 6,
}
}
diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs
index 941e49e87e..578d88bd89 100644
--- a/osu.Game/OsuGame.cs
+++ b/osu.Game/OsuGame.cs
@@ -158,7 +158,7 @@ namespace osu.Game
/// Show a beatmap set as an overlay.
///
/// The set to display.
- public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId);
+ public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
///
/// Show a user's profile as an overlay.
@@ -196,6 +196,7 @@ namespace osu.Game
}
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap);
+ Beatmap.Value.Mods.Value = s.Mods;
menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay)));
}
diff --git a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
index 8b19bca671..66e3148065 100644
--- a/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
+++ b/osu.Game/Overlays/BeatmapSet/AuthorInfo.cs
@@ -23,9 +23,8 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly ClickableArea clickableArea;
private readonly FillFlowContainer fields;
- private UserProfileOverlay profile;
-
private BeatmapSetInfo beatmapSet;
+
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
@@ -34,28 +33,36 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return;
beatmapSet = value;
- var i = BeatmapSet.OnlineInfo;
+ updateDisplay();
+ }
+ }
- avatar.User = BeatmapSet.Metadata.Author;
- clickableArea.Action = () => profile?.ShowUser(avatar.User);
+ private void updateDisplay()
+ {
+ avatar.User = BeatmapSet?.Metadata.Author;
- fields.Children = new Drawable[]
- {
- new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"),
- new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
- {
- Margin = new MarginPadding { Top = 5 },
- },
- };
+ fields.Clear();
+ if (BeatmapSet == null)
+ return;
- if (i.Ranked.HasValue)
+ var online = BeatmapSet.OnlineInfo;
+
+ fields.Children = new Drawable[]
+ {
+ new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"),
+ new Field("submitted on", online.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
{
- fields.Add(new Field("ranked on ", i.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
- }
- else if (i.LastUpdated.HasValue)
- {
- fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
- }
+ Margin = new MarginPadding { Top = 5 },
+ },
+ };
+
+ if (online.Ranked.HasValue)
+ {
+ fields.Add(new Field("ranked on ", online.Ranked.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
+ }
+ else if (online.LastUpdated.HasValue)
+ {
+ fields.Add(new Field("last updated on ", online.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold"));
}
}
@@ -73,6 +80,7 @@ namespace osu.Game.Overlays.BeatmapSet
Masking = true,
Child = avatar = new UpdateableAvatar
{
+ ShowGuestOnNull = false,
Size = new Vector2(height),
},
EdgeEffect = new EdgeEffectParameters
@@ -95,8 +103,12 @@ namespace osu.Game.Overlays.BeatmapSet
[BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile)
{
- this.profile = profile;
- clickableArea.Action = () => profile?.ShowUser(avatar.User);
+ clickableArea.Action = () =>
+ {
+ if (avatar.User != null) profile?.ShowUser(avatar.User);
+ };
+
+ updateDisplay();
}
private class Field : FillFlowContainer
diff --git a/osu.Game/Overlays/BeatmapSet/BasicStats.cs b/osu.Game/Overlays/BeatmapSet/BasicStats.cs
index 8fd34914a7..a7b6b16dcc 100644
--- a/osu.Game/Overlays/BeatmapSet/BasicStats.cs
+++ b/osu.Game/Overlays/BeatmapSet/BasicStats.cs
@@ -18,6 +18,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Statistic length, bpm, circleCount, sliderCount;
private BeatmapSetInfo beatmapSet;
+
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
@@ -26,11 +27,12 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return;
beatmapSet = value;
- bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##");
+ updateDisplay();
}
}
private BeatmapInfo beatmap;
+
public BeatmapInfo Beatmap
{
get { return beatmap; }
@@ -39,6 +41,22 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmap) return;
beatmap = value;
+ updateDisplay();
+ }
+ }
+
+ private void updateDisplay()
+ {
+ bpm.Value = BeatmapSet?.OnlineInfo.BPM.ToString(@"0.##") ?? "-";
+
+ if (beatmap == null)
+ {
+ length.Value = string.Empty;
+ circleCount.Value = string.Empty;
+ sliderCount.Value = string.Empty;
+ }
+ else
+ {
length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss");
circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString();
sliderCount.Value = beatmap.OnlineInfo.SliderCount.ToString();
@@ -62,12 +80,19 @@ namespace osu.Game.Overlays.BeatmapSet
};
}
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ updateDisplay();
+ }
+
private class Statistic : Container, IHasTooltip
{
private readonly string name;
private readonly OsuSpriteText value;
public string TooltipText => name;
+
public string Value
{
get { return value.Text; }
diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
index 87dc9b104b..6b75ac095d 100644
--- a/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
+++ b/osu.Game/Overlays/BeatmapSet/BeatmapPicker.cs
@@ -41,9 +41,16 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return;
beatmapSet = value;
- Beatmap.Value = BeatmapSet.Beatmaps.First();
- plays.Value = BeatmapSet.OnlineInfo.PlayCount;
- favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount;
+ updateDisplay();
+ }
+ }
+
+ private void updateDisplay()
+ {
+ difficulties.Clear();
+
+ if (BeatmapSet != null)
+ {
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b)
{
State = DifficultySelectorState.NotSelected,
@@ -53,14 +60,16 @@ namespace osu.Game.Overlays.BeatmapSet
starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
starRating.FadeIn(100);
},
- OnClicked = beatmap =>
- {
- Beatmap.Value = beatmap;
- },
+ OnClicked = beatmap => { Beatmap.Value = beatmap; },
});
-
- updateDifficultyButtons();
}
+
+ starRating.FadeOut(100);
+ Beatmap.Value = BeatmapSet?.Beatmaps.FirstOrDefault();
+ plays.Value = BeatmapSet?.OnlineInfo.PlayCount ?? 0;
+ favourites.Value = BeatmapSet?.OnlineInfo.FavouriteCount ?? 0;
+
+ updateDifficultyButtons();
}
public BeatmapPicker()
@@ -140,6 +149,7 @@ namespace osu.Game.Overlays.BeatmapSet
private void load(OsuColour colours)
{
starRating.Colour = colours.Yellow;
+ updateDisplay();
}
protected override void LoadComplete()
@@ -150,7 +160,10 @@ namespace osu.Game.Overlays.BeatmapSet
Beatmap.TriggerChange();
}
- private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version;
+ private void showBeatmap(BeatmapInfo beatmap)
+ {
+ version.Text = beatmap?.Version;
+ }
private void updateDifficultyButtons()
{
diff --git a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs
similarity index 97%
rename from osu.Game/Overlays/BeatmapSet/DownloadButton.cs
rename to osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs
index 0c6414c718..c699ae2328 100644
--- a/osu.Game/Overlays/BeatmapSet/DownloadButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/DownloadButton.cs
@@ -7,7 +7,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using OpenTK;
-namespace osu.Game.Overlays.BeatmapSet
+namespace osu.Game.Overlays.BeatmapSet.Buttons
{
public class DownloadButton : HeaderButton
{
diff --git a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs
similarity index 98%
rename from osu.Game/Overlays/BeatmapSet/FavouriteButton.cs
rename to osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs
index 29bc00038c..3821c96369 100644
--- a/osu.Game/Overlays/BeatmapSet/FavouriteButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/FavouriteButton.cs
@@ -10,7 +10,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
using OpenTK;
-namespace osu.Game.Overlays.BeatmapSet
+namespace osu.Game.Overlays.BeatmapSet.Buttons
{
public class FavouriteButton : HeaderButton
{
diff --git a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderButton.cs
similarity index 94%
rename from osu.Game/Overlays/BeatmapSet/HeaderButton.cs
rename to osu.Game/Overlays/BeatmapSet/Buttons/HeaderButton.cs
index e1c4f5cc67..b46b5d2a0e 100644
--- a/osu.Game/Overlays/BeatmapSet/HeaderButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/HeaderButton.cs
@@ -1,12 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
-using osu.Framework.Allocation;
-namespace osu.Game.Overlays.BeatmapSet
+namespace osu.Game.Overlays.BeatmapSet.Buttons
{
public class HeaderButton : TriangleButton
{
diff --git a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs
similarity index 97%
rename from osu.Game/Overlays/BeatmapSet/PreviewButton.cs
rename to osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs
index 6b5ffa57ad..08a99f1aea 100644
--- a/osu.Game/Overlays/BeatmapSet/PreviewButton.cs
+++ b/osu.Game/Overlays/BeatmapSet/Buttons/PreviewButton.cs
@@ -3,6 +3,7 @@
using osu.Framework.Allocation;
using osu.Framework.Audio.Track;
+using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -11,12 +12,11 @@ using osu.Framework.Input;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
+using osu.Game.Overlays.Direct;
using OpenTK;
using OpenTK.Graphics;
-using osu.Game.Overlays.Direct;
-using osu.Framework.Configuration;
-namespace osu.Game.Overlays.BeatmapSet
+namespace osu.Game.Overlays.BeatmapSet.Buttons
{
public class PreviewButton : OsuClickableContainer
{
@@ -85,6 +85,8 @@ namespace osu.Game.Overlays.BeatmapSet
// prevent negative (potential infinite) width if a track without length was loaded
progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f;
}
+ else
+ progress.Width = 0;
}
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game/Overlays/BeatmapSet/Details.cs b/osu.Game/Overlays/BeatmapSet/Details.cs
index 7f3b6d1584..5264caf936 100644
--- a/osu.Game/Overlays/BeatmapSet/Details.cs
+++ b/osu.Game/Overlays/BeatmapSet/Details.cs
@@ -1,11 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
+using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Screens.Select.Details;
using OpenTK;
using OpenTK.Graphics;
@@ -20,6 +22,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly UserRatings ratings;
private BeatmapSetInfo beatmapSet;
+
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
@@ -33,19 +36,24 @@ namespace osu.Game.Overlays.BeatmapSet
}
private BeatmapInfo beatmap;
+
public BeatmapInfo Beatmap
{
get { return beatmap; }
set
{
if (value == beatmap) return;
- beatmap = value;
- basic.Beatmap = advanced.Beatmap = Beatmap;
- ratings.Metrics = Beatmap.Metrics;
+ basic.Beatmap = advanced.Beatmap = beatmap = value;
+ updateDisplay();
}
}
+ private void updateDisplay()
+ {
+ ratings.Metrics = Beatmap?.Metrics;
+ }
+
public Details()
{
Width = BeatmapSetOverlay.RIGHT_WIDTH;
@@ -88,6 +96,12 @@ namespace osu.Game.Overlays.BeatmapSet
};
}
+ [BackgroundDependencyLoader]
+ private void load()
+ {
+ updateDisplay();
+ }
+
public void StopPreview() => preview.Playing.Value = false;
private class DetailBox : Container
diff --git a/osu.Game/Overlays/BeatmapSet/Header.cs b/osu.Game/Overlays/BeatmapSet/Header.cs
index 755039e7bc..9b25d61f58 100644
--- a/osu.Game/Overlays/BeatmapSet/Header.cs
+++ b/osu.Game/Overlays/BeatmapSet/Header.cs
@@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays.BeatmapSet.Buttons;
using OpenTK;
using OpenTK.Graphics;
@@ -18,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet
{
public class Header : Container
{
- private const float transition_duration = 250;
+ private const float transition_duration = 200;
private const float tabs_height = 50;
private const float buttons_height = 45;
private const float buttons_spacing = 5;
@@ -34,12 +35,13 @@ namespace osu.Game.Overlays.BeatmapSet
public Details Details;
private BeatmapManager beatmaps;
-
private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker;
private BeatmapSetInfo beatmapSet;
+ private readonly FavouriteButton favouriteButton;
+
public BeatmapSetInfo BeatmapSet
{
get { return beatmapSet; }
@@ -49,15 +51,26 @@ namespace osu.Game.Overlays.BeatmapSet
beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet;
- title.Text = BeatmapSet.Metadata.Title;
- artist.Text = BeatmapSet.Metadata.Artist;
- onlineStatusPill.Status = BeatmapSet.OnlineInfo.Status;
- downloadButtonsContainer.FadeIn();
+ updateDisplay();
+ }
+ }
+
+ private void updateDisplay()
+ {
+ title.Text = BeatmapSet?.Metadata.Title ?? string.Empty;
+ artist.Text = BeatmapSet?.Metadata.Artist ?? string.Empty;
+ onlineStatusPill.Status = BeatmapSet?.OnlineInfo.Status ?? BeatmapSetOnlineStatus.None;
+
+ cover?.FadeOut(400, Easing.Out);
+ if (BeatmapSet != null)
+ {
+ downloadButtonsContainer.FadeIn(transition_duration);
+ favouriteButton.FadeIn(transition_duration);
+
noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration);
videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration);
- cover?.FadeOut(400, Easing.Out);
coverContainer.Add(cover = new DelayedLoadWrapper(
new BeatmapSetCover(BeatmapSet)
{
@@ -71,6 +84,11 @@ namespace osu.Game.Overlays.BeatmapSet
RelativeSizeAxes = Axes.Both,
});
}
+ else
+ {
+ downloadButtonsContainer.FadeOut(transition_duration);
+ favouriteButton.FadeOut(transition_duration);
+ }
}
public Header()
@@ -166,7 +184,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Top = 10 },
Children = new Drawable[]
{
- new FavouriteButton(),
+ favouriteButton = new FavouriteButton(),
downloadButtonsContainer = new Container
{
RelativeSizeAxes = Axes.Both,
@@ -238,6 +256,8 @@ namespace osu.Game.Overlays.BeatmapSet
this.beatmaps = beatmaps;
beatmaps.ItemAdded += handleBeatmapAdd;
+
+ updateDisplay();
}
protected override void Dispose(bool isDisposing)
diff --git a/osu.Game/Overlays/BeatmapSet/Info.cs b/osu.Game/Overlays/BeatmapSet/Info.cs
index 9a402515ae..cd0b7386e8 100644
--- a/osu.Game/Overlays/BeatmapSet/Info.cs
+++ b/osu.Game/Overlays/BeatmapSet/Info.cs
@@ -34,11 +34,16 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return;
beatmapSet = value;
- source.Text = BeatmapSet.Metadata.Source;
- tags.Text = BeatmapSet.Metadata.Tags;
+ updateDisplay();
}
}
+ private void updateDisplay()
+ {
+ source.Text = BeatmapSet?.Metadata.Source ?? string.Empty;
+ tags.Text = BeatmapSet?.Metadata.Tags ?? string.Empty;
+ }
+
public BeatmapInfo Beatmap
{
get { return successRate.Beatmap; }
@@ -132,6 +137,8 @@ namespace osu.Game.Overlays.BeatmapSet
successRateBackground.Colour = colours.GrayE;
source.TextColour = description.TextColour = colours.Gray5;
tags.TextColour = colours.BlueDark;
+
+ updateDisplay();
}
private class MetadataSection : FillFlowContainer
diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs
index d5c5bd8ddd..185282bec9 100644
--- a/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs
+++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoresContainer.cs
@@ -2,15 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
-using OpenTK.Graphics;
-using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests;
using System.Collections.Generic;
using System.Linq;
+using osu.Framework.Allocation;
+using osu.Game.Beatmaps;
+using osu.Game.Online.API;
namespace osu.Game.Overlays.BeatmapSet.Scores
{
@@ -22,49 +22,75 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly FillFlowContainer flow;
private readonly DrawableTopScore topScore;
private readonly LoadingAnimation loadingAnimation;
- private readonly Box foreground;
- private bool isLoading;
- public bool IsLoading
+ private bool loading
{
- get { return isLoading; }
- set
- {
- if (isLoading == value) return;
- isLoading = value;
-
- foreground.FadeTo(isLoading ? 1 : 0, fade_duration);
- loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration);
- }
+ set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
}
private IEnumerable scores;
+ private BeatmapInfo beatmap;
+
public IEnumerable Scores
{
get { return scores; }
set
{
+ getScoresRequest?.Cancel();
scores = value;
- var scoresAmount = scores.Count();
- if (scoresAmount == 0)
- {
- CleanAllScores();
- return;
- }
- topScore.Score = scores.FirstOrDefault();
- topScore.Show();
-
- flow.Clear();
-
- if (scoresAmount < 2)
- return;
-
- for (int i = 1; i < scoresAmount; i++)
- flow.Add(new DrawableScore(i, scores.ElementAt(i)));
+ updateDisplay();
}
}
+ private GetScoresRequest getScoresRequest;
+ private APIAccess api;
+
+ public BeatmapInfo Beatmap
+ {
+ get => beatmap;
+ set
+ {
+ beatmap = value;
+
+ Scores = null;
+
+ if (beatmap?.OnlineBeatmapID.HasValue != true)
+ return;
+
+ loading = true;
+
+ getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
+ getScoresRequest.Success += r => Scores = r.Scores;
+ api.Queue(getScoresRequest);
+ }
+ }
+
+ private void updateDisplay()
+ {
+ loading = false;
+
+ var scoreCount = scores?.Count() ?? 0;
+
+ if (scoreCount == 0)
+ {
+ topScore.Hide();
+ flow.Clear();
+ return;
+ }
+
+ topScore.Score = scores.FirstOrDefault();
+ topScore.Show();
+
+ flow.Clear();
+
+ if (scoreCount < 2)
+ return;
+
+ for (int i = 1; i < scoreCount; i++)
+ flow.Add(new DrawableScore(i, scores.ElementAt(i)));
+ }
+
public ScoresContainer()
{
RelativeSizeAxes = Axes.X;
@@ -93,23 +119,19 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
},
}
},
- foreground = new Box
- {
- RelativeSizeAxes = Axes.Both,
- Colour = Color4.Black.Opacity(0.7f),
- Alpha = 0,
- },
loadingAnimation = new LoadingAnimation
{
Alpha = 0,
+ Margin = new MarginPadding(20)
},
};
}
- public void CleanAllScores()
+ [BackgroundDependencyLoader]
+ private void load(APIAccess api)
{
- topScore.Hide();
- flow.Clear();
+ this.api = api;
+ updateDisplay();
}
}
}
diff --git a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
index c64d3988a6..6572e520bd 100644
--- a/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
+++ b/osu.Game/Overlays/BeatmapSet/SuccessRate.cs
@@ -29,18 +29,23 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmap) return;
beatmap = value;
- int passCount = beatmap.OnlineInfo.PassCount;
- int playCount = beatmap.OnlineInfo.PlayCount;
-
- var rate = playCount != 0 ? (float)passCount / playCount : 0;
- successPercent.Text = rate.ToString("P0");
- successRate.Length = rate;
- percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
-
- graph.Metrics = Beatmap.Metrics;
+ updateDisplay();
}
}
+ private void updateDisplay()
+ {
+ int passCount = beatmap?.OnlineInfo.PassCount ?? 0;
+ int playCount = beatmap?.OnlineInfo.PlayCount ?? 0;
+
+ var rate = playCount != 0 ? (float)passCount / playCount : 0;
+ successPercent.Text = rate.ToString("P0");
+ successRate.Length = rate;
+ percentContainer.ResizeWidthTo(successRate.Length, 250, Easing.InOutCubic);
+
+ graph.Metrics = beatmap?.Metrics;
+ }
+
public SuccessRate()
{
Children = new Drawable[]
@@ -74,7 +79,6 @@ namespace osu.Game.Overlays.BeatmapSet
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre,
- Text = @"0%",
TextSize = 13,
},
},
@@ -103,6 +107,8 @@ namespace osu.Game.Overlays.BeatmapSet
successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5;
successRate.AccentColour = colours.Green;
successRate.BackgroundColour = colours.GrayD;
+
+ updateDisplay();
}
protected override void UpdateAfterChildren()
diff --git a/osu.Game/Overlays/BeatmapSetOverlay.cs b/osu.Game/Overlays/BeatmapSetOverlay.cs
index d2cf0a6ef1..366c34eae3 100644
--- a/osu.Game/Overlays/BeatmapSetOverlay.cs
+++ b/osu.Game/Overlays/BeatmapSetOverlay.cs
@@ -27,11 +27,8 @@ namespace osu.Game.Overlays
private readonly Header header;
private readonly Info info;
- private readonly ScoresContainer scores;
-
private APIAccess api;
private RulesetStore rulesets;
- private GetScoresRequest getScoresRequest;
private readonly ScrollContainer scroll;
@@ -40,6 +37,7 @@ namespace osu.Game.Overlays
public BeatmapSetOverlay()
{
+ ScoresContainer scores;
Waves.FirstWaveColour = OsuColour.Gray(0.4f);
Waves.SecondWaveColour = OsuColour.Gray(0.3f);
Waves.ThirdWaveColour = OsuColour.Gray(0.2f);
@@ -88,31 +86,10 @@ namespace osu.Game.Overlays
header.Picker.Beatmap.ValueChanged += b =>
{
info.Beatmap = b;
- updateScores(b);
+ scores.Beatmap = b;
};
}
- private void updateScores(BeatmapInfo beatmap)
- {
- getScoresRequest?.Cancel();
-
- if (!beatmap.OnlineBeatmapID.HasValue)
- {
- scores.CleanAllScores();
- return;
- }
-
- scores.IsLoading = true;
-
- getScoresRequest = new GetScoresRequest(beatmap, beatmap.Ruleset);
- getScoresRequest.Success += r =>
- {
- scores.Scores = r.Scores;
- scores.IsLoading = false;
- };
- api.Queue(getScoresRequest);
- }
-
[BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets)
{
@@ -139,7 +116,7 @@ namespace osu.Game.Overlays
return true;
}
- public void ShowBeatmapSet(int beatmapSetId)
+ public void FetchAndShowBeatmapSet(int beatmapSetId)
{
// todo: display the overlay while we are loading here. we need to support setting BeatmapSet to null for this to work.
var req = new GetBeatmapSetRequest(beatmapSetId);
diff --git a/osu.Game/Overlays/Direct/FilterControl.cs b/osu.Game/Overlays/Direct/FilterControl.cs
index c77994efb2..8883dfdebb 100644
--- a/osu.Game/Overlays/Direct/FilterControl.cs
+++ b/osu.Game/Overlays/Direct/FilterControl.cs
@@ -7,15 +7,15 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
-using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
+using osu.Game.Online.API.Requests;
using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets;
namespace osu.Game.Overlays.Direct
{
- public class FilterControl : SearchableListFilterControl
+ public class FilterControl : SearchableListFilterControl
{
public readonly Bindable Ruleset = new Bindable();
private FillFlowContainer modeButtons;
diff --git a/osu.Game/Overlays/Direct/PlayButton.cs b/osu.Game/Overlays/Direct/PlayButton.cs
index 44e24d8157..4913b11ae1 100644
--- a/osu.Game/Overlays/Direct/PlayButton.cs
+++ b/osu.Game/Overlays/Direct/PlayButton.cs
@@ -78,12 +78,7 @@ namespace osu.Game.Overlays.Direct
loadingAnimation = new LoadingAnimation(),
});
- Playing.ValueChanged += playing =>
- {
- icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
- icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
- updatePreviewTrack(playing);
- };
+ Playing.ValueChanged += updatePreviewTrack;
}
[BackgroundDependencyLoader]
@@ -125,6 +120,15 @@ namespace osu.Game.Overlays.Direct
private void updatePreviewTrack(bool playing)
{
+ if (playing && BeatmapSet == null)
+ {
+ Playing.Value = false;
+ return;
+ }
+
+ icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
+ icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
+
if (playing)
{
if (Preview == null)
diff --git a/osu.Game/Overlays/DirectOverlay.cs b/osu.Game/Overlays/DirectOverlay.cs
index ddc7ed3737..f437546888 100644
--- a/osu.Game/Overlays/DirectOverlay.cs
+++ b/osu.Game/Overlays/DirectOverlay.cs
@@ -22,7 +22,7 @@ using OpenTK.Graphics;
namespace osu.Game.Overlays
{
- public class DirectOverlay : SearchableListOverlay
+ public class DirectOverlay : SearchableListOverlay
{
private const float panel_padding = 10f;
@@ -40,7 +40,7 @@ namespace osu.Game.Overlays
protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265");
protected override SearchableListHeader CreateHeader() => new Header();
- protected override SearchableListFilterControl CreateFilterControl() => new FilterControl();
+ protected override SearchableListFilterControl CreateFilterControl() => new FilterControl();
private IEnumerable beatmapSets;
diff --git a/osu.Game/Overlays/Profile/Header/BadgeContainer.cs b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs
new file mode 100644
index 0000000000..291db45e97
--- /dev/null
+++ b/osu.Game/Overlays/Profile/Header/BadgeContainer.cs
@@ -0,0 +1,195 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using osu.Framework.Allocation;
+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.Input;
+using osu.Game.Graphics;
+using osu.Game.Graphics.Sprites;
+using osu.Game.Users;
+using OpenTK;
+
+namespace osu.Game.Overlays.Profile.Header
+{
+ public class BadgeContainer : Container
+ {
+ private static readonly Vector2 badge_size = new Vector2(86, 40);
+ private static readonly MarginPadding outer_padding = new MarginPadding(3);
+
+ private OsuSpriteText badgeCountText;
+ private FillFlowContainer badgeFlowContainer;
+ private FillFlowContainer outerBadgeContainer;
+
+ [BackgroundDependencyLoader]
+ private void load(OsuColour colours)
+ {
+ Child = new Container
+ {
+ Masking = true,
+ CornerRadius = 4,
+ AutoSizeAxes = Axes.Both,
+ Children = new Drawable[]
+ {
+ new Box
+ {
+ RelativeSizeAxes = Axes.Both,
+ Colour = colours.Gray3
+ },
+ outerBadgeContainer = new OuterBadgeContainer(onOuterHover, onOuterHoverLost)
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ Direction = FillDirection.Vertical,
+ Padding = outer_padding,
+ Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal,
+ AutoSizeAxes = Axes.Y,
+ Children = new Drawable[]
+ {
+ badgeCountText = new OsuSpriteText
+ {
+ Alpha = 0,
+ TextSize = 12,
+ Anchor = Anchor.BottomCentre,
+ Origin = Anchor.BottomCentre,
+ Font = "Exo2.0-Regular"
+ },
+ new Container
+ {
+ Anchor = Anchor.BottomLeft,
+ Origin = Anchor.BottomLeft,
+ AutoSizeAxes = Axes.Both,
+ Child = badgeFlowContainer = new FillFlowContainer
+ {
+ Direction = FillDirection.Horizontal,
+ AutoSizeAxes = Axes.Both,
+ }
+ }
+ }
+ },
+ }
+ };
+
+ Scheduler.AddDelayed(rotateBadges, 3000, true);
+ }
+
+ private void rotateBadges()
+ {
+ if (outerBadgeContainer.IsHovered) return;
+
+ visibleBadge = (visibleBadge + 1) % badgeCount;
+
+ badgeFlowContainer.MoveToX(-DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge, 500, Easing.InOutQuad);
+ }
+
+ private int visibleBadge;
+ private int badgeCount;
+
+ public void ShowBadges(Badge[] badges)
+ {
+ switch (badges.Length)
+ {
+ case 0:
+ Hide();
+ return;
+ case 1:
+ badgeCountText.Hide();
+ break;
+ default:
+ badgeCountText.Show();
+ badgeCountText.Text = $"{badges.Length} badges";
+ break;
+ }
+
+ Show();
+ badgeCount = badges.Length;
+ visibleBadge = 0;
+
+ badgeFlowContainer.Clear();
+ foreach (var badge in badges)
+ {
+ LoadComponentAsync(new DrawableBadge(badge)
+ {
+ Anchor = Anchor.TopCentre,
+ Origin = Anchor.TopCentre,
+ }, badgeFlowContainer.Add);
+ }
+ }
+
+ private void onOuterHover()
+ {
+ badgeFlowContainer.ClearTransforms();
+ badgeFlowContainer.X = 0;
+ badgeFlowContainer.Direction = FillDirection.Full;
+ outerBadgeContainer.AutoSizeAxes = Axes.Both;
+
+ badgeFlowContainer.MaximumSize = new Vector2(ChildSize.X, float.MaxValue);
+ }
+
+ private void onOuterHoverLost()
+ {
+ badgeFlowContainer.X = -DrawableBadge.DRAWABLE_BADGE_SIZE.X * visibleBadge;
+ badgeFlowContainer.Direction = FillDirection.Horizontal;
+ outerBadgeContainer.AutoSizeAxes = Axes.Y;
+ outerBadgeContainer.Width = DrawableBadge.DRAWABLE_BADGE_SIZE.X + outer_padding.TotalHorizontal;
+ }
+
+ private class OuterBadgeContainer : FillFlowContainer
+ {
+ private readonly Action hoverAction;
+ private readonly Action hoverLostAction;
+
+ public OuterBadgeContainer(Action hoverAction, Action hoverLostAction)
+ {
+ this.hoverAction = hoverAction;
+ this.hoverLostAction = hoverLostAction;
+ }
+
+ protected override bool OnHover(InputState state)
+ {
+ hoverAction();
+ return true;
+ }
+
+ protected override void OnHoverLost(InputState state) => hoverLostAction();
+ }
+
+ private class DrawableBadge : Container, IHasTooltip
+ {
+ public static readonly Vector2 DRAWABLE_BADGE_SIZE = badge_size + outer_padding.Total;
+
+ private readonly Badge badge;
+
+ public DrawableBadge(Badge badge)
+ {
+ this.badge = badge;
+ Padding = outer_padding;
+ Size = DRAWABLE_BADGE_SIZE;
+ }
+
+ [BackgroundDependencyLoader]
+ private void load(TextureStore textures)
+ {
+ Child = new Sprite
+ {
+ FillMode = FillMode.Fit,
+ RelativeSizeAxes = Axes.Both,
+ Texture = textures.Get(badge.ImageUrl),
+ };
+ }
+
+ protected override void LoadComplete()
+ {
+ base.LoadComplete();
+ Child.FadeInFromZero(200);
+ }
+
+ public string TooltipText => badge.Description;
+ }
+ }
+}
diff --git a/osu.Game/Overlays/Profile/RankGraph.cs b/osu.Game/Overlays/Profile/Header/RankGraph.cs
similarity index 99%
rename from osu.Game/Overlays/Profile/RankGraph.cs
rename to osu.Game/Overlays/Profile/Header/RankGraph.cs
index 72dd4352f6..2c70507536 100644
--- a/osu.Game/Overlays/Profile/RankGraph.cs
+++ b/osu.Game/Overlays/Profile/Header/RankGraph.cs
@@ -2,9 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
+using System.Collections.Generic;
using System.Linq;
-using OpenTK;
using osu.Framework.Allocation;
+using osu.Framework.Configuration;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
@@ -14,10 +15,9 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Users;
-using System.Collections.Generic;
-using osu.Framework.Configuration;
+using OpenTK;
-namespace osu.Game.Overlays.Profile
+namespace osu.Game.Overlays.Profile.Header
{
public class RankGraph : Container
{
diff --git a/osu.Game/Overlays/Profile/SupporterIcon.cs b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs
similarity index 98%
rename from osu.Game/Overlays/Profile/SupporterIcon.cs
rename to osu.Game/Overlays/Profile/Header/SupporterIcon.cs
index e8d52bf50e..37ad63464c 100644
--- a/osu.Game/Overlays/Profile/SupporterIcon.cs
+++ b/osu.Game/Overlays/Profile/Header/SupporterIcon.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using OpenTK;
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
@@ -9,8 +8,9 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds;
+using OpenTK;
-namespace osu.Game.Overlays.Profile
+namespace osu.Game.Overlays.Profile.Header
{
public class SupporterIcon : CircularContainer, IHasTooltip
{
diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs
index 4a88431cc4..4c411b3210 100644
--- a/osu.Game/Overlays/Profile/ProfileHeader.cs
+++ b/osu.Game/Overlays/Profile/ProfileHeader.cs
@@ -17,13 +17,14 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
+using osu.Game.Overlays.Profile.Header;
using osu.Game.Users;
namespace osu.Game.Overlays.Profile
{
public class ProfileHeader : Container
{
- private readonly OsuTextFlowContainer infoTextLeft;
+ private readonly LinkFlowContainer infoTextLeft;
private readonly LinkFlowContainer infoTextRight;
private readonly FillFlowContainer scoreText, scoreNumberText;
private readonly RankGraph rankGraph;
@@ -35,6 +36,7 @@ namespace osu.Game.Overlays.Profile
private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA;
private readonly Box colourBar;
private readonly DrawableFlag countryFlag;
+ private readonly BadgeContainer badgeContainer;
private const float cover_height = 350;
private const float info_height = 150;
@@ -42,6 +44,7 @@ namespace osu.Game.Overlays.Profile
private const float avatar_size = 110;
private const float level_position = 30;
private const float level_height = 60;
+ private const float stats_width = 280;
public ProfileHeader(User user)
{
@@ -66,9 +69,9 @@ namespace osu.Game.Overlays.Profile
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
- X = UserProfileOverlay.CONTENT_X_MARGIN,
- Y = -20,
- AutoSizeAxes = Axes.Both,
+ Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN, Bottom = 20, Right = stats_width + UserProfileOverlay.CONTENT_X_MARGIN },
+ AutoSizeAxes = Axes.Y,
+ RelativeSizeAxes = Axes.X,
Children = new Drawable[]
{
new UpdateableAvatar
@@ -116,7 +119,15 @@ namespace osu.Game.Overlays.Profile
Height = 20
}
}
- }
+ },
+ badgeContainer = new BadgeContainer
+ {
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Origin = Anchor.BottomLeft,
+ Margin = new MarginPadding { Bottom = 5 },
+ Alpha = 0,
+ },
}
},
colourBar = new Box
@@ -130,7 +141,7 @@ namespace osu.Game.Overlays.Profile
}
}
},
- infoTextLeft = new OsuTextFlowContainer(t => t.TextSize = 14)
+ infoTextLeft = new LinkFlowContainer(t => t.TextSize = 14)
{
X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = cover_height + 20,
@@ -156,7 +167,7 @@ namespace osu.Game.Overlays.Profile
{
X = -UserProfileOverlay.CONTENT_X_MARGIN,
RelativeSizeAxes = Axes.Y,
- Width = 280,
+ Width = stats_width,
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Children = new Drawable[]
@@ -339,7 +350,7 @@ namespace osu.Game.Overlays.Profile
if (user.Country != null)
{
- infoTextLeft.AddText("from ", lightText);
+ infoTextLeft.AddText("From ", lightText);
infoTextLeft.AddText(user.Country.FullName, boldItalic);
countryFlag.Country = user.Country;
}
@@ -367,6 +378,10 @@ namespace osu.Game.Overlays.Profile
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic);
}
+ infoTextLeft.NewLine();
+ infoTextLeft.AddText("Contributed ", lightText);
+ infoTextLeft.AddLink($@"{user.PostCount} forum posts", url: $"https://osu.ppy.sh/users/{user.Id}/posts", creationParameters: boldItalic);
+
string websiteWithoutProtcol = user.Website;
if (!string.IsNullOrEmpty(websiteWithoutProtcol))
{
@@ -381,8 +396,10 @@ namespace osu.Game.Overlays.Profile
infoTextRight.NewParagraph();
if (!string.IsNullOrEmpty(user.Twitter))
tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{user.Twitter}");
- tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website);
+ tryAddInfoRightLine(FontAwesome.fa_gamepad, user.Discord);
tryAddInfoRightLine(FontAwesome.fa_skype, user.Skype, @"skype:" + user.Skype + @"?chat");
+ tryAddInfoRightLine(FontAwesome.fa_lastfm, user.Lastfm, $@"https://last.fm/users/{user.Lastfm}");
+ tryAddInfoRightLine(FontAwesome.fa_globe, websiteWithoutProtcol, user.Website);
if (user.Statistics != null)
{
@@ -417,6 +434,8 @@ namespace osu.Game.Overlays.Profile
rankGraph.User.Value = user;
}
+
+ badgeContainer.ShowBadges(user.Badges);
}
private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null)
diff --git a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs
index da08c08179..97079c77f3 100644
--- a/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs
+++ b/osu.Game/Overlays/Profile/Sections/BeatmapMetadataContainer.cs
@@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Sections
{
Action = () =>
{
- if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value);
+ if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value);
};
Child = new FillFlowContainer
diff --git a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
index 7575105ef7..7893d76fb8 100644
--- a/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
+++ b/osu.Game/Overlays/Settings/Sections/Gameplay/SongSelectSettings.cs
@@ -17,6 +17,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{
Children = new Drawable[]
{
+ new SettingsCheckbox
+ {
+ LabelText = "Right mouse drag to absolute scroll",
+ Bindable = config.GetBindable(OsuSetting.SongSelectRightMouseScroll),
+ },
new SettingsCheckbox
{
LabelText = "Show converted beatmaps",
diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs
index c2af4d566c..cd1d030afe 100644
--- a/osu.Game/Rulesets/Ruleset.cs
+++ b/osu.Game/Rulesets/Ruleset.cs
@@ -14,6 +14,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI;
+using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets
{
@@ -33,6 +34,13 @@ namespace osu.Game.Rulesets
public abstract IEnumerable GetModsFor(ModType type);
+ ///
+ /// Converts mods from legacy enum values. Do not override if you're not a legacy ruleset.
+ ///
+ /// The legacy enum which will be converted
+ /// An enumerable of constructed s
+ public virtual IEnumerable ConvertLegacyMods(LegacyMods mods) => new Mod[] { };
+
public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
protected Ruleset(RulesetInfo rulesetInfo = null)
diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs
index 5ee009ba98..239f200e29 100644
--- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs
+++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreParser.cs
@@ -9,6 +9,8 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Legacy;
using osu.Game.Users;
using SharpCompress.Compressors.LZMA;
+using osu.Game.Beatmaps.Legacy;
+using System.Linq;
namespace osu.Game.Rulesets.Scoring.Legacy
{
@@ -64,7 +66,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy
/* score.Perfect = */
sr.ReadBoolean();
/* score.EnabledMods = (Mods)*/
- sr.ReadInt32();
+ score.Mods = currentRuleset.ConvertLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
/* score.HpGraphString = */
sr.ReadString();
/* score.Date = */
diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs
index 2076807d18..3c9a14e1f4 100644
--- a/osu.Game/Screens/Select/BeatmapCarousel.cs
+++ b/osu.Game/Screens/Select/BeatmapCarousel.cs
@@ -97,6 +97,9 @@ namespace osu.Game.Screens.Select
private readonly Container scrollableContent;
+
+ public Bindable RightClickScrollingEnabled = new Bindable();
+
public Bindable RandomAlgorithm = new Bindable();
private readonly List previouslyVisitedRandomSets = new List();
private readonly Stack randomSelectedBeatmaps = new Stack();
@@ -122,6 +125,10 @@ namespace osu.Game.Screens.Select
private void load(OsuConfigManager config)
{
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
+ config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled);
+
+ RightClickScrollingEnabled.ValueChanged += v => RightMouseScrollbar = v;
+ RightClickScrollingEnabled.TriggerChange();
}
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)
diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
index ad7588edde..d554a22735 100644
--- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
+++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs
@@ -46,7 +46,7 @@ namespace osu.Game.Screens.Select.Carousel
restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore);
dialogOverlay = overlay;
if (beatmapOverlay != null)
- viewDetails = beatmapOverlay.ShowBeatmapSet;
+ viewDetails = beatmapOverlay.FetchAndShowBeatmapSet;
Children = new Drawable[]
{
diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs
index cad7ed7d81..852e4f190f 100644
--- a/osu.Game/Screens/Select/Details/AdvancedStats.cs
+++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs
@@ -40,10 +40,10 @@ namespace osu.Game.Screens.Select.Details
firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0;
}
- hpDrain.Value = beatmap.BaseDifficulty?.DrainRate ?? 0;
- accuracy.Value = beatmap.BaseDifficulty?.OverallDifficulty ?? 0;
- approachRate.Value = beatmap.BaseDifficulty?.ApproachRate ?? 0;
- starDifficulty.Value = (float)beatmap.StarDifficulty;
+ hpDrain.Value = Beatmap?.BaseDifficulty?.DrainRate ?? 0;
+ accuracy.Value = Beatmap?.BaseDifficulty?.OverallDifficulty ?? 0;
+ approachRate.Value = Beatmap?.BaseDifficulty?.ApproachRate ?? 0;
+ starDifficulty.Value = (float)(Beatmap?.StarDifficulty ?? 0);
}
}
diff --git a/osu.Game/Screens/Select/Details/FailRetryGraph.cs b/osu.Game/Screens/Select/Details/FailRetryGraph.cs
index a33cee21ed..bf4eb07108 100644
--- a/osu.Game/Screens/Select/Details/FailRetryGraph.cs
+++ b/osu.Game/Screens/Select/Details/FailRetryGraph.cs
@@ -25,10 +25,10 @@ namespace osu.Game.Screens.Select.Details
if (value == metrics) return;
metrics = value;
- var retries = Metrics.Retries;
- var fails = Metrics.Fails;
+ var retries = Metrics?.Retries ?? new int[0];
+ var fails = Metrics?.Fails ?? new int[0];
- float maxValue = fails.Zip(retries, (fail, retry) => fail + retry).Max();
+ float maxValue = fails.Any() ? fails.Zip(retries, (fail, retry) => fail + retry).Max() : 0;
failGraph.MaxValue = maxValue;
retryGraph.MaxValue = maxValue;
diff --git a/osu.Game/Screens/Select/Details/UserRatings.cs b/osu.Game/Screens/Select/Details/UserRatings.cs
index bf50217048..787b22f965 100644
--- a/osu.Game/Screens/Select/Details/UserRatings.cs
+++ b/osu.Game/Screens/Select/Details/UserRatings.cs
@@ -21,6 +21,7 @@ namespace osu.Game.Screens.Select.Details
private readonly BarGraph graph;
private BeatmapMetrics metrics;
+
public BeatmapMetrics Metrics
{
get { return metrics; }
@@ -31,15 +32,25 @@ namespace osu.Game.Screens.Select.Details
const int rating_range = 10;
- var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0.
+ if (metrics == null)
+ {
+ negativeRatings.Text = "0";
+ positiveRatings.Text = "0";
+ ratingsBar.Length = 0;
+ graph.Values = new float[rating_range];
+ }
+ else
+ {
+ var ratings = Metrics.Ratings.Skip(1).Take(rating_range); // adjust for API returning weird empty data at 0.
- var negativeCount = ratings.Take(rating_range / 2).Sum();
- var totalCount = ratings.Sum();
+ var negativeCount = ratings.Take(rating_range / 2).Sum();
+ var totalCount = ratings.Sum();
- negativeRatings.Text = negativeCount.ToString();
- positiveRatings.Text = (totalCount - negativeCount).ToString();
- ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount;
- graph.Values = ratings.Take(rating_range).Select(r => (float)r);
+ negativeRatings.Text = negativeCount.ToString();
+ positiveRatings.Text = (totalCount - negativeCount).ToString();
+ ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount;
+ graph.Values = ratings.Take(rating_range).Select(r => (float)r);
+ }
}
}
diff --git a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
index 3a6ab8f84b..9dae8fb273 100644
--- a/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
+++ b/osu.Game/Screens/Select/Leaderboards/Leaderboard.cs
@@ -39,8 +39,9 @@ namespace osu.Game.Screens.Select.Leaderboards
private readonly LoadingAnimation loading;
- private IEnumerable scores;
+ private ScheduledDelegate showScoresDelegate;
+ private IEnumerable scores;
public IEnumerable Scores
{
get { return scores; }
@@ -59,29 +60,34 @@ namespace osu.Game.Screens.Select.Leaderboards
// ensure placeholder is hidden when displaying scores
PlaceholderState = PlaceholderState.Successful;
- // schedule because we may not be loaded yet (LoadComponentAsync complains).
- Schedule(() =>
+ var flow = scrollFlow = new FillFlowContainer
{
- LoadComponentAsync(new FillFlowContainer
- {
- RelativeSizeAxes = Axes.X,
- AutoSizeAxes = Axes.Y,
- Spacing = new Vector2(0f, 5f),
- Padding = new MarginPadding { Top = 10, Bottom = 5 },
- ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
- }, f =>
- {
- scrollContainer.Add(scrollFlow = f);
+ RelativeSizeAxes = Axes.X,
+ AutoSizeAxes = Axes.Y,
+ Spacing = new Vector2(0f, 5f),
+ Padding = new MarginPadding { Top = 10, Bottom = 5 },
+ ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
+ };
- int i = 0;
- foreach (var s in f.Children)
- {
- using (s.BeginDelayedSequence(i++ * 50, true))
- s.Show();
- }
+ // schedule because we may not be loaded yet (LoadComponentAsync complains).
+ showScoresDelegate?.Cancel();
+ if (!IsLoaded)
+ showScoresDelegate = Schedule(showScores);
+ else
+ showScores();
- scrollContainer.ScrollTo(0f, false);
- });
+ void showScores() => LoadComponentAsync(flow, _ =>
+ {
+ scrollContainer.Add(flow);
+
+ int i = 0;
+ foreach (var s in flow.Children)
+ {
+ using (s.BeginDelayedSequence(i++ * 50, true))
+ s.Show();
+ }
+
+ scrollContainer.ScrollTo(0f, false);
});
}
}
@@ -103,6 +109,10 @@ namespace osu.Game.Screens.Select.Leaderboards
private PlaceholderState placeholderState;
+ ///
+ /// Update the placeholder visibility.
+ /// Setting this to anything other than PlaceholderState.Successful will cancel all existing retrieval requests and hide scores.
+ ///
protected PlaceholderState PlaceholderState
{
get { return placeholderState; }
@@ -250,43 +260,45 @@ namespace osu.Game.Screens.Select.Leaderboards
loading.Show();
getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope);
- getScoresRequest.Success += r =>
+ getScoresRequest.Success += r => Schedule(() =>
{
Scores = r.Scores;
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores;
- };
- getScoresRequest.Failure += onUpdateFailed;
+ });
+
+ getScoresRequest.Failure += e => Schedule(() =>
+ {
+ if (e is OperationCanceledException)
+ return;
+
+ PlaceholderState = PlaceholderState.NetworkFailure;
+ Logger.Error(e, @"Couldn't fetch beatmap scores!");
+ });
api.Queue(getScoresRequest);
}
- private void onUpdateFailed(Exception e)
- {
- if (e is OperationCanceledException)
- return;
-
- PlaceholderState = PlaceholderState.NetworkFailure;
- Logger.Error(e, @"Couldn't fetch beatmap scores!");
- }
+ private Placeholder currentPlaceholder;
private void replacePlaceholder(Placeholder placeholder)
{
- var existingPlaceholder = placeholderContainer.Children.LastOrDefault() as Placeholder;
-
- if (placeholder != null && placeholder.Equals(existingPlaceholder))
+ if (placeholder != null && placeholder.Equals(currentPlaceholder))
return;
- existingPlaceholder?.FadeOut(150, Easing.OutQuint).Expire();
+ currentPlaceholder?.FadeOut(150, Easing.OutQuint).Expire();
if (placeholder == null)
+ {
+ currentPlaceholder = null;
return;
+ }
- Scores = null;
-
- placeholderContainer.Add(placeholder);
+ placeholderContainer.Child = placeholder;
placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint);
placeholder.FadeInFromZero(fade_duration, Easing.OutQuint);
+
+ currentPlaceholder = placeholder;
}
protected override void Update()
diff --git a/osu.Game/Tests/Visual/OsuTestCase.cs b/osu.Game/Tests/Visual/OsuTestCase.cs
index decf0c9bdb..02f425c296 100644
--- a/osu.Game/Tests/Visual/OsuTestCase.cs
+++ b/osu.Game/Tests/Visual/OsuTestCase.cs
@@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd .
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
-using System;
using System.IO;
using System.Reflection;
using osu.Framework.Testing;
@@ -10,28 +9,27 @@ namespace osu.Game.Tests.Visual
{
public abstract class OsuTestCase : TestCase
{
- public override void RunTest()
- {
- using (var host = new CleanRunHeadlessGameHost($"test-{Guid.NewGuid()}", realtime: false))
- host.Run(new OsuTestCaseTestRunner(this));
- }
+ protected override ITestCaseTestRunner CreateRunner() => new OsuTestCaseTestRunner();
- public class OsuTestCaseTestRunner : OsuGameBase
+ public class OsuTestCaseTestRunner : OsuGameBase, ITestCaseTestRunner
{
- private readonly OsuTestCase testCase;
-
protected override string MainResourceFile => File.Exists(base.MainResourceFile) ? base.MainResourceFile : Assembly.GetExecutingAssembly().Location;
- public OsuTestCaseTestRunner(OsuTestCase testCase)
+ private readonly TestCaseTestRunner.TestRunner runner;
+
+ public OsuTestCaseTestRunner()
{
- this.testCase = testCase;
+ runner = new TestCaseTestRunner.TestRunner();
}
protected override void LoadComplete()
{
base.LoadComplete();
- Add(new TestCaseTestRunner.TestRunner(testCase));
+
+ Add(runner);
}
+
+ public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test);
}
}
}
diff --git a/osu.Game/Users/Badge.cs b/osu.Game/Users/Badge.cs
new file mode 100644
index 0000000000..25ef8ffdf4
--- /dev/null
+++ b/osu.Game/Users/Badge.cs
@@ -0,0 +1,20 @@
+// Copyright (c) 2007-2018 ppy Pty Ltd .
+// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
+
+using System;
+using Newtonsoft.Json;
+
+namespace osu.Game.Users
+{
+ public class Badge
+ {
+ [JsonProperty("awarded_at")]
+ public DateTimeOffset AwardedAt;
+
+ [JsonProperty("description")]
+ public string Description;
+
+ [JsonProperty("image_url")]
+ public string ImageUrl;
+ }
+}
diff --git a/osu.Game/Users/UpdateableAvatar.cs b/osu.Game/Users/UpdateableAvatar.cs
index 31455801da..6c0b841abf 100644
--- a/osu.Game/Users/UpdateableAvatar.cs
+++ b/osu.Game/Users/UpdateableAvatar.cs
@@ -15,6 +15,11 @@ namespace osu.Game.Users
private User user;
+ ///
+ /// Whether to show a default guest representation on null user (as opposed to nothing).
+ ///
+ public bool ShowGuestOnNull = true;
+
public User User
{
get { return user; }
@@ -40,13 +45,16 @@ namespace osu.Game.Users
{
displayedAvatar?.FadeOut(300);
displayedAvatar?.Expire();
- Add(displayedAvatar = new DelayedLoadWrapper(
- new Avatar(user)
- {
- RelativeSizeAxes = Axes.Both,
- OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint),
- })
- );
+ if (user != null || ShowGuestOnNull)
+ {
+ Add(displayedAvatar = new DelayedLoadWrapper(
+ new Avatar(user)
+ {
+ RelativeSizeAxes = Axes.Both,
+ OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint),
+ })
+ );
+ }
}
}
}
diff --git a/osu.Game/Users/User.cs b/osu.Game/Users/User.cs
index 441e4ffd28..e1f68e1ce8 100644
--- a/osu.Game/Users/User.cs
+++ b/osu.Game/Users/User.cs
@@ -98,9 +98,15 @@ namespace osu.Game.Users
[JsonProperty(@"skype")]
public string Skype;
+ [JsonProperty(@"discord")]
+ public string Discord;
+
[JsonProperty(@"website")]
public string Website;
+ [JsonProperty(@"post_count")]
+ public int PostCount;
+
[JsonProperty(@"playstyle")]
public string[] PlayStyle;
@@ -137,6 +143,9 @@ namespace osu.Game.Users
[JsonProperty(@"rankHistory")]
public RankHistoryData RankHistory;
+ [JsonProperty("badges")]
+ public Badge[] Badges;
+
public override string ToString() => Username;
}
}
diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs
index 424d7b7a8f..bcb91c1955 100644
--- a/osu.Game/Users/UserPanel.cs
+++ b/osu.Game/Users/UserPanel.cs
@@ -17,7 +17,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Containers;
-using osu.Game.Overlays.Profile;
+using osu.Game.Overlays.Profile.Header;
namespace osu.Game.Users
{