Merge remote-tracking branch 'refs/remotes/ppy/master' into friends-layout-split

This commit is contained in:
Andrei Zavatski 2020-03-17 22:12:48 +03:00
commit 4d376636c3
65 changed files with 2460 additions and 1499 deletions

View File

@ -52,6 +52,6 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.314.0" />
<PackageReference Include="ppy.osu.Framework.Android" Version="2020.317.0" />
</ItemGroup>
</Project>

View File

@ -417,7 +417,7 @@ public async Task TestImportWhenFileOpen()
[Test]
public async Task TestImportWithDuplicateHashes()
{
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportNestedStructure)))
using (HeadlessGameHost host = new CleanRunHeadlessGameHost(nameof(TestImportWithDuplicateHashes)))
{
try
{

View File

@ -27,6 +27,7 @@
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play;
using osu.Game.Screens.Play.PlayerSettings;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Select;
using osu.Game.Tests.Resources;
using osu.Game.Users;
@ -203,7 +204,7 @@ public void PauseTest()
}
/// <summary>
/// Check if the visual settings container removes user dim when suspending <see cref="Player"/> for <see cref="SoloResults"/>
/// Check if the visual settings container removes user dim when suspending <see cref="Player"/> for <see cref="ResultsScreen"/>
/// </summary>
[Test]
public void TransitionTest()
@ -335,7 +336,7 @@ private void load(OsuConfigManager config)
public bool IsBackgroundCurrent() => ((FadeAccessibleBackground)Background).IsCurrentScreen();
}
private class FadeAccessibleResults : SoloResults
private class FadeAccessibleResults : ResultsScreen
{
public FadeAccessibleResults(ScoreInfo score)
: base(score)

View File

@ -11,7 +11,7 @@
using System.Collections.Generic;
using osu.Framework.Allocation;
using osu.Game.Rulesets;
using osu.Game.Screens.Ranking.Pages;
using osu.Game.Screens.Ranking;
namespace osu.Game.Tests.Visual.Gameplay
{

View File

@ -1,106 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Game.Beatmaps;
using osu.Game.Online.API;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Screens.Multi.Ranking;
using osu.Game.Screens.Multi.Ranking.Pages;
using osu.Game.Screens.Multi.Ranking.Types;
using osu.Game.Screens.Ranking;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Multiplayer
{
public class TestSceneMatchResults : MultiplayerTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(MatchResults),
typeof(RoomLeaderboardPageInfo),
typeof(RoomLeaderboardPage)
};
[Resolved]
private BeatmapManager beatmaps { get; set; }
[BackgroundDependencyLoader]
private void load()
{
var beatmapInfo = beatmaps.QueryBeatmap(b => b.RulesetID == 0);
if (beatmapInfo != null)
Beatmap.Value = beatmaps.GetWorkingBeatmap(beatmapInfo);
Room.RoomID.Value = 1;
Room.Name.Value = "an awesome room";
LoadScreen(new TestMatchResults(new ScoreInfo
{
User = new User { Id = 10 },
}));
}
private class TestMatchResults : MatchResults
{
public TestMatchResults(ScoreInfo score)
: base(score)
{
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new[] { new TestRoomLeaderboardPageInfo(Score, Beatmap.Value) };
}
private class TestRoomLeaderboardPageInfo : RoomLeaderboardPageInfo
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
public TestRoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
this.score = score;
this.beatmap = beatmap;
}
public override ResultsPage CreatePage() => new TestRoomLeaderboardPage(score, beatmap);
}
private class TestRoomLeaderboardPage : RoomLeaderboardPage
{
public TestRoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
}
protected override MatchLeaderboard CreateLeaderboard() => new TestMatchLeaderboard();
}
private class TestMatchLeaderboard : RoomLeaderboardPage.ResultsMatchLeaderboard
{
protected override APIRequest FetchScores(Action<IEnumerable<APIUserScoreAggregate>> scoresCallback)
{
var scores = Enumerable.Range(0, 50).Select(createRoomScore).ToArray();
scoresCallback?.Invoke(scores);
ScoresLoaded?.Invoke(scores);
return null;
}
private APIUserScoreAggregate createRoomScore(int id) => new APIUserScoreAggregate
{
User = new User { Id = id, Username = $"User {id}" },
Accuracy = 0.98,
TotalScore = 987654,
TotalAttempts = 13,
CompletedBeatmaps = 5
};
}
}
}

View File

@ -0,0 +1,155 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneAccuracyCircle : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(AccuracyCircle),
typeof(RankBadge),
typeof(RankNotch),
typeof(RankText),
typeof(SmoothCircularProgress)
};
[Test]
public void TestDRank()
{
var score = createScore();
score.Accuracy = 0.5;
score.Rank = ScoreRank.D;
addCircleStep(score);
}
[Test]
public void TestCRank()
{
var score = createScore();
score.Accuracy = 0.75;
score.Rank = ScoreRank.C;
addCircleStep(score);
}
[Test]
public void TestBRank()
{
var score = createScore();
score.Accuracy = 0.85;
score.Rank = ScoreRank.B;
addCircleStep(score);
}
[Test]
public void TestARank()
{
var score = createScore();
score.Accuracy = 0.925;
score.Rank = ScoreRank.A;
addCircleStep(score);
}
[Test]
public void TestSRank()
{
var score = createScore();
score.Accuracy = 0.975;
score.Rank = ScoreRank.S;
addCircleStep(score);
}
[Test]
public void TestAlmostSSRank()
{
var score = createScore();
score.Accuracy = 0.9999;
score.Rank = ScoreRank.S;
addCircleStep(score);
}
[Test]
public void TestSSRank()
{
var score = createScore();
score.Accuracy = 1;
score.Rank = ScoreRank.X;
addCircleStep(score);
}
private void addCircleStep(ScoreInfo score) => AddStep("add panel", () =>
{
Children = new Drawable[]
{
new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 700),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#555"), Color4Extensions.FromHex("#333"))
}
}
},
new AccuracyCircle(score)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(230)
}
};
});
private ScoreInfo createScore() => new ScoreInfo
{
User = new User
{
Id = 2,
Username = "peppy",
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 2845370,
Accuracy = 0.95,
MaxCombo = 999,
Rank = ScoreRank.S,
Date = DateTimeOffset.Now,
Statistics =
{
{ HitResult.Miss, 1 },
{ HitResult.Meh, 50 },
{ HitResult.Good, 100 },
{ HitResult.Great, 300 },
}
};
}
}

View File

@ -0,0 +1,80 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Screens.Ranking.Expanded.Statistics;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneExpandedPanelMiddleContent : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ExpandedPanelMiddleContent),
typeof(AccuracyCircle),
typeof(AccuracyStatistic),
typeof(ComboStatistic),
typeof(CounterStatistic),
typeof(StarRatingDisplay),
typeof(StatisticDisplay),
typeof(TotalScoreCounter)
};
public TestSceneExpandedPanelMiddleContent()
{
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 700),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#444"),
},
new ExpandedPanelMiddleContent(createTestScore())
}
};
}
private ScoreInfo createTestScore() => new ScoreInfo
{
User = new User
{
Id = 2,
Username = "peppy",
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 999999,
Accuracy = 0.95,
MaxCombo = 999,
Rank = ScoreRank.S,
Date = DateTimeOffset.Now,
Statistics =
{
{ HitResult.Miss, 1 },
{ HitResult.Meh, 50 },
{ HitResult.Good, 100 },
{ HitResult.Great, 300 },
}
};
}
}

View File

@ -0,0 +1,35 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Users;
using osuTK;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneExpandedPanelTopContent : OsuTestScene
{
public TestSceneExpandedPanelTopContent()
{
Child = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Size = new Vector2(500, 200),
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#444"),
},
new ExpandedPanelTopContent(new User { Id = 2, Username = "peppy" }),
}
};
}
}
}

View File

@ -10,29 +10,27 @@
using osu.Framework.Graphics.Containers;
using osu.Framework.Screens;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Pages;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Gameplay
namespace osu.Game.Tests.Visual.Ranking
{
[TestFixture]
public class TestSceneResults : ScreenTestScene
public class TestSceneResultsScreen : ScreenTestScene
{
private BeatmapManager beatmaps;
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(Results),
typeof(ResultsPage),
typeof(ScoreResultsPage),
typeof(ResultsScreen),
typeof(RetryButton),
typeof(ReplayDownloadButton),
typeof(LocalLeaderboardPage),
typeof(TestPlayer)
};
@ -65,6 +63,7 @@ protected override void LoadComplete()
{ HitResult.Meh, 50 },
{ HitResult.Miss, 1 }
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
User = new User
{
Username = "peppy",
@ -119,7 +118,7 @@ public TestResultsContainer(IScreen screen)
}
}
private class TestSoloResults : SoloResults
private class TestSoloResults : ResultsScreen
{
public HotkeyRetryOverlay RetryOverlay;

View File

@ -0,0 +1,143 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using NUnit.Framework;
using osu.Framework.Graphics;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.Osu;
using osu.Game.Rulesets.Osu.Mods;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Expanded;
using osu.Game.Tests.Beatmaps;
using osu.Game.Users;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneScorePanel : OsuTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(ScorePanel),
typeof(PanelState),
typeof(ExpandedPanelMiddleContent),
typeof(ExpandedPanelTopContent),
};
[Test]
public void TestDRank()
{
var score = createScore();
score.Accuracy = 0.5;
score.Rank = ScoreRank.D;
addPanelStep(score);
}
[Test]
public void TestCRank()
{
var score = createScore();
score.Accuracy = 0.75;
score.Rank = ScoreRank.C;
addPanelStep(score);
}
[Test]
public void TestBRank()
{
var score = createScore();
score.Accuracy = 0.85;
score.Rank = ScoreRank.B;
addPanelStep(score);
}
[Test]
public void TestARank()
{
var score = createScore();
score.Accuracy = 0.925;
score.Rank = ScoreRank.A;
addPanelStep(score);
}
[Test]
public void TestSRank()
{
var score = createScore();
score.Accuracy = 0.975;
score.Rank = ScoreRank.S;
addPanelStep(score);
}
[Test]
public void TestAlmostSSRank()
{
var score = createScore();
score.Accuracy = 0.9999;
score.Rank = ScoreRank.S;
addPanelStep(score);
}
[Test]
public void TestSSRank()
{
var score = createScore();
score.Accuracy = 1;
score.Rank = ScoreRank.X;
addPanelStep(score);
}
[Test]
public void TestAllHitResults()
{
var score = createScore();
score.Statistics[HitResult.Perfect] = 350;
score.Statistics[HitResult.Ok] = 200;
addPanelStep(score);
}
private void addPanelStep(ScoreInfo score) => AddStep("add panel", () =>
{
Child = new ScorePanel(score)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
State = PanelState.Expanded
};
});
private ScoreInfo createScore() => new ScoreInfo
{
User = new User
{
Id = 2,
Username = "peppy",
},
Beatmap = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo,
Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() },
TotalScore = 2845370,
Accuracy = 0.95,
MaxCombo = 999,
Rank = ScoreRank.S,
Date = DateTimeOffset.Now,
Statistics =
{
{ HitResult.Miss, 1 },
{ HitResult.Meh, 50 },
{ HitResult.Good, 100 },
{ HitResult.Great, 300 },
}
};
}
}

View File

