// Copyright (c) ppy Pty Ltd . 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.Graphics; using osu.Game.Configuration; using osu.Game.Online.API.Requests; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; using osu.Game.Screens.Select; using osu.Game.Users; namespace osu.Game.Screens.Play.HUD { public class SoloGameplayLeaderboard : GameplayLeaderboard { private const int duration = 100; private readonly Bindable configVisibility = new Bindable(); private readonly Bindable scoreSource = new Bindable(); private readonly IUser trackingUser; public readonly IBindableList Scores = new BindableList(); // hold references to ensure bindables are updated. private readonly List> scoreBindables = new List>(); [Resolved] private ScoreProcessor scoreProcessor { get; set; } = null!; [Resolved] private ScoreManager scoreManager { get; set; } = null!; /// /// Whether the leaderboard should be visible regardless of the configuration value. /// This is true by default, but can be changed. /// public readonly Bindable AlwaysVisible = new Bindable(true); public SoloGameplayLeaderboard(IUser trackingUser) { this.trackingUser = trackingUser; } [BackgroundDependencyLoader] private void load(OsuConfigManager config) { config.BindWith(OsuSetting.GameplayLeaderboard, configVisibility); config.BindWith(OsuSetting.BeatmapDetailTab, scoreSource); } protected override void LoadComplete() { base.LoadComplete(); Scores.BindCollectionChanged((_, _) => Scheduler.AddOnce(showScores), true); // Alpha will be updated via `updateVisibility` below. Alpha = 0; AlwaysVisible.BindValueChanged(_ => updateVisibility()); configVisibility.BindValueChanged(_ => updateVisibility(), true); } private void showScores() { Clear(); scoreBindables.Clear(); if (!Scores.Any()) return; foreach (var s in Scores) { var score = Add(s.User, false); var bindableTotal = scoreManager.GetBindableTotalScore(s); // Direct binding not possible due to differing types (see https://github.com/ppy/osu/issues/20298). bindableTotal.BindValueChanged(total => score.TotalScore.Value = total.NewValue, true); scoreBindables.Add(bindableTotal); score.Accuracy.Value = s.Accuracy; score.Combo.Value = s.MaxCombo; score.DisplayOrder.Value = s.OnlineID > 0 ? s.OnlineID : s.Date.ToUnixTimeSeconds(); } ILeaderboardScore local = Add(trackingUser, true); local.TotalScore.BindTarget = scoreProcessor.TotalScore; local.Accuracy.BindTarget = scoreProcessor.Accuracy; local.Combo.BindTarget = scoreProcessor.HighestCombo; // Local score should always show lower than any existing scores in cases of ties. local.DisplayOrder.Value = long.MaxValue; } protected override bool CheckValidScorePosition(int i) { // change displayed position to '-' when there are 50 already submitted scores and tracked score is last if (scoreSource.Value != PlayBeatmapDetailArea.TabType.Local) { if (i == Flow.Count && Flow.Count > GetScoresRequest.MAX_SCORES_PER_REQUEST) return false; } return base.CheckValidScorePosition(i); } private void updateVisibility() => this.FadeTo(AlwaysVisible.Value || configVisibility.Value ? 1 : 0, duration); } }