Move approach rate to EffectControlPoint

This commit is contained in:
Dean Herbert 2021-08-31 23:59:36 +09:00
parent 4eb09ea1a9
commit a3d9ab1e2e
29 changed files with 88 additions and 59 deletions

View File

@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Catch.Objects
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SpeedMultiplier; double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
Velocity = scoringDistance / timingPoint.BeatLength; Velocity = scoringDistance / timingPoint.BeatLength;
TickDistance = scoringDistance / difficulty.SliderTickRate; TickDistance = scoringDistance / difficulty.SliderTickRate;

View File

@ -388,7 +388,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}, },
}; };
beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f }); beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SliderVelocity = 0.1f });
} }
AddStep("load player", () => AddStep("load player", () =>

View File

@ -148,7 +148,7 @@ namespace osu.Game.Rulesets.Mania.Tests
}, },
}); });
Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f }); Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SliderVelocity = 0.1f });
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });

View File

@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy
#pragma warning restore 618 #pragma warning restore 618
beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier; beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
else else
beatLength = timingPoint.BeatLength / difficultyPoint.SpeedMultiplier; beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity;
SpanCount = repeatsData?.SpanCount() ?? 1; SpanCount = repeatsData?.SpanCount() ?? 1;
StartTime = (int)Math.Round(hitObject.StartTime); StartTime = (int)Math.Round(hitObject.StartTime);

View File

@ -407,7 +407,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
}); });
Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f }); Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SliderVelocity = 0.1f });
SelectedMods.Value = new[] { new OsuModClassic() }; SelectedMods.Value = new[] { new OsuModClassic() };

View File

@ -329,7 +329,7 @@ namespace osu.Game.Rulesets.Osu.Tests
private Drawable createDrawable(Slider slider, float circleSize, double speedMultiplier) private Drawable createDrawable(Slider slider, float circleSize, double speedMultiplier)
{ {
var cpi = new ControlPointInfo(); var cpi = new ControlPointInfo();
cpi.Add(0, new DifficultyControlPoint { SpeedMultiplier = speedMultiplier }); cpi.Add(0, new DifficultyControlPoint { SliderVelocity = speedMultiplier });
slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize, SliderTickRate = 3 }); slider.ApplyDefaults(cpi, new BeatmapDifficulty { CircleSize = circleSize, SliderTickRate = 3 });

View File

@ -362,7 +362,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
}); });
Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f }); Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SliderVelocity = 0.1f });
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });

View File

@ -369,7 +369,7 @@ namespace osu.Game.Rulesets.Osu.Tests
}, },
}); });
Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 0.1f }); Beatmap.Value.Beatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SliderVelocity = 0.1f });
var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } });

View File

@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Beatmaps
LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset, LegacyLastTickOffset = (original as IHasLegacyLastTickOffset)?.LegacyLastTickOffset,
// prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance. // prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance.
// this results in more (or less) ticks being generated in <v8 maps for the same time duration. // this results in more (or less) ticks being generated in <v8 maps for the same time duration.
TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(original.StartTime).SpeedMultiplier : 1 TickDistanceMultiplier = beatmap.BeatmapInfo.BeatmapVersion < 8 ? 1f / ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(original.StartTime).SliderVelocity : 1
}.Yield(); }.Yield();
case IHasDuration endTimeData: case IHasDuration endTimeData:

View File

@ -141,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Objects
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
double scoringDistance = BASE_SCORING_DISTANCE * difficulty.SliderMultiplier * DifficultyControlPoint.SpeedMultiplier; double scoringDistance = BASE_SCORING_DISTANCE * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
Velocity = scoringDistance / timingPoint.BeatLength; Velocity = scoringDistance / timingPoint.BeatLength;
TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier; TickDistance = scoringDistance / difficulty.SliderTickRate * TickDistanceMultiplier;

View File

@ -162,7 +162,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
#pragma warning restore 618 #pragma warning restore 618
beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier; beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
else else
beatLength = timingPoint.BeatLength / difficultyPoint.SpeedMultiplier; beatLength = timingPoint.BeatLength / difficultyPoint.SliderVelocity;
double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate; double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate;