@ -0,0 +1,32 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Beatmaps;
using osu.Game.Screens.Ranking.Expanded;
namespace osu.Game.Tests.Visual.Ranking
{
public class TestSceneStarRatingDisplay : OsuTestScene
{
public TestSceneStarRatingDisplay()
{
Child = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 1.23 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 2.34 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 3.45 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 4.56 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 5.67 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 6.78 }),
new StarRatingDisplay(new BeatmapInfo { StarDifficulty = 10.11 }),
}
};
}
}
}

View File

@ -0,0 +1,40 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Graphics;
using osu.Game.Tournament.Components;
using osu.Game.Tournament.Models;
namespace osu.Game.Tournament.Tests.Components
{
public class TestSceneRoundDisplay : TournamentTestScene
{
public override IReadOnlyList<Type> RequiredTypes => new[]
{
typeof(DrawableTournamentHeaderText),
typeof(DrawableTournamentHeaderLogo),
};
public TestSceneRoundDisplay()
{
Children = new Drawable[]
{
new RoundDisplay(new TournamentMatch
{
Round =
{
Value = new TournamentRound
{
Name = { Value = "Test Round" }
}
}
})
{
Margin = new MarginPadding(20)
}
};
}
}
}

View File

@ -11,9 +11,13 @@ namespace osu.Game.Tournament.Components
{
public class DrawableTournamentHeaderText : CompositeDrawable
{
public DrawableTournamentHeaderText()
public DrawableTournamentHeaderText(bool center = true)
{
InternalChild = new TextSprite();
InternalChild = new TextSprite
{
Anchor = center ? Anchor.Centre : Anchor.TopLeft,
Origin = center ? Anchor.Centre : Anchor.TopLeft,
};
Height = 22;
RelativeSizeAxes = Axes.X;
@ -27,9 +31,6 @@ private void load(TextureStore textures)
RelativeSizeAxes = Axes.Both;
FillMode = FillMode.Fit;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Texture = textures.Get("header-text");
}
}

View File

@ -12,19 +12,27 @@ public class RoundDisplay : CompositeDrawable
{
public RoundDisplay(TournamentMatch match)
{
AutoSizeAxes = Axes.Both;
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
InternalChildren = new Drawable[]
{
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
AutoSizeAxes = Axes.Y,
RelativeSizeAxes = Axes.X,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new DrawableTournamentHeaderText(),
new DrawableTournamentHeaderText(false)
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
},
new TournamentSpriteText
{
Anchor = Anchor.TopLeft,
Origin = Anchor.TopLeft,
Text = match.Round.Value?.Name.Value ?? "Unknown Round",
Font = OsuFont.Torus.With(size: 26, weight: FontWeight.SemiBold)
},

View File

@ -35,7 +35,9 @@ public TeamScoreDisplay(TeamColour teamColour)
private void load(LadderInfo ladder)
{
currentMatch.BindTo(ladder.CurrentMatch);
currentMatch.BindValueChanged(matchChanged, true);
currentMatch.BindValueChanged(matchChanged);
updateMatch();
}
private void matchChanged(ValueChangedEvent<TournamentMatch> match)
@ -43,10 +45,19 @@ private void matchChanged(ValueChangedEvent<TournamentMatch> match)
currentTeamScore.UnbindBindings();
currentTeam.UnbindBindings();
if (match.NewValue != null)
Scheduler.AddOnce(updateMatch);
}
private void updateMatch()
{
var match = currentMatch.Value;
if (match != null)
{
currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1Score : match.NewValue.Team2Score);
currentTeam.BindTo(teamColour == TeamColour.Red ? match.NewValue.Team1 : match.NewValue.Team2);
match.StartMatch();
currentTeamScore.BindTo(teamColour == TeamColour.Red ? match.Team1Score : match.Team2Score);
currentTeam.BindTo(teamColour == TeamColour.Red ? match.Team1 : match.Team2);
}
// team may change to same team, which means score is not in a good state.

View File

@ -1,6 +1,7 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using osu.Framework.Audio.Track;
using osu.Framework.Graphics.Textures;
@ -60,8 +61,9 @@ public interface IWorkingBeatmap
/// </summary>
/// <param name="ruleset">The <see cref="RulesetInfo"/> to create a playable <see cref="IBeatmap"/> for.</param>
/// <param name="mods">The <see cref="Mod"/>s to apply to the <see cref="IBeatmap"/>.</param>
/// <param name="timeout">The maximum length in milliseconds to wait for load to complete. Defaults to 10,000ms.</param>
/// <returns>The converted <see cref="IBeatmap"/>.</returns>
/// <exception cref="BeatmapInvalidForRulesetException">If <see cref="Beatmap"/> could not be converted to <paramref name="ruleset"/>.</exception>
IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null);
IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null);
}
}

View File

@ -83,55 +83,81 @@ protected virtual Track GetVirtualTrack(double emptyLength = 0)
/// <returns>The applicable <see cref="IBeatmapConverter"/>.</returns>
protected virtual IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) => ruleset.CreateBeatmapConverter(beatmap);
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null)
public IBeatmap GetPlayableBeatmap(RulesetInfo ruleset, IReadOnlyList<Mod> mods = null, TimeSpan? timeout = null)
{
mods ??= Array.Empty<Mod>();
var rulesetInstance = ruleset.CreateInstance();
IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
// Check if the beatmap can be converted
if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert())
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
// Apply conversion mods
foreach (var mod in mods.OfType<IApplicableToBeatmapConverter>())
mod.ApplyToBeatmapConverter(converter);
// Convert
IBeatmap converted = converter.Convert();
// Apply difficulty mods
if (mods.Any(m => m is IApplicableToDifficulty))
using (var cancellationSource = new CancellationTokenSource(timeout ?? TimeSpan.FromSeconds(10)))
{
converted.BeatmapInfo = converted.BeatmapInfo.Clone();
converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone();
mods ??= Array.Empty<Mod>();
foreach (var mod in mods.OfType<IApplicableToDifficulty>())
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
}
var rulesetInstance = ruleset.CreateInstance();
IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted);
IBeatmapConverter converter = CreateBeatmapConverter(Beatmap, rulesetInstance);
processor?.PreProcess();
// Check if the beatmap can be converted
if (Beatmap.HitObjects.Count > 0 && !converter.CanConvert())
throw new BeatmapInvalidForRulesetException($"{nameof(Beatmaps.Beatmap)} can not be converted for the ruleset (ruleset: {ruleset.InstantiationInfo}, converter: {converter}).");
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
foreach (var obj in converted.HitObjects)
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
// Apply conversion mods
foreach (var mod in mods.OfType<IApplicableToBeatmapConverter>())
{
if (cancellationSource.IsCancellationRequested)
throw new BeatmapLoadTimeoutException(BeatmapInfo);
foreach (var mod in mods.OfType<IApplicableToHitObject>())
{
mod.ApplyToBeatmapConverter(converter);
}
// Convert
IBeatmap converted = converter.Convert();
// Apply difficulty mods
if (mods.Any(m => m is IApplicableToDifficulty))
{
converted.BeatmapInfo = converted.BeatmapInfo.Clone();
converted.BeatmapInfo.BaseDifficulty = converted.BeatmapInfo.BaseDifficulty.Clone();
foreach (var mod in mods.OfType<IApplicableToDifficulty>())
{
if (cancellationSource.IsCancellationRequested)
throw new BeatmapLoadTimeoutException(BeatmapInfo);
mod.ApplyToDifficulty(converted.BeatmapInfo.BaseDifficulty);
}
}
IBeatmapProcessor processor = rulesetInstance.CreateBeatmapProcessor(converted);
processor?.PreProcess();
// Compute default values for hitobjects, including creating nested hitobjects in-case they're needed
foreach (var obj in converted.HitObjects)
mod.ApplyToHitObject(obj);
{
if (cancellationSource.IsCancellationRequested)
throw new BeatmapLoadTimeoutException(BeatmapInfo);
obj.ApplyDefaults(converted.ControlPointInfo, converted.BeatmapInfo.BaseDifficulty);
}
foreach (var mod in mods.OfType<IApplicableToHitObject>())
{
foreach (var obj in converted.HitObjects)
{
if (cancellationSource.IsCancellationRequested)
throw new BeatmapLoadTimeoutException(BeatmapInfo);
mod.ApplyToHitObject(obj);
}
}
processor?.PostProcess();
foreach (var mod in mods.OfType<IApplicableToBeatmap>())
{
cancellationSource.Token.ThrowIfCancellationRequested();
mod.ApplyToBeatmap(converted);
}
return converted;
}
processor?.PostProcess();
foreach (var mod in mods.OfType<IApplicableToBeatmap>())
mod.ApplyToBeatmap(converted);
return converted;
}
private CancellationTokenSource loadCancellation = new CancellationTokenSource();
@ -297,5 +323,13 @@ public T Value
private void recreate() => lazy = new Lazy<T>(valueFactory, LazyThreadSafetyMode.ExecutionAndPublication);
}
private class BeatmapLoadTimeoutException : TimeoutException
{
public BeatmapLoadTimeoutException(BeatmapInfo beatmapInfo)
: base($"Timed out while loading beatmap ({beatmapInfo}).")
{
}
}
}
}

View File

@ -3,6 +3,7 @@
using osu.Framework.Extensions.Color4Extensions;
using osu.Game.Beatmaps;
using osu.Game.Scoring;
using osuTK.Graphics;
namespace osu.Game.Graphics
@ -37,6 +38,35 @@ public Color4 ForDifficultyRating(DifficultyRating difficulty, bool useLighterCo
}
}
/// <summary>
/// Retrieves the colour for a <see cref="ScoreRank"/>.
/// </summary>
public static Color4 ForRank(ScoreRank rank)
{
switch (rank)
{
case ScoreRank.XH:
case ScoreRank.X:
return Color4Extensions.FromHex(@"ce1c9d");
case ScoreRank.SH:
case ScoreRank.S:
return Color4Extensions.FromHex(@"00a8b5");
case ScoreRank.A:
return Color4Extensions.FromHex(@"7cce14");
case ScoreRank.B:
return Color4Extensions.FromHex(@"e3b130");
case ScoreRank.C:
return Color4Extensions.FromHex(@"f18252");
default:
return Color4Extensions.FromHex(@"e95353");
}
}
// See https://github.com/ppy/osu-web/blob/master/resources/assets/less/colors.less
public readonly Color4 PurpleLighter = Color4Extensions.FromHex(@"eeeeff");
public readonly Color4 PurpleLight = Color4Extensions.FromHex(@"aa88ff");

View File

@ -43,6 +43,18 @@ public ColourInfo GlowColour
set => blurredText.Colour = value;
}
public Vector2 Spacing
{
get => spriteText.Spacing;
set => spriteText.Spacing = blurredText.Spacing = value;
}
public bool UseFullGlyphHeight
{
get => spriteText.UseFullGlyphHeight;
set => spriteText.UseFullGlyphHeight = blurredText.UseFullGlyphHeight = value;
}
public GlowingSpriteText()
{
AutoSizeAxes = Axes.Both;

View File

@ -28,7 +28,7 @@ public DrawableRank(ScoreRank rank)
FillMode = FillMode.Fit;
FillAspectRatio = 2;
var rankColour = getRankColour();
var rankColour = OsuColour.ForRank(rank);
InternalChild = new DrawSizePreservingFillContainer
{
TargetDrawSize = new Vector2(64, 32),
@ -59,7 +59,7 @@ public DrawableRank(ScoreRank rank)
Padding = new MarginPadding { Top = 5 },
Colour = getRankNameColour(),
Font = OsuFont.Numeric.With(size: 25),
Text = getRankName(),
Text = GetRankName(rank),
ShadowColour = Color4.Black.Opacity(0.3f),
ShadowOffset = new Vector2(0, 0.08f),
Shadow = true,
@ -69,36 +69,7 @@ public DrawableRank(ScoreRank rank)
};
}
private string getRankName() => rank.GetDescription().TrimEnd('+');
/// <summary>
/// Retrieves the grade background colour.
/// </summary>
private Color4 getRankColour()
{
switch (rank)
{
case ScoreRank.XH:
case ScoreRank.X:
return Color4Extensions.FromHex(@"ce1c9d");
case ScoreRank.SH:
case ScoreRank.S:
return Color4Extensions.FromHex(@"00a8b5");
case ScoreRank.A:
return Color4Extensions.FromHex(@"7cce14");
case ScoreRank.B:
return Color4Extensions.FromHex(@"e3b130");
case ScoreRank.C:
return Color4Extensions.FromHex(@"f18252");
default:
return Color4Extensions.FromHex(@"e95353");
}
}
public static string GetRankName(ScoreRank rank) => rank.GetDescription().TrimEnd('+');
/// <summary>
/// Retrieves the grade text colour.

