Пример #1
0
        public static void getEndOffsetFromObjectsByCount(Beatmap beatmap, double startOffset,
                                                          int count, out double endOffset)
        {
            SearchUtils.SortBeatmapElements(beatmap.HitObjects);

            int index      = 1;
            int foundIndex = -1;

            for (int i = 0; i < beatmap.HitObjects.Count; i++)
            {
                if (beatmap.HitObjects[i].Offset >= startOffset)
                {
                    while (index < count && i++ < beatmap.HitObjects.Count)
                    {
                        index++;
                    }
                    foundIndex = i;
                    break;
                }
            }
            if (foundIndex != -1)
            {
                endOffset = beatmap.HitObjects[foundIndex].Offset;
            }
            else
            {
                endOffset = -1;
            }
        }
Пример #2
0
        private static void resnapElements(IEnumerable <BeatmapElement> elements, Beatmap beatmap, onFailure <Beatmap, BeatmapElement, int> listener, shouldChange <BeatmapElement> condition)
        {
            foreach (BeatmapElement hitObject in elements)
            {
                if (!condition.Invoke(hitObject))
                {
                    continue;
                }

                TimingPoint closestPoint         = SearchUtils.GetClosestTimingPoint(beatmap.TimingPoints, hitObject.Offset);
                int         closestSnappedOffset = getClosestSnappedOffset(hitObject, closestPoint, out int closestSnapValue);
                if (closestSnapValue != -1)
                {
                    // We have a note that is equal distance to defined snaps
                    // in the editor. Present this to the user.
                    listener.Invoke(beatmap, hitObject, closestSnapValue);
                }
                else if (closestSnappedOffset != 0)
                {
                    // The note is not snapped. We need to snap the note with
                    // new snap value which is closestSnapInBeat + closestSnapValue.
                    // Then the offset requires a recalculation.
                    hitObject.Offset = closestSnappedOffset;
                }
            }
        }
Пример #3
0
        public static bool SnapInheritedPointsOnClosestTimingPoints(Form form, Beatmap beatmap,
                                                                    double firstOffset, double lastOffset)
        {
            SearchUtils.GetObjectsInBetween(beatmap, firstOffset, lastOffset,
                                            out IList <TimingPoint> points);

            Dictionary <TimingPoint, double> newOffsets = new Dictionary <TimingPoint, double>();
            bool isHighRangeDetectedAndVerified         = false;

            foreach (TimingPoint point in points)
            {
                if (point.IsInherited)
                {
                    TimingPoint closestPreviousPoint = SearchUtils.GetClosestTimingPoint(beatmap.TimingPoints, point.Offset);
                    TimingPoint closestNextPoint     = SearchUtils.GetClosestNextTimingPoint(beatmap.TimingPoints, point);

                    double closestPreviousPointOffset = closestPreviousPoint != null ? closestPreviousPoint.Offset : 0;
                    double closestNextPointOffset     = closestNextPoint != null ? closestNextPoint.Offset : 0;

                    double firstDifference  = point.Offset - closestPreviousPointOffset;
                    double secondDifference = closestNextPointOffset - point.Offset;

                    if (!isHighRangeDetectedAndVerified && !VerifyUtils.verifyRangeAny(-400, 400, firstDifference, secondDifference))
                    {
                        if (MessageBoxUtils.showQuestionYesNo("Inherited points with more than 400 milliseconds gap between closest timing points detected. This might result in inherited points getting completely losing their purpose.".AddLines(2) + "Are you sure you want to continue?") == DialogResult.Yes)
                        {
                            isHighRangeDetectedAndVerified = true;
                        }
                        else
                        {
                            return(false);
                        }
                    }

                    double targetOffset;
                    if (point.Offset - closestPreviousPointOffset < closestNextPointOffset - point.Offset)
                    {
                        targetOffset = closestPreviousPointOffset;
                    }
                    else
                    {
                        targetOffset = closestNextPointOffset;
                    }
                    newOffsets.Add(point, targetOffset);
                }
            }
            newOffsets.ForEach((key, value) =>
            {
                key.Offset = value;
            });
            newOffsets.Clear();
            return(true);
        }
