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:
parent
71f880aa94
commit
a3570e18dd
@ -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;
|
||||
|
88
osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs
Normal file
88
osu.Game/Rulesets/Edit/Checks/CheckConcurrentObjects.cs
Normal 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.")
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
|
@ -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.
|
Loading…
Reference in New Issue
Block a user