View File

@ -148,7 +148,7 @@ public DateContainer(DateTime date)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.GetFont(Typeface.Torus, 12, FontWeight.Black, false, false),
Font = OsuFont.GetFont(Typeface.Torus, 12, FontWeight.Bold, false, false),
Text = date.ToString("d MMM yyy").ToUpper(),
Margin = new MarginPadding
{

View File

@ -84,13 +84,13 @@ private void load(OsuColour colours)
new OsuSpriteText
{
Text = titleText.ToUpperInvariant(),
Font = OsuFont.GetFont(weight: FontWeight.Black)
Font = OsuFont.GetFont(weight: FontWeight.Bold)
},
countDrawable = new OsuSpriteText
{
Text = "3",
Colour = colours.Yellow,
Font = OsuFont.GetFont(weight: FontWeight.Black)
Font = OsuFont.GetFont(weight: FontWeight.Bold)
},
}
},

View File

@ -53,7 +53,7 @@ protected Toast(string description, string value, string shortcut)
{
Padding = new MarginPadding(10),
Name = "Description",
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Black),
Font = OsuFont.GetFont(size: 14, weight: FontWeight.Bold),
Spacing = new Vector2(1, 0),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,

View File

@ -59,7 +59,7 @@ private void load(OverlayColourProvider colourProvider, OsuColour colours)
new OsuSpriteText
{
Text = MainText,
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black),
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold),
},
new OsuSpriteText
{

View File

@ -80,7 +80,7 @@ public void APIStateChanged(IAPIProvider api, APIState state) => Schedule(() =>
{
Text = "ACCOUNT",
Margin = new MarginPadding { Bottom = 5 },
Font = OsuFont.GetFont(weight: FontWeight.Black),
Font = OsuFont.GetFont(weight: FontWeight.Bold),
},
form = new LoginForm
{

View File

@ -54,7 +54,7 @@ private void load()
{
Text = Header.ToUpperInvariant(),
Margin = new MarginPadding { Bottom = 10, Left = SettingsPanel.CONTENT_MARGINS, Right = SettingsPanel.CONTENT_MARGINS },
Font = OsuFont.GetFont(weight: FontWeight.Black),
Font = OsuFont.GetFont(weight: FontWeight.Bold),
},
FlowContent
});

View File

@ -23,6 +23,7 @@
using System.Collections.Generic;
using osu.Framework;
using osu.Framework.Input.Bindings;
using osu.Framework.Logging;
using osu.Game.Beatmaps;
using osu.Game.Graphics.Cursor;
using osu.Game.Input.Bindings;
@ -86,7 +87,18 @@ private void load(OsuColour colours, GameHost host)
// todo: remove caching of this and consume via editorBeatmap?
dependencies.Cache(beatDivisor);
playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
try
{
playableBeatmap = Beatmap.Value.GetPlayableBeatmap(Beatmap.Value.BeatmapInfo.Ruleset);
}
catch (Exception e)
{
Logger.Error(e, "Could not load beatmap successfully!");
// couldn't load, hard abort!
this.Exit();
return;
}
AddInternal(editorBeatmap = new EditorBeatmap(playableBeatmap));
dependencies.CacheAs(editorBeatmap);

View File

@ -152,7 +152,7 @@ private class HeaderText : OsuSpriteText
public HeaderText(string text)
{
Text = text.ToUpper();
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Black);
Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold);
}
}

View File

@ -14,9 +14,7 @@
using osu.Game.Online.Multiplayer;
using osu.Game.Rulesets;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Ranking;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.Multi.Play
{
@ -115,7 +113,5 @@ protected override void Dispose(bool isDisposing)
Exited = null;
}
protected override Results CreateResults(ScoreInfo score) => new MatchResults(score);
}
}

View File

@ -1,26 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Ranking.Types;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Types;
namespace osu.Game.Screens.Multi.Ranking
{
public class MatchResults : Results
{
public MatchResults(ScoreInfo score)
: base(score)
{
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new IResultPageInfo[]
{
new ScoreOverviewPageInfo(Score, Beatmap.Value),
new LocalLeaderboardPageInfo(Score, Beatmap.Value),
new RoomLeaderboardPageInfo(Score, Beatmap.Value),
};
}
}

View File

@ -1,135 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using Microsoft.EntityFrameworkCore.Internal;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Lists;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Online.API.Requests.Responses;
using osu.Game.Online.Leaderboards;
using osu.Game.Online.Multiplayer;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Match.Components;
using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.Multi.Ranking.Pages
{
public class RoomLeaderboardPage : ResultsPage
{
[Resolved]
private OsuColour colours { get; set; }
private TextFlowContainer rankText;
[Resolved(typeof(Room), nameof(Room.Name))]
private Bindable<string> name { get; set; }
public RoomLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
}
[BackgroundDependencyLoader]
private void load()
{
MatchLeaderboard leaderboard;
Children = new Drawable[]
{
new Box
{
Colour = colours.Gray6,
RelativeSizeAxes = Axes.Both,
},
new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
BackgroundColour = colours.Gray6,
Child = leaderboard = CreateLeaderboard()
},
rankText = new TextFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Width = 0.5f,
AutoSizeAxes = Axes.Y,
Y = 50,
TextAnchor = Anchor.TopCentre
},
};
leaderboard.Origin = Anchor.Centre;
leaderboard.Anchor = Anchor.Centre;
leaderboard.RelativeSizeAxes = Axes.Both;
leaderboard.Height = 0.8f;
leaderboard.Y = 55;
leaderboard.ScoresLoaded = scoresLoaded;
}
private void scoresLoaded(IEnumerable<APIUserScoreAggregate> scores)
{
void gray(SpriteText s) => s.Colour = colours.GrayC;
void white(SpriteText s)
{
s.Font = s.Font.With(size: s.Font.Size * 1.4f);
s.Colour = colours.GrayF;
}
rankText.AddText(name + "\n", white);
rankText.AddText("You are placed ", gray);
int index = scores.IndexOf(new APIUserScoreAggregate { User = Score.User }, new FuncEqualityComparer<APIUserScoreAggregate>((s1, s2) => s1.User.Id.Equals(s2.User.Id)));
rankText.AddText($"#{index + 1} ", s =>
{
s.Font = s.Font.With(Typeface.Torus, weight: FontWeight.Bold);
s.Colour = colours.YellowDark;
});
rankText.AddText("in the room!", gray);
}
protected virtual MatchLeaderboard CreateLeaderboard() => new ResultsMatchLeaderboard();
public class ResultsMatchLeaderboard : MatchLeaderboard
{
protected override bool FadeTop => true;
protected override LeaderboardScore CreateDrawableScore(APIUserScoreAggregate model, int index)
=> new ResultsMatchLeaderboardScore(model, index);
protected override FillFlowContainer<LeaderboardScore> CreateScoreFlow()
{
var flow = base.CreateScoreFlow();
flow.Padding = new MarginPadding
{
Top = LeaderboardScore.HEIGHT * 2,
Bottom = LeaderboardScore.HEIGHT * 3,
};
return flow;
}
private class ResultsMatchLeaderboardScore : MatchLeaderboardScore
{
public ResultsMatchLeaderboardScore(APIUserScoreAggregate score, int rank)
: base(score, rank)
{
}
[BackgroundDependencyLoader]
private void load()
{
}
}
}
}
}

View File

@ -1,29 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Scoring;
using osu.Game.Screens.Multi.Ranking.Pages;
using osu.Game.Screens.Ranking;
namespace osu.Game.Screens.Multi.Ranking.Types
{
public class RoomLeaderboardPageInfo : IResultPageInfo
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
public RoomLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
{
this.score = score;
this.beatmap = beatmap;
}
public IconUsage Icon => FontAwesome.Solid.Users;
public string Name => "Room Leaderboard";
public virtual ResultsPage CreatePage() => new RoomLeaderboardPage(score, beatmap);
}
}

View File

