1
0
mirror of https://github.com/ppy/osu.git synced 2024-12-15 09:02:55 +08:00

Merge branch 'master' into fix-overlapping-speedchanges

This commit is contained in:
Dean Herbert 2018-04-20 15:36:28 +09:00 committed by GitHub
commit dce569c9be
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 1171 additions and 273 deletions

1
.gitattributes vendored
View File

@ -14,6 +14,7 @@ App.config text eol=crlf
*.bat text eol=crlf *.bat text eol=crlf
*.cmd text eol=crlf *.cmd text eol=crlf
*.snippet text eol=crlf *.snippet text eol=crlf
*.manifest text eol=crlf
# Check out with lf (UNIX) line endings # Check out with lf (UNIX) line endings
*.sh text eol=lf *.sh text eol=lf

46
app.manifest Normal file
View File

@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<asmv1:assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1" xmlns:asmv1="urn:schemas-microsoft-com:asm.v1" xmlns:asmv2="urn:schemas-microsoft-com:asm.v2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
<assemblyIdentity version="1.0.0.0" name="osu!" />
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
<applicationRequestMinimum>
<defaultAssemblyRequest permissionSetReference="Custom" />
<PermissionSet class="System.Security.PermissionSet" version="1" Unrestricted="true" ID="Custom" SameSite="site" />
</applicationRequestMinimum>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- Windows Vista -->
<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />
<!-- Windows 7 -->
<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />
<!-- Windows 8 -->
<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />
<!-- Windows 8.1 -->
<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />
<!-- Windows 10 -->
<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />
</application>
</compatibility>
<asmv3:application>
<asmv3:windowsSettings xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">
<dpiAware>true</dpiAware>
</asmv3:windowsSettings>
</asmv3:application>
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
</asmv1:assembly>

30
appveyor_deploy.yml Normal file
View File

@ -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\*'

@ -1 +1 @@
Subproject commit 7e8788e601b62577e51197a29e24f56eeeac0286 Subproject commit 16e6a453db9a8f4454238a2911eb5f1444b7ec2a

View File

@ -57,8 +57,12 @@ namespace osu.Desktop.Deploy
private static string codeSigningPassword; private static string codeSigningPassword;
private static bool interactive;
public static void Main(string[] args) public static void Main(string[] args)
{ {
interactive = args.Length == 0;
displayHeader(); displayHeader();
findSolutionPath(); findSolutionPath();
@ -82,15 +86,15 @@ namespace osu.Desktop.Deploy
string version = $"{verBase}{increment}"; string version = $"{verBase}{increment}";
Console.ForegroundColor = ConsoleColor.White; Console.ForegroundColor = ConsoleColor.White;
Console.Write($"Ready to deploy {version}: "); Console.Write($"Ready to deploy {version}!");
Console.ReadLine(); pauseIfInteractive();
sw.Start(); sw.Start();
if (!string.IsNullOrEmpty(CodeSigningCertificate)) if (!string.IsNullOrEmpty(CodeSigningCertificate))
{ {
Console.Write("Enter code signing password: "); Console.Write("Enter code signing password: ");
codeSigningPassword = readLineMasked(); codeSigningPassword = args.Length > 0 ? args[0] : readLineMasked();
} }
write("Updating AssemblyInfo..."); write("Updating AssemblyInfo...");
@ -124,7 +128,7 @@ namespace osu.Desktop.Deploy
updateCsprojVersion("0.0.0"); updateCsprojVersion("0.0.0");
write("Done!", ConsoleColor.White); write("Done!", ConsoleColor.White);
Console.ReadLine(); pauseIfInteractive();
} }
private static void displayHeader() private static void displayHeader()
@ -388,10 +392,18 @@ namespace osu.Desktop.Deploy
Console.ForegroundColor = ConsoleColor.Red; Console.ForegroundColor = ConsoleColor.Red;
Console.WriteLine($"FATAL ERROR: {message}"); Console.WriteLine($"FATAL ERROR: {message}");
Console.ReadLine(); pauseIfInteractive();
Environment.Exit(-1); Environment.Exit(-1);
} }
private static void pauseIfInteractive()
{
if (interactive)
Console.ReadLine();
else
Console.WriteLine();
}
private static void write(string message, ConsoleColor col = ConsoleColor.Gray) private static void write(string message, ConsoleColor col = ConsoleColor.Gray)
{ {
if (sw.ElapsedMilliseconds > 0) if (sw.ElapsedMilliseconds > 0)

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Catch namespace osu.Game.Rulesets.Catch
{ {
@ -29,6 +30,44 @@ namespace osu.Game.Rulesets.Catch
new KeyBinding(InputKey.Shift, CatchAction.Dash), new KeyBinding(InputKey.Shift, CatchAction.Dash),
}; };
public override IEnumerable<Mod> 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<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
switch (type) switch (type)

View File

@ -1,13 +1,88 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // 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 osu.Game.Rulesets.Mods;
using System;
namespace osu.Game.Rulesets.Catch.Mods namespace osu.Game.Rulesets.Catch.Mods
{ {
public class CatchModHardRock : ModHardRock public class CatchModHardRock : ModHardRock, IApplicableToHitObject<CatchHitObject>
{ {
public override double ScoreMultiplier => 1.12; public override double ScoreMultiplier => 1.12;
public override bool Ranked => true; 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;
}
} }
} }

View File

@ -14,6 +14,7 @@ using osu.Framework.Input.Bindings;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Rulesets.Mania.Replays; using osu.Game.Rulesets.Mania.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Mania 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 RulesetContainer CreateRulesetContainerWith(WorkingBeatmap beatmap, bool isForCurrentRuleset) => new ManiaRulesetContainer(this, beatmap, isForCurrentRuleset);
public override IEnumerable<Mod> 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<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
switch (type) switch (type)

View File

@ -21,6 +21,7 @@ using osu.Game.Rulesets.Objects.Types;
using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Osu.Replays;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Osu namespace osu.Game.Rulesets.Osu
{ {
@ -66,6 +67,53 @@ namespace osu.Game.Rulesets.Osu
}; };
} }
public override IEnumerable<Mod> 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<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
switch (type) switch (type)

View File

@ -12,6 +12,7 @@ using osu.Framework.Graphics;
using osu.Framework.Input.Bindings; using osu.Framework.Input.Bindings;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Rulesets.Taiko.Replays;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets.Taiko namespace osu.Game.Rulesets.Taiko
{ {
@ -31,6 +32,44 @@ namespace osu.Game.Rulesets.Taiko
new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim), new KeyBinding(InputKey.MouseRight, TaikoAction.RightRim),
}; };
public override IEnumerable<Mod> 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<Mod> GetModsFor(ModType type) public override IEnumerable<Mod> GetModsFor(ModType type)
{ {
switch (type) switch (type)

View File

@ -0,0 +1,62 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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<Type> 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()));
}
}
}

