mirror of
https://github.com/ppy/osu.git
synced 2024-09-22 04:07:25 +08:00
Replace old bad daily challenge leaderboard with new implementation
- Actually shows scores rather than playlist aggregates (which are useful... in playlists, where there is more than one item) - Actually allows scores to be shown by clicking on them - Doesn't completely break down visually on smaller window sizes The general appearance is not as polished as the old one in details but I wanted something quick that we can get out by next weekend. Also includes the naive method of refetching scores once a new top 50 score is detected. I can add a stagger if required.
This commit is contained in:
parent
8e8909c999
commit
5fa586848d
@ -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 System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using NUnit.Framework;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Utils;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.API.Requests.Responses;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Overlays;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.OnlinePlay.DailyChallenge;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Tests.Visual.DailyChallenge
|
||||||
|
{
|
||||||
|
public partial class TestSceneDailyChallengeLeaderboard : OsuTestScene
|
||||||
|
{
|
||||||
|
private DummyAPIAccess dummyAPI => (DummyAPIAccess)API;
|
||||||
|
|
||||||
|
[Cached]
|
||||||
|
private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Plum);
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestBasicBehaviour()
|
||||||
|
{
|
||||||
|
DailyChallengeLeaderboard leaderboard = null!;
|
||||||
|
|
||||||
|
AddStep("set up response without user best", () =>
|
||||||
|
{
|
||||||
|
dummyAPI.HandleRequest = req =>
|
||||||
|
{
|
||||||
|
if (req is IndexPlaylistScoresRequest indexRequest)
|
||||||
|
{
|
||||||
|
indexRequest.TriggerSuccess(createResponse(50, false));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = { Value = 1 } }, new PlaylistItem(Beatmap.Value.BeatmapInfo))
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(0.8f),
|
||||||
|
});
|
||||||
|
|
||||||
|
AddStep("set up response with user best", () =>
|
||||||
|
{
|
||||||
|
dummyAPI.HandleRequest = req =>
|
||||||
|
{
|
||||||
|
if (req is IndexPlaylistScoresRequest indexRequest)
|
||||||
|
{
|
||||||
|
indexRequest.TriggerSuccess(createResponse(50, true));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
AddStep("force refetch", () => leaderboard.RefetchScores());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestLoadingBehaviour()
|
||||||
|
{
|
||||||
|
IndexPlaylistScoresRequest pendingRequest = null!;
|
||||||
|
DailyChallengeLeaderboard leaderboard = null!;
|
||||||
|
|
||||||
|
AddStep("set up requests handler", () =>
|
||||||
|
{
|
||||||
|
dummyAPI.HandleRequest = req =>
|
||||||
|
{
|
||||||
|
if (req is IndexPlaylistScoresRequest indexRequest)
|
||||||
|
{
|
||||||
|
pendingRequest = indexRequest;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
AddStep("create leaderboard", () => Child = leaderboard = new DailyChallengeLeaderboard(new Room { RoomID = { Value = 1 } }, new PlaylistItem(Beatmap.Value.BeatmapInfo))
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
Size = new Vector2(0.8f),
|
||||||
|
});
|
||||||
|
AddStep("complete load", () => pendingRequest.TriggerSuccess(createResponse(3, true)));
|
||||||
|
AddStep("force refetch", () => leaderboard.RefetchScores());
|
||||||
|
AddStep("complete load", () => pendingRequest.TriggerSuccess(createResponse(4, true)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private IndexedMultiplayerScores createResponse(int scoreCount, bool returnUserBest)
|
||||||
|
{
|
||||||
|
var result = new IndexedMultiplayerScores();
|
||||||
|
|
||||||
|
for (int i = 0; i < scoreCount; ++i)
|
||||||
|
{
|
||||||
|
result.Scores.Add(new MultiplayerScore
|
||||||
|
{
|
||||||
|
ID = i,
|
||||||
|
Accuracy = 1 - (float)i / (2 * scoreCount),
|
||||||
|
Position = i + 1,
|
||||||
|
EndedAt = DateTimeOffset.Now,
|
||||||
|
Passed = true,
|
||||||
|
Rank = (ScoreRank)RNG.Next((int)ScoreRank.D, (int)ScoreRank.XH),
|
||||||
|
MaxCombo = 1000 - i,
|
||||||
|
TotalScore = (long)(1_000_000 * (1 - (float)i / (2 * scoreCount))),
|
||||||
|
User = new APIUser { Username = $"user {i}" },
|
||||||
|
Statistics = new Dictionary<HitResult, int>()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnUserBest)
|
||||||
|
{
|
||||||
|
result.UserScore = new MultiplayerScore
|
||||||
|
{
|
||||||
|
ID = 99999,
|
||||||
|
Accuracy = 0.91,
|
||||||
|
Position = 4,
|
||||||
|
EndedAt = DateTimeOffset.Now,
|
||||||
|
Passed = true,
|
||||||
|
Rank = ScoreRank.A,
|
||||||
|
MaxCombo = 100,
|
||||||
|
TotalScore = 800000,
|
||||||
|
User = dummyAPI.LocalUser.Value,
|
||||||
|
Statistics = new Dictionary<HitResult, int>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -50,35 +50,73 @@ namespace osu.Game.Tests.Visual.SongSelect
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[SetUp]
|
[Test]
|
||||||
public void Setup() => Schedule(() =>
|
public void TestSheared()
|
||||||
{
|
{
|
||||||
Children = new Drawable[]
|
AddStep("create content", () =>
|
||||||
{
|
{
|
||||||
fillFlow = new FillFlowContainer
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Width = relativeWidth,
|
fillFlow = new FillFlowContainer
|
||||||
Anchor = Anchor.Centre,
|
{
|
||||||
Origin = Anchor.Centre,
|
Width = relativeWidth,
|
||||||
RelativeSizeAxes = Axes.X,
|
Anchor = Anchor.Centre,
|
||||||
AutoSizeAxes = Axes.Y,
|
Origin = Anchor.Centre,
|
||||||
Spacing = new Vector2(0f, 2f),
|
RelativeSizeAxes = Axes.X,
|
||||||
Shear = new Vector2(OsuGame.SHEAR, 0)
|
AutoSizeAxes = Axes.Y,
|
||||||
},
|
Spacing = new Vector2(0f, 2f),
|
||||||
drawWidthText = new OsuSpriteText(),
|
Shear = new Vector2(OsuGame.SHEAR, 0)
|
||||||
};
|
},
|
||||||
|
drawWidthText = new OsuSpriteText(),
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var scoreInfo in getTestScores())
|
foreach (var scoreInfo in getTestScores())
|
||||||
|
{
|
||||||
|
fillFlow.Add(new LeaderboardScoreV2(scoreInfo)
|
||||||
|
{
|
||||||
|
Rank = scoreInfo.Position,
|
||||||
|
IsPersonalBest = scoreInfo.User.Id == 2,
|
||||||
|
Shear = Vector2.Zero,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var score in fillFlow.Children)
|
||||||
|
score.Show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
[Test]
|
||||||
|
public void TestNonSheared()
|
||||||
|
{
|
||||||
|
AddStep("create content", () =>
|
||||||
{
|
{
|
||||||
fillFlow.Add(new LeaderboardScoreV2(scoreInfo, scoreInfo.Position, scoreInfo.User.Id == 2)
|
Children = new Drawable[]
|
||||||
{
|
{
|
||||||
Shear = Vector2.Zero,
|
fillFlow = new FillFlowContainer
|
||||||
});
|
{
|
||||||
}
|
Width = relativeWidth,
|
||||||
|
Anchor = Anchor.Centre,
|
||||||
|
Origin = Anchor.Centre,
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Spacing = new Vector2(0f, 2f),
|
||||||
|
},
|
||||||
|
drawWidthText = new OsuSpriteText(),
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var score in fillFlow.Children)
|
foreach (var scoreInfo in getTestScores())
|
||||||
score.Show();
|
{
|
||||||
});
|
fillFlow.Add(new LeaderboardScoreV2(scoreInfo)
|
||||||
|
{
|
||||||
|
Rank = scoreInfo.Position,
|
||||||
|
IsPersonalBest = scoreInfo.User.Id == 2,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var score in fillFlow.Children)
|
||||||
|
score.Show();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
[SetUpSteps]
|
[SetUpSteps]
|
||||||
public void SetUpSteps()
|
public void SetUpSteps()
|
||||||
|
@ -49,7 +49,7 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
private readonly Bindable<IReadOnlyList<Mod>> userMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
private readonly Bindable<IReadOnlyList<Mod>> userMods = new Bindable<IReadOnlyList<Mod>>(Array.Empty<Mod>());
|
||||||
|
|
||||||
private OnlinePlayScreenWaveContainer waves = null!;
|
private OnlinePlayScreenWaveContainer waves = null!;
|
||||||
private MatchLeaderboard leaderboard = null!;
|
private DailyChallengeLeaderboard leaderboard = null!;
|
||||||
private RoomModSelectOverlay userModsSelectOverlay = null!;
|
private RoomModSelectOverlay userModsSelectOverlay = null!;
|
||||||
private Sample? sampleStart;
|
private Sample? sampleStart;
|
||||||
private IDisposable? userModsSelectOverlayRegistration;
|
private IDisposable? userModsSelectOverlayRegistration;
|
||||||
@ -208,33 +208,17 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
feed = new DailyChallengeEventFeed
|
feed = new DailyChallengeEventFeed
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
PresentScore = id =>
|
PresentScore = presentScore
|
||||||
{
|
|
||||||
if (this.IsCurrentScreen())
|
|
||||||
this.Push(new PlaylistItemScoreResultsScreen(room.RoomID.Value!.Value, playlistItem, id));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
null,
|
null,
|
||||||
// Middle column (leaderboard)
|
// Middle column (leaderboard)
|
||||||
new GridContainer
|
leaderboard = new DailyChallengeLeaderboard(room, playlistItem)
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Content = new[]
|
PresentScore = presentScore,
|
||||||
{
|
|
||||||
new Drawable[]
|
|
||||||
{
|
|
||||||
new SectionHeader("Leaderboard")
|
|
||||||
},
|
|
||||||
[leaderboard = new MatchLeaderboard { RelativeSizeAxes = Axes.Both }],
|
|
||||||
},
|
|
||||||
RowDimensions = new[]
|
|
||||||
{
|
|
||||||
new Dimension(GridSizeMode.AutoSize),
|
|
||||||
new Dimension(),
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// Spacer
|
// Spacer
|
||||||
null,
|
null,
|
||||||
@ -330,6 +314,12 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
metadataClient.MultiplayerRoomScoreSet += onRoomScoreSet;
|
metadataClient.MultiplayerRoomScoreSet += onRoomScoreSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void presentScore(long id)
|
||||||
|
{
|
||||||
|
if (this.IsCurrentScreen())
|
||||||
|
this.Push(new PlaylistItemScoreResultsScreen(room.RoomID.Value!.Value, playlistItem, id));
|
||||||
|
}
|
||||||
|
|
||||||
private void onRoomScoreSet(MultiplayerRoomScoreSetEvent e)
|
private void onRoomScoreSet(MultiplayerRoomScoreSetEvent e)
|
||||||
{
|
{
|
||||||
if (e.RoomID != room.RoomID.Value || e.PlaylistItemID != playlistItem.ID)
|
if (e.RoomID != room.RoomID.Value || e.PlaylistItemID != playlistItem.ID)
|
||||||
@ -351,6 +341,9 @@ namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
|||||||
{
|
{
|
||||||
breakdown.AddNewScore(ev);
|
breakdown.AddNewScore(ev);
|
||||||
feed.AddNewScore(ev);
|
feed.AddNewScore(ev);
|
||||||
|
|
||||||
|
if (e.NewRank <= 50)
|
||||||
|
Schedule(() => leaderboard.RefetchScores());
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,175 @@
|
|||||||
|
// 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.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using osu.Framework.Allocation;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Graphics;
|
||||||
|
using osu.Framework.Graphics.Containers;
|
||||||
|
using osu.Game.Beatmaps;
|
||||||
|
using osu.Game.Graphics.Containers;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Online.API;
|
||||||
|
using osu.Game.Online.Rooms;
|
||||||
|
using osu.Game.Rulesets;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
using osu.Game.Screens.SelectV2.Leaderboards;
|
||||||
|
using osuTK;
|
||||||
|
|
||||||
|
namespace osu.Game.Screens.OnlinePlay.DailyChallenge
|
||||||
|
{
|
||||||
|
public partial class DailyChallengeLeaderboard : CompositeDrawable
|
||||||
|
{
|
||||||
|
public Action<long>? PresentScore { get; init; }
|
||||||
|
|
||||||
|
private readonly Room room;
|
||||||
|
private readonly PlaylistItem playlistItem;
|
||||||
|
|
||||||
|
private FillFlowContainer<LeaderboardScoreV2> scoreFlow = null!;
|
||||||
|
private Container userBestContainer = null!;
|
||||||
|
private SectionHeader userBestHeader = null!;
|
||||||
|
private LoadingLayer loadingLayer = null!;
|
||||||
|
|
||||||
|
private CancellationTokenSource? cancellationTokenSource;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IAPIProvider api { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private ScoreManager scoreManager { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private RulesetStore rulesets { get; set; } = null!;
|
||||||
|
|
||||||
|
[Resolved]
|
||||||
|
private IBindable<WorkingBeatmap> beatmap { get; set; } = null!;
|
||||||
|
|
||||||
|
public DailyChallengeLeaderboard(Room room, PlaylistItem playlistItem)
|
||||||
|
{
|
||||||
|
this.room = room;
|
||||||
|
this.playlistItem = playlistItem;
|
||||||
|
}
|
||||||
|
|
||||||
|
[BackgroundDependencyLoader]
|
||||||
|
private void load()
|
||||||
|
{
|
||||||
|
InternalChild = new GridContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
RowDimensions =
|
||||||
|
[
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
new Dimension(),
|
||||||
|
new Dimension(GridSizeMode.AutoSize),
|
||||||
|
new Dimension(GridSizeMode.AutoSize)
|
||||||
|
],
|
||||||
|
Content = new[]
|
||||||
|
{
|
||||||
|
new Drawable[] { new SectionHeader("Leaderboard") },
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Children = new Drawable[]
|
||||||
|
{
|
||||||
|
new OsuScrollContainer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
Child = scoreFlow = new FillFlowContainer<LeaderboardScoreV2>
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding { Right = 20, },
|
||||||
|
Direction = FillDirection.Vertical,
|
||||||
|
Spacing = new Vector2(5),
|
||||||
|
Scale = new Vector2(0.8f),
|
||||||
|
Width = 1 / 0.8f,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
loadingLayer = new LoadingLayer
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.Both,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
new Drawable[] { userBestHeader = new SectionHeader("Personal best") { Alpha = 0, } },
|
||||||
|
new Drawable[]
|
||||||
|
{
|
||||||
|
userBestContainer = new Container
|
||||||
|
{
|
||||||
|
RelativeSizeAxes = Axes.X,
|
||||||
|
AutoSizeAxes = Axes.Y,
|
||||||
|
Padding = new MarginPadding { Right = 20, },
|
||||||
|
Scale = new Vector2(0.8f),
|
||||||
|
Width = 1 / 0.8f,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void LoadComplete()
|
||||||
|
{
|
||||||
|
base.LoadComplete();
|
||||||
|
RefetchScores();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefetchScores()
|
||||||
|
{
|
||||||
|
var request = new IndexPlaylistScoresRequest(room.RoomID.Value!.Value, playlistItem.ID);
|
||||||
|
|
||||||
|
request.Success += req =>
|
||||||
|
{
|
||||||
|
var best = req.Scores.Select(s => s.CreateScoreInfo(scoreManager, rulesets, playlistItem, beatmap.Value.BeatmapInfo)).ToArray();
|
||||||
|
var userBest = req.UserScore?.CreateScoreInfo(scoreManager, rulesets, playlistItem, beatmap.Value.BeatmapInfo);
|
||||||
|
|
||||||
|
cancellationTokenSource?.Cancel();
|
||||||
|
cancellationTokenSource = null;
|
||||||
|
cancellationTokenSource ??= new CancellationTokenSource();
|
||||||
|
|
||||||
|
if (best.Length == 0)
|
||||||
|
{
|
||||||
|
scoreFlow.Clear();
|
||||||
|
loadingLayer.Hide();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LoadComponentsAsync(best.Select(s => new LeaderboardScoreV2(s, sheared: false)
|
||||||
|
{
|
||||||
|
Rank = s.Position,
|
||||||
|
IsPersonalBest = s.UserID == api.LocalUser.Value.Id,
|
||||||
|
Action = () => PresentScore?.Invoke(s.OnlineID),
|
||||||
|
}), loaded =>
|
||||||
|
{
|
||||||
|
scoreFlow.Clear();
|
||||||
|
scoreFlow.AddRange(loaded);
|
||||||
|
scoreFlow.FadeTo(1, 400, Easing.OutQuint);
|
||||||
|
loadingLayer.Hide();
|
||||||
|
}, cancellationTokenSource.Token);
|
||||||
|
}
|
||||||
|
|
||||||
|
userBestContainer.Clear();
|
||||||
|
|
||||||
|
if (userBest != null)
|
||||||
|
{
|
||||||
|
userBestContainer.Add(new LeaderboardScoreV2(userBest, sheared: false)
|
||||||
|
{
|
||||||
|
Rank = userBest.Position,
|
||||||
|
IsPersonalBest = true,
|
||||||
|
Action = () => PresentScore?.Invoke(userBest.OnlineID),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
userBestHeader.FadeTo(userBest == null ? 0 : 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
loadingLayer.Show();
|
||||||
|
scoreFlow.FadeTo(0.5f, 400, Easing.OutQuint);
|
||||||
|
api.Queue(request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -43,6 +43,9 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
{
|
{
|
||||||
public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip<ScoreInfo>
|
public partial class LeaderboardScoreV2 : OsuClickableContainer, IHasContextMenu, IHasCustomTooltip<ScoreInfo>
|
||||||
{
|
{
|
||||||
|
public int? Rank { get; init; }
|
||||||
|
public bool IsPersonalBest { get; init; }
|
||||||
|
|
||||||
private const float expanded_right_content_width = 210;
|
private const float expanded_right_content_width = 210;
|
||||||
private const float grade_width = 40;
|
private const float grade_width = 40;
|
||||||
private const float username_min_width = 125;
|
private const float username_min_width = 125;
|
||||||
@ -52,15 +55,12 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
private const float rank_label_visibility_width_cutoff = rank_label_width + height + username_min_width + statistics_regular_min_width + expanded_right_content_width;
|
private const float rank_label_visibility_width_cutoff = rank_label_width + height + username_min_width + statistics_regular_min_width + expanded_right_content_width;
|
||||||
|
|
||||||
private readonly ScoreInfo score;
|
private readonly ScoreInfo score;
|
||||||
|
private readonly bool sheared;
|
||||||
|
|
||||||
private const int height = 60;
|
private const int height = 60;
|
||||||
private const int corner_radius = 10;
|
private const int corner_radius = 10;
|
||||||
private const int transition_duration = 200;
|
private const int transition_duration = 200;
|
||||||
|
|
||||||
private readonly int? rank;
|
|
||||||
|
|
||||||
private readonly bool isPersonalBest;
|
|
||||||
|
|
||||||
private Colour4 foregroundColour;
|
private Colour4 foregroundColour;
|
||||||
private Colour4 backgroundColour;
|
private Colour4 backgroundColour;
|
||||||
private ColourInfo totalScoreBackgroundGradient;
|
private ColourInfo totalScoreBackgroundGradient;
|
||||||
@ -104,13 +104,12 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
public ITooltip<ScoreInfo> GetCustomTooltip() => new LeaderboardScoreTooltip();
|
public ITooltip<ScoreInfo> GetCustomTooltip() => new LeaderboardScoreTooltip();
|
||||||
public virtual ScoreInfo TooltipContent => score;
|
public virtual ScoreInfo TooltipContent => score;
|
||||||
|
|
||||||
public LeaderboardScoreV2(ScoreInfo score, int? rank, bool isPersonalBest = false)
|
public LeaderboardScoreV2(ScoreInfo score, bool sheared = true)
|
||||||
{
|
{
|
||||||
this.score = score;
|
this.score = score;
|
||||||
this.rank = rank;
|
this.sheared = sheared;
|
||||||
this.isPersonalBest = isPersonalBest;
|
|
||||||
|
|
||||||
Shear = new Vector2(OsuGame.SHEAR, 0);
|
Shear = new Vector2(sheared ? OsuGame.SHEAR : 0, 0);
|
||||||
RelativeSizeAxes = Axes.X;
|
RelativeSizeAxes = Axes.X;
|
||||||
Height = height;
|
Height = height;
|
||||||
}
|
}
|
||||||
@ -120,8 +119,8 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
{
|
{
|
||||||
var user = score.User;
|
var user = score.User;
|
||||||
|
|
||||||
foregroundColour = isPersonalBest ? colourProvider.Background1 : colourProvider.Background5;
|
foregroundColour = IsPersonalBest ? colourProvider.Background1 : colourProvider.Background5;
|
||||||
backgroundColour = isPersonalBest ? colourProvider.Background2 : colourProvider.Background4;
|
backgroundColour = IsPersonalBest ? colourProvider.Background2 : colourProvider.Background4;
|
||||||
totalScoreBackgroundGradient = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0), backgroundColour);
|
totalScoreBackgroundGradient = ColourInfo.GradientHorizontal(backgroundColour.Opacity(0), backgroundColour);
|
||||||
|
|
||||||
statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s, score)
|
statisticsLabels = GetStatistics(score).Select(s => new ScoreComponentLabel(s, score)
|
||||||
@ -159,7 +158,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.X,
|
AutoSizeAxes = Axes.X,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
Child = rankLabel = new RankLabel(rank)
|
Child = rankLabel = new RankLabel(Rank, sheared)
|
||||||
{
|
{
|
||||||
Width = rank_label_width,
|
Width = rank_label_width,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
@ -243,7 +242,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
User = score.User,
|
User = score.User,
|
||||||
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0),
|
||||||
Anchor = Anchor.BottomLeft,
|
Anchor = Anchor.BottomLeft,
|
||||||
Origin = Anchor.BottomLeft,
|
Origin = Anchor.BottomLeft,
|
||||||
Colour = ColourInfo.GradientHorizontal(Colour4.White.Opacity(0.5f), Colour4.FromHex(@"222A27").Opacity(1)),
|
Colour = ColourInfo.GradientHorizontal(Colour4.White.Opacity(0.5f), Colour4.FromHex(@"222A27").Opacity(1)),
|
||||||
@ -274,7 +273,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Scale = new Vector2(1.1f),
|
Scale = new Vector2(1.1f),
|
||||||
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0),
|
||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
})
|
})
|
||||||
{
|
{
|
||||||
@ -292,7 +291,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
RelativeSizeAxes = Axes.Both,
|
RelativeSizeAxes = Axes.Both,
|
||||||
Colour = Colour4.Black.Opacity(0.5f),
|
Colour = Colour4.Black.Opacity(0.5f),
|
||||||
},
|
},
|
||||||
new RankLabel(rank)
|
new RankLabel(Rank, sheared)
|
||||||
{
|
{
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
@ -314,7 +313,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
{
|
{
|
||||||
flagBadgeAndDateContainer = new FillFlowContainer
|
flagBadgeAndDateContainer = new FillFlowContainer
|
||||||
{
|
{
|
||||||
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0),
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(5),
|
Spacing = new Vector2(5),
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
@ -338,7 +337,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
nameLabel = new TruncatingSpriteText
|
nameLabel = new TruncatingSpriteText
|
||||||
{
|
{
|
||||||
RelativeSizeAxes = Axes.X,
|
RelativeSizeAxes = Axes.X,
|
||||||
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0),
|
||||||
Text = user.Username,
|
Text = user.Username,
|
||||||
Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold)
|
Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold)
|
||||||
}
|
}
|
||||||
@ -354,7 +353,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
Name = @"Statistics container",
|
Name = @"Statistics container",
|
||||||
Padding = new MarginPadding { Right = 40 },
|
Padding = new MarginPadding { Right = 40 },
|
||||||
Spacing = new Vector2(25, 0),
|
Spacing = new Vector2(25, 0),
|
||||||
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0),
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
@ -412,7 +411,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
},
|
},
|
||||||
RankContainer = new Container
|
RankContainer = new Container
|
||||||
{
|
{
|
||||||
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0),
|
||||||
Anchor = Anchor.CentreRight,
|
Anchor = Anchor.CentreRight,
|
||||||
Origin = Anchor.CentreRight,
|
Origin = Anchor.CentreRight,
|
||||||
RelativeSizeAxes = Axes.Y,
|
RelativeSizeAxes = Axes.Y,
|
||||||
@ -470,7 +469,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
UseFullGlyphHeight = false,
|
UseFullGlyphHeight = false,
|
||||||
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0),
|
||||||
Current = scoreManager.GetBindableTotalScoreString(score),
|
Current = scoreManager.GetBindableTotalScoreString(score),
|
||||||
Font = OsuFont.GetFont(size: 30, weight: FontWeight.Light),
|
Font = OsuFont.GetFont(size: 30, weight: FontWeight.Light),
|
||||||
},
|
},
|
||||||
@ -478,7 +477,7 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
{
|
{
|
||||||
Anchor = Anchor.TopRight,
|
Anchor = Anchor.TopRight,
|
||||||
Origin = Anchor.TopRight,
|
Origin = Anchor.TopRight,
|
||||||
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0),
|
||||||
AutoSizeAxes = Axes.Both,
|
AutoSizeAxes = Axes.Both,
|
||||||
Direction = FillDirection.Horizontal,
|
Direction = FillDirection.Horizontal,
|
||||||
Spacing = new Vector2(2f, 0f),
|
Spacing = new Vector2(2f, 0f),
|
||||||
@ -656,14 +655,14 @@ namespace osu.Game.Screens.SelectV2.Leaderboards
|
|||||||
|
|
||||||
private partial class RankLabel : Container, IHasTooltip
|
private partial class RankLabel : Container, IHasTooltip
|
||||||
{
|
{
|
||||||
public RankLabel(int? rank)
|
public RankLabel(int? rank, bool sheared)
|
||||||
{
|
{
|
||||||
if (rank >= 1000)
|
if (rank >= 1000)
|
||||||
TooltipText = $"#{rank:N0}";
|
TooltipText = $"#{rank:N0}";
|
||||||
|
|
||||||
Child = new OsuSpriteText
|
Child = new OsuSpriteText
|
||||||
{
|
{
|
||||||
Shear = new Vector2(-OsuGame.SHEAR, 0),
|
Shear = new Vector2(sheared ? -OsuGame.SHEAR : 0, 0),
|
||||||
Anchor = Anchor.Centre,
|
Anchor = Anchor.Centre,
|
||||||
Origin = Anchor.Centre,
|
Origin = Anchor.Centre,
|
||||||
Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold, italics: true),
|
Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold, italics: true),
|
||||||
|
@ -99,6 +99,54 @@ namespace osu.Game.Tests.Visual.OnlinePlay
|
|||||||
});
|
});
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case IndexPlaylistScoresRequest roomLeaderboardRequest:
|
||||||
|
roomLeaderboardRequest.TriggerSuccess(new IndexedMultiplayerScores
|
||||||
|
{
|
||||||
|
Scores =
|
||||||
|
{
|
||||||
|
new MultiplayerScore
|
||||||
|
{
|
||||||
|
ID = currentScoreId++,
|
||||||
|
Accuracy = 1,
|
||||||
|
Position = 1,
|
||||||
|
EndedAt = DateTimeOffset.Now,
|
||||||
|
Passed = true,
|
||||||
|
Rank = ScoreRank.S,
|
||||||
|
MaxCombo = 1000,
|
||||||
|
TotalScore = 1000000,
|
||||||
|
User = new APIUser { Username = "best user" },
|
||||||
|
Statistics = new Dictionary<HitResult, int>()
|
||||||
|
},
|
||||||
|
new MultiplayerScore
|
||||||
|
{
|
||||||
|
ID = currentScoreId++,
|
||||||
|
Accuracy = 0.7,
|
||||||
|
Position = 2,
|
||||||
|
EndedAt = DateTimeOffset.Now,
|
||||||
|
Passed = true,
|
||||||
|
Rank = ScoreRank.B,
|
||||||
|
MaxCombo = 100,
|
||||||
|
TotalScore = 200000,
|
||||||
|
User = new APIUser { Username = "worst user" },
|
||||||
|
Statistics = new Dictionary<HitResult, int>()
|
||||||
|
},
|
||||||
|
},
|
||||||
|
UserScore = new MultiplayerScore
|
||||||
|
{
|
||||||
|
ID = currentScoreId++,
|
||||||
|
Accuracy = 0.91,
|
||||||
|
Position = 4,
|
||||||
|
EndedAt = DateTimeOffset.Now,
|
||||||
|
Passed = true,
|
||||||
|
Rank = ScoreRank.A,
|
||||||
|
MaxCombo = 100,
|
||||||
|
TotalScore = 800000,
|
||||||
|
User = localUser,
|
||||||
|
Statistics = new Dictionary<HitResult, int>()
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
|
||||||
case PartRoomRequest partRoomRequest:
|
case PartRoomRequest partRoomRequest:
|
||||||
partRoomRequest.TriggerSuccess();
|
partRoomRequest.TriggerSuccess();
|
||||||
return true;
|
return true;
|
||||||
|
Loading…
Reference in New Issue
Block a user