2020-06-15 21:45:18 +08: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.
2022-02-03 18:00:03 +08:00
using System.Collections.Generic ;
2020-06-19 19:31:52 +08:00
using System.Linq ;
2020-06-19 18:12:55 +08:00
using System.Threading ;
2020-06-22 17:38:41 +08:00
using System.Threading.Tasks ;
2020-06-18 21:21:30 +08:00
using osu.Framework.Allocation ;
2022-07-22 18:06:31 +08:00
using osu.Framework.Audio ;
using osu.Framework.Audio.Sample ;
2020-06-18 21:21:30 +08:00
using osu.Framework.Bindables ;
2022-07-26 14:12:22 +08:00
using osu.Framework.Extensions ;
2020-06-15 21:45:18 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
2020-06-23 14:21:23 +08:00
using osu.Framework.Input.Events ;
2020-06-22 17:38:41 +08:00
using osu.Game.Beatmaps ;
2022-02-02 17:20:22 +08:00
using osu.Game.Graphics.Containers ;
2020-06-19 18:12:55 +08:00
using osu.Game.Graphics.UserInterface ;
2020-06-18 21:21:30 +08:00
using osu.Game.Online.Placeholders ;
2020-06-15 21:45:18 +08:00
using osu.Game.Scoring ;
using osuTK ;
namespace osu.Game.Screens.Ranking.Statistics
{
2020-06-18 15:50:45 +08:00
public partial class StatisticsPanel : VisibilityContainer
2020-06-15 21:45:18 +08:00
{
2020-06-18 16:06:05 +08:00
public const float SIDE_PADDING = 30 ;
2024-05-14 19:01:26 +08:00
public readonly Bindable < ScoreInfo ? > Score = new Bindable < ScoreInfo ? > ( ) ;
2020-06-18 15:50:45 +08:00
2020-06-18 21:21:30 +08:00
protected override bool StartHidden = > true ;
2020-06-15 21:45:18 +08:00
2020-06-22 17:38:41 +08:00
[Resolved]
2024-05-14 19:01:26 +08:00
private BeatmapManager beatmapManager { get ; set ; } = null ! ;
2020-06-22 17:38:41 +08:00
2020-06-18 21:21:30 +08:00
private readonly Container content ;
2020-06-19 18:12:55 +08:00
private readonly LoadingSpinner spinner ;
2020-06-15 21:45:18 +08:00
2022-07-22 18:06:31 +08:00
private bool wasOpened ;
2024-05-14 19:01:26 +08:00
private Sample ? popInSample ;
private Sample ? popOutSample ;
private CancellationTokenSource ? loadCancellation ;
2022-07-22 18:06:31 +08:00
2020-06-18 21:21:30 +08:00
public StatisticsPanel ( )
{
2020-06-18 15:50:45 +08:00
InternalChild = new Container
2020-06-15 21:45:18 +08:00
{
2020-06-18 15:50:45 +08:00
RelativeSizeAxes = Axes . Both ,
Padding = new MarginPadding
2020-06-15 21:45:18 +08:00
{
2020-06-18 16:06:05 +08:00
Left = ScorePanel . EXPANDED_WIDTH + SIDE_PADDING * 3 ,
Right = SIDE_PADDING ,
Top = SIDE_PADDING ,
Bottom = 50 // Approximate padding to the bottom of the score panel.
2020-06-15 21:45:18 +08:00
} ,
2020-06-19 18:12:55 +08:00
Children = new Drawable [ ]
{
content = new Container { RelativeSizeAxes = Axes . Both } ,
spinner = new LoadingSpinner ( )
}
2020-06-18 21:21:30 +08:00
} ;
}
[BackgroundDependencyLoader]
2022-07-22 18:06:31 +08:00
private void load ( AudioManager audio )
2020-06-18 21:21:30 +08:00
{
Score . BindValueChanged ( populateStatistics , true ) ;
2022-07-22 18:06:31 +08:00
popInSample = audio . Samples . Get ( @"Results/statistics-panel-pop-in" ) ;
popOutSample = audio . Samples . Get ( @"Results/statistics-panel-pop-out" ) ;
2020-06-18 21:21:30 +08:00
}
2024-05-14 19:01:26 +08:00
private void populateStatistics ( ValueChangedEvent < ScoreInfo ? > score )
2020-06-18 21:21:30 +08:00
{
2020-06-19 18:12:55 +08:00
loadCancellation ? . Cancel ( ) ;
2020-06-22 19:20:42 +08:00
loadCancellation = null ;
2020-06-19 18:12:55 +08:00
2020-06-18 21:21:30 +08:00
foreach ( var child in content )
child . FadeOut ( 150 ) . Expire ( ) ;
2021-11-08 11:35:30 +08:00
spinner . Hide ( ) ;
2020-06-18 21:21:30 +08:00
var newScore = score . NewValue ;
2020-06-19 21:47:55 +08:00
if ( newScore = = null )
return ;
2022-02-02 13:41:51 +08:00
spinner . Show ( ) ;
var localCancellationSource = loadCancellation = new CancellationTokenSource ( ) ;
2022-07-26 14:12:22 +08:00
var workingBeatmap = beatmapManager . GetWorkingBeatmap ( newScore . BeatmapInfo ) ;
2022-02-02 13:41:51 +08:00
// Todo: The placement of this is temporary. Eventually we'll both generate the playable beatmap _and_ run through it in a background task to generate the hit events.
2022-07-26 14:12:22 +08:00
Task . Run ( ( ) = > workingBeatmap . GetPlayableBeatmap ( newScore . Ruleset , newScore . Mods ) , loadCancellation . Token ) . ContinueWith ( task = > Schedule ( ( ) = >
2020-09-24 11:39:08 +08:00
{
2022-02-02 13:41:51 +08:00
bool hitEventsAvailable = newScore . HitEvents . Count ! = 0 ;
2022-02-03 11:52:37 +08:00
Container < Drawable > container ;
2022-02-02 13:41:51 +08:00
2023-06-01 13:35:14 +08:00
var statisticItems = CreateStatisticItems ( newScore , task . GetResultSafely ( ) ) ;
2022-02-02 13:41:51 +08:00
2023-06-01 13:35:14 +08:00
if ( ! hitEventsAvailable & & statisticItems . All ( c = > c . RequiresHitEvents ) )
2022-02-03 11:52:37 +08:00
{
container = new FillFlowContainer
2020-09-24 11:21:08 +08:00
{
2022-02-03 11:52:37 +08:00
RelativeSizeAxes = Axes . Both ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Direction = FillDirection . Vertical ,
Children = new Drawable [ ]
2020-09-24 11:21:08 +08:00
{
2022-02-03 11:52:37 +08:00
new MessagePlaceholder ( "Extended statistics are only available after watching a replay!" ) ,
new ReplayDownloadButton ( newScore )
2022-02-02 13:41:51 +08:00
{
2022-02-03 11:52:37 +08:00
Scale = new Vector2 ( 1.5f ) ,
2022-02-02 13:41:51 +08:00
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2022-02-03 11:52:37 +08:00
} ,
}
} ;
2022-02-02 13:41:51 +08:00
}
2022-02-03 11:52:37 +08:00
else
2020-06-22 17:38:41 +08:00
{
2023-06-28 17:17:57 +08:00
FillFlowContainer flow ;
2022-02-03 11:52:37 +08:00
container = new OsuScrollContainer ( Direction . Vertical )
2020-06-18 21:21:30 +08:00
{
2022-02-03 11:52:37 +08:00
RelativeSizeAxes = Axes . Both ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2023-07-13 18:00:03 +08:00
Masking = false ,
ScrollbarOverlapsContent = false ,
2022-02-03 11:52:37 +08:00
Alpha = 0 ,
Children = new [ ]
2020-08-28 02:07:30 +08:00
{
2023-06-28 17:17:57 +08:00
flow = new FillFlowContainer
2020-08-28 02:07:30 +08:00
{
2022-02-03 11:52:37 +08:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
2023-06-28 17:17:57 +08:00
Spacing = new Vector2 ( 30 , 15 ) ,
Direction = FillDirection . Full ,
2022-02-03 11:52:37 +08:00
}
}
} ;
2022-02-03 18:00:03 +08:00
bool anyRequiredHitEvents = false ;
2022-02-03 11:52:37 +08:00
2023-06-01 13:35:14 +08:00
foreach ( var item in statisticItems )
2022-02-03 11:52:37 +08:00
{
2023-06-01 13:35:14 +08:00
if ( ! hitEventsAvailable & & item . RequiresHitEvents )
2022-02-03 18:00:03 +08:00
{
2023-06-01 13:35:14 +08:00
anyRequiredHitEvents = true ;
continue ;
}
2022-02-03 18:00:03 +08:00
2023-07-13 17:55:54 +08:00
flow . Add ( new StatisticItemContainer ( item )
2023-06-01 13:35:14 +08:00
{
2023-07-14 02:00:25 +08:00
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
2023-06-01 13:35:14 +08:00
} ) ;
2022-02-02 17:20:22 +08:00
}
2022-02-03 11:52:37 +08:00
2022-02-03 18:00:03 +08:00
if ( anyRequiredHitEvents )
2022-02-02 17:20:22 +08:00
{
2023-06-28 17:17:57 +08:00
flow . Add ( new FillFlowContainer
2022-02-02 17:20:22 +08:00
{
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Direction = FillDirection . Vertical ,
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
Children = new Drawable [ ]
{
new MessagePlaceholder ( "More statistics available after watching a replay!" ) ,
new ReplayDownloadButton ( newScore )
{
Scale = new Vector2 ( 1.5f ) ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
} ,
}
} ) ;
}
2022-02-02 13:41:51 +08:00
}
2020-06-22 17:38:41 +08:00
2022-02-02 17:20:22 +08:00
LoadComponentAsync ( container , d = >
2022-02-02 13:41:51 +08:00
{
2024-05-14 19:01:26 +08:00
if ( Score . Value ? . Equals ( newScore ) ! = true )
2022-02-02 13:41:51 +08:00
return ;
spinner . Hide ( ) ;
content . Add ( d ) ;
d . FadeIn ( 250 , Easing . OutQuint ) ;
} , localCancellationSource . Token ) ;
} ) , localCancellationSource . Token ) ;
2020-06-15 21:45:18 +08:00
}
2020-06-18 15:50:45 +08:00
2022-12-24 17:51:09 +08:00
/// <summary>
2023-06-01 13:35:14 +08:00
/// Creates the <see cref="StatisticItem"/>s to be displayed in this panel for a given <paramref name="newScore"/>.
2022-12-24 17:51:09 +08:00
/// </summary>
/// <param name="newScore">The score to create the rows for.</param>
/// <param name="playableBeatmap">The beatmap on which the score was set.</param>
2023-06-01 13:35:14 +08:00
protected virtual ICollection < StatisticItem > CreateStatisticItems ( ScoreInfo newScore , IBeatmap playableBeatmap )
2022-12-24 17:51:09 +08:00
= > newScore . Ruleset . CreateInstance ( ) . CreateStatisticsForScore ( newScore , playableBeatmap ) ;
2020-06-23 14:21:23 +08:00
protected override bool OnClick ( ClickEvent e )
{
ToggleVisibility ( ) ;
return true ;
}
2022-07-22 18:06:31 +08:00
protected override void PopIn ( )
{
2023-07-07 16:32:22 +08:00
this . FadeIn ( 350 , Easing . OutQuint ) ;
2022-07-22 18:06:31 +08:00
popInSample ? . Play ( ) ;
wasOpened = true ;
}
protected override void PopOut ( )
{
2023-07-07 16:32:22 +08:00
this . FadeOut ( 250 , Easing . OutQuint ) ;
2020-06-18 15:50:45 +08:00
2022-07-22 18:06:31 +08:00
if ( wasOpened )
popOutSample ? . Play ( ) ;
}
2020-06-22 19:20:42 +08:00
protected override void Dispose ( bool isDisposing )
{
loadCancellation ? . Cancel ( ) ;
base . Dispose ( isDisposing ) ;
}
2020-06-15 21:45:18 +08:00
}
}