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 ;
2016-11-17 20:29:35 +08:00
using osu.Game.Modes.Objects.Drawables ;
2016-12-06 17:54:32 +08:00
using osu.Game.Modes.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 ;
2016-11-17 16:20:51 +08:00
namespace osu.Game.Modes.Osu.Objects.Drawables
{
2017-02-14 09:22:28 +08:00
public class DrawableSlider : DrawableOsuHitObject , IDrawableHitObjectWithProxiedApproach
2016-11-17 16:20:51 +08:00
{
2016-11-28 11:40:24 +08:00
private Slider slider ;
2016-11-28 16:05:30 +08:00
2016-12-06 23:02:27 +08:00
private DrawableHitCircle initialCircle ;
2016-12-06 17:54:32 +08:00
private List < ISliderProgress > components = new List < ISliderProgress > ( ) ;
2016-11-28 11:40:24 +08:00
2017-02-16 16:02:36 +08:00
private Container < DrawableSliderTick > ticks ;
2016-12-06 23:02:27 +08:00
SliderBody body ;
2016-12-08 18:54:22 +08:00
SliderBall ball ;
2016-12-06 23:02:27 +08:00
SliderBouncer bouncer1 , bouncer2 ;
2016-11-28 11:15:25 +08:00
public DrawableSlider ( Slider s ) : base ( s )
2016-11-17 16:20:51 +08:00
{
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-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 ) ,
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 ,
2016-11-28 11:40:24 +08:00
Colour = s . Colour ,
2016-12-08 18:54:22 +08:00
Sample = s . Sample ,
2016-12-08 20:07:20 +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 ) ;
var repeatDuration = s . Curve . Length / s . Velocity ;
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
}
2016-12-11 17:11:22 +08:00
// Since the DrawableSlider itself is just a container without a size we need to
// pass all input through.
public override bool Contains ( Vector2 screenSpacePos ) = > true ;
2016-12-08 18:54:22 +08:00
int currentRepeat ;
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 ) ;
progress = slider . CurveProgressAt ( 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 )
2016-12-08 18:54:22 +08:00
PlaySample ( ) ;
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.
if ( initialCircle . Judgement ? . Result ! = HitResult . Hit )
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
}
2016-11-29 20:40:24 +08:00
protected override void CheckJudgement ( bool userTriggered )
{
var j = Judgement as OsuJudgementInfo ;
2016-12-06 23:02:27 +08:00
var sc = initialCircle . Judgement as OsuJudgementInfo ;
2016-11-29 20:40:24 +08:00
if ( ! userTriggered & & Time . Current > = HitObject . EndTime )
{
2017-02-13 03:38:05 +08:00
var ticksCount = ticks . Children . Count ( ) + 1 ;
var ticksHit = ticks . Children . Count ( t = > t . Judgement . Result = = HitResult . Hit ) ;
if ( sc . Result = = HitResult . Hit )
ticksHit + + ;
var hitFraction = ( double ) ticksHit / ticksCount ;
if ( hitFraction = = 1 & & sc . Score = = OsuScoreResult . Hit300 )
j . Score = OsuScoreResult . Hit300 ;
else if ( hitFraction > = 0.5 & & sc . Score > = OsuScoreResult . Hit100 )
j . Score = OsuScoreResult . Hit100 ;
else if ( hitFraction > 0 )
j . Score = OsuScoreResult . Hit50 ;
else
j . Score = OsuScoreResult . Miss ;
j . Result = j . Score ! = OsuScoreResult . Miss ? HitResult . Hit : 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
}
2016-11-17 16:20:51 +08:00
protected override void UpdateState ( ArmedState state )
{
2016-11-28 17:40:54 +08:00
base . UpdateState ( state ) ;
2016-11-17 20:29:35 +08:00
2016-12-08 20:07:20 +08:00
ball . FadeIn ( ) ;
2016-12-06 23:02:27 +08:00
Delay ( HitObject . Duration , true ) ;
2016-12-08 20:07:20 +08:00
2016-12-06 23:02:27 +08:00
body . FadeOut ( 160 ) ;
2016-12-08 20:07:20 +08:00
ball . FadeOut ( 160 ) ;
2016-12-06 23:02:27 +08:00
FadeOut ( 800 ) ;
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
}
2016-12-06 17:54:32 +08:00
}