mirror of
https://github.com/ppy/osu.git
synced 2024-09-21 22:07:25 +08:00
Introduce legacy timing point fp errors
This commit is contained in:
parent
1116703e92
commit
0ea13dea55
@ -10,6 +10,7 @@ using System.Collections.Generic;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using osu.Game.Audio;
|
using osu.Game.Audio;
|
||||||
using osu.Game.Beatmaps.ControlPoints;
|
using osu.Game.Beatmaps.ControlPoints;
|
||||||
|
using osu.Game.Beatmaps.Formats;
|
||||||
|
|
||||||
namespace osu.Game.Rulesets.Taiko.Beatmaps
|
namespace osu.Game.Rulesets.Taiko.Beatmaps
|
||||||
{
|
{
|
||||||
@ -82,37 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
{
|
{
|
||||||
case IHasDistance distanceData:
|
case IHasDistance distanceData:
|
||||||
{
|
{
|
||||||
// Number of spans of the object - one for the initial length and for each repeat
|
if (shouldConvertSliderToHits(obj, beatmap, distanceData, out var taikoDuration, out var tickSpacing))
|
||||||
int spans = (obj as IHasRepeats)?.SpanCount() ?? 1;
|
|
||||||
|
|
||||||
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
|
|
||||||
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
|
|
||||||
|
|
||||||
double speedAdjustment = difficultyPoint.SpeedMultiplier;
|
|
||||||
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 * 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 / speedAdjustedBeatLength;
|
|
||||||
// The duration of the taiko hit object
|
|
||||||
double taikoDuration = distance / taikoVelocity;
|
|
||||||
|
|
||||||
// The velocity of the osu! hit object - calculated as the velocity of a slider
|
|
||||||
double osuVelocity = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / speedAdjustedBeatLength;
|
|
||||||
// The duration of the osu! hit object
|
|
||||||
double osuDuration = distance / osuVelocity;
|
|
||||||
|
|
||||||
// osu-stable always uses the speed-adjusted beatlength to determine the velocities, but
|
|
||||||
// only uses it for tick rate if beatmap version < 8
|
|
||||||
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
|
|
||||||
speedAdjustedBeatLength *= speedAdjustment;
|
|
||||||
|
|
||||||
// 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 / spans);
|
|
||||||
|
|
||||||
if (!isForCurrentRuleset && tickSpacing > 0 && osuDuration < 2 * speedAdjustedBeatLength)
|
|
||||||
{
|
{
|
||||||
List<IList<HitSampleInfo>> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
|
List<IList<HitSampleInfo>> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List<IList<HitSampleInfo>>(new[] { samples });
|
||||||
|
|
||||||
@ -184,6 +155,52 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDistance distanceData, out double taikoDuration, out double tickSpacing)
|
||||||
|
{
|
||||||
|
// DO NOT CHANGE OR REFACTOR ANYTHING IN HERE WITHOUT TESTING AGAINST _ALL_ BEATMAPS.
|
||||||
|
// Some of these calculations look redundant, but they are not - extremely small floating point errors are introduced to maintain 1:1 compatibility with stable.
|
||||||
|
// Rounding cannot be used as an alternative since the error deltas have been observed to be between 1e-2 and 1e-6.
|
||||||
|
|
||||||
|
// The true distance, accounting for any repeats. This ends up being the drum roll distance later
|
||||||
|
int spans = (obj as IHasRepeats)?.SpanCount() ?? 1;
|
||||||
|
double distance = distanceData.Distance * spans * LEGACY_VELOCITY_MULTIPLIER;
|
||||||
|
|
||||||
|
TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime);
|
||||||
|
DifficultyControlPoint difficultyPoint = beatmap.ControlPointInfo.DifficultyPointAt(obj.StartTime);
|
||||||
|
|
||||||
|
double beatLength;
|
||||||
|
#pragma warning disable 618
|
||||||
|
if (difficultyPoint is LegacyBeatmapDecoder.LegacyDifficultyControlPoint legacyDifficultyPoint)
|
||||||
|
#pragma warning restore 618
|
||||||
|
beatLength = timingPoint.BeatLength * legacyDifficultyPoint.BpmMultiplier;
|
||||||
|
else
|
||||||
|
beatLength = timingPoint.BeatLength / difficultyPoint.SpeedMultiplier;
|
||||||
|
|
||||||
|
double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.BeatmapInfo.BaseDifficulty.SliderMultiplier / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate;
|
||||||
|
|
||||||
|
// The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll.
|
||||||
|
double taikoVelocity = sliderScoringPointDistance * beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate;
|
||||||
|
taikoDuration = distance / taikoVelocity * beatLength;
|
||||||
|
|
||||||
|
if (isForCurrentRuleset)
|
||||||
|
{
|
||||||
|
tickSpacing = 0;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
double osuVelocity = taikoVelocity * (1000f / beatLength);
|
||||||
|
|
||||||
|
// osu-stable always uses the speed-adjusted beatlength to determine the osu! velocity, but only uses it for conversion if beatmap version < 8
|
||||||
|
if (beatmap.BeatmapInfo.BeatmapVersion >= 8)
|
||||||
|
beatLength = timingPoint.BeatLength;
|
||||||
|
|
||||||
|
// If the drum roll is to be split into hit circles, assume the ticks are 1/8 spaced within the duration of one beat
|
||||||
|
tickSpacing = Math.Min(beatLength / beatmap.BeatmapInfo.BaseDifficulty.SliderTickRate, taikoDuration / spans);
|
||||||
|
|
||||||
|
return tickSpacing > 0
|
||||||
|
&& distance / osuVelocity * 1000 < 2 * beatLength;
|
||||||
|
}
|
||||||
|
|
||||||
protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap();
|
protected override Beatmap<TaikoHitObject> CreateBeatmap() => new TaikoBeatmap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -369,7 +369,9 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
addControlPoint(time, controlPoint, true);
|
addControlPoint(time, controlPoint, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
addControlPoint(time, new LegacyDifficultyControlPoint
|
#pragma warning disable 618
|
||||||
|
addControlPoint(time, new LegacyDifficultyControlPoint(beatLength)
|
||||||
|
#pragma warning restore 618
|
||||||
{
|
{
|
||||||
SpeedMultiplier = speedMultiplier,
|
SpeedMultiplier = speedMultiplier,
|
||||||
}, timingChange);
|
}, timingChange);
|
||||||
|
@ -159,11 +159,20 @@ namespace osu.Game.Beatmaps.Formats
|
|||||||
Mania,
|
Mania,
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class LegacyDifficultyControlPoint : DifficultyControlPoint
|
[Obsolete("Do not use unless you're a legacy ruleset and 100% sure.")]
|
||||||
|
public class LegacyDifficultyControlPoint : DifficultyControlPoint
|
||||||
{
|
{
|
||||||
public LegacyDifficultyControlPoint()
|
/// <summary>
|
||||||
|
/// Legacy BPM multiplier that introduces floating-point errors for rulesets that depend on it.
|
||||||
|
/// DO NOT USE THIS UNLESS 100% SURE.
|
||||||
|
/// </summary>
|
||||||
|
public readonly float BpmMultiplier;
|
||||||
|
|
||||||
|
public LegacyDifficultyControlPoint(double beatLength)
|
||||||
{
|
{
|
||||||
SpeedMultiplierBindable.Precision = double.Epsilon;
|
SpeedMultiplierBindable.Precision = double.Epsilon;
|
||||||
|
|
||||||
|
BpmMultiplier = beatLength < 0 ? Math.Clamp((float)-beatLength, 10, 10000) / 100f : 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user