Пример #4
0
        public static int calculateEndOffset(Beatmap beatmap, double startOffset, double gridSnap, double count)
        {
            double step           = gridSnap;
            double totalSnap      = step * count;
            double calculatedSnap = 0;
            double targetOffset   = startOffset;

            double snapOffset = 0;

            while (calculatedSnap < totalSnap)
            {
                // This one has to return non-null. If it does, the exception is deserved.
                TimingPoint closestPoint = SearchUtils.GetClosestTimingPoint(beatmap.TimingPoints, targetOffset);

                // This can return null. It means we don't need to worry about this point and
                // calculate the offset directly.
                TimingPoint nextPoint = SearchUtils.GetClosestNextTimingPoint(beatmap.TimingPoints, closestPoint);

                snapOffset      = step * closestPoint.PointValue;
                targetOffset   += snapOffset;
                calculatedSnap += step;

                // Now, if the target offset temp passed the next point
                // calculate an estimated snap difference.
                // This is required for unsnapped timing points and a relative
                // end offset calculation.
                if (nextPoint != null && targetOffset > nextPoint.Offset)
                {
                    double difference     = nextPoint.Offset - targetOffset;
                    double differenceSnap = difference / nextPoint.PointValue;

                    // Here, we reset the target offset as the next point value
                    // and reduce the calculated total grid snap. Step value
                    // is not changed and the next snaps are calculated
                    // relatively to the next point.
                    targetOffset    = nextPoint.Offset;
                    calculatedSnap -= differenceSnap;
                }
            }

            // And, at the end of the day, return the target offset.
            return((int)targetOffset);
        }
Пример #5
0
        public static double[] getRelativeSnap(List <TimingPoint> timingPoints, BeatmapElement target)
        {
            // First holds the true snap which is from the first point,
            // second holds the snap value from the closest timing point.
            double[] result = new double[2] {
                0, 0
            };

            // If this is the first timing point or the element is snapped on the first timing point,
            // its actual and relative snaps should always equal to 0. Check the condition
            // and return it immediately.
            if (timingPoints.Count == 0 || (target.Offset == timingPoints[0].Offset && !timingPoints[0].IsInherited))
            {
                return(result);
            }

            // Get the closest timing point to this target.
            TimingPoint closestPoint = SearchUtils.GetClosestTimingPoint(timingPoints, target.Offset);

            // Now, the beat snap divisor divides the beat to specific parts, and
            // we need to calculate both relative and actual snaps.
            // Actual snap would be closestPoint + relativeSnap while
            // relative snap is calculated by closest red point's point
            // value (a.k.a millis value between 2 beats, 60000 / BPM).

            // Diff of objects in millis calculated as decimal
            // for maximum precision.
            decimal diff = Convert.ToDecimal(target.Offset - closestPoint.Offset);

            // Relative snap value based on BEAT_SNAP_DIVISOR.
            decimal relativeSnap = diff * BEAT_SNAP_DIVISOR_2 / Convert.ToDecimal(closestPoint.PointValue);

            // Actual snap value.
            decimal actualSnap = Convert.ToDecimal(closestPoint.GetSnap()) + relativeSnap;

            // Set the results and return. First is actual snap
            // from the start of the map, second is relative
            // snap from the closest red point.
            result[0] = Convert.ToDouble(actualSnap);
            result[1] = Convert.ToDouble(relativeSnap);
            return(result);
        }
