1
0
mirror of https://github.com/ppy/osu.git synced 2025-01-12 16:02:55 +08:00

Merge pull request #24932 from smoogipoo/spinner-od-based-max-rpm

Cap maximum spinner RPM based on OD
This commit is contained in:
Dean Herbert 2023-09-27 15:56:27 +09:00 committed by GitHub
commit 0a208a5a47
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 52 additions and 5 deletions

View File

@ -43,7 +43,8 @@ namespace osu.Game.Rulesets.Osu.Tests
AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8); AddUntilStep("Pitch starts low", () => getSpinningSample().Frequency.Value < 0.8);
AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8); AddUntilStep("Pitch increases", () => getSpinningSample().Frequency.Value > 0.8);
PausableSkinnableSound getSpinningSample() => drawableSpinner.ChildrenOfType<PausableSkinnableSound>().FirstOrDefault(s => s.Samples.Any(i => i.LookupNames.Any(l => l.Contains("spinnerspin")))); PausableSkinnableSound getSpinningSample() =>
drawableSpinner.ChildrenOfType<PausableSkinnableSound>().FirstOrDefault(s => s.Samples.Any(i => i.LookupNames.Any(l => l.Contains("spinnerspin"))));
} }
[TestCase(false)] [TestCase(false)]
@ -64,6 +65,39 @@ namespace osu.Game.Rulesets.Osu.Tests
AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1); AddUntilStep("Short spinner implicitly completes", () => drawableSpinner.Progress == 1);
} }
[TestCase(0, 4, 6)]
[TestCase(5, 7, 10)]
[TestCase(10, 11, 8)]
public void TestSpinnerSpinRequirements(int od, int normalTicks, int bonusTicks)
{
Spinner spinner = null;
AddStep("add spinner", () => SetContents(_ =>
{
spinner = new Spinner
{
StartTime = Time.Current,
EndTime = Time.Current + 3000,
Samples = new List<HitSampleInfo>
{
new HitSampleInfo(HitSampleInfo.HIT_NORMAL)
}
};
spinner.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty { OverallDifficulty = od });
return drawableSpinner = new TestDrawableSpinner(spinner, true)
{
Anchor = Anchor.Centre,
Depth = depthIndex++,
Scale = new Vector2(0.75f)
};
}));
AddAssert("number of normal ticks matches", () => spinner.SpinsRequired, () => Is.EqualTo(normalTicks));
AddAssert("number of bonus ticks matches", () => spinner.MaximumBonusSpins, () => Is.EqualTo(bonusTicks));
}
private Drawable testSingle(float circleSize, bool auto = false, double length = 3000) private Drawable testSingle(float circleSize, bool auto = false, double length = 3000)
{ {
const double delay = 2000; const double delay = 2000;

View File

@ -18,6 +18,16 @@ namespace osu.Game.Rulesets.Osu.Objects
{ {
public class Spinner : OsuHitObject, IHasDuration public class Spinner : OsuHitObject, IHasDuration
{ {
/// <summary>
/// The RPM required to clear the spinner at ODs [ 0, 5, 10 ].
/// </summary>
private static readonly (int min, int mid, int max) clear_rpm_range = (90, 150, 225);
/// <summary>
/// The RPM required to complete the spinner and receive full score at ODs [ 0, 5, 10 ].
/// </summary>
private static readonly (int min, int mid, int max) complete_rpm_range = (250, 380, 430);
public double EndTime public double EndTime
{ {
get => StartTime + Duration; get => StartTime + Duration;
@ -52,13 +62,16 @@ namespace osu.Game.Rulesets.Osu.Objects
{ {
base.ApplyDefaultsToSelf(controlPointInfo, difficulty); base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
const double maximum_rotations_per_second = 477f / 60f; // The average RPS required over the length of the spinner to clear the spinner.
double minRps = IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, clear_rpm_range) / 60;
// The RPS required over the length of the spinner to receive full score (all normal + bonus ticks).
double maxRps = IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, complete_rpm_range) / 60;
double secondsDuration = Duration / 1000; double secondsDuration = Duration / 1000;
double minimumRotationsPerSecond = IBeatmapDifficultyInfo.DifficultyRange(difficulty.OverallDifficulty, 1.5, 2.5, 3.75);
SpinsRequired = (int)(secondsDuration * minimumRotationsPerSecond); SpinsRequired = (int)(minRps * secondsDuration);
MaximumBonusSpins = (int)((maximum_rotations_per_second - minimumRotationsPerSecond) * secondsDuration) - bonus_spins_gap; MaximumBonusSpins = Math.Max(0, (int)(maxRps * secondsDuration) - SpinsRequired - bonus_spins_gap);
} }
protected override void CreateNestedHitObjects(CancellationToken cancellationToken) protected override void CreateNestedHitObjects(CancellationToken cancellationToken)