osu/osu.Game/Screens/Ranking/ResultsScreen.cs

264 lines
9.9 KiB
C#
Raw Normal View History

2020-03-17 08:43:16 +00:00
// 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.
2020-03-18 09:28:42 +00:00
using System;
2020-05-26 08:00:41 +00:00
using System.Collections.Generic;
2020-06-17 13:29:00 +00:00
using System.Linq;
2020-03-17 08:43:16 +00:00
using osu.Framework.Allocation;
using osu.Framework.Bindables;
2020-03-17 08:43:16 +00:00
using osu.Framework.Extensions.Color4Extensions;
using osu.Framework.Graphics;
using osu.Framework.Graphics.Containers;
using osu.Framework.Graphics.Shapes;
using osu.Framework.Screens;
2020-06-17 13:29:00 +00:00
using osu.Framework.Utils;
2020-03-17 13:21:16 +00:00
using osu.Game.Graphics.Containers;
2020-03-17 08:43:16 +00:00
using osu.Game.Graphics.UserInterface;
using osu.Game.Online.API;
2020-03-17 08:43:16 +00:00
using osu.Game.Scoring;
using osu.Game.Screens.Backgrounds;
2020-03-17 08:45:25 +00:00
using osu.Game.Screens.Play;
using osu.Game.Screens.Ranking.Statistics;
2020-03-17 08:43:16 +00:00
using osuTK;
namespace osu.Game.Screens.Ranking
{
2020-05-26 08:00:41 +00:00
public abstract class ResultsScreen : OsuScreen
2020-03-17 08:43:16 +00:00
{
protected const float BACKGROUND_BLUR = 20;
private static readonly float screen_height = 768 - TwoLayerButton.SIZE_EXTENDED.Y;
2020-03-17 08:43:16 +00:00
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);
public readonly Bindable<ScoreInfo> SelectedScore = new Bindable<ScoreInfo>();
public readonly ScoreInfo Score;
private readonly bool allowRetry;
2020-03-17 08:45:25 +00:00
[Resolved(CanBeNull = true)]
private Player player { get; set; }
[Resolved]
private IAPIProvider api { get; set; }
2020-06-17 13:29:00 +00:00
private Container<ScorePanel> scorePanelContainer;
private ResultsScrollContainer scrollContainer;
private Container expandedPanelProxyContainer;
2020-03-17 08:43:16 +00:00
private Drawable bottomPanel;
private ScorePanelList panels;
2020-03-17 08:43:16 +00:00
2020-05-26 08:31:50 +00:00
protected ResultsScreen(ScoreInfo score, bool allowRetry = true)
2020-03-17 08:43:16 +00:00
{
2020-03-29 14:50:16 +00:00
Score = score;
this.allowRetry = allowRetry;
SelectedScore.Value = score;
2020-03-17 08:43:16 +00:00
}
[BackgroundDependencyLoader]
private void load()
{
2020-06-17 13:29:00 +00:00
scorePanelContainer = new Container<ScorePanel>
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
RelativeSizeAxes = Axes.Both,
};
FillFlowContainer buttons;
InternalChild = new GridContainer
2020-03-17 08:43:16 +00:00
{
RelativeSizeAxes = Axes.Both,
Content = new[]
2020-03-17 08:43:16 +00:00
{
new Drawable[]
2020-03-17 13:21:16 +00:00
{
2020-06-17 13:29:00 +00:00
new Container
2020-03-17 08:43:16 +00:00
{
2020-06-17 13:29:00 +00:00
RelativeSizeAxes = Axes.Both,
Children = new Drawable[]
{
2020-06-17 13:29:00 +00:00
scorePanelContainer,
scrollContainer = new ResultsScrollContainer
{
2020-06-17 13:29:00 +00:00
Child = new FillFlowContainer
{
RelativeSizeAxes = Axes.X,
2020-06-17 13:29:00 +00:00
AutoSizeAxes = Axes.Y,
Children = new Drawable[]
{
panels = new ScorePanelList(scorePanelContainer)
{
RelativeSizeAxes = Axes.X,
Height = screen_height,
SelectedScore = { BindTarget = SelectedScore }
},
new StatisticsPanel(Score)
{
RelativeSizeAxes = Axes.X,
Height = screen_height,
}
}
}
2020-06-17 13:29:00 +00:00
},
expandedPanelProxyContainer = new Container { RelativeSizeAxes = Axes.Both }
}
}
},
new[]
{
bottomPanel = new Container
2020-03-17 08:43:16 +00:00
{
Anchor = Anchor.BottomLeft,
Origin = Anchor.BottomLeft,
RelativeSizeAxes = Axes.X,
Height = TwoLayerButton.SIZE_EXTENDED.Y,
Alpha = 0,
2020-03-17 08:43:16 +00:00
Children = new Drawable[]
{
new Box
{
RelativeSizeAxes = Axes.Both,
Colour = Color4Extensions.FromHex("#333")
},
buttons = new FillFlowContainer
{
Anchor = Anchor.Centre,
Origin = Anchor.Centre,
AutoSizeAxes = Axes.Both,
Spacing = new Vector2(5),
Direction = FillDirection.Horizontal,
Children = new Drawable[]
{
new ReplayDownloadButton(null)
{
Score = { BindTarget = SelectedScore },
Width = 300
},
}
}
2020-03-17 08:43:16 +00:00
}
}
}
},
RowDimensions = new[]
{
new Dimension(),
new Dimension(GridSizeMode.AutoSize)
2020-03-17 08:43:16 +00:00
}
};
2020-03-17 08:45:25 +00:00
if (Score != null)
panels.AddScore(Score);
2020-03-30 09:56:35 +00:00
if (player != null && allowRetry)
2020-03-17 08:45:25 +00:00
{
2020-03-30 09:56:35 +00:00
buttons.Add(new RetryButton { Width = 300 });
2020-03-30 09:56:35 +00:00
AddInternal(new HotkeyRetryOverlay
{
Action = () =>
2020-03-17 08:45:25 +00:00
{
2020-03-30 09:56:35 +00:00
if (!this.IsCurrentScreen()) return;
2020-03-17 08:45:25 +00:00
2020-03-30 09:56:35 +00:00
player?.Restart();
},
});
2020-03-17 08:45:25 +00:00
}
2020-03-17 08:43:16 +00:00
}
protected override void LoadComplete()
{
base.LoadComplete();
2020-05-26 08:00:41 +00:00
var req = FetchScores(scores => Schedule(() =>
{
2020-05-26 08:00:41 +00:00
foreach (var s in scores)
panels.AddScore(s);
2020-05-26 08:00:41 +00:00
}));
2020-05-26 08:00:41 +00:00
if (req != null)
api.Queue(req);
}
2020-05-26 08:00:41 +00:00
/// <summary>
/// Performs a fetch/refresh of scores to be displayed.
/// </summary>
/// <param name="scoresCallback">A callback which should be called when fetching is completed. Scheduling is not required.</param>
/// <returns>An <see cref="APIRequest"/> responsible for the fetch operation. This will be queued and performed automatically.</returns>
protected virtual APIRequest FetchScores(Action<IEnumerable<ScoreInfo>> scoresCallback) => null;
2020-06-17 13:29:00 +00:00
protected override void UpdateAfterChildren()
{
base.UpdateAfterChildren();
ScorePanel expandedPanel = scorePanelContainer.Single(p => p.State == PanelState.Expanded);
expandedPanel.Tracking = false;
expandedPanel.Anchor = Anchor.Centre;
expandedPanel.Origin = Anchor.Centre;
scorePanelContainer.X = (float)Interpolation.Lerp(0, -DrawWidth / 2 + ScorePanel.EXPANDED_WIDTH / 2f, Math.Clamp(scrollContainer.Current / (screen_height * 0.8f), 0, 1));
if (expandedPanelProxyContainer.Count == 0)
expandedPanelProxyContainer.Add(expandedPanel.CreateProxy());
}
2020-03-17 08:43:16 +00:00
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);
}
2020-03-17 13:21:16 +00:00
[Cached]
2020-03-17 13:21:16 +00:00
private class ResultsScrollContainer : OsuScrollContainer
{
public ResultsScrollContainer()
{
RelativeSizeAxes = Axes.Both;
ScrollbarVisible = false;
}
protected override void OnUserScroll(float value, bool animated = true, double? distanceDecay = default)
2020-03-17 13:21:16 +00:00
{
if (!animated)
{
// If the user is scrolling via mouse drag, follow the mouse 1:1.
base.OnUserScroll(value, false, distanceDecay);
2020-06-17 13:29:00 +00:00
return;
}
float direction = Math.Sign(value - Target);
float target = Target + direction * screen_height;
if (target <= -screen_height / 2 || target >= ScrollableExtent + screen_height / 2)
{
// If the user is already at either extent and scrolling in the clamped direction, we want to follow the default scroll exactly so that the bounces aren't too harsh.
base.OnUserScroll(value, true, distanceDecay);
}
else
{
2020-06-17 13:29:00 +00:00
// Otherwise, scroll one screen in the target direction.
base.OnUserScroll(target, true, distanceDecay);
}
2020-03-17 13:21:16 +00:00
}
}
2020-03-17 08:43:16 +00:00
}
}