mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 07:42:57 +08:00
Merge pull request #11133 from smoogipoo/difficulty-adjustment-extension
Allow ModDifficultyAdjustment to extend beyond the sane limits of the game
This commit is contained in:
commit
6e2994f98e
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
public class CatchModDifficultyAdjust : ModDifficultyAdjust
|
||||
{
|
||||
[SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)]
|
||||
public BindableNumber<float> CircleSize { get; } = new BindableFloat
|
||||
public BindableNumber<float> CircleSize { get; } = new BindableFloatWithLimitExtension
|
||||
{
|
||||
Precision = 0.1f,
|
||||
MinValue = 1,
|
||||
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
};
|
||||
|
||||
[SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)]
|
||||
public BindableNumber<float> ApproachRate { get; } = new BindableFloat
|
||||
public BindableNumber<float> ApproachRate { get; } = new BindableFloatWithLimitExtension
|
||||
{
|
||||
Precision = 0.1f,
|
||||
MinValue = 1,
|
||||
@ -31,6 +31,14 @@ namespace osu.Game.Rulesets.Catch.Mods
|
||||
Value = 5,
|
||||
};
|
||||
|
||||
protected override void ApplyLimits(bool extended)
|
||||
{
|
||||
base.ApplyLimits(extended);
|
||||
|
||||
CircleSize.MaxValue = extended ? 11 : 10;
|
||||
ApproachRate.MaxValue = extended ? 11 : 10;
|
||||
}
|
||||
|
||||
public override string SettingDescription
|
||||
{
|
||||
get
|
||||
|
@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
public class OsuModDifficultyAdjust : ModDifficultyAdjust
|
||||
{
|
||||
[SettingSource("Circle Size", "Override a beatmap's set CS.", FIRST_SETTING_ORDER - 1)]
|
||||
public BindableNumber<float> CircleSize { get; } = new BindableFloat
|
||||
public BindableNumber<float> CircleSize { get; } = new BindableFloatWithLimitExtension
|
||||
{
|
||||
Precision = 0.1f,
|
||||
MinValue = 0,
|
||||
@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
};
|
||||
|
||||
[SettingSource("Approach Rate", "Override a beatmap's set AR.", LAST_SETTING_ORDER + 1)]
|
||||
public BindableNumber<float> ApproachRate { get; } = new BindableFloat
|
||||
public BindableNumber<float> ApproachRate { get; } = new BindableFloatWithLimitExtension
|
||||
{
|
||||
Precision = 0.1f,
|
||||
MinValue = 0,
|
||||
@ -31,6 +31,14 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
Value = 5,
|
||||
};
|
||||
|
||||
protected override void ApplyLimits(bool extended)
|
||||
{
|
||||
base.ApplyLimits(extended);
|
||||
|
||||
CircleSize.MaxValue = extended ? 11 : 10;
|
||||
ApproachRate.MaxValue = extended ? 11 : 10;
|
||||
}
|
||||
|
||||
public override string SettingDescription
|
||||
{
|
||||
get
|
||||
|
@ -110,8 +110,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables.Connections
|
||||
double startTime = start.GetEndTime();
|
||||
double duration = end.StartTime - startTime;
|
||||
|
||||
// Preempt time can go below 800ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
|
||||
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear preempt function (see: OsuHitObject).
|
||||
// Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good.
|
||||
double preempt = PREEMPT * Math.Min(1, start.TimePreempt / OsuHitObject.PREEMPT_MIN);
|
||||
|
||||
fadeOutTime = startTime + fraction * duration;
|
||||
fadeInTime = fadeOutTime - PREEMPT;
|
||||
fadeInTime = fadeOutTime - preempt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
||||
// See the LICENCE file in the repository root for full licence text.
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
@ -25,6 +26,11 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
/// </summary>
|
||||
internal const float BASE_SCORING_DISTANCE = 100;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum preempt time at AR=10.
|
||||
/// </summary>
|
||||
public const double PREEMPT_MIN = 450;
|
||||
|
||||
public double TimePreempt = 600;
|
||||
public double TimeFadeIn = 400;
|
||||
|
||||
@ -112,8 +118,13 @@ namespace osu.Game.Rulesets.Osu.Objects
|
||||
{
|
||||
base.ApplyDefaultsToSelf(controlPointInfo, difficulty);
|
||||
|
||||
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450);
|
||||
TimeFadeIn = 400; // as per osu-stable
|
||||
TimePreempt = (float)BeatmapDifficulty.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN);
|
||||
|
||||
// Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR.
|
||||
// This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above.
|
||||
// Note that this doesn't exactly match the AR>10 visuals as they're classically known, but it feels good.
|
||||
// This adjustment is necessary for AR>10, otherwise TimePreempt can become smaller leading to hitcircles not fully fading in.
|
||||
TimeFadeIn = 400 * Math.Min(1, TimePreempt / PREEMPT_MIN);
|
||||
|
||||
Scale = (1.0f - 0.7f * (difficulty.CircleSize - 5) / 5) / 2;
|
||||
}
|
||||
|
@ -68,12 +68,29 @@ namespace osu.Game.Tests.Online
|
||||
Assert.That(converted.FinalRate.Value, Is.EqualTo(0.25));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void TestDeserialiseDifficultyAdjustModWithExtendedLimits()
|
||||
{
|
||||
var apiMod = new APIMod(new TestModDifficultyAdjust
|
||||
{
|
||||
OverallDifficulty = { Value = 11 },
|
||||
ExtendedLimits = { Value = true }
|
||||
});
|
||||
|
||||
var deserialised = JsonConvert.DeserializeObject<APIMod>(JsonConvert.SerializeObject(apiMod));
|
||||
var converted = (TestModDifficultyAdjust)deserialised.ToMod(new TestRuleset());
|
||||
|
||||
Assert.That(converted.ExtendedLimits.Value, Is.True);
|
||||
Assert.That(converted.OverallDifficulty.Value, Is.EqualTo(11));
|
||||
}
|
||||
|
||||
private class TestRuleset : Ruleset
|
||||
{
|
||||
public override IEnumerable<Mod> GetModsFor(ModType type) => new Mod[]
|
||||
{
|
||||
new TestMod(),
|
||||
new TestModTimeRamp(),
|
||||
new TestModDifficultyAdjust()
|
||||
};
|
||||
|
||||
public override DrawableRuleset CreateDrawableRulesetWith(IBeatmap beatmap, IReadOnlyList<Mod> mods = null) => throw new System.NotImplementedException();
|
||||
@ -135,5 +152,9 @@ namespace osu.Game.Tests.Online
|
||||
Value = true
|
||||
};
|
||||
}
|
||||
|
||||
private class TestModDifficultyAdjust : ModDifficultyAdjust
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
protected const int LAST_SETTING_ORDER = 2;
|
||||
|
||||
[SettingSource("HP Drain", "Override a beatmap's set HP.", FIRST_SETTING_ORDER)]
|
||||
public BindableNumber<float> DrainRate { get; } = new BindableFloat
|
||||
public BindableNumber<float> DrainRate { get; } = new BindableFloatWithLimitExtension
|
||||
{
|
||||
Precision = 0.1f,
|
||||
MinValue = 0,
|
||||
@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
};
|
||||
|
||||
[SettingSource("Accuracy", "Override a beatmap's set OD.", LAST_SETTING_ORDER)]
|
||||
public BindableNumber<float> OverallDifficulty { get; } = new BindableFloat
|
||||
public BindableNumber<float> OverallDifficulty { get; } = new BindableFloatWithLimitExtension
|
||||
{
|
||||
Precision = 0.1f,
|
||||
MinValue = 0,
|
||||
@ -53,6 +53,24 @@ namespace osu.Game.Rulesets.Mods
|
||||
Value = 5,
|
||||
};
|
||||
|
||||
[SettingSource("Extended Limits", "Adjust difficulty beyond sane limits.")]
|
||||
public BindableBool ExtendedLimits { get; } = new BindableBool();
|
||||
|
||||
protected ModDifficultyAdjust()
|
||||
{
|
||||
ExtendedLimits.BindValueChanged(extend => ApplyLimits(extend.NewValue));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Changes the difficulty adjustment limits. Occurs when the value of <see cref="ExtendedLimits"/> is changed.
|
||||
/// </summary>
|
||||
/// <param name="extended">Whether limits should extend beyond sane ranges.</param>
|
||||
protected virtual void ApplyLimits(bool extended)
|
||||
{
|
||||
DrainRate.MaxValue = extended ? 11 : 10;
|
||||
OverallDifficulty.MaxValue = extended ? 11 : 10;
|
||||
}
|
||||
|
||||
public override string SettingDescription
|
||||
{
|
||||
get
|
||||
@ -152,5 +170,62 @@ namespace osu.Game.Rulesets.Mods
|
||||
TransferSettings(difficulty);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="BindableDouble"/> that extends its min/max values to support any assigned value.
|
||||
/// </summary>
|
||||
protected class BindableDoubleWithLimitExtension : BindableDouble
|
||||
{
|
||||
public override double Value
|
||||
{
|
||||
get => base.Value;
|
||||
set
|
||||
{
|
||||
if (value < MinValue)
|
||||
MinValue = value;
|
||||
if (value > MaxValue)
|
||||
MaxValue = value;
|
||||
base.Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="BindableFloat"/> that extends its min/max values to support any assigned value.
|
||||
/// </summary>
|
||||
protected class BindableFloatWithLimitExtension : BindableFloat
|
||||
{
|
||||
public override float Value
|
||||
{
|
||||
get => base.Value;
|
||||
set
|
||||
{
|
||||
if (value < MinValue)
|
||||
MinValue = value;
|
||||
if (value > MaxValue)
|
||||
MaxValue = value;
|
||||
base.Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A <see cref="BindableInt"/> that extends its min/max values to support any assigned value.
|
||||
/// </summary>
|
||||
protected class BindableIntWithLimitExtension : BindableInt
|
||||
{
|
||||
public override int Value
|
||||
{
|
||||
get => base.Value;
|
||||
set
|
||||
{
|
||||
if (value < MinValue)
|
||||
MinValue = value;
|
||||
if (value > MaxValue)
|
||||
MaxValue = value;
|
||||
base.Value = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user