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