public HitsoundPreviewHelperVm() { _items = new ObservableCollection <HitsoundZone>(); RhythmGuideCommand = new CommandImplementation( _ => { try { if (_rhythmGuideWindow == null) { _rhythmGuideWindow = new RhythmGuideWindow(); _rhythmGuideWindow.Closed += RhythmGuideWindowOnClosed; _rhythmGuideWindow.Show(); } else { _rhythmGuideWindow.Focus(); } } catch (Exception ex) { ex.Show(); } }); AddCommand = new CommandImplementation( _ => { try { var newZone = new HitsoundZone(); if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) { var editor = EditorReaderStuff.GetBeatmapEditor(EditorReaderStuff.GetFullEditorReader(), out var selected); if (selected.Count > 0) { newZone.XPos = selected[0].Pos.X; newZone.YPos = editor.Beatmap.General["Mode"].IntValue == 3 ? -1 : selected[0].Pos.Y; } else { MessageBox.Show("Please select a hit object to fetch the coordinates."); } } Items.Add(newZone); } catch (Exception ex) { ex.Show(); } }); CopyCommand = new CommandImplementation( _ => { try { int initialCount = Items.Count; for (int i = 0; i < initialCount; i++) { if (Items[i].IsSelected) { Items.Add(Items[i].Copy()); } } } catch (Exception ex) { ex.Show(); } }); RemoveCommand = new CommandImplementation( _ => { try { Items.RemoveAll(o => o.IsSelected); } catch (Exception ex) { ex.Show(); } }); }
private async void CoolSave(object sender, RoutedEventArgs e) { try { await Task.Run(() => EditorReaderStuff.BetterSave()); } catch (Exception ex) { ex.Show(); } }
private void CoolSave(object sender, RoutedEventArgs e) { try { EditorReaderStuff.BetterSave(); } catch (Exception ex) { ex.Show(); } }
private static void QuickBetterSave() { try { EditorReaderStuff.BetterSave(); } catch (Exception e) { e.Show(); } }
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!"); }
public static List <HitsoundLayer> ImportStoryboard(string path, bool volumes, bool removeDuplicates) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path); Beatmap beatmap = editor.Beatmap; string mapDir = editor.GetParentFolder(); var hitsoundLayers = ImportStoryboard(path, volumes, removeDuplicates, beatmap, mapDir); return(hitsoundLayers); }
/// <summary> /// Generates a temp file with the newest version of specified map and returns the path to that temp file. /// </summary> /// <param name="mapPath"></param> /// <returns></returns> private static string GetNewestVersionPath(string mapPath) { var editor = EditorReaderStuff.GetNewestVersionOrNot(mapPath); // Save temp version var tempPath = Path.Combine(MainWindow.AppDataPath, "temp.osu"); Editor.SaveFile(tempPath, editor.Beatmap.GetLines()); return(tempPath); }
public static void ExportHitsounds(IEnumerable <HitsoundEvent> hitsounds, string baseBeatmap, string exportFolder, bool useGreenlines = true, bool useStoryboard = false) { EditorReaderStuff.TryGetNewestVersion(baseBeatmap, out var editor); Beatmap beatmap = editor.Beatmap; if (useStoryboard) { beatmap.StoryboardSoundSamples = hitsounds.Select(h => new StoryboardSoundSample(h.Time, 0, h.Filename, h.Volume)) .ToList(); } else { // Make new timing points // Add red lines List <TimingPoint> timingPoints = beatmap.BeatmapTiming.GetAllRedlines(); List <TimingPointsChange> timingPointsChanges = timingPoints.Select(tp => new TimingPointsChange(tp, mpb: true, meter: true, inherited: true, omitFirstBarLine: true)) .ToList(); // Add hitsound stuff // Replace all hitobjects with the hitsounds beatmap.HitObjects.Clear(); foreach (HitsoundEvent h in hitsounds) { if (useGreenlines) { TimingPoint tp = beatmap.BeatmapTiming.GetTimingPointAtTime(h.Time + 5).Copy(); tp.Offset = h.Time; tp.SampleIndex = h.CustomIndex; h.CustomIndex = 0; // Set it to default value because it gets handled by greenlines now tp.Volume = Math.Round(tp.Volume * h.Volume); h.Volume = 0; // Set it to default value because it gets handled by greenlines now timingPointsChanges.Add(new TimingPointsChange(tp, index: true, volume: true)); } beatmap.HitObjects.Add(new HitObject(h.Pos, h.Time, 5, h.GetHitsounds(), h.SampleSet, h.Additions, h.CustomIndex, h.Volume * 100, h.Filename)); } // Replace the old timingpoints beatmap.BeatmapTiming.TimingPoints.Clear(); TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges); } // Change version to hitsounds beatmap.General["StackLeniency"] = new TValue("0.0"); beatmap.General["Mode"] = new TValue("0"); beatmap.Metadata["Version"] = new TValue("Hitsounds"); beatmap.Difficulty["CircleSize"] = new TValue("4"); // Save the file to the export folder editor.SaveFile(Path.Combine(exportFolder, beatmap.GetFileName())); }
public ComboColourProject() { ColourPoints = new ObservableCollection <ColourPoint>(); ComboColours = new ObservableCollection <SpecialColour>(); MaxBurstLength = 1; AddColourPointCommand = new CommandImplementation(_ => { double time = ColourPoints.Count > 1 ? ColourPoints.Count(o => o.IsSelected) > 0 ? ColourPoints.Where(o => o.IsSelected).Max(o => o.Time) : ColourPoints.Last().Time : 0; if (Keyboard.IsKeyDown(Key.LeftShift) || Keyboard.IsKeyDown(Key.RightShift)) { try { time = EditorReaderStuff.GetEditorTime(); } catch (Exception ex) { ex.Show(); } } ColourPoints.Add(GenerateNewColourPoint(time)); }); RemoveColourPointCommand = new CommandImplementation(_ => { if (ColourPoints.Any(o => o.IsSelected)) { ColourPoints.RemoveAll(o => o.IsSelected); return; } if (ColourPoints.Count > 0) { ColourPoints.RemoveAt(ColourPoints.Count - 1); } }); AddComboCommand = new CommandImplementation(_ => { if (ComboColours.Count >= 8) { return; } ComboColours.Add(ComboColours.Count > 0 ? new SpecialColour(ComboColours[ComboColours.Count - 1].Color, $"Combo{ComboColours.Count + 1}") : new SpecialColour(Colors.White, $"Combo{ComboColours.Count + 1}")); }); RemoveComboCommand = new CommandImplementation(_ => { if (ComboColours.Count > 0) { ComboColours.RemoveAt(ComboColours.Count - 1); } }); }
private static void GenerateBetterSaveMD5(List <string> lines) { var tempPath = System.IO.Path.Combine(MainWindow.AppDataPath, "temp.osu"); if (!File.Exists(tempPath)) { File.Create(tempPath).Dispose(); } File.WriteAllLines(tempPath, lines); EditorReaderStuff.DontCoolSaveWhenMD5EqualsThisString = EditorReaderStuff.GetMD5FromPath(tempPath); }
private string Run_Program(Arguments args, BackgroundWorker worker, DoWorkEventArgs _) { var result = new MapCleaner.MapCleanerResult(); bool editorRead = EditorReaderStuff.TryGetFullEditorReader(out var reader); if (args.Paths.Length == 1) { var editor = EditorReaderStuff.GetBeatmapEditor(args.Paths[0], reader, editorRead); List<TimingPoint> orgininalTimingPoints = new List<TimingPoint>(); foreach (TimingPoint tp in editor.Beatmap.BeatmapTiming.TimingPoints) { orgininalTimingPoints.Add(tp.Copy()); } int oldTimingPointsCount = editor.Beatmap.BeatmapTiming.TimingPoints.Count; result.Add(MapCleaner.CleanMap(editor, args.CleanerArguments, 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.GetBeatmapEditor(path, reader, editorRead); int oldTimingPointsCount = editor.Beatmap.BeatmapTiming.TimingPoints.Count; result.Add(MapCleaner.CleanMap(editor, args.CleanerArguments, 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, editorRead)); // Make an accurate message string message = $"Successfully {(result.TimingPointsRemoved < 0 ? "added" : "removed")} {Math.Abs(result.TimingPointsRemoved)} {(Math.Abs(result.TimingPointsRemoved) == 1 ? "greenline" : "greenlines")}" + (args.CleanerArguments.ResnapObjects ? $" and resnapped {result.ObjectsResnapped} {(result.ObjectsResnapped == 1 ? "object" : "objects")}" : "") + (args.CleanerArguments.RemoveUnusedSamples ? $" and removed {result.SamplesRemoved} unused {(result.SamplesRemoved == 1 ? "sample" : "samples")}" : "") + "!"; return args.Quick ? "" : message; }
public override void SaveFile() { var tempPath = System.IO.Path.Combine(MainWindow.AppDataPath, "temp.osu"); if (!File.Exists(tempPath)) { File.Create(tempPath).Dispose(); } File.WriteAllLines(tempPath, TextFile.GetLines()); EditorReaderStuff.DontCoolSaveWhenMD5EqualsThisString = EditorReaderStuff.GetMD5FromPath(tempPath); base.SaveFile(); }
public static string GetCurrentBeatmap() { string path; try { string songs = SettingsManager.GetSongsPath(); if (string.IsNullOrEmpty(songs)) { throw new Exception( @"Can't fetch current in-game beatmap, because there is no Songs path specified in Preferences."); } string folder = PioReader.GetMapFolderName(); string filename = PioReader.GetOsuFileName(); if (string.IsNullOrEmpty(folder)) { throw new Exception(@"Can't fetch the folder name of the current in-game beatmap."); } if (string.IsNullOrEmpty(filename)) { throw new Exception(@"Can't fetch the file name of the current in-game beatmap."); } path = Path.Combine(songs, folder, filename); } catch (Exception ex) { Console.WriteLine(ex.Message); Console.WriteLine(ex.StackTrace); try { var reader = EditorReaderStuff.GetEditorReader(); reader.FetchEditor(); reader.SetHOM(); reader.ReadHOM(); reader.FetchBeatmap(); path = EditorReaderStuff.GetCurrentBeatmap(reader); } catch (Exception ex2) { Console.WriteLine(ex2.Message); Console.WriteLine(ex2.StackTrace); throw ex; } } return(path); }
private void PeriodicBackupTimerOnTick(object sender, EventArgs e) { try { // Get the newest beatmap, save a temp version, get the hash and compare it to the previous hash, backup temp file var path = IOHelper.GetCurrentBeatmap(); if (string.IsNullOrEmpty(path)) { return; } // Don't make period backup if the editor is not open if (!EditorReaderStuff.IsEditorOpen()) { return; } EditorReader reader = null; try { reader = EditorReaderStuff.GetFullEditorReader(); } catch (Exception ex) { Console.WriteLine(ex.MessageStackTrace()); } var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader); // Save temp version var tempPath = Path.Combine(MainWindow.AppDataPath, "temp.osu"); Editor.SaveFile(tempPath, editor.Beatmap.GetLines()); // Get MD5 from temp file var currentMapHash = EditorReaderStuff.GetMD5FromPath(tempPath); // Comparing with previously made periodic backup if (currentMapHash == previousPeriodicBackupHash) { return; } // Saving backup of the map BackupManager.SaveMapBackup(tempPath, true, Path.GetFileName(path), "PB"); // PB stands for Periodic Backup previousPeriodicBackupHash = currentMapHash; } catch (Exception ex) { Console.WriteLine(ex.Message); } }
public static List <double> TimesFromStack(string path, double x, double y) { List <double> times = new List <double>(); var editor = EditorReaderStuff.GetNewestVersionOrNot(path); bool xIgnore = x == -1; bool yIgnore = y == -1; foreach (HitObject ho in editor.Beatmap.HitObjects) { if ((Math.Abs(ho.Pos.X - x) < 3 || xIgnore) && (Math.Abs(ho.Pos.Y - y) < 3 || yIgnore)) { times.Add(ho.Time); } } return(times); }
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 static void OnChangedFsWatcher(object sender, FileSystemEventArgs e) { try { var currentPath = IOHelper.GetCurrentBeatmap(); if (e.FullPath != currentPath) { return; } var proc = System.Diagnostics.Process.GetProcessesByName("osu!").FirstOrDefault(); if (proc != null) { var oldHandle = GetForegroundWindow(); if (oldHandle != proc.MainWindowHandle) { return; } } string hashString = ""; try { if (File.Exists(currentPath)) { hashString = EditorReaderStuff.GetMD5FromPath(currentPath); } } catch { return; } if (EditorReaderStuff.DontCoolSaveWhenMD5EqualsThisString == hashString) { return; } EditorReaderStuff.BetterSave(); } catch (Exception ex) { MessageBox.Show("Failed to overwrite osu! save with BetterSave."); ex.Show(); } }
public static void ExportHitsounds(List<HitsoundEvent> hitsounds, string baseBeatmap, string exportFolder) { EditorReaderStuff.TryGetNewestVersion(baseBeatmap, out var editor); Beatmap beatmap = editor.Beatmap; // Make new timing points List<TimingPointsChange> timingPointsChanges = new List<TimingPointsChange>(); // Add red lines List<TimingPoint> timingPoints = beatmap.BeatmapTiming.GetAllRedlines(); foreach (TimingPoint tp in timingPoints) { timingPointsChanges.Add(new TimingPointsChange(tp, mpb: true, meter: true, inherited: true, omitFirstBarLine: true)); } // Add hitsound stuff foreach (HitsoundEvent h in hitsounds) { TimingPoint tp = beatmap.BeatmapTiming.GetTimingPointAtTime(h.Time + 5).Copy(); tp.Offset = h.Time; tp.SampleIndex = h.CustomIndex; tp.Volume = Math.Round(tp.Volume * h.Volume); timingPointsChanges.Add(new TimingPointsChange(tp, index: true, volume: true)); } // Replace the old timingpoints beatmap.BeatmapTiming.TimingPoints.Clear(); TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges); // Replace all hitobjects with the hitsounds beatmap.HitObjects.Clear(); foreach (HitsoundEvent h in hitsounds) { beatmap.HitObjects.Add(new HitObject(h.Time, h.GetHitsounds(), h.SampleSet, h.Additions)); } // Change version to hitsounds beatmap.General["StackLeniency"] = new TValue("0.0"); beatmap.General["Mode"] = new TValue("0"); beatmap.Metadata["Version"] = new TValue("Hitsounds"); beatmap.Difficulty["CircleSize"] = new TValue("4"); // Save the file to the export folder editor.SaveFile(Path.Combine(exportFolder, beatmap.GetFileName())); }
private void PeriodicBackupTimerOnTick(object sender, EventArgs e) { try { // Get the newest beatmap, save a temp version, get the hash and compare it to the previous hash, backup temp file var path = IOHelper.GetCurrentBeatmap(); if (string.IsNullOrEmpty(path)) { return; } EditorReaderStuff.TryGetNewestVersion(path, out var editor); // Save temp version var tempPath = Path.Combine(MainWindow.AppDataPath, "temp.osu"); if (!File.Exists(tempPath)) { File.Create(tempPath).Dispose(); } File.WriteAllLines(tempPath, editor.Beatmap.GetLines()); // Get MD5 from temp file var currentMapHash = EditorReaderStuff.GetMD5FromPath(tempPath); // Comparing with previously made periodic backup if (currentMapHash == previousPeriodicBackupHash) { return; } // Saving backup of the map IOHelper.SaveMapBackup(tempPath, true, Path.GetFileName(path)); previousPeriodicBackupHash = currentMapHash; } catch (Exception ex) { Console.WriteLine(ex.Message); } }
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); }
/// <summary> /// Extract every used sample in a beatmap and return them as hitsound layers. /// </summary> /// <param name="path">The path to the beatmap.</param> /// <param name="volumes">Taking the volumes from the map and making different layers for different volumes.</param> /// <param name="detectDuplicateSamples">Detect duplicate samples and optimise hitsound layer count with that.</param> /// <param name="removeDuplicates">Removes duplicate sounds at the same millisecond.</param> /// <param name="includeStoryboard">Also imports storyboarded samples.</param> /// <returns>The hitsound layers</returns> public static List <HitsoundLayer> ImportHitsounds(string path, bool volumes, bool detectDuplicateSamples, bool removeDuplicates, bool includeStoryboard) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path); Beatmap beatmap = editor.Beatmap; Timeline timeline = beatmap.GetTimeline(); GameMode mode = (GameMode)beatmap.General["Mode"].IntValue; string mapDir = editor.GetParentFolder(); Dictionary <string, string> firstSamples = AnalyzeSamples(mapDir, false, detectDuplicateSamples); List <HitsoundLayer> hitsoundLayers = new List <HitsoundLayer>(); foreach (TimelineObject tlo in timeline.TimelineObjects) { if (!tlo.HasHitsound) { continue; } double volume = volumes ? tlo.FenoSampleVolume / 100 : 1; List <string> samples = tlo.GetPlayingFilenames(mode); foreach (string filename in samples) { bool isFilename = tlo.UsesFilename; SampleSet sampleSet = isFilename ? tlo.FenoSampleSet : GetSamplesetFromFilename(filename); Hitsound hitsound = isFilename ? tlo.GetHitsound() : GetHitsoundFromFilename(filename); string samplePath = Path.Combine(mapDir, filename); string fullPathExtLess = Path.Combine( Path.GetDirectoryName(samplePath) ?? throw new InvalidOperationException(), Path.GetFileNameWithoutExtension(samplePath)); // Get the first occurence of this sound to not get duplicated if (firstSamples.Keys.Contains(fullPathExtLess)) { samplePath = firstSamples[fullPathExtLess]; } else { // Sample doesn't exist if (!isFilename) { samplePath = Path.Combine( Path.GetDirectoryName(samplePath) ?? throw new InvalidOperationException(), $"{sampleSet.ToString().ToLower()}-hit{hitsound.ToString().ToLower()}-1.wav"); } } string extLessFilename = Path.GetFileNameWithoutExtension(samplePath); var importArgs = new LayerImportArgs(ImportType.Hitsounds) { Path = path, SamplePath = samplePath, Volume = volume, DetectDuplicateSamples = detectDuplicateSamples, DiscriminateVolumes = volumes, RemoveDuplicates = removeDuplicates }; // Find the hitsoundlayer with this path HitsoundLayer layer = hitsoundLayers.Find(o => o.ImportArgs == importArgs); if (layer != null) { // Find hitsound layer with this path and add this time layer.Times.Add(tlo.Time); } else { // Add new hitsound layer with this path HitsoundLayer newLayer = new HitsoundLayer(extLessFilename, sampleSet, hitsound, new SampleGeneratingArgs(samplePath) { Volume = volume }, importArgs); newLayer.Times.Add(tlo.Time); hitsoundLayers.Add(newLayer); } } } if (includeStoryboard) { hitsoundLayers.AddRange(ImportStoryboard(path, volumes, removeDuplicates, beatmap, mapDir, "SB: ")); } // Sort layers by name hitsoundLayers = hitsoundLayers.OrderBy(o => o.Name).ToList(); if (removeDuplicates) { foreach (var hitsoundLayer in hitsoundLayers) { hitsoundLayer.Times.Sort(); hitsoundLayer.RemoveDuplicates(); } } return(hitsoundLayers); }
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 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."); }
public static void ExportHitsounds(List <HitsoundEvent> hitsounds, string baseBeatmap, string exportFolder, string exportMapName, GameMode exportGameMode, bool useGreenlines, bool useStoryboard) { var editor = EditorReaderStuff.GetNewestVersionOrNot(baseBeatmap); Beatmap beatmap = editor.Beatmap; if (useStoryboard) { beatmap.StoryboardSoundSamples.Clear(); foreach (var h in hitsounds.Where(h => !string.IsNullOrEmpty(h.Filename))) { beatmap.StoryboardSoundSamples.Add(new StoryboardSoundSample(h.Time, 0, h.Filename, h.Volume * 100)); } } else { // Make new timing points // Add red lines List <TimingPoint> timingPoints = beatmap.BeatmapTiming.GetAllRedlines(); List <TimingPointsChange> timingPointsChanges = timingPoints.Select(tp => new TimingPointsChange(tp, mpb: true, meter: true, inherited: true, omitFirstBarLine: true)) .ToList(); // Add hitsound stuff // Replace all hitobjects with the hitsounds beatmap.HitObjects.Clear(); foreach (HitsoundEvent h in hitsounds) { if (useGreenlines) { TimingPoint tp = beatmap.BeatmapTiming.GetTimingPointAtTime(h.Time + 5).Copy(); tp.Offset = h.Time; tp.SampleIndex = h.CustomIndex; h.CustomIndex = 0; // Set it to default value because it gets handled by greenlines now tp.Volume = Math.Round(tp.Volume * h.Volume); h.Volume = 0; // Set it to default value because it gets handled by greenlines now timingPointsChanges.Add(new TimingPointsChange(tp, index: true, volume: true)); } beatmap.HitObjects.Add(new HitObject(h.Pos, h.Time, 5, h.GetHitsounds(), h.SampleSet, h.Additions, h.CustomIndex, h.Volume * 100, h.Filename)); } // Replace the old timingpoints beatmap.BeatmapTiming.TimingPoints.Clear(); TimingPointsChange.ApplyChanges(beatmap.BeatmapTiming, timingPointsChanges); } // Change version to hitsounds beatmap.General["StackLeniency"] = new TValue("0.0"); beatmap.General["Mode"] = new TValue(((int)exportGameMode).ToInvariant()); beatmap.Metadata["Version"] = new TValue(exportMapName); if (exportGameMode == GameMode.Mania) { // Count the number of distinct X positions int numXPositions = new HashSet <double>(hitsounds.Select(h => h.Pos.X)).Count; int numKeys = MathHelper.Clamp(numXPositions, 1, 18); beatmap.Difficulty["CircleSize"] = new TValue(numKeys.ToInvariant()); } else { beatmap.Difficulty["CircleSize"] = new TValue("4"); } // Save the file to the export folder editor.SaveFile(Path.Combine(exportFolder, beatmap.GetFileName())); }
private string Merge_Sliders(Arguments arg, BackgroundWorker worker) { var slidersMerged = 0; var editorRead = EditorReaderStuff.TryGetFullEditorReader(out var reader); foreach (var path in arg.Paths) { var editor = EditorReaderStuff.GetBeatmapEditor(path, reader, editorRead, out var selected, out var editorActuallyRead); if (arg.SelectionMode == 0 && !editorActuallyRead) { return(EditorReaderStuff.SelectedObjectsReadFailText); } var beatmap = editor.Beatmap; var markedObjects = arg.SelectionMode == 0 ? selected : arg.SelectionMode == 1 ? beatmap.GetBookmarkedObjects() : beatmap.HitObjects; var mergeLast = false; for (var i = 0; i < markedObjects.Count - 1; i++) { var ho1 = markedObjects[i]; var ho2 = markedObjects[i + 1]; if (ho1.IsSlider && ho2.IsSlider && (ho1.CurvePoints.Last() - ho2.Pos).Length <= arg.Leniency) { var sp1 = BezierConverter.ConvertToBezier(ho1.SliderPath).ControlPoints; var sp2 = BezierConverter.ConvertToBezier(ho2.SliderPath).ControlPoints; double extraLength = 0; switch (arg.ConnectionMode) { case ConnectionMode.Move: Move(sp2, sp1.Last() - sp2.First()); break; case ConnectionMode.Linear: sp1.Add(sp1.Last()); sp1.Add(sp2.First()); extraLength = (ho1.CurvePoints.Last() - ho2.Pos).Length; break; } var mergedAnchors = sp1.Concat(sp2).ToList(); mergedAnchors.Round(); var linearLinear = arg.LinearOnLinear && IsLinearBezier(sp1) && IsLinearBezier(sp2); if (linearLinear) { for (var j = 0; j < mergedAnchors.Count - 1; j++) { if (mergedAnchors[j] != mergedAnchors[j + 1]) { continue; } mergedAnchors.RemoveAt(j); j--; } } var mergedPath = new SliderPath(linearLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(), ho1.PixelLength + ho2.PixelLength + extraLength); ho1.SliderPath = mergedPath; beatmap.HitObjects.Remove(ho2); markedObjects.Remove(ho2); i--; slidersMerged++; if (!mergeLast) { slidersMerged++; } mergeLast = true; } else if (ho1.IsSlider && ho2.IsCircle && (ho1.CurvePoints.Last() - ho2.Pos).Length <= arg.Leniency) { var sp1 = BezierConverter.ConvertToBezier(ho1.SliderPath).ControlPoints; sp1.Add(sp1.Last()); sp1.Add(ho2.Pos); var extraLength = (ho1.CurvePoints.Last() - ho2.Pos).Length; var mergedAnchors = sp1; mergedAnchors.Round(); var linearLinear = arg.LinearOnLinear && IsLinearBezier(sp1); if (linearLinear) { for (var j = 0; j < mergedAnchors.Count - 1; j++) { if (mergedAnchors[j] != mergedAnchors[j + 1]) { continue; } mergedAnchors.RemoveAt(j); j--; } } var mergedPath = new SliderPath(linearLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(), ho1.PixelLength + extraLength); ho1.SliderPath = mergedPath; beatmap.HitObjects.Remove(ho2); markedObjects.Remove(ho2); i--; slidersMerged++; if (!mergeLast) { slidersMerged++; } mergeLast = true; } else if (ho1.IsCircle && ho2.IsSlider && (ho1.Pos - ho2.Pos).Length <= arg.Leniency) { var sp2 = BezierConverter.ConvertToBezier(ho2.SliderPath).ControlPoints; sp2.Insert(0, sp2.First()); sp2.Insert(0, ho1.Pos); var extraLength = (ho1.Pos - ho2.Pos).Length; var mergedAnchors = sp2; mergedAnchors.Round(); var linearLinear = arg.LinearOnLinear && IsLinearBezier(sp2); if (linearLinear) { for (var j = 0; j < mergedAnchors.Count - 1; j++) { if (mergedAnchors[j] != mergedAnchors[j + 1]) { continue; } mergedAnchors.RemoveAt(j); j--; } } var mergedPath = new SliderPath(linearLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(), ho2.PixelLength + extraLength); ho2.SliderPath = mergedPath; beatmap.HitObjects.Remove(ho1); markedObjects.Remove(ho1); i--; slidersMerged++; if (!mergeLast) { slidersMerged++; } mergeLast = true; } else if (ho1.IsCircle && ho2.IsCircle && (ho1.Pos - ho2.Pos).Length <= arg.Leniency) { var mergedAnchors = new List <Vector2> { ho1.Pos, ho2.Pos }; var mergedPath = new SliderPath(arg.LinearOnLinear ? PathType.Linear : PathType.Bezier, mergedAnchors.ToArray(), (ho1.Pos - ho2.Pos).Length); ho1.SliderPath = mergedPath; ho1.IsCircle = false; ho1.IsSlider = true; ho1.Repeat = 1; ho1.EdgeHitsounds = new List <int> { ho1.GetHitsounds(), ho2.GetHitsounds() }; ho1.EdgeSampleSets = new List <SampleSet> { ho1.SampleSet, ho2.SampleSet }; ho1.EdgeAdditionSets = new List <SampleSet> { ho1.AdditionSet, ho2.AdditionSet }; beatmap.HitObjects.Remove(ho2); markedObjects.Remove(ho2); i--; slidersMerged++; if (!mergeLast) { slidersMerged++; } mergeLast = true; } else { mergeLast = false; } if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i / markedObjects.Count); } } // Save the file editor.SaveFile(); } // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do stuff if (arg.Quick) { RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, editorRead)); } // Make an accurate message var message = ""; if (Math.Abs(slidersMerged) == 1) { message += "Successfully merged " + slidersMerged + " slider!"; } else { message += "Successfully merged " + slidersMerged + " sliders!"; } return(arg.Quick ? "" : message); }
private string Complete_Sliders(SliderCompletionatorVm arg, BackgroundWorker worker, DoWorkEventArgs _) { int slidersCompleted = 0; var reader = EditorReaderStuff.GetFullEditorReaderOrNot(out var editorReaderException1); if (arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Selected && editorReaderException1 != null) { throw new Exception("Could not fetch selected hit objects.", editorReaderException1); } foreach (string path in arg.Paths) { var editor = EditorReaderStuff.GetNewestVersionOrNot(path, reader, out var selected, out var editorReaderException2); if (arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Selected && editorReaderException2 != null) { throw new Exception("Could not fetch selected hit objects.", editorReaderException2); } Beatmap beatmap = editor.Beatmap; Timing timing = beatmap.BeatmapTiming; List <HitObject> markedObjects = arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Selected ? selected : arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Bookmarked ? beatmap.GetBookmarkedObjects() : arg.ImportModeSetting == SliderCompletionatorVm.ImportMode.Time ? beatmap.QueryTimeCode(arg.TimeCode).ToList() : beatmap.HitObjects; for (int i = 0; i < markedObjects.Count; i++) { HitObject ho = markedObjects[i]; if (ho.IsSlider) { double oldSpatialLength = ho.PixelLength; double newSpatialLength = arg.SpatialLength != -1 ? ho.GetSliderPath(fullLength: true).Distance *arg.SpatialLength : oldSpatialLength; double oldTemporalLength = timing.CalculateSliderTemporalLength(ho.Time, ho.PixelLength); double newTemporalLength = arg.TemporalLength != -1 ? timing.GetMpBAtTime(ho.Time) * arg.TemporalLength : oldTemporalLength; double oldSv = timing.GetSvAtTime(ho.Time); double newSv = oldSv / ((newSpatialLength / oldSpatialLength) / (newTemporalLength / oldTemporalLength)); if (double.IsNaN(newSv)) { throw new Exception("Encountered NaN slider velocity. Make sure none of the inputs are zero."); } ho.SliderVelocity = newSv; ho.PixelLength = newSpatialLength; // Scale anchors to completion if (arg.MoveAnchors) { ho.SetAllCurvePoints(SliderPathUtil.MoveAnchorsToLength( ho.GetAllCurvePoints(), ho.SliderType, ho.PixelLength, out var pathType)); ho.SliderType = pathType; } slidersCompleted++; } if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(i / markedObjects.Count); } } // Reconstruct SliderVelocity List <TimingPointsChange> timingPointsChanges = new List <TimingPointsChange>(); // Add Hitobject stuff foreach (HitObject ho in beatmap.HitObjects) { if (ho.IsSlider) // SliderVelocity changes { TimingPoint tp = ho.TimingPoint.Copy(); tp.Offset = ho.Time; tp.MpB = ho.SliderVelocity; timingPointsChanges.Add(new TimingPointsChange(tp, mpb: true)); } } // Add the new SliderVelocity changes TimingPointsChange.ApplyChanges(timing, timingPointsChanges); // Save the file editor.SaveFile(); } // Complete progressbar if (worker != null && worker.WorkerReportsProgress) { worker.ReportProgress(100); } // Do stuff if (arg.Quick) { RunFinished?.Invoke(this, new RunToolCompletedEventArgs(true, reader != null)); } // Make an accurate message string message = ""; if (Math.Abs(slidersCompleted) == 1) { message += "Successfully completed " + slidersCompleted + " slider!"; } else { message += "Successfully completed " + slidersCompleted + " sliders!"; } return(arg.Quick ? "" : message); }
private static void QuickBetterSave() { EditorReaderStuff.BetterSave(); }
private static void SmartQuickRun() { try { if (!SettingsManager.Settings.SmartQuickRunEnabled) { QuickRunCurrentTool(); return; } var reader = EditorReaderStuff.GetFullEditorReader(); var so = reader.hitObjects.Count(o => o.IsSelected); IQuickRun tool = null; if (System.Windows.Application.Current.Dispatcher == null) { return; } System.Windows.Application.Current.Dispatcher.Invoke(() => { if (so == 0) { if (SettingsManager.Settings.NoneQuickRunTool == "<Current Tool>") { QuickRunCurrentTool(); return; } if (!(MainWindow.AppWindow.Views.GetView(SettingsManager.Settings.NoneQuickRunTool) is IQuickRun noneTool)) { return; } tool = noneTool; } else if (so == 1) { if (SettingsManager.Settings.SingleQuickRunTool == "<Current Tool>") { QuickRunCurrentTool(); return; } if (!(MainWindow.AppWindow.Views.GetView(SettingsManager.Settings.SingleQuickRunTool) is IQuickRun singleTool)) { return; } tool = singleTool; } else if (so > 1) { if (SettingsManager.Settings.MultipleQuickRunTool == "<Current Tool>") { QuickRunCurrentTool(); return; } if (!(MainWindow.AppWindow.Views.GetView(SettingsManager.Settings.MultipleQuickRunTool) is IQuickRun multiTool)) { return; } tool = multiTool; } if (tool == null) { return; } tool.RunFinished -= Reload; tool.RunFinished += Reload; tool.QuickRun(); }); } catch (Exception ex) { ex.Show(); } }