2017-02-07 12:59:30 +08:00
// Copyright (c) 2007-2017 ppy Pty Ltd <contact@ppy.sh>.
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
2016-12-03 20:56:35 +08:00
2017-02-13 03:38:05 +08:00
using OpenTK ;
2016-11-28 15:52:57 +08:00
using osu.Framework.Graphics ;
2017-04-18 15:05:58 +08:00
using osu.Game.Rulesets.Objects.Drawables ;
using osu.Game.Rulesets.Osu.Objects.Drawables.Pieces ;
2017-02-13 03:38:05 +08:00
using System.Collections.Generic ;
using System.Linq ;
2017-02-16 16:02:36 +08:00
using osu.Framework.Graphics.Containers ;
2017-09-06 16:02:13 +08:00
using osu.Game.Rulesets.Osu.Judgements ;
2016-11-17 16:20:51 +08:00
2017-04-18 15:05:58 +08:00
namespace osu.Game.Rulesets.Osu.Objects.Drawables
2016-11-17 16:20:51 +08:00
{
2017-02-14 09:22:28 +08:00
public class DrawableSlider : DrawableOsuHitObject , IDrawableHitObjectWithProxiedApproach
2016-11-17 16:20:51 +08:00
{
2017-03-23 12:41:50 +08:00
private readonly Slider slider ;
2016-11-28 16:05:30 +08:00
2017-03-23 12:41:50 +08:00
private readonly DrawableHitCircle initialCircle ;
2016-12-06 17:54:32 +08:00
2017-03-23 12:41:50 +08:00
private readonly List < ISliderProgress > components = new List < ISliderProgress > ( ) ;
2016-11-28 11:40:24 +08:00
2017-03-23 12:41:50 +08:00
private readonly Container < DrawableSliderTick > ticks ;
2017-02-16 16:02:36 +08:00
2017-03-23 12:41:50 +08:00
private readonly SliderBody body ;
private readonly SliderBall ball ;
2016-12-06 23:02:27 +08:00
2017-03-23 12:41:50 +08:00
private readonly SliderBouncer bouncer2 ;
2016-12-06 23:02:27 +08:00
2016-11-28 11:15:25 +08:00
public DrawableSlider ( Slider s ) : base ( s )
2016-11-17 16:20:51 +08:00
{
2017-03-09 14:52:40 +08:00
SliderBouncer bouncer1 ;
2016-11-28 11:40:24 +08:00
slider = s ;
2016-11-27 22:36:31 +08:00
2016-11-28 11:40:24 +08:00
Children = new Drawable [ ]
2016-11-28 11:15:25 +08:00
{
2016-12-06 17:54:32 +08:00
body = new SliderBody ( s )
2016-11-29 02:04:03 +08:00
{
2017-03-23 16:11:51 +08:00
AccentColour = AccentColour ,
2017-02-09 15:29:21 +08:00
Position = s . StackedPosition ,
2017-02-06 21:17:29 +08:00
PathWidth = s . Scale * 64 ,
2016-12-11 17:11:22 +08:00
} ,
2017-02-16 16:02:36 +08:00
ticks = new Container < DrawableSliderTick > ( ) ,
2016-12-11 17:11:22 +08:00
bouncer1 = new SliderBouncer ( s , false )
{
Position = s . Curve . PositionAt ( 1 ) ,
Scale = new Vector2 ( s . Scale ) ,
} ,
bouncer2 = new SliderBouncer ( s , true )
{
2017-02-09 15:29:21 +08:00
Position = s . StackedPosition ,
2016-12-11 17:11:22 +08:00
Scale = new Vector2 ( s . Scale ) ,
} ,
ball = new SliderBall ( s )
{
Scale = new Vector2 ( s . Scale ) ,
2017-03-23 14:37:16 +08:00
AccentColour = AccentColour
2016-11-29 02:04:03 +08:00
} ,
2016-12-06 23:02:27 +08:00
initialCircle = new DrawableHitCircle ( new HitCircle
2016-11-28 11:40:24 +08:00
{
2017-02-15 22:23:55 +08:00
//todo: avoid creating this temporary HitCircle.
2016-11-28 11:40:24 +08:00
StartTime = s . StartTime ,
2017-02-09 15:29:21 +08:00
Position = s . StackedPosition ,
2017-02-15 22:23:55 +08:00
ComboIndex = s . ComboIndex ,
2016-12-11 17:11:22 +08:00
Scale = s . Scale ,
2017-03-13 18:15:25 +08:00
ComboColour = s . ComboColour ,
2017-04-06 10:41:16 +08:00
Samples = s . Samples ,
2017-09-13 15:17:01 +08:00
} )
2016-11-28 11:40:24 +08:00
} ;
2016-11-17 20:29:35 +08:00
2016-12-06 17:54:32 +08:00
components . Add ( body ) ;
components . Add ( ball ) ;
2016-12-06 23:02:27 +08:00
components . Add ( bouncer1 ) ;
components . Add ( bouncer2 ) ;
2017-02-16 16:02:36 +08:00
AddNested ( initialCircle ) ;
2017-03-15 11:52:25 +08:00
var repeatDuration = s . Curve . Distance / s . Velocity ;
2017-02-16 16:02:36 +08:00
foreach ( var tick in s . Ticks )
{
var repeatStartTime = s . StartTime + tick . RepeatIndex * repeatDuration ;
var fadeInTime = repeatStartTime + ( tick . StartTime - repeatStartTime ) / 2 - ( tick . RepeatIndex = = 0 ? TIME_FADEIN : TIME_FADEIN / 2 ) ;
var fadeOutTime = repeatStartTime + repeatDuration ;
var drawableTick = new DrawableSliderTick ( tick )
{
FadeInTime = fadeInTime ,
FadeOutTime = fadeOutTime ,
Position = tick . Position ,
} ;
ticks . Add ( drawableTick ) ;
AddNested ( drawableTick ) ;
}
2016-12-03 20:56:19 +08:00
}
2017-03-07 09:59:19 +08:00
private int currentRepeat ;
2016-12-08 18:54:22 +08:00
2016-12-06 17:54:32 +08:00
protected override void Update ( )
2016-11-17 20:29:35 +08:00
{
2016-12-06 17:54:32 +08:00
base . Update ( ) ;
2016-11-17 16:20:51 +08:00
2016-12-06 17:54:32 +08:00
double progress = MathHelper . Clamp ( ( Time . Current - slider . StartTime ) / slider . Duration , 0 , 1 ) ;
2016-11-28 15:31:19 +08:00
2017-03-06 10:11:29 +08:00
int repeat = slider . RepeatAt ( progress ) ;
2017-03-15 11:52:25 +08:00
progress = slider . ProgressAt ( progress ) ;
2016-11-28 15:31:19 +08:00
2016-12-08 18:54:22 +08:00
if ( repeat > currentRepeat )
{
2017-02-22 12:51:40 +08:00
if ( repeat < slider . RepeatCount & & ball . Tracking )
2017-04-05 20:34:28 +08:00
PlaySamples ( ) ;
2016-12-08 18:54:22 +08:00
currentRepeat = repeat ;
}
2017-02-09 15:29:21 +08:00
bouncer2 . Position = slider . Curve . PositionAt ( body . SnakedEnd ? ? 0 ) ;
2016-12-07 13:52:34 +08:00
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
2017-09-06 16:02:13 +08:00
if ( ! initialCircle . Judgements . Any ( j = > j . IsHit ) )
2017-02-09 15:29:21 +08:00
initialCircle . Position = slider . Curve . PositionAt ( progress ) ;
2016-12-06 23:02:27 +08:00
2017-02-16 16:02:36 +08:00
foreach ( var c in components ) c . UpdateProgress ( progress , repeat ) ;
foreach ( var t in ticks . Children ) t . Tracking = ball . Tracking ;
2016-12-03 21:40:35 +08:00
}
2017-09-06 16:02:13 +08:00
protected override void CheckForJudgements ( bool userTriggered , double timeOffset )
2016-11-29 20:40:24 +08:00
{
2017-03-13 18:15:25 +08:00
if ( ! userTriggered & & Time . Current > = slider . EndTime )
2016-11-29 20:40:24 +08:00
{
2017-06-25 13:46:59 +08:00
var ticksCount = ticks . Children . Count + 1 ;
2017-09-06 16:02:13 +08:00
var ticksHit = ticks . Children . Count ( t = > t . Judgements . Any ( j = > j . IsHit ) ) ;
if ( initialCircle . Judgements . Any ( j = > j . IsHit ) )
2017-02-13 03:38:05 +08:00
ticksHit + + ;
var hitFraction = ( double ) ticksHit / ticksCount ;
2017-09-06 16:02:13 +08:00
if ( hitFraction = = 1 & & initialCircle . Judgements . Any ( j = > j . Result = = HitResult . Great ) )
AddJudgement ( new OsuJudgement { Result = HitResult . Great } ) ;
else if ( hitFraction > = 0.5 & & initialCircle . Judgements . Any ( j = > j . Result > = HitResult . Good ) )
AddJudgement ( new OsuJudgement { Result = HitResult . Good } ) ;
2017-02-13 03:38:05 +08:00
else if ( hitFraction > 0 )
2017-09-06 16:02:13 +08:00
AddJudgement ( new OsuJudgement { Result = HitResult . Meh } ) ;
2017-02-13 03:38:05 +08:00
else
2017-09-06 16:02:13 +08:00
AddJudgement ( new OsuJudgement { Result = HitResult . Miss } ) ;
2016-11-29 20:40:24 +08:00
}
}
2016-12-06 23:02:27 +08:00
protected override void UpdateInitialState ( )
{
base . UpdateInitialState ( ) ;
body . Alpha = 1 ;
2016-12-08 20:07:20 +08:00
2017-02-05 16:40:58 +08:00
//we need to be present to handle input events. note that we still don't get enough events (we don't get a position if the mouse hasn't moved since the slider appeared).
ball . AlwaysPresent = true ;
ball . Alpha = 0 ;
2016-12-06 23:02:27 +08:00
}
2017-04-27 16:37:38 +08:00
protected override void UpdateCurrentState ( ArmedState state )
2016-11-17 16:20:51 +08:00
{
2016-12-08 20:07:20 +08:00
ball . FadeIn ( ) ;
2017-07-17 22:05:24 +08:00
using ( BeginDelayedSequence ( slider . Duration , true ) )
{
body . FadeOut ( 160 ) ;
ball . FadeOut ( 160 ) ;
2017-03-17 14:30:19 +08:00
2017-07-17 22:05:24 +08:00
this . FadeOut ( 800 )
. Expire ( ) ;
}
2016-11-17 16:20:51 +08:00
}
2017-02-14 09:22:28 +08:00
public Drawable ProxiedLayer = > initialCircle . ApproachCircle ;
2016-12-06 17:54:32 +08:00
}
2016-11-28 16:05:30 +08:00
2016-12-06 17:54:32 +08:00
internal interface ISliderProgress
{
void UpdateProgress ( double progress , int repeat ) ;
2016-11-17 16:20:51 +08:00
}
2017-04-27 16:37:38 +08:00
}