View File

@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Taiko.Mods
protected double MultiplierAt(HitObject obj) protected double MultiplierAt(HitObject obj)
{ {
double beatLength = controlPointInfo.TimingPointAt(obj.StartTime).BeatLength; double beatLength = controlPointInfo.TimingPointAt(obj.StartTime).BeatLength;
double speedMultiplier = obj.DifficultyControlPoint.SpeedMultiplier; double speedMultiplier = obj.DifficultyControlPoint.SliderVelocity;
return speedMultiplier * TimingControlPoint.DEFAULT_BEAT_LENGTH / beatLength; return speedMultiplier * TimingControlPoint.DEFAULT_BEAT_LENGTH / beatLength;
} }

View File

@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Taiko.Objects
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
double scoringDistance = base_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SpeedMultiplier; double scoringDistance = base_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
Velocity = scoringDistance / timingPoint.BeatLength; Velocity = scoringDistance / timingPoint.BeatLength;
tickSpacing = timingPoint.BeatLength / TickRate; tickSpacing = timingPoint.BeatLength / TickRate;

View File

@ -191,15 +191,15 @@ namespace osu.Game.Tests.Beatmaps.Formats
var difficultyPoint = controlPoints.DifficultyPointAt(0); var difficultyPoint = controlPoints.DifficultyPointAt(0);
Assert.AreEqual(0, difficultyPoint.Time); Assert.AreEqual(0, difficultyPoint.Time);
Assert.AreEqual(1.0, difficultyPoint.SpeedMultiplier); Assert.AreEqual(1.0, difficultyPoint.SliderVelocity);
difficultyPoint = controlPoints.DifficultyPointAt(48428); difficultyPoint = controlPoints.DifficultyPointAt(48428);
Assert.AreEqual(0, difficultyPoint.Time); Assert.AreEqual(0, difficultyPoint.Time);
Assert.AreEqual(1.0, difficultyPoint.SpeedMultiplier); Assert.AreEqual(1.0, difficultyPoint.SliderVelocity);
difficultyPoint = controlPoints.DifficultyPointAt(116999); difficultyPoint = controlPoints.DifficultyPointAt(116999);
Assert.AreEqual(116999, difficultyPoint.Time); Assert.AreEqual(116999, difficultyPoint.Time);
Assert.AreEqual(0.75, difficultyPoint.SpeedMultiplier, 0.1); Assert.AreEqual(0.75, difficultyPoint.SliderVelocity, 0.1);
var soundPoint = controlPoints.SamplePointAt(0); var soundPoint = controlPoints.SamplePointAt(0);
Assert.AreEqual(956, soundPoint.Time); Assert.AreEqual(956, soundPoint.Time);
@ -248,10 +248,10 @@ namespace osu.Game.Tests.Beatmaps.Formats
Assert.That(controlPoints.EffectPoints.Count, Is.EqualTo(3)); Assert.That(controlPoints.EffectPoints.Count, Is.EqualTo(3));
Assert.That(controlPoints.SamplePoints.Count, Is.EqualTo(3)); Assert.That(controlPoints.SamplePoints.Count, Is.EqualTo(3));
Assert.That(controlPoints.DifficultyPointAt(500).SpeedMultiplier, Is.EqualTo(1.5).Within(0.1)); Assert.That(controlPoints.DifficultyPointAt(500).SliderVelocity, Is.EqualTo(1.5).Within(0.1));
Assert.That(controlPoints.DifficultyPointAt(1500).SpeedMultiplier, Is.EqualTo(1.5).Within(0.1)); Assert.That(controlPoints.DifficultyPointAt(1500).SliderVelocity, Is.EqualTo(1.5).Within(0.1));
Assert.That(controlPoints.DifficultyPointAt(2500).SpeedMultiplier, Is.EqualTo(0.75).Within(0.1)); Assert.That(controlPoints.DifficultyPointAt(2500).SliderVelocity, Is.EqualTo(0.75).Within(0.1));
Assert.That(controlPoints.DifficultyPointAt(3500).SpeedMultiplier, Is.EqualTo(1.5).Within(0.1)); Assert.That(controlPoints.DifficultyPointAt(3500).SliderVelocity, Is.EqualTo(1.5).Within(0.1));
Assert.That(controlPoints.EffectPointAt(500).KiaiMode, Is.True); Assert.That(controlPoints.EffectPointAt(500).KiaiMode, Is.True);
Assert.That(controlPoints.EffectPointAt(1500).KiaiMode, Is.True); Assert.That(controlPoints.EffectPointAt(1500).KiaiMode, Is.True);
@ -280,8 +280,8 @@ namespace osu.Game.Tests.Beatmaps.Formats
{ {
var controlPoints = (LegacyControlPointInfo)decoder.Decode(stream).ControlPointInfo; var controlPoints = (LegacyControlPointInfo)decoder.Decode(stream).ControlPointInfo;
Assert.That(controlPoints?.DifficultyPointAt(0).SpeedMultiplier, Is.EqualTo(0.5).Within(0.1)); Assert.That(controlPoints?.DifficultyPointAt(0).SliderVelocity, Is.EqualTo(0.5).Within(0.1));
Assert.That(controlPoints?.DifficultyPointAt(2000).SpeedMultiplier, Is.EqualTo(1).Within(0.1)); Assert.That(controlPoints?.DifficultyPointAt(2000).SliderVelocity, Is.EqualTo(1).Within(0.1));
} }
} }
@ -395,10 +395,10 @@ namespace osu.Game.Tests.Beatmaps.Formats
{ {
var controlPointInfo = (LegacyControlPointInfo)decoder.Decode(stream).ControlPointInfo; var controlPointInfo = (LegacyControlPointInfo)decoder.Decode(stream).ControlPointInfo;
Assert.That(controlPointInfo.DifficultyPointAt(5).SpeedMultiplier, Is.EqualTo(1)); Assert.That(controlPointInfo.DifficultyPointAt(5).SliderVelocity, Is.EqualTo(1));
Assert.That(controlPointInfo.DifficultyPointAt(1000).SpeedMultiplier, Is.EqualTo(10)); Assert.That(controlPointInfo.DifficultyPointAt(1000).SliderVelocity, Is.EqualTo(10));
Assert.That(controlPointInfo.DifficultyPointAt(2000).SpeedMultiplier, Is.EqualTo(1.8518518518518519d)); Assert.That(controlPointInfo.DifficultyPointAt(2000).SliderVelocity, Is.EqualTo(1.8518518518518519d));
Assert.That(controlPointInfo.DifficultyPointAt(3000).SpeedMultiplier, Is.EqualTo(0.5)); Assert.That(controlPointInfo.DifficultyPointAt(3000).SliderVelocity, Is.EqualTo(0.5));
} }
} }

