2019-01-24 17:43:03 +09: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.
2018-04-13 18:19:50 +09:00
2020-10-14 17:51:03 +09:00
using osu.Framework.Allocation ;
2020-10-14 18:15:06 +09:00
using osu.Framework.Bindables ;
2018-04-13 18:19:50 +09:00
using osu.Framework.Graphics ;
2020-10-14 18:15:06 +09:00
using osu.Framework.Graphics.Containers ;
2020-10-14 17:51:03 +09:00
using osu.Framework.Graphics.Sprites ;
2021-05-03 16:47:47 +09:00
using osu.Game.Rulesets.Scoring ;
2020-10-14 18:15:06 +09:00
using osuTK ;
2018-04-13 18:19:50 +09:00
2022-07-28 08:36:26 +08:00
namespace osu.Game.Skinning
2018-04-13 18:19:50 +09:00
{
/// <summary>
/// Uses the 'x' symbol and has a pop-out effect while rolling over.
/// </summary>
2023-02-15 16:01:26 +09:00
public partial class LegacyComboCounter : CompositeDrawable , ISerialisableDrawable
2018-04-13 18:19:50 +09:00
{
2021-05-18 09:08:56 +03:00
public Bindable < int > Current { get ; } = new BindableInt { MinValue = 0 } ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:34:51 +09:00
private uint scheduledPopOutCurrentId ;
2022-03-28 16:36:37 +02:00
private const double big_pop_out_duration = 300 ;
private const double small_pop_out_duration = 100 ;
2020-10-14 18:34:51 +09:00
private const double fade_out_duration = 100 ;
/// <summary>
/// Duration in milliseconds for the counter roll-up animation for each element.
/// </summary>
private const double rolling_duration = 20 ;
2021-05-18 09:08:56 +03:00
private readonly Drawable popOutCount ;
2020-10-14 18:34:51 +09:00
2021-05-18 09:08:56 +03:00
private readonly Drawable displayedCountSpriteText ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:15:06 +09:00
private int previousValue ;
2020-10-14 18:34:51 +09:00
2020-10-14 18:15:06 +09:00
private int displayedCount ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:34:51 +09:00
private bool isRolling ;
2021-05-18 09:08:56 +03:00
private readonly Container counterContainer ;
2021-05-31 00:07:29 +03:00
/// <summary>
2023-02-15 15:47:41 +09:00
/// Hides the combo counter internally without affecting its <see cref="SerialisedDrawableInfo"/>.
2021-05-31 00:07:29 +03:00
/// </summary>
/// <remarks>
2021-05-31 09:24:26 +03:00
/// This is used for rulesets that provide their own combo counter and don't want this HUD one to be visible,
2021-05-31 00:07:29 +03:00
/// without potentially affecting the user's selected skin.
/// </remarks>
2021-05-31 09:24:26 +03:00
public bool HiddenByRulesetImplementation
2021-05-18 09:08:56 +03:00
{
set = > counterContainer . Alpha = value ? 1 : 0 ;
}
2021-06-08 08:22:35 -04:00
public bool UsesFixedAnchor { get ; set ; }
2021-06-06 23:47:47 -04:00
2020-10-14 17:21:56 +09:00
public LegacyComboCounter ( )
{
2020-10-14 18:15:06 +09:00
AutoSizeAxes = Axes . Both ;
2020-10-14 17:21:56 +09:00
Anchor = Anchor . BottomLeft ;
Origin = Anchor . BottomLeft ;
2020-10-15 16:55:47 +09:00
Margin = new MarginPadding ( 10 ) ;
2020-10-14 18:15:06 +09:00
2022-03-28 19:33:00 +02:00
Scale = new Vector2 ( 1.28f ) ;
2021-05-18 09:08:56 +03:00
2021-12-08 14:56:38 +09:00
InternalChildren = new [ ]
2021-05-18 09:08:56 +03:00
{
2021-12-08 14:56:38 +09:00
counterContainer = new Container
{
AlwaysPresent = true ,
Children = new [ ]
2021-05-18 09:08:56 +03:00
{
2021-12-26 16:44:59 +01:00
popOutCount = new LegacySpriteText ( LegacyFont . Combo )
{
Alpha = 0 ,
Blending = BlendingParameters . Additive ,
Anchor = Anchor . BottomLeft ,
BypassAutoSizeAxes = Axes . Both ,
} ,
2021-12-08 14:56:38 +09:00
displayedCountSpriteText = new LegacySpriteText ( LegacyFont . Combo )
{
Alpha = 0 ,
2022-03-28 16:36:37 +02:00
AlwaysPresent = true ,
Anchor = Anchor . BottomLeft ,
BypassAutoSizeAxes = Axes . Both ,
2021-12-08 14:56:38 +09:00
} ,
}
2021-05-18 09:08:56 +03:00
}
} ;
2020-10-14 17:21:56 +09:00
}
2020-10-14 18:15:06 +09:00
/// <summary>
/// Value shown at the current moment.
/// </summary>
public virtual int DisplayedCount
2020-10-14 17:51:03 +09:00
{
2020-10-14 18:15:06 +09:00
get = > displayedCount ;
2020-10-14 18:34:51 +09:00
private set
2020-10-14 18:15:06 +09:00
{
if ( displayedCount . Equals ( value ) )
return ;
2020-10-14 17:51:03 +09:00
2020-10-14 18:34:51 +09:00
if ( isRolling )
2022-02-01 21:30:28 +01:00
onDisplayedCountRolling ( value ) ;
2020-10-14 18:34:51 +09:00
else if ( displayedCount + 1 = = value )
onDisplayedCountIncrement ( value ) ;
else
onDisplayedCountChange ( value ) ;
2020-10-14 18:15:06 +09:00
2020-10-14 18:34:51 +09:00
displayedCount = value ;
}
2020-10-14 18:15:06 +09:00
}
[BackgroundDependencyLoader]
2022-03-28 18:08:35 +02:00
private void load ( ScoreProcessor scoreProcessor )
2020-10-14 18:15:06 +09:00
{
2021-05-03 16:47:47 +09:00
Current . BindTo ( scoreProcessor . Combo ) ;
2020-10-14 17:51:03 +09:00
}
2018-04-13 18:19:50 +09:00
protected override void LoadComplete ( )
{
base . LoadComplete ( ) ;
2020-10-14 18:34:51 +09:00
( ( IHasText ) displayedCountSpriteText ) . Text = formatCount ( Current . Value ) ;
2022-03-28 16:36:37 +02:00
( ( IHasText ) popOutCount ) . Text = formatCount ( Current . Value ) ;
2021-05-03 20:11:24 +09:00
Current . BindValueChanged ( combo = > updateCount ( combo . NewValue = = 0 ) , true ) ;
2022-03-28 18:08:35 +02:00
updateLayout ( ) ;
2018-04-13 18:19:50 +09:00
}
2022-03-28 16:36:37 +02:00
private void updateLayout ( )
{
const float font_height_ratio = 0.625f ;
const float vertical_offset = 9 ;
displayedCountSpriteText . OriginPosition = new Vector2 ( 0 , font_height_ratio * displayedCountSpriteText . Height + vertical_offset ) ;
displayedCountSpriteText . Position = new Vector2 ( 0 , - ( 1 - font_height_ratio ) * displayedCountSpriteText . Height + vertical_offset ) ;
popOutCount . OriginPosition = new Vector2 ( 3 , font_height_ratio * popOutCount . Height + vertical_offset ) ; // In stable, the bigger pop out scales a bit to the left
popOutCount . Position = new Vector2 ( 0 , - ( 1 - font_height_ratio ) * popOutCount . Height + vertical_offset ) ;
counterContainer . Size = displayedCountSpriteText . Size ;
}
2020-10-14 18:34:51 +09:00
private void updateCount ( bool rolling )
2018-04-13 18:19:50 +09:00
{
2020-10-14 18:34:51 +09:00
int prev = previousValue ;
previousValue = Current . Value ;
if ( ! IsLoaded )
return ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:34:51 +09:00
if ( ! rolling )
{
FinishTransforms ( false , nameof ( DisplayedCount ) ) ;
isRolling = false ;
DisplayedCount = prev ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:34:51 +09:00
if ( prev + 1 = = Current . Value )
onCountIncrement ( prev , Current . Value ) ;
else
2022-02-01 21:30:28 +01:00
onCountChange ( Current . Value ) ;
2020-10-14 18:34:51 +09:00
}
else
{
onCountRolling ( displayedCount , Current . Value ) ;
isRolling = true ;
}
2018-04-13 18:19:50 +09:00
}
2020-10-14 18:34:51 +09:00
private void transformPopOut ( int newValue )
2018-04-13 18:19:50 +09:00
{
2020-10-14 18:34:51 +09:00
( ( IHasText ) popOutCount ) . Text = formatCount ( newValue ) ;
2022-03-29 13:18:31 +09:00
popOutCount . ScaleTo ( 1.56f )
. ScaleTo ( 1 , big_pop_out_duration ) ;
2020-10-14 18:34:51 +09:00
2022-03-29 13:18:31 +09:00
popOutCount . FadeTo ( 0.6f )
. FadeOut ( big_pop_out_duration ) ;
2018-04-13 18:19:50 +09:00
}
2020-10-14 18:34:51 +09:00
private void transformNoPopOut ( int newValue )
2018-04-13 18:19:50 +09:00
{
2020-10-14 18:34:51 +09:00
( ( IHasText ) displayedCountSpriteText ) . Text = formatCount ( newValue ) ;
2022-03-28 16:36:37 +02:00
counterContainer . Size = displayedCountSpriteText . Size ;
2020-10-14 18:34:51 +09:00
displayedCountSpriteText . ScaleTo ( 1 ) ;
2018-04-13 18:19:50 +09:00
}
2020-10-14 18:34:51 +09:00
private void transformPopOutSmall ( int newValue )
2018-04-13 18:19:50 +09:00
{
2020-10-14 18:34:51 +09:00
( ( IHasText ) displayedCountSpriteText ) . Text = formatCount ( newValue ) ;
2022-03-28 16:36:37 +02:00
counterContainer . Size = displayedCountSpriteText . Size ;
2022-03-28 19:57:59 +02:00
displayedCountSpriteText . ScaleTo ( 1 ) . Then ( )
2022-03-30 00:48:59 +02:00
. ScaleTo ( 1.1f , small_pop_out_duration / 2 , Easing . In ) . Then ( )
. ScaleTo ( 1 , small_pop_out_duration / 2 , Easing . Out ) ;
2018-04-13 18:19:50 +09:00
}
2020-10-14 18:34:51 +09:00
private void scheduledPopOutSmall ( uint id )
2018-04-13 18:19:50 +09:00
{
// Too late; scheduled task invalidated
2020-10-14 18:34:51 +09:00
if ( id ! = scheduledPopOutCurrentId )
2018-04-13 18:19:50 +09:00
return ;
DisplayedCount + + ;
}
2020-10-14 18:34:51 +09:00
private void onCountIncrement ( int currentValue , int newValue )
2018-04-13 18:19:50 +09:00
{
2020-10-14 18:34:51 +09:00
scheduledPopOutCurrentId + + ;
2018-04-13 18:19:50 +09:00
if ( DisplayedCount < currentValue )
DisplayedCount + + ;
2020-10-14 18:34:51 +09:00
displayedCountSpriteText . Show ( ) ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:34:51 +09:00
transformPopOut ( newValue ) ;
uint newTaskId = scheduledPopOutCurrentId ;
2018-04-13 18:19:50 +09:00
Scheduler . AddDelayed ( delegate
{
2020-10-14 18:34:51 +09:00
scheduledPopOutSmall ( newTaskId ) ;
2022-03-28 16:36:37 +02:00
} , big_pop_out_duration - 140 ) ;
2018-04-13 18:19:50 +09:00
}
2020-10-14 18:34:51 +09:00
private void onCountRolling ( int currentValue , int newValue )
2018-04-13 18:19:50 +09:00
{
2020-10-14 18:34:51 +09:00
scheduledPopOutCurrentId + + ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:34:51 +09:00
// Hides displayed count if was increasing from 0 to 1 but didn't finish
if ( currentValue = = 0 & & newValue = = 0 )
displayedCountSpriteText . FadeOut ( fade_out_duration ) ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:34:51 +09:00
transformRoll ( currentValue , newValue ) ;
2018-04-13 18:19:50 +09:00
}
2022-02-01 21:30:28 +01:00
private void onCountChange ( int newValue )
2018-04-13 18:19:50 +09:00
{
2020-10-14 18:34:51 +09:00
scheduledPopOutCurrentId + + ;
2018-04-13 18:19:50 +09:00
if ( newValue = = 0 )
2020-10-14 18:34:51 +09:00
displayedCountSpriteText . FadeOut ( ) ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:34:51 +09:00
DisplayedCount = newValue ;
2018-04-13 18:19:50 +09:00
}
2022-02-01 21:30:28 +01:00
private void onDisplayedCountRolling ( int newValue )
2018-04-13 18:19:50 +09:00
{
2020-10-14 18:34:51 +09:00
if ( newValue = = 0 )
displayedCountSpriteText . FadeOut ( fade_out_duration ) ;
else
displayedCountSpriteText . Show ( ) ;
2018-04-13 18:19:50 +09:00
2020-10-14 18:34:51 +09:00
transformNoPopOut ( newValue ) ;
2018-04-13 18:19:50 +09:00
}
2020-10-14 18:34:51 +09:00
private void onDisplayedCountChange ( int newValue )
2018-04-13 18:19:50 +09:00
{
2020-10-14 18:34:51 +09:00
displayedCountSpriteText . FadeTo ( newValue = = 0 ? 0 : 1 ) ;
transformNoPopOut ( newValue ) ;
2018-04-13 18:19:50 +09:00
}
2020-10-14 18:15:06 +09:00
2020-10-14 18:34:51 +09:00
private void onDisplayedCountIncrement ( int newValue )
2020-10-14 18:15:06 +09:00
{
2020-10-14 18:34:51 +09:00
displayedCountSpriteText . Show ( ) ;
transformPopOutSmall ( newValue ) ;
2020-10-14 18:15:06 +09:00
}
2020-10-14 18:34:51 +09:00
private void transformRoll ( int currentValue , int newValue ) = >
2021-07-05 23:52:39 +08:00
this . TransformTo ( nameof ( DisplayedCount ) , newValue , getProportionalDuration ( currentValue , newValue ) ) ;
2020-10-14 18:15:06 +09:00
2020-10-14 18:34:51 +09:00
private string formatCount ( int count ) = > $@"{count}x" ;
2020-10-14 18:15:06 +09:00
private double getProportionalDuration ( int currentValue , int newValue )
{
double difference = currentValue > newValue ? currentValue - newValue : newValue - currentValue ;
2020-10-14 18:34:51 +09:00
return difference * rolling_duration ;
2020-10-14 18:15:06 +09:00
}
2018-04-13 18:19:50 +09:00
}
}