/// <summary> /// Converts the input of a specific row from the data grid into a Qua file. /// </summary> /// <param name="row"></param> /// <param name="parseMap"></param> /// <returns></returns> private MapData ParseMapsRowData(int row, bool parseMap = false) { var file = parseMap ? AddRoot(GetMapFilePath(row)) : GetMapFilePath(row); var difficulty = float.Parse(DataGrid.Rows[row].Cells[2].Value.ToString()); var weight = float.Parse(DataGrid.Rows[row].Cells[3].Value.ToString()); Qua map = null; if (parseMap) { try { if (file.EndsWith(".qua")) { map = Qua.Parse(file); } else if (file.EndsWith(".osu")) { map = new OsuBeatmap(file).ToQua(); } PrintToOutput($"Map successfully parsed: {file}"); } catch (Exception e) { ErrorToOutput($"Error parsing map: {e.Message}"); } } return(new MapData(file, difficulty, weight, map)); }
/// <summary> /// Adds any new files that are currently not cached. /// Used if the user adds a file to the folder. /// </summary> /// <param name="files"></param> private static void AddNonCachedFiles(List <string> files) { var maps = FetchAll(); foreach (var file in files) { if (maps.Any(x => BackslashToForward(file) == BackslashToForward($"{ConfigManager.SongDirectory.Value}/{x.Directory}/{x.Path}"))) { continue; } // Found map that isn't cached in the database yet. try { var map = Map.FromQua(Qua.Parse(file), file); map.CalculateDifficulties(); map.DifficultyProcessorVersion = DifficultyProcessorKeys.Version; InsertMap(map, file); } catch (Exception e) { Logger.Error(e, LogType.Runtime); } } }
/// <summary> /// Ctor - /// </summary> /// <param name="map"></param> /// <param name="md5"></param> /// <param name="scores"></param> /// <param name="replay"></param> /// <param name="isPlayTesting"></param> /// <param name="playTestTime"></param> /// <param name="isCalibratingOffset"></param> public GameplayScreen(Qua map, string md5, List <Score> scores, Replay replay = null, bool isPlayTesting = false, double playTestTime = 0, bool isCalibratingOffset = false) { TimePlayed = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); if (isPlayTesting) { var testingQua = ObjectHelper.DeepClone(map); testingQua.HitObjects.RemoveAll(x => x.StartTime < playTestTime); Map = testingQua; OriginalEditorMap = map; } else { Map = map; } LocalScores = scores; MapHash = md5; LoadedReplay = replay; IsPlayTesting = isPlayTesting; PlayTestAudioTime = playTestTime; IsCalibratingOffset = isCalibratingOffset; Timing = new GameplayAudioTiming(this); // Remove paused modifier if enabled. if (ModManager.IsActivated(ModIdentifier.Paused)) { ModManager.RemoveMod(ModIdentifier.Paused); } // Handle autoplay replays. if (ModManager.IsActivated(ModIdentifier.Autoplay)) { LoadedReplay = ReplayHelper.GeneratePerfectReplay(map, MapHash); } // Determine if we're in replay mode. if (LoadedReplay != null) { InReplayMode = true; } // Create the current replay that will be captured. ReplayCapturer = new ReplayCapturer(this); SetRuleset(); SetRichPresence(); AudioTrack.AllowPlayback = true; if (IsCalibratingOffset) { Metronome = new Metronome(map); } View = new GameplayScreenView(this); }
/// <summary> /// Inserts an entire extracted directory of maps to the DB /// </summary> /// <param name="extractDirectory"></param> private static Map InsertAndUpdateSelectedMap(string extractDirectory) { // Go through each file in the directory and import it into the database. var quaFiles = Directory.GetFiles(extractDirectory, "*.qua", SearchOption.AllDirectories).ToList(); Map lastImported = null; try { foreach (var quaFile in quaFiles) { var map = Map.FromQua(Qua.Parse(quaFile), quaFile); map.DifficultyProcessorVersion = DifficultyProcessorKeys.Version; var info = OnlineManager.Client?.RetrieveMapInfo(map.MapId); if (info != null) { map.RankedStatus = info.Map.RankedStatus; } map.CalculateDifficulties(); MapDatabaseCache.InsertMap(map, quaFile); lastImported = map; } } catch (Exception e) { Logger.Error(e, LogType.Runtime); } return(lastImported); }
/// <inheritdoc /> /// <summary> /// </summary> /// <param name="screen"></param> /// <param name="map"></param> public GameplayRulesetKeys(GameplayScreen screen, Qua map) : base(screen, map) { if (ConfigManager.DisplayTimingLines.Value) { TimingLineManager = CreateTimingLineManager(); } }
/// <summary> /// </summary> /// <param name="screen"></param> /// <param name="file"></param> private static void OnConfirm(QuaverScreen screen, string file) { for (var i = DialogManager.Dialogs.Count - 1; i >= 0; i--) { DialogManager.Dialogs[i].Destroy(); DialogManager.Dialogs.Remove(DialogManager.Dialogs[i]); } DialogManager.Update(new GameTime()); if (!MapDatabaseCache.MapsToUpdate.Contains(MapManager.Selected.Value)) { MapDatabaseCache.MapsToUpdate.Add(MapManager.Selected.Value); } screen.Exit(() => { try { return(new EditorScreen(Qua.Parse(file, false))); } catch (Exception exception) { Logger.Error(exception, LogType.Runtime); NotificationManager.Show(NotificationLevel.Error, "Failed to reload editor. Is your .qua file invalid?"); return(new SelectScreen()); } }); }
/// <summary> /// Ctor - /// </summary> /// <param name="map"></param> /// <param name="md5"></param> /// <param name="scores"></param> /// <param name="replay"></param> public GameplayScreen(Qua map, string md5, List<Score> scores, Replay replay = null) { TimePlayed = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds(); Map = map; LocalScores = scores; MapHash = md5; LoadedReplay = replay; Timing = new GameplayAudioTiming(this); // Remove paused modifier if enabled. if (ModManager.IsActivated(ModIdentifier.Paused)) ModManager.RemoveMod(ModIdentifier.Paused); // Handle autoplay replays. if (ModManager.IsActivated(ModIdentifier.Autoplay)) LoadedReplay = ReplayHelper.GeneratePerfectReplay(map, MapHash); // Determine if we're in replay mode. if (LoadedReplay != null) { InReplayMode = true; AddModsFromReplay(); } // Create the current replay that will be captured. ReplayCapturer = new ReplayCapturer(this); SetRuleset(); SetRichPresence(); AudioTrack.AllowPlayback = true; View = new GameplayScreenView(this); }
/// <summary> /// Responsible for converting a Qua object, to a Map object /// a Map object is one that is stored in the db. /// </summary> /// <param name="qua"></param> /// <param name="path"></param> /// <returns></returns> public static Map FromQua(Qua qua, string path) { var map = new Map { Md5Checksum = MapsetHelper.GetMd5Checksum(path), Directory = new DirectoryInfo(System.IO.Path.GetDirectoryName(path) ?? throw new InvalidOperationException()).Name.Replace("\\", "/"), Path = System.IO.Path.GetFileName(path)?.Replace("\\", "/"), Artist = qua.Artist, Title = qua.Title, HighestRank = Grade.None, AudioPath = qua.AudioFile, AudioPreviewTime = qua.SongPreviewTime, BackgroundPath = qua.BackgroundFile, Description = qua.Description, MapId = qua.MapId, MapSetId = qua.MapSetId, Bpm = qua.GetCommonBpm(), Creator = qua.Creator, DifficultyName = qua.DifficultyName, Source = qua.Source, Tags = qua.Tags, SongLength = qua.Length, Mode = qua.Mode, }; map.LastFileWrite = File.GetLastWriteTimeUtc(map.Path); return(map); }
public MapData(string file, float difficulty, float weight, Qua map = null) { TargetDifficulty = difficulty; Weight = weight; FilePath = file; Map = map; }
/// <summary> /// </summary> /// <param name="ruleset"></param> /// <param name="workingMap"></param> /// <param name="time"></param> public EditorActionSetPreviewTime(EditorRuleset ruleset, Qua workingMap, int time) { Ruleset = ruleset; WorkingMap = workingMap; PreviousPreviewTime = WorkingMap.SongPreviewTime; Time = time; }
public void SoundEffects() { var qua = Qua.Parse("./Quaver/Resources/sound-effects.qua"); Assert.True(qua.IsValid()); Assert.Equal(new [] { new CustomAudioSampleInfo() { Path = "hello.wav", UnaffectedByRate = false }, new CustomAudioSampleInfo() { Path = "world.mp3", UnaffectedByRate = true } }, qua.CustomAudioSamples, CustomAudioSampleInfo.ByValueComparer); Assert.Equal(new [] { new SoundEffectInfo() { StartTime = 123, Sample = 2, Volume = 100 }, new SoundEffectInfo() { StartTime = 200, Sample = 1, Volume = 53 } }, qua.SoundEffects, SoundEffectInfo.ByValueComparer); }
/// <summary> /// Initialize Info Pool. Info pool is used to pass info around to Hit Objects. /// </summary> /// <param name="map"></param> private void InitializeInfoPool(Qua map) { // Initialize collections var keyCount = Ruleset.Map.GetKeyCount(); HitObjectQueueLanes = new List <Queue <HitObjectInfo> >(keyCount); ActiveNoteLanes = new List <Queue <GameplayHitObjectKeys> >(keyCount); DeadNoteLanes = new List <Queue <GameplayHitObjectKeys> >(keyCount); HeldLongNoteLanes = new List <Queue <GameplayHitObjectKeys> >(keyCount); // Add HitObject Info to Info pool for (var i = 0; i < Ruleset.Map.GetKeyCount(); i++) { HitObjectQueueLanes.Add(new Queue <HitObjectInfo>()); ActiveNoteLanes.Add(new Queue <GameplayHitObjectKeys>(InitialPoolSizePerLane)); DeadNoteLanes.Add(new Queue <GameplayHitObjectKeys>()); HeldLongNoteLanes.Add(new Queue <GameplayHitObjectKeys>()); } // Sort Hit Object Info into their respective lanes foreach (var info in map.HitObjects) { HitObjectQueueLanes[info.Lane - 1].Enqueue(info); } }
/// <summary> /// </summary> /// <param name="args"></param> public VirtualReplayPlayerCommand(string[] args) : base(args) { var readHeaderLess = false; if (args.Length == 5) { if (args[4] == "-hl") { readHeaderLess = true; } } Replay = new Replay(args[1], readHeaderLess) { Mods = (ModIdentifier)long.Parse(args[3]) }; if (args[2].EndsWith(".qua")) { Map = Qua.Parse(args[2]); } else if (args[2].EndsWith(".osu")) { Map = new OsuBeatmap(args[2]).ToQua(); } MapMd5 = CryptoHelper.FileToMd5(args[2]); if (MapMd5 != Replay.MapMd5 && !readHeaderLess) { throw new ArgumentException("The specified replay doesn't match the map."); } }
/// <inheritdoc /> /// <summary> /// </summary> /// <param name="map"></param> public EditorTimingChanger(Qua map) { Calculator = new EditorBpmCalculator(); Dialog = new EditorBpmTimingDialog(this); WorkingMap = map; SelectClosestPoint(); }
/// <summary> /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private static void OnConfirm(object sender, EventArgs e) { var game = GameBase.Game as QuaverGame; try { var path = $"{ConfigManager.SongDirectory}/{MapManager.Selected.Value.Directory}/{MapManager.Selected.Value.Path}.autosave"; var realPath = path.Replace(".autosave", ""); File.Delete(realPath); File.Copy(path, realPath, true); File.Delete(path); if (!MapDatabaseCache.MapsToUpdate.Contains(MapManager.Selected.Value)) { MapDatabaseCache.MapsToUpdate.Add(MapManager.Selected.Value); } var qua = Qua.Parse(realPath, false); game?.CurrentScreen.Exit(() => new EditorScreen(qua)); } catch (Exception exception) { Logger.Error(exception, LogType.Runtime); NotificationManager.Show(NotificationLevel.Error, "Could not load autosave map"); } }
public void KeySoundZeroVolume() { var converter = new OsuBeatmap("./Osu/Resources/keysound-zero-volume.osu"); var qua = converter.ToQua(); var groundTruthQua = Qua.Parse("./Osu/Resources/keysound-zero-volume.qua"); Assert.Equal(groundTruthQua.HitObjects, qua.HitObjects, HitObjectInfo.ByValueComparer); }
/// <summary> /// Ctor - /// </summary> /// <param name="map"></param> /// <param name="mods"></param> public ScoreProcessor(Qua map, ModIdentifier mods) { Map = map; Mods = mods; Stats = new List <HitStat>(); InitializeMods(); }
public ChangeQuaIdsCommand(string[] args) : base(args) { var qua = Qua.Parse(args[1], false); qua.MapId = int.Parse(args[3]); qua.MapSetId = int.Parse(args[4]); qua.Save(args[2]); }
public void SoundEffectsFromO2JamConvert() { var converter = new OsuBeatmap("./Osu/Resources/Glide.osu"); var qua = converter.ToQua(); var groundTruthQua = Qua.Parse("./Osu/Resources/Glide-sound-effects.qua"); Assert.Equal(groundTruthQua.CustomAudioSamples, qua.CustomAudioSamples, CustomAudioSampleInfo.ByValueComparer); Assert.Equal(groundTruthQua.SoundEffects, qua.SoundEffects, SoundEffectInfo.ByValueComparer); }
public void FullConversionCheck() { var converter = new OsuBeatmap(BeatmapFilename); var qua = converter.ToQua(); var groundTruthQua = Qua.Parse(Path.ChangeExtension(BeatmapFilename, "qua")); Assert.True(qua.EqualByValue(groundTruthQua)); }
public void FullConversionWithKeysoundsCheck() { var converter = new OsuBeatmap("./Osu/Resources/megalovania.osu"); var qua = converter.ToQua(); var groundTruthQua = Qua.Parse("./Osu/Resources/megalovania.qua"); Assert.True(qua.EqualByValue(groundTruthQua)); }
public void HitSoundTypeConversionCheck() { var converter = new OsuBeatmap("./Osu/Resources/hitsounds.osu"); var qua = converter.ToQua(); var groundTruthQua = Qua.Parse("./Osu/Resources/hitsounds.qua"); Assert.True(qua.EqualByValue(groundTruthQua)); }
/// <summary> /// Ctor - /// </summary> /// <param name="map"></param> public HitObjectManager(Qua map) { SnapIndices = new Dictionary <HitObjectInfo, int>(); foreach (var hitObject in map.HitObjects) { SnapIndices.Add(hitObject, GetBeatSnap(hitObject, hitObject.GetTimingPoint(map.TimingPoints))); } }
/// <summary> /// /// </summary> /// <param name="screen"></param> /// <param name="map"></param> protected GameplayRuleset(GameplayScreen screen, Qua map) { Screen = screen; Map = map; ScoreProcessor = CreateScoreProcessor(Map); CreatePlayfield(); InputManager = CreateInputManager(); HitObjectManager = CreateHitObjectManager(); }
/// <summary> /// Generates a perfect replay for the keys game mode. /// </summary> /// <param name="replay"></param> /// <param name="map"></param> /// <returns></returns> public static Replay GeneratePerfectReplayKeys(Replay replay, Qua map) { var nonCombined = new List <ReplayAutoplayFrame>(); foreach (var hitObject in map.HitObjects) { // Add key press frame nonCombined.Add(new ReplayAutoplayFrame(hitObject, ReplayAutoplayFrameType.Press, hitObject.StartTime, KeyLaneToPressState(hitObject.Lane))); // If LN, add key up state at end time if (hitObject.IsLongNote) { nonCombined.Add(new ReplayAutoplayFrame(hitObject, ReplayAutoplayFrameType.Release, hitObject.EndTime - 1, KeyLaneToPressState(hitObject.Lane))); } // If not ln, add key up frame 1ms after object. else { nonCombined.Add(new ReplayAutoplayFrame(hitObject, ReplayAutoplayFrameType.Release, hitObject.StartTime + 30, KeyLaneToPressState(hitObject.Lane))); } } // Order objects by time nonCombined = nonCombined.OrderBy(x => x.Time).ToList(); // Global replay state so we can loop through in track it. ReplayKeyPressState state = 0; // Add beginning frame w/ no press state. (-10000 just to be on the safe side.) replay.Frames.Add(new ReplayFrame(-10000, 0)); var startTimeGroup = nonCombined.GroupBy(x => x.Time).ToDictionary(x => x.Key, x => x.ToList()); foreach (var item in startTimeGroup) { foreach (var frame in item.Value) { switch (frame.Type) { case ReplayAutoplayFrameType.Press: state |= KeyLaneToPressState(frame.HitObject.Lane); break; case ReplayAutoplayFrameType.Release: state -= KeyLaneToPressState(frame.HitObject.Lane); break; default: throw new ArgumentOutOfRangeException(); } } //Console.WriteLine($"Added frame at: {item.Key} with state: {state}"); replay.Frames.Add(new ReplayFrame(item.Key, state)); } return(replay); }
/// <summary> /// </summary> public EditorScreen(Qua map) { if (OnlineManager.IsSpectatingSomeone) { OnlineManager.Client?.StopSpectating(); } OriginalMap = map; WorkingMap = ObjectHelper.DeepClone(OriginalMap); FixInvalidHitObjectLayers(); MapManager.Selected.Value.Qua = WorkingMap; // Discord Rich Presence DiscordHelper.Presence.Details = WorkingMap.ToString(); DiscordHelper.Presence.State = "Editing"; DiscordHelper.Presence.StartTimestamp = (long)(TimeHelper.GetUnixTimestampMilliseconds() / 1000); DiscordHelper.Presence.EndTimestamp = 0; DiscordRpc.UpdatePresence(ref DiscordHelper.Presence); ActiveLayerInterface = new Bindable <EditorLayerInterface>(EditorLayerInterface.Composition) { Value = EditorLayerInterface.Composition }; ModManager.RemoveSpeedMods(); if (!LoadAudioTrack()) { return; } CustomAudioSampleCache.LoadSamples(MapManager.Selected.Value, MapManager.Selected.Value.Md5Checksum); SetHitSoundObjectIndex(); GameBase.Game.IsMouseVisible = true; GameBase.Game.GlobalUserInterface.Cursor.Visible = false; GameBase.Game.Window.FileDropped += OnFileDropped; if (MapManager.Selected.Value.Game == MapGame.Quaver) { BeginWatchingFiles(); } Metronome = new Metronome(WorkingMap); View = new EditorScreenView(this); CreateRuleset(); AppDomain.CurrentDomain.UnhandledException += OnCrash; if (File.Exists($"{ConfigManager.SongDirectory}/{MapManager.Selected.Value.Directory}/{MapManager.Selected.Value.Path}.autosave")) { DialogManager.Show(new EditorAutosaveDetectionDialog()); } }
/// <summary> /// Ctor - /// </summary> /// <param name="map"></param> /// <param name="mods"></param> /// <param name="windows"></param> public ScoreProcessor(Qua map, ModIdentifier mods, JudgementWindows windows = null) { Map = map; Mods = mods; Stats = new List <HitStat>(); InitializeJudgementWindows(windows); InitializeMods(); }
public static Replay GenerateAutoplayReplay(Qua map) { var replay = new Replay(); replay.PlayerName = "Autoplay"; replay.Mode = map.Mode; replay.Frames = new List <ReplayFrame>(); var nonCombined = new List <ReplayAutoplayFrame>(); foreach (var hitObject in map.HitObjects) { nonCombined.Add(new ReplayAutoplayFrame(hitObject, ReplayAutoplayFrameType.Press, hitObject.StartTime, KeyLaneToPressState(hitObject.Lane))); if (hitObject.IsLongNote) { nonCombined.Add(new ReplayAutoplayFrame(hitObject, ReplayAutoplayFrameType.Release, hitObject.EndTime - 1, KeyLaneToPressState(hitObject.Lane))); } else { nonCombined.Add(new ReplayAutoplayFrame(hitObject, ReplayAutoplayFrameType.Release, hitObject.StartTime + 30, KeyLaneToPressState(hitObject.Lane))); } } nonCombined = nonCombined.OrderBy(x => x.Time).ToList(); ReplayKeyPressState state = 0; replay.Frames.Add(new ReplayFrame(-10000, 0)); var startTimeGroup = nonCombined.GroupBy(x => x.Time).ToDictionary(x => x.Key, x => x.ToList()); foreach (var item in startTimeGroup) { foreach (var frame in item.Value) { switch (frame.Type) { case ReplayAutoplayFrameType.Press: state |= KeyLaneToPressState(frame.HitObject.Lane); break; case ReplayAutoplayFrameType.Release: state -= KeyLaneToPressState(frame.HitObject.Lane); break; default: throw new ArgumentOutOfRangeException(); } } replay.Frames.Add(new ReplayFrame(item.Key, state)); } return(replay); }
public OsuBeatmap(Qua qua, Arguments args) { GeneralSection = new GeneralSection(qua, args); EditorSection = new EditorSection(qua, args); MetadataSection = new MetadataSection(qua, args); DifficultySection = new DifficultySection(qua, args); EventsSection = new EventsSection(qua, args); TimingPointsSection = new TimingPointsSection(qua, args); HitObjectsSection = new HitObjectsSection(qua, args); }
/// <inheritdoc /> /// <summary> /// </summary> /// <param name="map"></param> /// <returns></returns> protected override ScoreProcessor CreateScoreProcessor(Qua map) { if (Screen.IsMultiplayerGame) { return(new ScoreProcessorKeys(map, ModManager.Mods, new ScoreProcessorMultiplayer((MultiplayerHealthType)OnlineManager.CurrentGame.HealthType, OnlineManager.CurrentGame.Lives))); } return(new ScoreProcessorKeys(map, ModManager.Mods)); }