Пример #6
0
        public static void changeBpmOfTimingPoint(Beatmap beatmap, HtmlDisplayer htmlDisplayer,
                                                  double offset, double newValue, bool shiftRestOfBeatmap,
                                                  bool saveBackups, string customPath)
        {
            if (saveBackups)
            {
                beatmap.save(customPath + "//" + beatmap.FileName);
            }

            decimal newValueDecimal = Convert.ToDecimal(newValue);

            // We need to extract the objects between the selected offset and the next timing point.
            List <TimingPoint> originalPoints = beatmap.TimingPoints;

            // Get exact and next timing points. Exact timing point cannot be null. If it is null, throw
            // a message and bail out.
            TimingPoint sourcePoint = SearchUtils.GetExactTimingPoint(originalPoints, offset);
            TimingPoint nextPoint   = SearchUtils.GetClosestNextTimingPoint(originalPoints, sourcePoint);

            if (sourcePoint == null)
            {
                if (!htmlDisplayer.containsSections())
                {
                    htmlDisplayer.addSection("Starting uninherited timing point not found in one or more difficulties.");
                }
                else
                {
                    htmlDisplayer.addLineBreak();
                }
                htmlDisplayer.addSubsection("Beatmap difficulty: " + beatmap.DifficultyName);
                htmlDisplayer.addWarning(StringUtils.GetOffsetWithLink(offset) + " does not exist.");
                return;
            }

            // Get the objects we require.
            SearchUtils.GetObjectsInBetween(beatmap, offset, nextPoint != null ? nextPoint.Offset : double.MaxValue,
                                            out IList <Bookmark> bookmarks, out IList <TimingPoint> timingPoints, out IList <HitObject> hitObjects);

            // Since the snaps are already calculated, it should be easy to calculate the next offsets
            // after changing the BPM.

            // Remove the next timing point offset from all lists.
            if (nextPoint != null)
            {
                ((SubList <Bookmark>)bookmarks).TrimEnd(x => x.Offset == nextPoint.Offset);
                ((SubList <TimingPoint>)timingPoints).TrimEnd(x => x.Offset == nextPoint.Offset);
                ((SubList <HitObject>)hitObjects).TrimEnd(x => x.Offset == nextPoint.Offset);
            }

            // Next point's snap is important to determine the offset difference for rest of the objects,
            // if shifting is enabled.
            if (shiftRestOfBeatmap && nextPoint != null)
            {
                // This means there is a next point and it should be included in timingPoints list.
                // Check the snap differences between them and shift them all first.
                decimal snapDifference   = Convert.ToDecimal(nextPoint.GetSnap()) - Convert.ToDecimal(sourcePoint.GetSnap());
                int     offsetDifference = SnapUtils.calculateEndOffsetFromBpmValue(offset, snapDifference, newValueDecimal);

                // Shift all the objects starting from the last object offset.
                SearchUtils.GetObjectsInBetween(beatmap, offset, nextPoint.Offset,
                                                out IList <Bookmark> bookmarks2, out IList <TimingPoint> timingPoints2, out IList <HitObject> hitObjects2);
                SnapUtils.shiftAllElementsByOffset(offsetDifference, hitObjects2, timingPoints2, bookmarks2);
            }

            // After this, now start calculating the end offsets from the relative snaps of the elements.
            sourcePoint.PointValue = newValue;

            // Use the newValueDecimal to adjust everything.
            SnapUtils.shiftAllElementsByNewPointValue(beatmap, sourcePoint, offset, newValueDecimal, hitObjects, timingPoints, bookmarks);

            // And the process should be complete.
        }
