mirror of
https://github.com/ppy/osu.git
synced 2025-01-15 09:22:54 +08:00
Clean up off-by-one offsets from repeat-related properties
This commit is contained in:
parent
4f360eac56
commit
d37844c068
@ -21,9 +21,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const float base_scoring_distance = 100;
|
private const float base_scoring_distance = 100;
|
||||||
|
|
||||||
public readonly SliderCurve Curve = new SliderCurve();
|
public int RepeatCount { get; set; }
|
||||||
|
|
||||||
public int RepeatCount { get; set; } = 1;
|
|
||||||
|
|
||||||
public double Velocity;
|
public double Velocity;
|
||||||
public double TickDistance;
|
public double TickDistance;
|
||||||
@ -55,7 +53,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
|
|
||||||
var length = Curve.Distance;
|
var length = Curve.Distance;
|
||||||
var tickDistance = Math.Min(TickDistance, length);
|
var tickDistance = Math.Min(TickDistance, length);
|
||||||
var repeatDuration = length / Velocity;
|
var spanDuration = length / Velocity;
|
||||||
|
|
||||||
var minDistanceFromEnd = Velocity * 0.01;
|
var minDistanceFromEnd = Velocity * 0.01;
|
||||||
|
|
||||||
@ -67,10 +65,10 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
X = X
|
X = X
|
||||||
});
|
});
|
||||||
|
|
||||||
for (var repeat = 0; repeat < RepeatCount; repeat++)
|
for (var span = 0; span < this.SpanCount(); span++)
|
||||||
{
|
{
|
||||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
var spanStartTime = StartTime + span * spanDuration;
|
||||||
var reversed = repeat % 2 == 1;
|
var reversed = span % 2 == 1;
|
||||||
|
|
||||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||||
{
|
{
|
||||||
@ -80,7 +78,7 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
var timeProgress = d / length;
|
var timeProgress = d / length;
|
||||||
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
var distanceProgress = reversed ? 1 - timeProgress : timeProgress;
|
||||||
|
|
||||||
var lastTickTime = repeatStartTime + timeProgress * repeatDuration;
|
var lastTickTime = spanStartTime + timeProgress * spanDuration;
|
||||||
AddNested(new Droplet
|
AddNested(new Droplet
|
||||||
{
|
{
|
||||||
StartTime = lastTickTime,
|
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)
|
while (tinyTickInterval > 100)
|
||||||
tinyTickInterval /= 2;
|
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
|
AddNested(new TinyDroplet
|
||||||
{
|
{
|
||||||
StartTime = repeatStartTime + t,
|
StartTime = spanStartTime + t,
|
||||||
ComboColour = ComboColour,
|
ComboColour = ComboColour,
|
||||||
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
X = Curve.PositionAt(progress).X / CatchPlayfield.BASE_WIDTH,
|
||||||
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
Samples = new List<SampleInfo>(Samples.Select(s => new SampleInfo
|
||||||
@ -121,15 +119,15 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
{
|
{
|
||||||
Samples = Samples,
|
Samples = Samples,
|
||||||
ComboColour = ComboColour,
|
ComboColour = ComboColour,
|
||||||
StartTime = repeatStartTime + repeatDuration,
|
StartTime = spanStartTime + spanDuration,
|
||||||
X = Curve.PositionAt(reversed ? 0 : 1).X / CatchPlayfield.BASE_WIDTH
|
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;
|
public double Duration => EndTime - StartTime;
|
||||||
|
|
||||||
@ -139,6 +137,8 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
set { Curve.Distance = value; }
|
set { Curve.Distance = value; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SliderCurve Curve { get; } = new SliderCurve();
|
||||||
|
|
||||||
public List<Vector2> ControlPoints
|
public List<Vector2> ControlPoints
|
||||||
{
|
{
|
||||||
get { return Curve.ControlPoints; }
|
get { return Curve.ControlPoints; }
|
||||||
@ -152,17 +152,5 @@ namespace osu.Game.Rulesets.Catch.Objects
|
|||||||
get { return Curve.CurveType; }
|
get { return Curve.CurveType; }
|
||||||
set { Curve.CurveType = value; }
|
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps
|
|||||||
if (curveData == null)
|
if (curveData == null)
|
||||||
return HitObject.Samples;
|
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);
|
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||||
return curveData.RepeatSamples[index];
|
return curveData.RepeatSamples[index];
|
||||||
|
@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
private readonly double endTime;
|
private readonly double endTime;
|
||||||
private readonly double segmentDuration;
|
private readonly double segmentDuration;
|
||||||
private readonly int repeatCount;
|
private readonly int spanCount;
|
||||||
|
|
||||||
private PatternType convertType;
|
private PatternType convertType;
|
||||||
|
|
||||||
@ -39,25 +39,25 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
var distanceData = hitObject as IHasDistance;
|
var distanceData = hitObject as IHasDistance;
|
||||||
var repeatsData = hitObject as IHasRepeats;
|
var repeatsData = hitObject as IHasRepeats;
|
||||||
|
|
||||||
repeatCount = repeatsData?.RepeatCount ?? 1;
|
spanCount = repeatsData?.SpanCount() ?? 1;
|
||||||
|
|
||||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime);
|
||||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
|
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(hitObject.StartTime);
|
||||||
|
|
||||||
// The true distance, accounting for any repeats
|
// 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
|
// 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;
|
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / timingPoint.BeatLength;
|
||||||
// The duration of the osu! hit object
|
// The duration of the osu! hit object
|
||||||
double osuDuration = distance / osuVelocity;
|
double osuDuration = distance / osuVelocity;
|
||||||
|
|
||||||
endTime = hitObject.StartTime + osuDuration;
|
endTime = hitObject.StartTime + osuDuration;
|
||||||
segmentDuration = (endTime - HitObject.StartTime) / repeatCount;
|
segmentDuration = (endTime - HitObject.StartTime) / spanCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override Pattern Generate()
|
public override Pattern Generate()
|
||||||
{
|
{
|
||||||
if (repeatCount > 1)
|
if (spanCount > 1)
|
||||||
{
|
{
|
||||||
if (segmentDuration <= 90)
|
if (segmentDuration <= 90)
|
||||||
return generateRandomHoldNotes(HitObject.StartTime, 1);
|
return generateRandomHoldNotes(HitObject.StartTime, 1);
|
||||||
@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
if (segmentDuration <= 120)
|
if (segmentDuration <= 120)
|
||||||
{
|
{
|
||||||
convertType |= PatternType.ForceNotStack;
|
convertType |= PatternType.ForceNotStack;
|
||||||
return generateRandomNotes(HitObject.StartTime, repeatCount + 1);
|
return generateRandomNotes(HitObject.StartTime, spanCount + 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segmentDuration <= 160)
|
if (segmentDuration <= 160)
|
||||||
@ -78,7 +78,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
if (duration >= 4000)
|
if (duration >= 4000)
|
||||||
return generateNRandomNotes(HitObject.StartTime, 0.23, 0, 0);
|
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 generateTiledHoldNotes(HitObject.StartTime);
|
||||||
|
|
||||||
return generateHoldAndNormalNotes(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);
|
int column = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
bool increasing = Random.NextDouble() > 0.5;
|
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);
|
addToPattern(pattern, column, startTime, startTime);
|
||||||
startTime += segmentDuration;
|
startTime += segmentDuration;
|
||||||
@ -262,7 +262,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
|
int interval = Random.Next(1, TotalColumns - (legacy ? 1 : 0));
|
||||||
|
|
||||||
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
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);
|
addToPattern(pattern, nextColumn, startTime, startTime);
|
||||||
|
|
||||||
@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
|
|
||||||
var pattern = new Pattern();
|
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);
|
int nextColumn = GetColumn((HitObject as IHasXPosition)?.X ?? 0, true);
|
||||||
if ((convertType & PatternType.ForceNotStack) > 0 && PreviousPattern.ColumnWithObjects < TotalColumns)
|
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);
|
int nextColumn = Random.Next(RandomStart, TotalColumns);
|
||||||
|
|
||||||
var rowPattern = new Pattern();
|
var rowPattern = new Pattern();
|
||||||
for (int i = 0; i <= repeatCount; i++)
|
for (int i = 0; i <= spanCount; i++)
|
||||||
{
|
{
|
||||||
if (!(ignoreHead && startTime == HitObject.StartTime))
|
if (!(ignoreHead && startTime == HitObject.StartTime))
|
||||||
{
|
{
|
||||||
@ -442,7 +442,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
|
|||||||
if (curveData == null)
|
if (curveData == null)
|
||||||
return HitObject.Samples;
|
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);
|
int index = (int)(segmentTime == 0 ? 0 : (time - HitObject.StartTime) / segmentTime);
|
||||||
return curveData.RepeatSamples[index];
|
return curveData.RepeatSamples[index];
|
||||||
|
@ -10,6 +10,7 @@ using System.Linq;
|
|||||||
using osu.Framework.Graphics.Containers;
|
using osu.Framework.Graphics.Containers;
|
||||||
using osu.Game.Rulesets.Osu.Judgements;
|
using osu.Game.Rulesets.Osu.Judgements;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using osu.Game.Rulesets.Scoring;
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables
|
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;
|
public bool Tracking;
|
||||||
|
|
||||||
protected override void Update()
|
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);
|
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);
|
progress = slider.ProgressAt(progress);
|
||||||
|
|
||||||
if (repeat > currentRepeat)
|
if (span > currentSpan)
|
||||||
currentRepeat = repeat;
|
currentSpan = span;
|
||||||
|
|
||||||
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
|
//todo: we probably want to reconsider this before adding scoring, but it looks and feels nice.
|
||||||
if (!InitialCircle.Judgements.Any(j => j.IsHit))
|
if (!InitialCircle.Judgements.Any(j => j.IsHit))
|
||||||
InitialCircle.Position = slider.Curve.PositionAt(progress);
|
InitialCircle.Position = slider.Curve.PositionAt(progress);
|
||||||
|
|
||||||
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(progress, repeat);
|
foreach (var c in components.OfType<ISliderProgress>()) c.UpdateProgress(progress, span);
|
||||||
foreach (var c in components.OfType<ITrackSnaking>()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0));
|
foreach (var c in components.OfType<ITrackSnaking>()) c.UpdateSnakingPosition(slider.Curve.PositionAt(Body.SnakedStart ?? 0), slider.Curve.PositionAt(Body.SnakedEnd ?? 0));
|
||||||
foreach (var t in ticks.Children) t.Tracking = Ball.Tracking;
|
foreach (var t in ticks.Children) t.Tracking = Ball.Tracking;
|
||||||
}
|
}
|
||||||
|
@ -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);
|
Position = slider.Curve.PositionAt(progress);
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,7 @@ using OpenTK;
|
|||||||
using OpenTK.Graphics.ES30;
|
using OpenTK.Graphics.ES30;
|
||||||
using OpenTK.Graphics;
|
using OpenTK.Graphics;
|
||||||
using osu.Framework.Graphics.Primitives;
|
using osu.Framework.Graphics.Primitives;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
||||||
{
|
{
|
||||||
@ -164,14 +165,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Pieces
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateProgress(double progress, int repeat)
|
public void UpdateProgress(double progress, int span)
|
||||||
{
|
{
|
||||||
double start = 0;
|
double start = 0;
|
||||||
double end = snakingIn ? MathHelper.Clamp((Time.Current - (slider.StartTime - slider.TimePreempt)) / slider.TimeFadein, 0, 1) : 1;
|
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;
|
start = 0;
|
||||||
end = snakingOut ? progress : 1;
|
end = snakingOut ? progress : 1;
|
||||||
|
@ -5,6 +5,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
public interface ISliderProgress
|
public interface ISliderProgress
|
||||||
{
|
{
|
||||||
void UpdateProgress(double progress, int repeat);
|
void UpdateProgress(double progress, int span);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,12 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const float base_scoring_distance = 100;
|
private const float base_scoring_distance = 100;
|
||||||
|
|
||||||
public readonly SliderCurve Curve = new SliderCurve();
|
public double EndTime => StartTime + this.SpanCount() * Curve.Distance / Velocity;
|
||||||
|
|
||||||
public double EndTime => StartTime + RepeatCount * Curve.Distance / Velocity;
|
|
||||||
public double Duration => EndTime - StartTime;
|
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<Vector2> ControlPoints
|
public List<Vector2> ControlPoints
|
||||||
{
|
{
|
||||||
@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
internal float LazyTravelDistance;
|
internal float LazyTravelDistance;
|
||||||
|
|
||||||
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
|
public List<List<SampleInfo>> RepeatSamples { get; set; } = new List<List<SampleInfo>>();
|
||||||
public int RepeatCount { get; set; } = 1;
|
public int RepeatCount { get; set; }
|
||||||
|
|
||||||
private int stackHeight;
|
private int stackHeight;
|
||||||
|
|
||||||
@ -88,18 +88,6 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
TickDistance = scoringDistance / difficulty.SliderTickRate;
|
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()
|
protected override void CreateNestedHitObjects()
|
||||||
{
|
{
|
||||||
base.CreateNestedHitObjects();
|
base.CreateNestedHitObjects();
|
||||||
@ -114,14 +102,14 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
var length = Curve.Distance;
|
var length = Curve.Distance;
|
||||||
var tickDistance = Math.Min(TickDistance, length);
|
var tickDistance = Math.Min(TickDistance, length);
|
||||||
var repeatDuration = length / Velocity;
|
var spanDuration = length / Velocity;
|
||||||
|
|
||||||
var minDistanceFromEnd = Velocity * 0.01;
|
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 spanStartTime = StartTime + span * spanDuration;
|
||||||
var reversed = repeat % 2 == 1;
|
var reversed = span % 2 == 1;
|
||||||
|
|
||||||
for (var d = tickDistance; d <= length; d += tickDistance)
|
for (var d = tickDistance; d <= length; d += tickDistance)
|
||||||
{
|
{
|
||||||
@ -144,8 +132,8 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
|
|
||||||
AddNested(new SliderTick
|
AddNested(new SliderTick
|
||||||
{
|
{
|
||||||
RepeatIndex = repeat,
|
RepeatIndex = span,
|
||||||
StartTime = repeatStartTime + timeProgress * repeatDuration,
|
StartTime = spanStartTime + timeProgress * spanDuration,
|
||||||
Position = Curve.PositionAt(distanceProgress),
|
Position = Curve.PositionAt(distanceProgress),
|
||||||
StackHeight = StackHeight,
|
StackHeight = StackHeight,
|
||||||
Scale = Scale,
|
Scale = Scale,
|
||||||
@ -160,7 +148,7 @@ namespace osu.Game.Rulesets.Osu.Objects
|
|||||||
{
|
{
|
||||||
var repeatDuration = Distance / Velocity;
|
var repeatDuration = Distance / Velocity;
|
||||||
|
|
||||||
for (var repeat = 1; repeat < RepeatCount; repeat++)
|
for (var repeat = 1; repeat <= RepeatCount; repeat++)
|
||||||
{
|
{
|
||||||
var repeatStartTime = StartTime + repeat * repeatDuration;
|
var repeatStartTime = StartTime + repeat * repeatDuration;
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@
|
|||||||
|
|
||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Rulesets.Osu.Objects;
|
using osu.Game.Rulesets.Osu.Objects;
|
||||||
|
|
||||||
|
@ -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)
|
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<List<SampleInfo>>();
|
var repeatSamples = new List<List<SampleInfo>>();
|
||||||
if (repeats > 1)
|
for (int i = 0; i < repeats; i++)
|
||||||
{
|
repeatSamples.Add(new List<SampleInfo>());
|
||||||
for (int i = 0; i < repeats; i++)
|
|
||||||
repeatSamples.Add(new List<SampleInfo>());
|
|
||||||
}
|
|
||||||
|
|
||||||
var slider = new Slider
|
var slider = new Slider
|
||||||
{
|
{
|
||||||
|
@ -84,7 +84,8 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
|
|
||||||
if (distanceData != null)
|
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);
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
|
||||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
|
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
|
||||||
@ -93,7 +94,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
|
double speedAdjustedBeatLength = timingPoint.BeatLength / speedAdjustment;
|
||||||
|
|
||||||
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
|
// 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
|
// 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;
|
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;
|
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
|
// 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)
|
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
||||||
{
|
{
|
||||||
|
@ -77,6 +77,10 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
if (repeatCount > 9000)
|
if (repeatCount > 9000)
|
||||||
throw new ArgumentOutOfRangeException(nameof(repeatCount), @"Repeat count is way too high");
|
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)
|
if (split.Length > 7)
|
||||||
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
|
length = Convert.ToDouble(split[7], CultureInfo.InvariantCulture);
|
||||||
|
|
||||||
@ -84,8 +88,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
readCustomSampleBanks(split[10], bankInfo);
|
readCustomSampleBanks(split[10], bankInfo);
|
||||||
|
|
||||||
// One node for each repeat + the start and end nodes
|
// 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 = repeatCount + 2;
|
||||||
int nodes = Math.Max(0, repeatCount - 1) + 2;
|
|
||||||
|
|
||||||
// Populate node sample bank infos with the default hit object sample bank
|
// Populate node sample bank infos with the default hit object sample bank
|
||||||
var nodeBankInfos = new List<SampleBankInfo>();
|
var nodeBankInfos = new List<SampleBankInfo>();
|
||||||
@ -128,7 +131,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
|
|
||||||
// Generate the final per-node samples
|
// Generate the final per-node samples
|
||||||
var nodeSamples = new List<List<SampleInfo>>(nodes);
|
var nodeSamples = new List<List<SampleInfo>>(nodes);
|
||||||
for (int i = 0; i <= repeatCount; i++)
|
for (int i = 0; i < nodes; i++)
|
||||||
nodeSamples.Add(convertSoundType(nodeSoundTypes[i], nodeBankInfos[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);
|
result = CreateSlider(new Vector2(int.Parse(split[0]), int.Parse(split[1])), combo, points, length, curveType, repeatCount, nodeSamples);
|
||||||
|
@ -2,7 +2,6 @@
|
|||||||
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
// Licensed under the MIT Licence - https://raw.githubusercontent.com/ppy/osu/master/LICENCE
|
||||||
|
|
||||||
using osu.Game.Rulesets.Objects.Types;
|
using osu.Game.Rulesets.Objects.Types;
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using OpenTK;
|
using OpenTK;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
@ -18,34 +17,23 @@ namespace osu.Game.Rulesets.Objects.Legacy
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const float base_scoring_distance = 100;
|
private const float base_scoring_distance = 100;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// <see cref="ConvertSlider"/>s don't need a curve since they're converted to ruleset-specific hitobjects.
|
||||||
|
/// </summary>
|
||||||
|
public SliderCurve Curve { get; } = null;
|
||||||
public List<Vector2> ControlPoints { get; set; }
|
public List<Vector2> ControlPoints { get; set; }
|
||||||
public CurveType CurveType { get; set; }
|
public CurveType CurveType { get; set; }
|
||||||
|
|
||||||
public double Distance { get; set; }
|
public double Distance { get; set; }
|
||||||
|
|
||||||
public List<List<SampleInfo>> RepeatSamples { get; set; }
|
public List<List<SampleInfo>> 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 Duration => EndTime - StartTime;
|
||||||
|
|
||||||
public double Velocity = 1;
|
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)
|
protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, BeatmapDifficulty difficulty)
|
||||||
{
|
{
|
||||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||||
|
@ -11,6 +11,11 @@ namespace osu.Game.Rulesets.Objects.Types
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public interface IHasCurve : IHasDistance, IHasRepeats
|
public interface IHasCurve : IHasDistance, IHasRepeats
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The curve.
|
||||||
|
/// </summary>
|
||||||
|
SliderCurve Curve { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The control points that shape the curve.
|
/// The control points that shape the curve.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -20,7 +25,10 @@ namespace osu.Game.Rulesets.Objects.Types
|
|||||||
/// The type of curve.
|
/// The type of curve.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
CurveType CurveType { get; }
|
CurveType CurveType { get; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class HasCurveExtensions
|
||||||
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Computes the position on the curve at a given progress, accounting for repeat logic.
|
/// Computes the position on the curve at a given progress, accounting for repeat logic.
|
||||||
/// <para>
|
/// <para>
|
||||||
@ -28,20 +36,28 @@ namespace osu.Game.Rulesets.Objects.Types
|
|||||||
/// </para>
|
/// </para>
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
||||||
Vector2 PositionAt(double progress);
|
public static Vector2 PositionAt(this IHasCurve obj, double progress)
|
||||||
|
=> obj.Curve.PositionAt(obj.ProgressAt(progress));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finds the progress along the curve, accounting for repeat logic.
|
/// Finds the progress along the curve, accounting for repeat logic.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
||||||
/// <returns>[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</returns>
|
/// <returns>[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</returns>
|
||||||
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;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines which repeat of the curve the progress point is on.
|
/// Determines which span of the curve the progress point is on.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
/// <param name="progress">[0, 1] where 0 is the beginning of the curve and 1 is the end of the curve.</param>
|
||||||
/// <returns>[0, RepeatCount] where 0 is the first run.</returns>
|
/// <returns>[0, SpanCount) where 0 is the first run.</returns>
|
||||||
int RepeatAt(double progress);
|
public static int SpanAt(this IHasCurve obj, double progress)
|
||||||
|
=> (int)(progress * obj.SpanCount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,4 +21,13 @@ namespace osu.Game.Rulesets.Objects.Types
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
List<List<SampleInfo>> RepeatSamples { get; }
|
List<List<SampleInfo>> RepeatSamples { get; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class HasRepeatsExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of times the length of this <see cref="IHasRepeats"/> spans.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">The object that has repeats.</param>
|
||||||
|
public static int SpanCount(this IHasRepeats obj) => obj.RepeatCount + 1;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user