View File

@ -56,7 +56,7 @@ namespace osu.Game.Tests.Editing
composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 1; composer.EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier = 1;
composer.EditorBeatmap.ControlPointInfo.Clear(); composer.EditorBeatmap.ControlPointInfo.Clear();
composer.EditorBeatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = 1 }); composer.EditorBeatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SliderVelocity = 1 });
composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 }); composer.EditorBeatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = 1000 });
}); });
@ -76,7 +76,7 @@ namespace osu.Game.Tests.Editing
AddStep($"set multiplier = {multiplier}", () => AddStep($"set multiplier = {multiplier}", () =>
{ {
composer.EditorBeatmap.ControlPointInfo.Clear(); composer.EditorBeatmap.ControlPointInfo.Clear();
composer.EditorBeatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SpeedMultiplier = multiplier }); composer.EditorBeatmap.ControlPointInfo.Add(0, new DifficultyControlPoint { SliderVelocity = multiplier });
}); });
assertSnapDistance(100 * multiplier); assertSnapDistance(100 * multiplier);

View File

@ -55,7 +55,7 @@ namespace osu.Game.Tests.NonVisual
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0)); Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(0));
Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0)); Assert.That(cpi.AllControlPoints.Count(), Is.EqualTo(0));
cpi.Add(1000, new DifficultyControlPoint { SpeedMultiplier = 2 }); // is not redundant cpi.Add(1000, new DifficultyControlPoint { SliderVelocity = 2 }); // is not redundant
Assert.That(cpi.Groups.Count, Is.EqualTo(1)); Assert.That(cpi.Groups.Count, Is.EqualTo(1));
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(1)); Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(1));
@ -180,11 +180,11 @@ namespace osu.Game.Tests.NonVisual
Assert.That(cpi.Groups.Count, Is.EqualTo(1)); Assert.That(cpi.Groups.Count, Is.EqualTo(1));
group.Add(new DifficultyControlPoint()); group.Add(new DifficultyControlPoint());
group.Add(new DifficultyControlPoint { SpeedMultiplier = 2 }); group.Add(new DifficultyControlPoint { SliderVelocity = 2 });
Assert.That(group.ControlPoints.Count, Is.EqualTo(1)); Assert.That(group.ControlPoints.Count, Is.EqualTo(1));
Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(1)); Assert.That(cpi.DifficultyPoints.Count, Is.EqualTo(1));
Assert.That(cpi.DifficultyPoints.First().SpeedMultiplier, Is.EqualTo(2)); Assert.That(cpi.DifficultyPoints.First().SliderVelocity, Is.EqualTo(2));
} }
[Test] [Test]
@ -214,8 +214,8 @@ namespace osu.Game.Tests.NonVisual
cpi.Add(1000, new TimingControlPoint { BeatLength = 500 }); cpi.Add(1000, new TimingControlPoint { BeatLength = 500 });
cpi.Add(10000, new TimingControlPoint { BeatLength = 200 }); cpi.Add(10000, new TimingControlPoint { BeatLength = 200 });
cpi.Add(5000, new TimingControlPoint { BeatLength = 100 }); cpi.Add(5000, new TimingControlPoint { BeatLength = 100 });
cpi.Add(3000, new DifficultyControlPoint { SpeedMultiplier = 2 }); cpi.Add(3000, new DifficultyControlPoint { SliderVelocity = 2 });
cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SpeedMultiplier = 4 }); cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SliderVelocity = 4 });
cpi.GroupAt(1000).Add(new SampleControlPoint { SampleVolume = 0 }); cpi.GroupAt(1000).Add(new SampleControlPoint { SampleVolume = 0 });
cpi.GroupAt(8000, true).Add(new EffectControlPoint { KiaiMode = true }); cpi.GroupAt(8000, true).Add(new EffectControlPoint { KiaiMode = true });
@ -236,8 +236,8 @@ namespace osu.Game.Tests.NonVisual
cpi.Add(1000, new TimingControlPoint { BeatLength = 500 }); cpi.Add(1000, new TimingControlPoint { BeatLength = 500 });
cpi.Add(10000, new TimingControlPoint { BeatLength = 200 }); cpi.Add(10000, new TimingControlPoint { BeatLength = 200 });
cpi.Add(5000, new TimingControlPoint { BeatLength = 100 }); cpi.Add(5000, new TimingControlPoint { BeatLength = 100 });
cpi.Add(3000, new DifficultyControlPoint { SpeedMultiplier = 2 }); cpi.Add(3000, new DifficultyControlPoint { SliderVelocity = 2 });
cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SpeedMultiplier = 4 }); cpi.GroupAt(7000, true).Add(new DifficultyControlPoint { SliderVelocity = 4 });
cpi.GroupAt(1000).Add(new SampleControlPoint { SampleVolume = 0 }); cpi.GroupAt(1000).Add(new SampleControlPoint { SampleVolume = 0 });
cpi.GroupAt(8000, true).Add(new EffectControlPoint { KiaiMode = true }); cpi.GroupAt(8000, true).Add(new EffectControlPoint { KiaiMode = true });