@ -30,7 +30,7 @@ public BreakInfo()
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = "current progress".ToUpperInvariant(),
Font = OsuFont.GetFont(weight: FontWeight.Black, size: 15),
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 15),
},
new FillFlowContainer
{

View File

@ -401,14 +401,18 @@ private void onCompletion()
protected virtual ScoreInfo CreateScore()
{
var score = DrawableRuleset.ReplayScore?.ScoreInfo ?? new ScoreInfo
var score = new ScoreInfo
{
Beatmap = Beatmap.Value.BeatmapInfo,
Ruleset = rulesetInfo,
Mods = Mods.Value.ToArray(),
User = api.LocalUser.Value,
};
if (DrawableRuleset.ReplayScore != null)
score.User = DrawableRuleset.ReplayScore.ScoreInfo?.User ?? new GuestUser();
else
score.User = api.LocalUser.Value;
ScoreProcessor.PopulateScore(score);
return score;
@ -416,7 +420,7 @@ protected virtual ScoreInfo CreateScore()
protected override bool OnScroll(ScrollEvent e) => mouseWheelDisabled.Value && !GameplayClockContainer.IsPaused.Value;
protected virtual Results CreateResults(ScoreInfo score) => new SoloResults(score);
protected virtual ResultsScreen CreateResults(ScoreInfo score) => new ResultsScreen(score);
#region Fail Logic

View File

@ -1,24 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking;
using osu.Game.Screens.Ranking.Types;
namespace osu.Game.Screens.Play
{
public class SoloResults : Results
{
public SoloResults(ScoreInfo score)
: base(score)
{
}
protected override IEnumerable<IResultPageInfo> CreateResultPages() => new IResultPageInfo[]
{
new ScoreOverviewPageInfo(Score, Beatmap.Value),
new LocalLeaderboardPageInfo(Score, Beatmap.Value)
};
}
}

View File

@ -0,0 +1,253 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Utils;
using osu.Game.Graphics;
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Accuracy
{
/// <summary>
/// The component that displays the player's accuracy on the results screen.
/// </summary>
public class AccuracyCircle : CompositeDrawable
{
/// <summary>
/// Duration for the transforms causing this component to appear.
/// </summary>
public const double APPEAR_DURATION = 200;
/// <summary>
/// Delay before the accuracy circle starts filling.
/// </summary>
public const double ACCURACY_TRANSFORM_DELAY = 450;
/// <summary>
/// Duration for the accuracy circle fill.
/// </summary>
public const double ACCURACY_TRANSFORM_DURATION = 3000;
/// <summary>
/// Delay after <see cref="ACCURACY_TRANSFORM_DURATION"/> for the rank text (A/B/C/D/S/SS) to appear.
/// </summary>
public const double TEXT_APPEAR_DELAY = ACCURACY_TRANSFORM_DURATION / 2;
/// <summary>
/// Delay before the rank circles start filling.
/// </summary>
public const double RANK_CIRCLE_TRANSFORM_DELAY = 150;
/// <summary>
/// Duration for the rank circle fills.
/// </summary>
public const double RANK_CIRCLE_TRANSFORM_DURATION = 800;
/// <summary>
/// Relative width of the rank circles.
/// </summary>
public const float RANK_CIRCLE_RADIUS = 0.06f;
/// <summary>
/// Relative width of the circle showing the accuracy.
/// </summary>
private const float accuracy_circle_radius = 0.2f;
/// <summary>
/// SS is displayed as a 1% region, otherwise it would be invisible.
/// </summary>
private const double virtual_ss_percentage = 0.01;
/// <summary>
/// The easing for the circle filling transforms.
/// </summary>
public static readonly Easing ACCURACY_TRANSFORM_EASING = Easing.OutPow10;
private readonly ScoreInfo score;
private SmoothCircularProgress accuracyCircle;
private SmoothCircularProgress innerMask;
private Container<RankBadge> badges;
private RankText rankText;
public AccuracyCircle(ScoreInfo score)
{
this.score = score;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
{
new SmoothCircularProgress
{
Name = "Background circle",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Colour = OsuColour.Gray(47),
Alpha = 0.5f,
InnerRadius = accuracy_circle_radius + 0.01f, // Extends a little bit into the circle
Current = { Value = 1 },
},
accuracyCircle = new SmoothCircularProgress
{
Name = "Accuracy circle",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#7CF6FF"), Color4Extensions.FromHex("#BAFFA9")),
InnerRadius = accuracy_circle_radius,
},
new BufferedContainer
{
Name = "Graded circles",
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.8f),
Padding = new MarginPadding(2),
Children = new Drawable[]
{
new SmoothCircularProgress
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#BE0089"),
InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 1 }
},
new SmoothCircularProgress
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#0096A2"),
InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 1 - virtual_ss_percentage }
},
new SmoothCircularProgress
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#72C904"),
InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 0.95f }
},
new SmoothCircularProgress
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#D99D03"),
InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 0.9f }
},
new SmoothCircularProgress
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#EA7948"),
InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 0.8f }
},
new SmoothCircularProgress
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#FF5858"),
InnerRadius = RANK_CIRCLE_RADIUS,
Current = { Value = 0.7f }
},
new RankNotch(0),
new RankNotch((float)(1 - virtual_ss_percentage)),
new RankNotch(0.95f),
new RankNotch(0.9f),
new RankNotch(0.8f),
new RankNotch(0.7f),
new BufferedContainer
{
Name = "Graded circle mask",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(1),
Blending = new BlendingParameters
{
Source = BlendingType.DstColor,
Destination = BlendingType.OneMinusSrcAlpha,
SourceAlpha = BlendingType.One,
DestinationAlpha = BlendingType.SrcAlpha
},
Child = innerMask = new SmoothCircularProgress
{
RelativeSizeAxes = Axes.Both,
InnerRadius = RANK_CIRCLE_RADIUS - 0.01f,
}
}
}
},
badges = new Container<RankBadge>
{
Name = "Rank badges",
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Vertical = -15, Horizontal = -20 },
Children = new[]
{
new RankBadge(1f, ScoreRank.X),
new RankBadge(0.95f, ScoreRank.S),
new RankBadge(0.9f, ScoreRank.A),
new RankBadge(0.8f, ScoreRank.B),
new RankBadge(0.7f, ScoreRank.C),
}
},
rankText = new RankText(score.Rank)
};
}
protected override void LoadComplete()
{
base.LoadComplete();
this.ScaleTo(0).Then().ScaleTo(1, APPEAR_DURATION, Easing.OutQuint);
using (BeginDelayedSequence(RANK_CIRCLE_TRANSFORM_DELAY, true))
innerMask.FillTo(1f, RANK_CIRCLE_TRANSFORM_DURATION, ACCURACY_TRANSFORM_EASING);
using (BeginDelayedSequence(ACCURACY_TRANSFORM_DELAY, true))
{
double targetAccuracy = score.Rank == ScoreRank.X || score.Rank == ScoreRank.XH ? 1 : Math.Min(1 - virtual_ss_percentage, score.Accuracy);
accuracyCircle.FillTo(targetAccuracy, ACCURACY_TRANSFORM_DURATION, ACCURACY_TRANSFORM_EASING);
foreach (var badge in badges)
{
if (badge.Accuracy > score.Accuracy)
continue;
using (BeginDelayedSequence(inverseEasing(ACCURACY_TRANSFORM_EASING, Math.Min(1 - virtual_ss_percentage, badge.Accuracy) / targetAccuracy) * ACCURACY_TRANSFORM_DURATION, true))
badge.Appear();
}
using (BeginDelayedSequence(TEXT_APPEAR_DELAY, true))
rankText.Appear();
}
}
private double inverseEasing(Easing easing, double targetValue)
{
double test = 0;
double result = 0;
int count = 2;
while (Math.Abs(result - targetValue) > 0.005)
{
int dir = Math.Sign(targetValue - result);
test += dir * 1.0 / count;
result = Interpolation.ApplyEasing(easing, test);
count++;
}
return test;
}
}
}

View File

@ -0,0 +1,99 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Online.Leaderboards;
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Accuracy
{
/// <summary>
/// Contains a <see cref="DrawableRank"/> that is positioned around the <see cref="AccuracyCircle"/>.
/// </summary>
public class RankBadge : CompositeDrawable
{
/// <summary>
/// The accuracy value corresponding to the <see cref="ScoreRank"/> displayed by this badge.
/// </summary>
public readonly float Accuracy;
private readonly ScoreRank rank;
private Drawable rankContainer;
private Drawable overlay;
/// <summary>
/// Creates a new <see cref="RankBadge"/>.
/// </summary>
/// <param name="accuracy">The accuracy value corresponding to <paramref name="rank"/>.</param>
/// <param name="rank">The <see cref="ScoreRank"/> to be displayed in this <see cref="RankBadge"/>.</param>
public RankBadge(float accuracy, ScoreRank rank)
{
Accuracy = accuracy;
this.rank = rank;
RelativeSizeAxes = Axes.Both;
Alpha = 0;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = rankContainer = new Container
{
Origin = Anchor.Centre,
Size = new Vector2(28, 14),
Children = new[]
{
new DrawableRank(rank),
overlay = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Blending = BlendingParameters.Additive,
Masking = true,
EdgeEffect = new EdgeEffectParameters
{
Type = EdgeEffectType.Glow,
Colour = OsuColour.ForRank(rank).Opacity(0.2f),
Radius = 10,
},
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true,
}
}
}
};
}
/// <summary>
/// Shows this <see cref="RankBadge"/>.
/// </summary>
public void Appear()
{
this.FadeIn(50);
overlay.FadeIn().FadeOut(500, Easing.In);
}
protected override void Update()
{
base.Update();
// Starts at -90deg (top) and moves counter-clockwise by the accuracy
rankContainer.Position = circlePosition(-MathF.PI / 2 - (1 - Accuracy) * MathF.PI * 2);
}
private Vector2 circlePosition(float t)
=> DrawSize / 2 + new Vector2(MathF.Cos(t), MathF.Sin(t)) * DrawSize / 2;
}
}

View File

@ -0,0 +1,49 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Accuracy
{
/// <summary>
/// A solid "notch" of the <see cref="AccuracyCircle"/> that appears at the ends of the rank circles to add separation.
/// </summary>
public class RankNotch : CompositeDrawable
{
private readonly float position;
public RankNotch(float position)
{
this.position = position;
RelativeSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Rotation = position * 360f,
Child = new Box
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
Height = AccuracyCircle.RANK_CIRCLE_RADIUS,
Width = 1f,
Colour = OsuColour.Gray(0.3f),
EdgeSmoothness = new Vector2(1f)
}
};
}
}
}

View File

@ -0,0 +1,137 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Online.Leaderboards;
using osu.Game.Scoring;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking.Expanded.Accuracy
{
/// <summary>
/// The text that appears in the middle of the <see cref="AccuracyCircle"/> displaying the user's rank.
/// </summary>
public class RankText : CompositeDrawable
{
private readonly ScoreRank rank;
private BufferedContainer flash;
private BufferedContainer superFlash;
private GlowingSpriteText rankText;
public RankText(ScoreRank rank)
{
this.rank = rank;
Anchor = Anchor.Centre;
Origin = Anchor.Centre;
Alpha = 0;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
{
rankText = new GlowingSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
GlowColour = OsuColour.ForRank(rank),
Spacing = new Vector2(-15, 0),
Text = DrawableRank.GetRankName(rank),
Font = OsuFont.Numeric.With(size: 76),
UseFullGlyphHeight = false
},
superFlash = new BufferedContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlurSigma = new Vector2(85),
Size = new Vector2(600),
CacheDrawnFrameBuffer = true,
Blending = BlendingParameters.Additive,
Alpha = 0,
Children = new[]
{
new Box
{
Colour = Color4.White,
Size = new Vector2(150),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
},
},
flash = new BufferedContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
BlurSigma = new Vector2(35),
BypassAutoSizeAxes = Axes.Both,
RelativeSizeAxes = Axes.Both,
CacheDrawnFrameBuffer = true,
Blending = BlendingParameters.Additive,
Alpha = 0,
Size = new Vector2(2f), // increase buffer size to allow for scale
Scale = new Vector2(1.8f),
Children = new[]
{
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Spacing = new Vector2(-15, 0),
Text = DrawableRank.GetRankName(rank),
Font = OsuFont.Numeric.With(size: 76),
UseFullGlyphHeight = false,
Shadow = false
},
},
},
};
}
public void Appear()
{
this.FadeIn();
if (rank < ScoreRank.A)
{
this
.MoveToOffset(new Vector2(0, -20))
.MoveToOffset(new Vector2(0, 20), 200, Easing.OutBounce);
if (rank <= ScoreRank.D)
{
this.Delay(700)
.RotateTo(5, 150, Easing.In)
.MoveToOffset(new Vector2(0, 3), 150, Easing.In);
}
this.FadeInFromZero(200, Easing.OutQuint);
return;
}
flash.Colour = OsuColour.ForRank(rank);
flash.FadeIn().Then().FadeOut(1200, Easing.OutQuint);
if (rank >= ScoreRank.S)
rankText.ScaleTo(1.05f).ScaleTo(1, 3000, Easing.OutQuint);
if (rank >= ScoreRank.X)
{
flash.FadeIn().Then().FadeOut(3000);
superFlash.FadeIn().Then().FadeOut(800, Easing.OutQuint);
}
}
}
}

View File

