diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index 188af58e6a..be1e360fce 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -21,9 +21,7 @@ namespace osu.Game.Rulesets.Catch.Objects /// private const float base_scoring_distance = 100; - public readonly SliderCurve Curve = new SliderCurve(); - - public int RepeatCount { get; set; } = 1; + public int RepeatCount { get; set; } public double Velocity; public double TickDistance; @@ -55,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Objects var length = Curve.Distance; var tickDistance = Math.Min(TickDistance, length); - var repeatDuration = length / Velocity; + var spanDuration = length / Velocity; var minDistanceFromEnd = Velocity * 0.01; @@ -67,10 +65,10 @@ namespace osu.Game.Rulesets.Catch.Objects X = X }); - for (var repeat = 0; repeat < RepeatCount; repeat++) + for (var span = 0; span < this.SpanCount(); span++) { - var repeatStartTime = StartTime + repeat * repeatDuration; - var reversed = repeat % 2 == 1; + var spanStartTime = StartTime + span * spanDuration; + var reversed = span % 2 == 1; for (var d = tickDistance; d <= length; d += tickDistance) { @@ -80,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Objects var timeProgress = d / length; var distanceProgress = reversed ? 1 - timeProgress : timeProgress; - var lastTickTime = repeatStartTime + timeProgress * repeatDuration; + var lastTickTime = spanStartTime + timeProgress * spanDuration; AddNested(new Droplet { StartTime = lastTickTime, @@ -95,17 +93,17 @@ namespace osu.Game.Rulesets.Catch.Objects }); } - double tinyTickInterval = tickDistance / length * repeatDuration; + double tinyTickInterval = tickDistance / length * spanDuration; while (tinyTickInterval > 100) tinyTickInterval /= 2; - for (double t = 0; t < repeatDuration; t += tinyTickInterval) + for (double t = 0; t < spanDuration; t += tinyTickInterval) { - double progress = reversed ? 1 - t / repeatDuration : t / repeatDuration; + double progress = reversed ? 1 - t / spanDuration : t / spanDuration; AddNested(new TinyDroplet { - StartTime = repeatStartTime + t, + StartTime = spanStartTime + t, ComboColour = ComboColour, X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH, Samples = new List(Samples.Select(s => new SampleInfo @@ -121,15 +119,15 @@ namespace osu.Game.Rulesets.Catch.Objects { Samples = Samples, ComboColour = ComboColour, - StartTime = repeatStartTime + repeatDuration, + StartTime = spanStartTime + spanDuration, X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH }); } } - public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity; + public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; - public float EndX => Curve.PositionAt(ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH; + public float EndX => Curve.PositionAt(this.ProgressAt(1)).X / CatchPlayfield.BASE_WIDTH; public double Duration => EndTime - StartTime; @@ -139,6 +137,8 @@ namespace osu.Game.Rulesets.Catch.Objects set { Curve.Distance = value; } } + public SliderCurve Curve { get; } = new SliderCurve(); + public List ControlPoints { get { return Curve.ControlPoints; } @@ -152,17 +152,5 @@ namespace osu.Game.Rulesets.Catch.Objects get { return Curve.CurveType; } set { Curve.CurveType = value; } } - - 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); } } diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index 557ce5eb1b..9922d4c8ad 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps if (curveData == null) return HitObject.Samples; - double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.RepeatCount; + double segmentTime = (curveData.EndTime - HitObject.StartTime) / curveData.SpanCount(); int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); return curveData.RepeatSamples[index]; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index 1d739c114e..a102781e70 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private readonly double endTime; private readonly double segmentDuration; - private readonly int repeatCount; + private readonly int spanCount; private PatternType convertType; @@ -39,25 +39,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy var distanceData = hitObject as IHasDistance; var repeatsData = hitObject as IHasRepeats; - repeatCount = repeatsData?.RepeatCount ?? 1; + spanCount = repeatsData?.SpanCount() ?? 1; TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime); // The true distance, accounting for any repeats - double distance = (distanceData?.Distance ?? 0) * repeatCount; + double distance = (distanceData?.Distance ?? 0) * spanCount; // The velocity of the osu! hit object - calculated as the velocity of a slider double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength; // The duration of the osu! hit object double osuDuration = distance / osuVelocity; endTime = hitObject.StartTime + osuDuration; - segmentDuration = (endTime - HitObject.StartTime) / repeatCount; + segmentDuration = (endTime - HitObject.StartTime) / spanCount; } public override Pattern Generate() { - if (repeatCount > 1) + if (spanCount > 1) { if (segmentDuration <= 90) return generateRandomHoldNotes(HitObject.StartTime, 1); @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (segmentDuration <= 120) { convertType |= PatternType.ForceNotStack; - return generateRandomNotes(HitObject.StartTime, repeatCount + 1); + return generateRandomNotes(HitObject.StartTime, spanCount + 1); } if (segmentDuration <= 160) @@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (duration >= 4000) return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0); - if (segmentDuration > 400 && repeatCount < TotalColumns - 1 - RandomStart) + if (segmentDuration > 400 && spanCount < TotalColumns - 1 - RandomStart) return generateTiledHoldNotes(HitObject.StartTime); return generateHoldAndNormalNotes(HitObject.StartTime); @@ -212,7 +212,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); bool increasing = Random.NextDouble() > 0.5; - for (int i = 0; i <= repeatCount; i++) + for (int i = 0; i <= spanCount; i++) { addToPattern(pattern, column, startTime, startTime); startTime += segmentDuration; @@ -262,7 +262,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0)); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); - for (int i = 0; i <= repeatCount; i++) + for (int i = 0; i <= spanCount; i++) { addToPattern(pattern, nextColumn, startTime, startTime); @@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy var pattern = new Pattern(); - int columnRepeat = Math.Min(repeatCount, TotalColumns); + int columnRepeat = Math.Min(spanCount, TotalColumns); int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true); if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns) @@ -409,7 +409,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy int nextColumn = Random.Next(RandomStart, TotalColumns); var rowPattern = new Pattern(); - for (int i = 0; i <= repeatCount; i++) + for (int i = 0; i <= spanCount; i++) { if (!(ignoreHead && startTime == HitObject.StartTime)) { @@ -442,7 +442,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy if (curveData == null) return HitObject.Samples; - double segmentTime = (endTime - HitObject.StartTime) / repeatCount; + double segmentTime = (endTime - HitObject.StartTime) / spanCount; int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime); return curveData.RepeatSamples[index]; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 232964587c..fae56dcce2 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -10,6 +10,7 @@ using System.Linq; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Osu.Judgements; using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Objects.Drawables @@ -109,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - private int currentRepeat; + private int currentSpan; public bool Tracking; protected override void Update() @@ -120,17 +121,17 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables double progress = MathHelper.Clamp((Time.Current - slider.StartTime) / slider.Duration, 0, 1); - int repeat = slider.RepeatAt(progress); + int span = slider.SpanAt(progress); progress = slider.ProgressAt(progress); - if (repeat > currentRepeat) - currentRepeat = repeat; + if (span > currentSpan) + currentSpan = span; //todo: we probably want to reconsider this before adding scoring, but it looks and feels nice. if (!InitialCircle.Judgements.Any(j => j.IsHit)) InitialCircle.Position = slider.Curve.PositionAt(progress); - foreach (var c in components.OfType()) c.UpdateProgress(progress, repeat); + foreach (var c in components.OfType()) c.UpdateProgress(progress, span); foreach (var c in components.OfType()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0)); foreach (var t in ticks.Children) t.Tracking = Ball.Tracking; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs index cf2ac5124f..2fda299389 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBall.cs @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces } } - public void UpdateProgress(double progress, int repeat) + public void UpdateProgress(double progress, int span) { Position = slider.Curve.PositionAt(progress); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs index ddd0f2d650..6fe1fda8eb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/Pieces/SliderBody.cs @@ -15,6 +15,7 @@ using OpenTK; using OpenTK.Graphics.ES30; using OpenTK.Graphics; using osu.Framework.Graphics.Primitives; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces { @@ -164,14 +165,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces return true; } - public void UpdateProgress(double progress, int repeat) + public void UpdateProgress(double progress, int span) { double start = 0; double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1; - if (repeat >= slider.RepeatCount - 1) + if (span >= slider.SpanCount() - 1) { - if (Math.Min(repeat, slider.RepeatCount - 1) % 2 == 1) + if (Math.Min(span, slider.SpanCount() - 1) % 2 == 1) { start = 0; end = snakingOut ? progress : 1; diff --git a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs index 31125121b0..54f783b664 100644 --- a/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs +++ b/osu.Game.Rulesets.Osu/Objects/ISliderProgress.cs @@ -5,6 +5,6 @@ namespace osu.Game.Rulesets.Osu.Objects { public interface ISliderProgress { - void UpdateProgress(double progress, int repeat); + void UpdateProgress(double progress, int span); } } diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 2da285a434..c49fbce16d 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Osu.Objects /// private const float base_scoring_distance = 100; - public readonly SliderCurve Curve = new SliderCurve(); - - public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity; + public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity; public double Duration => EndTime - StartTime; - public override Vector2 EndPosition => PositionAt(1); + public override Vector2 EndPosition => this.PositionAt(1); + + public SliderCurve Curve { get; } = new SliderCurve(); public List ControlPoints { @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects internal float LazyTravelDistance; public List> RepeatSamples { get; set; } = new List>(); - public int RepeatCount { get; set; } = 1; + public int RepeatCount { get; set; } private int stackHeight; @@ -88,18 +88,6 @@ namespace osu.Game.Rulesets.Osu.Objects TickDistance = scoringDistance / difficulty.SliderTickRate; } - 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); - protected override void CreateNestedHitObjects() { base.CreateNestedHitObjects(); @@ -114,14 +102,14 @@ namespace osu.Game.Rulesets.Osu.Objects var length = Curve.Distance; var tickDistance = Math.Min(TickDistance, length); - var repeatDuration = length / Velocity; + var spanDuration = length / Velocity; var minDistanceFromEnd = Velocity * 0.01; - for (var repeat = 0; repeat < RepeatCount; repeat++) + for (var span = 0; span < this.SpanCount(); span++) { - var repeatStartTime = StartTime + repeat * repeatDuration; - var reversed = repeat % 2 == 1; + var spanStartTime = StartTime + span * spanDuration; + var reversed = span % 2 == 1; for (var d = tickDistance; d <= length; d += tickDistance) { @@ -144,8 +132,8 @@ namespace osu.Game.Rulesets.Osu.Objects AddNested(new SliderTick { - RepeatIndex = repeat, - StartTime = repeatStartTime + timeProgress * repeatDuration, + RepeatIndex = span, + StartTime = spanStartTime + timeProgress * spanDuration, Position = Curve.PositionAt(distanceProgress), StackHeight = StackHeight, Scale = Scale, @@ -160,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Objects { var repeatDuration = Distance / Velocity; - for (var repeat = 1; repeat < RepeatCount; repeat++) + for (var repeat = 1; repeat <= RepeatCount; repeat++) { var repeatStartTime = StartTime + repeat * repeatDuration; diff --git a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs index 557e59e6f4..4f950353dc 100644 --- a/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs +++ b/osu.Game.Rulesets.Osu/OsuDifficulty/Preprocessing/OsuDifficultyHitObject.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using osu.Game.Rulesets.Objects.Types; using OpenTK; using osu.Game.Rulesets.Osu.Objects; diff --git a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs index c395c5edb8..ddf24cc405 100644 --- a/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs +++ b/osu.Game.Rulesets.Osu/Tests/TestCaseSlider.cs @@ -84,14 +84,9 @@ namespace osu.Game.Rulesets.Osu.Tests private void createSlider(float circleSize = 2, float distance = 400, int repeats = 0, double speedMultiplier = 2) { - repeats++; // The first run through the slider is considered a repeat - var repeatSamples = new List>(); - if (repeats > 1) - { - for (int i = 0; i < repeats; i++) - repeatSamples.Add(new List()); - } + for (int i = 0; i < repeats; i++) + repeatSamples.Add(new List()); var slider = new Slider { diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index a3c9857d0c..e5fe288f20 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -84,7 +84,8 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps if (distanceData != null) { - int repeats = repeatsData?.RepeatCount ?? 1; + // Number of spans of the object - one for the initial length and for each repeat + int spans = repeatsData?.SpanCount() ?? 1; TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime); @@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment; // The true distance, accounting for any repeats. This ends up being the drum roll distance later - double distance = distanceData.Distance * repeats * legacy_velocity_multiplier; + double distance = distanceData.Distance * spans * legacy_velocity_multiplier; // The velocity of the taiko hit object - calculated as the velocity of a drum roll double taikoVelocity = taiko_base_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * legacy_velocity_multiplier / speedAdjustedBeatLength; @@ -111,7 +112,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps double osuDuration = distance / osuVelocity; // If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat - double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / repeats); + double tickSpacing = Math.Min(speedAdjustedBeatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans); if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength) { diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 840322f8a8..5fdc9a07e1 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -77,6 +77,10 @@ namespace osu.Game.Rulesets.Objects.Legacy if (repeatCount > 9000) throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high"); + // osu-stable treated the first span of the slider as a repeat, but no repeats are happening + repeatCount = Math.Max(0, repeatCount - 1); + + if (split.Length > 7) length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture); @@ -84,8 +88,7 @@ namespace osu.Game.Rulesets.Objects.Legacy readCustomSampleBanks(split[10], bankInfo); // One node for each repeat + the start and end nodes - // Note that the first length of the slider is considered a repeat, but there are no actual repeats happening - int nodes = Math.Max(0, repeatCount - 1) + 2; + int nodes = repeatCount + 2; // Populate node sample bank infos with the default hit object sample bank var nodeBankInfos = new List(); @@ -128,7 +131,7 @@ namespace osu.Game.Rulesets.Objects.Legacy // Generate the final per-node samples var nodeSamples = new List>(nodes); - for (int i = 0; i <= repeatCount; i++) + for (int i = 0; i < nodes; i++) nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[i])); result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples); diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs index 4e01402668..df7c8b0a83 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertSlider.cs @@ -2,7 +2,6 @@ // Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE using osu.Game.Rulesets.Objects.Types; -using System; using System.Collections.Generic; using OpenTK; using osu.Game.Audio; @@ -18,34 +17,23 @@ namespace osu.Game.Rulesets.Objects.Legacy /// private const float base_scoring_distance = 100; + /// + /// s don't need a curve since they're converted to ruleset-specific hitobjects. + /// + public SliderCurve Curve { get; } = null; public List ControlPoints { get; set; } public CurveType CurveType { get; set; } public double Distance { get; set; } public List> RepeatSamples { get; set; } - public int RepeatCount { get; set; } = 1; + public int RepeatCount { get; set; } - public double EndTime => StartTime + RepeatCount * Distance / Velocity; + public double EndTime => StartTime + this.SpanCount() * Distance / Velocity; public double Duration => EndTime - StartTime; public double Velocity = 1; - public Vector2 PositionAt(double progress) - { - throw new NotImplementedException(); - } - - public double ProgressAt(double progress) - { - throw new NotImplementedException(); - } - - public int RepeatAt(double progress) - { - throw new NotImplementedException(); - } - protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty) { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); diff --git a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs index 92df232ea9..0254a829f4 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCurve.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCurve.cs @@ -11,6 +11,11 @@ namespace osu.Game.Rulesets.Objects.Types /// public interface IHasCurve : IHasDistance, IHasRepeats { + /// + /// The curve. + /// + SliderCurve Curve { get; } + /// /// The control points that shape the curve. /// @@ -20,7 +25,10 @@ namespace osu.Game.Rulesets.Objects.Types /// The type of curve. /// CurveType CurveType { get; } + } + public static class HasCurveExtensions + { /// /// Computes the position on the curve at a given progress, accounting for repeat logic. /// @@ -28,20 +36,28 @@ namespace osu.Game.Rulesets.Objects.Types /// /// /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - Vector2 PositionAt(double progress); + public static Vector2 PositionAt(this IHasCurve obj, double progress) + => obj.Curve.PositionAt(obj.ProgressAt(progress)); /// /// Finds the progress along the curve, accounting for repeat logic. /// /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - double ProgressAt(double progress); + public static double ProgressAt(this IHasCurve obj, double progress) + { + double p = progress * obj.SpanCount() % 1; + if (obj.SpanAt(progress) % 2 == 1) + p = 1 - p; + return p; + } /// - /// Determines which repeat of the curve the progress point is on. + /// Determines which span of the curve the progress point is on. /// /// [0, 1] where 0 is the beginning of the curve and 1 is the end of the curve. - /// [0, RepeatCount] where 0 is the first run. - int RepeatAt(double progress); + /// [0, SpanCount) where 0 is the first run. + public static int SpanAt(this IHasCurve obj, double progress) + => (int)(progress * obj.SpanCount()); } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs index bd097cb96f..75dd3776f9 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasRepeats.cs @@ -21,4 +21,13 @@ namespace osu.Game.Rulesets.Objects.Types /// List> RepeatSamples { get; } } + + public static class HasRepeatsExtensions + { + /// + /// The amount of times the length of this spans. + /// + /// The object that has repeats. + public static int SpanCount(this IHasRepeats obj) => obj.RepeatCount + 1; + } }