View File

@ -93,9 +93,9 @@ namespace osu.Game.Tests.Visual.Gameplay
private IList<MultiplierControlPoint> testControlPoints => new List<MultiplierControlPoint> private IList<MultiplierControlPoint> testControlPoints => new List<MultiplierControlPoint>
{ {
new MultiplierControlPoint(time_range) { DifficultyPoint = { SpeedMultiplier = 1.25 } }, new MultiplierControlPoint(time_range) { DifficultyPoint = { SliderVelocity = 1.25 } },
new MultiplierControlPoint(1.5 * time_range) { DifficultyPoint = { SpeedMultiplier = 1 } }, new MultiplierControlPoint(1.5 * time_range) { DifficultyPoint = { SliderVelocity = 1 } },
new MultiplierControlPoint(2 * time_range) { DifficultyPoint = { SpeedMultiplier = 1.5 } } new MultiplierControlPoint(2 * time_range) { DifficultyPoint = { SliderVelocity = 1.5 } }
}; };
[Test] [Test]

View File

@ -7,17 +7,20 @@ using osuTK.Graphics;
namespace osu.Game.Beatmaps.ControlPoints namespace osu.Game.Beatmaps.ControlPoints
{ {
/// <remarks>
/// Note that going forward, this control point type should always be assigned directly to HitObjects.
/// </remarks>
public class DifficultyControlPoint : ControlPoint public class DifficultyControlPoint : ControlPoint
{ {
public static readonly DifficultyControlPoint DEFAULT = new DifficultyControlPoint public static readonly DifficultyControlPoint DEFAULT = new DifficultyControlPoint
{ {
SpeedMultiplierBindable = { Disabled = true }, SliderVelocityBindable = { Disabled = true },
}; };
/// <summary> /// <summary>
/// The speed multiplier at this control point. /// The slider velocity at this control point.
/// </summary> /// </summary>
public readonly BindableDouble SpeedMultiplierBindable = new BindableDouble(1) public readonly BindableDouble SliderVelocityBindable = new BindableDouble(1)
{ {
Precision = 0.01, Precision = 0.01,
Default = 1, Default = 1,
@ -30,19 +33,19 @@ namespace osu.Game.Beatmaps.ControlPoints
/// <summary> /// <summary>
/// The speed multiplier at this control point. /// The speed multiplier at this control point.
/// </summary> /// </summary>
public double SpeedMultiplier public double SliderVelocity
{ {
get => SpeedMultiplierBindable.Value; get => SliderVelocityBindable.Value;
set => SpeedMultiplierBindable.Value = value; set => SliderVelocityBindable.Value = value;
} }
public override bool IsRedundant(ControlPoint existing) public override bool IsRedundant(ControlPoint existing)
=> existing is DifficultyControlPoint existingDifficulty => existing is DifficultyControlPoint existingDifficulty
&& SpeedMultiplier == existingDifficulty.SpeedMultiplier; && SliderVelocity == existingDifficulty.SliderVelocity;
public override void CopyFrom(ControlPoint other) public override void CopyFrom(ControlPoint other)
{ {
SpeedMultiplier = ((DifficultyControlPoint)other).SpeedMultiplier; SliderVelocity = ((DifficultyControlPoint)other).SliderVelocity;
base.CopyFrom(other); base.CopyFrom(other);
} }

View File

@ -12,7 +12,8 @@ namespace osu.Game.Beatmaps.ControlPoints
public static readonly EffectControlPoint DEFAULT = new EffectControlPoint public static readonly EffectControlPoint DEFAULT = new EffectControlPoint
{ {
KiaiModeBindable = { Disabled = true }, KiaiModeBindable = { Disabled = true },
OmitFirstBarLineBindable = { Disabled = true } OmitFirstBarLineBindable = { Disabled = true },
ApproachRateBindable = { Disabled = true }
}; };
/// <summary> /// <summary>
@ -20,6 +21,26 @@ namespace osu.Game.Beatmaps.ControlPoints
/// </summary> /// </summary>
public readonly BindableBool OmitFirstBarLineBindable = new BindableBool(); public readonly BindableBool OmitFirstBarLineBindable = new BindableBool();
/// <summary>
/// The relative approach rate at this control point.
/// </summary>
public readonly BindableDouble ApproachRateBindable = new BindableDouble(1)
{
Precision = 0.01,
Default = 1,
MinValue = 0.01,
MaxValue = 10
};
/// <summary>
/// The relative approach rate.
/// </summary>
public double ApproachRate
{
get => ApproachRateBindable.Value;
set => ApproachRateBindable.Value = value;
}
public override Color4 GetRepresentingColour(OsuColour colours) => colours.Purple; public override Color4 GetRepresentingColour(OsuColour colours) => colours.Purple;
/// <summary> /// <summary>
@ -49,12 +70,14 @@ namespace osu.Game.Beatmaps.ControlPoints
=> !OmitFirstBarLine => !OmitFirstBarLine
&& existing is EffectControlPoint existingEffect && existing is EffectControlPoint existingEffect
&& KiaiMode == existingEffect.KiaiMode && KiaiMode == existingEffect.KiaiMode
&& OmitFirstBarLine == existingEffect.OmitFirstBarLine; && OmitFirstBarLine == existingEffect.OmitFirstBarLine
&& ApproachRate == existingEffect.ApproachRate;
public override void CopyFrom(ControlPoint other) public override void CopyFrom(ControlPoint other)
{ {
KiaiMode = ((EffectControlPoint)other).KiaiMode; KiaiMode = ((EffectControlPoint)other).KiaiMode;
OmitFirstBarLine = ((EffectControlPoint)other).OmitFirstBarLine; OmitFirstBarLine = ((EffectControlPoint)other).OmitFirstBarLine;
ApproachRate = ((EffectControlPoint)other).ApproachRate;
base.CopyFrom(other); base.CopyFrom(other);
} }

View File

@ -8,6 +8,9 @@ using osuTK.Graphics;
namespace osu.Game.Beatmaps.ControlPoints namespace osu.Game.Beatmaps.ControlPoints
{ {
/// <remarks>
/// Note that going forward, this control point type should always be assigned directly to HitObjects.
/// </remarks>
public class SampleControlPoint : ControlPoint public class SampleControlPoint : ControlPoint
{ {
public const string DEFAULT_BANK = "normal"; public const string DEFAULT_BANK = "normal";

View File

@ -380,7 +380,7 @@ namespace osu.Game.Beatmaps.Formats
addControlPoint(time, new LegacyDifficultyControlPoint(beatLength) addControlPoint(time, new LegacyDifficultyControlPoint(beatLength)
#pragma warning restore 618 #pragma warning restore 618
{ {
SpeedMultiplier = speedMultiplier, SliderVelocity = speedMultiplier,
}, timingChange); }, timingChange);
addControlPoint(time, new EffectControlPoint addControlPoint(time, new EffectControlPoint

View File

@ -219,7 +219,7 @@ namespace osu.Game.Beatmaps.Formats
// Output any remaining effects as secondary non-timing control point. // Output any remaining effects as secondary non-timing control point.
var difficultyPoint = ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(group.Time); var difficultyPoint = ((LegacyControlPointInfo)beatmap.ControlPointInfo).DifficultyPointAt(group.Time);
writer.Write(FormattableString.Invariant($"{group.Time},")); writer.Write(FormattableString.Invariant($"{group.Time},"));
writer.Write(FormattableString.Invariant($"{-100 / difficultyPoint.SpeedMultiplier},")); writer.Write(FormattableString.Invariant($"{-100 / difficultyPoint.SliderVelocity},"));
outputControlPointAt(group.Time, false); outputControlPointAt(group.Time, false);
} }

View File

@ -181,7 +181,7 @@ namespace osu.Game.Beatmaps.Formats
public LegacyDifficultyControlPoint() public LegacyDifficultyControlPoint()
{ {
SpeedMultiplierBindable.Precision = double.Epsilon; SliderVelocityBindable.Precision = double.Epsilon;
} }
public override void CopyFrom(ControlPoint other) public override void CopyFrom(ControlPoint other)

View File

@ -392,7 +392,7 @@ namespace osu.Game.Rulesets.Edit
public override float GetBeatSnapDistanceAt(double referenceTime) public override float GetBeatSnapDistanceAt(double referenceTime)
{ {
DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime); DifficultyControlPoint difficultyPoint = EditorBeatmap.ControlPointInfo.DifficultyPointAt(referenceTime);
return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SpeedMultiplier / BeatSnapProvider.BeatDivisor); return (float)(100 * EditorBeatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier * difficultyPoint.SliderVelocity / BeatSnapProvider.BeatDivisor);
} }
public override float DurationToDistance(double referenceTime, double duration) public override float DurationToDistance(double referenceTime, double duration)

View File

@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Objects.Legacy
TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime);
double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SpeedMultiplier; double scoringDistance = base_scoring_distance * difficulty.SliderMultiplier * DifficultyControlPoint.SliderVelocity;
Velocity = scoringDistance / timingPoint.BeatLength; Velocity = scoringDistance / timingPoint.BeatLength;
} }

View File

@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Timing
/// <summary> /// <summary>
/// The aggregate multiplier which this <see cref="MultiplierControlPoint"/> provides. /// The aggregate multiplier which this <see cref="MultiplierControlPoint"/> provides.
/// </summary> /// </summary>
public double Multiplier => Velocity * DifficultyPoint.SpeedMultiplier * BaseBeatLength / TimingPoint.BeatLength; public double Multiplier => Velocity * DifficultyPoint.SliderVelocity * BaseBeatLength / TimingPoint.BeatLength;
/// <summary> /// <summary>
/// The base beat length to scale the <see cref="TimingPoint"/> provided multiplier relative to. /// The base beat length to scale the <see cref="TimingPoint"/> provided multiplier relative to.

View File

@ -13,7 +13,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline
public DifficultyPointPiece(DifficultyControlPoint point) public DifficultyPointPiece(DifficultyControlPoint point)
: base(point) : base(point)
{ {
speedMultiplier = point.SpeedMultiplierBindable.GetBoundCopy(); speedMultiplier = point.SliderVelocityBindable.GetBoundCopy();
Y = Height; Y = Height;
} }

View File

@ -18,7 +18,7 @@ namespace osu.Game.Screens.Edit.Timing
{ {
multiplierSlider = new SliderWithTextBoxInput<double>("Speed Multiplier") multiplierSlider = new SliderWithTextBoxInput<double>("Speed Multiplier")
{ {
Current = new DifficultyControlPoint().SpeedMultiplierBindable, Current = new DifficultyControlPoint().SliderVelocityBindable,
KeyboardStep = 0.1f KeyboardStep = 0.1f
} }
}); });
@ -28,12 +28,12 @@ namespace osu.Game.Screens.Edit.Timing
{ {
if (point.NewValue != null) if (point.NewValue != null)
{ {
var selectedPointBindable = point.NewValue.SpeedMultiplierBindable; var selectedPointBindable = point.NewValue.SliderVelocityBindable;
// there may be legacy control points, which contain infinite precision for compatibility reasons (see LegacyDifficultyControlPoint). // there may be legacy control points, which contain infinite precision for compatibility reasons (see LegacyDifficultyControlPoint).
// generally that level of precision could only be set by externally editing the .osu file, so at the point // generally that level of precision could only be set by externally editing the .osu file, so at the point
// a user is looking to update this within the editor it should be safe to obliterate this additional precision. // a user is looking to update this within the editor it should be safe to obliterate this additional precision.
double expectedPrecision = new DifficultyControlPoint().SpeedMultiplierBindable.Precision; double expectedPrecision = new DifficultyControlPoint().SliderVelocityBindable.Precision;
if (selectedPointBindable.Precision < expectedPrecision) if (selectedPointBindable.Precision < expectedPrecision)
selectedPointBindable.Precision = expectedPrecision; selectedPointBindable.Precision = expectedPrecision;

View File

@ -18,7 +18,7 @@ namespace osu.Game.Screens.Edit.Timing.RowAttributes
public DifficultyRowAttribute(DifficultyControlPoint difficulty) public DifficultyRowAttribute(DifficultyControlPoint difficulty)
: base(difficulty, "difficulty") : base(difficulty, "difficulty")
{ {
speedMultiplier = difficulty.SpeedMultiplierBindable.GetBoundCopy(); speedMultiplier = difficulty.SliderVelocityBindable.GetBoundCopy();
} }
[BackgroundDependencyLoader] [BackgroundDependencyLoader]