@ -0,0 +1,126 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Transforms;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Accuracy
{
/// <summary>
/// Contains a <see cref="CircularProgress"/> with smoothened edges.
/// </summary>
public class SmoothCircularProgress : CompositeDrawable
{
public Bindable<double> Current
{
get => progress.Current;
set => progress.Current = value;
}
public float InnerRadius
{
get => progress.InnerRadius;
set
{
progress.InnerRadius = value;
innerSmoothingContainer.Size = new Vector2(1 - value);
smoothingWedge.Height = value / 2;
}
}
private readonly CircularProgress progress;
private readonly Container innerSmoothingContainer;
private readonly Drawable smoothingWedge;
public SmoothCircularProgress()
{
Container smoothingWedgeContainer;
InternalChild = new BufferedContainer
{
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
progress = new CircularProgress { RelativeSizeAxes = Axes.Both },
smoothingWedgeContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Child = smoothingWedge = new Box
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.Y,
Width = 1f,
EdgeSmoothness = new Vector2(2, 0),
}
},
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding(-1),
Child = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
BorderThickness = 2,
Masking = true,
BorderColour = OsuColour.Gray(0.5f).Opacity(0.75f),
Blending = new BlendingParameters
{
AlphaEquation = BlendingEquation.ReverseSubtract,
},
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
innerSmoothingContainer = new Container
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = Vector2.Zero,
Padding = new MarginPadding(-1),
Child = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
BorderThickness = 2,
BorderColour = OsuColour.Gray(0.5f).Opacity(0.75f),
Masking = true,
Blending = new BlendingParameters
{
AlphaEquation = BlendingEquation.ReverseSubtract,
},
Child = new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
}
}
},
}
};
Current.BindValueChanged(c =>
{
smoothingWedgeContainer.Alpha = c.NewValue > 0 ? 1 : 0;
smoothingWedgeContainer.Rotation = (float)(360 * c.NewValue);
}, true);
}
public TransformSequence<CircularProgress> FillTo(double newValue, double duration = 0, Easing easing = Easing.None)
=> progress.FillTo(newValue, duration, easing);
}
}

View File

@ -0,0 +1,231 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Bindables;
using osu.Framework.Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Scoring;
using osu.Game.Screens.Play.HUD;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Screens.Ranking.Expanded.Statistics;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded
{
/// <summary>
/// The content that appears in the middle section of the <see cref="ScorePanel"/>.
/// </summary>
public class ExpandedPanelMiddleContent : CompositeDrawable
{
private readonly ScoreInfo score;
private readonly List<StatisticDisplay> statisticDisplays = new List<StatisticDisplay>();
private RollingCounter<long> scoreCounter;
/// <summary>
/// Creates a new <see cref="ExpandedPanelMiddleContent"/>.
/// </summary>
/// <param name="score">The score to display.</param>
public ExpandedPanelMiddleContent(ScoreInfo score)
{
this.score = score;
RelativeSizeAxes = Axes.Both;
Masking = true;
Padding = new MarginPadding { Vertical = 10, Horizontal = 10 };
}
[BackgroundDependencyLoader]
private void load(Bindable<WorkingBeatmap> working)
{
var beatmap = working.Value.BeatmapInfo;
var metadata = beatmap.Metadata;
var topStatistics = new List<StatisticDisplay>
{
new AccuracyStatistic(score.Accuracy),
new ComboStatistic(score.MaxCombo, true),
new CounterStatistic("pp", (int)(score.PP ?? 0)),
};
var bottomStatistics = new List<StatisticDisplay>();
foreach (var stat in score.SortedStatistics)
bottomStatistics.Add(new CounterStatistic(stat.Key.GetDescription(), stat.Value));
statisticDisplays.AddRange(topStatistics);
statisticDisplays.AddRange(bottomStatistics);
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Spacing = new Vector2(20),
Children = new Drawable[]
{
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = new LocalisedString((metadata.Title, metadata.TitleUnicode)),
Font = OsuFont.Torus.With(size: 20, weight: FontWeight.SemiBold),
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = new LocalisedString((metadata.Artist, metadata.ArtistUnicode)),
Font = OsuFont.Torus.With(size: 14, weight: FontWeight.SemiBold)
},
new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Top = 40 },
RelativeSizeAxes = Axes.X,
Height = 230,
Child = new AccuracyCircle(score)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fit,
}
},
scoreCounter = new TotalScoreCounter
{
Margin = new MarginPadding { Top = 0, Bottom = 5 },
Current = { Value = 0 },
Alpha = 0,
AlwaysPresent = true
},
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new StarRatingDisplay(beatmap)
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft
},
new ModDisplay
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
DisplayUnrankedText = false,
ExpansionMode = ExpansionMode.AlwaysExpanded,
Scale = new Vector2(0.5f),
Current = { Value = score.Mods }
}
}
},
new FillFlowContainer
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Direction = FillDirection.Vertical,
AutoSizeAxes = Axes.Both,
Children = new Drawable[]
{
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = beatmap.Version,
Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold),
},
new OsuTextFlowContainer(s => s.Font = OsuFont.Torus.With(size: 12))
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
}.With(t =>
{
t.AddText("mapped by ");
t.AddText(score.UserString, s => s.Font = s.Font.With(weight: FontWeight.SemiBold));
})
}
},
}
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Spacing = new Vector2(0, 5),
Children = new Drawable[]
{
new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Content = new[] { topStatistics.Cast<Drawable>().ToArray() },
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
}
},
new GridContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Content = new[] { bottomStatistics.Cast<Drawable>().ToArray() },
RowDimensions = new[]
{
new Dimension(GridSizeMode.AutoSize),
}
}
}
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
// Score counter value setting must be scheduled so it isn't transferred instantaneously
ScheduleAfterChildren(() =>
{
using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DELAY, true))
{
scoreCounter.FadeIn();
scoreCounter.Current.Value = score.TotalScore;
double delay = 0;
foreach (var stat in statisticDisplays)
{
using (BeginDelayedSequence(delay, true))
stat.Appear();
delay += 200;
}
}
});
}
}
}

View File

@ -0,0 +1,64 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Users;
using osu.Game.Users.Drawables;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded
{
/// <summary>
/// The content that appears in the middle section of the <see cref="ScorePanel"/>.
/// </summary>
public class ExpandedPanelTopContent : CompositeDrawable
{
private readonly User user;
/// <summary>
/// Creates a new <see cref="ExpandedPanelTopContent"/>.
/// </summary>
/// <param name="user">The <see cref="User"/> to display.</param>
public ExpandedPanelTopContent(User user)
{
this.user = user;
Anchor = Anchor.TopCentre;
Origin = Anchor.Centre;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new UpdateableAvatar(user)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(80),
CornerRadius = 20,
CornerExponent = 2.5f,
Masking = true,
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Text = user.Username,
Font = OsuFont.Torus.With(size: 16, weight: FontWeight.SemiBold)
}
}
};
}
}
}

View File

@ -0,0 +1,108 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Globalization;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Containers;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking.Expanded
{
/// <summary>
/// A pill that displays the star rating of a <see cref="BeatmapInfo"/>.
/// </summary>
public class StarRatingDisplay : CompositeDrawable
{
private readonly BeatmapInfo beatmap;
/// <summary>
/// Creates a new <see cref="StarRatingDisplay"/>.
/// </summary>
/// <param name="beatmap">The <see cref="BeatmapInfo"/> to display the star difficulty of.</param>
public StarRatingDisplay(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
AutoSizeAxes = Axes.Both;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
var starRatingParts = beatmap.StarDifficulty.ToString("0.00", CultureInfo.InvariantCulture).Split('.');
string wholePart = starRatingParts[0];
string fractionPart = starRatingParts[1];
string separator = CultureInfo.CurrentCulture.NumberFormat.NumberDecimalSeparator;
ColourInfo backgroundColour = beatmap.DifficultyRating == DifficultyRating.ExpertPlus
? ColourInfo.GradientVertical(Color4Extensions.FromHex("#C1C1C1"), Color4Extensions.FromHex("#595959"))
: (ColourInfo)colours.ForDifficultyRating(beatmap.DifficultyRating);
InternalChildren = new Drawable[]
{
new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = backgroundColour
},
}
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Padding = new MarginPadding { Horizontal = 8, Vertical = 4 },
Direction = FillDirection.Horizontal,
Spacing = new Vector2(2, 0),
Children = new Drawable[]
{
new SpriteIcon
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Size = new Vector2(7),
Icon = FontAwesome.Solid.Star,
Colour = Color4.Black
},
new OsuTextFlowContainer(s => s.Font = OsuFont.Numeric.With(weight: FontWeight.Black))
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
TextAnchor = Anchor.BottomLeft,
}.With(t =>
{
t.AddText($"{wholePart}", s =>
{
s.Colour = Color4.Black;
s.Font = s.Font.With(size: 14);
s.UseFullGlyphHeight = false;
});
t.AddText($"{separator}{fractionPart}", s =>
{
s.Colour = Color4.Black;
s.Font = s.Font.With(size: 7);
s.UseFullGlyphHeight = false;
});
})
}
}
};
}
}
}

View File

@ -0,0 +1,58 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osu.Game.Utils;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Statistics
{
/// <summary>
/// A <see cref="StatisticDisplay"/> to display the player's accuracy.
/// </summary>
public class AccuracyStatistic : StatisticDisplay
{
private readonly double accuracy;
private RollingCounter<double> counter;
/// <summary>
/// Creates a new <see cref="AccuracyStatistic"/>.
/// </summary>
/// <param name="accuracy">The accuracy to display.</param>
public AccuracyStatistic(double accuracy)
: base("accuracy")
{
this.accuracy = accuracy;
}
public override void Appear()
{
base.Appear();
counter.Current.Value = accuracy;
}
protected override Drawable CreateContent() => counter = new Counter();
private class Counter : RollingCounter<double>
{
protected override double RollingDuration => AccuracyCircle.ACCURACY_TRANSFORM_DURATION;
protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING;
public Counter()
{
DisplayedCountSpriteText.Font = OsuFont.Torus.With(size: 20, fixedWidth: true);
DisplayedCountSpriteText.Spacing = new Vector2(-2, 0);
}
protected override string FormatCount(double count) => count.FormatAccuracy();
public override void Increment(double amount)
=> Current.Value += amount;
}
}
}

View File

@ -0,0 +1,71 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Statistics
{
/// <summary>
/// A <see cref="StatisticDisplay"/> to display the player's combo.
/// </summary>
public class ComboStatistic : CounterStatistic
{
private readonly bool isPerfect;
private Drawable perfectText;
/// <summary>
/// Creates a new <see cref="ComboStatistic"/>.
/// </summary>
/// <param name="combo">The combo to be displayed.</param>
/// <param name="isPerfect">Whether this is a perfect combo.</param>
public ComboStatistic(int combo, bool isPerfect)
: base("combo", combo)
{
this.isPerfect = isPerfect;
}
public override void Appear()
{
base.Appear();
if (isPerfect)
{
using (BeginDelayedSequence(AccuracyCircle.ACCURACY_TRANSFORM_DURATION / 2, true))
perfectText.FadeIn(50);
}
}
protected override Drawable CreateContent() => new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Spacing = new Vector2(10, 0),
Children = new[]
{
base.CreateContent().With(d =>
{
Anchor = Anchor.CentreLeft;
Origin = Anchor.CentreLeft;
}),
perfectText = new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.CentreLeft,
Text = "PERFECT",
Font = OsuFont.Torus.With(size: 11, weight: FontWeight.SemiBold),
Colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#66FFCC"), Color4Extensions.FromHex("#FF9AD7")),
Alpha = 0,
UseFullGlyphHeight = false,
}
}
};
}
}

View File