View File

@ -14,6 +14,8 @@ using osu.Game.Rulesets.Scoring;
using osu.Game.Users; using osu.Game.Users;
using System.Collections.Generic; using System.Collections.Generic;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
{ {
@ -47,11 +49,11 @@ namespace osu.Game.Tests.Visual
AddStep("scores pack 1", () => scoresContainer.Scores = scores); AddStep("scores pack 1", () => scoresContainer.Scores = scores);
AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores); AddStep("scores pack 2", () => scoresContainer.Scores = anotherScores);
AddStep("only top score", () => scoresContainer.Scores = new[] { topScore }); AddStep("only top score", () => scoresContainer.Scores = new[] { topScore });
AddStep("remove scores", scoresContainer.CleanAllScores); AddStep("remove scores", () => scoresContainer.Scores = null);
AddStep("turn on loading", () => scoresContainer.IsLoading = true);
AddStep("turn off loading", () => scoresContainer.IsLoading = false);
AddStep("resize to big", () => container.ResizeWidthTo(1, 300)); AddStep("resize to big", () => container.ResizeWidthTo(1, 300));
AddStep("resize to normal", () => container.ResizeWidthTo(0.8f, 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[] scores = new[]
{ {

View File

@ -8,6 +8,9 @@ using NUnit.Framework;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Overlays; 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.Rulesets;
using osu.Game.Users; using osu.Game.Users;
@ -18,6 +21,26 @@ namespace osu.Game.Tests.Visual
{ {
private readonly BeatmapSetOverlay overlay; private readonly BeatmapSetOverlay overlay;
public override IReadOnlyList<Type> 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() public TestCaseBeatmapSetOverlay()
{ {
Add(overlay = new BeatmapSetOverlay()); Add(overlay = new BeatmapSetOverlay());
@ -29,6 +52,10 @@ namespace osu.Game.Tests.Visual
var mania = rulesets.GetRuleset(3); var mania = rulesets.GetRuleset(3);
var taiko = rulesets.GetRuleset(1); var taiko = rulesets.GetRuleset(1);
AddStep(@"show loading", () => overlay.ShowBeatmapSet(null));
AddStep(@"show online", () => overlay.FetchAndShowBeatmapSet(55));
AddStep(@"show first", () => AddStep(@"show first", () =>
{ {
overlay.ShowBeatmapSet(new BeatmapSetInfo overlay.ShowBeatmapSet(new BeatmapSetInfo

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // 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;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using OpenTK; using OpenTK;
@ -11,6 +10,7 @@ using System.Collections.Generic;
using System; using System;
using NUnit.Framework; using NUnit.Framework;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual

View File

@ -8,6 +8,7 @@ using NUnit.Framework;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Tests.Visual namespace osu.Game.Tests.Visual
@ -23,6 +24,7 @@ namespace osu.Game.Tests.Visual
typeof(UserProfileOverlay), typeof(UserProfileOverlay),
typeof(RankGraph), typeof(RankGraph),
typeof(LineGraph), typeof(LineGraph),
typeof(BadgeContainer)
}; };
public TestCaseUserProfile() public TestCaseUserProfile()
@ -53,6 +55,15 @@ namespace osu.Game.Tests.Visual
{ {
Mode = @"osu", Mode = @"osu",
Data = Enumerable.Range(2345, 45).Concat(Enumerable.Range(2109, 40)).ToArray() 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)); }, false));

View File

@ -3,6 +3,9 @@
<PropertyGroup Label="C#"> <PropertyGroup Label="C#">
<LangVersion>7</LangVersion> <LangVersion>7</LangVersion>
</PropertyGroup> </PropertyGroup>
<PropertyGroup>
<ApplicationManifest>..\app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup Label="License"> <ItemGroup Label="License">
<None Include="..\osu.licenseheader"> <None Include="..\osu.licenseheader">
<Link>osu.licenseheader</Link> <Link>osu.licenseheader</Link>

View File

@ -0,0 +1,42 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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,
}
}

View File

@ -1,23 +0,0 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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,
}
}

View File

@ -85,6 +85,8 @@ namespace osu.Game.Configuration
Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg); Set(OsuSetting.ScreenshotFormat, ScreenshotFormat.Jpg);
Set(OsuSetting.ScreenshotCaptureMenuCursor, false); Set(OsuSetting.ScreenshotCaptureMenuCursor, false);
Set(OsuSetting.SongSelectRightMouseScroll, false);
} }
public OsuConfigManager(Storage storage) : base(storage) public OsuConfigManager(Storage storage) : base(storage)
@ -130,6 +132,7 @@ namespace osu.Game.Configuration
SpeedChangeVisualisation, SpeedChangeVisualisation,
Skin, Skin,
ScreenshotFormat, ScreenshotFormat,
ScreenshotCaptureMenuCursor ScreenshotCaptureMenuCursor,
SongSelectRightMouseScroll
} }
} }

View File

@ -61,9 +61,9 @@ namespace osu.Game.Graphics.Containers
AddText(text.Substring(previousLinkEnd)); 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<SpriteText> creationParameters = null)
{ {
AddInternal(new DrawableLinkCompiler(AddText(text).ToList()) AddInternal(new DrawableLinkCompiler(AddText(text, creationParameters).ToList())
{ {
TooltipText = tooltipText ?? (url != text ? url : string.Empty), TooltipText = tooltipText ?? (url != text ? url : string.Empty),
Action = () => Action = () =>

View File

@ -2,7 +2,7 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System.Collections.Generic; using System.Collections.Generic;
using osu.Game.Beatmaps; using System.ComponentModel;
using osu.Game.Overlays; using osu.Game.Overlays;
using osu.Game.Overlays.Direct; using osu.Game.Overlays.Direct;
using osu.Game.Rulesets; using osu.Game.Rulesets;
@ -13,21 +13,36 @@ namespace osu.Game.Online.API.Requests
{ {
private readonly string query; private readonly string query;
private readonly RulesetInfo ruleset; private readonly RulesetInfo ruleset;
private readonly RankStatus rankStatus; private readonly BeatmapSearchCategory searchCategory;
private readonly DirectSortCriteria sortCriteria; private readonly DirectSortCriteria sortCriteria;
private readonly SortDirection direction; private readonly SortDirection direction;
private string directionString => direction == SortDirection.Descending ? @"desc" : @"asc"; 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.query = System.Uri.EscapeDataString(query);
this.ruleset = ruleset; this.ruleset = ruleset;
this.rankStatus = rankStatus; this.searchCategory = searchCategory;
this.sortCriteria = sortCriteria; this.sortCriteria = sortCriteria;
this.direction = direction; this.direction = direction;
} }
// ReSharper disable once ImpureMethodCallOnReadonlyValueField // 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,
} }
} }

View File

@ -158,7 +158,7 @@ namespace osu.Game
/// Show a beatmap set as an overlay. /// Show a beatmap set as an overlay.
/// </summary> /// </summary>
/// <param name="setId">The set to display.</param> /// <param name="setId">The set to display.</param>
public void ShowBeatmapSet(int setId) => beatmapSetOverlay.ShowBeatmapSet(setId); public void ShowBeatmapSet(int setId) => beatmapSetOverlay.FetchAndShowBeatmapSet(setId);
/// <summary> /// <summary>
/// Show a user's profile as an overlay. /// Show a user's profile as an overlay.
@ -196,6 +196,7 @@ namespace osu.Game
} }
Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap); Beatmap.Value = BeatmapManager.GetWorkingBeatmap(s.Beatmap);
Beatmap.Value.Mods.Value = s.Mods;
menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay))); menu.Push(new PlayerLoader(new ReplayPlayer(s.Replay)));
} }

