mirror of
https://github.com/ppy/osu.git
synced 2024-12-14 05:32:54 +08:00
Merge pull request #18402 from ggliv/mod-accuracy-challenge
Add accuracy challenge mod
This commit is contained in:
commit
a966d6c330
@ -113,6 +113,7 @@ namespace osu.Game.Rulesets.Catch
|
|||||||
new MultiMod(new CatchModDoubleTime(), new CatchModNightcore()),
|
new MultiMod(new CatchModDoubleTime(), new CatchModNightcore()),
|
||||||
new CatchModHidden(),
|
new CatchModHidden(),
|
||||||
new CatchModFlashlight(),
|
new CatchModFlashlight(),
|
||||||
|
new ModAccuracyChallenge(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Conversion:
|
case ModType.Conversion:
|
||||||
|
@ -245,6 +245,7 @@ namespace osu.Game.Rulesets.Mania
|
|||||||
new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()),
|
new MultiMod(new ManiaModDoubleTime(), new ManiaModNightcore()),
|
||||||
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
|
new MultiMod(new ManiaModFadeIn(), new ManiaModHidden()),
|
||||||
new ManiaModFlashlight(),
|
new ManiaModFlashlight(),
|
||||||
|
new ModAccuracyChallenge(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Conversion:
|
case ModType.Conversion:
|
||||||
|
14
osu.Game.Rulesets.Osu/Mods/OsuModAccuracyChallenge.cs
Normal file
14
osu.Game.Rulesets.Osu/Mods/OsuModAccuracyChallenge.cs
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
// 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.Game.Rulesets.Mods;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Osu.Mods
|
||||||
|
{
|
||||||
|
public class OsuModAccuracyChallenge : ModAccuracyChallenge
|
||||||
|
{
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray();
|
||||||
|
}
|
||||||
|
}
|
@ -164,7 +164,8 @@ namespace osu.Game.Rulesets.Osu
|
|||||||
new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()),
|
new MultiMod(new OsuModDoubleTime(), new OsuModNightcore()),
|
||||||
new OsuModHidden(),
|
new OsuModHidden(),
|
||||||
new MultiMod(new OsuModFlashlight(), new OsuModBlinds()),
|
new MultiMod(new OsuModFlashlight(), new OsuModBlinds()),
|
||||||
new OsuModStrictTracking()
|
new OsuModStrictTracking(),
|
||||||
|
new OsuModAccuracyChallenge(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Conversion:
|
case ModType.Conversion:
|
||||||
|
@ -144,6 +144,7 @@ namespace osu.Game.Rulesets.Taiko
|
|||||||
new MultiMod(new TaikoModDoubleTime(), new TaikoModNightcore()),
|
new MultiMod(new TaikoModDoubleTime(), new TaikoModNightcore()),
|
||||||
new TaikoModHidden(),
|
new TaikoModHidden(),
|
||||||
new TaikoModFlashlight(),
|
new TaikoModFlashlight(),
|
||||||
|
new ModAccuracyChallenge(),
|
||||||
};
|
};
|
||||||
|
|
||||||
case ModType.Conversion:
|
case ModType.Conversion:
|
||||||
|
80
osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs
Normal file
80
osu.Game/Rulesets/Mods/ModAccuracyChallenge.cs
Normal file
@ -0,0 +1,80 @@
|
|||||||
|
// 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.Globalization;
|
||||||
|
using System.Linq;
|
||||||
|
using osu.Framework.Bindables;
|
||||||
|
using osu.Framework.Localisation;
|
||||||
|
using osu.Game.Configuration;
|
||||||
|
using osu.Game.Graphics.UserInterface;
|
||||||
|
using osu.Game.Overlays.Settings;
|
||||||
|
using osu.Game.Rulesets.Scoring;
|
||||||
|
using osu.Game.Rulesets.Judgements;
|
||||||
|
using osu.Game.Scoring;
|
||||||
|
|
||||||
|
namespace osu.Game.Rulesets.Mods
|
||||||
|
{
|
||||||
|
public class ModAccuracyChallenge : ModFailCondition, IApplicableToScoreProcessor
|
||||||
|
{
|
||||||
|
public override string Name => "Accuracy Challenge";
|
||||||
|
|
||||||
|
public override string Acronym => "AC";
|
||||||
|
|
||||||
|
public override LocalisableString Description => "Fail if your accuracy drops too low!";
|
||||||
|
|
||||||
|
public override ModType Type => ModType.DifficultyIncrease;
|
||||||
|
|
||||||
|
public override double ScoreMultiplier => 1.0;
|
||||||
|
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ModEasyWithExtraLives), typeof(ModPerfect) }).ToArray();
|
||||||
|
|
||||||
|
public override bool RequiresConfiguration => false;
|
||||||
|
|
||||||
|
public override string SettingDescription => base.SettingDescription.Replace(MinimumAccuracy.ToString(), MinimumAccuracy.Value.ToString("##%", NumberFormatInfo.InvariantInfo));
|
||||||
|
|
||||||
|
[SettingSource("Minimum accuracy", "Trigger a failure if your accuracy goes below this value.", SettingControlType = typeof(SettingsSlider<double, PercentSlider>))]
|
||||||
|
public BindableNumber<double> MinimumAccuracy { get; } = new BindableDouble
|
||||||
|
{
|
||||||
|
MinValue = 0.60,
|
||||||
|
MaxValue = 0.99,
|
||||||
|
Precision = 0.01,
|
||||||
|
Default = 0.9,
|
||||||
|
Value = 0.9,
|
||||||
|
};
|
||||||
|
|
||||||
|
private ScoreProcessor scoreProcessor = null!;
|
||||||
|
|
||||||
|
public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) => this.scoreProcessor = scoreProcessor;
|
||||||
|
|
||||||
|
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
||||||
|
|
||||||
|
protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result)
|
||||||
|
{
|
||||||
|
if (!result.Type.AffectsAccuracy())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return getAccuracyWithImminentResultAdded(result) < MinimumAccuracy.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private double getAccuracyWithImminentResultAdded(JudgementResult result)
|
||||||
|
{
|
||||||
|
var score = new ScoreInfo { Ruleset = scoreProcessor.Ruleset.RulesetInfo };
|
||||||
|
|
||||||
|
// This is super ugly, but if we don't do it this way we will not have the most recent result added to the accuracy value.
|
||||||
|
// Hopefully we can improve this in the future.
|
||||||
|
scoreProcessor.PopulateScore(score);
|
||||||
|
score.Statistics[result.Type]++;
|
||||||
|
|
||||||
|
return scoreProcessor.ComputeAccuracy(score);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public partial class PercentSlider : OsuSliderBar<double>
|
||||||
|
{
|
||||||
|
public PercentSlider()
|
||||||
|
{
|
||||||
|
DisplayAsPercentage = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -1,6 +1,8 @@
|
|||||||
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
|
// 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.
|
// See the LICENCE file in the repository root for full licence text.
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
using osu.Framework.Bindables;
|
using osu.Framework.Bindables;
|
||||||
using osu.Game.Beatmaps;
|
using osu.Game.Beatmaps;
|
||||||
@ -19,6 +21,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
};
|
};
|
||||||
|
|
||||||
public override string SettingDescription => Retries.IsDefault ? string.Empty : $"{"lives".ToQuantity(Retries.Value)}";
|
public override string SettingDescription => Retries.IsDefault ? string.Empty : $"{"lives".ToQuantity(Retries.Value)}";
|
||||||
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModAccuracyChallenge)).ToArray();
|
||||||
|
|
||||||
private int retries;
|
private int retries;
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Mods
|
|||||||
public override double ScoreMultiplier => 1;
|
public override double ScoreMultiplier => 1;
|
||||||
public override LocalisableString Description => "SS or quit.";
|
public override LocalisableString Description => "SS or quit.";
|
||||||
|
|
||||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModSuddenDeath)).ToArray();
|
public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ModSuddenDeath), typeof(ModAccuracyChallenge) }).ToArray();
|
||||||
|
|
||||||
protected ModPerfect()
|
protected ModPerfect()
|
||||||
{
|
{
|
||||||
|
@ -97,7 +97,11 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
protected virtual double ClassicScoreMultiplier => 36;
|
protected virtual double ClassicScoreMultiplier => 36;
|
||||||
|
|
||||||
private readonly Ruleset ruleset;
|
/// <summary>
|
||||||
|
/// The ruleset this score processor is valid for.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Ruleset Ruleset;
|
||||||
|
|
||||||
private readonly double accuracyPortion;
|
private readonly double accuracyPortion;
|
||||||
private readonly double comboPortion;
|
private readonly double comboPortion;
|
||||||
|
|
||||||
@ -145,7 +149,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
|
|
||||||
public ScoreProcessor(Ruleset ruleset)
|
public ScoreProcessor(Ruleset ruleset)
|
||||||
{
|
{
|
||||||
this.ruleset = ruleset;
|
Ruleset = ruleset;
|
||||||
|
|
||||||
accuracyPortion = DefaultAccuracyPortion;
|
accuracyPortion = DefaultAccuracyPortion;
|
||||||
comboPortion = DefaultComboPortion;
|
comboPortion = DefaultComboPortion;
|
||||||
@ -291,8 +295,8 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
[Pure]
|
[Pure]
|
||||||
public double ComputeAccuracy(ScoreInfo scoreInfo)
|
public double ComputeAccuracy(ScoreInfo scoreInfo)
|
||||||
{
|
{
|
||||||
if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
if (!Ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
||||||
throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
throw new ArgumentException($"Unexpected score ruleset. Expected \"{Ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
||||||
|
|
||||||
// We only extract scoring values from the score's statistics. This is because accuracy is always relative to the point of pass or fail rather than relative to the whole beatmap.
|
// We only extract scoring values from the score's statistics. This is because accuracy is always relative to the point of pass or fail rather than relative to the whole beatmap.
|
||||||
extractScoringValues(scoreInfo.Statistics, out var current, out var maximum);
|
extractScoringValues(scoreInfo.Statistics, out var current, out var maximum);
|
||||||
@ -312,8 +316,8 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
[Pure]
|
[Pure]
|
||||||
public long ComputeScore(ScoringMode mode, ScoreInfo scoreInfo)
|
public long ComputeScore(ScoringMode mode, ScoreInfo scoreInfo)
|
||||||
{
|
{
|
||||||
if (!ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
if (!Ruleset.RulesetInfo.Equals(scoreInfo.Ruleset))
|
||||||
throw new ArgumentException($"Unexpected score ruleset. Expected \"{ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
throw new ArgumentException($"Unexpected score ruleset. Expected \"{Ruleset.RulesetInfo.ShortName}\" but was \"{scoreInfo.Ruleset.ShortName}\".");
|
||||||
|
|
||||||
extractScoringValues(scoreInfo, out var current, out var maximum);
|
extractScoringValues(scoreInfo, out var current, out var maximum);
|
||||||
|
|
||||||
@ -552,7 +556,7 @@ namespace osu.Game.Rulesets.Scoring
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
maxResult = maxBasicResult ??= ruleset.GetHitResults().MaxBy(kvp => Judgement.ToNumericResult(kvp.result)).result;
|
maxResult = maxBasicResult ??= Ruleset.GetHitResults().MaxBy(kvp => Judgement.ToNumericResult(kvp.result)).result;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user