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

Add concurrent objects check

Here we use `IHasColumn` to support rulesets with columns, and so I moved that interface out into `osu.Game` from `osu.Game.Rulesets.Mania`.

We also use the same threshold as the unsnap check to ensure that no problems slip through. Specifically where an object is simultaneously not concurrent and not unsnapped but still on the same tick.
This commit is contained in:
Naxess 2021-04-26 20:17:18 +02:00
parent 71f880aa94
commit a3570e18dd
4 changed files with 91 additions and 4 deletions

View File

@ -2,7 +2,6 @@
// See the LICENCE file in the repository root for full licence text.
using osu.Framework.Bindables;
using osu.Game.Rulesets.Mania.Objects.Types;
using osu.Game.Rulesets.Mania.Scoring;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;

View File

@ -0,0 +1,88 @@
// 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.Collections.Generic;
using osu.Game.Beatmaps;
using osu.Game.Rulesets.Edit.Checks.Components;
using osu.Game.Rulesets.Objects;
using osu.Game.Rulesets.Objects.Types;
namespace osu.Game.Rulesets.Edit.Checks
{
public class CheckConcurrentObjects : ICheck
{
// We guarantee that the objects are either treated as concurrent or unsnapped when near the same beat divisor.
private const double ms_leniency = CheckUnsnaps.UNSNAP_MS_THRESHOLD;
public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Compose, "Concurrent hitobjects");
public virtual IEnumerable<IssueTemplate> PossibleTemplates => new IssueTemplate[]
{
new IssueTemplateConcurrentSame(this),
new IssueTemplateConcurrentDifferent(this)
};
public virtual IEnumerable<Issue> Run(IBeatmap playableBeatmap, IWorkingBeatmap workingBeatmap)
{
for (int i = 0; i < playableBeatmap.HitObjects.Count - 1; ++i)
{
var hitobject = playableBeatmap.HitObjects[i];
for (int j = i + 1; j < playableBeatmap.HitObjects.Count; ++j)
{
var nextHitobject = playableBeatmap.HitObjects[j];
// Accounts for rulesets with hitobjects separated by columns, such as Mania.
// In these cases we only care about concurrent objects within the same column.
if ((hitobject as IHasColumn)?.Column != (nextHitobject as IHasColumn)?.Column)
continue;
// Two hitobjects cannot be concurrent without also being concurrent with all objects in between.
// So if the next object is not concurrent, then we know no future objects will be either.
if (!areConcurrent(hitobject, nextHitobject))
break;
if (hitobject.GetType() == nextHitobject.GetType())
yield return new IssueTemplateConcurrentSame(this).Create(hitobject, nextHitobject);
else
yield return new IssueTemplateConcurrentDifferent(this).Create(hitobject, nextHitobject);
}
}
}
protected bool areConcurrent(HitObject hitobject, HitObject nextHitobject) => nextHitobject.StartTime <= hitobject.GetEndTime() + ms_leniency;
public abstract class IssueTemplateConcurrent : IssueTemplate
{
protected IssueTemplateConcurrent(ICheck check, string unformattedMessage)
: base(check, IssueType.Problem, unformattedMessage)
{
}
public Issue Create(HitObject hitobject, HitObject nextHitobject)
{
var hitobjects = new List<HitObject> { hitobject, nextHitobject };
return new Issue(hitobjects, this, hitobject.GetType().Name, nextHitobject.GetType().Name)
{
Time = nextHitobject.StartTime
};
}
}
public class IssueTemplateConcurrentSame : IssueTemplateConcurrent
{
public IssueTemplateConcurrentSame(ICheck check)
: base(check, "{0}s are concurrent here.")
{
}
}
public class IssueTemplateConcurrentDifferent : IssueTemplateConcurrent
{
public IssueTemplateConcurrentDifferent(ICheck check)
: base(check, "{0} and {1} are concurrent here.")
{
}
}
}
}

View File

@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Edit.Checks
{
public class CheckUnsnaps : ICheck
{
private const double unsnap_ms_threshold = 2;
public const double UNSNAP_MS_THRESHOLD = 2;
public CheckMetadata Metadata { get; } = new CheckMetadata(CheckCategory.Compose, "Unsnapped hitobjects");
@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Edit.Checks
private IEnumerable<Issue> getUnsnapIssues(HitObject hitobject, double unsnap, double time, string postfix = "")
{
if (Math.Abs(unsnap) >= unsnap_ms_threshold)
if (Math.Abs(unsnap) >= UNSNAP_MS_THRESHOLD)
yield return new IssueTemplate2MsOrMore(this).Create(hitobject, unsnap, time, postfix);
else if (Math.Abs(unsnap) >= 1)
yield return new IssueTemplate1MsOrMore(this).Create(hitobject, unsnap, time, postfix);

View File

@ -1,7 +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.
namespace osu.Game.Rulesets.Mania.Objects.Types
namespace osu.Game.Rulesets.Objects.Types
{
/// <summary>
/// A type of hit object which lies in one of a number of predetermined columns.