View File

@ -23,9 +23,8 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly ClickableArea clickableArea; private readonly ClickableArea clickableArea;
private readonly FillFlowContainer fields; private readonly FillFlowContainer fields;
private UserProfileOverlay profile;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -34,28 +33,36 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
var i = BeatmapSet.OnlineInfo; updateDisplay();
}
}
avatar.User = BeatmapSet.Metadata.Author; private void updateDisplay()
clickableArea.Action = () => profile?.ShowUser(avatar.User); {
avatar.User = BeatmapSet?.Metadata.Author;
fields.Children = new Drawable[] fields.Clear();
{ if (BeatmapSet == null)
new Field("made by", BeatmapSet.Metadata.Author.Username, @"Exo2.0-RegularItalic"), return;
new Field("submitted on", i.Submitted.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")
{
Margin = new MarginPadding { Top = 5 },
},
};
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")); Margin = new MarginPadding { Top = 5 },
} },
else if (i.LastUpdated.HasValue) };
{
fields.Add(new Field("last updated on ", i.LastUpdated.Value.ToString(@"MMM d, yyyy"), @"Exo2.0-Bold")); 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, Masking = true,
Child = avatar = new UpdateableAvatar Child = avatar = new UpdateableAvatar
{ {
ShowGuestOnNull = false,
Size = new Vector2(height), Size = new Vector2(height),
}, },
EdgeEffect = new EdgeEffectParameters EdgeEffect = new EdgeEffectParameters
@ -95,8 +103,12 @@ namespace osu.Game.Overlays.BeatmapSet
[BackgroundDependencyLoader(true)] [BackgroundDependencyLoader(true)]
private void load(UserProfileOverlay profile) private void load(UserProfileOverlay profile)
{ {
this.profile = profile; clickableArea.Action = () =>
clickableArea.Action = () => profile?.ShowUser(avatar.User); {
if (avatar.User != null) profile?.ShowUser(avatar.User);
};
updateDisplay();
} }
private class Field : FillFlowContainer private class Field : FillFlowContainer

View File

@ -18,6 +18,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly Statistic length, bpm, circleCount, sliderCount; private readonly Statistic length, bpm, circleCount, sliderCount;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -26,11 +27,12 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
bpm.Value = BeatmapSet.OnlineInfo.BPM.ToString(@"0.##"); updateDisplay();
} }
} }
private BeatmapInfo beatmap; private BeatmapInfo beatmap;
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
get { return beatmap; } get { return beatmap; }
@ -39,6 +41,22 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmap) return; if (value == beatmap) return;
beatmap = value; 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"); length.Value = TimeSpan.FromSeconds(beatmap.OnlineInfo.Length).ToString(@"m\:ss");
circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString(); circleCount.Value = beatmap.OnlineInfo.CircleCount.ToString();
sliderCount.Value = beatmap.OnlineInfo.SliderCount.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 class Statistic : Container, IHasTooltip
{ {
private readonly string name; private readonly string name;
private readonly OsuSpriteText value; private readonly OsuSpriteText value;
public string TooltipText => name; public string TooltipText => name;
public string Value public string Value
{ {
get { return value.Text; } get { return value.Text; }

View File

@ -41,9 +41,16 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
Beatmap.Value = BeatmapSet.Beatmaps.First(); updateDisplay();
plays.Value = BeatmapSet.OnlineInfo.PlayCount; }
favourites.Value = BeatmapSet.OnlineInfo.FavouriteCount; }
private void updateDisplay()
{
difficulties.Clear();
if (BeatmapSet != null)
{
difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b) difficulties.ChildrenEnumerable = BeatmapSet.Beatmaps.OrderBy(beatmap => beatmap.StarDifficulty).Select(b => new DifficultySelectorButton(b)
{ {
State = DifficultySelectorState.NotSelected, State = DifficultySelectorState.NotSelected,
@ -53,14 +60,16 @@ namespace osu.Game.Overlays.BeatmapSet
starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##"); starRating.Text = beatmap.StarDifficulty.ToString("Star Difficulty 0.##");
starRating.FadeIn(100); starRating.FadeIn(100);
}, },
OnClicked = beatmap => OnClicked = beatmap => { Beatmap.Value = 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() public BeatmapPicker()
@ -140,6 +149,7 @@ namespace osu.Game.Overlays.BeatmapSet
private void load(OsuColour colours) private void load(OsuColour colours)
{ {
starRating.Colour = colours.Yellow; starRating.Colour = colours.Yellow;
updateDisplay();
} }
protected override void LoadComplete() protected override void LoadComplete()
@ -150,7 +160,10 @@ namespace osu.Game.Overlays.BeatmapSet
Beatmap.TriggerChange(); Beatmap.TriggerChange();
} }
private void showBeatmap(BeatmapInfo beatmap) => version.Text = beatmap.Version; private void showBeatmap(BeatmapInfo beatmap)
{
version.Text = beatmap?.Version;
}
private void updateDifficultyButtons() private void updateDifficultyButtons()
{ {

View File

@ -7,7 +7,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using OpenTK; using OpenTK;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class DownloadButton : HeaderButton public class DownloadButton : HeaderButton
{ {

View File

@ -10,7 +10,7 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using OpenTK; using OpenTK;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class FavouriteButton : HeaderButton public class FavouriteButton : HeaderButton
{ {

View File

@ -1,12 +1,12 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Framework.Allocation;
namespace osu.Game.Overlays.BeatmapSet namespace osu.Game.Overlays.BeatmapSet.Buttons
{ {
public class HeaderButton : TriangleButton public class HeaderButton : TriangleButton
{ {

View File

@ -3,6 +3,7 @@
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Audio.Track; using osu.Framework.Audio.Track;
using osu.Framework.Configuration;
using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -11,12 +12,11 @@ using osu.Framework.Input;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Direct;
using OpenTK; using OpenTK;
using OpenTK.Graphics; 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 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 // prevent negative (potential infinite) width if a track without length was loaded
progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f; progress.Width = preview.Length > 0 ? (float)(preview.CurrentTime / preview.Length) : 0f;
} }
else
progress.Width = 0;
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)

View File

@ -1,11 +1,13 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // 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.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps; using osu.Game.Beatmaps;
using osu.Game.Overlays.BeatmapSet.Buttons;
using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Details;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -20,6 +22,7 @@ namespace osu.Game.Overlays.BeatmapSet
private readonly UserRatings ratings; private readonly UserRatings ratings;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -33,19 +36,24 @@ namespace osu.Game.Overlays.BeatmapSet
} }
private BeatmapInfo beatmap; private BeatmapInfo beatmap;
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
get { return beatmap; } get { return beatmap; }
set set
{ {
if (value == beatmap) return; if (value == beatmap) return;
beatmap = value;
basic.Beatmap = advanced.Beatmap = Beatmap; basic.Beatmap = advanced.Beatmap = beatmap = value;
ratings.Metrics = Beatmap.Metrics; updateDisplay();
} }
} }
private void updateDisplay()
{
ratings.Metrics = Beatmap?.Metrics;
}
public Details() public Details()
{ {
Width = BeatmapSetOverlay.RIGHT_WIDTH; 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; public void StopPreview() => preview.Playing.Value = false;
private class DetailBox : Container private class DetailBox : Container

View File

@ -11,6 +11,7 @@ using osu.Game.Beatmaps;
using osu.Game.Beatmaps.Drawables; using osu.Game.Beatmaps.Drawables;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.BeatmapSet.Buttons;
using OpenTK; using OpenTK;
using OpenTK.Graphics; using OpenTK.Graphics;
@ -18,7 +19,7 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
public class Header : Container 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 tabs_height = 50;
private const float buttons_height = 45; private const float buttons_height = 45;
private const float buttons_spacing = 5; private const float buttons_spacing = 5;
@ -34,12 +35,13 @@ namespace osu.Game.Overlays.BeatmapSet
public Details Details; public Details Details;
private BeatmapManager beatmaps; private BeatmapManager beatmaps;
private DelayedLoadWrapper cover; private DelayedLoadWrapper cover;
public readonly BeatmapPicker Picker; public readonly BeatmapPicker Picker;
private BeatmapSetInfo beatmapSet; private BeatmapSetInfo beatmapSet;
private readonly FavouriteButton favouriteButton;
public BeatmapSetInfo BeatmapSet public BeatmapSetInfo BeatmapSet
{ {
get { return beatmapSet; } get { return beatmapSet; }
@ -49,15 +51,26 @@ namespace osu.Game.Overlays.BeatmapSet
beatmapSet = value; beatmapSet = value;
Picker.BeatmapSet = author.BeatmapSet = Details.BeatmapSet = BeatmapSet; 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); noVideoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 0 : 1, transition_duration);
videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration); videoButtons.FadeTo(BeatmapSet.OnlineInfo.HasVideo ? 1 : 0, transition_duration);
cover?.FadeOut(400, Easing.Out);
coverContainer.Add(cover = new DelayedLoadWrapper( coverContainer.Add(cover = new DelayedLoadWrapper(
new BeatmapSetCover(BeatmapSet) new BeatmapSetCover(BeatmapSet)
{ {
@ -71,6 +84,11 @@ namespace osu.Game.Overlays.BeatmapSet
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
}); });
} }
else
{
downloadButtonsContainer.FadeOut(transition_duration);
favouriteButton.FadeOut(transition_duration);
}
} }
public Header() public Header()
@ -166,7 +184,7 @@ namespace osu.Game.Overlays.BeatmapSet
Margin = new MarginPadding { Top = 10 }, Margin = new MarginPadding { Top = 10 },
Children = new Drawable[] Children = new Drawable[]
{ {
new FavouriteButton(), favouriteButton = new FavouriteButton(),
downloadButtonsContainer = new Container downloadButtonsContainer = new Container
{ {
RelativeSizeAxes = Axes.Both, RelativeSizeAxes = Axes.Both,
@ -238,6 +256,8 @@ namespace osu.Game.Overlays.BeatmapSet
this.beatmaps = beatmaps; this.beatmaps = beatmaps;
beatmaps.ItemAdded += handleBeatmapAdd; beatmaps.ItemAdded += handleBeatmapAdd;
updateDisplay();
} }
protected override void Dispose(bool isDisposing) protected override void Dispose(bool isDisposing)

View File

@ -34,11 +34,16 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmapSet) return; if (value == beatmapSet) return;
beatmapSet = value; beatmapSet = value;
source.Text = BeatmapSet.Metadata.Source; updateDisplay();
tags.Text = BeatmapSet.Metadata.Tags;
} }
} }
private void updateDisplay()
{
source.Text = BeatmapSet?.Metadata.Source ?? string.Empty;
tags.Text = BeatmapSet?.Metadata.Tags ?? string.Empty;
}
public BeatmapInfo Beatmap public BeatmapInfo Beatmap
{ {
get { return successRate.Beatmap; } get { return successRate.Beatmap; }
@ -132,6 +137,8 @@ namespace osu.Game.Overlays.BeatmapSet
successRateBackground.Colour = colours.GrayE; successRateBackground.Colour = colours.GrayE;
source.TextColour = description.TextColour = colours.Gray5; source.TextColour = description.TextColour = colours.Gray5;
tags.TextColour = colours.BlueDark; tags.TextColour = colours.BlueDark;
updateDisplay();
} }
private class MetadataSection : FillFlowContainer private class MetadataSection : FillFlowContainer

View File

@ -2,15 +2,15 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK; using OpenTK;
using OpenTK.Graphics;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
namespace osu.Game.Overlays.BeatmapSet.Scores namespace osu.Game.Overlays.BeatmapSet.Scores
{ {
@ -22,49 +22,75 @@ namespace osu.Game.Overlays.BeatmapSet.Scores
private readonly FillFlowContainer flow; private readonly FillFlowContainer flow;
private readonly DrawableTopScore topScore; private readonly DrawableTopScore topScore;
private readonly LoadingAnimation loadingAnimation; private readonly LoadingAnimation loadingAnimation;
private readonly Box foreground;
private bool isLoading; private bool loading
public bool IsLoading
{ {
get { return isLoading; } set => loadingAnimation.FadeTo(value ? 1 : 0, fade_duration);
set
{
if (isLoading == value) return;
isLoading = value;
foreground.FadeTo(isLoading ? 1 : 0, fade_duration);
loadingAnimation.FadeTo(isLoading ? 1 : 0, fade_duration);
}
} }
private IEnumerable<OnlineScore> scores; private IEnumerable<OnlineScore> scores;
private BeatmapInfo beatmap;
public IEnumerable<OnlineScore> Scores public IEnumerable<OnlineScore> Scores
{ {
get { return scores; } get { return scores; }
set set
{ {
getScoresRequest?.Cancel();
scores = value; scores = value;
var scoresAmount = scores.Count();
if (scoresAmount == 0)
{
CleanAllScores();
return;
}
topScore.Score = scores.FirstOrDefault(); updateDisplay();
topScore.Show();
flow.Clear();
if (scoresAmount < 2)
return;
for (int i = 1; i < scoresAmount; i++)
flow.Add(new DrawableScore(i, scores.ElementAt(i)));
} }
} }
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() public ScoresContainer()
{ {
RelativeSizeAxes = Axes.X; 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 loadingAnimation = new LoadingAnimation
{ {
Alpha = 0, Alpha = 0,
Margin = new MarginPadding(20)
}, },
}; };
} }
public void CleanAllScores() [BackgroundDependencyLoader]
private void load(APIAccess api)
{ {
topScore.Hide(); this.api = api;
flow.Clear(); updateDisplay();
} }
} }
} }

