示例#1
0
        public IEnumerable <Issue> Run(BeatmapVerifierContext context)
        {
            if (context.InterpretedDifficulty > DifficultyRating.Normal)
            {
                yield break;
            }

            var prevObservedTimeDistances = new List <ObservedTimeDistance>();
            var hitObjects = context.Beatmap.HitObjects;

            for (int i = 0; i < hitObjects.Count - 1; ++i)
            {
                if (!(hitObjects[i] is OsuHitObject hitObject) || hitObject is Spinner)
                {
                    continue;
                }

                if (!(hitObjects[i + 1] is OsuHitObject nextHitObject) || nextHitObject is Spinner)
                {
                    continue;
                }

                var deltaTime = nextHitObject.StartTime - hitObject.GetEndTime();

                // Ignore objects that are far enough apart in time to not be considered the same pattern.
                if (deltaTime > pattern_lifetime)
                {
                    continue;
                }

                // Relying on FastInvSqrt is probably good enough here. We'll be taking the difference between distances later, hence square not being sufficient.
                var distance = (hitObject.StackedEndPosition - nextHitObject.StackedPosition).LengthFast;

                // Ignore stacks and half-stacks, as these are close enough to where they can't be confused for being time-distanced.
                if (distance < stack_leniency)
                {
                    continue;
                }

                var observedTimeDistance = new ObservedTimeDistance(nextHitObject.StartTime, deltaTime, distance);
                var expectedDistance     = getExpectedDistance(prevObservedTimeDistances, observedTimeDistance);

                if (expectedDistance == 0)
                {
                    // There was nothing relevant to compare to.
                    prevObservedTimeDistances.Add(observedTimeDistance);
                    continue;
                }

                if ((Math.Abs(expectedDistance - distance) - distance_leniency_absolute_problem) / distance > distance_leniency_percent_problem)
                {
                    yield return(new IssueTemplateIrregularSpacingProblem(this).Create(expectedDistance, distance, hitObject, nextHitObject));
                }
                else if ((Math.Abs(expectedDistance - distance) - distance_leniency_absolute_warning) / distance > distance_leniency_percent_warning)
                {
                    yield return(new IssueTemplateIrregularSpacingWarning(this).Create(expectedDistance, distance, hitObject, nextHitObject));
                }
                else
                {
                    // We use `else` here to prevent issues from cascading; an object spaced too far could cause regular spacing to be considered "too short" otherwise.
                    prevObservedTimeDistances.Add(observedTimeDistance);
                }
            }
        }
示例#2
0
        private double getExpectedDistance(IEnumerable <ObservedTimeDistance> prevObservedTimeDistances, ObservedTimeDistance observedTimeDistance)
        {
            var observations = prevObservedTimeDistances.Count();

            int    count = 0;
            double sum   = 0;

            // Looping this in reverse allows us to break before going through all elements, as we're only interested in the most recent ones.
            for (int i = observations - 1; i >= 0; --i)
            {
                var prevObservedTimeDistance = prevObservedTimeDistances.ElementAt(i);

                // Only consider observations within the last few seconds - this allows the map to build spacing up/down over time, but prevents it from being too sudden.
                if (observedTimeDistance.ObservationTime - prevObservedTimeDistance.ObservationTime > observation_lifetime)
                {
                    break;
                }

                // Only consider observations which have a similar time difference - this leniency allows handling of multi-BPM maps which speed up/down slowly.
                if (Math.Abs(observedTimeDistance.DeltaTime - prevObservedTimeDistance.DeltaTime) > similar_time_leniency)
                {
                    break;
                }

                count += 1;
                sum   += prevObservedTimeDistance.Distance / Math.Max(prevObservedTimeDistance.DeltaTime, 1);
            }

            return(sum / Math.Max(count, 1) * observedTimeDistance.DeltaTime);
        }