private string Run_Program(MapCleanerVm args, BackgroundWorker worker, DoWorkEventArgs _) { var result = new Classes.Tools.MapCleaner.MapCleanerResult(); var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); if (args.Paths.Length == 1) { var editor = EditorReaderStuff.GetNewestVersionOrNot(args.Paths[0], reader); List <TimingPoint> orgininalTimingPoints = editor.Beatmap.BeatmapTiming.TimingPoints.Select(tp => tp.Copy()).ToList(); int oldTimingPointsCount = editor.Beatmap.BeatmapTiming.TimingPoints.Count; result.Add(Classes.Tools.MapCleaner.CleanMap(editor, args.MapCleanerArgs, worker)); // Update result with removed count int removed = oldTimingPointsCount - editor.Beatmap.BeatmapTiming.TimingPoints.Count; result.TimingPointsRemoved += removed; List <TimingPoint> newTimingPoints = editor.Beatmap.BeatmapTiming.TimingPoints; Monitor_Differences(orgininalTimingPoints, newTimingPoints); // Save the file editor.SaveFile(); } else { foreach (string path in args.Paths) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader); int oldTimingPointsCount = editor.Beatmap.BeatmapTiming.TimingPoints.Count; result.Add(Classes.Tools.MapCleaner.CleanMap(editor, args.MapCleanerArgs, worker)); // Update result with removed count int removed = oldTimingPointsCount - editor.Beatmap.BeatmapTiming.TimingPoints.Count; result.TimingPointsRemoved += removed; // Save the file editor.SaveFile(); } } // Do stuff if (args.Quick) { RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null)); } // Make an accurate message string message = $"Successfully {(result.TimingPointsRemoved < 0 ? "added" : "removed")} {Math.Abs(result.TimingPointsRemoved)} {(Math.Abs(result.TimingPointsRemoved) == 1 ? "greenline" : "greenlines")}" + (args.MapCleanerArgs.ResnapObjects ? $" and resnapped {result.ObjectsResnapped} {(result.ObjectsResnapped == 1 ? "object" : "objects")}" : "") + (args.MapCleanerArgs.RemoveUnusedSamples ? $" and removed {result.SamplesRemoved} unused {(result.SamplesRemoved == 1 ? "sample" : "samples")}" : "") + "!"; return(args.Quick ? string.Empty : message); }
private string PlaceHitsounds(Arguments args, BackgroundWorker worker, DoWorkEventArgs _) { if (args.Zones.Count == 0) { return("There are no zones!"); } var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (string path in args.Paths) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader); Beatmap beatmap = editor.Beatmap; Timeline timeline = beatmap.GetTimeline(); for (int i = 0; i < timeline.TimelineObjects.Count; i++) { var tlo = timeline.TimelineObjects[i]; var column = args.Zones.FirstOrDefault(); double best = double.MaxValue; foreach (var c in args.Zones) { double dist = c.Distance(tlo.Origin.Pos); if (dist < best) { best = dist; column = c; } } if (column == null) { continue; } tlo.Filename = column.Filename; tlo.SampleSet = column.SampleSet; tlo.AdditionSet = column.AdditionsSet; tlo.CustomIndex = column.CustomIndex; tlo.SampleVolume = 0; tlo.SetHitsound(column.Hitsound); tlo.HitsoundsToOrigin(); UpdateProgressBar(worker, (int)(100f * i / beatmap.HitObjects.Count)); } // Save the file editor.SaveFile(); } // Do stuff RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null, args.Quick)); return(args.Quick ? "" : "Done!"); }
private static string Copy_Metadata(MetadataManagerVm arg, BackgroundWorker worker, DoWorkEventArgs _) { var paths = arg.ExportPath.Split('|'); var mapsDone = 0; var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (var path in paths) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader); var beatmap = editor.Beatmap; beatmap.Metadata["ArtistUnicode"].Value = arg.Artist; beatmap.Metadata["Artist"].Value = arg.RomanisedArtist; beatmap.Metadata["TitleUnicode"].Value = arg.Title; beatmap.Metadata["Title"].Value = arg.RomanisedTitle; beatmap.Metadata["Creator"].Value = arg.BeatmapCreator; beatmap.Metadata["Source"].Value = arg.Source; beatmap.Metadata["Tags"].Value = arg.Tags; beatmap.General["PreviewTime"] = new TValue(arg.PreviewTime.ToRoundInvariant()); if (arg.UseComboColours) { beatmap.ComboColours = new List <ComboColour>(arg.ComboColours); beatmap.SpecialColours.Clear(); foreach (var specialColour in arg.SpecialColours) { beatmap.SpecialColours.Add(specialColour.Name, specialColour); } } // Save the file with name update because we updated the metadata editor.SaveFileWithNameUpdate(); // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(++mapsDone * 100 / paths.Length); } } // Make an accurate message var message = $"Successfully exported metadata to {mapsDone} {(mapsDone == 1 ? "beatmap" : "beatmaps")}!"; return(message); }
private string ExportPattern(PatternGalleryVm args, BackgroundWorker worker, DoWorkEventArgs _) { var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); var editor = EditorReaderStuff.GetNewestVersionOrNot(IOHelper.GetCurrentBeatmapOrCurrentBeatmap(), reader); var pattern = args.Patterns.FirstOrDefault(o => o.IsSelected); if (pattern == null) { throw new Exception("No pattern has been selected to export."); } var patternBeatmap = pattern.GetPatternBeatmap(args.FileHandler); var patternPlacer = args.OsuPatternPlacer; if (reader != null) { patternPlacer.PlaceOsuPatternAtTime(patternBeatmap, editor.Beatmap, reader.EditorTime(), false); } else { patternPlacer.PlaceOsuPattern(patternBeatmap, editor.Beatmap, protectBeatmapPattern: false); } editor.SaveFile(); // Increase pattern use count and time pattern.UseCount++; pattern.LastUsedTime = DateTime.Now; // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do stuff if (args.Quick) { RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null)); } return("Successfully exported pattern!"); }
private string Copy_Timing(TimingCopierVm arg, BackgroundWorker worker, DoWorkEventArgs _) { string[] paths = arg.ExportPath.Split('|'); int mapsDone = 0; var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (string exportPath in paths) { var editorTo = EditorReaderStuff.GetNewestVersionOrNot(exportPath, reader); var editorFrom = EditorReaderStuff.GetNewestVersionOrNot(arg.ImportPath, reader); Beatmap beatmapTo = editorTo.Beatmap; Beatmap beatmapFrom = editorFrom.Beatmap; Timing timingTo = beatmapTo.BeatmapTiming; Timing timingFrom = beatmapFrom.BeatmapTiming; // Get markers for hitobjects if mode 1 is used List <Marker> markers = new List <Marker>(); if (arg.ResnapMode == "Number of beats between objects stays the same") { markers = GetMarkers(beatmapTo, timingTo); } // Rid the beatmap of redlines // If a greenline exists at the same time as a redline then the redline ceizes to exist // Else convert the redline to a greenline: Inherited = false & MpB = -100 List <TimingPoint> removeList = new List <TimingPoint>(); foreach (TimingPoint redline in timingTo.GetAllRedlines()) { TimingPoint greenlineHere = timingTo.GetGreenlineAtTime(redline.Offset); if (greenlineHere.Offset == redline.Offset) { removeList.Add(redline); } else { redline.Uninherited = false; redline.MpB = -100; } } foreach (TimingPoint tp in removeList) { timingTo.TimingPoints.Remove(tp); } // Make new timing points changes List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>(); // Add redlines List <TimingPoint> redlines = timingFrom.GetAllRedlines(); foreach (TimingPoint tp in redlines) { timingPointsChanges.Add(new TimingPointsChange(tp, mpb: true, meter: true, inherited: true, omitFirstBarLine: true)); } // Apply timing changes TimingPointsChange.ApplyChanges(timingTo, timingPointsChanges); if (arg.ResnapMode == "Number of beats between objects stays the same") { redlines = timingTo.GetAllRedlines(); List <double> newBookmarks = new List <double>(); double lastTime = redlines.FirstOrDefault().Offset; foreach (Marker marker in markers) { // Get redlines between this and last marker TimingPoint redline = timingTo.GetRedlineAtTime(lastTime, redlines.FirstOrDefault()); double beatsFromLastTime = marker.BeatsFromLastMarker; while (true) { List <TimingPoint> redlinesBetween = redlines.Where(o => o.Offset <= lastTime + redline.MpB * beatsFromLastTime && o.Offset > lastTime).ToList(); if (redlinesBetween.Count == 0) { break; } TimingPoint first = redlinesBetween.First(); double diff = first.Offset - lastTime; beatsFromLastTime -= diff / redline.MpB; redline = first; lastTime = first.Offset; } // Last time is the time of the last redline in between double newTime = lastTime + redline.MpB * beatsFromLastTime; newTime = timingTo.Resnap(newTime, arg.Snap1, arg.Snap2, firstTp: redlines.FirstOrDefault()); marker.Time = newTime; lastTime = marker.Time; } // Add the bookmarks foreach (Marker marker in markers) { // Check whether the marker is a bookmark if (marker.Object is double) { // Don't resnap bookmarks newBookmarks.Add((double)marker.Object); } } beatmapTo.SetBookmarks(newBookmarks); } else if (arg.ResnapMode == "Just resnap") { // Resnap hitobjects foreach (HitObject ho in beatmapTo.HitObjects) { ho.ResnapSelf(timingTo, arg.Snap1, arg.Snap2, firstTp: redlines.FirstOrDefault()); ho.ResnapEnd(timingTo, arg.Snap1, arg.Snap2, firstTp: redlines.FirstOrDefault()); } // Resnap greenlines foreach (TimingPoint tp in timingTo.GetAllGreenlines()) { tp.ResnapSelf(timingTo, arg.Snap1, arg.Snap2, firstTP: redlines.FirstOrDefault()); } } else { // Don't move objects } // Save the file editorTo.SaveFile(); // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(++mapsDone * 100 / paths.Length); } } // Make an accurate message string message = $"Successfully copied timing to {mapsDone} {(mapsDone == 1 ? "beatmap" : "beatmaps")}!"; return(message); }
private string Run_Program(AutoFailDetectorVm args, BackgroundWorker worker, DoWorkEventArgs _) { var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); var editor = EditorReaderStuff.GetNewestVersionOrNot(args.Paths[0], reader); var beatmap = editor.Beatmap; // Get approach time and radius of the 50 score hit window var ar = args.ApproachRateOverride == -1 ? editor.Beatmap.Difficulty["ApproachRate"].DoubleValue : args.ApproachRateOverride; var approachTime = (int)Beatmap.GetApproachTime(ar); var od = args.OverallDifficultyOverride == -1 ? editor.Beatmap.Difficulty["OverallDifficulty"].DoubleValue : args.OverallDifficultyOverride; var window50 = (int)Math.Ceiling(200 - 10 * od); // Start time and end time var mapStartTime = (int)beatmap.GetMapStartTime(); var mapEndTime = (int)beatmap.GetMapEndTime(); var autoFailTime = (int)beatmap.GetAutoFailCheckTime(); // Detect auto-fail var autoFailDetector = new Classes.Tools.AutoFailDetector(beatmap.HitObjects, mapStartTime, mapEndTime, autoFailTime, approachTime, window50, args.PhysicsUpdateLeniency); var autoFail = autoFailDetector.DetectAutoFail(); if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(33); } // Fix auto-fail if (args.GetAutoFailFix) { var placedFix = autoFailDetector.AutoFailFixDialogue(args.AutoPlaceFix); if (placedFix) { editor.SaveFile(); } } if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(67); } // Set the timeline lists _unloadingObjects = args.ShowUnloadingObjects ? autoFailDetector.UnloadingObjects : new List <double>(); _potentialUnloadingObjects = args.ShowPotentialUnloadingObjects ? autoFailDetector.PotentialUnloadingObjects : new List <double>(); _potentialDisruptors = args.ShowPotentialDisruptors ? autoFailDetector.Disruptors : new List <double>(); // Set end time for the timeline _endTimeMonitor = mapEndTime; // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do stuff RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, false, args.Quick)); return(autoFail ? $"{autoFailDetector.UnloadingObjects.Count} unloading objects detected and {autoFailDetector.PotentialUnloadingObjects.Count} potential unloading objects detected!" : autoFailDetector.PotentialUnloadingObjects.Count > 0 ? $"No auto-fail, but {autoFailDetector.PotentialUnloadingObjects.Count} potential unloading objects detected." : "No auto-fail detected."); }
private string Copy_Hitsounds(HitsoundCopierVm arg, BackgroundWorker worker) { var doMutedIndex = arg.MutedIndex >= 0; var paths = arg.PathTo.Split('|'); var mapsDone = 0; var sampleSchema = new SampleSchema(); var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (var pathTo in paths) { BeatmapEditor editorTo = EditorReaderStuff.GetNewestVersionOrNot(pathTo, reader);; Beatmap beatmapTo = editorTo.Beatmap; Beatmap beatmapFrom; if (!string.IsNullOrEmpty(arg.PathFrom)) { var editorFrom = EditorReaderStuff.GetNewestVersionOrNot(arg.PathFrom, reader); beatmapFrom = editorFrom.Beatmap; } else { // Copy from an empty beatmap similar to the map to copy to beatmapFrom = beatmapTo.DeepCopy(); beatmapFrom.HitObjects.Clear(); beatmapFrom.BeatmapTiming.Clear(); } Timeline processedTimeline; if (arg.CopyMode == 0) { // Every defined hitsound and sampleset on hitsound gets copied to their copyTo destination // Timelines var tlTo = beatmapTo.GetTimeline(); var tlFrom = beatmapFrom.GetTimeline(); var volumeMuteTimes = arg.CopyVolumes && arg.AlwaysPreserve5Volume ? new List <double>() : null; if (arg.CopyHitsounds) { ResetHitObjectHitsounds(beatmapTo); CopyHitsounds(arg, tlFrom, tlTo); } // Save tlo times where timingpoint volume is 5% // Timingpointchange all the undefined tlo from copyFrom volumeMuteTimes?.AddRange(from tloTo in tlTo.TimelineObjects where tloTo.CanCopy && Math.Abs(tloTo.SampleVolume) < Precision.DOUBLE_EPSILON && Math.Abs(tloTo.FenoSampleVolume - 5) < Precision.DOUBLE_EPSILON select tloTo.Time); // Volumes and samplesets and customindices greenlines get copied with timingpointchanges and allafter enabled var timingPointsChanges = beatmapFrom.BeatmapTiming.TimingPoints.Select(tp => new TimingPointsChange(tp, sampleset: arg.CopySampleSets, index: arg.CopySampleSets, volume: arg.CopyVolumes)).ToList(); // Apply the timingpoint changes TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChanges, true); processedTimeline = tlTo; // Return 5% volume to tlo that had it before if (volumeMuteTimes != null) { var timingPointsChangesMute = new List <TimingPointsChange>(); processedTimeline.GiveTimingPoints(beatmapTo.BeatmapTiming); // Exclude objects which use their own sample volume property instead foreach (var tloTo in processedTimeline.TimelineObjects.Where(o => Math.Abs(o.SampleVolume) < Precision.DOUBLE_EPSILON)) { if (volumeMuteTimes.Contains(tloTo.Time)) { // Add timingpointschange to copy timingpoint hitsounds var tp = tloTo.HitsoundTimingPoint.Copy(); tp.Offset = tloTo.Time; tp.Volume = 5; timingPointsChangesMute.Add(new TimingPointsChange(tp, volume: true)); } else { // Add timingpointschange to preserve index and volume var tp = tloTo.HitsoundTimingPoint.Copy(); tp.Offset = tloTo.Time; tp.Volume = tloTo.FenoSampleVolume; timingPointsChangesMute.Add(new TimingPointsChange(tp, volume: true)); } } // Apply the timingpoint changes TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChangesMute); } } else { // Smarty mode // Copy the defined hitsounds literally (not feno, that will be reserved for cleaner). Only the tlo that have been defined by copyFrom get overwritten. var tlTo = beatmapTo.GetTimeline(); var tlFrom = beatmapFrom.GetTimeline(); var timingPointsChanges = new List <TimingPointsChange>(); var mode = (GameMode)beatmapTo.General["Mode"].IntValue; var mapDir = editorTo.GetParentFolder(); var firstSamples = HitsoundImporter.AnalyzeSamples(mapDir); if (arg.CopyHitsounds) { CopyHitsounds(arg, beatmapTo, tlFrom, tlTo, timingPointsChanges, mode, mapDir, firstSamples, ref sampleSchema); } if (arg.CopyBodyHitsounds) { // Remove timingpoints in beatmapTo that are in a sliderbody/spinnerbody for both beatmapTo and BeatmapFrom foreach (var tp in from ho in beatmapTo.HitObjects from tp in ho.BodyHitsounds where beatmapFrom.HitObjects.Any(o => o.Time <tp.Offset && o.EndTime> tp.Offset) where !tp.Uninherited select tp) { beatmapTo.BeatmapTiming.Remove(tp); } // Get timingpointschanges for every timingpoint from beatmapFrom that is in a sliderbody/spinnerbody for both beatmapTo and BeatmapFrom timingPointsChanges.AddRange(from ho in beatmapFrom.HitObjects from tp in ho.BodyHitsounds where beatmapTo.HitObjects.Any(o => o.Time <tp.Offset && o.EndTime> tp.Offset) select new TimingPointsChange(tp.Copy(), sampleset: arg.CopySampleSets, index: arg.CopySampleSets, volume: arg.CopyVolumes)); } // Apply the timingpoint changes TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChanges); processedTimeline = tlTo; } if (arg.CopyStoryboardedSamples) { if (arg.CopyMode == 0) { beatmapTo.StoryboardSoundSamples.Clear(); } beatmapTo.GiveObjectsGreenlines(); processedTimeline.GiveTimingPoints(beatmapTo.BeatmapTiming); var mapDir = editorTo.GetParentFolder(); var firstSamples = HitsoundImporter.AnalyzeSamples(mapDir, true); var samplesTo = new HashSet <StoryboardSoundSample>(beatmapTo.StoryboardSoundSamples); var mode = (GameMode)beatmapTo.General["Mode"].IntValue; foreach (var sampleFrom in beatmapFrom.StoryboardSoundSamples) { if (arg.IgnoreHitsoundSatisfiedSamples) { var tloHere = processedTimeline.TimelineObjects.FindAll(o => Math.Abs(o.Time - sampleFrom.StartTime) <= arg.TemporalLeniency); var samplesHere = new HashSet <string>(); foreach (var tlo in tloHere) { foreach (var filename in tlo.GetPlayingFilenames(mode)) { var samplePath = Path.Combine(mapDir, filename); var fullPathExtLess = Path.Combine(Path.GetDirectoryName(samplePath), Path.GetFileNameWithoutExtension(samplePath)); if (firstSamples.Keys.Contains(fullPathExtLess)) { samplePath = firstSamples[fullPathExtLess]; } samplesHere.Add(samplePath); } } var sbSamplePath = Path.Combine(mapDir, sampleFrom.FilePath); var sbFullPathExtLess = Path.Combine(Path.GetDirectoryName(sbSamplePath), Path.GetFileNameWithoutExtension(sbSamplePath)); if (firstSamples.Keys.Contains(sbFullPathExtLess)) { sbSamplePath = firstSamples[sbFullPathExtLess]; } if (samplesHere.Contains(sbSamplePath)) { continue; } } // Add the StoryboardSoundSamples from beatmapFrom to beatmapTo if it doesn't already have the sample if (!samplesTo.Contains(sampleFrom)) { beatmapTo.StoryboardSoundSamples.Add(sampleFrom); } } // Sort the storyboarded samples beatmapTo.StoryboardSoundSamples.Sort(); } if (arg.MuteSliderends) { var timingPointsChanges = new List <TimingPointsChange>(); beatmapTo.GiveObjectsGreenlines(); processedTimeline.GiveTimingPoints(beatmapTo.BeatmapTiming); foreach (var tloTo in processedTimeline.TimelineObjects) { if (FilterMuteTlo(tloTo, beatmapTo, arg)) { // Set volume to 5%, remove all hitsounds, apply customindex and sampleset tloTo.SampleSet = arg.MutedSampleSet; tloTo.AdditionSet = 0; tloTo.Normal = false; tloTo.Whistle = false; tloTo.Finish = false; tloTo.Clap = false; tloTo.HitsoundsToOrigin(); // Add timingpointschange to copy timingpoint hitsounds var tp = tloTo.HitsoundTimingPoint.Copy(); tp.Offset = tloTo.Time; tp.SampleSet = arg.MutedSampleSet; tp.SampleIndex = arg.MutedIndex; tp.Volume = 5; timingPointsChanges.Add(new TimingPointsChange(tp, sampleset: true, index: doMutedIndex, volume: true)); } else { // Add timingpointschange to preserve index and volume and sampleset var tp = tloTo.HitsoundTimingPoint.Copy(); tp.Offset = tloTo.Time; timingPointsChanges.Add(new TimingPointsChange(tp, sampleset: true, index: doMutedIndex, volume: true)); } } // Apply the timingpoint changes TimingPointsChange.ApplyChanges(beatmapTo.BeatmapTiming, timingPointsChanges); } // Save the file editorTo.SaveFile(); // Export the sample schema if there are samples if (sampleSchema.Count > 0) { string exportFolder = MainWindow.ExportPath; DirectoryInfo di = new DirectoryInfo(exportFolder); foreach (FileInfo file in di.GetFiles()) { file.Delete(); } HitsoundExporter.ExportSampleSchema(sampleSchema, exportFolder); System.Diagnostics.Process.Start(exportFolder); } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(++mapsDone * 100 / paths.Length); } } return("Done!"); }
private string Adjust_Timing(Arguments arg, BackgroundWorker worker, DoWorkEventArgs _) { // Count int RedlinesAdded = 0; var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (string path in arg.Paths) { // Open beatmap var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader); Beatmap beatmap = editor.Beatmap; Timing timing = beatmap.BeatmapTiming; // Get all the times to snap List <Marker> markers = new List <Marker>(); if (arg.Objects) { foreach (HitObject ho in beatmap.HitObjects) { markers.Add(new Marker(ho.Time)); } } if (arg.Bookmarks) { foreach (double time in beatmap.GetBookmarks()) { markers.Add(new Marker(time)); } } if (arg.Greenlines) { // Get the offsets of greenlines foreach (TimingPoint tp in timing.TimingPoints) { if (tp.Uninherited == false) { markers.Add(new Marker(tp.Offset)); } } } if (arg.Redlines) { // Get the offsets of redlines foreach (TimingPoint tp in timing.TimingPoints) { if (tp.Uninherited == true) { markers.Add(new Marker(tp.Offset)); } } } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(20); } // Sort the markers markers = markers.OrderBy(o => o.Time).ToList(); // Calculate the beats between time and the last time or redline for each time // Time the same is 0 // Time a little after is smallest snap for (int i = 0; i < markers.Count; i++) { Marker marker = markers[i]; double time = marker.Time; TimingPoint redline = timing.GetRedlineAtTime(time - 1); // Resnap to that redline only double resnappedTime = timing.Resnap(time, arg.BeatDivisors, false, tp: redline); // Calculate beats from the redline double beatsFromRedline = (resnappedTime - redline.Offset) / redline.MpB; // Avoid problems if (MathHelper.ApproximatelyEquivalent(beatsFromRedline, 0, 0.0001)) { beatsFromRedline = arg.BeatDivisors.Max(o => o.GetValue()); } if (time == redline.Offset) { beatsFromRedline = 0; } // Initialize the beats from last marker double beatsFromLastMarker = beatsFromRedline; // Get the times between redline and this time List <Marker> timesBefore = markers.Where(o => o.Time <time && o.Time> redline.Offset).ToList(); if (timesBefore.Count > 0) { // Get the last time info double lastTime = timesBefore.Last().Time; double resnappedTimeL = timing.Resnap(lastTime, arg.BeatDivisors, false); // Change the beats from last marker beatsFromLastMarker = (resnappedTime - resnappedTimeL) / redline.MpB; // Avoid problems if (MathHelper.ApproximatelyEquivalent(beatsFromLastMarker, 0, 0.0001)) { beatsFromLastMarker = arg.BeatDivisors.Max(o => o.GetValue()); } if (lastTime == time) { beatsFromLastMarker = 0; } } // Set the variable marker.BeatsFromLastMarker = beatsFromLastMarker; } // Remove redlines except the first redline if (!arg.Redlines) { var first = timing.TimingPoints.FirstOrDefault(o => o.Uninherited); timing.RemoveAll(o => o.Uninherited && o != first); } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(40); } // Loop through all the markers for (int i = 0; i < markers.Count; i++) { Marker marker = markers[i]; double time = marker.Time; TimingPoint redline = timing.GetRedlineAtTime(time - 1); double beatsFromLastMarker = arg.BeatsBetween != -1 ? arg.BeatsBetween : marker.BeatsFromLastMarker; // Skip if 0 beats from last marker if (beatsFromLastMarker == 0) { continue; } // Get the times between redline and this time including this time List <Marker> markersBefore = markers.Where(o => o.Time <time && o.Time> redline.Offset).ToList(); markersBefore.Add(marker); // Calculate MpB // Average MpB from timesBefore and use time from redline double mpb = 0; double beatsFromRedline = 0; foreach (Marker markerB in markersBefore) { beatsFromRedline += markerB.BeatsFromLastMarker; mpb += GetMpB(markerB.Time - redline.Offset, beatsFromRedline, 0); } mpb /= markersBefore.Count; // Check if this MpB doesn't make the markers go offsnap too far bool canChangeRedline = CheckMpB(mpb, markersBefore, redline, arg); // Make changes if (canChangeRedline) { // Round the MpB to human values first mpb = HumanRoundMpB(mpb, markersBefore, redline, arg); // Change the MpB of the redline redline.MpB = mpb; } else { // Get the last time info and not the current markersBefore.Remove(marker); double lastTime = markersBefore.Last().Time; // Make new redline TimingPoint newRedline = redline.Copy(); TimingPoint lastHitsounds = timing.GetTimingPointAtTime(lastTime + 5); newRedline.Offset = lastTime; newRedline.OmitFirstBarLine = arg.OmitBarline; // Set omit to the argument newRedline.Kiai = lastHitsounds.Kiai; newRedline.SampleIndex = lastHitsounds.SampleIndex; newRedline.SampleSet = lastHitsounds.SampleSet; newRedline.Volume = lastHitsounds.Volume; timing.Add(newRedline); // Set the MpB newRedline.MpB = GetMpB(time - lastTime, beatsFromLastMarker, arg.Leniency); // Update the counter RedlinesAdded++; } // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i * 60 / markers.Count + 40); } } // Save the file editor.SaveFile(); } // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Make an accurate message string message = "Successfully added "; message += RedlinesAdded; if (Math.Abs(RedlinesAdded) == 1) { message += " redlines!"; } else { message += " redlines!"; } return(message); }
private string ExportPattern(PatternGalleryVm args, BackgroundWorker worker, DoWorkEventArgs _) { EditorReader reader; double exportTime = 0; bool usePatternOffset = false; switch (args.ExportTimeMode) { case ExportTimeMode.Current: try { reader = EditorReaderStuff.GetFullEditorReader(); exportTime = reader.EditorTime(); } catch (Exception e) { throw new Exception("Could not fetch the current editor time.", e); } break; case ExportTimeMode.Pattern: reader = EditorReaderStuff.GetFullEditorReaderOrNot(); usePatternOffset = true; break; case ExportTimeMode.Custom: reader = EditorReaderStuff.GetFullEditorReaderOrNot(); exportTime = args.CustomExportTime; break; default: throw new ArgumentOutOfRangeException(nameof(ExportTimeMode), "Invalid value encountered"); } var editor = EditorReaderStuff.GetNewestVersionOrNot(args.Paths[0], reader); var patternCount = args.Patterns.Count(o => o.IsSelected); if (patternCount == 0) { throw new Exception("No pattern has been selected to export."); } var patternPlacer = args.OsuPatternPlacer; foreach (var pattern in args.Patterns.Where(o => o.IsSelected)) { var patternBeatmap = pattern.GetPatternBeatmap(args.FileHandler); if (usePatternOffset) { patternPlacer.PlaceOsuPattern(patternBeatmap, editor.Beatmap, protectBeatmapPattern: false); } else { patternPlacer.PlaceOsuPatternAtTime(patternBeatmap, editor.Beatmap, exportTime, false); } // Increase pattern use count and time pattern.UseCount++; pattern.LastUsedTime = DateTime.Now; } editor.SaveFile(); // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do stuff RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null, args.Quick)); return("Successfully exported pattern!"); }
private string TransformProperties(PropertyTransformerVm vm, BackgroundWorker worker, DoWorkEventArgs _) { var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (string path in vm.ExportPaths) { Editor editor; if (Path.GetExtension(path) == ".osb") { editor = new StoryboardEditor(path); } else { editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader); } if (editor is BeatmapEditor beatmapEditor) { Beatmap beatmap = beatmapEditor.Beatmap; List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>(); foreach (TimingPoint tp in beatmap.BeatmapTiming.TimingPoints) { // Offset if (vm.TimingpointOffsetMultiplier != 1 || vm.TimingpointOffsetOffset != 0) { if (Filter(tp.Offset, tp.Offset, vm)) { tp.Offset = Math.Round(tp.Offset * vm.TimingpointOffsetMultiplier + vm.TimingpointOffsetOffset); } } // BPM if (vm.TimingpointBPMMultiplier != 1 || vm.TimingpointBPMOffset != 0) { if (tp.Uninherited) { if (Filter(tp.GetBpm(), tp.Offset, vm)) { double newBPM = tp.GetBpm() * vm.TimingpointBPMMultiplier + vm.TimingpointBPMOffset; newBPM = vm.ClipProperties ? MathHelper.Clamp(newBPM, 15, 10000) : newBPM; // Clip the value if specified tp.MpB = 60000 / newBPM; } } } // Slider Velocity if (vm.TimingpointSVMultiplier != 1 || vm.TimingpointSVOffset != 0) { if (Filter(beatmap.BeatmapTiming.GetSvMultiplierAtTime(tp.Offset), tp.Offset, vm)) { TimingPoint tpchanger = tp.Copy(); double newSV = beatmap.BeatmapTiming.GetSvMultiplierAtTime(tp.Offset) * vm.TimingpointSVMultiplier + vm.TimingpointSVOffset; newSV = vm.ClipProperties ? MathHelper.Clamp(newSV, 0.1, 10) : newSV; // Clip the value if specified tpchanger.MpB = -100 / newSV; timingPointsChanges.Add(new TimingPointsChange(tpchanger, mpb: true)); } } // Index if (vm.TimingpointIndexMultiplier != 1 || vm.TimingpointIndexOffset != 0) { if (Filter(tp.SampleIndex, tp.Offset, vm)) { int newIndex = (int)Math.Round(tp.SampleIndex * vm.TimingpointIndexMultiplier + vm.TimingpointIndexOffset); tp.SampleIndex = vm.ClipProperties ? MathHelper.Clamp(newIndex, 0, int.MaxValue) : newIndex; } } // Volume if (vm.TimingpointVolumeMultiplier != 1 || vm.TimingpointVolumeOffset != 0) { if (Filter(tp.Volume, tp.Offset, vm)) { int newVolume = (int)Math.Round(tp.Volume * vm.TimingpointVolumeMultiplier + vm.TimingpointVolumeOffset); tp.Volume = vm.ClipProperties ? MathHelper.Clamp(newVolume, 5, 100) : newVolume; } } } UpdateProgressBar(worker, 20); // Hitobject time if (vm.HitObjectTimeMultiplier != 1 || vm.HitObjectTimeOffset != 0) { foreach (HitObject ho in beatmap.HitObjects) { // Get the end time early because the start time gets modified double oldEndTime = ho.GetEndTime(false); if (Filter(ho.Time, ho.Time, vm)) { ho.Time = Math.Round(ho.Time * vm.HitObjectTimeMultiplier + vm.HitObjectTimeOffset); } // Transform end time of hold notes and spinner if ((ho.IsHoldNote || ho.IsSpinner) && Filter(oldEndTime, oldEndTime, vm)) { ho.EndTime = Math.Round(oldEndTime * vm.HitObjectTimeMultiplier + vm.HitObjectTimeOffset); } } } UpdateProgressBar(worker, 30); // Bookmark time if (vm.BookmarkTimeMultiplier != 1 || vm.BookmarkTimeOffset != 0) { List <double> newBookmarks = new List <double>(); List <double> bookmarks = beatmap.GetBookmarks(); foreach (double bookmark in bookmarks) { if (Filter(bookmark, bookmark, vm)) { newBookmarks.Add( Math.Round(bookmark * vm.BookmarkTimeMultiplier + vm.BookmarkTimeOffset)); } else { newBookmarks.Add(bookmark); } } beatmap.SetBookmarks(newBookmarks); } UpdateProgressBar(worker, 40); // Storyboarded event time if (vm.SBEventTimeMultiplier != 1 || vm.SBEventTimeOffset != 0) { foreach (Event ev in beatmap.StoryboardLayerBackground.Concat(beatmap.StoryboardLayerFail) .Concat(beatmap.StoryboardLayerPass).Concat(beatmap.StoryboardLayerForeground) .Concat(beatmap.StoryboardLayerOverlay)) { TransformEventTime(ev, vm.SBEventTimeMultiplier, vm.SBEventTimeOffset, vm); } } UpdateProgressBar(worker, 50); // Storyboarded sample time if (vm.SBSampleTimeMultiplier != 1 || vm.SBSampleTimeOffset != 0) { foreach (StoryboardSoundSample ss in beatmap.StoryboardSoundSamples) { if (Filter(ss.StartTime, ss.StartTime, vm)) { ss.StartTime = (int)Math.Round(ss.StartTime * vm.SBSampleTimeMultiplier + vm.SBSampleTimeOffset); } } } UpdateProgressBar(worker, 60); // Break time if (vm.BreakTimeMultiplier != 1 || vm.BreakTimeOffset != 0) { foreach (Break br in beatmap.BreakPeriods) { if (Filter(br.StartTime, br.StartTime, vm)) { br.StartTime = (int)Math.Round(br.StartTime * vm.BreakTimeMultiplier + vm.BreakTimeOffset); } if (Filter(br.EndTime, br.EndTime, vm)) { br.EndTime = (int)Math.Round(br.EndTime * vm.BreakTimeMultiplier + vm.BreakTimeOffset); } } } UpdateProgressBar(worker, 70); // Video start time if (vm.VideoTimeMultiplier != 1 || vm.VideoTimeOffset != 0) { foreach (Event ev in beatmap.BackgroundAndVideoEvents) { if (ev is Video video) { if (Filter(video.StartTime, video.StartTime, vm)) { video.StartTime = (int)Math.Round(video.StartTime * vm.VideoTimeMultiplier + vm.VideoTimeOffset); } } } } UpdateProgressBar(worker, 80); // Preview point time if (vm.PreviewTimeMultiplier != 1 || vm.PreviewTimeOffset != 0) { if (beatmap.General.ContainsKey("PreviewTime") && beatmap.General["PreviewTime"].IntValue != -1) { var previewTime = beatmap.General["PreviewTime"].DoubleValue; if (Filter(previewTime, previewTime, vm)) { var newPreviewTime = Math.Round(previewTime * vm.PreviewTimeMultiplier + vm.PreviewTimeOffset); beatmap.General["PreviewTime"].SetDouble(newPreviewTime); } } } UpdateProgressBar(worker, 90); TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges); // Save the file beatmapEditor.SaveFile(); UpdateProgressBar(worker, 100); } else if (editor is StoryboardEditor storyboardEditor) { StoryBoard storyboard = storyboardEditor.StoryBoard; // Storyboarded event time if (vm.SBEventTimeMultiplier != 1 || vm.SBEventTimeOffset != 0) { foreach (Event ev in storyboard.StoryboardLayerBackground.Concat(storyboard.StoryboardLayerFail) .Concat(storyboard.StoryboardLayerPass).Concat(storyboard.StoryboardLayerForeground) .Concat(storyboard.StoryboardLayerOverlay)) { TransformEventTime(ev, vm.SBEventTimeMultiplier, vm.SBEventTimeOffset, vm); } } UpdateProgressBar(worker, 50); // Storyboarded sample time if (vm.SBSampleTimeMultiplier != 1 || vm.SBSampleTimeOffset != 0) { foreach (StoryboardSoundSample ss in storyboard.StoryboardSoundSamples) { if (Filter(ss.StartTime, ss.StartTime, vm)) { ss.StartTime = (int)Math.Round(ss.StartTime * vm.SBSampleTimeMultiplier + vm.SBSampleTimeOffset); } } } UpdateProgressBar(worker, 70); // Video start time if (vm.VideoTimeMultiplier != 1 || vm.VideoTimeOffset != 0) { foreach (Event ev in storyboard.BackgroundAndVideoEvents) { if (ev is Video video) { if (Filter(video.StartTime, video.StartTime, vm)) { video.StartTime = (int)Math.Round(video.StartTime * vm.VideoTimeMultiplier + vm.VideoTimeOffset); } } } } UpdateProgressBar(worker, 90); // Save the file storyboardEditor.SaveFile(); UpdateProgressBar(worker, 100); } } return("Done!"); }
private string TransformProperties(PropertyTransformerVm vm, BackgroundWorker worker, DoWorkEventArgs _) { bool doFilterMatch = vm.MatchFilter != -1 && vm.EnableFilters; bool doFilterRange = (vm.MinTimeFilter != -1 || vm.MaxTimeFilter != -1) && vm.EnableFilters; double min = vm.MinTimeFilter == -1 ? double.NegativeInfinity : vm.MinTimeFilter; double max = vm.MaxTimeFilter == -1 ? double.PositiveInfinity : vm.MaxTimeFilter; var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (string path in vm.ExportPaths) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader); Beatmap beatmap = editor.Beatmap; // Count all the total amount of things to loop through int loops = 0; int totalLoops = beatmap.BeatmapTiming.TimingPoints.Count; if (vm.HitObjectTimeMultiplier != 1 || vm.HitObjectTimeOffset != 0) { totalLoops += beatmap.HitObjects.Count; } if (vm.BookmarkTimeMultiplier != 1 || vm.BookmarkTimeOffset != 0) { totalLoops += beatmap.GetBookmarks().Count; } if (vm.SBSampleTimeMultiplier != 1 || vm.SBSampleTimeOffset != 0) { totalLoops += beatmap.StoryboardSoundSamples.Count; } List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>(); foreach (TimingPoint tp in beatmap.BeatmapTiming.TimingPoints) { // Offset if (vm.TimingpointOffsetMultiplier != 1 || vm.TimingpointOffsetOffset != 0) { if (Filter(tp.Offset, tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max)) { tp.Offset = Math.Round(tp.Offset * vm.TimingpointOffsetMultiplier + vm.TimingpointOffsetOffset); } } // BPM if (vm.TimingpointBPMMultiplier != 1 || vm.TimingpointBPMOffset != 0) { if (tp.Uninherited) { if (Filter(tp.GetBpm(), tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max)) { double newBPM = tp.GetBpm() * vm.TimingpointBPMMultiplier + vm.TimingpointBPMOffset; newBPM = vm.ClipProperties ? MathHelper.Clamp(newBPM, 15, 10000) : newBPM; // Clip the value if specified tp.MpB = 60000 / newBPM; } } } // Slider Velocity if (vm.TimingpointSVMultiplier != 1 || vm.TimingpointSVOffset != 0) { if (Filter(beatmap.BeatmapTiming.GetSvMultiplierAtTime(tp.Offset), tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max)) { TimingPoint tpchanger = tp.Copy(); double newSV = beatmap.BeatmapTiming.GetSvMultiplierAtTime(tp.Offset) * vm.TimingpointSVMultiplier + vm.TimingpointSVOffset; newSV = vm.ClipProperties ? MathHelper.Clamp(newSV, 0.1, 10) : newSV; // Clip the value if specified tpchanger.MpB = -100 / newSV; timingPointsChanges.Add(new TimingPointsChange(tpchanger, mpb: true)); } } // Index if (vm.TimingpointIndexMultiplier != 1 || vm.TimingpointIndexOffset != 0) { if (Filter(tp.SampleIndex, tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max)) { int newIndex = (int)Math.Round(tp.SampleIndex * vm.TimingpointIndexMultiplier + vm.TimingpointIndexOffset); tp.SampleIndex = vm.ClipProperties ? MathHelper.Clamp(newIndex, 0, 100) : newIndex; } } // Volume if (vm.TimingpointVolumeMultiplier != 1 || vm.TimingpointVolumeOffset != 0) { if (Filter(tp.Volume, tp.Offset, doFilterMatch, doFilterRange, vm.MatchFilter, min, max)) { int newVolume = (int)Math.Round(tp.Volume * vm.TimingpointVolumeMultiplier + vm.TimingpointVolumeOffset); tp.Volume = vm.ClipProperties ? MathHelper.Clamp(newVolume, 5, 100) : newVolume; } } // Update progress bar loops++; UpdateProgressBar(worker, loops * 100 / totalLoops); } // Hitobject Time if (vm.HitObjectTimeMultiplier != 1 || vm.HitObjectTimeOffset != 0) { foreach (HitObject ho in beatmap.HitObjects) { if (Filter(ho.Time, ho.Time, doFilterMatch, doFilterRange, vm.MatchFilter, min, max)) { ho.Time = Math.Round(ho.Time * vm.HitObjectTimeMultiplier + vm.HitObjectTimeOffset); } // Update progress bar loops++; UpdateProgressBar(worker, loops * 100 / totalLoops); } } // Bookmark Time if (vm.BookmarkTimeMultiplier != 1 || vm.BookmarkTimeOffset != 0) { List <double> newBookmarks = new List <double>(); List <double> bookmarks = beatmap.GetBookmarks(); foreach (double bookmark in bookmarks) { if (Filter(bookmark, bookmark, doFilterMatch, doFilterRange, vm.MatchFilter, min, max)) { newBookmarks.Add(Math.Round(bookmark * vm.BookmarkTimeMultiplier + vm.BookmarkTimeOffset)); } else { newBookmarks.Add(bookmark); } // Update progress bar loops++; UpdateProgressBar(worker, loops * 100 / totalLoops); } beatmap.SetBookmarks(newBookmarks); } // Storyboarded sample Time if (vm.SBSampleTimeMultiplier != 1 || vm.SBSampleTimeOffset != 0) { foreach (StoryboardSoundSample ss in beatmap.StoryboardSoundSamples) { if (Filter(ss.Time, ss.Time, doFilterMatch, doFilterRange, vm.MatchFilter, min, max)) { ss.Time = Math.Round(ss.Time * vm.SBSampleTimeMultiplier + vm.SBSampleTimeOffset); } // Update progress bar loops++; UpdateProgressBar(worker, loops * 100 / totalLoops); } } // Preview point time if (vm.PreviewTimeMultiplier != 1 || vm.PreviewTimeOffset != 0) { if (beatmap.General.ContainsKey("PreviewTime") && beatmap.General["PreviewTime"].IntValue != -1) { var previewTime = beatmap.General["PreviewTime"].DoubleValue; if (Filter(previewTime, previewTime, doFilterMatch, doFilterRange, vm.MatchFilter, min, max)) { var newPreviewTime = Math.Round(previewTime * vm.PreviewTimeMultiplier + vm.PreviewTimeOffset); beatmap.General["PreviewTime"].SetDouble(newPreviewTime); } } } TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges); // Save the file editor.SaveFile(); } return("Done!"); }
private static string Export_ComboColours(ComboColourStudioVm arg, BackgroundWorker worker, DoWorkEventArgs _) { var paths = arg.ExportPath.Split('|'); var mapsDone = 0; var orderedColourPoints = arg.Project.ColourPoints.OrderBy(o => o.Time).ToList(); var orderedComboColours = arg.Project.ComboColours.OrderBy(o => o.Name).ToList(); var reader = EditorReaderStuff.GetFullEditorReaderOrNot(); foreach (var path in paths) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader); var beatmap = editor.Beatmap; // Setting the combo colours beatmap.ComboColours = new List <ComboColour>(arg.Project.ComboColours); // Setting the combo skips if (beatmap.HitObjects.Count > 0 && orderedColourPoints.Count > 0) { int lastColourPointColourIndex = -1; var lastColourPoint = orderedColourPoints[0]; int lastColourIndex = 0; var exceptions = new List <ColourPoint>(); foreach (var newCombo in beatmap.HitObjects.Where(o => o.ActualNewCombo && !o.IsSpinner)) { int comboLength = GetComboLength(newCombo, beatmap.HitObjects); //Console.WriteLine(comboLength); // Get the colour point for this new combo var colourPoint = GetColourPoint(orderedColourPoints, newCombo.Time, exceptions, comboLength <= arg.Project.MaxBurstLength); var colourSequence = colourPoint.ColourSequence.ToList(); // Add the colour point to the exceptions so it doesnt get used again if (colourPoint.Mode == ColourPointMode.Burst) { exceptions.Add(colourPoint); } // Get the last colour index on the sequence of this colour point lastColourPointColourIndex = lastColourPointColourIndex == -1 || lastColourPoint.Equals(colourPoint) ? lastColourPointColourIndex : colourSequence.FindIndex(o => o.Name == orderedComboColours[lastColourIndex].Name); // Get the next colour index on this colour point // Check if colourSequence count is 0 to prevent division by 0 var colourPointColourIndex = lastColourPointColourIndex == -1 || colourSequence.Count == 0 ? 0 : lastColourPoint.Equals(colourPoint) ? MathHelper.Mod(lastColourPointColourIndex + 1, colourSequence.Count) : // If the colour point changed try going back to index 0 lastColourPointColourIndex == 0 && colourSequence.Count > 1 ? 1 : 0; //Console.WriteLine("colourPointColourIndex: " + colourPointColourIndex); //Console.WriteLine("colourPointColour: " + colourPoint.ColourSequence[colourPointColourIndex].Name); // Find the combo index of the chosen colour in the sequence // Check if the colourSequence count is 0 to prevent an out-of-range exception var colourIndex = colourSequence.Count == 0 ? MathHelper.Mod(lastColourIndex + 1, orderedComboColours.Count) : orderedComboColours.FindIndex(o => o.Name == colourSequence[colourPointColourIndex].Name); if (colourIndex == -1) { throw new ArgumentException($"Can not use colour {colourSequence[colourPointColourIndex].Name} of colour point at offset {colourPoint.Time} because it does not exist in the combo colours."); } //Console.WriteLine("colourIndex: " + colourIndex); var comboIncrease = MathHelper.Mod(colourIndex - lastColourIndex, arg.Project.ComboColours.Count); // Do -1 combo skip since it always does +1 combo colour for each new combo which is not on a spinner newCombo.ComboSkip = MathHelper.Mod(comboIncrease - 1, arg.Project.ComboColours.Count); // Set new combo to true for the case this is the first object and new combo is false if (!newCombo.NewCombo && newCombo.ComboSkip != 0) { newCombo.NewCombo = true; } //Console.WriteLine("comboSkip: " + newCombo.ComboSkip); lastColourPointColourIndex = colourPointColourIndex; lastColourPoint = colourPoint; lastColourIndex = colourIndex; } } // Save the file editor.SaveFile(); // Update progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(++mapsDone * 100 / paths.Length); } } // Make an accurate message var message = $"Successfully exported colours to {mapsDone} {(mapsDone == 1 ? "beatmap" : "beatmaps")}!"; return(message); }