View File

@ -29,18 +29,23 @@ namespace osu.Game.Overlays.BeatmapSet
if (value == beatmap) return; if (value == beatmap) return;
beatmap = value; beatmap = value;
int passCount = beatmap.OnlineInfo.PassCount; updateDisplay();
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;
} }
} }
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() public SuccessRate()
{ {
Children = new Drawable[] Children = new Drawable[]
@ -74,7 +79,6 @@ namespace osu.Game.Overlays.BeatmapSet
{ {
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopCentre, Origin = Anchor.TopCentre,
Text = @"0%",
TextSize = 13, TextSize = 13,
}, },
}, },
@ -103,6 +107,8 @@ namespace osu.Game.Overlays.BeatmapSet
successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5; successRateLabel.Colour = successPercent.Colour = graphLabel.Colour = colours.Gray5;
successRate.AccentColour = colours.Green; successRate.AccentColour = colours.Green;
successRate.BackgroundColour = colours.GrayD; successRate.BackgroundColour = colours.GrayD;
updateDisplay();
} }
protected override void UpdateAfterChildren() protected override void UpdateAfterChildren()

View File

@ -27,11 +27,8 @@ namespace osu.Game.Overlays
private readonly Header header; private readonly Header header;
private readonly Info info; private readonly Info info;
private readonly ScoresContainer scores;
private APIAccess api; private APIAccess api;
private RulesetStore rulesets; private RulesetStore rulesets;
private GetScoresRequest getScoresRequest;
private readonly ScrollContainer scroll; private readonly ScrollContainer scroll;
@ -40,6 +37,7 @@ namespace osu.Game.Overlays
public BeatmapSetOverlay() public BeatmapSetOverlay()
{ {
ScoresContainer scores;
Waves.FirstWaveColour = OsuColour.Gray(0.4f); Waves.FirstWaveColour = OsuColour.Gray(0.4f);
Waves.SecondWaveColour = OsuColour.Gray(0.3f); Waves.SecondWaveColour = OsuColour.Gray(0.3f);
Waves.ThirdWaveColour = OsuColour.Gray(0.2f); Waves.ThirdWaveColour = OsuColour.Gray(0.2f);
@ -88,31 +86,10 @@ namespace osu.Game.Overlays
header.Picker.Beatmap.ValueChanged += b => header.Picker.Beatmap.ValueChanged += b =>
{ {
info.Beatmap = 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] [BackgroundDependencyLoader]
private void load(APIAccess api, RulesetStore rulesets) private void load(APIAccess api, RulesetStore rulesets)
{ {
@ -139,7 +116,7 @@ namespace osu.Game.Overlays
return true; 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. // 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); var req = new GetBeatmapSetRequest(beatmapSetId);

View File

@ -7,15 +7,15 @@ using osu.Framework.Allocation;
using osu.Framework.Configuration; using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Online.API.Requests;
using osu.Game.Overlays.SearchableList; using osu.Game.Overlays.SearchableList;
using osu.Game.Rulesets; using osu.Game.Rulesets;
namespace osu.Game.Overlays.Direct namespace osu.Game.Overlays.Direct
{ {
public class FilterControl : SearchableListFilterControl<DirectSortCriteria, RankStatus> public class FilterControl : SearchableListFilterControl<DirectSortCriteria, BeatmapSearchCategory>
{ {
public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>(); public readonly Bindable<RulesetInfo> Ruleset = new Bindable<RulesetInfo>();
private FillFlowContainer<RulesetToggleButton> modeButtons; private FillFlowContainer<RulesetToggleButton> modeButtons;

View File

@ -78,12 +78,7 @@ namespace osu.Game.Overlays.Direct
loadingAnimation = new LoadingAnimation(), loadingAnimation = new LoadingAnimation(),
}); });
Playing.ValueChanged += playing => Playing.ValueChanged += updatePreviewTrack;
{
icon.Icon = playing ? FontAwesome.fa_pause : FontAwesome.fa_play;
icon.FadeColour(playing || IsHovered ? hoverColour : Color4.White, 120, Easing.InOutQuint);
updatePreviewTrack(playing);
};
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]
@ -125,6 +120,15 @@ namespace osu.Game.Overlays.Direct
private void updatePreviewTrack(bool playing) 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 (playing)
{ {
if (Preview == null) if (Preview == null)

View File

@ -22,7 +22,7 @@ using OpenTK.Graphics;
namespace osu.Game.Overlays namespace osu.Game.Overlays
{ {
public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, RankStatus> public class DirectOverlay : SearchableListOverlay<DirectTab, DirectSortCriteria, BeatmapSearchCategory>
{ {
private const float panel_padding = 10f; private const float panel_padding = 10f;
@ -40,7 +40,7 @@ namespace osu.Game.Overlays
protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265"); protected override Color4 TrianglesColourDark => OsuColour.FromHex(@"3f5265");
protected override SearchableListHeader<DirectTab> CreateHeader() => new Header(); protected override SearchableListHeader<DirectTab> CreateHeader() => new Header();
protected override SearchableListFilterControl<DirectSortCriteria, RankStatus> CreateFilterControl() => new FilterControl(); protected override SearchableListFilterControl<DirectSortCriteria, BeatmapSearchCategory> CreateFilterControl() => new FilterControl();
private IEnumerable<BeatmapSetInfo> beatmapSets; private IEnumerable<BeatmapSetInfo> beatmapSets;

View File

@ -0,0 +1,195 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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;
}
}
}

View File

@ -2,9 +2,10 @@
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System; using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using OpenTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Configuration;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
@ -14,10 +15,9 @@ using osu.Game.Graphics;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Game.Users; using osu.Game.Users;
using System.Collections.Generic; using OpenTK;
using osu.Framework.Configuration;
namespace osu.Game.Overlays.Profile namespace osu.Game.Overlays.Profile.Header
{ {
public class RankGraph : Container public class RankGraph : Container
{ {

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using OpenTK;
using osu.Framework.Allocation; using osu.Framework.Allocation;
using osu.Framework.Graphics; using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Containers;
@ -9,8 +8,9 @@ using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Backgrounds;
using OpenTK;
namespace osu.Game.Overlays.Profile namespace osu.Game.Overlays.Profile.Header
{ {
public class SupporterIcon : CircularContainer, IHasTooltip public class SupporterIcon : CircularContainer, IHasTooltip
{ {

View File

@ -17,13 +17,14 @@ using osu.Framework.Graphics.Textures;
using osu.Game.Graphics; using osu.Game.Graphics;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites; using osu.Game.Graphics.Sprites;
using osu.Game.Overlays.Profile.Header;
using osu.Game.Users; using osu.Game.Users;
namespace osu.Game.Overlays.Profile namespace osu.Game.Overlays.Profile
{ {
public class ProfileHeader : Container public class ProfileHeader : Container
{ {
private readonly OsuTextFlowContainer infoTextLeft; private readonly LinkFlowContainer infoTextLeft;
private readonly LinkFlowContainer infoTextRight; private readonly LinkFlowContainer infoTextRight;
private readonly FillFlowContainer<SpriteText> scoreText, scoreNumberText; private readonly FillFlowContainer<SpriteText> scoreText, scoreNumberText;
private readonly RankGraph rankGraph; private readonly RankGraph rankGraph;
@ -35,6 +36,7 @@ namespace osu.Game.Overlays.Profile
private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA; private readonly GradeBadge gradeSSPlus, gradeSS, gradeSPlus, gradeS, gradeA;
private readonly Box colourBar; private readonly Box colourBar;
private readonly DrawableFlag countryFlag; private readonly DrawableFlag countryFlag;
private readonly BadgeContainer badgeContainer;
private const float cover_height = 350; private const float cover_height = 350;
private const float info_height = 150; private const float info_height = 150;
@ -42,6 +44,7 @@ namespace osu.Game.Overlays.Profile
private const float avatar_size = 110; private const float avatar_size = 110;
private const float level_position = 30; private const float level_position = 30;
private const float level_height = 60; private const float level_height = 60;
private const float stats_width = 280;
public ProfileHeader(User user) public ProfileHeader(User user)
{ {
@ -66,9 +69,9 @@ namespace osu.Game.Overlays.Profile
{ {
Anchor = Anchor.BottomLeft, Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft, Origin = Anchor.BottomLeft,
X = UserProfileOverlay.CONTENT_X_MARGIN, Padding = new MarginPadding { Left = UserProfileOverlay.CONTENT_X_MARGIN, Bottom = 20, Right = stats_width + UserProfileOverlay.CONTENT_X_MARGIN },
Y = -20, AutoSizeAxes = Axes.Y,
AutoSizeAxes = Axes.Both, RelativeSizeAxes = Axes.X,
Children = new Drawable[] Children = new Drawable[]
{ {
new UpdateableAvatar new UpdateableAvatar
@ -116,7 +119,15 @@ namespace osu.Game.Overlays.Profile
Height = 20 Height = 20
} }
} }
} },
badgeContainer = new BadgeContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Origin = Anchor.BottomLeft,
Margin = new MarginPadding { Bottom = 5 },
Alpha = 0,
},
} }
}, },
colourBar = new Box 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, X = UserProfileOverlay.CONTENT_X_MARGIN,
Y = cover_height + 20, Y = cover_height + 20,
@ -156,7 +167,7 @@ namespace osu.Game.Overlays.Profile
{ {
X = -UserProfileOverlay.CONTENT_X_MARGIN, X = -UserProfileOverlay.CONTENT_X_MARGIN,
RelativeSizeAxes = Axes.Y, RelativeSizeAxes = Axes.Y,
Width = 280, Width = stats_width,
Anchor = Anchor.TopRight, Anchor = Anchor.TopRight,
Origin = Anchor.TopRight, Origin = Anchor.TopRight,
Children = new Drawable[] Children = new Drawable[]
@ -339,7 +350,7 @@ namespace osu.Game.Overlays.Profile
if (user.Country != null) if (user.Country != null)
{ {
infoTextLeft.AddText("from ", lightText); infoTextLeft.AddText("From ", lightText);
infoTextLeft.AddText(user.Country.FullName, boldItalic); infoTextLeft.AddText(user.Country.FullName, boldItalic);
countryFlag.Country = user.Country; countryFlag.Country = user.Country;
} }
@ -367,6 +378,10 @@ namespace osu.Game.Overlays.Profile
infoTextLeft.AddText(string.Join(", ", user.PlayStyle), boldItalic); 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; string websiteWithoutProtcol = user.Website;
if (!string.IsNullOrEmpty(websiteWithoutProtcol)) if (!string.IsNullOrEmpty(websiteWithoutProtcol))
{ {
@ -381,8 +396,10 @@ namespace osu.Game.Overlays.Profile
infoTextRight.NewParagraph(); infoTextRight.NewParagraph();
if (!string.IsNullOrEmpty(user.Twitter)) if (!string.IsNullOrEmpty(user.Twitter))
tryAddInfoRightLine(FontAwesome.fa_twitter, "@" + user.Twitter, $@"https://twitter.com/{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_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) if (user.Statistics != null)
{ {
@ -417,6 +434,8 @@ namespace osu.Game.Overlays.Profile
rankGraph.User.Value = user; rankGraph.User.Value = user;
} }
badgeContainer.ShowBadges(user.Badges);
} }
private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null) private void tryAddInfoRightLine(FontAwesome icon, string str, string url = null)

View File

@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Profile.Sections
{ {
Action = () => Action = () =>
{ {
if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.ShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value); if (beatmap.OnlineBeatmapSetID.HasValue) beatmapSetOverlay?.FetchAndShowBeatmapSet(beatmap.OnlineBeatmapSetID.Value);
}; };
Child = new FillFlowContainer Child = new FillFlowContainer

View File

@ -17,6 +17,11 @@ namespace osu.Game.Overlays.Settings.Sections.Gameplay
{ {
Children = new Drawable[] Children = new Drawable[]
{ {
new SettingsCheckbox
{
LabelText = "Right mouse drag to absolute scroll",
Bindable = config.GetBindable<bool>(OsuSetting.SongSelectRightMouseScroll),
},
new SettingsCheckbox new SettingsCheckbox
{ {
LabelText = "Show converted beatmaps", LabelText = "Show converted beatmaps",

View File

@ -14,6 +14,7 @@ using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Replays.Types; using osu.Game.Rulesets.Replays.Types;
using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring;
using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI;
using osu.Game.Beatmaps.Legacy;
namespace osu.Game.Rulesets namespace osu.Game.Rulesets
{ {
@ -33,6 +34,13 @@ namespace osu.Game.Rulesets
public abstract IEnumerable<Mod> GetModsFor(ModType type); public abstract IEnumerable<Mod> GetModsFor(ModType type);
/// <summary>
/// Converts mods from legacy enum values. Do not override if you're not a legacy ruleset.
/// </summary>
/// <param name="mods">The legacy enum which will be converted</param>
/// <returns>An enumerable of constructed <see cref="Mod"/>s</returns>
public virtual IEnumerable<Mod> ConvertLegacyMods(LegacyMods mods) => new Mod[] { };
public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay); public Mod GetAutoplayMod() => GetAllMods().First(mod => mod is ModAutoplay);
protected Ruleset(RulesetInfo rulesetInfo = null) protected Ruleset(RulesetInfo rulesetInfo = null)

View File

@ -9,6 +9,8 @@ using osu.Game.Rulesets.Replays;
using osu.Game.Rulesets.Replays.Legacy; using osu.Game.Rulesets.Replays.Legacy;
using osu.Game.Users; using osu.Game.Users;
using SharpCompress.Compressors.LZMA; using SharpCompress.Compressors.LZMA;
using osu.Game.Beatmaps.Legacy;
using System.Linq;
namespace osu.Game.Rulesets.Scoring.Legacy namespace osu.Game.Rulesets.Scoring.Legacy
{ {
@ -64,7 +66,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy
/* score.Perfect = */ /* score.Perfect = */
sr.ReadBoolean(); sr.ReadBoolean();
/* score.EnabledMods = (Mods)*/ /* score.EnabledMods = (Mods)*/
sr.ReadInt32(); score.Mods = currentRuleset.ConvertLegacyMods((LegacyMods)sr.ReadInt32()).ToArray();
/* score.HpGraphString = */ /* score.HpGraphString = */
sr.ReadString(); sr.ReadString();
/* score.Date = */ /* score.Date = */

View File

@ -97,6 +97,9 @@ namespace osu.Game.Screens.Select
private readonly Container<DrawableCarouselItem> scrollableContent; private readonly Container<DrawableCarouselItem> scrollableContent;
public Bindable<bool> RightClickScrollingEnabled = new Bindable<bool>();
public Bindable<RandomSelectAlgorithm> RandomAlgorithm = new Bindable<RandomSelectAlgorithm>(); public Bindable<RandomSelectAlgorithm> RandomAlgorithm = new Bindable<RandomSelectAlgorithm>();
private readonly List<CarouselBeatmapSet> previouslyVisitedRandomSets = new List<CarouselBeatmapSet>(); private readonly List<CarouselBeatmapSet> previouslyVisitedRandomSets = new List<CarouselBeatmapSet>();
private readonly Stack<CarouselBeatmap> randomSelectedBeatmaps = new Stack<CarouselBeatmap>(); private readonly Stack<CarouselBeatmap> randomSelectedBeatmaps = new Stack<CarouselBeatmap>();
@ -122,6 +125,10 @@ namespace osu.Game.Screens.Select
private void load(OsuConfigManager config) private void load(OsuConfigManager config)
{ {
config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm); config.BindWith(OsuSetting.RandomSelectAlgorithm, RandomAlgorithm);
config.BindWith(OsuSetting.SongSelectRightMouseScroll, RightClickScrollingEnabled);
RightClickScrollingEnabled.ValueChanged += v => RightMouseScrollbar = v;
RightClickScrollingEnabled.TriggerChange();
} }
public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet)

View File

@ -46,7 +46,7 @@ namespace osu.Game.Screens.Select.Carousel
restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore); restoreHiddenRequested = s => s.Beatmaps.ForEach(manager.Restore);
dialogOverlay = overlay; dialogOverlay = overlay;
if (beatmapOverlay != null) if (beatmapOverlay != null)
viewDetails = beatmapOverlay.ShowBeatmapSet; viewDetails = beatmapOverlay.FetchAndShowBeatmapSet;
Children = new Drawable[] Children = new Drawable[]
{ {

View File

@ -40,10 +40,10 @@ namespace osu.Game.Screens.Select.Details
firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0; firstValue.Value = Beatmap?.BaseDifficulty?.CircleSize ?? 0;
} }
hpDrain.Value = beatmap.BaseDifficulty?.DrainRate ?? 0; hpDrain.Value = Beatmap?.BaseDifficulty?.DrainRate ?? 0;
accuracy.Value = beatmap.BaseDifficulty?.OverallDifficulty ?? 0; accuracy.Value = Beatmap?.BaseDifficulty?.OverallDifficulty ?? 0;
approachRate.Value = beatmap.BaseDifficulty?.ApproachRate ?? 0; approachRate.Value = Beatmap?.BaseDifficulty?.ApproachRate ?? 0;
starDifficulty.Value = (float)beatmap.StarDifficulty; starDifficulty.Value = (float)(Beatmap?.StarDifficulty ?? 0);
} }
} }

View File

@ -25,10 +25,10 @@ namespace osu.Game.Screens.Select.Details
if (value == metrics) return; if (value == metrics) return;
metrics = value; metrics = value;
var retries = Metrics.Retries; var retries = Metrics?.Retries ?? new int[0];
var fails = Metrics.Fails; 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; failGraph.MaxValue = maxValue;
retryGraph.MaxValue = maxValue; retryGraph.MaxValue = maxValue;

View File

@ -21,6 +21,7 @@ namespace osu.Game.Screens.Select.Details
private readonly BarGraph graph; private readonly BarGraph graph;
private BeatmapMetrics metrics; private BeatmapMetrics metrics;
public BeatmapMetrics Metrics public BeatmapMetrics Metrics
{ {
get { return metrics; } get { return metrics; }
@ -31,15 +32,25 @@ namespace osu.Game.Screens.Select.Details
const int rating_range = 10; 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 negativeCount = ratings.Take(rating_range / 2).Sum();
var totalCount = ratings.Sum(); var totalCount = ratings.Sum();
negativeRatings.Text = negativeCount.ToString(); negativeRatings.Text = negativeCount.ToString();
positiveRatings.Text = (totalCount - negativeCount).ToString(); positiveRatings.Text = (totalCount - negativeCount).ToString();
ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount; ratingsBar.Length = totalCount == 0 ? 0 : (float)negativeCount / totalCount;
graph.Values = ratings.Take(rating_range).Select(r => (float)r); graph.Values = ratings.Take(rating_range).Select(r => (float)r);
}
} }
} }

View File

@ -39,8 +39,9 @@ namespace osu.Game.Screens.Select.Leaderboards
private readonly LoadingAnimation loading; private readonly LoadingAnimation loading;
private IEnumerable<Score> scores; private ScheduledDelegate showScoresDelegate;
private IEnumerable<Score> scores;
public IEnumerable<Score> Scores public IEnumerable<Score> Scores
{ {
get { return scores; } get { return scores; }
@ -59,29 +60,34 @@ namespace osu.Game.Screens.Select.Leaderboards
// ensure placeholder is hidden when displaying scores // ensure placeholder is hidden when displaying scores
PlaceholderState = PlaceholderState.Successful; PlaceholderState = PlaceholderState.Successful;
// schedule because we may not be loaded yet (LoadComponentAsync complains). var flow = scrollFlow = new FillFlowContainer<LeaderboardScore>
Schedule(() =>
{ {
LoadComponentAsync(new FillFlowContainer<LeaderboardScore> RelativeSizeAxes = Axes.X,
{ AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X, Spacing = new Vector2(0f, 5f),
AutoSizeAxes = Axes.Y, Padding = new MarginPadding { Top = 10, Bottom = 5 },
Spacing = new Vector2(0f, 5f), ChildrenEnumerable = scores.Select((s, index) => new LeaderboardScore(s, index + 1) { Action = () => ScoreSelected?.Invoke(s) })
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);
int i = 0; // schedule because we may not be loaded yet (LoadComponentAsync complains).
foreach (var s in f.Children) showScoresDelegate?.Cancel();
{ if (!IsLoaded)
using (s.BeginDelayedSequence(i++ * 50, true)) showScoresDelegate = Schedule(showScores);
s.Show(); 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; private PlaceholderState placeholderState;
/// <summary>
/// Update the placeholder visibility.
/// Setting this to anything other than PlaceholderState.Successful will cancel all existing retrieval requests and hide scores.
/// </summary>
protected PlaceholderState PlaceholderState protected PlaceholderState PlaceholderState
{ {
get { return placeholderState; } get { return placeholderState; }
@ -250,43 +260,45 @@ namespace osu.Game.Screens.Select.Leaderboards
loading.Show(); loading.Show();
getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope); getScoresRequest = new GetScoresRequest(Beatmap, osuGame?.Ruleset.Value ?? Beatmap.Ruleset, Scope);
getScoresRequest.Success += r => getScoresRequest.Success += r => Schedule(() =>
{ {
Scores = r.Scores; Scores = r.Scores;
PlaceholderState = Scores.Any() ? PlaceholderState.Successful : PlaceholderState.NoScores; 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); api.Queue(getScoresRequest);
} }
private void onUpdateFailed(Exception e) private Placeholder currentPlaceholder;
{
if (e is OperationCanceledException)
return;
PlaceholderState = PlaceholderState.NetworkFailure;
Logger.Error(e, @"Couldn't fetch beatmap scores!");
}
private void replacePlaceholder(Placeholder placeholder) private void replacePlaceholder(Placeholder placeholder)
{ {
var existingPlaceholder = placeholderContainer.Children.LastOrDefault() as Placeholder; if (placeholder != null && placeholder.Equals(currentPlaceholder))
if (placeholder != null && placeholder.Equals(existingPlaceholder))
return; return;
existingPlaceholder?.FadeOut(150, Easing.OutQuint).Expire(); currentPlaceholder?.FadeOut(150, Easing.OutQuint).Expire();
if (placeholder == null) if (placeholder == null)
{
currentPlaceholder = null;
return; return;
}
Scores = null; placeholderContainer.Child = placeholder;
placeholderContainer.Add(placeholder);
placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint); placeholder.ScaleTo(0.8f).Then().ScaleTo(1, fade_duration * 3, Easing.OutQuint);
placeholder.FadeInFromZero(fade_duration, Easing.OutQuint); placeholder.FadeInFromZero(fade_duration, Easing.OutQuint);
currentPlaceholder = placeholder;
} }
protected override void Update() protected override void Update()

View File

@ -1,7 +1,6 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>. // Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
using System;
using System.IO; using System.IO;
using System.Reflection; using System.Reflection;
using osu.Framework.Testing; using osu.Framework.Testing;
@ -10,28 +9,27 @@ namespace osu.Game.Tests.Visual
{ {
public abstract class OsuTestCase : TestCase public abstract class OsuTestCase : TestCase
{ {
public override void RunTest() protected override ITestCaseTestRunner CreateRunner() => new OsuTestCaseTestRunner();
{
using (var host = new CleanRunHeadlessGameHost($"test-{Guid.NewGuid()}", realtime: false))
host.Run(new OsuTestCaseTestRunner(this));
}
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; 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() protected override void LoadComplete()
{ {
base.LoadComplete(); base.LoadComplete();
Add(new TestCaseTestRunner.TestRunner(testCase));
Add(runner);
} }
public void RunTestBlocking(TestCase test) => runner.RunTestBlocking(test);
} }
} }
} }

20
osu.Game/Users/Badge.cs Normal file
View File

@ -0,0 +1,20 @@
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
// 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;
}
}

View File

@ -15,6 +15,11 @@ namespace osu.Game.Users
private User user; private User user;
/// <summary>
/// Whether to show a default guest representation on null user (as opposed to nothing).
/// </summary>
public bool ShowGuestOnNull = true;
public User User public User User
{ {
get { return user; } get { return user; }
@ -40,13 +45,16 @@ namespace osu.Game.Users
{ {
displayedAvatar?.FadeOut(300); displayedAvatar?.FadeOut(300);
displayedAvatar?.Expire(); displayedAvatar?.Expire();
Add(displayedAvatar = new DelayedLoadWrapper( if (user != null || ShowGuestOnNull)
new Avatar(user) {
{ Add(displayedAvatar = new DelayedLoadWrapper(
RelativeSizeAxes = Axes.Both, new Avatar(user)
OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint), {
}) RelativeSizeAxes = Axes.Both,
); OnLoadComplete = d => d.FadeInFromZero(300, Easing.OutQuint),
})
);
}
} }
} }
} }

View File

@ -98,9 +98,15 @@ namespace osu.Game.Users
[JsonProperty(@"skype")] [JsonProperty(@"skype")]
public string Skype; public string Skype;
[JsonProperty(@"discord")]
public string Discord;
[JsonProperty(@"website")] [JsonProperty(@"website")]
public string Website; public string Website;
[JsonProperty(@"post_count")]
public int PostCount;
[JsonProperty(@"playstyle")] [JsonProperty(@"playstyle")]
public string[] PlayStyle; public string[] PlayStyle;
@ -137,6 +143,9 @@ namespace osu.Game.Users
[JsonProperty(@"rankHistory")] [JsonProperty(@"rankHistory")]
public RankHistoryData RankHistory; public RankHistoryData RankHistory;
[JsonProperty("badges")]
public Badge[] Badges;
public override string ToString() => Username; public override string ToString() => Username;
} }
} }

View File

@ -17,7 +17,7 @@ using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterface;
using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Cursor;
using osu.Game.Graphics.Containers; using osu.Game.Graphics.Containers;
using osu.Game.Overlays.Profile; using osu.Game.Overlays.Profile.Header;
namespace osu.Game.Users namespace osu.Game.Users
{ {