mirror of
https://github.com/ppy/osu.git
synced 2025-02-22 20:52:54 +08:00
Combine fail blocking and forcing logic into a singular mod interface
This commit is contained in:
parent
950b001533
commit
9ed8528a40
@ -9,16 +9,19 @@ namespace osu.Game.Rulesets.Mania.Mods
|
||||
{
|
||||
public class ManiaModPerfect : ModPerfect
|
||||
{
|
||||
public override bool FailCondition(JudgementResult result)
|
||||
public override FailState CheckFail(JudgementResult? result)
|
||||
{
|
||||
if (result == null)
|
||||
return FailState.Allow;
|
||||
|
||||
if (!isRelevantResult(result.Judgement.MinResult) && !isRelevantResult(result.Judgement.MaxResult) && !isRelevantResult(result.Type))
|
||||
return false;
|
||||
return FailState.Allow;
|
||||
|
||||
// Mania allows imperfect "Great" hits without failing.
|
||||
if (result.Judgement.MaxResult == HitResult.Perfect)
|
||||
return result.Type < HitResult.Great;
|
||||
return result.Type >= HitResult.Great ? FailState.Allow : FailState.Force;
|
||||
|
||||
return result.Type != result.Judgement.MaxResult;
|
||||
return result.Type != result.Judgement.MaxResult ? FailState.Force : FailState.Allow;
|
||||
}
|
||||
|
||||
private bool isRelevantResult(HitResult result) => result.AffectsAccuracy() || result.AffectsCombo();
|
||||
|
@ -33,7 +33,7 @@ using osuTK.Graphics;
|
||||
namespace osu.Game.Rulesets.Osu.Mods
|
||||
{
|
||||
public class OsuModTargetPractice : ModWithVisibilityAdjustment, IApplicableToDrawableRuleset<OsuHitObject>,
|
||||
IApplicableToDifficulty, IHasSeed, IHidesApproachCircles, IHasFailCondition
|
||||
IApplicableToDifficulty, IHasSeed, IHidesApproachCircles, IApplicableFailOverride
|
||||
{
|
||||
public override string Name => "Target Practice";
|
||||
public override string Acronym => "TP";
|
||||
@ -100,14 +100,15 @@ namespace osu.Game.Rulesets.Osu.Mods
|
||||
|
||||
#region Sudden Death (IApplicableFailOverride)
|
||||
|
||||
public bool PerformFail() => true;
|
||||
|
||||
public bool RestartOnFail => false;
|
||||
|
||||
// Sudden death
|
||||
public bool FailCondition(JudgementResult result)
|
||||
=> result.Type.AffectsCombo()
|
||||
&& !result.IsHit;
|
||||
public FailState CheckFail(JudgementResult? result)
|
||||
{
|
||||
if (result == null)
|
||||
return FailState.Allow;
|
||||
|
||||
return result.Type.AffectsCombo() && !result.IsHit ? FailState.Force : FailState.Allow;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
@ -440,7 +440,7 @@ namespace osu.Game.Tests.Gameplay
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public override bool FailCondition(JudgementResult result) => result.Type == type;
|
||||
public override FailState CheckFail(JudgementResult result) => result?.Type == type ? FailState.Force : FailState.Allow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -61,7 +61,7 @@ namespace osu.Game.Tests.Visual.Gameplay
|
||||
public override double ScoreMultiplier => 1.0;
|
||||
public override string Acronym => "";
|
||||
|
||||
public override bool FailCondition(JudgementResult result) => true;
|
||||
public override FailState CheckFail(JudgementResult? result) => FailState.Force;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,22 +1,43 @@
|
||||
// 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 osu.Game.Rulesets.Judgements;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a mod which can override (and block) a fail.
|
||||
/// Represents a mod which can override failure, by either hard-blocking it, or forcing it immediately.
|
||||
/// </summary>
|
||||
public interface IApplicableFailOverride : IApplicableMod
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether we should allow failing at the current point in time.
|
||||
/// </summary>
|
||||
/// <returns>Whether the fail should be allowed to proceed. Return false to block.</returns>
|
||||
bool PerformFail();
|
||||
|
||||
/// <summary>
|
||||
/// Whether we want to restart on fail. Only used if <see cref="PerformFail"/> returns true.
|
||||
/// Whether we want to restart on fail.
|
||||
/// </summary>
|
||||
bool RestartOnFail { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Check the current failure allowance for this mod.
|
||||
/// </summary>
|
||||
/// <param name="result">The judgement result which should be considered. Importantly, will be <c>null</c> if a failure has already being triggered.</param>
|
||||
/// <returns>The current failure allowance (see <see cref="FailState"/>).</returns>
|
||||
FailState CheckFail(JudgementResult? result);
|
||||
}
|
||||
|
||||
public enum FailState
|
||||
{
|
||||
/// <summary>
|
||||
/// Failure is being blocked by this mod.
|
||||
/// </summary>
|
||||
Block,
|
||||
|
||||
/// <summary>
|
||||
/// Failure is allowed by this mod (but may be triggered by another mod or base behaviour).
|
||||
/// </summary>
|
||||
Allow,
|
||||
|
||||
/// <summary>
|
||||
/// Failure should be forced immediately.
|
||||
/// </summary>
|
||||
Force
|
||||
}
|
||||
}
|
||||
|
@ -1,26 +0,0 @@
|
||||
// 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 osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
/// <summary>
|
||||
/// Interface for a <see cref="Mod"/> that specifies its own conditions for failure.
|
||||
/// </summary>
|
||||
// todo: maybe IHasFailCondition and IApplicableFailOverride should be combined into a single interface.
|
||||
public interface IHasFailCondition : IApplicableFailOverride
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines whether <paramref name="result"/> should trigger a failure. Called every time a
|
||||
/// judgement is applied to <see cref="HealthProcessor"/>.
|
||||
/// </summary>
|
||||
/// <param name="result">The latest <see cref="JudgementResult"/>.</param>
|
||||
/// <returns>Whether the fail condition has been met.</returns>
|
||||
/// <remarks>
|
||||
/// This method should only be used to trigger failures based on <paramref name="result"/>
|
||||
/// </remarks>
|
||||
bool FailCondition(JudgementResult result);
|
||||
}
|
||||
}
|
@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public ScoreRank AdjustRank(ScoreRank rank, double accuracy) => rank;
|
||||
|
||||
public override bool FailCondition(JudgementResult result) => false;
|
||||
public override FailState CheckFail(JudgementResult? result) => FailState.Allow;
|
||||
|
||||
public enum AccuracyMode
|
||||
{
|
||||
|
@ -6,6 +6,7 @@ using System.Linq;
|
||||
using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Objects;
|
||||
using osu.Game.Rulesets.UI;
|
||||
using osu.Game.Screens.Play;
|
||||
@ -48,8 +49,8 @@ namespace osu.Game.Rulesets.Mods
|
||||
player.BreakOverlay.Hide();
|
||||
}
|
||||
|
||||
public bool PerformFail() => false;
|
||||
|
||||
public bool RestartOnFail => false;
|
||||
|
||||
public FailState CheckFail(JudgementResult? result) => FailState.Block;
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using Humanizer;
|
||||
using osu.Framework.Bindables;
|
||||
using osu.Game.Beatmaps;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
@ -33,18 +34,22 @@ namespace osu.Game.Rulesets.Mods
|
||||
retries = Retries.Value;
|
||||
}
|
||||
|
||||
public bool PerformFail()
|
||||
public bool RestartOnFail => false;
|
||||
|
||||
public FailState CheckFail(JudgementResult? result)
|
||||
{
|
||||
if (retries == 0) return true;
|
||||
if (result != null)
|
||||
return FailState.Block;
|
||||
|
||||
if (retries == 0)
|
||||
return FailState.Allow;
|
||||
|
||||
health.Value = health.MaxValue;
|
||||
retries--;
|
||||
|
||||
return false;
|
||||
return FailState.Block;
|
||||
}
|
||||
|
||||
public bool RestartOnFail => false;
|
||||
|
||||
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||
{
|
||||
health.BindTo(healthProcessor.Health);
|
||||
|
@ -9,17 +9,16 @@ using osu.Game.Rulesets.Scoring;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
{
|
||||
public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IHasFailCondition
|
||||
public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IApplicableFailOverride
|
||||
{
|
||||
public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModCinema) };
|
||||
|
||||
[SettingSource("Restart on fail", "Automatically restarts when failed.")]
|
||||
public BindableBool Restart { get; } = new BindableBool();
|
||||
|
||||
public virtual bool PerformFail() => true;
|
||||
public virtual bool AllowFail => true;
|
||||
|
||||
public virtual bool RestartOnFail => Restart.Value;
|
||||
|
||||
private Action<Mod>? triggerFailureDelegate;
|
||||
|
||||
public void ApplyToHealthProcessor(HealthProcessor healthProcessor)
|
||||
@ -32,6 +31,6 @@ namespace osu.Game.Rulesets.Mods
|
||||
/// </summary>
|
||||
protected void TriggerFailure() => triggerFailureDelegate?.Invoke(this);
|
||||
|
||||
public abstract bool FailCondition(JudgementResult result);
|
||||
public abstract FailState CheckFail(JudgementResult? result);
|
||||
}
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites;
|
||||
using osu.Framework.Localisation;
|
||||
using osu.Game.Configuration;
|
||||
using osu.Game.Graphics;
|
||||
using osu.Game.Rulesets.Judgements;
|
||||
using osu.Game.Screens.Play;
|
||||
|
||||
namespace osu.Game.Rulesets.Mods
|
||||
@ -24,12 +25,12 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
private readonly Bindable<bool> showHealthBar = new Bindable<bool>();
|
||||
|
||||
public bool RestartOnFail => false;
|
||||
|
||||
/// <summary>
|
||||
/// We never fail, 'yo.
|
||||
/// </summary>
|
||||
public bool PerformFail() => false;
|
||||
|
||||
public bool RestartOnFail => false;
|
||||
public FailState CheckFail(JudgementResult? result) => FailState.Block;
|
||||
|
||||
public void ReadFromConfig(OsuConfigManager config)
|
||||
{
|
||||
|
@ -28,9 +28,16 @@ namespace osu.Game.Rulesets.Mods
|
||||
Restart.Value = Restart.Default = true;
|
||||
}
|
||||
|
||||
public override bool FailCondition(JudgementResult result)
|
||||
=> (isRelevantResult(result.Judgement.MinResult) || isRelevantResult(result.Judgement.MaxResult) || isRelevantResult(result.Type))
|
||||
&& result.Type != result.Judgement.MaxResult;
|
||||
public override FailState CheckFail(JudgementResult? result)
|
||||
{
|
||||
if (result == null)
|
||||
return FailState.Allow;
|
||||
|
||||
return (isRelevantResult(result.Judgement.MinResult) || isRelevantResult(result.Judgement.MaxResult) || isRelevantResult(result.Type))
|
||||
&& result.Type != result.Judgement.MaxResult
|
||||
? FailState.Force
|
||||
: FailState.Allow;
|
||||
}
|
||||
|
||||
private bool isRelevantResult(HitResult result) => result.AffectsAccuracy() || result.AffectsCombo();
|
||||
}
|
||||
|
@ -23,8 +23,12 @@ namespace osu.Game.Rulesets.Mods
|
||||
|
||||
public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModPerfect)).ToArray();
|
||||
|
||||
public override bool FailCondition(JudgementResult result)
|
||||
=> result.Type.AffectsCombo()
|
||||
&& !result.IsHit;
|
||||
public override FailState CheckFail(JudgementResult? result)
|
||||
{
|
||||
if (result == null)
|
||||
return FailState.Allow;
|
||||
|
||||
return result.Type.AffectsCombo() && !result.IsHit ? FailState.Force : FailState.Allow;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -102,9 +102,9 @@ namespace osu.Game.Rulesets.Scoring
|
||||
if (CheckDefaultFailCondition(result))
|
||||
return true;
|
||||
|
||||
foreach (var condition in Mods.Value.OfType<IHasFailCondition>())
|
||||
foreach (var condition in Mods.Value.OfType<IApplicableFailOverride>())
|
||||
{
|
||||
if (condition.FailCondition(result))
|
||||
if (condition.CheckFail(result) == FailState.Force)
|
||||
{
|
||||
ModTriggeringFailure = condition as Mod;
|
||||
return true;
|
||||
|
@ -152,7 +152,7 @@ namespace osu.Game.Screens.Play
|
||||
/// Whether failing should be allowed.
|
||||
/// By default, this checks whether all selected mods allow failing.
|
||||
/// </summary>
|
||||
protected virtual bool CheckModsAllowFailure() => GameplayState.Mods.OfType<IApplicableFailOverride>().All(m => m.PerformFail());
|
||||
protected virtual bool CheckModsAllowFailure() => HealthProcessor.Mods.Value.OfType<IApplicableFailOverride>().All(m => m.CheckFail(null) != FailState.Block);
|
||||
|
||||
public readonly PlayerConfiguration Configuration;
|
||||
|
||||
@ -244,7 +244,7 @@ namespace osu.Game.Screens.Play
|
||||
|
||||
HealthProcessor = gameplayMods.OfType<IApplicableHealthProcessor>().FirstOrDefault()?.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime);
|
||||
HealthProcessor ??= ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime);
|
||||
HealthProcessor.Mods.Value = gameplayMods.OrderByDescending(m => m is IHasFailCondition mod && mod.RestartOnFail).ToArray();
|
||||
HealthProcessor.Mods.Value = gameplayMods.OrderByDescending(m => m is IApplicableFailOverride mod && mod.RestartOnFail).ToArray();
|
||||
HealthProcessor.ApplyBeatmap(playableBeatmap);
|
||||
|
||||
dependencies.CacheAs(HealthProcessor);
|
||||
|
Loading…
Reference in New Issue
Block a user