@ -0,0 +1,56 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded.Statistics
{
/// <summary>
/// A <see cref="StatisticDisplay"/> to display general numeric values.
/// </summary>
public class CounterStatistic : StatisticDisplay
{
private readonly int count;
private RollingCounter<int> counter;
/// <summary>
/// Creates a new <see cref="CounterStatistic"/>.
/// </summary>
/// <param name="header">The name of the statistic.</param>
/// <param name="count">The value to display.</param>
public CounterStatistic(string header, int count)
: base(header)
{
this.count = count;
}
public override void Appear()
{
base.Appear();
counter.Current.Value = count;
}
protected override Drawable CreateContent() => counter = new Counter();
private class Counter : RollingCounter<int>
{
protected override double RollingDuration => AccuracyCircle.ACCURACY_TRANSFORM_DURATION;
protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING;
public Counter()
{
DisplayedCountSpriteText.Font = OsuFont.Torus.With(size: 20, fixedWidth: true);
DisplayedCountSpriteText.Spacing = new Vector2(-2, 0);
}
public override void Increment(int amount)
=> Current.Value += amount;
}
}
}

View File

@ -0,0 +1,95 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
namespace osu.Game.Screens.Ranking.Expanded.Statistics
{
/// <summary>
/// A statistic from the score to be displayed in the <see cref="ExpandedPanelMiddleContent"/>.
/// </summary>
public abstract class StatisticDisplay : CompositeDrawable
{
private readonly string header;
private Drawable content;
/// <summary>
/// Creates a new <see cref="StatisticDisplay"/>.
/// </summary>
/// <param name="header">The name of the statistic.</param>
protected StatisticDisplay(string header)
{
this.header = header;
RelativeSizeAxes = Axes.X;
AutoSizeAxes = Axes.Y;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChild = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new[]
{
new CircularContainer
{
RelativeSizeAxes = Axes.X,
Height = 12,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#222")
},
new OsuSpriteText
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Font = OsuFont.Torus.With(size: 12, weight: FontWeight.SemiBold),
Text = header.ToUpperInvariant(),
}
}
},
new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
AutoSizeAxes = Axes.Both,
Children = new[]
{
content = CreateContent().With(d =>
{
d.Anchor = Anchor.TopCentre;
d.Origin = Anchor.TopCentre;
d.Alpha = 0;
d.AlwaysPresent = true;
}),
}
}
}
};
}
/// <summary>
/// Shows the statistic value.
/// </summary>
public virtual void Appear() => content.FadeIn(100);
/// <summary>
/// Creates the content for this <see cref="StatisticDisplay"/>.
/// </summary>
protected abstract Drawable CreateContent();
}
}

View File

@ -0,0 +1,38 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Game.Graphics;
using osu.Game.Graphics.UserInterface;
using osu.Game.Screens.Ranking.Expanded.Accuracy;
using osuTK;
namespace osu.Game.Screens.Ranking.Expanded
{
/// <summary>
/// A counter for the player's total score to be displayed in the <see cref="ExpandedPanelMiddleContent"/>.
/// </summary>
public class TotalScoreCounter : RollingCounter<long>
{
protected override double RollingDuration => AccuracyCircle.ACCURACY_TRANSFORM_DURATION;
protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING;
public TotalScoreCounter()
{
// Todo: AutoSize X removed here due to https://github.com/ppy/osu-framework/issues/3369
AutoSizeAxes = Axes.Y;
RelativeSizeAxes = Axes.X;
DisplayedCountSpriteText.Anchor = Anchor.TopCentre;
DisplayedCountSpriteText.Origin = Anchor.TopCentre;
DisplayedCountSpriteText.Font = OsuFont.Torus.With(size: 60, weight: FontWeight.Light, fixedWidth: true);
DisplayedCountSpriteText.Spacing = new Vector2(-5, 0);
}
protected override string FormatCount(long count) => count.ToString("N0");
public override void Increment(long amount)
=> Current.Value += amount;
}
}

View File

@ -1,43 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Scoring;
using osu.Game.Screens.Select.Leaderboards;
using osuTK;
namespace osu.Game.Screens.Ranking.Pages
{
public class LocalLeaderboardPage : ResultsPage
{
public LocalLeaderboardPage(ScoreInfo score, WorkingBeatmap beatmap = null)
: base(score, beatmap)
{
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new Box
{
Colour = colours.Gray6,
RelativeSizeAxes = Axes.Both,
},
new BeatmapLeaderboard
{
Origin = Anchor.Centre,
Anchor = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Beatmap = Beatmap.BeatmapInfo ?? Score.Beatmap,
Scale = new Vector2(0.7f)
}
};
}
}
}

View File

@ -1,428 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Graphics.Textures;
using osu.Framework.Localisation;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Graphics.Sprites;
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.Leaderboards;
using osu.Game.Rulesets.Scoring;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
using osu.Game.Users;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking.Pages
{
public class ScoreResultsPage : ResultsPage
{
private Container scoreContainer;
private ScoreCounter scoreCounter;
private readonly ScoreInfo score;
public ScoreResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
: base(score, beatmap)
{
this.score = score;
}
private FillFlowContainer<DrawableScoreStatistic> statisticsContainer;
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
const float user_header_height = 120;
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
Padding = new MarginPadding { Top = user_header_height },
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
}
},
new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Direction = FillDirection.Vertical,
Children = new Drawable[]
{
new DelayedLoadWrapper(new UserHeader(Score.User)
{
RelativeSizeAxes = Axes.Both,
})
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Height = user_header_height,
},
new UpdateableRank(Score.Rank)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Size = new Vector2(150, 60),
Margin = new MarginPadding(20),
},
scoreContainer = new Container
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
RelativeSizeAxes = Axes.X,
Height = 60,
Children = new Drawable[]
{
new SongProgressGraph
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.5f,
Objects = Beatmap.Beatmap.HitObjects,
},
scoreCounter = new SlowScoreCounter(6)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Colour = colours.PinkDarker,
Y = 10,
TextSize = 56,
},
}
},
new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Colour = colours.PinkDarker,
Shadow = false,
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Text = "total score",
Margin = new MarginPadding { Bottom = 15 },
},
new BeatmapDetails(Beatmap.BeatmapInfo)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Bottom = 10 },
},
new DateTimeDisplay(Score.Date.LocalDateTime)
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
new Container
{
RelativeSizeAxes = Axes.X,
Size = new Vector2(0.75f, 1),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Margin = new MarginPadding { Top = 10, Bottom = 10 },
Children = new Drawable[]
{
new Box
{
Colour = ColourInfo.GradientHorizontal(
colours.GrayC.Opacity(0),
colours.GrayC.Opacity(0.9f)),
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.5f, 1),
},
new Box
{
Anchor = Anchor.TopRight,
Origin = Anchor.TopRight,
Colour = ColourInfo.GradientHorizontal(
colours.GrayC.Opacity(0.9f),
colours.GrayC.Opacity(0)),
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.5f, 1),
},
}
},
statisticsContainer = new FillFlowContainer<DrawableScoreStatistic>
{
AutoSizeAxes = Axes.Both,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Direction = FillDirection.Horizontal,
LayoutDuration = 200,
LayoutEasing = Easing.OutQuint
},
},
},
new FillFlowContainer
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Margin = new MarginPadding { Bottom = 10 },
Spacing = new Vector2(5),
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new ReplayDownloadButton(score),
new RetryButton()
}
},
};
statisticsContainer.ChildrenEnumerable = Score.SortedStatistics.Select(s => new DrawableScoreStatistic(s));
}
protected override void LoadComplete()
{
base.LoadComplete();
Schedule(() =>
{
scoreCounter.Increment(Score.TotalScore);
int delay = 0;
foreach (var s in statisticsContainer.Children)
{
s.FadeOut()
.Then(delay += 200)
.FadeIn(300 + delay, Easing.Out);
}
});
}
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
scoreCounter.Scale = new Vector2(Math.Min(1f, (scoreContainer.DrawWidth - 20) / scoreCounter.DrawWidth));
}
private class DrawableScoreStatistic : Container
{
private readonly KeyValuePair<HitResult, int> statistic;
public DrawableScoreStatistic(KeyValuePair<HitResult, int> statistic)
{
this.statistic = statistic;
AutoSizeAxes = Axes.Both;
Margin = new MarginPadding { Left = 5, Right = 5 };
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new OsuSpriteText
{
Text = statistic.Value.ToString().PadLeft(4, '0'),
Colour = colours.Gray7,
Font = OsuFont.GetFont(size: 30),
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
new OsuSpriteText
{
Text = statistic.Key.GetDescription(),
Colour = colours.Gray7,
Font = OsuFont.GetFont(weight: FontWeight.Bold),
Y = 26,
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
},
};
}
}
private class DateTimeDisplay : Container
{
private readonly DateTime date;
public DateTimeDisplay(DateTime date)
{
this.date = date;
AutoSizeAxes = Axes.Both;
Masking = true;
CornerRadius = 5;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray6,
},
new FillFlowContainer
{
AutoSizeAxes = Axes.Both,
Direction = FillDirection.Horizontal,
Padding = new MarginPadding { Horizontal = 10, Vertical = 5 },
Spacing = new Vector2(10),
Children = new[]
{
new OsuSpriteText
{
Text = date.ToShortDateString(),
Colour = Color4.White,
},
new OsuSpriteText
{
Text = date.ToShortTimeString(),
Colour = Color4.White,
}
}
},
};
}
}
private class BeatmapDetails : Container
{
private readonly BeatmapInfo beatmap;
private readonly OsuSpriteText title;
private readonly OsuSpriteText artist;
private readonly OsuSpriteText versionMapper;
public BeatmapDetails(BeatmapInfo beatmap)
{
this.beatmap = beatmap;
AutoSizeAxes = Axes.Both;
Children = new Drawable[]
{
new FillFlowContainer
{
Direction = FillDirection.Vertical,
RelativeSizeAxes = Axes.X,
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
title = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Shadow = false,
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 24, italics: true),
},
artist = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Shadow = false,
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 20, italics: true),
},
versionMapper = new OsuSpriteText
{
Anchor = Anchor.TopCentre,
Origin = Anchor.TopCentre,
Shadow = false,
Font = OsuFont.GetFont(weight: FontWeight.Bold),
},
}
}
};
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
title.Colour = artist.Colour = colours.BlueDarker;
versionMapper.Colour = colours.Gray8;
var creator = beatmap.Metadata.Author?.Username;
if (!string.IsNullOrEmpty(creator))
{
versionMapper.Text = $"mapped by {creator}";
if (!string.IsNullOrEmpty(beatmap.Version))
versionMapper.Text = $"{beatmap.Version} - " + versionMapper.Text;
}
title.Text = new LocalisedString((beatmap.Metadata.TitleUnicode, beatmap.Metadata.Title));
artist.Text = new LocalisedString((beatmap.Metadata.ArtistUnicode, beatmap.Metadata.Artist));
}
}
[LongRunningLoad]
private class UserHeader : Container
{
private readonly User user;
private readonly Sprite cover;
public UserHeader(User user)
{
this.user = user;
Children = new Drawable[]
{
cover = new Sprite
{
RelativeSizeAxes = Axes.Both,
FillMode = FillMode.Fill,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
},
new OsuSpriteText
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
Text = user.Username,
Font = OsuFont.GetFont(size: 30, weight: FontWeight.Regular, italics: true),
Padding = new MarginPadding { Bottom = 10 },
}
};
}
[BackgroundDependencyLoader]
private void load(LargeTextureStore textures)
{
if (!string.IsNullOrEmpty(user.CoverUrl))
cover.Texture = textures.Get(user.CoverUrl);
}
}
private class SlowScoreCounter : ScoreCounter
{
protected override double RollingDuration => 3000;
protected override Easing RollingEasing => Easing.OutPow10;
public SlowScoreCounter(uint leading = 0)
: base(leading)
{
DisplayedCountSpriteText.Shadow = false;
DisplayedCountSpriteText.Font = DisplayedCountSpriteText.Font.With(Typeface.Venera, weight: FontWeight.Light);
UseCommaSeparator = true;
}
}
}
}

