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-06-17 15:37:17 +08:00
#nullable disable
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 ;
using osu.Framework.Bindables ;
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 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 ;
2020-06-18 21:21:30 +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]
private BeatmapManager beatmapManager { get ; set ; }
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
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]
private void load ( )
{
Score . BindValueChanged ( populateStatistics , true ) ;
}
2020-06-19 18:12:55 +08:00
private CancellationTokenSource loadCancellation ;
2020-06-18 21:21:30 +08:00
private void populateStatistics ( ValueChangedEvent < ScoreInfo > score )
{
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 ( ) ;
IBeatmap playableBeatmap = null ;
// 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.
Task . Run ( ( ) = >
{
playableBeatmap = beatmapManager . GetWorkingBeatmap ( newScore . BeatmapInfo ) . GetPlayableBeatmap ( newScore . Ruleset , newScore . Mods ) ;
} , loadCancellation . Token ) . ContinueWith ( t = > 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
2022-02-03 11:52:37 +08:00
var statisticRows = newScore . Ruleset . CreateInstance ( ) . CreateStatisticsForScore ( newScore , playableBeatmap ) ;
2022-02-02 13:41:51 +08:00
2022-02-03 11:52:37 +08:00
if ( ! hitEventsAvailable & & statisticRows . SelectMany ( r = > r . Columns ) . All ( c = > c . RequiresHitEvents ) )
{
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
{
2022-02-03 11:52:37 +08:00
FillFlowContainer rows ;
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 ,
Alpha = 0 ,
Children = new [ ]
2020-08-28 02:07:30 +08:00
{
2022-02-03 11:52:37 +08:00
rows = new FillFlowContainer
2020-08-28 02:07:30 +08:00
{
2022-02-03 11:52:37 +08:00
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
Spacing = new Vector2 ( 30 , 15 )
}
}
} ;
2022-02-03 18:00:03 +08:00
bool anyRequiredHitEvents = false ;
2022-02-03 11:52:37 +08:00
foreach ( var row in statisticRows )
{
2022-02-03 18:00:03 +08:00
var columns = row . Columns ;
2022-02-03 11:52:37 +08:00
2022-02-03 18:00:03 +08:00
if ( columns . Length = = 0 )
2022-02-03 11:52:37 +08:00
continue ;
2022-02-03 18:00:03 +08:00
var columnContent = new List < Drawable > ( ) ;
var dimensions = new List < Dimension > ( ) ;
foreach ( var col in columns )
{
if ( ! hitEventsAvailable & & col . RequiresHitEvents )
{
anyRequiredHitEvents = true ;
continue ;
}
columnContent . Add ( new StatisticContainer ( col )
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
} ) ;
dimensions . Add ( col . Dimension ? ? new Dimension ( ) ) ;
}
2022-02-03 11:52:37 +08:00
rows . Add ( new GridContainer
{
Anchor = Anchor . TopCentre ,
Origin = Anchor . TopCentre ,
RelativeSizeAxes = Axes . X ,
AutoSizeAxes = Axes . Y ,
2022-02-03 18:00:03 +08:00
Content = new [ ] { columnContent . ToArray ( ) } ,
ColumnDimensions = dimensions . ToArray ( ) ,
2022-02-03 11:52:37 +08:00
RowDimensions = new [ ] { new Dimension ( GridSizeMode . AutoSize ) }
} ) ;
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
{
rows . Add ( new FillFlowContainer
{
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
{
if ( ! Score . Value . Equals ( newScore ) )
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
2020-06-23 14:21:23 +08:00
protected override bool OnClick ( ClickEvent e )
{
ToggleVisibility ( ) ;
return true ;
}
2020-06-19 20:41:48 +08:00
protected override void PopIn ( ) = > this . FadeIn ( 150 , Easing . OutQuint ) ;
2020-06-18 15:50:45 +08:00
2020-06-19 20:41:48 +08:00
protected override void PopOut ( ) = > this . FadeOut ( 150 , Easing . OutQuint ) ;
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
}
}