/// <summary> /// Places each hit object of the pattern beatmap into the other beatmap and applies timingpoint changes to copy timingpoint stuff aswell. /// The given pattern beatmap could be modified by this method if protectBeatmapPattern is false. /// </summary> /// <param name="patternBeatmap">The pattern beatmap to be placed into the beatmap.</param> /// <param name="beatmap">To beatmap to place the pattern in.</param> /// <param name="offset">An offset to move the pattern beatmap in time with.</param> /// <param name="protectBeatmapPattern">If true, copies the pattern beatmap to prevent the pattern beatmap from being modified by this method.</param> public void PlaceOsuPattern(Beatmap patternBeatmap, Beatmap beatmap, double offset = 0, bool protectBeatmapPattern = true) { if (protectBeatmapPattern) { // Copy so the original pattern doesnt get changed patternBeatmap = patternBeatmap.DeepCopy(); } // Do the offset if (Math.Abs(offset) > Precision.DOUBLE_EPSILON) { patternBeatmap.OffsetTime(offset); } // We adjust the pattern first so it alligns with the beatmap. // The right timing is applied and optional pre-processing is applied. // Sliderends and object timingpoints get recalculated. PreparePattern(patternBeatmap, beatmap, out var parts, out var timingPointsChanges); // Keep just the timing point changes which are inside the parts. // These timing point changes have everything that is necessary for inside the parts of the pattern. (even timing) timingPointsChanges = timingPointsChanges.Where(tpc => parts.Any(part => part.StartTime <= tpc.MyTP.Offset && part.EndTime >= tpc.MyTP.Offset)).ToList(); // Remove stuff if (PatternOverwriteMode != PatternOverwriteMode.NoOverwrite) { foreach (var part in parts) { RemovePartOfBeatmap(beatmap, part.StartTime - Padding, part.EndTime + Padding); } } // Add timingpoint changes for each hitobject to make sure they still have the wanted SV and hitsounds (especially near the edges of parts) // It is possible for the timingpoint of a hitobject at the start of a part to be outside of the part, so this fixes issues related to that timingPointsChanges.AddRange( beatmap.HitObjects.Where(ho => ho.TimingPoint != null) .Select(GetSvChange)); if (IncludeHitsounds) { timingPointsChanges.AddRange( beatmap.HitObjects.Where(ho => ho.HitsoundTimingPoint != null) .Select(GetHitsoundChange)); } // Apply the changes TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges); // Add the hitobjects of the pattern beatmap.HitObjects.AddRange(patternBeatmap.HitObjects); // Sort hitobjects beatmap.SortHitObjects(); if (FixColourHax) { beatmap.FixComboSkip(); } beatmap.GiveObjectsGreenlines(); beatmap.CalculateSliderEndTimes(); }