View File

@ -1,16 +1,11 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Screens.Ranking
{
public interface IResultPageInfo
public enum PanelState
{
IconUsage Icon { get; }
string Name { get; }
ResultsPage CreatePage();
Expanded,
Contracted
}
}

View File

@ -9,7 +9,7 @@
using osu.Game.Scoring;
using osuTK;
namespace osu.Game.Screens.Ranking.Pages
namespace osu.Game.Screens.Ranking
{
public class ReplayDownloadButton : DownloadTrackingComposite<ScoreInfo, ScoreManager>
{

View File

@ -1,97 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Cursor;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.UserInterface;
using osu.Game.Graphics;
using osuTK;
using osuTK.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Graphics.Sprites;
namespace osu.Game.Screens.Ranking
{
public class ResultModeButton : TabItem<IResultPageInfo>, IHasTooltip
{
private readonly IconUsage icon;
private Color4 activeColour;
private Color4 inactiveColour;
private CircularContainer colouredPart;
public ResultModeButton(IResultPageInfo mode)
: base(mode)
{
icon = mode.Icon;
TooltipText = mode.Name;
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
Size = new Vector2(50);
Masking = true;
CornerRadius = 25;
CornerExponent = 2;
activeColour = colours.PinkDarker;
inactiveColour = OsuColour.Gray(0.8f);
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.4f),
Type = EdgeEffectType.Shadow,
Radius = 5,
};
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
colouredPart = new CircularContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
Size = new Vector2(0.8f),
BorderThickness = 4,
BorderColour = Color4.White,
Colour = inactiveColour,
Children = new Drawable[]
{
new Box
{
AlwaysPresent = true, //for border rendering
RelativeSizeAxes = Axes.Both,
Colour = Color4.Transparent,
},
new SpriteIcon
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Shadow = false,
Colour = OsuColour.Gray(0.95f),
Icon = icon,
Size = new Vector2(20),
}
}
}
};
}
protected override void OnActivated() => colouredPart.FadeColour(activeColour, 200, Easing.OutQuint);
protected override void OnDeactivated() => colouredPart.FadeColour(inactiveColour, 200, Easing.OutQuint);
public string TooltipText { get; }
}
}

View File

@ -1,30 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics;
using osu.Framework.Graphics.UserInterface;
using osuTK;
namespace osu.Game.Screens.Ranking
{
public class ResultModeTabControl : TabControl<IResultPageInfo>
{
public ResultModeTabControl()
{
TabContainer.Anchor = Anchor.BottomCentre;
TabContainer.Origin = Anchor.BottomCentre;
TabContainer.Spacing = new Vector2(15);
TabContainer.Masking = false;
TabContainer.Padding = new MarginPadding(5);
}
protected override Dropdown<IResultPageInfo> CreateDropdown() => null;
protected override TabItem<IResultPageInfo> CreateTabItem(IResultPageInfo value) => new ResultModeButton(value)
{
Anchor = TabContainer.Anchor,
Origin = TabContainer.Origin
};
}
}

View File

@ -1,291 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System.Collections.Generic;
using System.Linq;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Extensions.IEnumerableExtensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Screens;
using osu.Game.Graphics.Containers;
using osu.Game.Screens.Backgrounds;
using osuTK;
using osuTK.Graphics;
using osu.Game.Graphics;
using osu.Framework.Graphics.Shapes;
using osu.Game.Graphics.Sprites;
using osu.Game.Scoring;
using osu.Game.Screens.Play;
namespace osu.Game.Screens.Ranking
{
public abstract class Results : OsuScreen
{
protected const float BACKGROUND_BLUR = 20;
private Container circleOuterBackground;
private Container circleOuter;
private Container circleInner;
private ParallaxContainer backgroundParallax;
private ResultModeTabControl modeChangeButtons;
[Resolved(canBeNull: true)]
private Player player { get; set; }
public override bool DisallowExternalBeatmapRulesetChanges => true;
protected readonly ScoreInfo Score;
private Container currentPage;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
private const float overscan = 1.3f;
private const float circle_outer_scale = 0.96f;
protected Results(ScoreInfo score)
{
Score = score;
}
private const float transition_time = 800;
private IEnumerable<Drawable> allCircles => new Drawable[] { circleOuterBackground, circleInner, circleOuter };
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
((BackgroundScreenBeatmap)Background).BlurAmount.Value = BACKGROUND_BLUR;
Background.ScaleTo(1.1f, transition_time, Easing.OutQuint);
allCircles.ForEach(c =>
{
c.FadeOut();
c.ScaleTo(0);
});
backgroundParallax.FadeOut();
modeChangeButtons.FadeOut();
currentPage?.FadeOut();
circleOuterBackground
.FadeIn(transition_time, Easing.OutQuint)
.ScaleTo(1, transition_time, Easing.OutQuint);
using (BeginDelayedSequence(transition_time * 0.25f, true))
{
circleOuter
.FadeIn(transition_time, Easing.OutQuint)
.ScaleTo(1, transition_time, Easing.OutQuint);
using (BeginDelayedSequence(transition_time * 0.3f, true))
{
backgroundParallax.FadeIn(transition_time, Easing.OutQuint);
circleInner
.FadeIn(transition_time, Easing.OutQuint)
.ScaleTo(1, transition_time, Easing.OutQuint);
using (BeginDelayedSequence(transition_time * 0.4f, true))
{
modeChangeButtons.FadeIn(transition_time, Easing.OutQuint);
currentPage?.FadeIn(transition_time, Easing.OutQuint);
}
}
}
}
public override bool OnExiting(IScreen next)
{
allCircles.ForEach(c => c.ScaleTo(0, transition_time, Easing.OutSine));
Background.ScaleTo(1f, transition_time / 4, Easing.OutQuint);
this.FadeOut(transition_time / 4);
return base.OnExiting(next);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
InternalChild = new AspectContainer
{
RelativeSizeAxes = Axes.Y,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Height = overscan,
Children = new Drawable[]
{
circleOuterBackground = new CircularContainer
{
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
Children = new Drawable[]
{
new Box
{
Alpha = 0.2f,
RelativeSizeAxes = Axes.Both,
Colour = Color4.Black,
}
}
},
circleOuter = new CircularContainer
{
Size = new Vector2(circle_outer_scale),
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.4f),
Type = EdgeEffectType.Shadow,
Radius = 15,
},
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
backgroundParallax = new ParallaxContainer
{
RelativeSizeAxes = Axes.Both,
ParallaxAmount = 0.01f,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Sprite
{
RelativeSizeAxes = Axes.Both,
Alpha = 0.2f,
Texture = Beatmap.Value.Background,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
FillMode = FillMode.Fill
}
}
},
modeChangeButtons = new ResultModeTabControl
{
Anchor = Anchor.BottomCentre,
Origin = Anchor.BottomCentre,
RelativeSizeAxes = Axes.X,
Height = 50,
Margin = new MarginPadding { Bottom = 110 },
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomCentre,
Text = $"{Score.MaxCombo}x",
RelativePositionAxes = Axes.X,
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 40),
X = 0.1f,
Colour = colours.BlueDarker,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.TopCentre,
Text = "max combo",
Font = OsuFont.GetFont(size: 20),
RelativePositionAxes = Axes.X,
X = 0.1f,
Colour = colours.Gray6,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.BottomCentre,
Text = Score.DisplayAccuracy,
Font = OsuFont.GetFont(weight: FontWeight.Bold, size: 40),
RelativePositionAxes = Axes.X,
X = 0.9f,
Colour = colours.BlueDarker,
},
new OsuSpriteText
{
Anchor = Anchor.CentreLeft,
Origin = Anchor.TopCentre,
Text = "accuracy",
Font = OsuFont.GetFont(size: 20),
RelativePositionAxes = Axes.X,
X = 0.9f,
Colour = colours.Gray6,
},
}
},
circleInner = new CircularContainer
{
Size = new Vector2(0.6f),
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.4f),
Type = EdgeEffectType.Shadow,
Radius = 15,
},
RelativeSizeAxes = Axes.Both,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Masking = true,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4.White,
},
}
}
}
};
if (player != null)
{
AddInternal(new HotkeyRetryOverlay
{
Action = () =>
{
if (!this.IsCurrentScreen()) return;
player?.Restart();
},
});
}
var pages = CreateResultPages();
foreach (var p in pages)
modeChangeButtons.AddItem(p);
modeChangeButtons.Current.Value = pages.FirstOrDefault();
modeChangeButtons.Current.BindValueChanged(page =>
{
currentPage?.FadeOut();
currentPage?.Expire();
currentPage = page.NewValue?.CreatePage();
if (currentPage != null)
LoadComponentAsync(currentPage, circleInner.Add);
}, true);
}
protected abstract IEnumerable<IResultPageInfo> CreateResultPages();
}
}

View File

@ -1,92 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Shapes;
using osu.Game.Beatmaps;
using osu.Game.Graphics;
using osu.Game.Scoring;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking
{
public abstract class ResultsPage : Container
{
protected readonly ScoreInfo Score;
protected readonly WorkingBeatmap Beatmap;
private CircularContainer content;
private Box fill;
protected override Container<Drawable> Content => content;
protected ResultsPage(ScoreInfo score, WorkingBeatmap beatmap)
{
Score = score;
Beatmap = beatmap;
RelativeSizeAxes = Axes.Both;
}
protected override void LoadComplete()
{
base.LoadComplete();
fill.Delay(400).FadeInFromZero(600);
}
[BackgroundDependencyLoader]
private void load(OsuColour colours)
{
AddRangeInternal(new Drawable[]
{
fill = new Box
{
Alpha = 0,
RelativeSizeAxes = Axes.Both,
Colour = colours.Gray6
},
new CircularContainer
{
EdgeEffect = new EdgeEffectParameters
{
Colour = colours.GrayF.Opacity(0.8f),
Type = EdgeEffectType.Shadow,
Radius = 1,
},
RelativeSizeAxes = Axes.Both,
Masking = true,
BorderThickness = 20,
BorderColour = Color4.White,
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Alpha = 0,
AlwaysPresent = true
},
}
},
content = new CircularContainer
{
EdgeEffect = new EdgeEffectParameters
{
Colour = Color4.Black.Opacity(0.2f),
Type = EdgeEffectType.Shadow,
Radius = 15,
},
RelativeSizeAxes = Axes.Both,
Masking = true,
Size = new Vector2(0.88f),
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
}
});
}
}
}

View File