Пример #7
0
        public static bool EqualizeSvInArea(EqualizeSvForm form, Beatmap beatmap)
        {
            // Parse the start and end offsets.
            double startOffset;
            double endOffset;

            if (form.EqualizeAll)
            {
                SearchUtils.SortBeatmapElements(beatmap.TimingPoints);
                SearchUtils.SortBeatmapElements(beatmap.HitObjects);

                if (beatmap.HitObjects.Count > 0)
                {
                    startOffset = Math.Min(beatmap.TimingPoints.First().Offset, beatmap.HitObjects.First().Offset);
                }
                else
                {
                    startOffset = beatmap.TimingPoints[0].Offset;
                }

                if (beatmap.HitObjects.Count > 0)
                {
                    endOffset = Math.Max(beatmap.TimingPoints.Last().Offset, beatmap.HitObjects.Last().Offset);
                }
                else
                {
                    endOffset = beatmap.TimingPoints.Last().Offset;
                }
            }
            else
            {
                startOffset = form.StartOffset;
                endOffset   = form.EndOffset;
            }

            // Get the multiplier and target BPM.
            double targetBpm    = form.TargetBpm;
            double svMultiplier = form.SvMultiplier == 0 ? 1 : form.SvMultiplier;

            // Fetch objects in between this area.
            // Hold the previous one as well to keep reference to add points from.
            TimingPoint         current;
            TimingPoint         closestRedPoint;
            IList <TimingPoint> points;

            bool originalRef             = form.EqualizeAll;
            bool scaleWithExistingPoints = form.UseRelativeSv;

            if (form.EqualizeAll)
            {
                points = beatmap.TimingPoints;
            }
            else
            {
                SearchUtils.GetObjectsInBetween(beatmap, startOffset, endOffset, out points);
            }

            int startIndex  = points.Count > 0 ? beatmap.TimingPoints.IndexOf(points[0]) : -1;
            int removeCount = points.Count;
            Dictionary <TimingPoint, double> originalInheritedPointValues = new Dictionary <TimingPoint, double>();

            for (int i = 0; i < points.Count; i++)
            {
                current = points[i];
                double offset = current.Offset;

                closestRedPoint = SearchUtils.GetClosestTimingPoint(beatmap.TimingPoints, offset);
                if (targetBpm == 0)
                {
                    targetBpm = closestRedPoint.getDisplayValue();
                }

                double baseMultiplier  = targetBpm / closestRedPoint.getDisplayValue();
                double finalMultiplier = baseMultiplier * svMultiplier;

                // If this point is an inherited point, it's no problem. Just apply the SV and continue.
                if (current.IsInherited)
                {
                    applyMultiplierToPoint(originalInheritedPointValues, current, finalMultiplier, scaleWithExistingPoints);
                }
                else
                {
                    // For timing points, we need to check the exact spot. If there is not an
                    // inherited point with the exact offset, then we need to add it.
                    TimingPoint exactInheritedPoint = SearchUtils.GetExactInheritedPoint(points, offset);
                    if (exactInheritedPoint != null)
                    {
                        // We've found the current inherited point. Change the value and continue.
                        applyMultiplierToPoint(originalInheritedPointValues, exactInheritedPoint, finalMultiplier, scaleWithExistingPoints);
                    }
                    else
                    {
                        // We need to add an inherited point here. Derive from the closest
                        // timing point with the base values.
                        TimingPoint newPoint = new TimingPoint(closestRedPoint)
                        {
                            IsInherited = true
                        };

                        // Apply the multiplier.
                        applyMultiplierToPoint(originalInheritedPointValues, newPoint, finalMultiplier, scaleWithExistingPoints);
                        if (i + 1 == points.Count)
                        {
                            points.Add(newPoint);
                        }
                        else
                        {
                            points.Insert(i + 1, newPoint);
                        }
                    }
                }
            }

            // Sort the elements again.
            SearchUtils.MarkChangeMade(beatmap.TimingPoints);
            SearchUtils.SortBeatmapElements(beatmap.TimingPoints);
            return(true);
        }
