2022-08-28 01:11:38 +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-08-30 03:48:27 +08:00
using System ;
2022-08-28 01:11:38 +08:00
using osu.Framework.Allocation ;
2022-08-30 03:48:27 +08:00
using osu.Framework.Bindables ;
2022-09-11 04:18:48 +08:00
using osu.Framework.Extensions.LocalisationExtensions ;
2022-08-28 01:11:38 +08:00
using osu.Framework.Graphics ;
using osu.Framework.Graphics.Containers ;
using osu.Framework.Graphics.Shapes ;
2022-08-30 03:48:27 +08:00
using osu.Framework.Graphics.UserInterface ;
2022-08-28 01:11:38 +08:00
using osu.Framework.Localisation ;
using osu.Game.Graphics ;
using osu.Game.Graphics.Sprites ;
2022-09-11 04:18:48 +08:00
using osu.Game.Graphics.UserInterface ;
2022-08-28 01:11:38 +08:00
using osu.Game.Rulesets.Mods ;
using osuTK ;
namespace osu.Game.Overlays.Mods
{
/// <summary>
2023-09-12 16:15:16 +08:00
/// Base class for displays of singular counters. Not to be confused with <see cref="BeatmapAttributesDisplay"/> which aggregates multiple attributes.
2022-08-28 01:11:38 +08:00
/// </summary>
2023-09-12 16:15:16 +08:00
public abstract partial class ModCounterDisplay : Container , IHasCurrentValue < double >
2022-08-28 01:11:38 +08:00
{
public const float HEIGHT = 42 ;
private const float transition_duration = 200 ;
private readonly Box contentBackground ;
private readonly Box labelBackground ;
2022-09-11 04:18:48 +08:00
private readonly FillFlowContainer content ;
2022-08-28 01:11:38 +08:00
2022-09-10 13:21:38 +08:00
public Bindable < double > Current
2022-08-30 03:48:27 +08:00
{
get = > current . Current ;
set = > current . Current = value ;
}
2022-09-10 13:21:38 +08:00
private readonly BindableWithCurrent < double > current = new BindableWithCurrent < double > ( ) ;
2022-08-30 03:48:27 +08:00
2022-08-28 01:11:38 +08:00
[Resolved]
private OsuColour colours { get ; set ; } = null ! ;
[Resolved]
private OverlayColourProvider colourProvider { get ; set ; } = null ! ;
/// <summary>
/// Text to display in the left area of the display.
/// </summary>
protected abstract LocalisableString Label { get ; }
2022-08-30 03:08:43 +08:00
protected virtual float ValueAreaWidth = > 56 ;
2022-09-12 20:51:18 +08:00
protected virtual string CounterFormat = > @"N0" ;
2022-09-11 04:18:48 +08:00
2022-08-28 01:11:38 +08:00
protected override Container < Drawable > Content = > content ;
2022-09-11 04:20:28 +08:00
protected readonly RollingCounter < double > Counter ;
2023-09-12 16:15:16 +08:00
protected ModCounterDisplay ( )
2022-08-28 01:11:38 +08:00
{
Height = HEIGHT ;
AutoSizeAxes = Axes . X ;
InternalChild = new InputBlockingContainer
{
RelativeSizeAxes = Axes . Y ,
AutoSizeAxes = Axes . X ,
Masking = true ,
CornerRadius = ModSelectPanel . CORNER_RADIUS ,
Shear = new Vector2 ( ShearedOverlayContainer . SHEAR , 0 ) ,
Children = new Drawable [ ]
{
contentBackground = new Box
{
Anchor = Anchor . CentreRight ,
Origin = Anchor . CentreRight ,
RelativeSizeAxes = Axes . Y ,
2022-08-30 03:08:43 +08:00
Width = ValueAreaWidth + ModSelectPanel . CORNER_RADIUS
2022-08-28 01:11:38 +08:00
} ,
new GridContainer
{
RelativeSizeAxes = Axes . Y ,
AutoSizeAxes = Axes . X ,
ColumnDimensions = new [ ]
{
new Dimension ( GridSizeMode . AutoSize ) ,
2022-08-30 03:08:43 +08:00
new Dimension ( GridSizeMode . Absolute , ValueAreaWidth )
2022-08-28 01:11:38 +08:00
} ,
Content = new [ ]
{
new Drawable [ ]
{
new Container
{
RelativeSizeAxes = Axes . Y ,
AutoSizeAxes = Axes . X ,
Masking = true ,
CornerRadius = ModSelectPanel . CORNER_RADIUS ,
Children = new Drawable [ ]
{
labelBackground = new Box
{
RelativeSizeAxes = Axes . Both
} ,
new OsuSpriteText
{
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
Margin = new MarginPadding { Horizontal = 18 } ,
Shear = new Vector2 ( - ShearedOverlayContainer . SHEAR , 0 ) ,
Text = Label ,
Font = OsuFont . Default . With ( size : 17 , weight : FontWeight . SemiBold )
}
}
} ,
2022-09-11 04:18:48 +08:00
content = new FillFlowContainer
2022-08-28 01:11:38 +08:00
{
AutoSizeAxes = Axes . Both ,
Anchor = Anchor . Centre ,
Origin = Anchor . Centre ,
2022-09-11 04:18:48 +08:00
Direction = FillDirection . Horizontal ,
Shear = new Vector2 ( - ShearedOverlayContainer . SHEAR , 0 ) ,
Spacing = new Vector2 ( 2 , 0 ) ,
2022-09-11 04:20:28 +08:00
Child = Counter = new EffectCounter ( CounterFormat )
2022-09-11 04:18:48 +08:00
{
Anchor = Anchor . CentreLeft ,
Origin = Anchor . CentreLeft ,
Current = { BindTarget = Current }
}
2022-08-28 01:11:38 +08:00
}
}
}
}
}
} ;
}
[BackgroundDependencyLoader]
private void load ( )
{
labelBackground . Colour = colourProvider . Background4 ;
}
2022-08-30 03:48:27 +08:00
protected override void LoadComplete ( )
{
Current . BindValueChanged ( e = >
{
2022-09-12 20:51:18 +08:00
var effect = CalculateEffectForComparison ( e . NewValue . CompareTo ( Current . Default ) ) ;
2022-08-30 03:48:27 +08:00
setColours ( effect ) ;
} , true ) ;
}
2022-08-28 01:11:38 +08:00
/// <summary>
/// Fades colours of text and its background according to displayed value.
/// </summary>
2022-08-30 03:48:27 +08:00
/// <param name="effect">Effect of the value.</param>
private void setColours ( ModEffect effect )
2022-08-28 01:11:38 +08:00
{
2022-08-30 03:48:27 +08:00
switch ( effect )
2022-08-28 01:11:38 +08:00
{
2022-08-30 03:48:27 +08:00
case ModEffect . NotChanged :
contentBackground . FadeColour ( colourProvider . Background3 , transition_duration , Easing . OutQuint ) ;
content . FadeColour ( Colour4 . White , transition_duration , Easing . OutQuint ) ;
break ;
case ModEffect . DifficultyReduction :
contentBackground . FadeColour ( colours . ForModType ( ModType . DifficultyReduction ) , transition_duration , Easing . OutQuint ) ;
content . FadeColour ( colourProvider . Background5 , transition_duration , Easing . OutQuint ) ;
break ;
case ModEffect . DifficultyIncrease :
contentBackground . FadeColour ( colours . ForModType ( ModType . DifficultyIncrease ) , transition_duration , Easing . OutQuint ) ;
content . FadeColour ( colourProvider . Background5 , transition_duration , Easing . OutQuint ) ;
break ;
default :
throw new ArgumentOutOfRangeException ( nameof ( effect ) ) ;
2022-08-28 01:11:38 +08:00
}
}
2022-08-30 03:48:27 +08:00
/// <summary>
/// Converts signed integer into <see cref="ModEffect"/>. Negative values are counted as difficulty reduction, positive as increase.
/// </summary>
2022-09-12 20:51:18 +08:00
/// <param name="comparison">Value to convert. Will arrive from comparison between <see cref="Current"/> bindable once it changes and it's <see cref="Bindable{T}.Default"/>.</param>
/// <returns>Effect of the value.</returns>
protected virtual ModEffect CalculateEffectForComparison ( int comparison )
2022-08-30 03:48:27 +08:00
{
if ( comparison = = 0 )
return ModEffect . NotChanged ;
if ( comparison < 0 )
return ModEffect . DifficultyReduction ;
return ModEffect . DifficultyIncrease ;
}
protected enum ModEffect
{
NotChanged ,
DifficultyReduction ,
DifficultyIncrease
}
2022-09-11 04:18:48 +08:00
private partial class EffectCounter : RollingCounter < double >
{
private readonly string? format ;
public EffectCounter ( string? format )
{
this . format = format ;
}
protected override double RollingDuration = > 500 ;
protected override LocalisableString FormatCount ( double count ) = > count . ToLocalisableString ( format ) ;
protected override OsuSpriteText CreateSpriteText ( ) = > new OsuSpriteText
{
Font = OsuFont . Default . With ( size : 17 , weight : FontWeight . SemiBold )
} ;
}
2022-08-28 01:11:38 +08:00
}
}