@ -0,0 +1,142 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
using osu.Game.Graphics.Containers;
using osu.Game.Graphics.UserInterface;
using osu.Game.Scoring;
using osu.Game.Screens.Backgrounds;
using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Screens.Ranking
{
public class ResultsScreen : OsuScreen
{
protected const float BACKGROUND_BLUR = 20;
public override bool DisallowExternalBeatmapRulesetChanges => true;
// Temporary for now to stop dual transitions. Should respect the current toolbar mode, but there's no way to do so currently.
public override bool HideOverlaysOnEnter => true;
protected override BackgroundScreen CreateBackground() => new BackgroundScreenBeatmap(Beatmap.Value);
[Resolved(CanBeNull = true)]
private Player player { get; set; }
private readonly ScoreInfo score;
private Drawable bottomPanel;
public ResultsScreen(ScoreInfo score)
{
this.score = score;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new[]
{
new ResultsScrollContainer
{
Child = new ScorePanel(score)
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
State = PanelState.Expanded
},
},
bottomPanel = new Container
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = TwoLayerButton.SIZE_EXTENDED.Y,
Alpha = 0,
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#333")
},
new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new ReplayDownloadButton(score) { Width = 300 },
new RetryButton { Width = 300 },
}
}
}
}
};
if (player != null)
{
AddInternal(new HotkeyRetryOverlay
{
Action = () =>
{
if (!this.IsCurrentScreen()) return;
player?.Restart();
},
});
}
}
public override void OnEntering(IScreen last)
{
base.OnEntering(last);
((BackgroundScreenBeatmap)Background).BlurAmount.Value = BACKGROUND_BLUR;
Background.FadeTo(0.5f, 250);
bottomPanel.FadeTo(1, 250);
}
public override bool OnExiting(IScreen next)
{
Background.FadeTo(1, 250);
return base.OnExiting(next);
}
private class ResultsScrollContainer : OsuScrollContainer
{
private readonly Container content;
protected override Container<Drawable> Content => content;
public ResultsScrollContainer()
{
base.Content.Add(content = new Container
{
RelativeSizeAxes = Axes.X
});
RelativeSizeAxes = Axes.Both;
ScrollbarVisible = false;
}
protected override void Update()
{
base.Update();
content.Height = DrawHeight;
}
}
}
}

View File

@ -10,7 +10,7 @@
using osu.Game.Screens.Play;
using osuTK;
namespace osu.Game.Screens.Ranking.Pages
namespace osu.Game.Screens.Ranking
{
public class RetryButton : OsuAnimatedButton
{

View File

@ -0,0 +1,223 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using System;
using osu.Framework;
using osu.Framework.Allocation;
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Colour;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Expanded;
using osuTK;
using osuTK.Graphics;
namespace osu.Game.Screens.Ranking
{
public class ScorePanel : CompositeDrawable, IStateful<PanelState>
{
/// <summary>
/// Width of the panel when contracted.
/// </summary>
private const float contracted_width = 160;
/// <summary>
/// Height of the panel when contracted.
/// </summary>
private const float contracted_height = 320;
/// <summary>
/// Width of the panel when expanded.
/// </summary>
private const float expanded_width = 360;
/// <summary>
/// Height of the panel when expanded.
/// </summary>
private const float expanded_height = 560;
/// <summary>
/// Height of the top layer when the panel is expanded.
/// </summary>
private const float expanded_top_layer_height = 53;
/// <summary>
/// Height of the top layer when the panel is contracted.
/// </summary>
private const float contracted_top_layer_height = 40;
/// <summary>
/// Duration for the panel to resize into its expanded/contracted size.
/// </summary>
private const double resize_duration = 200;
/// <summary>
/// Delay after <see cref="resize_duration"/> before the top layer is expanded.
/// </summary>
private const double top_layer_expand_delay = 100;
/// <summary>
/// Duration for the top layer expansion.
/// </summary>
private const double top_layer_expand_duration = 200;
/// <summary>
/// Duration for the panel contents to fade in.
/// </summary>
private const double content_fade_duration = 50;
private static readonly ColourInfo expanded_top_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#444"), Color4Extensions.FromHex("#333"));
private static readonly ColourInfo expanded_middle_layer_colour = ColourInfo.GradientVertical(Color4Extensions.FromHex("#555"), Color4Extensions.FromHex("#333"));
private static readonly Color4 contracted_top_layer_colour = Color4Extensions.FromHex("#353535");
private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#444");
public event Action<PanelState> StateChanged;
private readonly ScoreInfo score;
private Container topLayerContainer;
private Drawable topLayerBackground;
private Container topLayerContentContainer;
private Drawable topLayerContent;
private Container middleLayerContainer;
private Drawable middleLayerBackground;
private Container middleLayerContentContainer;
private Drawable middleLayerContent;
public ScorePanel(ScoreInfo score)
{
this.score = score;
}
[BackgroundDependencyLoader]
private void load()
{
InternalChildren = new Drawable[]
{
topLayerContainer = new Container
{
Name = "Top layer",
RelativeSizeAxes = Axes.X,
Height = 120,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = 20,
CornerExponent = 2.5f,
Masking = true,
Child = topLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
},
topLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
}
},
middleLayerContainer = new Container
{
Name = "Middle layer",
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
new Container
{
RelativeSizeAxes = Axes.Both,
CornerRadius = 20,
CornerExponent = 2.5f,
Masking = true,
Child = middleLayerBackground = new Box { RelativeSizeAxes = Axes.Both }
},
middleLayerContentContainer = new Container { RelativeSizeAxes = Axes.Both }
}
}
};
}
protected override void LoadComplete()
{
base.LoadComplete();
if (state == PanelState.Expanded)
{
topLayerBackground.FadeColour(expanded_top_layer_colour);
middleLayerBackground.FadeColour(expanded_middle_layer_colour);
}
else
{
topLayerBackground.FadeColour(contracted_top_layer_colour);
middleLayerBackground.FadeColour(contracted_middle_layer_colour);
}
updateState();
}
private PanelState state = PanelState.Contracted;
public PanelState State
{
get => state;
set
{
if (state == value)
return;
state = value;
if (LoadState >= LoadState.Ready)
updateState();
StateChanged?.Invoke(value);
}
}
private void updateState()
{
topLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint);
middleLayerContainer.MoveToY(0, resize_duration, Easing.OutQuint);
topLayerContent?.FadeOut(content_fade_duration).Expire();
middleLayerContent?.FadeOut(content_fade_duration).Expire();
switch (state)
{
case PanelState.Expanded:
this.ResizeTo(new Vector2(expanded_width, expanded_height), resize_duration, Easing.OutQuint);
topLayerBackground.FadeColour(expanded_top_layer_colour, resize_duration, Easing.OutQuint);
middleLayerBackground.FadeColour(expanded_middle_layer_colour, resize_duration, Easing.OutQuint);
topLayerContentContainer.Add(middleLayerContent = new ExpandedPanelTopContent(score.User).With(d => d.Alpha = 0));
middleLayerContentContainer.Add(topLayerContent = new ExpandedPanelMiddleContent(score).With(d => d.Alpha = 0));
break;
case PanelState.Contracted:
this.ResizeTo(new Vector2(contracted_width, contracted_height), resize_duration, Easing.OutQuint);
topLayerBackground.FadeColour(contracted_top_layer_colour, resize_duration, Easing.OutQuint);
middleLayerBackground.FadeColour(contracted_middle_layer_colour, resize_duration, Easing.OutQuint);
break;
}
using (BeginDelayedSequence(resize_duration + top_layer_expand_delay, true))
{
switch (state)
{
case PanelState.Expanded:
topLayerContainer.MoveToY(-expanded_top_layer_height / 2, top_layer_expand_duration, Easing.OutQuint);
middleLayerContainer.MoveToY(expanded_top_layer_height / 2, top_layer_expand_duration, Easing.OutQuint);
break;
case PanelState.Contracted:
topLayerContainer.MoveToY(-contracted_top_layer_height / 2, top_layer_expand_duration, Easing.OutQuint);
middleLayerContainer.MoveToY(contracted_top_layer_height / 2, top_layer_expand_duration, Easing.OutQuint);
break;
}
topLayerContent?.FadeIn(content_fade_duration);
middleLayerContent?.FadeIn(content_fade_duration);
}
}
}
}

View File

@ -1,28 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Pages;
namespace osu.Game.Screens.Ranking.Types
{
public class LocalLeaderboardPageInfo : IResultPageInfo
{
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
public LocalLeaderboardPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
{
this.score = score;
this.beatmap = beatmap;
}
public IconUsage Icon => FontAwesome.Solid.User;
public string Name => @"Local Leaderboard";
public ResultsPage CreatePage() => new LocalLeaderboardPage(score, beatmap);
}
}

View File

@ -1,30 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Graphics.Sprites;
using osu.Game.Beatmaps;
using osu.Game.Scoring;
using osu.Game.Screens.Ranking.Pages;
namespace osu.Game.Screens.Ranking.Types
{
public class ScoreOverviewPageInfo : IResultPageInfo
{
public IconUsage Icon => FontAwesome.Solid.Asterisk;
public string Name => "Overview";
private readonly ScoreInfo score;
private readonly WorkingBeatmap beatmap;
public ScoreOverviewPageInfo(ScoreInfo score, WorkingBeatmap beatmap)
{
this.score = score;
this.beatmap = beatmap;
}
public ResultsPage CreatePage()
{
return new ScoreResultsPage(score, beatmap);
}
}
}

View File

@ -23,6 +23,7 @@
using osu.Framework.Graphics.Effects;
using osu.Framework.Graphics.Sprites;
using osu.Framework.Localisation;
using osu.Framework.Logging;
using osu.Game.Rulesets;
using osu.Game.Rulesets.Mods;
using osu.Game.Rulesets.UI;
@ -311,20 +312,27 @@ private InfoLabel[] getInfoLabels()
Content = getBPMRange(b),
}));
IBeatmap playableBeatmap;
try
{
// Try to get the beatmap with the user's ruleset
playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty<Mod>());
}
catch (BeatmapInvalidForRulesetException)
{
// Can't be converted to the user's ruleset, so use the beatmap's own ruleset
playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Array.Empty<Mod>());
}
IBeatmap playableBeatmap;
labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)));
try
{
// Try to get the beatmap with the user's ruleset
playableBeatmap = beatmap.GetPlayableBeatmap(ruleset, Array.Empty<Mod>());
}
catch (BeatmapInvalidForRulesetException)
{
// Can't be converted to the user's ruleset, so use the beatmap's own ruleset
playableBeatmap = beatmap.GetPlayableBeatmap(beatmap.BeatmapInfo.Ruleset, Array.Empty<Mod>());
}
labels.AddRange(playableBeatmap.GetStatistics().Select(s => new InfoLabel(s)));
}
catch (Exception e)
{
Logger.Error(e, "Could not load beatmap successfully!");
}
}
return labels.ToArray();

View File

@ -8,6 +8,7 @@
using osu.Framework.Screens;
using osu.Game.Graphics;
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking;
using osu.Game.Users;
using osuTK.Input;
@ -31,7 +32,7 @@ private void load(OsuColour colours)
Edit();
}, Key.Number4);
((PlayBeatmapDetailArea)BeatmapDetails).Leaderboard.ScoreSelected += score => this.Push(new SoloResults(score));
((PlayBeatmapDetailArea)BeatmapDetails).Leaderboard.ScoreSelected += score => this.Push(new ResultsScreen(score));
}
protected override BeatmapDetailArea CreateBeatmapDetailArea() => new PlayBeatmapDetailArea();

View File

@ -23,7 +23,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.314.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.317.0" />
<PackageReference Include="Sentry" Version="2.1.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />

View File

@ -71,7 +71,7 @@
</ItemGroup>
<ItemGroup Label="Package References">
<PackageReference Include="ppy.osu.Game.Resources" Version="2020.315.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.314.0" />
<PackageReference Include="ppy.osu.Framework.iOS" Version="2020.317.0" />
</ItemGroup>
<!-- Xamarin.iOS does not automatically handle transitive dependencies from NuGet packages. -->
<ItemGroup Label="Transitive Dependencies">
@ -79,7 +79,7 @@
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite.Core" Version="2.2.6" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="ppy.osu.Framework" Version="2020.314.0" />
<PackageReference Include="ppy.osu.Framework" Version="2020.317.0" />
<PackageReference Include="SharpCompress" Version="0.24.0" />
<PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="SharpRaven" Version="2.4.0" />