Пример #8
0
        public static bool AddSvChanges(Form form, Beatmap beatmap, double firstOffset, double lastOffset,
                                        double firstSv, double lastSv, double targetBpm,
                                        double gridSnap, int svOffset, int svIncreaseMode, int count,
                                        double svIncreaseMultiplier, bool putPointsByNotes)
        {
            List <TimingPoint> actualPoints = beatmap.TimingPoints;

            if (count > 0 && lastOffset <= firstOffset)
            {
                if (putPointsByNotes)
                {
                    // We need to determine the end offset here from notes
                    // and note count.
                    SnapUtils.getEndOffsetFromObjectsByCount(beatmap, firstOffset, count, out lastOffset);
                }
                else
                {
                    if (gridSnap == 0)
                    {
                        showErrorMessageInMainThread(form, "End offset was not defined and grid snap and count values are" + Environment.NewLine + "also not defined, hence the end offset could not be calculated. Aborting.");
                        return(false);
                    }
                    lastOffset = SnapUtils.calculateEndOffset(beatmap, firstOffset, gridSnap, count);
                }
            }
            else if (lastOffset <= firstOffset)
            {
                showErrorMessageInMainThread(form, "End offset could not be calculated, necessary values are missing.");
                return(false);
            }

            SearchUtils.GetObjectsInBetween(beatmap, firstOffset, lastOffset,
                                            out IList <TimingPoint> points, out IList <HitObject> objects);

            // If "putPointsByNotes" is selected and no objects are found, throw an error
            // and return false.
            if (putPointsByNotes && objects.Count == 0)
            {
                showErrorMessageInMainThread(form, "No hit objects found in the specified area. Aborting.");
                return(false);
            }

            // We need at least 1 timing point to take reference from
            // the start of the list. Account that and add a timing
            // point if the first index is not a timing point already.
            if (!SearchUtils.IsFirstPointTimingPoint(points))
            {
                points.Insert(0, SearchUtils.GetClosestTimingPoint(actualPoints, firstOffset));
            }

            // After this, if we still don't have a timing point, we cannot proceed.
            // A map has to contain at least one timing point before or on the declared offset.
            if (!SearchUtils.ContainsTimingPoint(points))
            {
                showErrorMessageInMainThread(form, "No timing points found to take reference from. Aborting.");
                return(false);
            }

            // The POW value. This is determined by
            // "svIncreaseMode" and "svIncreaseMultiplier"
            // where "svIncreaseMode" is 0 for linear, 1 for exponential
            // and 2 for logarithmic. If mode is 0, POW is always 1.
            // If mode is 1, the POW is the original value.
            // If mode is 2, the POW is divided as 1/POW.
            double pow;

            switch (svIncreaseMode)
            {
            case 0:
                pow = 1;
                break;

            case 1:
                pow = svIncreaseMultiplier;
                break;

            case 2:
                pow = 1 / svIncreaseMultiplier;
                break;

            default:
                throw new ArgumentException("The sv increase mode has to be 0, 1 or 2, found " + svIncreaseMode + ".");
            }

            // This is a really corner case since we already prevent it from
            // happening in the SV Changer form itself, but it still is checked.
            if (pow == 0)
            {
                showErrorMessageInMainThread(form, "The sv increase mode and sv increase multiplier has" + Environment.NewLine + "resulted in the power value being 0. Aborting.");
                return(false);
            }

            // If "put points by notes" is selected,
            // we need to calculate the target offset by
            // the first note.
            if (putPointsByNotes)
            {
                firstOffset = objects[0].Offset;
            }

            // Get the start BPM value.
            double startBpmValue = SearchUtils.GetBpmValueInOffset(actualPoints, firstOffset);

            // If "targetBpm" is entered, we need to apply a ratio to the last SV.
            // Apply the logic here and change the last SV value depending on it.
            if (targetBpm != 0)
            {
                double ratio = targetBpm / SearchUtils.GetBpmInOffset(actualPoints, firstOffset);
                lastSv *= ratio;
            }

            // Now that we have set already in place, let's calculate the SVs and
            // add or edit them.

            // The temp value to use on additional SVs.
            double targetSv;

            // The temp value for the SV for osu! representation.
            double targetSvValue;

            // The current percentage that will result
            // in the final SV of the point. This should be
            // always equal to percentage change if the change is linear.
            double targetPowerValue;

            // Define a target offset object and
            // calculate the target offset depending
            // on the passed parameters, a.k.a "putPointsByNoteSnaps"
            // or "gridSnap" and "count".
            double targetOffset = firstOffset;

            // Used in determining which hit object
            // we should use if "put points by notes"
            // is selected, as the target offset.
            int hitObjectIndex = 0;

            while (targetOffset <= lastOffset)
            {
                // Compute the actual target offset, where the offset is determined
                // but needs to be shifted depending on user input.
                double actualTargetOffset = targetOffset + svOffset;

                // This is the BPM value, not the BPM itself, a.k.a it is the osu! representation
                // of a BPM value. For instance, this is 1000 if BPM is 120 in the map, where
                // a beat is in a total second.
                double currentBpmValue = SearchUtils.GetBpmValueInOffset(points, targetOffset);

                // The ratio that we need to multiply while calculating the SV.
                double ratio = startBpmValue / currentBpmValue;

                // Get the closest and exact inherited points (if exists)
                // Closest point cannot be null (it will return the first timing point
                // in worst case), but exact point can be null.
                TimingPoint closestPoint = SearchUtils.GetClosestPoint(actualPoints, targetOffset, true);
                TimingPoint exactPoint   = SearchUtils.GetExactInheritedPoint(actualPoints, targetOffset);

                // The copy point that we need to hold the attributes of.
                TimingPoint copy;

                // Determines if the object is already existing in the list.
                bool exists = false;

                // If exact point is not null, we need to edit that.
                if (exactPoint != null)
                {
                    copy   = exactPoint;
                    exists = true;
                }
                else
                {
                    copy = new TimingPoint(closestPoint)
                    {
                        // Make sure the copy one is inherited.
                        IsInherited = true
                    };
                }

                // Get the current offset power value. This determines
                // how much the target SV will be as in "startSv + this value".
                targetPowerValue = MathUtils.calculateMultiplierFromPower(svIncreaseMultiplier,
                                                                          firstOffset, lastOffset, targetOffset);

                // Calculate the target SV. Now, the value should be divided by -100 / target SV
                // to achieve the osu! representation of this value.
                // Ratio is the BPM ratio between the start and current offset. Should be 1
                // if the BPMs are the same.
                targetSv      = MathUtils.calculateValueFromPercentage(firstSv, lastSv, targetPowerValue) * ratio;
                targetSvValue = -100d / targetSv;

                // At this point, we need to either add the point, or
                // replace the existing one. If the existing one toggles kiai,
                // and the actualTargetOffset is different from this point's offset,
                // we need to add a point and change the kiai one too instead.
                // Otherwise, we just move the point.
                if (exists)
                {
                    if (SearchUtils.TogglesKiai(actualPoints, copy))
                    {
                        // If there is an offset change (a.k.a actualTargetOffset not
                        // equal to targetOffset) we need to add a point and change
                        // this one as well.
                        if (actualTargetOffset != targetOffset)
                        {
                            TimingPoint copy2 = new TimingPoint(copy)
                            {
                                Offset     = actualTargetOffset,
                                IsKiaiOpen = !copy.IsKiaiOpen,
                                PointValue = targetSvValue
                            };
                            copy.PointValue = targetSvValue;

                            // Now, there is a trick here. If the actual inherited point
                            // exists with the edited offset, we need to edit that, not
                            // add a duplicate one and force an exception.
                            // Just set "exact" values of "copy" into this.
                            TimingPoint exact = SearchUtils.GetExactInheritedPoint(actualPoints, actualTargetOffset);
                            if (exact != null)
                            {
                                exact.setTo(copy);
                            }
                            else
                            {
                                // We haven't found an exact timing point so this is fine.
                                actualPoints.Insert(SearchUtils.GetAdditionIndex(actualPoints, copy2), copy2);
                            }
                        }
                        else
                        {
                            copy.PointValue = targetSvValue;
                            copy.Offset     = actualTargetOffset;
                            SearchUtils.MarkChangeMade(actualPoints);
                        }
                    }
                    else
                    {
                        // If there is an offset change (a.k.a actualTargetOffset not
                        // equal to targetOffset) we need to add a point and change
                        // this one as well.
                        if (actualTargetOffset != targetOffset)
                        {
                            // This time, do not change the kiai.
                            TimingPoint copy2 = new TimingPoint(copy)
                            {
                                Offset     = actualTargetOffset,
                                PointValue = targetSvValue
                            };
                            copy.PointValue = targetSvValue;

                            // Now, there is a trick here. If the actual inherited point
                            // exists with the edited offset, we need to edit that, not
                            // add a duplicate one and force an exception.
                            // Just set "exact" values of "copy" into this.
                            TimingPoint exact = SearchUtils.GetExactInheritedPoint(actualPoints, actualTargetOffset);
                            if (exact != null)
                            {
                                exact.setTo(copy);
                            }
                            else
                            {
                                // We haven't found an exact timing point so this is fine.
                                actualPoints.Insert(SearchUtils.GetAdditionIndex(actualPoints, copy2), copy2);
                            }
                        }
                        else
                        {
                            copy.PointValue = targetSvValue;
                            copy.Offset     = actualTargetOffset;
                            SearchUtils.MarkChangeMade(actualPoints);
                        }

                        copy.PointValue = targetSvValue;
                        copy.Offset     = actualTargetOffset;
                        SearchUtils.MarkChangeMade(actualPoints);
                    }
                }
                else
                {
                    copy.PointValue = targetSvValue;
                    copy.Offset     = actualTargetOffset;

                    // Now, there is a trick here. If the actual inherited point
                    // exists with the edited offset, we need to edit that, not
                    // add a duplicate one and force an exception.
                    // Just set "exact" values of "copy" into this.
                    TimingPoint exact = SearchUtils.GetExactInheritedPoint(actualPoints, actualTargetOffset);
                    if (exact != null)
                    {
                        exact.setTo(copy);
                    }
                    else
                    {
                        // We haven't found an exact timing point so this is fine.
                        actualPoints.Insert(SearchUtils.GetAdditionIndex(actualPoints, copy), copy);
                    }
                }

                // At the bottom, calculate the next offset
                // and continue.
                if (putPointsByNotes)
                {
                    if (hitObjectIndex == objects.Count - 1)
                    {
                        break;
                    }
                    targetOffset = objects[++hitObjectIndex].Offset;
                }
                else
                {
                    targetOffset += gridSnap / currentBpmValue;
                }
            }

            // At the end, force sort the points
            // and return true.
            actualPoints.Sort();
            SearchUtils.MarkSorted(actualPoints);
            return(true);
        }