2018-01-05 19:21:19 +08:00
// Copyright (c) 2007-2018 ppy Pty Ltd <contact@ppy.sh>.
2017-02-07 12:59:30 +08:00
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
2016-09-02 17:35:49 +08:00
2016-12-06 20:14:38 +08:00
using OpenTK ;
2017-04-18 15:05:58 +08:00
using osu.Game.Rulesets.Objects.Types ;
2017-02-15 17:48:29 +08:00
using System ;
using System.Collections.Generic ;
2017-04-18 15:05:58 +08:00
using osu.Game.Rulesets.Objects ;
2017-04-06 10:41:16 +08:00
using System.Linq ;
using osu.Game.Audio ;
2017-07-26 12:22:46 +08:00
using osu.Game.Beatmaps ;
2017-05-23 12:55:18 +08:00
using osu.Game.Beatmaps.ControlPoints ;
2016-09-02 17:35:49 +08:00
2017-04-18 15:05:58 +08:00
namespace osu.Game.Rulesets.Osu.Objects
2016-09-02 17:35:49 +08:00
{
2017-03-15 11:52:25 +08:00
public class Slider : OsuHitObject , IHasCurve
2016-09-02 17:35:49 +08:00
{
2017-04-03 13:10:20 +08:00
/// <summary>
/// Scoring distance with a speed-adjusted beat length of 1 second.
/// </summary>
private const float base_scoring_distance = 100 ;
2017-04-22 19:43:20 +08:00
public readonly SliderCurve Curve = new SliderCurve ( ) ;
2017-03-15 11:52:25 +08:00
public double EndTime = > StartTime + RepeatCount * Curve . Distance / Velocity ;
2017-03-13 18:15:25 +08:00
public double Duration = > EndTime - StartTime ;
2016-11-28 14:31:54 +08:00
2017-03-06 10:11:29 +08:00
public override Vector2 EndPosition = > PositionAt ( 1 ) ;
2017-04-21 19:29:27 +08:00
public List < Vector2 > ControlPoints
{
get { return Curve . ControlPoints ; }
set { Curve . ControlPoints = value ; }
}
2017-03-06 10:11:29 +08:00
2017-04-21 19:29:27 +08:00
public CurveType CurveType
{
get { return Curve . CurveType ; }
set { Curve . CurveType = value ; }
}
2017-04-21 15:18:34 +08:00
2017-04-21 19:29:27 +08:00
public double Distance
{
get { return Curve . Distance ; }
set { Curve . Distance = value ; }
}
2017-03-15 11:52:25 +08:00
2017-11-17 19:28:41 +08:00
/// <summary>
2017-11-17 20:28:59 +08:00
/// The position of the cursor at the point of completion of this <see cref="Slider"/> if it was hit
/// with as few movements as possible. This is set and used by difficulty calculation.
2017-11-17 19:28:41 +08:00
/// </summary>
2017-11-17 20:28:59 +08:00
internal Vector2 ? LazyEndPosition ;
/// <summary>
/// The distance travelled by the cursor upon completion of this <see cref="Slider"/> if it was hit
/// with as few movements as possible. This is set and used by difficulty calculation.
/// </summary>
internal float LazyTravelDistance ;
2017-11-17 19:28:41 +08:00
2017-12-25 14:35:28 +08:00
public List < List < SampleInfo > > RepeatSamples { get ; set ; } = new List < List < SampleInfo > > ( ) ;
2017-04-21 19:29:27 +08:00
public int RepeatCount { get ; set ; } = 1 ;
2016-12-06 20:14:38 +08:00
2017-02-09 15:29:21 +08:00
private int stackHeight ;
2018-01-18 18:50:26 +08:00
2017-02-09 15:29:21 +08:00
public override int StackHeight
{
get { return stackHeight ; }
set
{
stackHeight = value ;
2017-02-14 17:40:37 +08:00
Curve . Offset = StackOffset ;
2017-02-09 15:29:21 +08:00
}
}
2016-11-28 17:45:50 +08:00
public double Velocity ;
2017-02-13 03:38:05 +08:00
public double TickDistance ;
2016-11-28 17:45:50 +08:00
2017-12-22 20:42:54 +08:00
protected override void ApplyDefaultsToSelf ( ControlPointInfo controlPointInfo , BeatmapDifficulty difficulty )
2016-11-28 17:45:50 +08:00
{
2017-12-22 20:42:54 +08:00
base . ApplyDefaultsToSelf ( controlPointInfo , difficulty ) ;
2017-12-21 15:02:33 +08:00
2017-05-23 12:55:18 +08:00
TimingControlPoint timingPoint = controlPointInfo . TimingPointAt ( StartTime ) ;
DifficultyControlPoint difficultyPoint = controlPointInfo . DifficultyPointAt ( StartTime ) ;
2017-02-13 03:38:05 +08:00
2017-08-21 10:45:57 +08:00
double scoringDistance = base_scoring_distance * difficulty . SliderMultiplier * difficultyPoint . SpeedMultiplier ;
2017-05-23 12:55:18 +08:00
Velocity = scoringDistance / timingPoint . BeatLength ;
2017-04-03 13:10:20 +08:00
TickDistance = scoringDistance / difficulty . SliderTickRate ;
2016-11-28 17:45:50 +08:00
}
2016-09-02 17:35:49 +08:00
2017-04-21 19:29:27 +08:00
public Vector2 PositionAt ( double progress ) = > Curve . PositionAt ( ProgressAt ( progress ) ) ;
public double ProgressAt ( double progress )
{
double p = progress * RepeatCount % 1 ;
if ( RepeatAt ( progress ) % 2 = = 1 )
p = 1 - p ;
return p ;
}
public int RepeatAt ( double progress ) = > ( int ) ( progress * RepeatCount ) ;
2017-12-22 20:42:54 +08:00
protected override void CreateNestedHitObjects ( )
2017-02-13 03:38:05 +08:00
{
2017-12-22 20:42:54 +08:00
base . CreateNestedHitObjects ( ) ;
createTicks ( ) ;
createRepeatPoints ( ) ;
}
2017-02-16 12:20:30 +08:00
2017-12-22 20:42:54 +08:00
private void createTicks ( )
{
if ( TickDistance = = 0 ) return ;
2017-02-13 03:38:05 +08:00
2017-12-22 20:42:54 +08:00
var length = Curve . Distance ;
var tickDistance = Math . Min ( TickDistance , length ) ;
var repeatDuration = length / Velocity ;
2017-02-13 03:38:05 +08:00
2017-12-22 20:42:54 +08:00
var minDistanceFromEnd = Velocity * 0.01 ;
2017-02-13 03:38:05 +08:00
2017-12-22 20:42:54 +08:00
for ( var repeat = 0 ; repeat < RepeatCount ; repeat + + )
{
var repeatStartTime = StartTime + repeat * repeatDuration ;
var reversed = repeat % 2 = = 1 ;
2017-02-13 03:38:05 +08:00
2017-12-22 20:42:54 +08:00
for ( var d = tickDistance ; d < = length ; d + = tickDistance )
{
if ( d > length - minDistanceFromEnd )
break ;
var distanceProgress = d / length ;
var timeProgress = reversed ? 1 - distanceProgress : distanceProgress ;
2017-02-13 03:38:05 +08:00
2018-01-18 19:37:36 +08:00
var firstSample = Samples . FirstOrDefault ( s = > s . Name = = SampleInfo . HIT_NORMAL ) ? ? Samples . FirstOrDefault ( ) ; // TODO: remove this when guaranteed sort is present for samples (https://github.com/ppy/osu/issues/1933)
2018-01-18 18:50:26 +08:00
var sampleList = new List < SampleInfo > ( ) ;
if ( firstSample ! = null )
sampleList . Add ( new SampleInfo
{
2018-01-18 18:57:49 +08:00
Bank = firstSample . Bank ,
Volume = firstSample . Volume ,
2018-01-18 18:50:26 +08:00
Name = @"slidertick" ,
} ) ;
2017-12-22 20:42:54 +08:00
AddNested ( new SliderTick
{
RepeatIndex = repeat ,
StartTime = repeatStartTime + timeProgress * repeatDuration ,
Position = Curve . PositionAt ( distanceProgress ) ,
StackHeight = StackHeight ,
Scale = Scale ,
ComboColour = ComboColour ,
2018-01-18 18:50:26 +08:00
Samples = sampleList
2017-12-22 20:42:54 +08:00
} ) ;
2017-02-13 03:38:05 +08:00
}
}
}
2017-12-22 20:42:54 +08:00
private void createRepeatPoints ( )
2017-09-27 00:13:34 +08:00
{
2017-12-27 19:37:28 +08:00
var repeatDuration = Distance / Velocity ;
2017-12-24 12:12:10 +08:00
2017-12-22 20:42:54 +08:00
for ( var repeat = 1 ; repeat < RepeatCount ; repeat + + )
{
2017-12-27 19:37:28 +08:00
var repeatStartTime = StartTime + repeat * repeatDuration ;
2017-12-24 12:12:10 +08:00
2017-12-27 19:37:28 +08:00
AddNested ( new RepeatPoint
2017-09-27 00:13:34 +08:00
{
2017-12-27 19:37:28 +08:00
RepeatIndex = repeat ,
StartTime = repeatStartTime ,
2017-12-27 19:40:38 +08:00
Position = Curve . PositionAt ( repeat % 2 ) ,
2017-12-27 19:37:28 +08:00
StackHeight = StackHeight ,
Scale = Scale ,
ComboColour = ComboColour ,
Samples = new List < SampleInfo > ( RepeatSamples [ repeat ] )
} ) ;
2017-09-27 00:13:34 +08:00
}
}
2016-09-02 17:35:49 +08:00
}
}