diff --git a/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs b/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs new file mode 100644 index 0000000000..c41c0dac2b --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/Checks/CheckConsecutiveCircles.cs @@ -0,0 +1,86 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Screens.Edit.Verify; +using osu.Game.Screens.Edit.Verify.Components; + +namespace osu.Game.Rulesets.Osu.Edit.Checks +{ + public class CheckConsecutiveCircles : BeatmapCheck + { + private const double consecutive_threshold = 3; + private const double delta_time_min_expected = 300; + private const double delta_time_min_threshold = 100; + + public override CheckMetadata Metadata() => new CheckMetadata + ( + category: CheckMetadata.CheckCategory.Spread, + description: "Too many or fast consecutive circles." + ); + + private IssueTemplate templateManyInARow = new IssueTemplate + ( + type: IssueTemplate.IssueType.Problem, + unformattedMessage: "There are {0} circles in a row here, expected at most {1}." + ); + + private IssueTemplate templateTooFast = new IssueTemplate + ( + type: IssueTemplate.IssueType.Warning, + unformattedMessage: "These circles are too fast ({0:0} ms), expected at least {1:0} ms." + ); + + private IssueTemplate templateAlmostTooFast = new IssueTemplate + ( + type: IssueTemplate.IssueType.Negligible, + unformattedMessage: "These circles are almost too fast ({0:0} ms), expected at least {1:0} ms." + ); + + public override IEnumerable Templates() => new[] + { + templateManyInARow, + templateTooFast, + templateAlmostTooFast + }; + + public override IEnumerable Run(IBeatmap beatmap) + { + List prevCircles = new List(); + + foreach (HitObject hitobject in beatmap.HitObjects) + { + if (!(hitobject is HitCircle circle) || hitobject == beatmap.HitObjects.Last()) + { + if (prevCircles.Count > consecutive_threshold) + { + yield return new Issue( + prevCircles, + templateManyInARow, + prevCircles.Count, consecutive_threshold + ); + } + + prevCircles.Clear(); + continue; + } + + double? prevDeltaTime = circle.StartTime - prevCircles.LastOrDefault()?.StartTime; + prevCircles.Add(circle); + + if (prevDeltaTime == null || prevDeltaTime >= delta_time_min_expected) + continue; + + yield return new Issue( + prevCircles.TakeLast(2), + prevDeltaTime < delta_time_min_threshold ? templateTooFast : templateAlmostTooFast, + prevDeltaTime, delta_time_min_expected + ); + } + } + } +} diff --git a/osu.Game/Checks/CheckMetadataVowels.cs b/osu.Game/Checks/CheckMetadataVowels.cs new file mode 100644 index 0000000000..8bcfe89c3a --- /dev/null +++ b/osu.Game/Checks/CheckMetadataVowels.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Screens.Edit.Verify; +using osu.Game.Screens.Edit.Verify.Components; + +namespace osu.Game.Checks +{ + public class CheckMetadataVowels : BeatmapCheck + { + private static readonly char[] vowels = { 'a', 'e', 'i', 'o', 'u' }; + + public override CheckMetadata Metadata() => new CheckMetadata + ( + category: CheckMetadata.CheckCategory.Metadata, + description: "Metadata fields contain vowels" + ); + + public override IEnumerable Templates() => new[] + { + templateArtistHasVowels + }; + + private IssueTemplate templateArtistHasVowels = new IssueTemplate + ( + type: IssueTemplate.IssueType.Warning, + unformattedMessage: "The {0} field \"{1}\" contains the vowel(s) {2}." + ); + + public override IEnumerable Run(IBeatmap beatmap) + { + foreach (var issue in GetVowelIssues("artist", beatmap.Metadata.Artist)) + yield return issue; + + foreach (var issue in GetVowelIssues("unicode artist", beatmap.Metadata.ArtistUnicode)) + yield return issue; + + foreach (var issue in GetVowelIssues("title", beatmap.Metadata.Title)) + yield return issue; + + foreach (var issue in GetVowelIssues("unicode title", beatmap.Metadata.TitleUnicode)) + yield return issue; + } + + private IEnumerable GetVowelIssues(string fieldName, string fieldValue) + { + if (fieldValue == null) + // Unicode fields can be null if same as respective romanized fields. + yield break; + + List matches = vowels.Where(c => fieldValue.ToLower().Contains(c)).ToList(); + + if (!matches.Any()) + yield break; + + yield return new Issue( + templateArtistHasVowels, + fieldName, fieldValue, string.Join(", ", matches) + ); + } + } +}