From f8a5ce0cd2df5e5247ed0affe00ec4d6e8ffc1a7 Mon Sep 17 00:00:00 2001 From: ekrctb Date: Thu, 2 Feb 2023 22:32:33 +0900 Subject: [PATCH 0001/1118] Use stable sort for catch hyperdash generation --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index ab61b14ac4..c51d3d5c70 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -209,7 +209,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } } - palpableObjects.Sort((h1, h2) => h1.StartTime.CompareTo(h2.StartTime)); + palpableObjects = palpableObjects.OrderBy(h => h.StartTime).ToList(); double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.Difficulty) / 2; From 491ba13b6f1a9dc15c80474fd60970cb66926c3f Mon Sep 17 00:00:00 2001 From: ekrctb Date: Fri, 3 Feb 2023 14:07:21 +0900 Subject: [PATCH 0002/1118] Factor out palpable obejct enumeration logic --- .../Beatmaps/CatchBeatmap.cs | 21 ++++++++++++++++ .../Beatmaps/CatchBeatmapProcessor.cs | 24 ++++--------------- .../Difficulty/CatchDifficultyCalculator.cs | 8 +++---- 3 files changed, 28 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs index f009c10a9c..1f05d66b86 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmap.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -38,5 +39,25 @@ namespace osu.Game.Rulesets.Catch.Beatmaps } }; } + + /// + /// Enumerate all s, sorted by their start times. + /// + /// + /// If multiple objects have the same start time, the ordering is preserved (it is a stable sorting). + /// + public static IEnumerable GetPalpableObjects(IEnumerable hitObjects) + { + return hitObjects.SelectMany(selectPalpableObjects).OrderBy(h => h.StartTime); + + IEnumerable selectPalpableObjects(HitObject h) + { + if (h is PalpableCatchHitObject palpable) + yield return palpable; + + foreach (var nested in h.NestedHitObjects.OfType()) + yield return nested; + } + } } } diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index c51d3d5c70..32134912f1 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; @@ -192,24 +191,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps private static void initialiseHyperDash(IBeatmap beatmap) { - List palpableObjects = new List(); - - foreach (var currentObject in beatmap.HitObjects) - { - if (currentObject is Fruit fruitObject) - palpableObjects.Add(fruitObject); - - if (currentObject is JuiceStream) - { - foreach (var juice in currentObject.NestedHitObjects) - { - if (juice is PalpableCatchHitObject palpableObject && !(juice is TinyDroplet)) - palpableObjects.Add(palpableObject); - } - } - } - - palpableObjects = palpableObjects.OrderBy(h => h.StartTime).ToList(); + var palpableObjects = CatchBeatmap.GetPalpableObjects(beatmap.HitObjects) + .Where(h => h is Fruit || (h is Droplet && h is not TinyDroplet)) + .ToArray(); double halfCatcherWidth = Catcher.CalculateCatchWidth(beatmap.Difficulty) / 2; @@ -221,7 +205,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps int lastDirection = 0; double lastExcess = halfCatcherWidth; - for (int i = 0; i < palpableObjects.Count - 1; i++) + for (int i = 0; i < palpableObjects.Length - 1; i++) { var currentObject = palpableObjects[i]; var nextObject = palpableObjects[i + 1]; diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs index 42cfde268e..959f4830dd 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchDifficultyCalculator.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Difficulty.Preprocessing; using osu.Game.Rulesets.Catch.Difficulty.Skills; using osu.Game.Rulesets.Catch.Mods; @@ -54,13 +55,10 @@ namespace osu.Game.Rulesets.Catch.Difficulty List objects = new List(); // In 2B beatmaps, it is possible that a normal Fruit is placed in the middle of a JuiceStream. - foreach (var hitObject in beatmap.HitObjects - .SelectMany(obj => obj is JuiceStream stream ? stream.NestedHitObjects.AsEnumerable() : new[] { obj }) - .Cast() - .OrderBy(x => x.StartTime)) + foreach (var hitObject in CatchBeatmap.GetPalpableObjects(beatmap.HitObjects)) { // We want to only consider fruits that contribute to the combo. - if (hitObject is BananaShower || hitObject is TinyDroplet) + if (hitObject is Banana || hitObject is TinyDroplet) continue; if (lastObject != null) From 3c76cb2fb0cc67763e50770670a0c890c1755cd9 Mon Sep 17 00:00:00 2001 From: Zyf Date: Sat, 8 Jul 2023 17:54:23 +0200 Subject: [PATCH 0003/1118] Scoring : Use square-root of combo --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 35a7dfe369..0760a89b90 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -293,7 +293,7 @@ namespace osu.Game.Rulesets.Scoring protected virtual double GetBonusScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type); - protected virtual double GetComboScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type) * (1 + result.ComboAfterJudgement / 10d); + protected virtual double GetComboScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type) * Math.Pow(result.ComboAfterJudgement, 0.5); protected virtual void ApplyScoreChange(JudgementResult result) { From 175bd3b5782e5858a4445f0de44a412b86788e8d Mon Sep 17 00:00:00 2001 From: Zyf Date: Sat, 8 Jul 2023 19:05:56 +0200 Subject: [PATCH 0004/1118] Scoring : Move some of the accuracy weight to ComboScore (edited) --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 0760a89b90..ea1140ef71 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -317,8 +317,8 @@ namespace osu.Game.Rulesets.Scoring protected virtual double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion) { - return 700000 * comboProgress + - 300000 * Math.Pow(Accuracy.Value, 10) * accuracyProgress + + return 700000 * Accuracy.Value * comboProgress + + 300000 * Math.Pow(Accuracy.Value, 8) * accuracyProgress + bonusPortion; } From ecb633f8fbd616b30fbeae5724f86659034ca2eb Mon Sep 17 00:00:00 2001 From: Zyf Date: Sat, 15 Jul 2023 14:55:43 +0200 Subject: [PATCH 0005/1118] Scoring : Remove ComputeTotalScore overide in OsuScoreProcessor --- osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index f97be0d7ff..dfdf3c58fd 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -17,12 +17,5 @@ namespace osu.Game.Rulesets.Osu.Scoring protected override HitEvent CreateHitEvent(JudgementResult result) => base.CreateHitEvent(result).With((result as OsuHitCircleJudgementResult)?.CursorPositionAtHit); - - protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion) - { - return 700000 * comboProgress - + 300000 * Math.Pow(Accuracy.Value, 10) * accuracyProgress - + bonusPortion; - } } } From b672b49e02b6d650f9b207ab908edfd3f4886dc3 Mon Sep 17 00:00:00 2001 From: Zyf Date: Sat, 15 Jul 2023 23:20:49 +0200 Subject: [PATCH 0006/1118] Scoring : Implement v1 to v3 conversion. --- .../Difficulty/OsuLegacyScoreSimulator.cs | 10 ++- .../StandardisedScoreMigrationTools.cs | 69 +++++++++++++++++-- .../Difficulty/DifficultyAttributes.cs | 10 +++ .../Rulesets/Scoring/ILegacyScoreSimulator.cs | 12 +++- 4 files changed, 92 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs index 980d86e4ad..78245f903a 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs @@ -20,9 +20,12 @@ namespace osu.Game.Rulesets.Osu.Difficulty public int ComboScore { get; private set; } - public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore; + public int LegacyBonusScore { get; private set; } + + public int MaxCombo { get; private set; } + + public double BonusScoreRatio => LegacyBonusScore == 0 ? 0 : (double)modernBonusScore / LegacyBonusScore; - private int legacyBonusScore; private int modernBonusScore; private int combo; @@ -77,6 +80,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty foreach (var obj in playableBeatmap.HitObjects) simulateHit(obj); + MaxCombo = combo; } private void simulateHit(HitObject hitObject) @@ -164,7 +168,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (isBonus) { - legacyBonusScore += scoreIncrease; + LegacyBonusScore += scoreIncrease; modernBonusScore += Judgement.ToNumericResult(bonusResult); } else diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 60530c31cb..61f0d1f195 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -218,7 +218,9 @@ namespace osu.Game.Database { LegacyAccuracyScore = sv1Simulator.AccuracyScore, LegacyComboScore = sv1Simulator.ComboScore, - LegacyBonusScoreRatio = sv1Simulator.BonusScoreRatio + LegacyBonusScoreRatio = sv1Simulator.BonusScoreRatio, + LegacyBonusScore = sv1Simulator.LegacyBonusScore, + LegacyMaxCombo = sv1Simulator.MaxCombo, }); } @@ -240,13 +242,16 @@ namespace osu.Game.Database int maximumLegacyAccuracyScore = attributes.LegacyAccuracyScore; int maximumLegacyComboScore = attributes.LegacyComboScore; double maximumLegacyBonusRatio = attributes.LegacyBonusScoreRatio; + int maximumLegacyBonusScore = attributes.LegacyBonusScore; + int maximumLegacyCombo = attributes.LegacyMaxCombo; double modMultiplier = score.Mods.Select(m => m.ScoreMultiplier).Aggregate(1.0, (c, n) => c * n); // The part of total score that doesn't include bonus. int maximumLegacyBaseScore = maximumLegacyAccuracyScore + maximumLegacyComboScore; + int maximumLegacyTotalScore = maximumLegacyBaseScore + maximumLegacyBonusScore; - // The combo proportion is calculated as a proportion of maximumLegacyBaseScore. - double comboProportion = Math.Min(1, (double)score.LegacyTotalScore / maximumLegacyBaseScore); + // The combo proportion is calculated as a proportion of maximumLegacyTotalScore. + double comboProportion = (double)score.LegacyTotalScore / maximumLegacyTotalScore; // The bonus proportion makes up the rest of the score that exceeds maximumLegacyBaseScore. double bonusProportion = Math.Max(0, ((long)score.LegacyTotalScore - maximumLegacyBaseScore) * maximumLegacyBonusRatio); @@ -254,9 +259,63 @@ namespace osu.Game.Database switch (score.Ruleset.OnlineID) { case 0: + if(score.MaxCombo == 0 || score.Accuracy == 0) + return (long)Math.Round(( + 0 + + 300000 * Math.Pow(score.Accuracy, 8) + + bonusProportion) * modMultiplier); + + double v3exp = 0.5; // Scorev3 combo exponent + + // Assumption : + // - sliders and slider-ticks are uniformly spread arround the beatmap + // thus we can ignore them without losing much precision (consider a map of hit-circles only !) + // - the Ok/Meh hit results are uniformly spread in the score + // thus we can simplify and consider each hit result to be score.Accuracy without losing much precision + // What is strippedV1/strippedV3 : + // This is the ComboScore of v1/v3 were we remove all (map-)constant multipliers and accuracy multipliers (including hit results), + // based on the previous assumptions. For Scorev1, this is basically the sum of squared combos (because without sliders: object_count == combo). + double maxStrippedV1 = Math.Pow(maximumLegacyCombo, 2); + double maxStrippedV3 = Math.Pow(maximumLegacyCombo, 1 + v3exp); + + double strippedV1 = maxStrippedV1 * comboProportion / score.Accuracy; + + double strippedV1FromMaxCombo = Math.Pow(score.MaxCombo, 2); + double strippedV3FromMaxCombo = Math.Pow(score.MaxCombo, 1 + v3exp); + + // Compute approximate lower estimate scorev3 for that play + // That is, a play were we made biggest amount of big combos (Repeat MaxCombo + 1 remaining big combo) + // And didn't combo anything in the reminder of the map + double possibleMaxComboRepeat = Math.Floor(strippedV1 / strippedV1FromMaxCombo); + double strippedV1FromMaxComboRepeat = possibleMaxComboRepeat * strippedV1FromMaxCombo; + double remainingStrippedV1 = strippedV1 - strippedV1FromMaxComboRepeat; + double remainingCombo = Math.Sqrt(remainingStrippedV1); + double remainingStrippedV3 = Math.Pow(remainingCombo, 1 + v3exp); + + double newLowerStrippedV3 = (possibleMaxComboRepeat * strippedV3FromMaxCombo) + remainingStrippedV3; + + // Compute approximate upper estimate scorev3 for that play + // That is, a play were all combos were equal (except MaxCombo) + remainingStrippedV1 = strippedV1 - strippedV1FromMaxCombo; + double remainingComboObjects = maximumLegacyCombo - score.MaxCombo - score.Statistics[HitResult.Miss]; + double remainingAverageCombo = remainingComboObjects > 0 ? remainingStrippedV1 / remainingComboObjects : 0; + remainingStrippedV3 = remainingComboObjects * Math.Pow(remainingAverageCombo, v3exp); + + double newUpperStrippedV3 = strippedV3FromMaxCombo + remainingStrippedV3; + + // Approximate by combining lower and upper estimates + // As the lower-estimate is very pessimistic, we use a 30/70 ratio + // And cap it with 1.2 times the middle-point to avoid overstimates + double strippedV3 = Math.Min( + 0.3 * newLowerStrippedV3 + 0.7 * newUpperStrippedV3, + 1.2 * (newLowerStrippedV3 + newUpperStrippedV3) / 2 + ); + + double newComboScoreProportion = (strippedV3 / maxStrippedV3) * score.Accuracy; + return (long)Math.Round(( - 700000 * comboProportion - + 300000 * Math.Pow(score.Accuracy, 10) + 700000 * newComboScoreProportion * score.Accuracy + + 300000 * Math.Pow(score.Accuracy, 8) + bonusProportion) * modMultiplier); case 1: diff --git a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs index 5a01faa417..69345872c6 100644 --- a/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs +++ b/osu.Game/Rulesets/Difficulty/DifficultyAttributes.cs @@ -64,6 +64,16 @@ namespace osu.Game.Rulesets.Difficulty /// public double LegacyBonusScoreRatio { get; set; } + /// + /// The bonus portion of the legacy (ScoreV1) total score. + /// + public int LegacyBonusScore { get; set; } + + /// + /// The maximum combo of the legacy (ScoreV1) total score. + /// + public int LegacyMaxCombo { get; set; } + /// /// Creates new . /// diff --git a/osu.Game/Rulesets/Scoring/ILegacyScoreSimulator.cs b/osu.Game/Rulesets/Scoring/ILegacyScoreSimulator.cs index 7240f0d73e..68ace15b20 100644 --- a/osu.Game/Rulesets/Scoring/ILegacyScoreSimulator.cs +++ b/osu.Game/Rulesets/Scoring/ILegacyScoreSimulator.cs @@ -23,9 +23,19 @@ namespace osu.Game.Rulesets.Scoring int ComboScore { get; } /// - /// A ratio of new_bonus_score / old_bonus_score for converting the bonus score of legacy scores to the new scoring. + /// The bonus portion of the legacy (ScoreV1) total score. /// This is made up of all judgements that would be or . /// + int LegacyBonusScore { get; } + + /// + /// The maximum combo of the legacy (ScoreV1) total score. + /// + int MaxCombo { get; } + + /// + /// A ratio of new_bonus_score / old_bonus_score for converting the bonus score of legacy scores to the new scoring. + /// double BonusScoreRatio { get; } /// From a97915180f37a87ac5716acdb0bfcafffe9e6452 Mon Sep 17 00:00:00 2001 From: Zyf Date: Sat, 15 Jul 2023 23:30:59 +0200 Subject: [PATCH 0007/1118] Scoring : Adds fields to Catch/Mania/Taiko Simulators too --- .../Difficulty/CatchLegacyScoreSimulator.cs | 10 +++++++--- .../Difficulty/ManiaLegacyScoreSimulator.cs | 3 +++ .../Difficulty/TaikoLegacyScoreSimulator.cs | 10 +++++++--- 3 files changed, 17 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs index c79fd36d96..284fd23aea 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs @@ -20,9 +20,12 @@ namespace osu.Game.Rulesets.Catch.Difficulty public int ComboScore { get; private set; } - public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore; + public int LegacyBonusScore { get; private set; } + + public int MaxCombo { get; private set; } + + public double BonusScoreRatio => LegacyBonusScore == 0 ? 0 : (double)modernBonusScore / LegacyBonusScore; - private int legacyBonusScore; private int modernBonusScore; private int combo; @@ -74,6 +77,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty foreach (var obj in playableBeatmap.HitObjects) simulateHit(obj); + MaxCombo = combo; } private void simulateHit(HitObject hitObject) @@ -129,7 +133,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (isBonus) { - legacyBonusScore += scoreIncrease; + LegacyBonusScore += scoreIncrease; modernBonusScore += Judgement.ToNumericResult(bonusResult); } else diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs index e544428979..f09c911b98 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs @@ -14,6 +14,8 @@ namespace osu.Game.Rulesets.Mania.Difficulty { public int AccuracyScore => 0; public int ComboScore { get; private set; } + public int LegacyBonusScore => 0; + public int MaxCombo { get; private set; } public double BonusScoreRatio => 0; public void Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap, IReadOnlyList mods) @@ -23,6 +25,7 @@ namespace osu.Game.Rulesets.Mania.Difficulty .Aggregate(1.0, (c, n) => c * n); ComboScore = (int)(1000000 * multiplier); + MaxCombo = playableBeatmap.GetMaxCombo(); } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index e77327d622..239ec5765c 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -20,9 +20,12 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public int ComboScore { get; private set; } - public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore; + public int LegacyBonusScore { get; private set; } + + public int MaxCombo { get; private set; } + + public double BonusScoreRatio => LegacyBonusScore == 0 ? 0 : (double)modernBonusScore / LegacyBonusScore; - private int legacyBonusScore; private int modernBonusScore; private int combo; @@ -80,6 +83,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty foreach (var obj in playableBeatmap.HitObjects) simulateHit(obj); + MaxCombo = combo; } private void simulateHit(HitObject hitObject) @@ -189,7 +193,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (isBonus) { - legacyBonusScore += scoreIncrease; + LegacyBonusScore += scoreIncrease; modernBonusScore += Judgement.ToNumericResult(bonusResult); } else From bbe4635a5080befefc78cccdf61d196d0941bc5d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2023 16:07:48 +0900 Subject: [PATCH 0008/1118] Remove unused usings --- osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs index dfdf3c58fd..97980c6d18 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuScoreProcessor.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; From 5dee43815c2051cd2bae237082f0307f68e1a283 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 22 Aug 2023 16:13:01 +0900 Subject: [PATCH 0009/1118] Bump version to allow migration to re-run --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 2 +- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index fbc53b9d70..7a77434f23 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -26,7 +26,7 @@ namespace osu.Game.Database if (score.IsLegacyScore) return false; - if (score.TotalScoreVersion > 30000002) + if (score.TotalScoreVersion > 30000003) return false; // Recalculate the old-style standardised score to see if this was an old lazer score. diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 6868c89d26..a1f984484e 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -30,9 +30,10 @@ namespace osu.Game.Scoring.Legacy /// 30000001: Appends to the end of scores. /// 30000002: Score stored to replay calculated using the Score V2 algorithm. Legacy scores on this version are candidate to Score V1 -> V2 conversion. /// 30000003: First version after converting legacy total score to standardised. + /// 30000004: First version after converting legacy total score to standardised (with combo exponent adjustment). /// /// - public const int LATEST_VERSION = 30000003; + public const int LATEST_VERSION = 30000004; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From 5ddb10281b06ad3696582a4b13706d710b30c284 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 24 Aug 2023 14:55:24 +0900 Subject: [PATCH 0010/1118] Move combo exponent to shared constant --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 12 +++++------- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 10 +++++++++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 7a77434f23..92290fcf1c 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -270,14 +270,12 @@ namespace osu.Game.Database switch (score.Ruleset.OnlineID) { case 0: - if(score.MaxCombo == 0 || score.Accuracy == 0) + if (score.MaxCombo == 0 || score.Accuracy == 0) return (long)Math.Round(( 0 + 300000 * Math.Pow(score.Accuracy, 8) + bonusProportion) * modMultiplier); - double v3exp = 0.5; // Scorev3 combo exponent - // Assumption : // - sliders and slider-ticks are uniformly spread arround the beatmap // thus we can ignore them without losing much precision (consider a map of hit-circles only !) @@ -287,12 +285,12 @@ namespace osu.Game.Database // This is the ComboScore of v1/v3 were we remove all (map-)constant multipliers and accuracy multipliers (including hit results), // based on the previous assumptions. For Scorev1, this is basically the sum of squared combos (because without sliders: object_count == combo). double maxStrippedV1 = Math.Pow(maximumLegacyCombo, 2); - double maxStrippedV3 = Math.Pow(maximumLegacyCombo, 1 + v3exp); + double maxStrippedV3 = Math.Pow(maximumLegacyCombo, 1 + ScoreProcessor.COMBO_EXPONENT); double strippedV1 = maxStrippedV1 * comboProportion / score.Accuracy; double strippedV1FromMaxCombo = Math.Pow(score.MaxCombo, 2); - double strippedV3FromMaxCombo = Math.Pow(score.MaxCombo, 1 + v3exp); + double strippedV3FromMaxCombo = Math.Pow(score.MaxCombo, 1 + ScoreProcessor.COMBO_EXPONENT); // Compute approximate lower estimate scorev3 for that play // That is, a play were we made biggest amount of big combos (Repeat MaxCombo + 1 remaining big combo) @@ -301,7 +299,7 @@ namespace osu.Game.Database double strippedV1FromMaxComboRepeat = possibleMaxComboRepeat * strippedV1FromMaxCombo; double remainingStrippedV1 = strippedV1 - strippedV1FromMaxComboRepeat; double remainingCombo = Math.Sqrt(remainingStrippedV1); - double remainingStrippedV3 = Math.Pow(remainingCombo, 1 + v3exp); + double remainingStrippedV3 = Math.Pow(remainingCombo, 1 + ScoreProcessor.COMBO_EXPONENT); double newLowerStrippedV3 = (possibleMaxComboRepeat * strippedV3FromMaxCombo) + remainingStrippedV3; @@ -310,7 +308,7 @@ namespace osu.Game.Database remainingStrippedV1 = strippedV1 - strippedV1FromMaxCombo; double remainingComboObjects = maximumLegacyCombo - score.MaxCombo - score.Statistics[HitResult.Miss]; double remainingAverageCombo = remainingComboObjects > 0 ? remainingStrippedV1 / remainingComboObjects : 0; - remainingStrippedV3 = remainingComboObjects * Math.Pow(remainingAverageCombo, v3exp); + remainingStrippedV3 = remainingComboObjects * Math.Pow(remainingAverageCombo, ScoreProcessor.COMBO_EXPONENT); double newUpperStrippedV3 = strippedV3FromMaxCombo + remainingStrippedV3; diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index ea1140ef71..514630b351 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -21,6 +21,14 @@ namespace osu.Game.Rulesets.Scoring { public partial class ScoreProcessor : JudgementProcessor { + /// + /// The exponent applied to combo in the default implementation of . + /// + /// + /// If a custom implementation overrides this may not be relevant. + /// + public const double COMBO_EXPONENT = 0.5; + public const double MAX_SCORE = 1000000; private const double accuracy_cutoff_x = 1; @@ -293,7 +301,7 @@ namespace osu.Game.Rulesets.Scoring protected virtual double GetBonusScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type); - protected virtual double GetComboScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type) * Math.Pow(result.ComboAfterJudgement, 0.5); + protected virtual double GetComboScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); protected virtual void ApplyScoreChange(JudgementResult result) { From ed886a4dc3e8cd7c0f722471dae0f42fd557b927 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Thu, 24 Aug 2023 16:49:42 +0300 Subject: [PATCH 0011/1118] Added a true AR/OD display when using DT/HT --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index a383298faa..c88bb2cb77 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -126,6 +126,22 @@ namespace osu.Game.Screens.Select.Details mod.ApplyToDifficulty(adjustedDifficulty); } + if (baseDifficulty != null && mods.Value.Any(m => m is ModRateAdjust)) + { + adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.Value.OfType()) + { + double speedChange = (float)mod.SpeedChange.Value; + + double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, 1800, 1200, 450) / speedChange; + adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); + + float hitwindow300 = (80.0f - 6 * adjustedDifficulty.OverallDifficulty) / (float)speedChange; + adjustedDifficulty.OverallDifficulty = (80.0f - hitwindow300) / 6; + } + } + switch (BeatmapInfo?.Ruleset.OnlineID) { case 3: From a885bf6ebf5081c52cf3c0eaa36d13ccafe1d5b0 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 24 Aug 2023 21:05:40 +0200 Subject: [PATCH 0012/1118] feat(editor/checks): check for delayed hitsounds I really just borrowed the implementation from MapsetVerifier --- .../Checks/CheckDelayedHitsoundsTest.cs | 97 ++++++++++++ .../Samples/hitsound-consequent-delay.wav | Bin 0 -> 49994 bytes .../Resources/Samples/hitsound-delay.wav | Bin 0 -> 49274 bytes .../Samples/hitsound-minor-delay.wav | Bin 0 -> 48562 bytes .../Resources/Samples/hitsound-no-delay.wav | Bin 0 -> 48210 bytes osu.Game/Rulesets/Edit/BeatmapVerifier.cs | 1 + .../Edit/Checks/CheckDelayedHitsounds.cs | 138 ++++++++++++++++++ 7 files changed, 236 insertions(+) create mode 100644 osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs create mode 100644 osu.Game.Tests/Resources/Samples/hitsound-consequent-delay.wav create mode 100644 osu.Game.Tests/Resources/Samples/hitsound-delay.wav create mode 100644 osu.Game.Tests/Resources/Samples/hitsound-minor-delay.wav create mode 100644 osu.Game.Tests/Resources/Samples/hitsound-no-delay.wav create mode 100644 osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs diff --git a/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs b/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs new file mode 100644 index 0000000000..5be76327b3 --- /dev/null +++ b/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs @@ -0,0 +1,97 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.IO; +using System.Linq; +using ManagedBass; +using Moq; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Models; +using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Edit.Checks; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Resources; +using osuTK.Audio; + +namespace osu.Game.Tests.Editing.Checks +{ + [TestFixture] + public class CheckDelayedHitsoundsTest + { + private CheckDelayedHitsounds check = null!; + private IBeatmap beatmap = null!; + + [SetUp] + public void SetUp() + { + check = new CheckDelayedHitsounds(); + beatmap = new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + BeatmapSet = new BeatmapSetInfo + { + Files = + { + new RealmNamedFileUsage(new RealmFile { Hash = "abcdef" }, "normal-hitnormal.wav"), + } + } + } + }; + + if (!Bass.Init(0) && Bass.LastError != Errors.Already) + throw new AudioException("Could not initialize Bass."); + } + + [Test] + public void TestNoDelayedHitsounds() + { + using var resourceStream = TestResources.OpenResource("Samples/hitsound-no-delay.wav"); + Assert.IsEmpty(check.Run(getContext(resourceStream))); + } + + [Test] + public void TestMinorDelayedHitsounds() + { + using (var resourceStream = TestResources.OpenResource("Samples/hitsound-minor-delay.wav")) + { + var issues = check.Run(getContext(resourceStream)).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckDelayedHitsounds.IssuTemplateMinorDelay); + } + } + + [Test] + public void TestDelayedHitsounds() + { + using var resourceStream = TestResources.OpenResource("Samples/hitsound-delay.wav"); + + var issues = check.Run(getContext(resourceStream)).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckDelayedHitsounds.IssueTemplateDelay); + } + + [Test] + public void TestConsequentlyDelayedHitsounds() + { + using var resourceStream = TestResources.OpenResource("Samples/hitsound-consequent-delay.wav"); + + var issues = check.Run(getContext(resourceStream)).ToList(); + + Assert.That(issues, Has.Count.EqualTo(1)); + Assert.That(issues.Single().Template is CheckDelayedHitsounds.IssueTemplateConsequentDelay); + } + + private BeatmapVerifierContext getContext(Stream? resourceStream) + { + var mockWorkingBeatmap = new Mock(beatmap, null, null); + mockWorkingBeatmap.Setup(w => w.GetStream(It.IsAny())).Returns(resourceStream); + + return new BeatmapVerifierContext(beatmap, mockWorkingBeatmap.Object); + } + } +} diff --git a/osu.Game.Tests/Resources/Samples/hitsound-consequent-delay.wav b/osu.Game.Tests/Resources/Samples/hitsound-consequent-delay.wav new file mode 100644 index 0000000000000000000000000000000000000000..049e54c62f62e0c42f6b8445c3aa3ee77d773146 GIT binary patch literal 49994 zcmeFYhhGz2*EYJRq!$Ri_ofuZhKdNHsMr;;H|)J5_QZPId+!yyf*^>Biin`t=}51k zg@n}UXMFDWJ>Pf!hVzi$WI~czd+k-OYwa~VlcI+YkIV*O>X0c>i&t%QGY0@b00zSz z17MyE0~DBpg>f6>YCf~X(0TNK#`yajN&fvbcn>2N9AV^twE>FH zsgUALDNsz){FMt7$bX(8c7(!rKr#04-}nAk|Nk8WR?u>U$H?6i2>GB7;U7l|{>nua z6f4iF22ynI1{5Fi{=M?=^MRW{u{8a!*3f^X|NS=fpKoXCr=MWt3*IyGrsItK=Tut$ zEr^l(1^3wc*DhD)+9n6|h7_dS;>}-OO;dnklr2!qDFKShAD`{{*BUcV$GikHcJHD7 zV`uC*EqA)b$S*fA@@O?9|9AF(rOmfsHI_2+@rsG#fa0Hc)@nka7z*Ylxp55;<@{fhIyiUc)w~r$KW6QS@DZT@OnBvb)z3%s@0*azHuwp?P zP{f=-6!u9#u`Pez`7u8}zEbFRk?(tt4bNVvTFS@=Z~LoF#(KXk%5ju@U@#-sK-^U! z&3A%Vk#qTJ)=`~zUB>@uBQF0O*1WMigxO0Mc6%Z+M15o)jz7rti8(Ini)lHu>hHV% zdOm3kEuTD|kr&3(a`PSRxbe3=KdjpOu5h~LnPWpB#eHX>7;gfK;##1%_X$?m-2#e= z|A~fwV*>r3oI}{$NI1sE+;gBfTna1JqCgSaYp!CzlC(BPK4%opj`@FUL$Uu|U(d*O z{TTU=j&@VZ^_u^W+!&yi{7sQ?_M$Ef5I`@x_WNlR+&d4W4G4i%C zl)N#Pkw0N`Wy!c%+oF3h@=>Dt<sYfIBEnVZ=b`+hpeFGeOEB@X7=@@VYJ-Olab$JSlp^)wYi??bPhVde6!H+; zI^i87KVHqqMFK{yVr_S19E%C_76>izEKW0Py+(}baGbXGiO<_j_^K9o@ zOgC9sDH|3`KNos(I(`%o_!qOTqOWP~!kQ>mQ~hnNbIS>;NmM4W?(5`rWVXei@SStm zI5y3J)IrQ~zj5bDpAff`1>yM>YeVKH_Id-Q2$Qc!nE8{8a|}O zR`O*ot51G+pfK+V6vKld#R2xUpBt=rZUq!2g2Fd%pheE!6eVxPS>z#Uc|5{mqKKBC zvS#Ej>?t|dcKVXdX57B(z$DtSGM^svDZ|Q`=FeZl*FH$-Hu&N#%b<(5 z=&iSZbnH&KR}=I-rereRt)OUNNI}B7#8O6HUdPBgIvM#fnwIa9FmjF~DUb4D1s9HD>jdD4=l{Cqg|Y|Yol8}vaB zL(INEb@U$fs-^FP{Cm-+x_{=Jq7JPKw?DmgUmv#8U^zlGlaarP>$O=j_fgz|7@Of{ z5x2ej_za`&@Ola*BQYMO>#Hl>F}e)Sb`5$1l8L`)y=fyD%^*dyJXt+L5lN zUFnsOBH}xnRy+~mwX9rv!^D!9&kMz5YW+_@|DTJvh&xWHej>y%Aw6tiqpEn_*0 z<;BK+y5(+>BSy{{x8JSL)F-CUv7$m{zg;hK97o-lPCA}_T5;|4ix0BPAD{euP;l}0 zn`wJhzaWL8v3dVU!{=)w;W?Rnerjzl{|x^qy4$~rOdP*V+qLqa*43Mf>b|Y3tK2%< zx}qW?x$=u!Q{8^U!PbQLKeQzWzfwS<<-&^IEZ*5z0>u++Sn-C*RH@!_gX8C0fNDO*2*fAu1aOS|^0S@7PdK`^j;y!AMgY4tfF+enG zm3niqq`ugEZQ0?L1qE^M(tm9_JG~Gn4wgZR%o?`VQnK}x8de-P07W96b4pKE=TuWQ zxD)lDk2Pt(rivit0#^5Cik2S+j64I8Z8*yvFzN~Kk@-{J!ylRGxYKzwtVm(6KiSUm zeH~DoW$VeA-yua-?wX{x**T_T+3KM!pBVXuZ?vR4-%_6K5tng#~cAvlJ{95``BhN6Z*YM6$8$;SC`7yT6 zNol9#G>e}zSUxd9EMAHj`E0Anlf+JX$I=Ws|#@v4OrnfXZ&|&}20nGZ^^$lzF@4vMOvtC)bZB9<>wKuJ3$oXt&O!Dv8 z347{0$4%9{j{E^T3|h#`_osMVyH;)v^Z-({_NQkR*26Qiz6)+YZxtVVXflHyLN>>f z%Uqdv0|Jj0{unT*W8yG5@ASB(?&1j*5ftCz=} zLoS-57X*l6f*vTE>tV%>VxX}7KHzNf7mv*L{KqQwPg{4ZGON+!>(NzQ6)m5IvCN0k z@|i5wAGW4e=Q$RPO?Kt_?r^^dwYr7Wg}8Lj{b^tMe1_$$d$A(VTW8?lJO0?wCy2WF zO>bq%&$qSJb+0Nb)YP9ivUf!t+^Z@YnO}=SW{u@krgDg>zU`t0QwiE%X%;qXwYAMD z`a$ac#?eH!(T6+0m@9ju-|U#Lb?_uKA%5%BV!!N8k;ip)wB@Rque687r=sjs=8(*=G`Q3sp)cg}H|uWk>!uPd8vPPWBYaM{|? zl95mLVB{-$X05?PJEv_J`Yk*ss=`|mxkA_!65bin?M=xIo7*2=!d9t!I)^@1SM|S_ zS#a&%_3y}|lRqv#+gH5$RZgAob5)m3X&Jh?<+sghVvKLA)T0m2TNkPy@FPq)dhvj` z=`(uVnES@fd2WGt{-+Yu;_`w#y7h_ybNc?##~El=2*D+*=gn+&gZY-vH4?yQO) zd#SiKX!-9Psp7Y`Yh3Z_^2AEd4^GO0r^&khi3UiM*d%%PWU%d&S67^F0VH|u6oe8$$s zPcH(M$r*{No}d00t;-bf@2(Y+eHQgLn|q&d;ZHvAnY*@-WtNldagg;#5zE1iw*SOy z%{!*_5Ez5Jp()5bETHsC*QI2%(uFVaJJK=#E_<}=C3seh(N;_#V1=O;e)DUTiDd354hC;JiB zmKO(<^ZxigB7`!jx&~`)^s2Y4^3UH@b|9^9X|MaM$`@aIQq8%P(tPi-b=S~a3fk>y zsM!2nw8Ma+mA-bJX1#g{1q0+hR-xP>8$#Ak`4*J1B;Ebc27lRzgmLuK^);;*7Uz_n znp~2X8hZDQnM>GlO&P7lC;4!bVEzQ3k^O#>J z53Dm*_Z}_^>3heqHTatMjP4PBCtVMFEwcFLxC?QTn(I1=#s;&NtHq~FBER&_z4rco z#^G09NgrRfJUa9`?8%M~=TcsL-S+NTS>5*oO8ct!#)T@tEucPHRPeVrcbb`b$61c) zmSVV&f}hOX4|5(ZOOSjm%4SQ-EVFU_t0;Ra3j}=oj%S9vUw3T z&kXjx&#yxYVY~X#^n#oO0)3Q^_q)N%}-CL!}e;;0I>3tIOx^QKrlpQCB|Iy?Vjuxa!0Kz3caS#I-Kx zy6Fm)3z()anZhwo8m;D@KH|hxq__daH#d%=+)1&^!#d&YUSVSjLOYclR7#Wra6P|TW+3>J4VQEok{4}1re?+Xhr#n|O7*6V%Q~yIxecXt@dU6KDUQ6W3&iR@3 zJbT!t{QMMW=!VW?$a9|W?$3Yx9@VIP<I0{^D;=xy}zQJc71xjiJ$4!0=V=j^jgX zOD7kWswNFDJT+QY*cDEd#0DO%uCck+BBLvHFKa2Lcg{t@@W(0Uw=OqZEj*WM)8o=s zo1^!hS-HHkv1qSYVAc&pOLDyBqUF)w1S!ic_-rSGL&>+YYompL?MG3jrQx_FD$2CN zZh*0~InekiwZkadXU5iI%W-x+P5{Low$B&L@`H#5iiympTN+SP_!Qo2wix|7`aXwU zud$r)ojt#0adiqS_cmLH9A#*EEcIl|Ph2!)fT;DgFDD;$J)OMLsXBd!&4y1i zrBRh@(C4Nl*c7dH_@GSmW}P?AQIdCIzK znexLmdoppli{zA(+x7$DuKkZ*aICog(E5AkU};!G5ciMNhAs`cU>Gr{u9KE;W^2VX zc17}*U0;jXV{609RSr!rhMiIpRt;^D4XK^=FI;pFjvi$FzKH>fiyWZPi(rMBg=%+! zEkBX(-1qll_r+kkSE>IZZ|4|mFUt)h-Ppd7JuSb)`e-1RmQNvk*Fu^L6JwOS{X^@9 z!*KQL@@rLl)7)x5T&!q3xOXPoU#ZiweEp=C%Dp zcMtpa!98t8#}=9&n%~afyQqa;I)~Bvj~m%?u3t)Zprb?SMP@=lQ-kK$y>FET*D?>4 z)}_3zUij>&(lNPF^D8YE-}BA`R^>~1kIH5UrZhVXl)A6nYRCb3YF0_hT=Mb#0rf`r zf&C1Qkz4h~q4D~CLn`$v`u8&|>DJw_%qCu+MtSP4){X7@oENNqmb60Uem%0I@A*M*2taSNqpX6MWLF5x29BD%-iPv&2qr{g`piQq66fakY=c z_P_6g@j2z%;5SQ}PruYxG{2r#bl_9+FG1-)ztyVuC7%S5wG(_st9X$ssS`7%Np7zk zbG{rr4TYw&?nL7bHHuh0}ua}y*Ajl06}DW6|pJtOXr`fR~k=T z(^S5_*Q=~J{Z&cUw~u9Wo1a$^fZsIQ-lcOx54|ZlViUY`bi1%*e7@}A*c~>#qAu9w zgjm~s^qFU^v@9|o3)+N%s*~vRs+IJdUyLa%w^Y|HE4pi0W~^rJYpo{vb(J>t?M*{r zHi`Mn0^Z5NIhNCV z6uY`PO1j%3Ga)vOurK!==bWLKx1dGC-(DRmAj=B*f#p{G5j8Zov-LeE-ZTfT zfzdD)EQf7+X2Li8DEOOWC45{s9Ufy`0}XEwfiDHK=?%F>ZTAtBA+#4wkARr8F$v7J==sd%@Wr%w&kT}u zT95zY8zrM*$D}Utc-xiM1B~*IovXq1L{`zQRB@~0tmztuVWvEbbRRuS5?&2sr!^|()s@? zF+IzFL5V(Zg)e+oa!ub>3LA?m#QkdBrF}Zhq_60&;vv#S!g!}xUb5e3B^5Zpj7_ z{dTz7rbhu5X4hm^S1pT76Ps_4}M%Mpb$WhI(-Qg7Kb(TyHccz&Y~!QhZj$tWSd_xD<%8v zJbu=&*=gDRV(#Ym9Xh3SY>-0PY&{xR5I*7^wTql;K3jBKmlhN}`@z~i!aHYs{YrET z*q97?)P9(3y`bzoZ-Q2f5t4@;y?s0Db%Ul=ejbxh-ZysWpS`i$tIv&fX{riart0o@ z%J^FP4Q$a}6)dkFoL;0acI$lA={_%qaeq~`(Cuf-Xy=)XqkUhibCv?1 z!;<>m-n=z~j9^oEAHpFl*6_V=WtY7BXLVnv+3GBDiYAphtn*Q4np6#!sb>|};gXV4 z9$tJ+e7$Umc|79MI~zq3W?w5;KROZzg z4Kp3&e`Y%%#TmAD3pZI@E-4YttvV0ID9;*q>0DH2pz%!>(*CuiokdMQ_tW*&-e=l+ z`Ggq{d)(r{j*#_v^U#y~QsAN38mxszcDqKjS#=7a7GWSEf;CQw@$!;I2v-s#ZRkWgKHoP^0z~V+(b&VLC z+EzYtS@XxR<&EBf)s5%vt|{H%`5lFAx!S8Gm3Zl=NVw}&cLA1qOnNbOvqjvityT#+ zyR6O>rdrHvnr=1*(+jIaC|BS#6FTEJnOfWP7{0Q%7@HbwGHJSZ!*{yJkQZ!9=@?OO z$QhC#?pPDjpuK|J*Z9CAy2_cwdI8lBcgI_quf`b8K*LP|Y;YF$H!8$k*fn7tRnEN! zCqcP9Be7RJ)v(R{wMK8DRb925p;~D%RDHrMLAOZs82970LF+)Y01z2wR)*nLL0wTc z_d6$AUr;TR_3oS@8mYmMy}EF6p~+dllU&ug4}f+dYHY3Nj%{z}@9Yc~3U#-HdeaEu za>`3^94z8l!;?51_&0nT1cNPfCN+cHOXT8@vAKAxX%n%)SV;Xgm_y$TpU`Q>SpGOu zgHVZ8iemB<<(^BcC5AD z-&%~+HM%< zzU3IsK}lcEE3O+Sg{eU)(-BmnZ9_(=TH)ud2cR9w4yK?vh(4;^LZ-G>;~uJb>}}U( z(+X3Qv6?m*^EpRN&&3b1V9OrFPdh8h%h{SKceR2(x>dmGu4d>irw{0U+t(;xhM+42 z$uI={07FbW>AcQOEEC0+j-mCC1IoAkruciW0q8bIPjMnH1@Sf)< z&n}_m|e#RZI5TcNf32jSxUqi5w5J>(Fn(e(EEdh1~-83<$kmrzK};7f`#j z5c5$N0NEL5AfdQ`lSNf>e4$mG5$FS?nQH+R@?B`5a5`~SI0aiTbT;i06c~T<0!)iI zw@eRVt*Moue%N$86?;c4!9A#9L?At!{7uiGn&=ty zR63fGQDP8Bq=A#zNQh^82l*L4Lp_Z9pb4f*5RZRkJgJ#<9&?NQ3Kig5Sd2Y@w;Jof zBttv(Sw93X)$cT1HcZeWrbQYq9;Mkx_SVW6zVQ+iLmogj!9UPD{BTZ$v>H8Y@g5m% z-4hPA83d+T_n;Fke94v4wKyW^gUOIm}dMQnhDw_e5D zsi8>g&WBw#s_K@fszvo_>NC|Nbswt!m@;bkba}%>kx=m2mKCH-+Zbq3)sxX#?Q)B@gdZ3{27brq_^H*EI+2GoA=OPDJz=1q{CZIn$g}f>Dwz$zpPh`NB3&nN@|Wx%1D7 zl7>7xfp@M2YL=fxTND;!(%PH)-Rccpcfn5e1+#+AH;x>5@aw zw$4MU1nc;|bfox}wo;nf=4Bq$tTQWbijnqjT_q}2Z|A+oYTJqyv)tLKDb&BgV%~l6ed(vX0F^?ZhsHy8rbyqTSLm9-ITJuw2EFU6*%NB}?%Xs?_8FeY*tIOr-Oy@+m=wj&$6p|GSzsOdbQ|7qUGO3GQA7O(1NwnOq zJ1w)>YK)b6Y51bpJ zNHqa|J4|8TJHcn?ef;wlpUtY!WNRD4ZF^⋘ensc@7&3n{0ah@{-ZNPYRBd6#-$x zH{&$T>`n<7sk|Z$tDon1zoxtQhZ<$~}gJPy{776BQhQmF$@A_@gEwenom+k+01JbJ2WeoIIdI9cDUE#Y}?Xi zZ}CI*UW9aAMLqSyDKFDfgNQh&&ZXSj+@EbdtNn0>cQ5b`Yp&?V*tq?@=|x1Vd2IjG_-VRJXG&UTsxbeg`cq$!Hb-`3mUvOo2}}$-)dWT%yzKfQu`{8 zdOH`#sWw+-wiZiy%f&OOZ=7IVHhsIj%Q&jJK{KzRTSr@cKx=-ZWy|Xpv~^nNt@c{| z8+9i6M?V3%OLz&+Kp!MMd2QyF;vE)W%>J?9T6oHKTg;J$%hn4+q}w@f1+SRF=r(L2 z-AkKgn%be$ZdcA!do@l`{j67Z9Bbfq&T2lR@oS%~KiWlOR=69}4W5jS5%v%mWRoPf z?G~F?I{R3RcVBAp)MJFK)4f)zby*<#Wj}?mxm%!EEakVW7(>QIJ=g@QY8Ppx*lu zuhP90jdrYtgqBHkyC9yZ1pk=gO;(1PnyI?CZA-Oe^E_>Fqn$3Y@u{9@G#FExbMUa% zW@>atAoN7N9i5;%$!jvk2 zv)Yk6yK57VZ|K2K#e491;1KT=Cy1LToPye$xxjuFY0MGJF;s6$SHes-7~3lOV9emZ zGaN-`8&=c%4b|9G;|xQ#sh93NzCrt$6zgo5X8ly?E!z$7B$en@hUC0pH@z9q1zd>x zh4Yk?iMpU;k+sl1$eDi3Wa6Ea&KOUI8FhcqOoRiQXLNpjk7s7Bi0R{wW9UoPZ|E_SmCQF@60++!s$&}NKwrb6ssJ_*fvuM&~GX_S~A z$;{ACgzQxwNRjd!+T3`CbFfjzIo|x3bE)k&IzY1r`ED$SuF%#1PgaO z*+h<6Be6|#hCCujqBfuwOg6n6L}G2wJzW5DNMnHpsIQ|h)cL5j_BP5fMxqk(0dfUW z!u~u9I7;jRMVt2pN{h)%mE~?)X4yntG~Y$#h*we+?-}(PUQFMo)-k=YRFI{2f}d&E zBF@?as7x2cS!9UhG+=3HBz+R;iTr^E@V_xqNh&p7mPw>q^~W1*24UB1)uu|D1T4ks zJsvE3NgfgRU}!EME&y%lc>FFeQ`;su*l|MSqKpx@*E@;_)n*B2)Xd>;t99o*ZU}{* zwCG3|^%0Z8xL4PUvDaMWUQ=01=61MSEbSO={YW*oq*B#z^O3C%vN_6|<_naxbYyF^7*T!VhiD(7$FYgv1dx%>d3{aOq~?0P zWqVhZtyx!weL|Oqy|+$c(_uJZuE2$YJ+wF24Zko1azAu*7lbtp5qZ^)6MI&Ni?7wV zisX$FLBEbcT#m5=&WE-zo5ca7*k&vycP0(q9&-IH&+j_e1JxgKwlk#I)Ei@@!||yc zcj_5A6nL@d9MoyYjcA+7-`!Fw_}#K!sB4QBp6+xJ^wx#)mSE}Vce)L-Lyhz=zCT$h zeuUMUJu~LX78zDseASo9#D+oU_QnU2VpE*Zi8#i+Ma_dp0DC$aHeh+^C_@Cdm)4x8 zR)_PlJAZQbbT)Cq)i=-wnn>iXwgnOzeuE5?1Y9E?GUwWBHJy= z2VtrL`m*{5xk_ik$75@-O7>>ZX7rLVS+LnCmI6~BnLYN);ya#aL6VI50p^q>9RA2} zL1AP%FOwW6IAXXcL^U$ex%LObH!VvA8Opglc}prPY`+gtor`HkHGOX==OV6WAy0LVj^SaO+^1fXCbs-X>Owx=fQq{fx_m z3PU>okzpqHrEx2=8Jh=6iEdO1-4`!_P8cE1VLjlF(j5{iwQI!lbz3A^`gxKU#$fSC z+*TMs_2PwqPlz|-2i|j%$Qs@=EK)Gn_*+2hR|_8Md+`?;!ntmyT;wut1|6lQ({}+y z97OE!W{%!;hPTeNj(^q^&!3Lf@Q&f{xF|V;bCt?N{Fw3ZVZebNLmFl=Y{{g-3+W`d zi<$$UqROBsYCgoJNKi^PfET14bf3Hi6_Rt|II24mOV=USnTaUNGU!b>6m3Itk!PqS zau4kfry-M}8fZC)VRkYxhOf=tzikxNV|Buwt61mGHN ziSofS-X&<6up3e;x`zG`<#NUg*KybL4stIeG-m=+i8>Jx$WCJvMC&4%@7kf1z3vI2 z)^EaPSS8k*s=*Aid!ImY?Mi{S2tqPgpdH=JO@9WVwi zU~(@u8qlWGNUiBUCmM_4y5M^_@x)TZpIQLC88yqPcd>zprBT2s(XZy(7{>Ep(`nu! zq93o0naHg~wde=ISfoL+1-fVcn0X+>s7&+IWQRmSEE9afZy?+8Zj=H)WN5>WsBaSQ z+x{Vwm8Yok=BM5-bAH=yK07TX! z!GhnXYZCvys-8EZy^6c7Z5wA{`%k1xC4yn?NHEcKpI%J&pap<=y8r2-@IdA{nR>&7vxVV&VaR5VnV7V=97fuuGY5#&h^J zQ>C#33ou+FQuT+ZJ%%gH8q;ZLC@~YcL1&}Ba3W_N$BOI6ujLp7Jvldo0@P7>7+%hA z1z$N4Og#LNYM~F2ONcwfW>X}w)G&ZproTk2*N-Gm7(%G+rinB{>|_e4N8mbm0}Vv( zz_U2>5t!G4aQPAF4E}i3hwqKf<-I`qaJfh_>JAGLJ17dmnSD$%-AHv(2T4zA2XUVK zjgKdf;c;XZ{*)X=gi*sugg!)7(E{c(lMJqaW$;W0MmwNUoDr}ScRJjUYXx86?18+| zXTTOtXU;JnXf>&%=HTJ*z9d3KEOBW)5tVPi@ZfB zF;*@+iZAFvzbe$1K7z{ZoSaBu1=v5y`> zMS-!5E4&-ZK;9v(Xg+5HXBBS(n`r>-4#_(r^pvi>*ZkWGB~`spm(*-G#QClfv0N7hya97yqi@Eq9Fo zMwjpf&@--%+Kl!haELK&Wo{Zhs0iagqSN>ZJC5O|E~3elMIXf;KtJ(;=ryu8H@b}m_RTHh`>RhW(q+rV*%Y})o2en~=;DhlUJ<_m<%Fu_C z7KYixP-6`qhJC@G5)Q;_`W0)5Yvc(ejk?4sq#bz8jFkTktm4N*gZcH)QJyQjh#SCm ziOt|wa6L2!JcWv=tq|L#hN{{AbEDxOa7yn8DBVM*w|*cq*-%XTn-0*o@riUiHJDxt zM$waz^>hvA16|4UV+IOdGL3>+pk3es%@kBa5&ZM;7j8Jh?wvvqM1@p=DkPOYfSe+$ z;7d3Km6=+>CSw|N)^L)ZY}iKS7%q|RMv8ckWfH;Ud%{3_kvixL`31dC4dy+godwSs zZ(%B!COizi5QM>@{OhnMHyz%EtcR09Go+?Gpp!%xNX14m2Thx3ds8vB#rea5zb4_ zBb4NLBQH44kP$7SKOymC1Uw#BfK#SqdY{pT8f<())EYnIqp)fC4q^q~pT_Zlkck+J zhEWn;GCfk@1HKA(K#N4TVK>n>1Qr6cC*O}nBn0(GP9TdxJ6uB9!HGl<2*N{wIW~o< zH|?c&W51|hIJ*y!6jRMKOa(%rR4j6jvf;$iF)R)>aUDP&*8{TP2Ev7CFcOWJp+}$` z6k`H8`{>@BbyNwOL0X_oNDVxR+zo9e+nC4XSh|YLAOW?UpvXA9ldQo?$xm1oIR^hs zP9$2%GI9v@oX(~`fvt2d9LrorM+1mE1nS}j!=b!3xSAV>lySBpbI~YdEqocC3a&%_ z>9JreSwO$T2U3YxGGU2L#2vBW*mdlg={kPQq$42g7{y?b%q0SXlBk=oC$K~x!M!*Z zoJNk3>&ETPXYVNS1spSe4)TH96D~v7g6r^OIvU8R9&{41jV#9FiB-5SaR7fn_z~@7 zce0e;OLd1(<|c9gv~yHYHcx_7@GB7u!6f-Px+w*`FnxtWvCnW6ejUjm`lIWqljt1g4muH9i@G5M@(P`a)N&5P zH@L}A9``d?%vCX4IA)9;8b&XJ6R6W5iF`}bgpis}TqX+f1pEO03fqp4#y;Y`uvx?` zY$BP39i=AV90nyWf*(YG_#K&qyrOE*T>3eun6cz40mf;7)^I+;+t9g45b^?f1*IY$ zUMf87=0Cdq|2ZQOc7kc zoI<7p1M(e+(Voyxy2PmGZg3eLf;aaMSja4zyMJwTC<|KR&kkEE$60`t5 z2}UB-430!G>(D&<0(yX+fv%y`5jp)DenW4Cc#JDp!Th8@FwZC%Xe7sh9prkjf!q&1 zvBz3!CkUfgfBU!-?_f?-2#BV( zgIKDal|2#S(08DdbR*=!&g>D}F?R><@ByHQ3cw?X0wW*@ddKP=0``N;j19174ltK! zk{(KLq=mGMHc@pHhc;3@>2Y*Ct*2ko1&jmJ4puUD@K+`haR5Wm)c|mwf=L_=2xr;h zJ~|wFfy6-z;rUQC7!M6)dazom!94OVm`02T>A3FyDeOGJ<*KUozt5R_69@!Cl_ms3 zM?wkGRhke${1B8PDpduggCL0b(M1HLmrqavQdNq8B7zD=5Tq+gFG2tXK}tyOojL1! zfA@8LXKwWW{AZq-GiRT@S9#aF*4}5&+$7E#x;S1M+BU8lo)AxW|JLl$JkzY8d8b#c z8nQ}zytz8Rb60hLbzb#qY*j7R++B|Cu2;4i zJ||8b>NYzJ9oJktv|{s(;rW_hbXRWHX^v^`jluY@>c%*){z^HavqCjEaA!4k;6wGL zop08EsE@0!t3F>JP?~D#IHvk%Pd}RNZdtynwch64CF0!f_04L%J)6avO`4_RW3um% zX5Q+j&9AD3Vsd?a>`}iPJJ(;(dj8JxvueI-t7?tvsj_i(cv-4iv;4C}Tv%RG4IcqpD|ro|1-SL4cN{rHLQ7anU~Zsv+7n{DF7=G!qY9*&R4_;OZ!Oyl+` zA1G&+-E=>3c)7eBQNCR^D+iY0m{QJ;x8r?rY5Y&KL#)(v;=bNZ%{jeOn(y`wXnwAB z!D7v{<}C4H&DclU$CQK1v@*L)t#(lzJGI)eKBKy<-lw{|o>*O>e_CSq`pEK3wQ>1q zHMyKvwk~(Y1!dt_pgOoYubSOks=m5+a(#a9clED&_tuLyC)NMcRP|=DaW$;f?oZ2S z;xpCW%?$0^Y*IhhJ*Ymq`=xr9?#lHU-Mg!~dmpau?Oju@)1L8laavqnJ`kr@&o!IW z_cZs2&tp0z=I(49kJT5){`Fh&WVKOQz4~VPO1Y?98dsI4nlsBX&7S3(z2(X~-M_?T z-P7Xa?&fh-cWf-!d$zf`_Xm0UZ1c0|Hh(W0#j^D&v3=)x?d+^mCJvrbE*zX*?jC%z zoHO`V(HvXW8@ReG+}XSQxEd^z%SCZkvq3D}d#RZ|{Qc&Rp%a=FhmL55W>0IT4!zvW zJN(g@+`UQpv`kqcE-5!^r)kCdbJaGT3#&y2eq9|ra9_2{z#Y|}JEvCf)Ss*#sNO5b zmJ7;%`y|tRPn#s-4v03wAIks84ez5sqXTx}{ zb9HPmu#8p$=a(Zpv&#PU$E&5PuU8kwnbi`_x2t=)pQ-K|o~zn$Sm+HeQ_dQ`G7j#3 zAg=7)Am)5tcO9!VcU8+bkJpWS@t(sV0UskWLk0t6mo8MJ$^rlu*dsmd_yZ?wQx~s-@-9wtc zcc1L--P=dsD{RzzwArM0Tb$9mtc-0=uWoAgst=6CI;)l+b{;Qf;Iq}lfnQW*;IGx? zorkNz&IQ%vdc$f;b#Ga`e6kEGbLMTn9vk;oi#xhioTht=+q<)y4SGw){LS_;Pn;ef zDEGw&t9i=2_2%VT?Y_^duPCq750?|`*UNKNl)4&IEmbKYn^iGg9 zJCtuU6Ux%@r+6{Wjf=`IF{PRm->F_}E~svAcB(E`l{u{$Q_g6Xh-;gP&2y>=lVbbs zx8v5~_hPrYCWb5l8C;A3UX z;O}FZ!9C)>fq@t^a98tIeNJ<3b$qi#Ilj4EEZ(;F=VtBh(s9V}!7=~v!?Eb_I_1RS zZlMyP<3q#RMX1~<)-SIvP->ZxvrkCOt0^X@771fTJ`2}Yqe~wS51iD zmPO;sWr`}rPO(HeHI`FOZ&cokJ<7!L4Pkm(`C>U$?}wEO%hYm1S)}}?JR5hG>*MY+ zEv_#=QU!P-zED;vtCh3Lt1-5^GLEah6i-!a>K=A{Y*-J(iuJg7t6D40t9FY;s%de& zBs?9Xv2qq07oH!*z9WchhRoW}oWPIHH=Q%6w`)rMgA(FBmwdOd8z0 z>@YYZmKgj&oHMX%+}K$oE)njZt~yaEBd%=bjW6^j$9CP#;-KNf;+diE#YIEEkDG^P z#`430kP z)2Yg*>t97(9Um9O7O_pUa4g+>quHSQSo7oI`pG})*;UQ-I7t5l&ZnDw zW3T4GIHx&1o>!&ZEZ&M+#nI);mg3|t<&Qd!)mnbx>ZO>!x*%RIQ{(4lxH-B^Z$46f z(u^;^Xr7N*%^jL6uhWj(4`XLB_tkQH98x_KGpe^^?RuHAXT5g$Ouc`DpoG^I5G#PAhxHmAd=5L;r5% zZ_3U^|BQ_0&`Zl#%hj48uUG4pnxQW%T3eLw#EIo=agd~LQx1$t`utR!p*_Cu#|CkG zJlp&wPHEnVO;l$VX_hSuG+UH)njOn`nuE)@I7aj0Nm}8ZByW!>*Hn9Gb+(aaQ`b?eN$Xh9T#&~+r*Ydzc-J6Hot8iZB}b;ZEosa*BsRQS+l#U>#@BO5BD~XjfLm~ z%`5RGt#9Yi9QuRuYWZNbdUbKNd-Zbl&1$^%0SD^?tG`s6Rwq<-wRm-fD!`UyvGQ14 z6km=tVvHhkV{=P$cyn{JOY@?B*Ed;f%OA#V&EjQ#)yKo5r#17|)%IoY>h7{*wOF-e zwQu!{a;DY~S5>#ih1Eym(CW|4I;xznmTx!TEjKqCYBljnTo^xyP0N?#?`6w4qxxXX zU#}he)EmWV_0Dl*ePpaz{~&%}JrW;L&R?e(EnYrVE{>zhnsH8frMay9y!nA*f1YB0 zSb47bgjTBWFQ1RU#!vJdim%1?Wk==o`jY=4t(w=5ovR&Vzv^hM%x{QKh=)s5+J7jY z)7s)&WlXuEd@pV+AC7Cv%gs6En&yCVQnP+JxEZHv{8(JrTpJHIr$nuF+eUFv91%y# z`cvb+_>ty`-^V}WAIih|%iLw9vS`_`tX#AcUp6kEEgNX9wqiM_EU1-Hql)^9D(Yi# zOL;KPD8GwOl}Dp4PsSDTUTmUu-d~%Q$_33v<+IJ!Wy@xp@=0;yo6Xwgk!I<#ZZvVD zn6!GiGk#RAip8o+e1N0nyL1QTANHQYnT1Y&!uG}`E+0N({f;Q zb6HE@q>XP*E)$y1mkpa0<>Tw|UUNen7stmU!s}fR<@~5)P4AO%OtHuPm7}!%YDiX%9H(N&)S zD*hn<{uIM8L;3K}IJ0!hU1i?#hLD=KTCz;4-d|R#mMJS&%a$dpNvhXNm*;dZ#w>J{ zUK@(JZ$_tFBZPMnPK(96@kiz8EzN6jN%M02Sp0rKItSwA*fCyq9PJluUY=2iVXx%Z=3 zvAIBX^f=9g$EcPZ5ns`6!$5sj9Ilnsb@fAfb>pad1>IxpAb$=mud6<8ro8@MoGsbM zm-Cw4RO8pu8o89^b+@yX3!pka$!u;z3zHdUDnkd z$}-hH2rp>q_<~V@=Jf+mstr z>EDTcm0|m6o;aYKPVyyi-hh5Gmy>`(Z;5LfN`pTh&luK`k zufK~Yp5HOhy}?y5;g>+^Z~8-&Z1y_bsf z7nh+pMeDS$$(PTTJ2a=jVyzm_jbqFCl08jc-=r+QPuBiTztkNR7L&x= z^~>do<885>Wc`;UY@!{OwaON%4WCyI?b`wD8?NpU$%{CiTBa-!y=LzQ_y*4p`H zSvscLz06?3MSbb316|`bqh`Rr#3arYTy_FGU^J z4EUPtcupwXBM)o6570eG|$(vg|>z^UuQQC9&W=-H(is%*D&< z!gd)+TtF-+WmDNTRT1B?JfQk>herKUalbL%h@Z*M+hpzZxI@voP0YVt)#+}{qK_+! z{}Jmdt{;_c+sn)C8B-ahmqIn!ggh4*&>oUro6Abs~9~msgG&= z-QxSNRbze~X9?BgWb;YNtMgNdAAz?h~cs#PUJy z*DR;X_C8@fLGPu^RIDDB$G0fwFI8;LQH;)1tj^Z!`>G*772?0u^%t}+G)sstBs4#$ zxU8@Ed_r1w5YnHOrMv2-SxPI|WmOsG*HvtPPf>eam1%|~KP^O_i<@=4QRhDz$=!E^ z%8>MrQRL=U<}9Ew*#5qe)$~qc!LHJ^xA?KM+IEoc4JCVdz2*^q|InB}ikrWVUR*0w zuaJ$?G^bxBowrEq-9qgd@$6mcnosMc52((qtqS~6)vC=#ULDC}zh<1Q9zXI7u@bp2 zDCeILmVeRfae4lnu$>{fZ;LOD@EE6vFQog+B_wYtjaXJWx`c9SF})U0y`EeCcg3Lh z)N@A6BkkjKw>hBKSVgl^=GBr=%HM_Qkj{0`m5%)C$$|mxA$N7GMk@bUeeOk9{f5Mx z*|K1^JR^gKbZoR*>8XAfS21 zI4pl=Y8;>6l?*hz`>*G0>3Th?Cdj+Fbajm0d*aS)X?;s}fz96}@lDBnU7u%4-)u>)w$k@=naKhX>M~t8n{0jhL-AwAl)BgTCYj z5;*d|LvYlOu!ML=@0E^&@)wCSU)@el231P zkDh4nj9e#IX~mj6^2ra5NOb>@CukzWk5{8zb={W;ID@XS1X)*vj@pOxINwxKG) z0`e1EkOo$qgA@_QZXUFQ1pUo4#(^H^un51CMaG+PV2MZYkeUo)_8f1G4tkIb$6O1z zmS?t(tN4K)Di#r@Dq$b!po!W@6-kC-8M1jAOV=h>wZxQBuFqq|H!PZ4n1KSU#Y@;q z>|i$%;V$hUOgo%)^}`~fghs#f$yVSu`e`AD>5)|nY|US;CKm9KKKM%JITrXre;(!$ zjD{aD8Me4GGS0n_hbGS43eNN*z8wAVHSDD?R?|N%rkW9fo{k_$J1zL>7I46htft^b z4^~znL_RaugDf*TbpQ@${<|)49i%~map-}=#Ji0g7m6=Uo* zY{7e@0)xiX|aNQTvLkpAYh*B02}8t!Oek>lqqH>;5hU;2;4 z4U0gN2Pl|1eDYad<;c}+^(SCes@BZfZun~TJpx`OB6?94Ss&0Wyq z6MTX>U_w7;S|5!u)i3=fLqW;dCy((Q@8EI9jtJOdM!=%1i?o9mSJBIn-e7=6&q_YC zXN)k8DHZ+T!_gzN2gqOve9R*@@l4F5Xd8%AmeNR zTk{*-94#}#s2fWn#l4#66EHA(_yBU|G3`W(D1aA;a3z_HRw6-tVTSTNge0m^M#*sk zDOY>5m6-+BVw)L&G)9^^EmuG}^857HS zU_mmORRvYA8CmHwi#R82OWJ@}phKjRtIlowV*X*qr7AMs+-!3|jsLf*(1bKl$8O@~Gj!#ufOz-{G2sg1c$jnOcg)aA?2y4H zV?v9Wz!;ceCUL}$^weB&Z_7l6wJ|EuVnj-E7P&Gp(z#{}?2~bF6p4v(A+w!DL^t!y zoC0^ZIS0JDL^kI% zB}*L>&WR)*w7Jf8v!%7$eUM<(odd`xhQLmebDp zzFC@4#OK7+ti)1A_{=%#c$(LE#t24&Kz}CmuMUud%^6uo+vw(uV$0ECjG5I_7YqY) zBaY4KXXc&9wU}n6xNh0&90OL_6Is{>B4|jB$syx`-t5CFv_-zFZP6uRv< zvdBwU3)sS{#H=Bfjv-lt4m?XnVk4i;yX=Q1>Z(_0*@`zHm0I9V#t9#61G$lNKx>t~ zVbx020Ygh=cZ+D?2_DhfMz7z>yb5C22NUhRs}r$xec_s|aiuqI*vh%%NmTI0^#E%) z$A%oM4zh+;#KjqRE!v&fNGXGu%@oJXFm&}9qlm(R@(=MCeKg7v=Ydc!Eo zNJep;OaW`pvc?VFM3iwLVq4%UE5O{fLW?;G{uyhk1MOZ#&UtLH!B(_oB=N?qgr~^$ zDuE;N9EFT(dt_Ad2W^fBo@JD5k7q^nCwg98pw}_Y3SquU(Es_5J+6D;;k9HQJ(Kj~ zY(Jz(4@b_{f~h|_TEr1umf-%*KeG!);9Iht*qM<&a!zEh6u&s4A^B;!)`@f@V@@%O zHXiLK`Vu$RjGjk9luUE4w6$GZxbop;wt>5mG|REV_}OY}c8(eyTV&5O(#hwHl~*Z7 zEU`A1TCbdGt_vV%PNpP~bCq%y82fg$>g;g#gRC`k-7JSq&Me-Iwex~2CW z6{3d?<|%y78bVD-7P(j6cfyJ`iaA@VNm|fM-XJftl`DQE8hLaMBdhhoQRF-T@o_xNcVr|#iB=+#SCfHU!*a*h zBg}@BW{XkPUJX$BKTGkcBt>_*17nlV-LWWmmfN&HfM( zBp^p`I&&|w+SMIwEXNgqaYn;q(o-_uyKQL52xd-r1jsnfp6T#9`A6sTJVaum&>q`QV8dC)4UD~e0$$`}W(qyr40@!b{s;M64Nj1fZ@7I@{B`G@?p7-^Y9uA}zL%t_Qa z5+(3(1tg-bfJ7909D9$pbYyVV_XVs2+ntC0d^Wy}PdUz!tVNEBrP1b@ka`Vn&Y9H6 zx$8R_>lFr-fOuwK=OlbEuCC{-Q@rB!YTaybMqp1Ho3u3ZA=g0WGd-<~Ppn+XR=(#k zCyb)mX6*2wwN58`B+|@-o;4jEQ0VuVzMuxfJj3SwezMp(Ykg)#Mi^8)&i9dtC>&}d zp0y``jo~q_A+`p*^RC9L97ea?u`Dd*Vf5^=QAM9~!L2zqdvZ)f_+<{3u*c$fIjTs8 zQ@*b;OXk$;p7e5rU2$60F$&iBj-?+lq%+!?;#Ip@MLRtiX**js^Qnz3u{Mv0m*dR{ zXAb?a%xfC6IW5Set4KR9pw;VV@01XOoPk_79IMs_+ltL_z;ek9Yc`9x!YgrdZE|GH z9Il(?AVD4AJ8HhMgyD=E%}}!me5e$zhj>a~XCX&qrhGIwk3C!AxpUa8=Dfd}ILlZ$ zwpn0?$`R>prKMI!8ttxVw1Wrzv7*Hs3%Gw_B}Ij01blB}m~r(;Bp@eqEhSlY%Sjku zEWo5)DYJh}&T*Xw)*#W9+%qKB(vsXy4&|(6pZ%Vgwk%+zaYzeNj-%XK1v890_#@Ai zInNw1+FLsvDO>03Edl~~1UdTiLZ+){uCG|J!8k@{H8!V>3FBcsEW`qt#Xn->Wlcu`VS)nX~;( zjlIqqn(LMH)w?2z0>0<0O>~Tlqide0_r$%Sm$yY^>SnI_}7@SDrhN#Upu?+`#@eHol6SjGFmr1Yq=>JL*KN^?O9c z$XmZnEfTh!Yi;dwR6)G;#CBSmtx4RIn`yPjm=Wn)p5>UVYmTJz4V#hQBHQXjqvzYK z^+tgh*k7a8KLREsQ>c7ishBfd@ji^d9p`ts?7f*qSdo?3Bhl^Z!N?Y0v=dj3JdmEN zAFutL>20onve%^e)X&b0nLRS!T<<+MGe;-?Knf}7a%BM7T$}mmcw-e>u-TcBJaWYG ziWW=6PJ76xe(UJtz8Oo8a1G1-Ij<60jL_~%LG^{RMiTFmWk$`~j5o zG}tEBAFkSSdh&34a=_NRZFDqjt1F3ZPF|*z?m;fa44Iif>M%(&64wvC{i zIp~>%`8y+SiJ*vn`!$*e^e{7%|Mr;?t!(q2h=7=}bbMOvo=v!xQ5b!+Ci~!(E%Ams z-WxGf(BSAGC$q>|mHxL(?60#eOVUm%Kx%bc#?#(8|2@a#y-M;w^UCqXHfo$_ZtoN0 zxv_NaTAOEjEaW;;Tmi7dzB%(!J{D&Ea6TDdbB-|}za1-B~ue;?qtqkIQ#UMSe=Al7X|^T5ch=9q&k5L&`vc zYg@MTTWwE4j9*^N@&RU`?v$KGG1hX3^fnnb5b8|nB z=D88iamhVK__J3?+{JrWg8tycJZ9-e(wUaba)eVxRy=35BhP&fIP%+T-<7wv5?gDx z9A}o<2p3&Hz=~dO>CZM0%N*$ES@N&-*-=M|GuBK;CbqU!nsymFqAz=ziD{d4VlPqi zTF)Ew2*|dV8(syJc|1 zU%tuXZ~543W=Di*;e9(NIHR3o@Heq(Ig+a``ziAz!R#;}a`on8TXUV=_}`B|c?=S^ z!=LSI);w29?rW%WR0Yp2=CyUQ9&s#<7|}r%ZT)l6f6AYBhQb$f6#x5o;xdo0(h+fm z$@>-81{pVU6)9k1zF2;GgI)dc!VAZ$tri~dYL*q&EKYn}E&4fOtIRIvJbja~mX(zv zt;?h1XS_1YJ<`0*Up~!wWDV)n0c=jJ-QxM*nTVaq_N+^egi$hXAY(4Jt9ztmg|TJW zg)}ha$!B^n%WzCDs3oq(_yA|ynbtjFPI5SPdz7n-r5iacbH)=J``Y?u=D~%`jkFmV zt&gbqwF;mdL83-1kxF6)bKpzqM<)0!HK^8thQcrg#K8XSYiqG zq{h@_nU-#Q=wtLa&v>vm##%JL=t|cPRQJPb$ z@nKB*hkUHyF`9F&XTHvu|5O3ZUh@Je=zs%re#6pl>wm(5v7;Zh;Q!Tz<;*I?hBY9w zkRy-1{h0}VZO*2b*l0VD0~e_TwwlU|N6u+~GuM8T#5hZ_r;c=TG^K(BdXa8y6G4uN z6Pj~=amD%fx{Efl1}VhZzWI(fD>|OEk+gK=nT_BCnxN3mmb98-w6cEYcP`A9NaEeq zgilEH!^~=DFL3ei*u8^3y5d=%V`RVWOa7+8mi5m8xpqn1K(R&J5yuAX>9@==#ye(X zc4>?(J?V=K$J;FFKeumqX&(5BhTOZau>#2>S}2I!YD=(`_X>NJlO~Go^^6} z*LBal;cCAZ&R)-djvOA+$2HVZ06*t|*#Z*I2DgJQtY(eNUu5&wwfuI4d`7P$ox6Qr zIix)qYb^79h<9o54jl2uoWqa)e8nOe`+pBx$tgbMXvVZr>-P%}yn~r5bk{leP8>i0 z&e&&HpEiq9o^>WKu)&`h={s-pz*#?XdBpnE0)m#ExO#lD#5tGuWBofs$pvSmIbr)i z*gS#jSlVhZlHSMiH?&?s;Vm!A$qaY(fK`rX#v!YK_eNd$LBd=&L(^u@+-+WGPPzp@ z&1SDlJY&NCe#ROtqnDr2mVP?_k>pnAAxxlmVx2jHW%y~1Bh@nkwy1GrRAv)Ldbsxb zUfRD<@!u=iQv2;%gH+@s8nj}m8Dm*3d#saI9*lyExx)3@KKBNxO!ln5e!w(y-InL< zM*etSB@1DIuVbsNvlK9N?L@Y74cnbbxy}YVys%93Dr*9j%DL;xmAgG2;YgZ8jHA_O z#NBn$jA?yCyJKNX@+>9dgXOkW5$lLG@3zPJYo0kKEl14-$BI^C(#FjiTQ<3t8AV&? zn7TrE_sz4Cady-|rx>t}*lj%Bvh(H>npd`AAT7AJb(&7(!w=k~)0qS+RDycvT3xeEx6?qzP+?)1tt zo6*Y*@j5H{n4|dq0~XTW=Ck9TSd8{-bW9TiticcPaYp8MKzL-{q=)p&{${u9Bs|Zk z&xul6lrhO;i@e#}&OW2(>ejkB8G~((VS98R-qC}{#tlTU1f-~z&PL8@M@o8XukB|W z(|+BqJB&f%=<#h0Xe|S2&kdl2w^!8nus@HGl$ni9aUjsxDLY%|<-D=TgrQLZ8r&DjS}(mv#6dmg#&nCDF5_~9$wS|Yx? zU%v&T<6zBRm%~w{8b>~5g+YpOC1$jPSifb-aUup+z{J(YtV3$9FYR#~M|+a17b1i< z>vJ8-*(Hx{?Z&cksIl*1HkbqEo_RtSq~WE%mxde8#8!8^!|96Ps+2wxLGze*qnH)w z-&f*}f^GJyv+dK?bJ*wzxaJeNIqmdHiEo?zyS!XWm`fn-6}t1mo?=&W6uXIZR_V5f zW9~f5cwn(BE9fL2eP8F=>mJ5{YT+ES9oB&l$uj44|D2o|$k8~X+t&LW3*5{6vHdNg zquIsPIUmUxR}f^u-~8P-H31IwBZtjsgW29YVef&}4mN?`=nQt{X|47=WWSN*eDn1@ z?`J31k?e8siC)h4tP-iiY{P@RE5q{sdjq)PJ3CrkzY|09AhQY`t_=9q`pLiN<~`o!|hK;xhJm9rDq zkY$|vd!mDf^&}FG2CeAuuPeM7Kn@mx40+~=xb@$&D>JY465pIL$#pPna~OW{Ag^mB4w}nVsyO*u%*~Nn)UXPQMQKbiEMgn8>ylG7gbn&Gb8$M zNAp`!OSUzPoU=x9ooKXkvvESVtBAi%U^YmbUFq}QH2<={wa?7RyFIF^WuhMpj1-?i z*scGb{iEZW=o&|ukc`fK6IV5(!&n&L{YX~!{Prz*>s666)v+aa&H1b!mTHVG&2<=D zuo`XNtpQQu*v8lAp1s^|dtnsz6K_!96Qk@qa^2>rCz~1JOvd75hDX|_#AZ%h6W{(L zz97Xmq-3upGlIn3xoO+bWd_=7&y&bNV=FJK2mX7P#=P=cgH~$>Z4mMOQ~qU}`Q&`I zpDDeSYb=Pt|L>8y94)V3{2hFL7jLWbE`u1@6SNtR(MQJ)y`y7h1ZZ!2aMs2RX~~_xCv!Ddm4R%l`u6nW#+w literal 0 HcmV?d00001 diff --git a/osu.Game.Tests/Resources/Samples/hitsound-delay.wav b/osu.Game.Tests/Resources/Samples/hitsound-delay.wav new file mode 100644 index 0000000000000000000000000000000000000000..4a3be92f9c6bfde6aeb1d27b27703b4ec2ea6028 GIT binary patch literal 49274 zcmYhi1zZ%}_dh$rf~}yUAYy=YcP-ny z)BoY|^Zmbmv#&dM=H7GP_uO;OIrrSzneoxXhOK!I08<7|9=c%pI(KUT06+i&hCBiQ z>tqN(0Bc}g+`2exmgfIG7zSXe0D%542LXoZyn@*hlFbn|r&j?=1hz!QWAJ z-gWlhiQ3tBCl8&n>y+GIioY`MEbZ*46RUHqok!<&=gg4*aZ0mwod|!iI?sQ{`j@A_ z_Y_OhIY)ozgTvBxj_xmBXUo5|)61roZ3ef2Rd6{xz!+t$m()qzc^)nQ_ce%TUW{!x@9J+P2ytGKr!(B06f_8{NAI zy@Xyw7oxGXw)L{|n)0aoA$K)T%L|~p(hEBlcO2&(;t0WdFtWiz~#3_z&G4xcy5WAPoww;t8Ar&#s(R`9hnn^{)4>KTI4U7Iw_< zn5dYduqLELGRsGacZs)*y`EY(yY63m)PA1J3YWv=E;2?jUXhJ`!UCvZDh7&%EY0TT z$($)1p+G2D%wNR6-~OmQ$ll-H#mm(zNEj$=YHw-x@%8e(&wt4Gl6%Mj87P}%o@Bn| zb<3+!+8_<746QUf8=U_%6*n23v`+nf2Kp>k%va=9|EL~|$Kt;fxr)72hpMKRPAQ$) zG_xtZw0~)XrcM*64boa{aGM>*Eyk73Yn_=6vSXTQnrUdohze_4iET?|Tjkl3(kU{&5qR^pp8I(k+|N0m}c2f1&O`!b449a2q0#rGvkm_X-pqkF&11u_s zJ$LrNQT=`gsKy?G{=4twh(w|P;di!VxD4vEkwp7tM0^}B=&XTM7)xbe1**Dq1ymVd z{_@$0w*LmCTAbE-53#AUg!&&=x^en(291ADppC~E^!pSN{Sn0C_6XY2DU*wH?2-dQ zI;m}5cgh2s45?!50M)EQKy~?J%I;1iKsD#o#B^%fuHBuorcj*4alT2T7wQ;vl!iiy z|4ElZ|B*AOyOc)9s>Y82Rr}Ths)a&WHS{2$+HoHF52=$s7V#^nO7jL(<`p12s&01( zG&7$;YtAJaT41xCR!Js-2ULM+bx5p}SHvg&Zkd6e`@ue-q z`5!|X*O&BR_R_z*J`|aTer6tw{g>-I;gsv<$!`X|8PqiSPmukx-BHIjY@dH^F@r9j z!=S;lG|TFy_{NH+PZ(6Nq{=HOexa4sv@fELIB$m?ORBs}<`;M*&-U}G9A6i{u6O;C z84e7Z;6kErd>C}29fS6iGU!@_K^qtb4HYryPjA}|_!#kQ5-lIh>QWaHU1!grvt21P zvnzwn4Q9{>{TOucNCrJTam|YC*y^rFCym#qE^hnAp!RPl)cPfj#($#GA@%HgT@vU==MJhdbVNa1}^im#}(yR-R8jQA3XZP zsu0$avi|vNMb!VSiDoqjN3XSa>WPI-oj;t_a|3Hr)H3qFdy_NyCB(v?`Jv$4~-oW5-iFiA0m8 zFzCE7Gq;ZF$)LkUwM%By+_o=B|9Uh0)L+C-UH*#(sF43%{a@Z$9&{E`Ilp}OuwQ9x zNr^eNDM$K^LYDKvB`gEr5m(Q!jq8}p*kI}Gc$$|>~iM+WtKPR4`j{C?tWfC#USdmU^! zqAHOBssXNus?>wEFDF>V69THoZIJ3pHlPwGtvJ2 z4B8${qld>(sPkL~O`S!cU1PMK(ShAEABzB$QzKWk^aG$;d<{`W|J&*PI{gW(Dq}rM z#kt|To0C+{ufAr+Fx3<~52Mj-GS&|U9$sBEa>?@OfAD#d<$Xt6ue#8cUM3Ks@gohZ zVoS=gzCV7;-EUUQ_;Nbd{LMw*791(7J2+u|?ac9$)HBAdCM^*M1as};UEu7y3>zuxWu3(?-{hLl|(a?3@V7^ZBB^`md@JpIEiK{I#&{hZLEA3A*v*H{W;$U zQHfc(DqvAF>t}m5(r8gTjY`Z6Is~FnnKWu(i=8-oHp@>= zH$Wxp4ylIr096OrYd?36>ZuK&`XeZM{TBSk#gn4YcAV8|l0-Mc4B8H8YFWgpiyKs$0Z~bcD z{`7WL&+n58W9hEH3;Xr{ySy`75b-tkc68TpWee{+=2c?xP9afA8iQ)zNmt&? zO&ou(a+I%L@2`&)q-O-WgdXz?L{!OrKvhBnq>_gNsu4XPRhc`WdM83uDTY6XW>!A> z)|C50{wc$1D*qjYZq8)qDv$L>)imm0V$jb*620TX&RZabw)HVi_m48W(u4HX=0SIp z#zY0~ztRs-_3sI&qWmD$MF&82O8~3-S^(9q`WJ_ZUwabUKQw;*H}!N?-lO%#zz3mL z-yggBM89kZeVB7E+EnxJ?9>r_tA^k&b5)%PyaKo?&|_Gco7>?+b;m zr|oI^395G2H|!f`%Dx&6&3eP*r&fREpWz=ucKJ7vcg8Nz>z5sDS-$C4&G*#}<(p^8 z$||B#%d_0;Y7UqVwyb~uU0-r3zm!TE&*X}EDw%+dnIN938e?%ZdtTh--_jR}~*@p7%TM-Lsz?&P>k-R0oS8)$1xiRnWxxe+{HMYywmd@J}c8WaXzy zstR|d?)8$BPAjVj92Ky<*Ha{V3}Db_ux#ylZl9P3yhQ6{UP9hWB=*!dnw2Zt{!AO9 zQr56}A*h=59aO!|T9Nc2^Rwk}rgm`iXA)hX%lai&uFq>2bWI0=K4Nj73n;X~RzHMt zd(pAiZ|#i?e^#CWkZK|8+xR|gjN%Nad=-f50UtfLlKxuHGY#)KqT}qkUac5XsWba$ALOU)s2DO@7vM*Yl!*=QF91$-kzI z+g;l+X0p*`#81ere;n_VKfo8+H*!CKkHFn6Vf6I;T4>gXJi(2WCh_qHxD|6BP@YIG zcB9_)4Lp{g*SBAL?2w(jQ)3o-h{wI?{cY%m5m0Z#_)xDqlPY-|#_1Y24H@=P)6@0# zVOQgkbF7}m8evsKEugyb2U01&_db)H?fpib^FZDH!`{QL*k=IqqkG?)aMvzbISv&mvlTNFCWyRlo`HJY?uH#nL^O@B zLz)VIys56Nd0k$n!As-Gu(B$sd&NljpK1uSsxPD7G(}lzTQ6!YYF^tb?Se+VzOpgJ z7^&%J9!|VBcjNXqXUbk0w>f>&+j-&IzFpR8gk9cuNIY+9Mkto(4&!BpZxsf!eda?v z=1w+Qb?_Gjt8P<(%AW*PW3k`+8DrVKOdaxLiYCa?r(>>joTe?}zM*iI71_SNoJ*s% z3O081WbL{8`;~aOX6l;3-y*YymiveXEfF^M9@sIk>&wEc$~ze^A$jV~j-ijV6=4Z) ze_v0y@*RA5D(}*hU4NFn%&HN8t7#adqnfdMSbT? zn;Lv`&Kq~9IYX^-CWF+A!LDt%-|Wio{CkBHo4XXu%_a*qiE)*R(?!i}Y{=SQ3k;~P zTle0hb@I9n`h5TB=Koc_CvdaFCx2el3SaHqoh~t(6J_i!g58;HGQHfsv6-_7tr$M~ z!k@aJrN1(ys$WLkm_N%)?v{IJI5quzoMH&SYXsGInD;dT>WUU$Bp)8;(1@~Pbn z{cXtxZXPqwzM`YGOKg=ewECAi{K(hakyk%`9iYh?*|*z|d?<+X6s@V^hdW;vP)j8+!Aukl{fK%*Oyhld}#C(ySnW6q|gF(4X(G_ zvtDnlnH(%I2Kj=M;W=1f(PiC*WTZXs)ZylpyS!Lg8CoL`KG9m;ScBzdbc5QOV?L_qb=xnc(d*PwRVkPQKr)*f!s$KGVJTIW2J4kVePM@@+PkG8akqq;%)5 zxz~-M@7%JCedMmMPXD6boV}rSR@M0CB=bu)`v24ndf#m86FN-`4aUrqN4GK8Ck*2+ zp5$)5aQqwPwvp|QJtDPErU0qqRr{;9VX%)?RQo#K%3?oe>6g{!pRY!1cRq7&@0&WZ z)$YZmi@@5+;ned$D+r6Q1r`>3dOf0o^xtc zfpzBckYS>}p|_lxdtCFG-Yv4rNw>q^|HyNlcEQe4nL$m|)XAD}{5erLAiHtmHnk6(hRl zD9XAf$!__I#f)13vPd}=FA|RFm_To=57MQSu4=UT)l@M$XJ@fCE30t#m!ri~zh_pg zFFx52-P}vFi>k!=%43{I-Q%scMQ7L%b9k;bt518}+%mvZzh#g+y_)ZwF}tTSZRjj3 z(C-1i7XA&{)sCX)eOgaI4|DO5^HMVE$P8x7p)h3V>7GLQ?SHNNzwlJ@fB$i~+}_*i z8TXvSO_w~ISv~ejLI*8DB;$7B2d1}cis$TVN}WBtreumoW&Fta%6ol#*6#F-Ygx|q zF#KwYXBx8K3dbbY*~~n3*jcDbWn1#JlFWXIR`dw%eJ7m*md_@{*TosE7a2-wbX~7F&}Uk3r{f}6VLy>P#*F0kxl8v z?TVcTUs{QGCJ2Y9u5d>09|aD%@SPf#97G(+9f75^<(cIYxhXHOpW*As**fok44T5# z3QhdRq>ia;A9QS3l;5##sz4JSH9-^L!P5?ep6F)Q{xC8h*WoV`PXm*$-sMTp`dRfl zb68n;ZjuXf-Q*dW!tvYnwYfyXLql$V>W=?X)7HO+*4Ix86dD9!r%r8=7hXP+s9E^bvG zO-QtHd1o(gFN?S80z^wb`=~^VqjLn$me}%16z4GL4wyva*mx$8GA)Y0r9+2Wme}_* zcQgi=vr^S&(OxMVi!H@r)oTJ!?I9smcXoXc)1Yb`^YNw*sLfA=c3Le!zK^`mq0tcb z*7KL4Il-)|=l>P6nuk3;+>TDjZGh4H~idk$PgT}$^o)Gx8>zGmP;hfYTWnx3RNLh8y+uh~1OSg^(B|S^ii~FQ!FAZN%?b~lIIVN_=rVKcqAjI(jfitlN?c-dthv-}zmxG%bWSKX1k2zt|HgnO0 z*A`*)JmOi{SSHx*3zEg@A?l^hmCY?KwmbCsh4b$8y>91{54oL5Ug2Dswne%6^K9ve z^5sa9WfAtq_JMj;51ab5;rjB6Q|6WIo;$JR)10dnrzb6Fpa;ItaDBr`kdwr#uc?x? zWL$M1?oM-+oOE{Iwts+I-y;{DO0M0r{rM(J+OIBvTPC%qi+Z0m4WC`tL8BYkSTVhU zjlbFbwU|v?>e7}wG^7nV)r8yBwhpRG?WlX^X1IG~KZd9_FzlX*1FDQ7SY;(|+m&x8 zyvub7`L)nv5irgBkNdz;WdLH zuyS?Djf!2*ovSl1RMsEZGp!9!)#@SDBUY!P;Lu%>!Z{blStqAC+wLwIX+MW~?Qqw_ z!$C8kr*g!kAJ&KBwfsHv>*$5EVMD-}Va;d5o>m4rITf8}#{F)p)BL=ZTk-qWn*&7^ zPhVFqd~&48KDl0-|2zv8^Q_v`p=OEvh~AT<`Wj z=N76ZH=b+KlRXVlUoKl(>gG{T5C+WkV)cIjE?7C97XO;L*D8KaH`$}<+hw6+y{!jF zG>hYWvv^x2{vcp{f_<$F(%JpE)0*&UUjzI;vbO8nUNyhot*RaJDZb%x?!A^@Madna z2Eh1B?~h*rO*AbT4iC3CbBB7k3KP48Nfd!^tcajnG9c)E>_yR$}!dV z#P+`)0^>dv>U+Fi(s&|WSJwLKpMu?=AODmT9sJdgktz6N7lY?)rWUxXruc*{%72lF0QFM5Wab8}EMUD7kXE z{^V6%`Rj!4#l>lF3g6^@D2{JTt-t_o!)OPOj@7|h%hRZ>(DsoH!arkw${vm0r0hQQ zvVB%>g?)za9NQK}k@ZNRNf@j?g(O!jrDy$QERk772EPw6x}|R>YG=LDYoETV(5Jk; zY5J3iW8OKBC|*GvsH`qUHmi$yW2yPVyW$1nET?+OVSj_vv)=`)4MP*HG^1BosmC3c zP8uzc>>Bn?c&-0Q-kYFH@GsYA^j1l&g-h<#CA8PJ+SOjHe_htKYE|jnvQ-r?N;2#6 zO1rh6ul&~-(hx-XcC3S+W3zY&N6+tSjq;ovgP?2=e>}zasV1;XZbOXk+Ddnid1d__ z)|OARLTaZXy!Ks~4FA>62_CEjAE*QMBX>!bZP4`jt_|g$H_xOM; z#mwLWH)khFHz#;(52Q7r*Pr|wA)`Nx4ch+{@v(Wd(*E~@kAka7y>O@30j-(eY%4Nq zeDnJd6TZJvIOV?V=JjK0gsgCF%;4&(*e&gKv6FE$rhqGt_#yN0pXB&V`NXXOsPov{ zn(aQW@QE|;(`4nF=cqLGu@@IkbfAlp(oEZ5{HwY5`A+Mh!dXqWtyvAj0kF~0Ho2+V z?_+CbZ)1nwV9Zbw{SyB=Y9U}7)6QucNDB{!F1Bj)Rw%m5xJm&XV*j{pwZo^f3J30w z%??Mh?Ceb$H*75#V)^7Rwvw&Cmhxm(lfbSTKOzKtVq7fmtKIAAsUFb%a+^zdVcW`q zU(~Gw`8rXAuW50}9X!nkVtOmqnO zvt z0y6jgJtF)=Gv52lVcel04u9169Iv335U*__NJY083J(vWiQx#7K57HAB|4tj6uE%5 z?*4)#o!8*Mcwfv>x)|fj3cgKy_Zxx;PMfzyTYuR2vF4S&y2ep|x1m+9ZO=2l zFyFFFgB{4DRwjmUEQ39~kMOwN+J*9f1PRvlm{nh|CDuFbM_Z2&sijNEbK)qqM4+y2 zM;;e02UI`o$zQoUElcyVjqmbD8a9@D8$L9J8@m}}%oSiP9w)`AQw|@%IiAj(8(mbq za{*`hIYAa471YjI6qo{S=ysg8_2uHE+XPLao1lwdTe+rOb{APtCJwFkkoM}ZlD?vI#gWo~gd3fw@RGV@!B2Yh z1WtzONt@o|@wlEjmc}j{EI*w>vA$v)e@?8WLfia-tcq&rY`!grlXse%|9uM2KR=WQ zRQho})hMz85JQ;?PiBwrC~|V|kC=WiViAw@F%KJg%q$+#8yg&!MSxzx%xaMb9IYMC z6BMo!5pRcDZGIRa7hjdzTtDw;OI+%2dn)0%&7YTXiedS)WkuRoQg7Kz(T(5Mpz%b5-F`dRDdc3fFJuq|TBnU&6cn_Gij?hn&)x zPcqNUm3$aHD><{zg!?%mgC`V?4cgsQZ#x3tMRXN!t&VrD`D)vB<#YcYv+u9%CA@vI z=g+%d0qc@!kE)C*w)2Wl^Tz6p7%92m9^$8|)%TxVo;_+yNv}y!rF$pss5~>;rJ*un zvAS!Qljhgb@4zO*WxgmZe@BKNkJYLB>2jK`~jdG5K*qg>`P4i3RK z=M_@l1Cpu`Ki;bTCSXHkFTyEek|`&&Ot+`oH%+kfbj@e+Gi@?;z|ckW*3wpYnMx|V z3KbR>@vuMF#5aoPSw~a~6k{4iHbt5%ic!R7Sv4n5a>q(0aJ5I_)vo8LUY<$j(OwgE zRUXgU8(c27%(QQ8ikGD}`3pw2EC8moN0{5RGt?)|74_e#;0h2aDl8J5`MpM({_~3L z$uB^W{b!rJz1+(BMB^XP3!N=@5$(gQ5nM4}mV0VohwaTg=cRQQo${)s_QPx1WUcjm zh0EFu;3{Jz4g&Y|kf>SR-B#9i*=2X@cCTseq|Z9-G4B!P{%!;4CCW#f4k1tSk?OBF zsVTF2TsPfuSLrNA@Xt%TJNfJ7mkW!9vno!5lbg<&QG<*6G&ruoM%t$uv$wA5>v6WW z%;!Wa`z+gh$n!b}bb@TpTgP~MnkmN-aw(=N(LKvX;L%X7}u4xYNx)2c6EeJ+a@7=;c{X(?pBAXFyw{aBOaj zoo>YFq}GxVD;hH+=GXfL*4Cf4ztrRn&2RtF`cZ$qunI5uJODDj3=)`A4@)nkZj#5p z+-kGo(+-<6`RVdG4U?=!VVJN&1agJWGr?0`CQ_@rAHkP~NU^CsOcqVIZaC^Oo;ecboC1P5X}**4TeRcMBInl3a$l)3jpG^mBKW{ zCRjI2na~kydqKTO7Sb_8G+b+e_Zp(ee=N>M6}h5gHvqJW5mQSQcWhe+e``l~p~P@g zsIv?gE~mT%M}UPqB{Yu1f%2gnKrpb4eoIXu_Ym3mBWxx<(Xxq{XZ}t7G+BdrrYvNV zc>+Jy(jctI%0(^2cOgRWpN7)u%p0k zS!0+3UG>ENTUm(Gx%9VeP+5a$epMG+GAsaVw{AgV{SuQI^5H$?A<&|#yJKSvf*WsSpb(y zfYACursj^M_Q9l2V;g@~ZGz37n$fNqbqb$5&9A%M*MxS3uw36(u+3wnaH7*TS(4(B zvOsj*VJ^aVG*R~Ua%`B5-l(v;VCXM6WmpFvGxnp)OfcSR?l3u7{Ed^%Hw*`izD8KT z%QR9m%OX=B#>3h_kjvF~>00d^V2$}X`^w65XgYT#TqThsaW=ORxdXz9aSG(<9Ah}c z?FV!2D@Jh+NWwTTxvre2ObtR=4j~eKD?Ciy3O#Mv2kvN6Gd~-H>0?bB$&{9A+*`d4 zd#&4OSv5-#8~MPsIsXu%bKh%ifmqbWt#+Zc6ZzdkOT+O@{1t{($VUdxZ#O zFtSXL41r)CFwml+zjX8@J)0DGNA&_rQu!8hS?MhE{W33$W0e5+Yse=>>KMS*Y=bNV zKJa`5I^jX<0Ld@g-qLFOD#;#)uHtugQ3Al`F2~#I8u&x7kNS*cVAp{=CJw#MpdqL0 z7gD?QH2u*K1lpUY!u@a|CzC4Yc!SG1Ba!=X4Oa#h^WA8!a4K;ta1kl6D-}H2< zo}NZerDGUtN(96cX~1!8ILNoW2mQ=nz+m$}FxFBAti?YuzSMO36LX8q1%KfNNQ@;w zTg`RAI8!V2%{UOxH=?HNrm=e3JYOrshiO-nJ@pEPZ@vIdBoDyrq3_6TeiUb*v;sLT z{{WA)4TkzFBY-KkL3Dz=3%OLf5=R7Km^FOKLXtl$tIglA7Tq=CPlppdM$HFVYepVu zig-&6BGFOIED-?V*5SNl*&C^sTyMKvmgfAA)hCYu;sf4J{Au3vU@MPRbiLzcthe0L zsO71&1ZmUpK&Mn!H$PU#*FM*rtBf|huPCv+s1nd+b>rakExq{e+KJ*U^9n0DJxI0% z-X~kZn_%4}cp$L|uL}}{2N7>UHl4`hV$V5A`t5K`hZ1yZOQGXimXJeQ#^Oz_J1njp zd8W_$9>$Ycpq zH?dZ}-mpixSRHAf)*NN`rXgDKxqhiQyCD*O)0}7tQ(tax(@(FRk4s85z{LV3FYEVk zaczOXT3$xTjP*hCGF^)GQ}Cn2O_nVvcZ=oR4|osk30p|c>Yr@+9$_$M_KP+|grw=8 z`3ViJ_G^s8_-SUyoQPj+z^O$=JHYR`TI5Go7e1I(DBP1%B(^EgNbc8|CGENrF$Uig z`rDk~{qXn*Z}P8UHg_LLp6k@Fo{L?GYoNLdw+!l-RdjLbW8e@TB?TIk2@e=o1 zZ5F?wMk%~q*HbvEslT94`*fZ_--2|(`av_97tAnD0+l5UA@5l&Bn~T3Vv2GmxyP=B zdZ#R8ZYiYD0BIOsxu@EjxILHQwCO74PAe@|(bqY9aNwc{WyT2ryYe z$$FjDK^@=ajqZitMnhfDZu99L$BF*IF@VLd4`-^gS`aPyAX!9?w2p7}mf4oMTD$xh zFRA-tFYwNiA=Wt=v^>86lUCm_?$WH+-2t|0&RG?9ymeZsN%PvIYxaF=^zyM_la<(U(Y5Tm7sDHEOLY8YW3YTb7GTG+TIYu{!7lETR)7>`QFQ14b{G z<2sQ?tA_Nrs!4K7(Qb9b^(W-f<}Ch1LQh>|%5+cR8%+VcDOJA&rjkezr+A*|S4l6S zW0jFNuE~g;)GY$js6SL3{}%pI=4-iOpJICNyv_8?Wv{u&=^B=1cZ4*{@|f`=Pxu+q zo109(;iqFyg!ZOt(HniN_@JrejSoTppPuhZ@3oYMaW!weUwZw6;b`-855c zFzeNhxUT&jxn8XVPHBqau7*!Mt>uE~4*g4-ig09qgr8+AtU(!Wvq0)4pQ=ILv8y*<8mp`3=gjpI$QT*Ga*N;(UNx z_uV{0JF7zo3~#z79Z);lDWNLBC$p-d+tJ!~e{Iu*E^{?!+-G1LZC&9J{5yg^2z6-0K zbQ4$Qb=y~?^WE8a-R+t>R4Fp=6)_AK{J_6&o?;!V8LEtL6+6~9Epl4hG{f<3vzuL0 ztG)b(`ke^UT|<0~Lnu$n5|fzNugRj^+vWl%n;RkP=A)b^EpzyO>V6`n9+N=WO6#S} ze)%iTTg3=bsv^y5fc&o9%j%nAvrwS$M;2M2!dq}0%#eieTCL^c?eecy|H^rCPuVW{TBxIy8P+*Zwl3cg{ z$GXy`t9-1-0(qk6aGBPlR;qEGE6R5m!uu-!4Mhld)8~NG_#M+Mb88!J7+;sJe^q{2 z|GD^#;a$-sld)tKw!G>)d9!ID0P1VtB)X3KudttBhV4^fch`|3fA1DyuJ2tzwa-Of zg-0tg!l@h-DW1}8f;B`LaKN&`qA*R@PBpx5U8u(!4hDiPe!)WBLF_W2ZmJkoHy{2JgzD~}}*SrT;claW% z8W-+N-FhD1)SaJ-2l08pLEcGD5ci943gTep40VyeU=AxrP$3F$!b;W;+ba2FPUF8e z9fRkXmeG4nRoGPXOjCv>$Z#ItsQ*F=40cSjaWeSEG6nV`8<8sv$$1X#g!DdeMnVHoq6jd7HvkNSJ;Bv94m4sp0MB$3sMoCp)SB&pz2+;hMKc6Mbot@lX(w0dn{(E22I?h4>t^K;q3T6eEnc(2FVJ?qeL-&lCmL3_0)T-bCLsjEN5Ib3hz9BF*Ox!C#}>8ITV=bB5wE3_>Da~9CkgfQhI^&mm( z&qStGEwNQ{mOLVOLajk$OeVbt7>qT8cML)B5v>gAqPd2=(0oH=`kRQrJO~kx_u=bc z6XeHJKqJH+;0Wt5ph+IfR4De*az!n5&Uz1(C0^Vdtz~**sX&I&5qhj& z3%ly~AXbK+oJFQcPBoT>45d%Pfp9Sx&d*~clBd*I*=r)zCLFI*_Q9^%87yVWb=Xsz z47`UdjXW$4W*xBr`U$ikg`G^7?yz3x?ci$=E89)`tWjJl*h9MmyP%h*0Pef?06|2<0Fh7iII&k{r1(me zt4LLEB?xWr%jK9g&^K@+vq>C4ij-q8l?!R|_S|8->6K@IJz?V^7kkq)WxaWvbTB@h z<4rvxqgdUZ00(p^xdU6L@^>~D34S;46Y5)|g{M1Q1w9SnyhYe^B$rl$4v2xy;|GwH z;zwAuRigQ`Y=LRD{JXJ0#y3S;yPEGwep=#$Zp2aU4Jr;A4%pGjkOljSj4(xTL-aO0 zttOoJp(CHWx1)v=p}B=T&_=;`^(`RZ^c#3>5djy8N6ba~B6ArmXZ9fxfFJKGus|>k z^c9)GRpQlU)Dxnm49X)%C7+vWO3jnX$Pof zpV6M?E{AkrFK9CL1RR3302@ z+P?w=)CKfK%{OwnfxyRN%diSM%CZr;WKI#RHw&e(B}}Ho^5j49Y&k~K)`ysrlK#*~ zek%gP(|GU5(Sk#!i$X+eEjrtFU-+tdvEWtH9NzZkr--QSK1g-Ur)fhpu@66I@d3`7 zrgQchs|53ngCrA8i>;&$jFXAHbC^eJ5 z1K`8~*a>gs=q#ss>n*GJCoG%zGq5t=Q9P3ilT$cXscg6lGX^>UaKI!`%fv$p<^?pL zehg`-InZgU7#vK+gM5kr3dwrl8EFqDkXOJzWE?by@`I<+_3$NT4B`i_My^18kPbK- zeuT*2`$%8t89WiJ0v7`l85J|0yiO0s3aP^;H|nb48u?A%i&PqvWV*2*Ip2~>LgX$= z2wbD(2oFf&T?7{iyTV1HYe=5xGiRJ|Eq66p{Ihzl_g-ew*Og2q8ij(#xZ zY)B;3#tpa(E5mwGHJA}xh8J@;5|N^B?wE(>&EYiE2RsG7jl3~ zwQZoUC>w$Gwi!^Id>(R1a)gu18_3-WMswE^uQ=UJcaVuXZ#Y;p9IWV=!VK1~WB zAaL_~ygRuI8wCK^DY(*dj}wDU;JV5f1lb|kO)M0A#Baje@hb z9S4(L13->bIndKK8~7q&EuGUByhA2|OAUje!1hnj(?&nozOEbmvZf4LQ5yoeHiUxP zT7*DLM;`5LN~Bhi-^jzzXj0D$BYLo@ih4MJ&p!_P(RaOH2kZPcIQ4^fV zKY}dd6e3fgG~^-Ug)E{%;b+8ea4p^sIEHPYwU+zjVGBr1uw1}~Tdr9)T6`?6mI0Rg z7-9K}kH$xn?xZ_Cf(`_(03nbbazQBMFy{a_g1eEYZ>GG~pku z0`S9%8(5-kG*)NZ1Bd-p+kTM1o;dK4GRq%^-MN9|Et^Zst7HoafEb z&KJzrpBJ7p1&B`IZNd(^P|z0^^Le~`oEkwZbX+6{%0<(uGNBdmfZrF}#j&>(fY;1Y zI@f#}ziO#8w`2aMOT;tderlKL8neoB92`W0V1Br#EzQhvaMPiLHnmld_rM6in&=9eW$)_Fx z*MQexKlnB@lM@evyk?lkk3y#N#~{9ZKV%N?DICHTz$u6eB!r#8A)tx*muaPIsaEPB z=|^oRPLn_JSn?=7m(0Wy$>BsK6-|QleyW-lFrSzd7JU&k6+{pXID#`2a^X&gLb*23 z1$EBUj)) z)D>bkJ)9Z=jAopnT_F3`G~9&ba@KK{@&bSm46@Lg6By{8)5l-cK2zC5C z{yD)r?m7X4{KMyiPq`*)EfP#HAcSpTu9>~5f#z^RZO+7wW28k#G+5r#hp>m>Z@f2h znGEB;p<{U%u$$)$*K%u+i=0qy16;@TfH3Y}AdWkj3FmC0AHzN9a&R^+1{&!J^d3e} z`m!I*qQD;F7;u2_VAqUAOg_o_c4`Ra#f%|m1Fwjk;8S7~6ixcW8>roIJG}+I0(608 z&@RXq&HyvuY@ie_V-CWNv>FPa-Jt|(2)Le_0qmq^FqKp+J)fRRAB2{9?KR{Q(JZ0O(-yfhvmo(^H@&^a{v@j)AHu6IeuWh(uMQ`x{0!9*bh$_Us4L}CJqBV32!iz zcmh5ndO_F8s}N09Ln#afy#mXiO!yj9z!?Z#;C%%lL4UAJa2tSx5U^b^ol)}N(g-(- zj)H$vJm4aAncPXmV3(++rV=XF;7t$FpP=XKc}%k55aVff1?J(;fj)E?=m)(7OE>_; z@PnYGj}r>Jy#EepkDdti!jH68tp@<3p*} zI6`}o8rni(OaR~r#zCIYBE%i8<5a`%xLc7|-1kTu=RGnUi9=xM4Lp%~3mqaafz4P~ z;FCFn9%_oGUKSb{ zIDlUb9_4vJ^SJ)d55x+30o8-EfrnrLwGA9f1b`Km3ZUM!4>)CX0Z7ALrk8O5Gtu;$ z_O~3QZ{p+V4b%X7B@jbThS$)QoD8~**OlonNMmXRGXS-~9h@ep00;5UL0`C0Fvxif zGms9hVKpa-J_w&B%b{~P36@yefsN)A=9K9SJ=wI0$~0Xj)n<}-i@hOwlJ8k>=|gJ4 zFJvzAfa=eCOuGtF7$4y?V2bb{_*@VHh4ZgLUfgtOC%hU;0-8V_1)H>_{B_awbgi_G~%xq>BxE`1a9{^jC>k!1<0dL{@A``i@ku#jW$Qa}d z{0uq+{Q<(k2KpW&pcM36;uPtJk0G{UtME0L0C&U#@K_=f$H^+9gdR!N0wnzmN(P1@ zanMLkKKzM8B1brBoQDX-@rF}5ZlD?YO@D?rkWtWBd?#?yl0@${+ff6|sYJE;D;|SQ z!?&~l5A>!9JRGzTW03xoh?hdg2z-ES;SO-V=qBVT+72@U67IzhWZ!M0;UM@lJP&Ar ziYR;NF3}wX@Vj_$O1ED`i4|q6ig&YMxBWA{*^DiCBM*9WGOVS2eNNS+*{4acryiI%JQ3D&RDmctZ? z4`MD55ST<=hr9qAy?H5E)J z6I;kX_y%G*?nms$@3CLutI4ip0lkk31R>@cd=O~ow1Xdc61bRO0V@R~kkR~6$OEn# zjzosRv%v|_WV#o)i{P+(^KW#TIi2cfE+uc9hmZnHNWz3Kxu3d9Is#LuL}&nAii~0| za7O{JdC_1$zCGl^e+|vy$zW@4H+Tj@LnEM55FgNiDXhobLaYHcV;`6*GeN&Ht)jb_ z8mRzN0`#C=*CPH^Ew9JwJ=-!SiDla*ogwU=P|3ET;@~Ii;X% z>3zfwdON%J0r*eG72gPa!Gl2qaT&Zo>A*b<0$l-%z$fr_a0jP>T`hY8nY_7-nsEHq=+aqUE(vSK-io^TnjPdRD@(=67d*`KFyZ7f_ zr?0nb=gQ9WgNF{DGI-Cxv4dL;JT$nf`m4Q1tJ%Bvrwuyy z#?-;5@;!qK=EXXbdrxWC{A~B^UY(!oZ66!w%hSX8jUexvuV`rCQ5df(~wt#MBM%haiks&=UMtNS?q~+opY3{U0`e&LRr&NEATwNF&*9XUj_1-Z~zc*B8#ZuLSad=uX z-5VFAIbzXj|2#>YymWnI@2L8+-ktRWy<6%p<-_aia(}%`98}GhZb*MmQ{#WCkMg|r zxO_~#SMR#|+3w`}r0)Lpk=jr{_0xC1knkrS!dYYjtw$TR)lqUay=7>xX+!*QaWi`>Woh>aN~1X-s}P zj>;eAZeA)M9q0DiX{+8bX@}nX>CE2tRXbl(os-|H7LWPro8!y%Vri-R)HI`dF5O&p z)5cY+dLaERt)5Ovm&drYkb2#Vq}!(X;5;(Vn+JNY_4@K)Z$jQG|1{6ccjdpvg7NS4 zlbFALGQQDTC~c@uL#4i3(hhwer?2-dQeD^Hw7R3UXSGkgSM^%jrrIc0sqXH5nD*#i zn|AJOlco&Lh;s+8jJF53i0e9I#zs+W&DTF1rL`d*Fo`nON7_dk)Q z_ODiL(0^*RK;IqJCFaqsOk zv%6(Fq&q1#>n;-Kb#Klq_V&pO<@NJ|u~}X=9h_&cZqI+J=8jeClVbP!GyU$H=Bpn_ zcU5Dm&8rF3D`^8&<1baKrjPZlF_)zeV!iZwJR5Jt5%EgQ6Hmu0c~bl?9~P7I+Hq?h z9e>T!@@(;P-Xh-0r^hIb-8yDZ=f$S!wfLdXcYgYQx;!10u1-g#U!@Jw{*t>^Iy*j6 z6ej6<+4iwqZpGuORp<9k&1d%x%y;#Eo)^eh<@0hUuN*&!=VD|!BwdwerY)*ntCy-X zs-5aHs%z?>RFBl-tI73~X^;Adr2ZzYU#*x)w*e=>KEyNbXmGG zu1&AxbJ8MtFMajEQfWr_A8}3hj5xWwQC#0074!67&bRfR$-l`j=i4Laf2VcTU!NM= zwcd>F+N-C<`%X(2^*x*(7cS4~`!tOecdymHHqF=iLAt4G>61B^$JvU)eERglBb_Vq zp9W9N%MKoq2L`UrTMWLNXX~sN6S{ZA*}X;6vhlyNbVT*#diQGU*2UGl?FXwv+K*H_ z>-hyS-)HrX)zj7I=@;pev{o#uFA%yX=Iw10hjc&6TXZkZCwF(rYxY*jtL2sRQL$0} zYxfe+ZU&!S~JuB^)~vf(~;H1aelR6KBao1``zlkPJgvt z=bE&5XR&lnXL20cT{5oi-J1Jj_k3hpA>UUmonNXq$iHe`l;3Fe^3>K@@l9QAJF9*$ z9bfHO9gyy;Hi`avBQev&-pAFn?xngOwqSLC_qeoV?_cp;Z|k@=Kc9aS$LAZ;-uXAx z;rY1w&b(Y}sko?hUEJGRKV95qx=TQ#X#R6Xj-_3vVQ{Xl-G`lz>MwRvxH zdR@KMwcVA}Uro&KcVF)9+uN@)wY$D{@0g_s`a$AxK^SiM>*&ZyoO_WzW3t$v*!PN(M)>D;_fT%Q-s zujiF|OX#}b>2X)*i`YYb&u*R9({-Iqs&98MsMhPmRZCPqtA185T3y_l zl1^=Jq>C{*FG@I!bNGTbh~QdpVOQ1V7et9 zO;^V)>1J`)KVt9nm9&x=XnM4($#GKkqj;&B5R2Ah;%jwFm1MSBwlQBlSYNyB zcW#JjoyX(FZc0n%3A(?xM`~5arM>D&>9W@3bV_@28sB$%+Mw@;slRWPv~znPc5F?F zIqPHM3hmaHi`nA|?J6$lKC15ep}c(OFL^}g&HVk&_?WA^eXQ8KF;2)?jJ#vIq`EoH z+j=`~-yUCWq0db}+qZc2j=lozjJ^lbAKC|{Gh0ifIqQdG|8!j3o;Qi{y;b7o&Ps97 z;12Q1z&UZrz_fUDV6*hS!AWWNPE}pn-AFa;N7dzVM72z{OLc0!T6I_J-E?vL!n8!+ zMyjW8$3lHq#|7Yofy-54~E{Qk?F+r zS$Zu!RxPQjvVL__yVeLT*YdrwNqihr;>&57w27{o?3P~CbGuqH%~wrR6}m9qN?XL;+La!i9?0vZ-{-l~ zl>BN8>BweQp`MWfao?q47idZN9Bnw}R)AC2L zL7p>>%S)!Y^Tuh7ymLB3R~$yi&!zb(X%E%ivDK03`f5*gXX~cj>-o~2^*gaxeMj6_ z9T%gjt(6Ch#fkB8eklJfuaNJ|clLgtf1N&E|EVrNIzEZxOtpeEw3E>YV22kJx;H8jHBzH z$AtPa?F;`NU#kX%*cH?KX@_)093>VL1LaBS&U|IMHD9Rb!_r%M8_8T)F?us@(Z5-7 zWbBxB63*6+_tWa}m1^zyPPJ3~q&il=zm09Hk7FSr>T&hb6NRxVU6ZbgJ2d**^qyp1 zn}3o{%WJ0t^K9wl{5K)TzQ|ZkTpWYo#O7a^lRn)h}c*>bqjp7qrsUI43<8+l%j7>7|$)gRw!3mL1CpZ|kMq z^5$s^*|cr`hB||_(qHpJX$^e~;BE1bSXn5%DNT;~t1IF_bp==Gdg7Go_qtE|TpTQ{ zkFAzSQ_`ksf-Ji`&PZR2o6{5d*7OtAxYczhZMJ-}FtdKQ*l*(EI!q~YpZxlo$2$D<84)f8F99}{gBZ3p*%H5wP0FOyYywM zh0|B0H!*2fi==;PFZR1MZ#qU}))K3H79(VFFLsrm#>I^ITNXp)_u}$AJ${>~#gqE& zm;4>$Lq+6c@pGLHO0%UWBxmtzg|u(Akr25_`c1W4x>8s@s@f@St7EBJGu@gNRn412 zefcMGu%embuW^O0zHX$NROhMjQtyR$ruUEdwD)?fs9d^KYb~Je;GTF-9r=4PuKFzg zT=le99IakMd&qi)^rHHXU+a3sQuXfXq-yW27Icd`o^`JDuBGK8@?mo}TY^ z|a)g zrBAd6_=bASOSLDRJ^n}6ocB(f=9SXoxz_LF@lDCQNV|){yiM#I8*0z7er%}g|2JvB z_I|n`me!tjt*TCIR1?w?y2trZ`ejnpPsgP@($!0 zKM=na&z>4nL-jX)7JrVj#kyC-xyp#6v=`Y=UB)ho_IKjYw6}CSNHP8^g@NIT0L+slGg^gA|9(lz(9bBMfyY+i#{6yC6mHrUh>34PI_X_Egm_uyPjpfrjT6L<}`ab#Nnpi(w zCf!ep1=2Cf{E5QQL{*2wlp{xLH+Zr3dy|FZzs2YAf%a+h2*0bQrBwe`O}nJ!Rd<#W zR>z84TKc}%Pc`a|cu1B^(u~ved{j&m^L?&p^ebN$PcOzY%7hKlYH6MHU2(!MH1j<9 zX0qg5nMSJ~48|{o{3Fs+vAZ#7L-&DGBT3kD^P1Azf=SKWEO^bt6i@qnW{FZw9Wu)gwt@^z9 z_I5Gu$x?lGVU()NBq~+s6-A(e#LBihVvMMPPpNY#< zgU--bOr5BFJ4N|@Q5+c8DHdG)D1WP9EJ@xwe($>@1zPRnM}Km~~B6 zi3NnGG2+iD- z6ap60uh?7~E~2cPJG~+Qzo<(5qGI)u_H1uT#wU`Q<h#je{1R5!N+l@uz8PceK_uw^80NgzTRdFRS`|qR}Iih4ZHMrRA22$G4^D_VU7xdcT!^zpjj3MYvi(x!Fo@ zDfZ9B<6_FYq}ye2pJe|jPLTa4O6ob%{|e#l5&7t!iq+hb`;|0avX&RWEH4gTRbAi& z{VuP#FRlC?C;!YP4^=|)3~4)U$Ol)&-{S(s=`8v70%k1j7N2=oL?>fc2?@PDm6}Lz9dz-lBcCB~g zQ0{&%O9thYesSAKW&J$TX>N6B+p>G6f0XBUE(N*{-=pQ&yQ=yy%3px1Bf_4}GPO|NH2=D#JsCs_m1ZZ_F8YA6PC%4>bf zq}dg_5yER*mbR2}U1`MkanF+HK9{7=Rc$|0ls}VBpG)pctuY{r2W4|lKJ4fn-!9Gf zS@UhxS*xL+zfW5qnUgfW=UZPTFf^(sNj=T(4(U8oufW>pk~FC28S$yk&d|D_$%2oE z>}!eV`XzUiJU2%2=F;yxvSB`1!|%Mp9{3w0TSn<@zy9GJzB&A3N&8efz9+pu(yNd3 z`g1)GNP4As_eG=8k+E+PqwYX0Jqx`dt=(euLc%2>Uo^X}6)ZD8dYbZ;w81M)@I zZ&&iO{E;PDw-UAPpnSr3?4LuA^JtxUHFqAZx`2L1554Dmw|mloZ`GbDnIRdU2~Tvg zpX#?KmYu2Rt}^8d`9Gxp7t%j8zB?47e#sw`W(pG#^J>+3Wc8f-olCFVvahXkgVOUe z$-%4d>K$=>N3xBRteD~>@X;E=T)(_HTK=9xn$IV>WAr>u>&&gAbI1nl>61T34p}!+ zBl~5MK7OSL@m=E`&1h*}C7<$5;eC?VujkPk11>nrNY0Md``Pq3YAC*=HI8q4CySB5 zI+f&&l;8P&^M0+N4|r${G01ui^2h+PCFv;M@7J%N@$W3H&$p8g>b&!%t2umsIoRdr zb4frNvdOuQUh%EzmFDtY^L#@+@A!^&^kUt%j`oH|<1@0*D+~hHJ*r_AOV&G3#c3rJGGr1OpU96_IcWhbk*^u9f0DQNV!#xtAmb7u}Y*p^Ix zS3F}qt52_3nQwjf_r~Kxz9pV__0V{K&pY3N?_+$|Jh_bjLz+I}0YXLKB?S=dX|;T&R!9(dArVG-J( zU-1RjFk)axcc1GH;Yve2pahx5U`#1tjZZy#b01C^bSUWYgvi2 zL?!Dz)fXm%g?$F^5tpv40hJ(-)mg*;TY-7n-`@`gjBc#MLqr2qVcTeV2&Tb1%)&4D zjq{8KWBqz$C3r(0z|$R}OBV-)}JE|1J(<(c}2M|j3Zuy9u>OGB)Qrc^;^0vf?R zjN%x6B37W9_l&|S|1T2w2poe%By*g9_B2O9bW2|12cLp*=CtG^WYj~x#yfZtyI=vc z1OG2Z!6@uNtcW^Ni8on`H?27`n9o1wA{g>J5X71w3!lMX%tacWV!ZtiPVoU}N_4<1 zubG1vu#|Dc6%D<9c?TLe3m#Yj1n_@Qa0I<=3DF^gjaf#G&}-^7cw&8y*gnw2s$daz zz;pO~b{!d%R#Yl%7_Dcrh~5W1c_u4BHL(NLAQ<)}>W-tM#!(`N)r|3v-yw@g`Tr>a z6UK=D?-L$5>u2UUO7<0=Lnku{IRHOo`GP30CO%?KG6QcW*}@U8QOhT=A9Vs7Jeo{E zM@BmSXb9?%;p$8kqbmbh#V%J3Fo-9J6gdln@@)U$clZFj(nI2nkvhg$Sd=)78uDLT z&*qC!I*+B)el+no=+Sh6ZF1(pZUqtbi{$r@JRZ??DzL zocTy2|2alxQ+u$Qe#v#7_`|70*i!Ko5{L%RtceFe1jr@o$T#}X2Oa2Zm;;wl&y7Lz zfpe7HCn8iSV%gPubg0CFbUMf-!&wvUJhz~jIF>x{nwl`AabZTY5!Nz?bF79myvH+Z zIFIa8#^FmOurfB{AF2t~*)PP>eHaLbG3?cntv=?}yee{uhr3fEV&754zz66AU+Bsh zdPX``JOj2nswr?b+JP;)3U*0k25f1c@CZxd6?FF*=QV!C0_Q#P2BQtf3>Wwea1DjzP_Ahi@ zw#pWezep^50PJ;LqOvg)>COV@I=-jhrt(t{89{u|+y5UHo-}`xvv>&Wu@|4=YjlQ# zKmz#>Hkjd_9cG}PVkEMeZ3eM-?D-NO*0om{iR3cM>}KCu3u6m=k;^z{nDITE4CRMM zbjH(Ec`Rl(LkB}$;4Jn~lV<51y}7dko9VXcGaWIGI2(v49^*XE&f|hU)-=1Bfl6dq z%{W62x#%4T`g=_@!4|BtpN)Am@%YBG&U||h3!OnQs?kG*$6^p?StWL^8rY1_(E_W6 za~cSC4@DI4t$mGl*01n}5$xzXe$EQl+>$}a_87+t<|$Drd4ybZ5}D#$Vr70=72jyc zf7W+x!YgF^Z1H5917^}lo z$O9L!3pK9b2+v^$*9WNL^jzr43|1oUtb}c)6M!>}d|1jjD;eNSEY|UC4IEuw6M1%g z;L`czqs}+hG8!3)|M8)5fc5qQ8D#{R`l{H#EGf@LA4>*VmP{=9u@{S`<^;4g z%89U1OHHOzV228q)49`2`#5=yWypp%EDsBjWHxBl!e(o4mOx`?AKof9A_u!U2Ug5B zKE?`2W&h_Y!^*7XI;0o>!2&D_reNn1FCtTX-dM_7B?rv@9AS?4c+R7evju%V?`$QC zMo-Zhl$zfpTGBvZ!Ms;4m@IOf6==^&WQc1k{<0)T)_x_sjX?ZVB19c0BCZ0Z&-Aq} z^l%mMO3njoj%MglFakQ8`rvwCG~*velkHruAp7Ax?-`Im)p0c#E)6`;!(x}wVip1s z*vCBfW8T@*TYxk7R$K|8hL(M%C15`@&DZvjQS01u)X)>WavokUC}mar#3TBE49>g9 zA@1nsxWQay93!!io(7gGy9ACCG5glBhn4VGk;#=1Y_QD|IrL-Am|@nWe%o`-C+p}e zah|i1V-MP`J%}-@;yHI6?6u9BEQD`d%~*k$k<;WZ`ZzC!6w(7SJ;R)0CV(Bvb&z5! zXIQz>(ou2#mpY7>=;OE+f=={x#))~^UUQZ63O;M^VHdRngqU~e^_zI%4QD0U=aFzO zGt3JmhlsHEVuj_<+_i!Vf?r&hKm|T_R+L;ZKiVqWjNObx19M@?+M*L0fkAQxHY%8* zLUM+@z=wWBn(9p0U%l)oWXVIp1=LvXMqj=Pn$%QFO&JG;+^P zFI_kYZ<@KV2-YpQve&2>+~ZqvjX*n|n=_8JFQdYLJRrn@#{1%C0~wycDH#FO#fw|W0YW)N@AS{FtY449kDL#)Lr z=-lAjY;Wzc+e}DISlKd>YtK7RsV=3far})2BN818@0lYVH++bH@E~0&j6g(qZ!a;< z+yhQrd$7`c?pkgJW_5dlT0y3ln(EjUuNA~$xiik1l{l9CFv=Z$yu(`5Aftsmwcqec zsUtpuhrCO6jlvV=P@~onF4|$0ttArJ>dXYyoFn_-?yjmHcSD;ipM_7oe_~B=L`I`6 zXYEsJ7c#A}k-%s?f)>`*d{Xut#<8tNBgdVL^iCcgVg<0~&Yr7G)EZ0EOG#!0uvL7? zv;A*7(U+z*}Ak6r;2hqgG@V7OxcUj#QxVFJHkGIs5GM;n*m-SVlS0?uD=$wmn$YuEsCOAZqy>j18XXJJ8#^U`?2U!aP2&M9^^AlE&f?J!z7 z0y?=8Lqxek3Obwxcng0spHJNIX`253uq>K5hDK=9|1~+{>RYOJvkyi$qZk~ZtMO%2 zW1sCU@naqQ;i#Y+ULy0TQE2CW23h`LjJsFYA2X$2+pi_Vn_Myyi)O3{8mxo0L^|gi z9BF`7GIAYHV}X%mo6(4_?n1Z&>d%andlLmY$iW-<8|`h2qlhHVvNnF8I-8MP|F|m6 z9)&yM?t9U^@u}m8)~x2v*VVo7cCpgDW-m66MiTRhIaiq78JgXU7iVJO;vt2lG0*lF znQOE+vGjZIsaVb4LIyIyCf#VM*kvb%wcKMP6Nn-+jeL8^9gSB-Ut@%4EH|=2xOa}u zHb>N$FiVohAm1!P_SrYa%W(7*)EQUyl^L>sD4*!*oY9U68nd1ef`|N5?C=h-^n2w> zxqWF4C!Qds?AnSpXzp{2CP$pPVu-HT>N&c?FxK3{=)%?5PS!Yz&Io60b0wfp+}+VW zf@>@8fzXEXBGMuJ}DKo zi4}apIqQmMoR|DJ7u%oC;&P1(Ey$5(cV=%=|B&pfX8tU0 zgMFCH9-vq8)nIl%Sm1sMoEm$j6LzlHi}q+?6XViIFZeTR&8*l|EWtvf*3l|!7**zY zv~_kHy^TN7g)HI63{!L{*F20ouh_5;tCp-{u2JVs(AakFaojiq2W3v-Fr>f>tcl-k zv9GAJj~k`AT%RLf`7!S^7Fo`9dyZAi3#E5-W;sICKzPPg6}AUIO>J^MA_;Dz;({^m zp@JylUasF+S8@yZiH&A@`@lBa8(3rQL9=^8K55LJ0Dqt_v(5bEh_3@W zL!2M(lgT4XLLz=KcHt>1+G3aGfC|s?KAX=~lpYqO z7bYy$4BuBdyX>3B+eE}yU`hwV2xq-lE_s4h_#3A1PJoD+O`QA9EHvg9$$Ub`pEt2& z<`mX&Rv1mJhA)T5(eLm9@*D|#=X#1Ztm!jGUO{`QY4(_r(fE}es#yy^l8fXddg1AU zFDiz+E>~A7_J3C#+gdukc3jY{mWtYs_C5G;lxw6A`uX0Zmov^b! z+eh#`wifGMiHMvlyYtcK3v%r(d%ILqBNW{DDkw3tJ`CjhEOFINJ-(T%p`wA(MDIvYrJ-S(jDO&y~l0iSa~C zN|w1RW>xna;KDkfy|Ka>Yr}K1CT9y8y=t++Qk~0;Vnw6T5g&uA5<8| zmhhPhN1)*&q!%2ZkEOadgjLI36kjzM?qm3andGs(R5%VPg-J_R*-|iO9D~fVmqShy z1#BgzrRQcHb7Hx#fq#e=BTN3`J4Y34mTMg37+N*VW+pTu@u{!yQmu%Md6pb@MJZP( ztPAqN09HFz<$4?$?<>PTi+;$XHgVTrrZjaA%uy^dGjNv*X246%a$iqiU5|19>E4+h znrjKpDx=H9@zfLCT0+@cG9qXMB7vmJq=8+X?@YVuKh)*gno;BE|AJgZ{(088-GO0odmw&10x^;F-~0gIPr$F#^YE zNBpUS=x6&_16lTopLuT%u@oNBvDv*is#t?{;N5o?K($%Z9A&(bzpkQ;E$FpIB|@$c zt|M@}uUvDzguLQT1bkxevl^?ImCyxT8L@u$XC~nl?zPX9H++A%)L`c|GmXijlewc* zMyxBID1ODu&P@ByUUnwf%Z^Fe*SN>AzZx0Ff!sIr!&#z2*7=jd)CR9j9E=b9vc%4n z5Ue|Q%vPL5ma7LdK_juHui=wiAQugh%dV>2MI=J3;|QTWmJO*l^ic3ro*g}V!;ffS z+!=`;$)jth`4tW5(TQd0HrzirQ}Ma8!l*T}i)Ng2A3p>}f=@1% zuFm)EjniVWE%hT_#tQ|PLn7q|Gro6jwr3?l@+a zv~)1;YuB#C*p8oEQ~3)!Q0#6VJKYa>XNmp3&Wdg2vk4qUD|9mhvpRlaMdv7T3Nno* z-eHg9YFjx2kKs|OD%UCe7X!wMaSHC3>!_RIu@F6|T_DkWDJ(CeS&1L!dNe4ZW;w@w z#{M17AIvM>1o_~BI*0$^5Rit|;L>sR{@Sd-J{!~%X~)nt0v%kZjAt|}*f3u(2aF&S z-HaHrt!QC(V&}o%z`|ivX0FINBFJ=(7CvrroOm_9pz2@|K7{|hr}Sv|VW7l*lez}G!s0=$Sk^u|6jlC|P^>2W#BQ8WP!-t9E(k2c;}kWWUED>`S8<9U|ik-k>u zIzf!l!CoV2#&eS$<^sG_>~Qv3b1d}U5{(OL@Qtq| zm9Du&tjPutTQb#|=2c5hBiS~Z8zoXdpv6C|@A+PX%yebJW2}$mXzi+lUo6}CQeNAB zX1Z&pA9JM}jw^gpq6yLq+u%9$gCkvc$*1B^=ePCoFKuKJ7Azsl z)d*dgjkg^$EV35X5}a6`>#TVS?%@m?;xkuMujeS^QD+R&ov~=;*qfVpB(~mBf@j`a zgJPX$;92{cM-YT|MA?$?I64(hb<8-2?)IX)B3CZw6n-?XvMN{fe1C)-Gzt~kpJ zyWnqPh%}hi(SX-@&umASM{wlKbw-pC%qALGG*aW}L>v`%kT938d-^UqE8|t<@ninC zY;cS;=TG5I$Aw)bR|A~qXkZ=fF=kk6&jn=-Z#gDL08)_wlT*bCD)GAct;rzkLS7Q_ z!XehuoLTNYxZV(jVmZFXD(5CgONZvJ!`a%{TDYHQN5yQ@#Gqs!xmzlkKfmB>L(V8Z zjpi$6_#GcepdMMtot!_h#&>woJ8^Y`yP8I&uY|fnG0!Y()+1)lJZE)@92#Re`VkZJ zmeIlL%x>7u*PQ6!n){0GS=?>8IuzD4!=jISKd;Y~Fz>O533?dk&Ro}uQqj#?_97OL zktMf14_+{Su%V#d9%DQVOSH(flI^f`Q}66wtS=+Yu*N2=N8T0GTe30fOtOEBzM=sd zJ4^6id1q}(tuWi*TaaqDF1YkPlZIW(S#z(?7VKj^`ycOvz;Nk&3r99rltCUI$2Pw* z_u$jQcIFWs>osFU@oIlm=*(dgprFGvGN&UITw#L$qKyY9LY zffHnK+$(_%d~L~P)yDVU{gDsmGqY`Tm&~1EsUGGYw8Byz!4P_*zw^NHw8m&?bm9?b zp?ClOD>kob{}ookc93A)gFxdH-J9GjT;zIyjmA5cG9FHV*L=UgI5M-KQQ_5seBui) zI=h_>K32LYX9*U&b07m8BkXF}w~3rYN++y$G>m%79-gbl5t=wkjV?x$=eu4Prol${ z51!TFq^!y6o^L&{m@5(V@5I5L!4@*d9B5QDEbS{7#xiorCB`_S#XrtGGo5WQ-dzP; zr_9Q(gB&lcid1761X%{?Ef^|@;;g+*W-`lJ*BFCfb)$(Z(qKD3A*rw*# z66;`#@DaQ~k7TxU1klL-Bs;LLFg9y53t6taNWfk^Y_Bl7xrfbJ$J5t~(8qHfUwaq1 z_zZiBHt3+Yn(uRthp~yj%1*QBY>Ul9 z?oHT3F~-*u%(i%lj4@-Fh4Gzn=6=C(!D944%i-D~r(oCFWzIGuyZ%taY%g}AnB6uzG3T^adC ze3a{tx zWUHmPi$<^EjOA5{t1AcgI#2Ael6z)1_eo&!WcsrsL=5?}tkmGih%=iz!_WyE80(C-ZurfdWt5?pu|!v2tRzqU_s5PDwz4`N z!J~y=InH}O+oNFIk%bQ!Z#0zam#k>4nz5Ql8jD7L;Z+6{Wm)UGISmaqTP<qvaE-=wCjmPtN$hKRQ_7;uGVBHSi30!c!c@cec*E zW4w&EXy9(bS91#5nPs#!eFTU>cIhfybD9d}t9<|c*A@+nVy$&RHh6Z$aGa0}1_~!K zk6flNni%)}pKr7V$l66EW`|jf%3Ky9l)MEU@HnH8YdJBktZ=*IC$Ph!JC1 z!?PWqfM{nLJZ5CbjG@=X`>claf)M+G46^shevqUG*uZb}6#^2a)9n45(P+yreH?p=pZ!SkOa7aXqP;@jd3fh@b`Xa~F2tXFL z`kIkHujpRO-J1P?bThL(V;*ERq%gkJ3szw){w$x-Ezv2QY`-y%`K)7Bz!&I9J{f0b zN}T)N6R{>=5L22HLC_CNOf9+SHO$BcL~&ud#AzJfvgX#g~5tofngQ*;}S3j4g^ zjalwO3O{T4A~P_XDLhxVVP;-liV21D+jyh|na z#G73tpE~t!l3D)2Rx^ze2*aAEeVxWWDmB^PE{qH`gnjxp)#5rGEI^FnER)IaA_cgqfE(!?hTXKs(}s z56pJ%5u0kkdE>*nlZB4oaQj#RgqJ>~#LMWn-PXc!bT#yNIFO8o9sj$&Ft*^Si7C15#x;i;%I6vybYh2d&UXM^HQo1l zt~my8G}VV}pc(Od-b(eT4llneiu7nzr7Bh Lc*tJ+9h&|ROxJj) literal 0 HcmV?d00001 diff --git a/osu.Game.Tests/Resources/Samples/hitsound-minor-delay.wav b/osu.Game.Tests/Resources/Samples/hitsound-minor-delay.wav new file mode 100644 index 0000000000000000000000000000000000000000..76dbce77f47e62f2f43c293b81ad3f031c5c1b8a GIT binary patch literal 48562 zcmYiO1za0V_XZ5F#uJj@?pmOwRA{L{sZn?8)ZN`Tb-(L(-JL4XmX_TN7HzwcS@`}TV6CB&69 zS0Y?Vu`GAoUI*r6P+W;{*U~J_okh6wG$*fJR{Pez4|8R^r1m=Pb=$82xUyYh`J0WC<{jhQ+4+|1eb*kME~2wO7kx_2f6QITF62Y{A?@kv={nRr)a(U&!G0Eh zi(9R8?L#CD`EJRzJhY};Gu+eNVL4B(6vzcPnOn@qzz>1hr8%W<++Vx zpqjJwzcwfg;@UV_N@IobEY^J(i}jyD|F4bqmg1@Q-gMeYVWLAUSCiu8=RE;bFaLn5 zN&0rVASb!KM7Z+jTR=7H;D5*e?-<(UgDMh(`7GwU9>;oT#eEzoY?lkE2n<#^RROB7 zP*C+Qr@fc$vLMxgtAJ`j2KawR?dMpoCdrNVt$ET>2AlPc#neYw>{}9zeUD_Zpr}rp zSd76gB)g=BM+00xm9Lm~`4a%uU}sd7Tnwnre@x%`pY$mwMn4B9@8U+)E|)$+VJ_EL z>~bB24b=Uo-S$rko2_Iq9~q5}RE-@0sdldgRP#l!YQO0f;Wrs9tB}R&G$giZNV_BwdlUC8xMw6&lEY^E-`_-ANf;KjeV6dK145kP9OS?B-k6K2}6g+WE z(BAeM^|zTk_oIK~+S2aqE~YT_f!H$O1AA}eL0;h4BVHRPyzlqAU+skANUtT^;|{D- z&Aq;m!WJcSHl3zhQlAt!S}=KBztTmO{vop#*f~wg5vykgxb9d~6;Lv_&^L8@sDIU% z>e$uYn-)!RC9yl6EcPmp#MV2rSZ7WzYY-NzXKAdPn8m&a*smi;$fncS-vkCL3}LZV zE)+J?i^blCa+>VOVt0EoSi(>iJ2Y+9u~@$* z3M0K)%xfXejlJEb$TAjNG={~-axyyGu~=7%#nv}-Rw-ezEk!hTwqfQvgnb@$$!UaP zW5m?=-aSB7R}M*^XSaJ^yDzj`1^HjeU{~APy_@0?)GnRoE*GDj0M85A0xA?b&5~KTcP5eMxNe z02XTi>B4wa4C5HQM|$2yR4?uUs;GykYFQeja^f)F=M|!w^#M@z`wFNw zloLBP7=gUW(nhD*9TX#|K`hobo#Ua!4Aylqi#2llqx&;hs6UI{Vp;5TIg7pd$YLSS z*jY>l+EbnjkzK22zUpQ>pemF>s{USp>bEbf!rTFsfWz)Rj`^ZD70NhfDLb98v*q!wrWaq{4gqUeY&OndTNE5`MI2mNICSyS;j{5MqNTCJ zl`Bq%GD}3_ZL@}&R*WtwCwlxUQ21P_Rtg-LQPx~fQ)^UTySja2H`GoWGe$FI)GEr> zd%tjo^9x7r9W=RSUZ=y zZ2?pd7XYf%QLyUY7(iv`i>l<(oRR@_>4ffj7Td`o`)?hK&F6SFx{<|-+gMCyrLcYw zi`mO!AD;k%XqeZezow=oV`UKU=^JXs#Nz7)u7sUQ&uh9n0S9-aZuze*0OYL{g=GHtL|o+LeuXI ztbE<4AC%(~i5>vzor>RP*ad8$YE{Wow#GaBNsV;VX>4q9G_-zI!)u^ zHn&MLXw2|dx%|r4yJPNGjtDm^&4@@{_f*I&wyS+a@zw=}lp1B?CsPGi@rSt`k;v)84?+K`SbcR&@f&tYTS4ee}i|^ezj@(iI;t=y|*Mp|_4POpC zIbHSb{wi~LN_V^O58VTYW;S=dop&=4uh~ESIK6*mU)K|xc6Z@0IF!MJQ#f4DjM^Yh zzCZK8SjT~~xEq08f)eN({HTKW>gNw9cx5W(dFp=Ig%i)R z?9YFC@a^u;vxTpw>}vf9sJ7QP?MbxcTp9o+zm^Ent3L=%363J$JJeIRMlLWKm+Ws| zx}l)v+sfMVP1BspO5+}tfAvwuxNZ!`sAK6085q`DQT}1BzIzM=7_@an;SXUw!PTgUx;-?HRoj z7ioOCc*)uFNf@N64&{6#2vCjnfmEkm(9eGIOt&rq=Q))iiwS8OQxOa{m&2nohsPx> zmm$C}$9EI_E9Fau=9EcWPkoU9DkmYV+62L>S{zi3(Gdq?8lA3sRh|22_y zX{B?(4fX4eb*A}|`S9V8LH2(G4*6XRv~-*j@~+3N$QOM)yFc&qqG#tGK7BiP+?%+_ zcfLSO90iIDvtlvplYKIP#w3x zsvE?I;})v&QzczZxYM`0I8z?W%Sap(b5^XUDeMryV41MP>f^j_LsIz<Hw)8hXbmGVW3J7$YuE+kSdt7+g-t~)61Ao^1rx7ocn~ z4T)kl2sY!6k{QO$iutXW&!qap(T6Gu87=+L(QVI<7bm9Tw_lq@S(!?ob*V2p z?|t?w{?rF}Sn98FqjuG5M@%q#5B?5$^qR-d?tt?}E?Qm|cmv$t9Lr2Etc8-^=Lv76 zX(fkK40d1&-|1*-p*QokSHz*hZ$0|9jqbmhe{#eUU-77C-SY=*7>sl`jp^)vZF~)X z<0yUO+W!4NnmYU4*zcu3d`1YVMw=m3eJ!ZERs^UVzx6zop6m0vCGVj||HIi=QKB3= zwwAvX(h%4ToZ}Lh#HMmwf5?$s_SyaS@N{oDc#H2vu-PZNCdMl=`@3st+GP8wx0A$y z*N#CQZ$#op9)P-rS6$RaKVDZ?*Sst*)lq+EQ(eodpiUJ-;aSxXsHiWaU#R=q)Gg=r zW(~jfm405M!C2Y&#N1og%{q+yWDVfOSaTJx%xm29jh_CvzH7)Toh;;oR^)d@H^hFi z;Q;a1lwV=8x_!7$48Hk=syXnB<~W%KRG}2C8jJtfhnuVKd@%q&#^^fPVztR0GjuJz z@0v=d$*GpLe|a1h>{)D#ABX2oAC?obtrJ%#{OtQ_KuMsq-%?RS_deRV&{xG*ovvp+ zgB+gh(e}UJTG9RX`-022FMI><9shpr(Vn7ZnICGzU#bj_e~OXyO$AOX$YH^1nO_${ zpefeU<9qM=q4Rpooiw@Q#pG8$UdaRPawf3Us|3GRTkxdH?}B^9W1E71&&;KYwGU=j zI-D$O0#%zh{58Wo)m3??T{`#BD^OtWN3Ra~n%xoWT|ai<$1M*s&QN&{+W3&mx%wkC zw$Ac$>&7Nz0ah_&_}QY$$VI=hWvX9#!-%34rMJp`vpm(mA3id5zhwgrw`!$t9`tjb zka@=AN}kDMVC8-1NyZfE23{^Z%cWM^7BaL-*tPMOHg?~aYkjYO%J0*bJ*Y>A9~qrH zmXGT2u+89u0-GIq%4*3QUo-kAVj;V;OE5X0>s#B6PFbee!G+o+cXQKe>9lG9SX&%y zT9d!JW!L+g4eMU4umAM4qdN7)O--lIf2~d>D(Ht{v2>rZ)@e=jF)zuuQ~n=U6mgN| zNFaqB^kcC?PX?=Z-nZ5$Z=KLlXpIa4li*~$!=HrvKUI(7& zl~8 zv)cOer#NTEWmtH#y!{ajV+yu7*K> z(w~*(g7uy{J9%KT{m{@n`_j-yiW@-^3E&ff%yk-0l#0e^$1tnvqYTgfE@@Q!YN;5N zx2;5*{iS$k&XJNy-`-WME;-dOyeV3T(dC5D=>&Se({#JdiJzS5WS&>`$`igPwPnZULFy7MnMfYoW!m<*b^CkGEyYI zaY!Ej!ovwI`0IMUt&95$-dWd6p5Gj1MD3PF_gjd_M(rZ@Pj1nbB=1tEPajxQn&ewK zd+4mnTRl40?)ID6ynyFxDpJp4>vG?SMm(r>n113P51uNG%lW_gpsEs&?c4nv*PYre zYDfne_u@l3JNk0%=z!IK&JEOm_ny@=Z_oN|xioh9D}$XcELeG@qIJ^4=Em4l*1fJH z;K`OFl9>eylyT|z97@k&_NoIJc6QrSMDeO~=!ktI!Tx9S=>e%7$s=C}prre=34S}=uy5B3EbaU4~(2iSuU%PQZfp*PAfi5O)g08bKs_zM<8fMpiGuu9{ zA>KSV35>aPhj0IHpk2pP|2UPN9_tBSvH0~%;{~g}l!ZK~Y70JYYl^7`R=$I5Ah4y2weuVmL}-7&kYX z0nk_kZClWrkPaAVTkO)qs%;Fnetx30ig!zKK(&P6*78VDwVPY#i{$c1JBC}^U_W0o z09Az#pTJF+bnee&b4#Q0q5RDH+K$K!@qm^S5a=fBU3 zV={A>#m=hs>p6!S-F1cQ&xlLirUq;s<>>ZlR%iLbCFl6Sva3MY{O9DMq^CBIxLekX z{^>Rr@ggTS_GU(ZdjfvS{E5^(2<2Z&kqB}hUF8?Ol=9-g6~LG3=723!3S)7!lEo1s zqAq@eEqUZl>)r8Nt)*k%*pS5O%-e*$} zd!I;M>`|40Ij#CUNj9K-1@go;AAjL^N3%N0rT*z4UHSQ>Ii)*iOep=7e4*mx`1uV? z{0ki#)RThHG=4)(r9z!`(Urb6#Y=qL)o0tj1n+K#Pr6rJe&G1^b*!v+T@BixMIILR3 z0vrYam08SF$&}4Iemje9@jScyTHremND3(JFfY)3tV4jq>O>y~yYI?k7vu~U!}&lG z8MFe_pBX(`y{ki?ntl*axvKO^#r9{O)gRAR*6-alu@zC(8v#`c$yde09e4B*%|1I; z{xHMEaZl+;m#J)~Yl^S0YfGQbPQ%9kl<%9R73`U-X68?4jo~8(H~rf!t+Io=`=1N! zsDj!$-OrS-RR!l?@BUMt{;qQ7qy1{HRJH#5vroj%xBieePsUFvnIxRh=p$@4W%K@m zK5)8SF(dK%K^zRPwtDpJYVq#3#cWDgZQdJSXxecjbZ!~}Z^GqvgAWenFj>K8e#2~VPaGVRl8|v#Wx*HNBb&PmrM@C*(un|X< zywHD@R__rCjeU*mIQmxH!7LPH)m?(`{r=8;&Q{~!UzeKxd0wF1^DM5V_w((I>)&|P zKh0TQH?L@Q{e=dZdZ*Q=wT0)WPjJ|3@$?o^4}#jj#13(SB^_#I8-t!XLO%bxYn-Qg zPm}ogbYX{j%Jny$hE?B_xc|BZ4F6PSjD9t*@%VE~S>wy(-}^qN{S^M${Y$6$Q2bfg zr+ReINDZ&wLi*_BDbgED50p)l z_B?5~WJ=20hQlZT_yZ3DAJ*Lof0zl^*H7kH&Ho23mll)?;m+v-{zYtPod zDGRGw_IG;Oii+2zpXze|hPGX(+-r_%h@^wHtKp~kbUqI?2!iF?`5tcJP@Zo%@hB)w z7ZvigVQ|ozN*~`jWqn*%m5;MSYNsQDwyn6F_^ItGj8sEz!)n)r-KgFcGoyBH!V2}o zA?tLJ!)k~ngF*h0KA-KUbS(7tc9(@Y!y}`FEw{V;D7+j!`u&*5z3E_}{ry!gzf(R4 z&p(bPyk3R3yw7*4_)rs2*n>>^_T1j_>zlC9ALDw9i`NYqTwO7GU0cKGNyN4x1w8xS zKNbER#<^uUJ@&2#YJGRN+zVCs2n&kBU*%sO3he15v11F~Vai(RDOh$5eG?@W+;q-Obvd1e>WWF_Xw1wg7M*(uy|31LA{S=h`&}xY&18 zfKC#oyUV@S<*uL0DqZq;%-JyL!X>wZ_jm zQI^btOL$wA1MR2j1m}PbZmIFOosA{jieaCC6MTMHqeu~cUrK}?wCmx&RKDG1 zsC=MUCtFILl=Rlfg@)=DigFnwq@UQ&94iGn%0*Gn6lJ8&7tO@)=F?R zF+)bsr(CnZseW$g&5#}ZDx6 zy0z`a;kKN%KAMV}T3uAhT2tiD65IW}G+Op$D|Grx9nbc4rKs-rA4yELzbr~Clf7p0 zBzGVs7U=TYbnn+b{Pncy|d&ydYQ8QLP<-yoze* zM4=mGVn+hx2Db9DxhH9gp6A;!vD9$9_QJ zWJQVooh(3+B)-})i61<4KQvk{`Y);V>~W{e( zlbuN)v9)eWzkx-g$2jLzOilZ6a)$8zfN4)ZbQya)uY1DyKf@w1b)(~OVms+C*;2j0 zqw0%1bk(ztQB&@&>>{~wyz`G+{^4s=S>MX6@s4v#PV+|?4LB{m-xeL*R%_@rxjcW^ z+S1PB`~TfDeoN)4;a&}uy%%aiLylWt%Dw`dO&5jp%4RFTF9zqiuV{~qjJZCVOug@n zPqgpL-}8LFH6?mZXFXgyJN#=e4mv2Ui4Nqi=w$`g_KhapdnZ}Gb}chtVL7_)9?7~l zlE?ZC`iRM2_rj*DyF@=Ky8smxm+{G>bCQcCi{$+(A^RbXN{7F?Gxmw(7DXNUO?ur< zA@p{^;N@Qb(%t>itwa1r8ESlAv^991Zl2PZg||W&FIPNd3hsbBZ=AHRTHViN-?lQ-d>aDHFgh6<)AjQ3mJ$*R4$= zkA-!o-G5apToP)U6q@>2(b85UxWwF--~o4xl&D41)zPl?tmlpv%ztVd8Ms`3Bw(bq zmv;=a$mucKCW56obe#P^xPwqLUEBOx#BE1Mt$76!{nzq z2@Yvc$a+`PE_PL2eNWa_1)gf@9Msc#!0#3cyE6{|%7^=}MUF>WtY1j>e}Cu{R7Ip59V4cyVKP@A>rs5w-RIx|~=0Lvz}Gw0tsNDXt)j zKKFx6nPEcPlYeAqo@`Lg&fMg%=F@hE(}hoz(;Ft*4aY5_axuyidrSn6g-oQ^bUHvR zh_=HgM_FvTurOkq?^x=r(;sHEI2!Z;rLY&?05=*hz&G_lP@gd0`o6sXLmv1AtOD0&h$zSUYN}cAi{WQ&DWt{G)-6qpK z@m(T_rvcXh1BDdv%Fe}-;LyP^$SFlT%JE;#B1Kp2B=I0U0q-{Tp=R4W&D*Hu+T8%% zB0})yI^M9>7Qr@clt^N_CNkItiK#4E*3Rn)e z%`E=7-=mOmm3@6J9dhcd@;6n5QjrUe?&nm8yE^l3&)<$@}k5MV~UYcu`dv(zCIJnxykHyO8}g zdAu~WhjLW?QjZCBrvri-sL;dBH5~%=*3b=jLO?1M>kdn@>~=ZWpuz4!%W?07=2oA~ zDmQPh(ot?v#S;6_KUN{H+z#kc-`CQlO>aw}0vcNdQ)+KJ?5!E*^{&n_@K#f1$Q@m` zPz3)Pqy}4jM~KF|Z&EzAf8bOozT!F)5xALYR~LJ{zk|{2V0YToTX@>E5oaf%^9-8ITMk!B zmB?&|n~0q&gbsJ_h-%#iqlqs4(YyA;(S6b`=u4gtn#xuo0Dc$|8JpllO)Hezya(K- z*0R4E!7ru9)D|CXIo@zv{o`^Yd(6!mMXb}N7;8Gf4XpS*3*G4^L7G1 z_>@8!-gd|ikB`VM=NE`bfgp>8sSpT$1Nzxi%x7&h6{J=YE!FdFY2_QNWq)T_@05Al z+^eKSaKkT3s7VA|tS-m`;1xeiXc6s}he?YZyUQwEYNWeegC%dBF1U2gob7p2P;53V&?tiI2x)@JU1} z{+3)s_|gevM`i%^ota8EF%y|_%n;U|mI5=#C%|z$5#-t4f}z$gV5D^yILcN9tRvpB ze)M$a3wwpi2MY-UB*9alP1YJCZHYA> zHyW;x`C3;dNh1W0X%--lb;bMzCW-h6Zj%at7N(zvdA{QwwJE(tfjiFm^Vd#*2&s;ErJ`@4Q|psn(s8SLtrrKhT#r>@y}h zT(W#s=!rb(9AK{CC{G4eNn*&=%C)8)PV+T!E}2bn&MzAV+h^A=ljJt^f?qW~vc+i5 zwKW^3)y^k`e>K3R-_HDx1%oB^zj<;+8K5-PhbSuy8S+%{z0^;UFD&sMh29T;2kh)N zkDA&m-S(@u(fqn+qA4Ld!}uszXwti^F~

SOwO{#J>$TdeI*Y%>QaYer5*;-3BzNdDeNPG#&(dm+?Y))TqLbK@#ZL(PX#VfO@_+WCpJ>HAL>Q9*H_k_UkwtFa>u(CwUl! zL!_^zbEy&Xc`g15Wtpek`^Q*mO^%B&Fk68r^RgIO;cr}7ebu~Gx5khHY}K8zE6~1i zU#)xQzs}Gc^u+8RXvJx-Ps|j@Y`99eS`f&@OKup;Wa%xz@&S!nyV8b&<6bHZ=NLOAe^G5Ite=KsfREpmi$4d4Y{)!_F2Su|C%LVlY6?)QG z40bV{roWlo@!jS!lgg5$H(GIxE78{Wp4y;s0FLQO;0~tGe4XvI_$KpP_5^_xzeQgZ zE98va=CDxa;}R`e>v{qya|vN&PMfWh6ajif9M+l%cWt6cwR)OqYs>jIxjsnU4PRCl z4d>S=Ws_>xx;Qs@`Mz(y5v0^dglw|)4%`mpccykAer*@$%uifll*U zByKh8`R;4fQ{8Sgc{!_FT$R5xZ^eM&5)x=0K>OJiTBPKD-DldTbrx`}sUA``9YG&9 zCkui!J;W|XLdx2f%a^hHm6_-(`$6Jo_Al&Wl{b{Zc3M6Q_`U#rvwd%Gbb$soV#^d_X)(Pgr1`4+) z{MayPEHXsYQHU$XO0T-ik(YahDo6XyRo?R(qA>VY$n;+G#J^no@bi?#P^@SNa~3#3 zT(=}!+gh!r(RG=|%<>DyoRU+f*MH7gtfedQB~@RkYw866WUPT7GPS(@qFCWn$8=GI z*AQ{XfELl0pqs+l!1MeH-zH?Rdo?Jsf5>Qr>&Q}Ik8Oj^!7@od(e%1yfe}zA8-Lcj zng-Y3H(Tm)>$AoWMDJ!bJ)|uHe5Biij5M9%H&{oClF5CNvy4f)8ayd0f=9^ypn=kp zd{?o9aHrs)C>d=Kv!FzJn1N-B$wRWswyCnYmVr{8sfT#9DNb4(p z6LNv2&}Wc}XF+E1STqk!Lti1T$Ow21xEpk5?y+wO9c{F(rQ$5}Nsak05o-NIG~h{O zE*(P+g2vH5d5f7$(QJ0S)DK9M-33laX&^z|2fWVj4aUNL;9`aZGPpnBf@lgB_d}(GCP5IycxV>ih%d)^3H%-If1RP-@z#C>*%xcUGzuELYn2L(l4NS zObWe%jl!P*AIzT61LHE-%Xkn`n!?f9mVRgho`J+ON8wIz8Q4>h&q}1v=rM{+^07lN zqRA-+KkIC?RXeT4A3J0b(Tben}W>>{@^>z7dH7y(5oG5`c*@EuIJIE1y zEN~2vQ>px}wj`OtY_o4MlselP-necsc)I$V6i!;pZaGFsg*zEPZdUl*63%?^rgGrL6+PKlg`;+Q9E3G&-wzUu$eg!qBx z{hscYXHHetQL@3rWYmX#K=lU#&7QQi%DIy3-q zXVM`P{uvo+>BsAAl=5}D1pWtY9&e|%1?{W5fZWj!fNvWcL4l(9T%|RDKr^0gXusG%?Cc> zTi9ANk9}i!z`WC~U^?phGyZx7bIw>ucd=fki^+}jUtls_g|48Fh%)FovREckp=Qo0 zzp#lBcVncyXv0n~^lK>zZVLwc|alte!S`{Qjuv$>uB5axu?(<9D_rnG z<#*zvlB8h8KK6(-9{MDxM__n5{}q)eJY=~fLiJMdsn&a<*G=<<&($;dSW`M8YE1zd z?QDiM4JCIGCvAbiS<6&(hq+w1$UIOw!ZO!RX;aG25G(DD(OacCz%G#<{>97URYOXl zfW0QVN-h_hZKK4|*2SWomS=)HmMOeU>n3>3Z~*!{DZ_B9*>%eUZ6k2A?#@A zFaU#hK?A!GvS*(_vzf<`iB5)&(0{Y}mQKOc4q60UWE6-Hc+5WsE)<2p#p27zPjNOnT(pw6hQFV824>K)>|ex< z><_D~@gQXy$bL8WrCm*rNu7BOp};HgXu1M7gDZ%?yj5gx@h|G7Y${VIC)tsTMW94} z6AF>uhMNTgkel!!#6qt@MsRn-V~r4;sLcd_wFUx_tt**DT-Lo?mq7M2hY+FUDf}6D z2oDj&5Kgk$WTq0P(;e%XOHK{ITE{GCrgAQFUV0eK=g0GQfP;Cf$yaD6%ME0_AqbAp z^#?1oli30K6|}c0hor2_iAZWMJ{%xz$KhJrU34%$mgi0EKv$Cs;YfNW;Kv%NKHxRH z2kc}OqJ`$=JXgyIK5IR}UqE)_SF&SxRfqw3CmaFSOE-YG<@ed!3W9zoKTb7ERpb(3 zHgOT&Omv`C#C}UFaaebmeA}|0N>d-BOBx?C%bH%YE7~3a`;5!La1wx?z+<3+l1&h8 zzXXbK>jTAmnZP{n!(f_MIEc7c0@03NfE;N#Fa_-n-lEdLMW%SDW7}uwabqy-Sr-OB zsVRq6*G56^4c)-4%|f7A`;+moq|nQ$AJjikB4yx5QL`nRsgHJ%be$rcDOJwma_q}M zu;LF`Ak#xF;zsz9;2&fWT7*o5o+Bx&KeCAK0zV-O!Bs>L;26H1(b{fPM{FoL%61YT zY`f0ohW<9St&i;iPTR7HVZ?CCi}GWJFrmOjAOI~7BNGC4XxCgeZuV|@t0MOHl!Y1o=bO3&hZz1jo-07u) ziEJV7DOd^rh1G06I+pCkKVeY{Vhm#hH#D{U#MTPl=9bOq?AD*KNh5?u%U zyTB{-8_^WqY5sKmeBoT^kTJt!55V^T`|J23tJ2z|xakWIji(G!LSVSz_o- zwowd^#MtljJ>UlL3hWABgObs?FvM?!5kVX>O)v)W7x*AE`7huuJQRM6_&{RV4eSr% z>>jp(si*byKFXKgMxLR55+kW2#0=^^@t8^^d(wj_9V>DC?olH!l6Kt{ccgrEVt7QPQ$&$ccwQdD4 zJPm9mKR{QQ<8Tsag5Se5d>U23FL_rW8*e_io@ZgZp^KQ+a4V$(50iV?ZA1y3gP=Q#&Fnx~P&J3l807F=JXczbzehoJu`RF=y3BM1zO)wGpD#(Kd3vvMiZzofL z1XAvhm~dj3+sx#4YZ$)ST4zbHEjE|oT9X^K()^ievRntF@fBS5rseswwSpl~s7Q$( z7tQ2*ikbw!1XqM_cuR#KGG8D9AM=d#IwYE;LB_V8y>1Plds};u2J2`15Kh`GWS#9b za|FK+ekWp(OH?%P4Kt2!1Ge+s;RaqkavF`|HFE2ezL15t3z*Ft$o53nG7sTsrV^aS z2!IAAiP_8QC_ezCb^^P}L%=@LA0WtiY#}v;vs4`I&yJy{1JB9r;8St~G?)s9*VEhK zR%Q!)2?&Da&`u~A&H}UGe4rFAW)H$`Oe++~_(KoqcyJ{>9oR)rWXtKX%v>gkM%e|_ zO*W0}0)&v~02@&b*pno%g#ay zBl`#FL;YY)&OkJ+hSFIh^a8Acvf+!+4>TUS$j=1RGd%u-5^F!?iy#^cNkBcFX@I>h=~J-PeCPk>k^ z7W9XnfaNF;q6J}4Pq7kOCix6*k#q%B;(vf!!Z&O;ek(H(5i#q*Y4j&1m)b+AC^fN> z5)*GIgy>JdBv8hm(lFd2EE@qhfis{WXc6KGSD|(A2i|%lgZBy1p)ZjB+-wWsqU|{L zGjxDD0k+^Bfh;RGx3SEnGt6<6qh%`D*IG@);a`Xcq#L=6c}e<#7pS9fCVdwD&baei zSUb*p7Yo*dae^A~Am0a?$LqkY63d~NPz^X0c*JRU6BtK^gSECwpvJNbIBE6*DAOG_ z+8oP{vlKDmw!O?%VhppM?#(O%hB4#e6-*WSfvMn!urb06wm~=@Xc2mVQ-#%Fyx$2EN7@TD?`4+Uy)H% z7HMYuC<~ZJWg~a!c>Y7iUHF6z5~Tr2qQl@bVJy^BaFzRw!Bg%LytPmoPz!44K=35l z3rNL>u!n5x7$;jXz0#(l*Wd?eFo&f`pY0yyg5Bw3eak4Ven+So1!5OFd9C$6&2O2?a2Ts~jnVnWAy07&q zS#8ZB61h7B+sMU43_}n-KpQy{=|zkA>C6ye0PsbGfeXdgAP?~-m?wge2tg3?6-AK{ z_z1iRXoU)B7bu172-0{gV2>xURkrQSZu~d>oftzuq-1m>1JdDOES&`3rXA4n%y?c1 zTg!6=@_GKC9WNXzLZaZoupBuEendz%65YvkMpx5+kPJ$JETHtz7-|=|fl{*%sF6$= z^_+s}MI=ehBJ@-xUQA`<@2Md~E;Wu+Q$e*(5J+0a<_0x}E$c=4ct*9nT{ zw?S3BWcUxd1)hNnfR{t(pd{c5*n=4k45JE|_e2l+I-W*4;iCx`d;oqGPqkel4syJT z;D=}$AHbd`Y2Yz^1M&kD$O9-Em8112&hzGV7JNky3s98vYdDJ=PbsnrxCGs2h5&Lp zl1U}EP({QVaw!o)?j`P$p=1jcN|i8s=ui-5ufcnPR#XSR=ZoMwAD#-1gC;W3+=J45AOX)|UT_&ijJ1TiZ5==%xQG&vK^#L|pgg%b(mkjbQ;G~{ z&+~=@FZlz(UII_ZLy!SY@w5(u4 zEsb;s%N_cQ`W`hfW3|I(`M~{Jfd9|Pi-yQ147ehCB z1>ik&AsCM|0mq@?KsxY+{lLVrB~&@%K%QjI&Yq|2n{K)b0x;38$EQJNvs=!L{FW-xx9&9prM(rkHPA3PW8M_htmkv)** z^f6=>dmR}Du0#T10)C22gB#HO&;?!^n8(Wj=J2%aCe)5~MtU=gpmp>KAeDN}(4>T( zOr9YNh|R=);u*e$n=57!o$+LH3O<7Rh##eg5-=;|VpJa49m=8}!_Vm&B%67J7PC&g zM!7 z=tAlU_xA@s>2d5}<|6QpDF(-}B~S@_7@h)<@K=D3bmH>V{a}B%1Uvz82nL5juYtKx z7B?%aVn#t;3?JG<-vT3O4^Tw!09@$r>~cDs)zF8TtBi!1#XhGKfNiupxSlQtPtjYT zYPu2{$B1DQ(*!+Yk3t6l3fu&a1?NIXfI-~jWCYxgU5Vr}r;)wPG-L&n4sT=LL9dv# zpn&xQ7P3E>PwWF)1~gKmfGyMtU^TT5$fkAy%V-r4$1DVb*#Uqf-~ki^P3%GNJxhSc z*@4hHHXcf59ie&b1Mo2G1Ab&T{6A%#2fSQW)%Mppb8iw#NB|)aBoyf&9Ra0xK|!h% z5v3@IfP%nlK}DJ<2vQa40--2P=}JdXumCE8V$gsfNH0n5o!RSq{`0usxxC-xckh`w zXP32}^{le@Id>9E$1n5Tm@nU}{mALf8@;94t9r+@7xsSM-qpLVU7{S>UQs5rd*&g{ zd*Y`W?_qrv!K-EAc9HTGtu(J}AMai%UVOGavfH#bbU)cl>FKVM^gpYVa%_IE91^GW zHgBf(Mw@?hztU#+m+f8}dty1PeZI_WX5@;^U*ePTv*yIyuYI^Y)vi(AZ13$o(Vp7d zy*;USRdZ+W;h3vDp2w6I%Sc(i9Fu4CI`QG&F|lj!#W=OMQ!`X9YtAUoHB07#?X}u5 zpB769&)LnRab?qs4>xUdr{rEYPKfh!kys>8$RFwJHa07Vm$}OV%HlJ7!{x2s8fC}w zrSfLEwfr%s=6~YzIi-ChH}5PKn+$zEn&InX$Kh9Fli?+r=|fvIH+S}H_G|ZPo{62C z&2r`Dj^3NGPxr^M`{)jF+sHe4+Q^Ucg^}&^^3lmTqxcJIzih@2t>0eKIi`I?HQ;s4 zC)-1#*G$W=%eK$Qo87JB=8!bEBA*lp(uKaWPcbSk+mhH5{_)bnxi|upX_`dl0Onxrj7pKMEaY=kRE{-q7 zsj*cY60>uiI6Gg@h4WHbYL{HGME<6COF6su?Q&Z0kaC;;{B%mWsGL=Lst-PskLQH= zVq6q+V!LJ!#j)=;d$y-F7qy>le%&tJT+u!l`?Oz;XPOOTgJzBRc5D-O=Xo(DCpCwa zbDNRgVq*Ns?K!>s#rQ|s#me#R4@#%qDmQ9o=U#Dh?3KGW`B1H-S!xx;V73Hr`|~`rw`8o4=f`z1p;@Wj zv)QI|ezVBXFPcM#e&2jz=#J)(ol~24+K)BAZDz%{;s>!-t`OtPFLPq=BYAZ9jk0z3 zta4)a6J@>L>Sc|xsyuXy@_YSJ=Hl(|%lkVUR#)3oV#L=BO@r8D~X3^$r z&G~tHGo^f272}@G-J=tljYcnvB}b>l8Kcwl@b3Hairx+4&nL@~v0}NeS*|?MZd^|5 z{Gj}!(<{I0oR%NyEFY(fo5waEZw}FVcZ-~--J(ot59__vJl8#^xw5->bCQYGb(YCDZ z%J-U^%6{!3xk%^zae3!SeZ9_}%@Xp#yf(?g2TJ4Yw~niI=c zbK~Bsd3QIp!hfUO*?qfwu(w1`D%~(oZfVB?%B=ez1x}t z%cISGc|)^ob5wIg`@ZJl&W-Vnp{-+{;fM0Q!~5oMhdMblbid;G8JgpSGBv(cE-ybW z+xGrk*6S{jhmL+B7Z`mk7aCnBjvqZaw(UNucs?pQ7ifN(L(P3LGj3|Giap!?;@WoJ zc%Xei8S;N}&35a&y;&mHZ6@afv2Y$FCax79&&A>t;d4)J7<2MdF)h9xJH>b7Gm0js z#u3`9*d}g>h2!q{d)_Y}y+3}OKaXqjtoW-&STRK! z-K%?l@0$Ep>E?W~dz>rG7wWtaI}J^3wi#Zfd3<>3=DFc$P944{Zd09pN@s~!pnWJ0 zjBn(vWviUhTO+R@T_w*N`FPHlb87xz&WriboXumOk;`L`(YCp;yJ@pvIjFfJk7}kh zdp4)FYc@aYycj6el~7zUQ+fy zQgMGc-y74*LwR`lTJBs9&i%`wd1m=u&MbH4rujzRp}K9U*fvg&-QuzMuwv~}@t)>i zIbU;bz7QYITVhT*CLSsq#?|uIyUO45jdD-!m_Ny1<@DS$u2ZW!^N8l@e71Qj*A<8M zY1fXu+nr;zcAxltvtR7l>=w7g7O_~Y9EaqB@to}X#qxG8UtZ10r4tJ&M}4$x96u_1 z$4EIPKAl&_19@jml5gx0x5hV=gD;K$i7WdaGk;tWGxD;yQJ!*2o)lln!&G;EB#y|X z;>etlrz_iCoSR6mr^-`#YI!9$DwAW1?&3`|IvPFPF>mPQ|Ht@`uv1$X~Z@c8>j<2V=Kp z;bv-caC1j|zu7pZH+SWE%?8@-`b$~AxuEuAio-GwSyT%=hc z9#J)QNSvf^Z@V%s%^<$OTQWf;Z8vR3YWGo+N$NX`lyyL!jJ|B!f=S_-p zr^kJ{Q~XK(`&9loXXlnVF=nXhJ+EvKdzI~C8?EwpEZfA_%Np@`St>q|qxrM^d#)Py z=1*gKPH8U6&&wMwQ>?u~HSLdu)j`eMxtKiVhS)mRj>F>CJT*3uPLC+R9a?URHOpl& zv78)}%O_$3`RfVd;p}oN*#*Jl@IHW8d+m_M%SovpuuKXb{*P83Ka$jznzsoCB z$BxS8+iDN=45590OpKSc;+n5{Z!Fm?9xFA|VnxZlShI}o-z^?9VhPRh9@V)E#fC!l zl^lwnWW+8xD;LYR^D)VNYxz%JP_%bbo|kQ&&7piDcgq*${ckIe6lJUV6gd{tn5)O< znvLR=W;-!b9MaD>^s{ZVSqwGn#kH|yd^F}#eLXwBC{K-iDlaOJNN2}gz+eh=dwkzLQHm0<@#%awL;}xw3Hj`hUpJ&G0`K>ssd|JF& zEvA*>SWftFt^4QamoMhqWd~wFJ|XsQ6(7s#icPPo-d#p(-F2HztS5}7H3!Cg>r^MlfR;W$8}d{VW^p&Ip@d39cy=Sh>3#hc@Dn!Z}< zgK?5P^PD^&uFZY5W4}+lo*xyPJ`f+2^gC)LcSt;!N5>!XD{-5CZdR>K+2$b-e9FR7kAO!I$JRzFjI za*gzSR5-tq<70tXJl0VB*eX_4#j$F9T>B3nm0zqIL-LGQh0k;OkTkg@|0wxq{1$RUOaOO6RgzR@ls~3^tM{DsO#767HpZxwUjy zNvJL$OTQxv|4qK|fU@zmIZ1WkGI3Ta9}1gGbJw^-J^rE$H=66ma>~`)N!uf&?HP(s z7Yezn;=DX3&duY5%QW7Fxt@;OiCa5rEY&EvnMPV&8FkUv zOFn$4;^pTRFZWaC{YX5bmDD3zy*{Pt{1wH*Ms`|QxqAgg`juj5jkQZI8=uqr6Q#k8 zYWde}#e&zg>YgKCeIvi4l z7Zke}$h&j7{7deh&*XQ-oWHA9c&`|Dv}%W|;$zCFpHj@)JLZ?4PiVFlr`M79ETX#l z4e9(?zN}F%&+p~_`K??_oLeZrrabbkoL|gdK^X5L-kmPFpAZI%#!<4o`;ai4zv zqH6oE@wIqZQeGKr>S|92&5(6(Qnh@(JWgMdDb^m8C*=Y81C2gik^JGjOj6#L9ql(P zrg~*fF=$J1Xb0J7XK`pJz1v1-Hqe!owE|dJ5q4bsM{+zP{=lbu6tnLXA8(U<4~mn& z*ZZ0J2TJowhUI0O)x^V1q~|*FiS=S7*=;F(T2bDyh}gfds`T-abVR*h&ZouAf68jF zYov$=#ioapt^bgUl=9?%i9xfp9vK$br%I+3#rpLWdp6fBTPpr+B|B~qzgOnFQ~KPI zUQ_P%^~XpXxS{cn@k{6@9;6Jq|WxwdS(nPl5RnQ#}`YI}L^M`W+HbX-Zf zxubWl<&-|7ZdToJscd|@BHP&-=hVDO*xn?L{UU!YEMAh2&Xylc6Hd#^^EOk4|FHaM zS9#*j%J)0P`{kFb$kP^-4&$Z8+o}hi5lf#Ceh;eZyG!-Xy{Z9kmwkTS|2a)Ix>S6- zSExRtZ@YLumW@=$YKB#1_s!M& z<2vr8v+ERYHvHW+_z8&V4HWM}CM4g$e7USa|V&_w0*i(uxe-ay> z)6YLO&fEGavhiGE(|aWKR9Sv8y0V$W<<4I|Q!N~o*% zsdK4o9rfx+yK#!h6NS-`EIq$|hQy=!gygV}Lqf6D*&>NX)pC|b!cK3=B5+tThW zjWt_i&sJU-mDi1`O;IbpvsfEqnroc;^KHiS_9ZWpb4c@!NQaKB-W3ab8U>kW%gSB# z^}jcD>`B_!bhgNgXX$ezOBS^mlDG41%JWFB`83ZY;fSp75h@D{QH~4fXOeWApx@(V zyRNjDqrPvd_iLK*MZJGrqyI-U!|f4$PW{Fnll!_)k|)ojxG=wXw2K3h_~C5>N^#Q)JaFYD(8jrO)aztq>hBdZsUJzhOp^&KyHhx;;fTuAYa zbCb2RI$pRm!n&yMoARXBr1?wwG*i<4OJn|BGr@&_O6r&N`DJ~6OT6d_Kfa}!3SeF# zynx;>&_DL%EwePf9%a?B>2m~o&60d`#EDtLg5zv)7kNkYIV5jS*ZJmZzNdOfS`SO^ zVWBZj_)h3!L=>M(`b^fbqw{l1htTiPCm1nlvU85 zYvXjj)p0_9q+$JLl(}@itMNckf5p)6!FO`ck*vIC3v<3R8*cSv(@x*oFno^AkEkb% zpQF#%xhpJ2)GGVu=BNjF(|1(wX7@*c7tGMrKhA>#En7*@Q$OAzJ>zi=4#RZ(6yHLM zR&o^~jAeQnk6v!i+~fMKkpc7=WmreX9+pN$GLF~ip-+74_mIxm7PJ6AP~)4yi$0G7 z1hphWxIIVY!5WoQx-Q~mt+7TD{wsU^l24F{j|6noZA&)d_L0{&kx4&bZ zk@zljB!)S(8WK~6`dYyiqz9XEVh%Hp*HthZC*45}o|$Dx4|ADwT>l4sL6q@1NB^Nd z8pg?TAdkP1z1z1mGJplxp(UE}+Yx!TJ`wre7JA@2>wmAqDH!S4J<>nIMf21w<@0DC zpV@uJr#K2qU5x`nM)gS>(;8{CKO^tVTbRu|;wR5m~r@Q z{6P_kVIER~6dVChMzk;65BQ%DIL~!>>fdC|5w#>{!hKpyD{!3N};K3beX2FB(^ zPs@*w!)x=ka+bDeZZC#2WFh*173}1?c?ut49Y_-;s8^z&Z9o{C*c&`6-1Yy#;NOnK zXoeg;;n9dgM0VmV7Q}w&=J(bO&fo)P6MbMHQsRA}XgolN_{Ny%==R2lIk6Tjtd?{P z1%J4TM?1#CNwd|_6ucb`urRZL7_GqAag{3|L|f+xY-02{!cfkTk3rRE@OrlaZ}{s> zuR8|%Or#;3qtiTn>G6MOV{iu(_=$b7HL~yp?Bjf=RIYOdPSB2{eH?9x$7t^n2JPwV%nFjk0%9*Qkt14RQFy|5aHui? zT+AoV%+`CVo!M$fEdx8@htYFe#vx`P57&{;Y}e5J7kz9!TM(~cAf&R|oO+5#TalGM?v z_23R{?+MXXdi2B)Mg;pAn5)kwssdZkW56Y>z%1x&8Ei51A-)j7JS!;S|9B?3 z*JFVQ^ZOiZ$41Osa}BY=+G6Vgie@SOT(yEbOmWRdB(E7BP0eLm!!Y6nj3AzXDd+7I zco4F46&r({Iqpmbr#Q!~L`m{4)ezF+y;uQx&3o|VjAJ8LDvQ-!$c>W(-TwND@rVwv z53ATK@Bs6ZTu$bOY0fU3w@;C&%`u`GvD1FTXl{el*a(Xd7lS2|hM5E86*4HCKu+WvU;u*70F3TYK?nQUM~x#|V|Dsk5+VY9Xk|MP zDSXE5xL(^^A4f{?XDr6VM$VYkCU~~lSFyBQp4IPt#Ssr(u^@67Gi+^595XqK?+{b0 z6}HDZ<`mvfoIyIz3`%%q)yPqaF>1Et=yeKq#-o_U%)mO9r_PHU*v>P-0r-Ni*39OJ z%$U4$K5}J7?DhPPY}l1N~2OY~+&qhEPMreh5P zgJ1%7wcQ;zbh3{Wl3G{v@qQE@=e)>OdOCVLKEgyxh_x6K-PB0$kkcsP!Nd*Xhb;nM zEM1MRAY}%iFIQnIm5*^lE?$fY!?2iH>{^&;j8C8`+SwCmi!6+8`P>URoCRS!h&v|Y z6>jJL6?f(~HrCAS;0!FPY(Z|z#?j}&0h`%F7zKWF95AT*tm6)N6Gz&S_4eFeX zT@fHYQUkM=cQj{g`#bzXetMd7MAhnF$i;bH#1K9+GG5>c#8x-z{bp)Mf7{S3vs7lA zBM~E^A)aX)*=7R^*n(Kqw%`bpjSE*{v@0=Wsk##HU=j=`W5ZB*0Q0JM(As`E*3PuT zzo^HE4B+Cb)DahHD_fb3SyHqBNTzhO@I2Urc4$F;I!o>6QZH8SaNY7c?|}%q!$8Z1jE*sASnE#n(msSeY450S zYj7R8d>(Fk6xdPYof!#w{4ftVVl$3|c)_^-%sVm%`y$=}x8Kssmapp=v_pPg_>_It zHJQCpC;R6e)#z<|^t<$XVRJ_^Dr%yPNK^@z$GvrJ&sJ9$1;4LFM2bpW2;E2E#`w8rXmE;P?rRo** zG)~}6MLMdzJYp|4h22!j`r7b*L~#7!DkGZx@W%Mt^RYAhhtFKGbdLK~UR$kN-SS#) z`f%QitG1wCWhb9KT8`|>VjJ(Bfgf5Rg<}M=nT_NYY~vh95AzpG+E=KX?2+~Zk6^#D zC9Q`U2IJ5Pn;-$PpjOf~;_GssMUE(z37$F1+jC$#bt9O24d?9SSWC2l#pWBa%GIu= zuJ$%ZYW`qOUi?Ngu!d{Sa%jS5q^PS7+lCm(E)7*X$I4~f1AfpCep4YOfl-7%M}G}tQpY=t!aZr$Y^k_ z&S;->M)&xGcw`HropT6%SW&WmC6!Q4))KQ1K~S$ zBGKIUBFsP62lT>A(8IPw7Z3+~&uDoq2{$gl=EC%wLUNbNh<>wiC*0s(A&Aud#L0aurdFP&!8Xkj>XzcHkR};uR}F7mcsMc^>N%G$8bM~yDSqF zIp*{$7v~HtZvIt-)u}%kb{t@XB(^lyt&zD0I?jynpWNf>tVR|rTamL*d#%9eRq|Q` zxxTlrQ(tkniF;~}S&rmnW3sfFSZhI;=UJ+^+ltKKHABVTTG&p`OSC~FYo=lT`;Iec zN8WU7C7u{_M<0y8Mn300M-@jQqf74addC=e zB(J(T0|S|Xe}f1;$Q@=V^YCIuUbe3FafI{;wlsa1)s-%La+MKvE-NEGxs4dzpe!aT=kP%_ePTWc@pS680qi!(ibKGa_?QiHpCL>v6j<9eSJW`io8 z16!Gg?8*HVcG9`e%!tTnE25h-%h*fH5VH4jk<{SdE#`v9A1Trm%In?&!v6 zysX9zq6gWFbz!Xu2h|KXI&M36Ixv7fUm`~QwQwK^fU)wo|RQn>==$W5-Pb-CMAd})Ekv_mEk zgh4ga)*S}t?W((xHWOh0eK|S?+dkDVXp7HKnR@5l(mKx9zV;f}$oa}zq!~M|ueg!| zdE|v51HJtj)QvCGg@9Q1RTpI@UgTC^o5g3LIguv?Ct6 zdwzd*oPw9&%}9>TM0Ve2ptWPFd-BQg%cC-mxny0@87n)^_}yR@=Nfb}M%CK2{xv&m zjm%7FLsarTn7Vqawy?b&mn{jhpp7eKM=VffX4f3d3?l|v$Q3O8s!m+5UgnCH&#u<6 z%pe;vHn>?G6DQ&FfM$yv()o4gz29H416wr#^1)=YSl?{TuP=5;vhaeJ3ns`T<& zk_<#9!aK31aUMwQ7>9JsMH{s9ddU@)^NOoEyv5wDGV!Ut7=!C$XL5!kGBAfNfrsMV zjtuM&c^zZC+|GJ?ma&ZYd`6Ss_zC@}^@#Y$Z=a|ZGv0MPtT6B;aB(g)lO4ev551B{ zZ$}!Ww>Q-OcqaIn5geJtNLD!q{#e;j<)g3JU{+hMijm_LhlR<`x47Z9WEXV@7Dh&|qY^W7EaSF>v1MU_EV zz;!4k6PY*DHwH$eQ-pX-w zH~-L$PqdNJ{g+upAIDbjwb)<1+k?cm0y6qOo!NorjspBPGK_6KLDAOW?kzQzecrLj zm4$0TM=mU1_f8!9v8U@Z?@{@Akxu_Zqb_8r5UbevnOV_3HihGc0beckFVUan59S40L&Z2Wwza=S?a#e8~I5TzhwJvHmp< zn2o+4hwPy25s|_Ca(*@*<~H%cadE&PywIrHe%RDr1Iot4mSZQj-glwmV!p~uv%(y~ zb~Sb*10Dl*pva3c?WOdxCpfNQGs})FmDiwcCe?`HjBJc+gk?-eCL%Aq~(y$ zZ-XtorIr1?*51rPOLnTbcjLd9sPUOf11~Vg&_t8=J(irrGkHAK=5;ysAV@fJRj;=G z;KL}@H_)8-=2p!%pvLITGGME1O^xRY*E?h8mu&_EJ*$sqE_N^@@jW!KZP46L)q$bC z+1OiC^GQPVdpq(WE29~6?mF>Qp6f2JPWfDG7)G$I>&n&M4niR7i0`$LQ9*0$S8Fb3 zT(4p5{oZvTa~cI=n6b77RchmBMpTa?Uy^}cX*qhpGIP%U;23OcF(Pbp{ID%N8?)mF z_=6)bcA&Mhf~{cWU>;K22W!58eZ)ZG05e!?Gs~7YE07%y@IpdnaisvZ?oTF$ukeu7 zI1-VY*$wa$bKGNcu6A$cbDVGvaOQ`r#tI8tddEln)|mwjkzpVgl2`9SKKpR3-@q4n z%~rF=oM#+*{@+Ys7Bi0T65Cts2S`&BO=eS~g z>+@=^4;=~6s@kOA>A$mjg;Dnau`4ryoAG*gPJA~X(2{vwt-*ZfD&J$S(TI3MwPBkh zImqG#L^3#0Ia%LhC(moY9;>14f}ZY+UT9{`EsHau8D2Axx#xbK+uYy`mgDa{{G66& zFxv(Z4Lsl?k(-%mh2>zQpX~r`-_1iBQ1l)nf4u@0-nXdCANZ6<0u_4UmGnm^>sV*P zD!#&*%30%I?_l~a4|9ME49C8B5o1@6;K=-Sz2pp3dFY)3IO>|h*7e>}^)9pn4aTS| zDYr*L`>u0*9mCeH+FL)^GgvhmTlBF{*9rud)LI%ZLIzvicCc4FE7po^>^jX^($73p zDO@8kj_piDb*8pe1%H1IbJg0`s@MMR8nVVABsNa0FWEJO??}yfgBNnc#2S0JRy_zS zRNq6sv9_~kfM~swH>lms3+v)&f|lO#^(xhKFqawS{7~_>Jn#e59Lumu#mZF+krRuU zqpniCF6HdN_8!>Hhw#3b{~ zdOK>OrI}Hs!b;?5?`UwXB02EXIvVmLgGWJrB=yUda(&0@!%s!=wDeGaUFm%g*j#H_ z*YS+uHHM!hBHJMgT)^91Nx8}}4=gdQ;2RS8g&ek%u`~Y$F%vZ0qVnAP?C)O1fH!rI zpGT?BEYupxcCa7Y?zW_3LXDmvh&8bkvpVn8`^T==UFnHL5U@j|t-}~`C6z$NO9W*~>Tz6Y4 zrv|KbJTQwuz|qdKSL8VdDj-hJifYYxv_m&^#WwUdkG%e<9zkEOSvssez}mL1>jCty zIg2Z{Gz>%oGYJgeJz7Q8)D%CloTAuw_eBC;k%h(RtV5jrQV0}byv$k3r3GC@+44**9_dc;d>si-& zaE*~|1rYLB*a``J2M2o*4ajw7dX>wTW9OqrJ|Y*njlCplkXj?aOr&CF*6SSY73K-H zb<8k_`R&z=tFW4TI6A_Eiu;b`3BAyVIPCQ&5dxm!O@nnFYpS}RU*Vvq%FxT0~@d3<`(v=yKc@+z`yffNfCpd4z;L1GT@pmoD z*yj3JFL4YWa6%JkM&>lfncH&JENte}!|S@bMt4ns)v!)oKY%a&&F?`(U=&d0FQObT z$-MS-M~aG~`(h*Wpymww8(M({JarZ_lEfA7oqM;78p~eG+$nMdRyjtB{Vr+1?wOz^A_=#MVu~^go;5ck{ zat3YjUPmfpjg(l;l9+pF`57kRzI!OIRBoGm7Jz|H=%$4xV9xudX6F+IPKTCoV9D*)ZS++Sntk zod)R3;Ye7Y`5<=ka(wYVl?nG_71^v8zj+P(&7SPI1LJHF(DJO+AK-wqv*Vg`BRp|LvWL8T z#bY#?6A8SRQ1^_CP3<)}I{LT*F{{i!&evSd8DnFcIAh{TbyQ}nt)2O+4?BnWs;er; zQuBWBqzpE;O=_$%idE8THM|_=IaWA+RW`HkUELW zzisDO$Fo>oZFs)|&R{csn+td^^UkGne4h^S;oUwA`>-c3v&{3*$~bcbVP>vAWbbjb z;5Q!Z8q6L_5A(|$s#YI+56!DW?BxAfTfjLA-|@VR1J1-ujx~b1B6B7m8%vD={HPg( zxgGJ$6-KPF&fdmvW^)`uM|{K;n{mXl#@2r45t##)!75^pIf}Pbwm7akR@53~>~1vU z&=38_K7&{H3Gf=X(VPE$4eVf>AzPivo@%}5fVR9WG3e03+_b#(b_>o{_R$8sYTW`C z?9p&)5Os+bzFXw|S$Z%Ak`FAvJG{bim3Mvy!IAp^kF+%|V--v5JkA{vX0XhaW7x}l zH9Hs|xoUlGt;}EZv093AAji`X-d#m9tbzTBC0NJ)WRCbV8PM0!(h=C~aUDWWP(U76 z(o|XA-}N1GBSBBIr$!-1f~t`{5(LaUHP@@YckXeFFlWth@bvl+85o0>HP0X?{!nWa zK4Twz#uBq~p}O}u+L~X3J|5F?(!9ko)P&A;cq_Php9GsZ8`rf?&4;zZ9K=jytzPbW zVW5E}#pmmjk=~cI4UM&y<)yrU*Tw^{nWYl*6;uEHvkgqoeVFJI-TN`#hV?iWn`iGZyoHBtGnzP-mb{_Z=m8ihPdr_JbNH zv03Fi_B0zUlOup_N0r5h$Yv`zOYzhXGSr&Sqt|N5D34v6F^9S5>;`uR9?p9tKq6bj z46B@Vq$3A`hPm%p>lp0{!TSk==e!1Sx$?fv|uiyfE>K=Cwr5B*Z%el+uZDg6s;o&^{A5KZRkQpNhEUZ>uSW6jMqbc#s*E0o;jGwSh|uKiTF@XqxX6%rYWNJ)gm zE=E~G>t&Rxk9!=iI*^mL%*$NhQtjthh$nfc2qbBN^*n0z16W~}ql2?HZ9Fb^wsu(A zdRZ%9;XN{wnUIG$z!m(-J1}YBugs4|#vSQvzBID*#a8AwS{fN!Z?L;Kb{=cbjFtpD zyXqP1Md0S^MxkPc)b-gvqw4&^D0tXFOEZuN=IW8VMtn=^ys{(TWD6TW5bbPrdgHeP z>$^I!H*lVJW2FKe#g!4X>;D;ibru@J+JIFdSsYmooS;G-j*$2Oa7D_3rw zk@Ic=PeH;FagceqKG4VU$eMr&x)VoSH#u9HPtNMr*0IcTx<2q~f$T?Yv-LsG5yXE3 zL|u+2BGCkC=D!N^-}VzH(59kZHAm9Q$Evw4=eyt3TU@t~*1CmJcsVB6HWeRp0sII4 zVvnm5*e{GeI$A?!Wfn&@G7K%Mg}n>OIs6RU52_ksbKRe;>lDvkxyT&oNlSZ5U5_{q zU>W0IZRGnqgQ^p5IR_BGu#e@&l17;7!_lgGp`#*k#dk!J$r@Q>M(}8`gxG*b)~xFF zh}i~zh&?`felvV5gKSe`hxM)189HGdkQe)555RcC1~zD1FG6D~MP@Y|SI+rp#jnH-r5X^sK zRLx!$(|4@qd}5@rD~B3aLC`VRjE4_ccF?-6p)C>ZeZ8`y)<>@JYo>=G<^ncEAIDrO zd%k(j`J{3KRL~NQnSpoJ>uOc#Yt;^L0SuX)9RVUIPYxh08J75Gt##-l2uF>k>)@IX zVKQT32Ujb23uk=RRl7X}smwWaLl*ROZ1-9Pja=g*76XY%obaRu^k(+v^i}H)%XPu>^;uF=7eo#-|%}g$9&6mi&Nvet3(um^5PLU3TAncO93~&jE+-ci8?193CI}^ilh* av)-y}tiC!wOMd>KeUCcq@O=+DBK{vTTq`*M literal 0 HcmV?d00001 diff --git a/osu.Game.Tests/Resources/Samples/hitsound-no-delay.wav b/osu.Game.Tests/Resources/Samples/hitsound-no-delay.wav new file mode 100644 index 0000000000000000000000000000000000000000..cdda5709b8ab904d2f5980228ad02010f4fe368a GIT binary patch literal 48210 zcmY&<1z1#F*Y-X!9WZovDWO=Xh#)4mB6eYSBle8>*xjAjfgp&2iim*NG}1ZLFvH~O z|M))N`+xs`&2{DsGv}Y>W{yqP_ z6ISpi0!7b-KoO>a6%}8B0#*R_1uIZR(@CIM#XXs~~WaJA!F!H7ojQr;`TK+AFk^2Sr*!I^hSLfL#2K0s$q}(F?udb%4 zKrz}DDCU*`#g$Lb_x@{*S!beOgPD8wQvb0tc7m2W-Dc!h8W?$$nvwrI`@hl_Sg;yP z8Tkaor13!UPaJDCAyAAu3@Nr>{5!|Ln*Ke*c|h^X3n;KPfSqUedyM>3AuX>{G4dUw z$^Y2$T||=afFPzg&#Bjg9#ue5lnyHvrT|6sNkn1)0w}iU&%ZGC$EP<6-EQ(j?{VQ- zi&V=P`QYt;wMpCHw^ccwk`D}K}%KgD`@=RKDRf7*yEKZiAMDi2}y z(S_ZfiVVX)F^?u3;`&6NcG)^LBQhgWIkh;*e%0QHW1Dv_zO|f@ubRimd(2UsdXyA#_tnkCtz=W8|?ejQo8!)?Yms`GbLs zeApO9ess#FHTjdOyB&?0qD@)e`jwVDWH55GHUB87L;0fTFk-DDHoT6?V6QqT+v|;oq1*|0m}VHaFsrvoZG)D2|lEighSZ zg!Y=J7_c;@jgikCjk9C^zuHjjUpF=|a$P@0{-dMalysx!|C9S~CPV*E#{CWj)5VB+*CyxwF_EmT>cbiNtAvd)@+W4rK5^PSwJ!58&GViH0{}o^gTi;sW#ia17g`s&Y{Jy}OZ$Xd$G-DAJk1)c8vXs7 zvVZf*XC6Rtn-3}8w*o~$e$tVUr~1TcC*o@%#YRt{XxQ`@&;DY0{1rxC_JNjHvT>Vf zL(2u>qOD1>{?ggo5?}nwJ+Q)Y2T(j&1}TyzLW*qCnUNQDu=!%nB&vEy_4iqb{@ z6lbwKlf=frV^lHe5m0qrihK9UJ9dYAD-N^1q2_q^qA|laT+fcadc~8&gPadP%nk+ZBq9~&C)%jX&Xt*D7 zuBgWft2|o%_#MmDsieI8CCl6FAxdSD))PEep)@wN2TqijS-}M?C#w6M7KbW z;sxuI2Vt;67Rqv1FpF8NUEi}Youn&2GOPOGSLL??i_ag$)46Oe?8u_!ML!t%9X5__ z42(QiNXzd!GxA=6l)Syaer7;~(S?ZYswE;HyciwffAHqOzsLp@5x$V(vOQ4T5kQIo zEJp5bOgl>d-utvAQ<--p`CQG{CmZ!ak3!78KXddR{idbw!~FYErn-OTo~91354S(F zZGRuO(qK74G>eg^$M)JHnfEyMV6@E$vxqz1eSC&dcX>SvGL>o1rscmo633!y%1uNu z>LO5Fyo4yq?*c`8TKuuw#s9p2YI>vbcE09zAR={Q;e77)4gaYQ7&*nh{vxi^PD*}n zLGrHW;S(0!wEZ@!tz8)SB5SOf>iW^HrroKPkRswco8|8y#k|Cfy&d;&+%CAmd;fJ>5&{qm5v|L!x zo5edDOQ3ja4J*>wd`UBpel|#msW(I1{sqQ;@<5jPrq|Q*#y>13mC*8$EO+)RXXIQq zZtrvy3^peZwR#BE7d194^0T>1`m(Xn9ahw{wb=_FpqT20D9+fSpMA{VIP~P(Evkpu ze52UB#wqzCHXm(SORQ!X`CH^|ObGwHgexAKTOnyZmoH{biqjz5!2!-^#K{!{HN-`4@fIkujh^&L`V=B$13E-Tw~JWD;aGb`v37k~N6R;}Sn!Y}<*!*zX|UD|V_e^M?)6=N>ytlFBnGh2)g4msy;+2Hf)#$O z-yZPyURXzeZsZw8_Zrc8dQ(UnB|pyAIZ5r5oM!QJCd(%#h{a10BcEe6WwO{w?>NTY zsy)zaXik)u^64IrSvOX@Y`W}iKkMcW%YKjdNXNgvAe@z7%Zsjkz}=;e{dufW+*655-N=WoMo#PhY=WJ}u>ZZjH>^*&ZrFeI<^?uhk8@4}ZBdWUor z7Y6mNobAkOj^>8wRAj^{fer_L4q!IaZfIDi z|M0CvnEA%aZA)TGuYD;+LoQ@NV-tVJOx#=FIewbnb<_{oVbCI8zCXp|+O=}Cp@)#7 zwLd+(upXY3`CV}5Wvlr3Ba<2Q2(memSmw&S9}sw~@W+5b9g~L1d1uBia~Drc3;8;H z^C&n(H@UCp-I!`#{6uYY{IH>)x_WusJ?x@6c2R&RChCErxgJ*BECveO?*q;ye(}g? z&wrv)|Fm_tDzh3hp&nh$RnhX<7|VPpEuY0={Sj+wO|E0XxI|a3?@sqiP^()=U5HEf zoS*iUFK1fLz8@p$=I)hq`u}sb{F_L zRUK^V-#OQ5fx12Hfv#+hIoTFh!DVYlOGZA$gORW7nYk7Z?VP@G=(q6f;T7JJ$d$sT zknqlkZs{dAZSH)04O=De?Hu|Yu=P`;Y$oh;=@i z`3mQ#txwsSYdFftHyhsU+}eUJ+f@}c?s9Q$(2C#LQpIm=*ZAT!0sNbZ>~Ds%vU=NuYO@WQ=1@(=YD4v+EsV9`%S13_HFpB3O$^6C;V1+ z{$N$kr~&>zU-jxy8SVe9qtlH8c3N{RYQ!Jh^_=3s70kXqKE&|8?@f1le$*}YE$obO z)VExe%&CPyT#1)%)7SX6eVMnK;?lM?ets3GOiW8q_5A$DXkDg&e|N2v?6;`5+0y%@ z3xCQ5&zyCIEVGx(%_I)xx?ye)-uVSmp!?054tnNz1yZqPu#UNuX9yfzr?l8t%+`VW=vCl zmws+xJa4u4?>ke23^f^J#r#<-d}H5=PAOxiIO8PkGEp&tsF;IoXf2w!Ac;ocG7~F(H&m)iqdaqgTCUm4E*3vV$po zOM5+7UB2Y{(`wG;q~`lqthS93u?po5*%-26 z>bIb@rK#?RH~Py)#*e3;ZK!FzxFoyu^puj^jGrLFlopL?mwb-P80%j&)# zRN7a4FfLL7ZUOblqJqEGxzo(dJJxb+w_MB8ZZBmCK0-0=8iX#j8Hbk%CwES!*Eja; zdi`fjv)S*Ks!92Zvd)~GlD)Yn%VvE4P_?n_T+`T=UTOtZjSFl}a-Q^zHQOGQWlPTE zxzw#c<9=&fxQBjQgd4eD?DT1Fkj<;%^UYx22mCsu5VoryLodvZC(y_Fc+ZRGWaP0K z%$6e|=%};7Li4+a&BM}MY+S81jDaZMdg-6g@{PvY*t3~INZF*eZW^?TRb1RqkHWuv_3(dNLC`q=rT(lzUn;>b0 z1)uF?a47jUc5So>u>B~?v@9H#3?FVK0?bSpkUQIN;Uowc`Go0dL-5`6FR~;Nk2=)y4U75H4og} zobS4J?|k&9*PqwL{nNgz4P994F>nz%vF}>@-+{OK&Gy`JB7a=zq z7D8?0Lt1Z*6J>!symrVoQ|yF2#)mOGjiu2UCVo^bk=k!E6XcqM=5Tt7`l)`(W|tM& zp8TBQwCnXA*DHy~T+bw~a;i?wV_KxE*eJ8t?Q)aTi9ALgkOGOOwo>r#f06C?9J#i+OAR$lb%feQ-~kF)@YahZnT-_bqOrm(69g{^Lir zobQ)Z9q8y#dWo4>(A1#$b^lvs!S#&8rFBVfs~0^#rgThf)ci`x!S}xRfK~ZY-s7^F zf~n2U0;TRNw;FOlo|#qBGM9Y(KtR3GePBO>W8^lyacG==|By=k%KrTfOS^S9EVqf% zr%;}{Yjxwg@^XXK&tI%mx!;KF=zF1WN8yF#D)U>(>f}UE-LTv%Cbri?Jx3|%yJuf6 zJ=i2zI~hm{X74qNofjZ`GIP7E-vl4?$S|ci)+dLzQ{oSix)<2j>VPhrpZDADXYX%9 zKMbz-eHT(!_1IUG9T^jN(t^|NyanbuLDptjD?QJ7FY?t3_}LBDk%vV;>D-(q?0ZIq?%3(q)NHbtzE_S&f5Bl23g)pi%x2{5Agd(s-W~ zYsBrGqsn%k>uj-;TR&#Jvs81(W_;~qvHkA{U_y4eHaLB0^O@KBisrZTiw=HH{3R&; z=eJt*q2#k5vUZ}+7!@ya6?Jmvbjh7nW1Z6Cs{DfEu;524P5su*yc|MC3i>4Y3bxq~F`@PDFQ{R+ie*085ulZ#a0r*X0>|HuH z_RyOWBR0dk#=0|aPd@Xlm6k>3<3O7*P<0A@S+$Cu`-?G! z<&^5WWkz)^&xp~?d#lwXzOB+Gzq@59%px(L{D%~$Xfb40TZ+c3%6Sv1#li&f67grp zM#*u1z0`f+d9%3TiDsH{E6qA4o|aA<$CD^Vd=TCmbei`z=n7Kkl1y)vG?@70?yh?s zb#3faGrwX()!XvShOd7DI<8b7&<8gKQofy=kyqGE9+zX_`uP1j*{-S$b?{i+x`2?I ze)#!O+u@SwN}D!zHh^w&uuf9e`ewC#4AFP`4aIcjQE7PY*yX@>bQ`B>2oN3VyVR`N z%i6Ms47Cx_ee9mLuebkHQDx8nxz+wij;)>P<1K5$M=|^O!AcVUdlk>7CI<3S`w{)1 zC;DX;gEad+Tvg#cZ?rpw7PYS(lBa4P!s`-*`5IRCzKf@L!%T=xBkaq4&pB@><}GZ| z@OM;)3dpiTeqgy3e`F2K?QH$Pi8IYb>v*xqa@!B^C9lbFXKMMZl zSP7pH&Va`n*Fqy2MBq!o9C~9;5&8YYeIhio74MgO1a~Z2fIn(<31!Wo$gNJ!%27C432O-ZPCPoi^aVc)7;Nt|)zKl}M}kuF#D5gsC|no@g|g z8@0K)y$opuH?WQx8Rel02Mh7va4+;UI#oD`Ggq94UKK7wQaHg-9W|NWuj@(h+C5F` z#-sY|y0_ZeI!A3nQ;W8<k&3@2`(C zRzlJE0;!2QZJ!0r^KjtY_S?-n7jT~cJ;=zzgE~1Y1E0eiyPu_PeIOinJ+F7LOVTt- zf2#7(ejWQUq~l9RxT>PAPTi+$v(EoliRpR%D@v5N4ZfJCg~VLu*S<;(R{?Lt=rDg4ROjoIfjj*JP(#`HrW6Mu}Kam<)w zM*gt=*zkTi1mxL+Su1iw#%iK@+>(tV`rQb#&5r{t%&yCq$;edVz-B`6(eTov}&1Q06@9YKn)?{h?kQsJ9KL4xFChH*?J7FAbi9- zYZp7!J2Da+13071rlu@}|w#(8fr&p;<+&bTM zy3fyM+}{)}a{Jjb#(5UwXy4cByrsbBh@`%^H*f7ABiJ0?hj0jsF?{b^*(L9ur|#=C zN1Z87(j-$ybUx|~ld9ng^}ON+TvAfX!;7zrZAU&a!J!E|4WE0|et+7lD|L!A6y4w(69z zqVYS`s|uh+C1rvO1skL(zplw%{sxvg#oH}9D`n=VnhQm*yKK2DXisLX;JWdKg@+p0 z?`jb`EpNE!_^Z~;Zg^e0ti7?naCN<B9F~BjBMHh+0&^*0T1i&U@N+dd}(~z1M3_ zc#Sms(Z4p$SbE(*CuiokdMQ_cQg?-e=o-`Ggsdc--c|j*#^Q^D&+q(KA7q(YEiQ_7{EU z)L-urQOS6GFAtS@RJf2tjdjyEWzMgJu8Wz|sa2_!yrao2=4NTfY+rv0bG%u6(D7XR z3%mWO&f=5fG||eQS@5<90*f7O)irWla$EVR<;|bMRy2ACRySU-yRLMD7jzW1X@=QYOfRewp?&uL=mk_i+#PRa zz8a%B0}ZzXu)$f}->49GVb_IqR5|xP`~u418Hs)3X@>3QZ#8-gt?HWPOw}rjq3V-n z@w&yLC%7NC4O$PP1b|30voegZ3hEke^PqE*^+nZkS?|u7qEQ+Q*{2I97nz*(yU5j@ z`vGVdqQ=&G?zr}L{;tkop-^{Qs5gxiuAsaGC%|H!H9VQafq%nyKrq-!XHYZAeMAoa z1e=G)m^Kp&jfK>2gE{og@EM(MjNy+rH3*ehwMa>P6(aOO9vAF}zeBr7TV$Q#9I{Ab zi+HJ0q4^#AnF*b3WQn!_3&Ot}9z$ljy#j+K**s6Z)Fxl;sc050sYyl$H!I1hY9GBL z5w6PNrYhYn#x^c>n$d90%d5%M?RaawzqiKJZ3`CZl?V@VgvFU=`>il|lq1h@+I2*0 zyIX3FgR6V_1cx3aQp@h81_8HH3VJsVF|>9jb_^xGo7?!a>mOR}uN&=>*j^q71-jgd-_vt3hT(H)d3hkj&;2GRC zNUg*Qoo{s;wXo-K#yAFY)DBUcD7#^t2bN&DQvx|*T8oj#%mY~P}M8G^17B*GB%0}L_kqH{a@k)BF3TvfZ=^rAA}Sovq3 z@qUGe$+<>|do>l164hv6Wwb_Df)6}DfmZmBxxb{)y1%r>u3ECszPtFnZG-@_O5k{y z-GF`z4p5)aOzbweZ$Rh`IxRU{yO7$gg_uve0Lacb6A8ryoJ^{c;|s0kj6@$I&0Gtp zknchZg)@k2!l~E>p|feXpuqT(7hqbx@uX(axy)_yD^!4MVKMd)-e#-=lMU@uo_+{ks^4X}Vwk8!Op7&Ke7I&4*;^}P z_{PgnGGFn2$d%SUd}qyM@fYKIGc$UyY$tM9wu(2=yjAd6Vi4XEJP;m4y#(LrCp;K?%}Lg7 zM@DwqLeA|k>DbogxUy}R$+h#lAxqm!e;zB=3c!P|S==icp}1Ck*1TGM*YcU> zkJUl#2&-#`pE50;Cy511_-DCNxI)~Yh_l$F+hem_6=9d!5@GwUDatataiuuFDI7^} zd1~sXy4um9omIaG7yZ$It3}ql&jq8z^+kMh%L-DaZ49)i>Pj+Cgg!|;WM2ejt`j*= z12VzxeoM%?gOW@?!n*XC14rw^d#7lVd_}r;yAApg{1hW>e1>0X!l)&sa_Gl54f-?3 zmk;HX3isugh^>k=l81FhNk>M-1GY-)olO~Y$5IT3OGD6`Txae&Y9a50wuKkkx|{c` z&W$^!DhuhTyam413aJ+@^RQA~cY_(6q}7@o?BYAW?@INJ(=`X}H=YbWK}7Tz4Gg~h zIn$j~g3*#p$r5s``Jy&YnN@|Wx%1CSl7?J6fp?AtYL=f#TND;!(%M`4J?f2J_rNao zMYDp=bjOYA*Pd}*Ek3XGp58``bjhaYSmz>Dg7th~I#PUFTPaO$^D-aatTQWbik9|o zT`ekA@8EsFYT;BwN%X3n{0ah@{-ZNPYI5e6#-$xH{*28oK6WCrMxN)tDo=qpr*U`#~Nk#6ZP%> zYULEaSoImVnb=lq7i0wgmv{y)x3=my8L{zej1|MVIVS(sp}Ilv#A! zd`_6jmFVDX)o?9mxAb&5UO&xeNzJqFlA7P$_tkay>}^hPy{_tEBQhQqF$@A_@*f$e znNL;^x0&B2c4$^EbzHBU<8Z&l*|w$4-r|Sqg9z!mhI;BpP+q2G1`%;cokO{|$AVKW zO|Y!x1m{`nT)vMgRAjF;N@&vt^R>(Yi`SfYmQkWtmaokQS=_boG5c;AFXUSWpi9jQ zNH=k_?f}=e!yZ^P#bDcO$gakU%N?Kpq_xbie9`>8Hl_KY($LbO@ld@daqS#_CVtjZ z1TS{(E@<%DVz#>50jurZG26j@%j~N>>g`+{r`cSS*;*{)tq{+ozHx$eS@fOuF5~Ft z2F?72ZXIp)0j>FsmMw2v(AMdlx7%y=>FNyfkA5O@kMI(lg+5Ap^4iQT#XBv&nEhkH zweXbfv6w3jmu(P+NOy4F3EnV+(e2nGx|cT7G_6CY-JzVN_G+A}`dP2+INreRoZWm@ z#|Vv%YFzi z-=YW}B-}$^0O#-o!yIF4JEoi5kfwcGc}1I3c0rd>ddXlaUyrS*`A*(dE&)K>fV`mV zxc>-41#_&Egn=%jML}L|!Y@7vf_m@Eyh`_0G|I6W5?a2X+XZn%CHTh_XRks9q2^eDPEH?S~!obOE1xwWFvG! zT8u!l|aEiB3!^}7Lkxpa+2msmlKDiH%xP-OAW&$9lB6av@V=~UN;7P zpwD3z8TrIx?66@N`K!yCS)$H>)^~P8oz;%qIbEB1d_xa@GTwvF1BZF1IYHc9;Z)S# z%mwzdNMVjzj-`5Ax)Nrx!Pqv*M`IfQz2O)#$FPPzV5r8X8D|=@Oucj$@QvC$QmnIK zn)TD5cWgJnlT@PD7?Sgf-SlQa7jhx)7tS+I2I_*2L)Jn2A!qstlYw_qI%6CeZdgWi z>dSFIV=3N*O(*iHe&h&v8ugpIj7}3SW~NHq!AR*ta9%=#2+?3Dfj0>1k9b0BXdJX* z`2aDT0LrfQpj|Bo_Uat4T|EMVy9%Jgx=1+RcnE$>oQBoR6gU9Qgm`=|go*}$8u2-1 zk;I2tE`}Kop*K^;-N!f}F!+bL39gcZAPwFQy~Yp0Ux^^3n94#Xf^q0;I1ycr7NITZ zCDe#mpeLa2h=wvj`>=DsSMR{g(LA9vsww0GRe!?JxgLL_Ww2n3!s?k=d=Sr%m?8}% zmszc#zSy0i5yuxyzQb1FX@`R-O9(n6egU4MZlE_gjcL-Jq<6Jv$ml_98J^?moDm-ifzo0MQ)Rh_mRUAYm&|ul z+2U0c#d}V@g_qC|sP#;5EE#0#o#5x%b%?X}AS%-ZaTXgQISp6}8cCl*dLn?e&i0LA9B}nKg6y+iTr9PZ~m@r!6|tMSawyFz(a!V(c}SxYt$Il6f8O7Rx%u zSU*-xx9Ot^vwousv@n^*OH!x^-U(O^0(mbnf6+YcFsW2E!hBS#gKVzymia;@EgjVw zB}P=A`61dz=m~5RI0VehSz*mM{Unk#UjU@z?r z_Q01q zw})JR+w;2)_CWPVo$U-sHuc76=?Hup$DMjk4h3E;ItO*yaU`){9i|*aP>{}p(YZ!r)`0RhTkB~ zBmvinN6dNpDsuz+!|X-FL3dsbSSXkUbrxwQO{#Cx*pS+>J4;|I+FI%Skf1@1yqRf2316Cp(?;Es)nfLW|7A{wYUZ*$zq{v(k`e|I2As}T?cERUhs741;oZN=+G;fv)T{z{H_hu zgs#5iYON#jLO&HRHfgYA(gkkKJsGDuL~m$7aTQQ5~3QJ=zRM_VS39lL7H+N zPu`M@3fmt*ROb?!(TyVZ;}=Zc;DTW`XRp3Ouv|Yk0}Kx^q0QEhhT$>X=E~N>qz}6pTX}BwL~T=1-W1GK|VF zKSOp%6vT4DXZ$9z1MfyD@WX~S{HXdC@uBS>GEsS&DsO&9uWot6tm}9V4ry0G{saWS zLMFq*#M@wCwG#Gsh=3zqG*G_lNhs09AL2MxgWlG;AV*RKW^+QI1o9=cTsH&`?)VHp zYxY5$8v>A&xp50jKYlI8An3`tDHNcN!Xxksek=IOiD2U3PgDzim|RNSCAOF%iDiZX#B%** zVuOAZdD0LlpKs^RGKsq!KxeL$cEI?ph3&Q0`pfmXsP#?ZGI*<1X>BHqB ziKsg)MC_p95YFspn(0QWlR89tQagzY z3YNpOAQ}IpKmG}tXqR$}HAuaL_p^z(_-N92`lC?QT>@oUc+l-Bdp{AAk3anG-K(5#4&`QH?s4un-6_A}= zU#6Zv9PTc(<(v}E;kgLg`M>zr1n;Si%&OGwFJ2DCNbBC+C86LIEWb z+u$)|HzbbQhp6an$W`ErSipN>Z{#DCg?s^JNCk5YX{K9Yf7%;sNMj^~H@;>vD=nedc%K*bGfs7!*E`k9bqyco1`QS5^2u{;+U>GwK{9*#ZARq#V zfSM@;IgAB#hglD;U_L_*tRHgd>F{cLE$m1~!F7}oDx+3G71VRUr++ac=u-L-t)y%j zg51sckRo&!0W`>&~|Y@NFh27?hD>AeR*y43{*^ShGtUP^bhhN zxt(mpcaTy%gM{#5)H|F*yOW(XM&V2Vu!CY@Z+IE%j5Kf>kayfIXgW6wZRez;BhV!% z3cp68nJoASc^+!R0>MY)dwP^%F_oqdCoK$fh@r+BJPiASKO-E7HS`8gxx!ZAczX70#!&deGoZKR>7BX3Mw_Q z0m~qQ$q$5q_9AuA7xD}GfEvtuPCE-;GTy>uFkN^AdL;;hL-{vgPi`u_8`%ISf@Vlf zc|fO#Fp!LmW)7J))ApufYOQH6wFx^wiHULwqIS|f*?&r#0j&eGkmFD*yW1Y-?m@P3 zJ<%9$EP9&L51ou&KwiRU;ZhI^HPVk50cB3d66Z)Cd_1ueTZ^y9gt!A9fJYNqxPh!D zs_Ai5HNfapI2nvU=fP2&VkC!yqenTfIge42x=Cmy#NIGPwuZLbfqa z$Z>QPnMML?1woOqcqdtdm6D&aOmZxqM@}MI$ue>X^^(q_K7(y^4jjW=LB{}yI|SFSa2FSMy?yTH=li?#20YP_}R!uZcn%jT?cN! zPv|Hhqk7OUi0x!C9!IRkeTjqkL&A?}C%coS^ggOPgfh2~gP@(Gg0grLq=H|GSO`X= zllW2SeXa%>f(}FGLQ~)vx;M0!;DI4n9{tAnmg;XTBNL3nNiHTNxr7gSfJF%>Fr9h~ z52h>7G0a8o1dzcS28HrnU`Ku`JfCNYm~;J+nJ5X5hR?w~&;`AuhCthhc(4siXR3`1 z{noIW?q+DA{0#|Ip5X-5V0=KuVF*2o*gpa8^P?(I#*To&=J?CzdIPGi78MZB3k~FJqhOt)?w>voV2gGLrNm(rcLptI*bcxbJ`xpqm4*h|iBHN+eoMxcn_5z=I z^O+9bNqQkKpX$Z+rZ%$?wg}!&dV%Yti9#ugNTOEYCutUMnN(8}cxCzug<^T|aQp_6 zM)XHFP^Zwj%w2R6v<`Jc2;>bq3#sKCfp2mXp)F{0+7PAA^0udttMQ+1Mm96FWvt#5oK~TmnCc{_uP91@eZfL38Mr zoMOh3s{|OQ0b0xX1aC*@AwkG1Kn0*Dj<(hfG(nVJ1WT2584|b9pz((=__{<*bs9hk8UI_x2p+E{;Kow9j`=LyRfzB|I z@Fr#$9Lre3iryjz;$$M;Etb#X_1oVtt2su)>KtA=0?Q~GIf*DA! z21lu(kOM`qHU4&(BHzMus1#U4uYp_WdZ?1=29<-k;01JmSqgupi{KtK#@4yN;KS4$ z_y~0mdPA}HXKRNYbUTgH-tvmJAH;0_-I zdZ++AhA1!+f}r=T-XY)sxWd=~d*&c>nI`F>^d?$J%V-l-M{#H))sr4i$I*KFHC@0s zFzsL!V+Vg_A`u5L1YH9F=NXvH(SUH49Uh<~pjSvNv!wg}DC=3XLLq>)$NLES00OCVXf~Z6llnjC( z;v1|+Hk0YwBA96^w*C^<+}5F`vtch%i*{d&3Itseb<|EjO*);;%}9oAlZ zpL0)D_ttU6@Wgns`?qGd=BZ{m%{#qn<+!*f!nLG10Sj{?tHWULw!trP4)SDztU7o#!=Njd-~CAck}X9 zt@SqTPKvX;*EXy4c5fDKHfolLkIBA+nt7_9HovMCjH&gpv0MF4>{x$6>-pQu&#L*V zEvnV3C(4G^p=F6`_43aWaejFUqnGx4DUyaL~b>k zshKMtZ?=l(n{UVXcrZR5^OiH>V;Z+t`9L|d?4tXLL(8S*u=4G)N!h;)$Fy>0ycO?@ zi{pQq?P7(d6ZiCPXwK@L)O@$MU-NUV3l?o=G-rqptH)l_KBgR4W|Y}wdbN}4*vZxQ z^=Z{5^%+@a)rRGx)zor)*|OXb=aq$G{_4QyoN9J&iTcXkiS@a? z-_^hB-CZx%oKXKyQ`MWqhSjiEyFV?TiO*DfHZN-5W~2I)u(7 z>wUPoyLVN&MtjEB#3^xU`9PdnJ=1Jd-__hDK9A{?7}wb_9;q*ked{;l@oIyzYW2E>tAZT?<1 zh^6b3V%yHM+SysFOddR`oIg0T+&TD0IcxCEqB*v#GjL^DsIzDJaWz<`mJ8yHX1!Rb z_d+vs`1{RmL&r7C4;|JF&7RRrA9|^oXZWKrwR?l|X{oYYTvV>tPSf)B=c=tb=T{34 z{JJ`D;GSxyf!nG-cTTR}u0L7bSG`+~F6WgG#R~ePy<1~qZ|nGK_l;)r?zzp0-JP1X zduudnG*g=+W0U6oa&)s~eSh=8&ie6c=gL@bU@5Hx&Mk*`-YfgoAFr0EzFwUlr&p7j zZ&!D9KU3W?JXf{;u+SS`s+=)=c^ugNKwRFtPR#kd?mAX#?x>b+9<4WOzSB9odA<|P zJ)P6y6P;ztY4yxm7W`)iN;yl@RYiGl9e&_MBQs+z6G4;dMjB0XyZCSOxHYU}# zH@~Z1?@h0!_bw~XcK;EVbytpSx(79X?>^q!v$vPNSJ0 zQSTp%c2+Jw>^xe^z-Oz;1HY)sz+bCNI}cWao%5=x_4?Jc>h7{w`D7VX=FHQ4JvQvE z61R1$I7Rmsw{~YY>-83o3C*@Kcbpm@DEGt%tGUZO^`_-&?Y_TPUshhOA1ue$ua#%2 zD0MZaTB3BSwc@REV)IxT)7(&=>m4U+wkzLgCYB}RPw{-59T$|HVp=sNzEi#0oLAl2 z>`+~(DsxIRrkvJHimRK+%`>VAQ)1ihx8vsFcVm~~1Iy0C&z7r(H>^_!yS4gq z^M~s8xUpKMI;1+Zo>G0Ub3-|9;A3UX;O}Fp!QJAXfq@t^a7XiIeO7aJb!;=K9NSzf z7H{4AbF*f5$v9~Ez?d-nU@S7cRylt7TV;Cpp|WLfs2tfWSX~_h)y(o-xuLqM>{Rbw zuBqoMGwVCzyY&&VM!jj=TrC~zR1@R3Ws&%DnWjpyLrf|s$1=+44a&Q*TbW$GAxuvx zUo0o<{g85gnO?3d3zy%Nr{nf=ZQNOA#I@x|ssNA07s^Uym2yUTCB{~l$1&BH;)!Z? z-NVis>(>LZd_6witk#Hgs$F8?YDOF@2~Wn-Wom3$j%&WCJ^6jh9=+X5r?*9Usynqj z-<`Ki>&=R*dQZn^n)_nqxHl%0hhn%qul3-Y@%4JH@>IQOsXOm4w`*m*PCc>gT=inJ z@??yQU#iAkq`W^a?il`BtT=o`#L%~5z2VE^h2eX(K5Sx{X7%#z*hNu3w(M5xd%n(< z<%EGt%i@FIDH{)dQQujuQFa=5FShIaJSNshDx)`&4fDob+BG`2dslPa@Ey%c!*@61 zhX2}pZg^76-~Cjq(z{X_(ZqaZhjMOpWm&lMO4)W`(Q5kO)av2ErK=YPOLfZN?d4{z z(@)V_YGOT8xE~!iY0bJ=Z}s@e@YFbGXuEiF_8D=(>=(3F-Ms8NbV=E1xUMejZd@(W z>{VSHhgDNlnNO~#RX0igc>`yaDTAAq?FL_rNrOL#vj%pK>pQE(MZ*2lRVONC#O2LA z@rB;h*rvNl958%HJT>&axM1k_apTaeSax`s^6K!`rRg4AHfzo-H)uy{a`l_?wff$2 zVdsJJ&4J&Sxd(43YYv`Y8vT-aFV)JUI#u~}{i~>}W8=KoEVgPEiY0rmH|upDX?{F> zZ}YX`hnp*h-)UCpE+21o_lp;MGh>!!#DQ|6=9Q<)Z}Fi1NOh%NH`fbSXV$;dzUH3g z=IXUrr8-34x(;exc11HY4$!~A`E;{)?9uEWXEmqBv#OMv#G7%mIJ!*PT%6pg{87iT zTFWm~y$}n@q@)W|ChyI|vQa)I%T3uM}TD??#vzk}? zfP?k^)nBTOtK+J=TCBQE6=3tSXn7zk^zNR7pdV@Hn-Z2iZ50BOBAH?sghvFm3`D+xT z#mcA3g>gh#J={k%l^%cWet6kHg9ucnb>^3tlumzA76`io9p73I5rj* zUO!U}+@zYO`n!3oT<;wF)ThR=sssDgL$Rvz=iX|UvQ>4w?!&)Zrf4mCN*t+J?p3Z+ zp6n|dKi&+**3I*=Pjhcv(%czuHh07p@dx?$rx=bGl@I@n(@Uq^QRXSH3#oak#mkiH z{bhw}sj_0VbXmNbqI$h#c}DkQ%tA-#wZ549Ms&(mLU;$^v}n8&e^id%)VvxOH7~`F z#qay1b0A)d?c>$BT-9Tu{^#9SH12-MS*lv4e6iZ7oK{UQKdg2v7ggJpqpDBKk4>a& z?Q)IgK>b&b@@%}V$UYyHY`v&?K&!F`qN91~FTFp-L%ql24b4-lN#8lm3$b{-61T+P zs8C2>y64qsx`~jpDV{zdz9PCZsoc7Tp1Tzmvt52 zvwJI)1$z_ABi;An!S2&BkLu@%y&uK$&3US$$7m)zO10#$_=+<7rj4I>S<;Jp*YTRp*dw1+q zZV~f-7PD2E?~mK#$8mR@B>o>4cgELM)lP{kV@8}Fzths$8c^|90%H4BJcd#D3+tva?p)|D~8tD?cdm-QNZCV~w2LxnpO`7$UnzE8AjD5n{EyMBbZo3!j*CwVo!!dSTJzkk zxV;{qDD&%{c%Ab8vaSA`;yy*cS=G+|5#?rOz>Tqw@^KFxwL2E;lt0Fbn>yUa=d)_hWNanICOxba&X)iN9uJ>JRDc4hCL9ogyJ~ODvOsj zR8uzB-O2WP?W8-vtrV4Yl|u_Em);a#e;1F(CGm*v)1QnZgwf#|duog=SH_a%PqA_t z)VB|-l@FI)Rg;d?=X3No2$w2*FBa!7EJJaU)@fgpFP|;9X->IG7T*-}t8%=j2tO8& zD!y+k+Vf}~xO_P~J}mpUEtkahiqvPyU&QBO;k8^@N_IsYtSmac%+&mTwPwo;wQ4*& zjxOg)_6&J_gR=M@S^GErQg={TOc8I_Ete{ex5PG*^(k#<ss z^>SBzZ}JI6VukXDn5#UY4E>vG;h6HRSU|JvN~#Mh>HGP$%BR%hD|#KT_UmK3Dr=|~ zycHkSTK7vjo?K?@F1(bBv{E@!{vTe(i!Ect$)1pWTQkRVagd~MrkXW5eiieJAq$G_ z%c*S##pM+7?{QVi$(oN2R_6U!Yv-3_>6mKQGNIbJjIXxR`LuGgX3!(0Z<=s@RZ=gF z@5UGOZ=j~dfw7PxG^lS7C#ZErWzde&dwMJA6C2`>N)UO_W6g!ty??CuWGX{~aeNKaP(tYNfWX*nGU^qZ#qEEbpi~P13BS@2M1f z<-O|Mc3Qc9Qa*1{KBl>8n%476P=_@GzA8JO5ej$7!`sEcUrNI*!eOTJ{lWO7kkLMy z=Isf>aEfBHrslNuG%K$sG(IE^Q}kR!nOK)e*MwK zne4np*3OLE6rEed{99F>?$j*$sIvGUv9{v+QQ5Ywyxd0KZmV~Fv#lK7rYx(ud8~N$ zS}Z2HgUZ}jgxQ}IhnaDvB%h|5Gb3(@pGxLEviU_-i$=5jVj8uw=A;evzJ)CPw4`k} z@>*5cuBa?qSW?H7_my`Pqh}@c5skl7eE+p-%&+4Np?ZvLK0s$|X(Dz|G+dq%5( zd6dyp6^&2oE^9x@|8LoSymTC2KB)bgWmMVTC#)yxy_8vs)r0c*CguFaip^Py(dmlS znRnc8y(q~~3Xx~xMjfx$`Hx0&_idpvB>iI)xpB&z`85XH-#4s68c~y(3-oX}$CT)wwlQ zfj_ERwaLheZ1~A2lN`NXjaO+S`te6yD%NnxemJ0 zkzYMoFrYo;u8!46qeE)%s->qs zJ!xyCNn0zjb3k(DlI68t*xr+*T1O-f%b!^q$LDt>0}b!|>p5Gx-j<%Oj_>JHrMT$J zE6ICX8PpZ}_+Dh^pdvpwQdJfdtKTOdCW-?S<=tGmI!5n3ac8!)zA3xF=5LbthUC7c z&$Fa&wj@{bbBruT)&i0-S+Dm?&J@jmlXaXdG!`GhXM)~AZH&+v(5R4=AxwIzJYC_z zv5|aq70p*2^=V}DTzVFb9Vhu6NgWW2JCYjW9seh}EB*S7zIu0s$J-;Az9(CUm9_7P zO~c~Xd*T=8?<%I~d}joit~?;BV`b628b7~0UQn7B&}*X3=hJbF5I5>X(XW?v1dA6n z{zYj;7hHQ=_RbPsjZhhspW_w7`DFKkI)dxMvU`5D&MUj|XN=I`+0o~oJcW_7#jjeU zyW(_5{fgQK)k5Dvoz1OJV{{JBXvrfo={ZhEdicqB4Fq#aJj%cSp znFW@ee~rMh0lkP9?;y@tKRf@DPj7OMo@nokTqjp)#hN_w$q$Z5bpMbiXd=Ur%@`hy zEam@xJM-z?s3%wP1%Ihtc#jwT3(}ukg~W;vJ008jkqjnGRpbUe2kQ`0CI24I*4;ZUJGA&T^_H?DCN|YIvx} zd(uMP;M|gs0VjzExSNx-fhxV=r7fjRA7w8-kx$ICuridpWU#XnU%-bP<7gg(IG@20 zPUo31W2CDND58gIh#vDEZqm+dLsfzW9& zj5p)J5|7{^H5tV0Io=u_^dK3IxfXCO&ukr6@dG_nEFw%*!amSJ6Sa{lk_^Q%Wb-tZ zu1&6Li7BI8pT~-CSTs(UfdZ_>OV~>6U^f!sF6|&pJDhd(!y=-DM!)mPR^T`KX(5N{ zkyQ(9&0nr27Vwci_)6wE7WhJc9_A5@h958)wzx7f&b^R_CeGXn&h#R_9R2V$?4>VO z(?2bynh}AXjvz=oE%@mcaKMhNrr<^oR#qTHJ~P*YEHgTF01jvVyDo4Yq(Opl=z+t; zyNw(edQQZ4`hqXE``nr4Gv479W9&6-!F!_uuJnPu5+LtkKk})!$R+c+GNe98hShM8 z{^qpT7TDn$?r33=yQx$klB1Cv<^6_HyI_`aJ0A z&(0bnhCce6i^QC|g6H7u%r!^NUC`nae1bV(LO*6&AB{29Fa0J%LCM%BkMSJu;Bm%| z2-spqz@n^+w1XE{(aVwEV1P!?N+w zfES2xC7Fy?B0+s&hVndwB&twG$#DWHS9`OSnFZEjn;C#KMw&S-S3o)SU;)|5(Xm1f z=Xec}_c6U%vEK{AOY;!=3|H2P{kbxg)V3xwxu{uXDM{BU$#0`u`OqXm%fGBcY7i=9ph_#XC z3`x8bU8*Y5Kn=|xXHT&NNxn*qu^cSPCv;h+{qbA_#^#T)2W@AXBWNGcgfvjcZsO%L zbmgjmc=!r2;R@q;m~-fN%+N~gkijQoLW`Ne7?@!uam0@F)Le0I%S48?F)GnwL`rfN zxiT@*xn>LOlW}qsiHUI`vz?i%bCPSkcG1?o;chyg?M~u*$&#}v1u_AGN+e||b`Ifvivu!65T5ssb8W(Qa zDx+^knSs~_ZfM3c*9hJn6H9Y^vCHu#OC1x=i6kDhxz2U7rM26AkYLoE1IQk#8O81%sJ|Kn%8*72u6ZHeS*8Cgf$=;n-K%h6$snblJl3)DG?87RwMZ(cGbB(a;m@#7%y6rcz$V*oX*utvBtRa?;Az6bCJWEDmBcIK??1v`m zs#j>)iZ>vYTHsE`2_I|&xsh`~Yn8oW)k@U?LrZ0Mi)i2p9?{xHuiwhN3S!s?6YagL z6R~xD;hL>+r8jQa%DLl7RPe_20BbnMh8(L7vW8a1vFA>{_&y$u?V2dF$dOlPNlW{j zN1m&&>|!4dal4^}gzQ!zjy0Msb}?0c+2)#tq#>lyM+pTi_}yz}&S$i#ZAY z8EdKo?OsLBd2F%4RXGQcUdR|?i z*D=isVZKSw|M`zSu6y9&wPYSWll0?kKcq+xN6yxQsXsYd#1UPV;Qr1(vkOMxTe6+l znUOwnPGqnYzc`{H`DwY)rfS1Cp;u{M`lubgSF3m|7srX-Mam2wss`*yYJ>~Qvj ztTl7pEQd|bEZ&W^^MWhnkNIZwsa?($qK6IUDSXcwLQO~(xmVtI!iqMEIa{hpTF^}1 zATP6(D}E#zd2|jVtM$TBz zkIV$;g`-PFWS56~Cm)Szq7P#D<~hzYs_Rl4$IKuzyv;uQFlWT^!LiTzsErstIv&r%!ZU^i(1Swv(kGcrez$Fm60=6|XZC{jH~s zv@tUJMvxt0cAe}eDDg-Pk!bty&Y9qfm6gx&1XpXx%HW7O3a;tK3Uo4uI2!X2r9#Vb za{l+XwVc8t5I3UOiH?jj5wrDJWZZ1ItE}%sS~fA7dY84+n$tqhF<#kat!g8Y(Miwk zW!o?FG$V(1whG(56V_HUY_+A%bk4JmdmRCL+=6|`G7dxtpRvP|+0%1Osi?l{NO~T} zihh)kg*W~9Q>Q(XX0{q-SGx4g{tyu)AV+UHb1$;m)g5ds#}$BaM#E#$Q!?MXZD_~{ zW=?nn$T-fP>F_%FPdv>?voU$&qbL==1+dylqsWN_8@1*`+xornH> zHolBcInI%+MUIN4(dLpL0i6$X`ncxGScBz!QguIH>%yyEq0-E441 zU{4#Hv^4V}*FfeoJ*|sRtX#-ezUMI~jH20Q?C_wqPA7UK(#(UNH60yL==Yeupa#P{ z!{+^dve-FmeP%^Q7*ssY_mPPx9BLz;wI_d#;W4fuwg$ZOuEwhzMz`FtEG*?=^z5-w zMW1uQtvNP(a!f?{We%3G$KrT7sz`=YzOOM$=G5z+^m2qPln{fQfm}Bn ztJVhFip_Aqa>)#9HjB8zD{*pda%9XLuAAi`K^@>bYQC|A;fx#2P_qeqs1&Y;cuHSq zAxC7Ud^9+ZJzL?qbJ(oryuX?_%UC(KSzw0B5$SEErB+88?XGCFg9rVwqQx8wxPM_K zMTKPqd~aizarHy`A?yCR7KzUQn>bc~CmYo4d~#K1UW zlLqMwt7z+wiTkweFx;7xa}O9BPc*wWm=8Kp?^a&RNplHotl8)~?#QrLo;#4mBYBkE z!2UKizKWcTn)zu2VDy|j>O`#ddql;^Tfa>$61JUdZS8YZLA>?Ec3PUPN!*j0X|>0g z5$Ri=<(RB%j->Mqn~~ol+v-H4=i99HMu8aEU!&GP0wyF=sC-_jm@{1QK8(K|=Xbg6 zy_rQ=k(JmZ(e3KN$QEC;6IYHrke;g_ul=3rZLWZ_*QEH=&(4gQJu=^1?>#p&M<@S4 z3MuGvWdPY+oB8N?V-;Gk*_n|%a>Vh97E8oVd&sDM>*(XY8B33F4a@yGuM%2}(C$h> z^@X!W67P~_M$OucIzG9Id1V5^*n-6wQS-zV*D>wal+k7kHl#O}PXrwYvxruDV0UXH zSY#y~9ZMqQ=-Kwn9M=oW0acisJsmqV*e2H>uG({Y@^E`{z}CBMbTn+MD~WARUZ#}f zGNX-quJLK%iGLY6qC-r~xY4Y(ji8)4=$VE2J0ot1poo6^HJS(XFf)_?_L&i_Z1bLo zfS9p#d|K_EO}Lg(7=5%R`{0!=@rFCz8!=PR;OHPHv&dPM{v2^ZQn`e3~iMxJJ%j8&v+&GEZbJ2!Mx6BB~Kg&*kKu-cRc^# ziLpdFc5u&+1^hk^d|Z{%v;EcO5%($LL~lN^5h4 z-d^cYue|4C>um#md6r0ym~GgCHay5XupDPwzzm&W06rPZM9jT>9n0|DSvI=j(@rms z%XdpfeoCg2fwS9MZXvZD??_rh%0Pl^TekFDZBIdtvGl+mkKt&}TB0knvw(dBvl}LX zVczZJHx@b9AuGqz)0Gil?2U$w_<(d|dT+v6Q zgi}UVJZH5d&wUO!^4n|QmAAGMTWhx*XO`Ir7hONVie7H%&o&Ut9O&m+@~`#TQAdh1 z)=WnxwzgH8b{RUNFMFDaX`6LoFH!Sa&$YyL7ORolt~jkFqtPDQs2c}sNPZ^MT|e?Y zvz5NsO^qVH*zA%0Z5b;Nx5vcLpYX#kqQ;J$_f3q5YeHgc24y5+HCnLH4C(ir9fWot z1}382T@Xj4_2y$+bDiDz-;Y0e3=+1(pY3bbJXcBXYp8Nm1njDPUs0SblngUH$RG3&*Oh79Q_v zmKD}4PJCP~`Z-~%%r56VeUq`4m6al`%cJ9GyfVu@(!9-IKFxV#4e8YZY)-7*;`!g1 zh@HvytV@oBQ8I2IV=lINpa7-_#C9cN!0B75o);(cPayWH+ zl&g!S8#yd<#uFR++WKbZ!G+9?v>6$#kEso5$qmL@BGIrX`JIfT>@^j79piQc(iv;i z@I2+Bp+6_ViN5`;wqA3D{#cq=VhQ%7#?)k)mTr6KWAr%Bc(6CdS~S1odR5B}<+bronw{Li99{9*PIygfnvyQ)k z!BY@$HFcG@Y)9Rr7;VkjFZae^Z0a4t!2tz%+B+mgnq7{&-#`3t@n-W2>#R6fkt{M7DDc+nq_d&IUWY zuuStRYXX(Zx$DZ6yFDJ^NSZ^8qt$1`-F4E8X?;VxV_{43EG6QD<+fE3>xeb)w#WHv zo;fBhN6iJtidJLN#?2aAHo2A=MO){XxEN!sCZ47tHu7ENc1LB>6=lpWJkkt z@Q`z`G14B1CZlE+Wt@=h9COCoQ|9h=ELu#%s?Qt7Ndy=acB7`>Ua~;arC68_G#to6_g4Wt{yBgQ(C9_i?)vF~9vm;>gXc|sSY;ibQq zh8xbrR(HF@>5Aa0ls*$d^O$#|m=);XSK^L>ZT70O?bFtC*ysqj<`cO&?et2CZ=3zQ zyj)9|OCaqPy7R%FVpnn$yNPsG>9&Vs?mWwQV6iJJ=p-L~U+3EE9>#!b;T*Fa)`1Vn zGUs*woSYfR(Kw^q*83a_+{^s2{Vk%S*~Qg4AITY45M;sM{M|P-0S@&ehs|h%+1@*0 z?}610Hi6&h40h#dt@b=*zmep8^YuLMXD8Q@>~ZjkUe5Qd5~;&%!-Kpl!}9)n1GwQk zJ6c`86GQSKvkD!q4EWXh$^Ucno(DPRnB`j9*<_S$5%xI$?E^hrg^jm;CnjjKrerO) zu}bsrgv}`Xp5A8c`VmEvt9*LmGgkt5?3&lch#Hj97;Q68Q5#gE{XEtVjyDf15Jz6YJ zC;H?X;0S2a7<4Jaa_c z`tRA5nb&%WZ_b$HIvBP&48M4g*E!4OEZUBAl#QwJG6s%OR&!%EC$o$YI%V9J=(r}j#t|kYqjTTHRn6!y76y1fl9fHb zeM{bYRpd-{Y{^}7KI@028e>ay9R?SyMw@qQK$JMP@%6c9FSpxX7=``B8x;7&DEp3F zw>j#`W=1%Zu{fFGk+vzZnG@HnPwk6()B6*!C(!U=WN6Y&jmgRhg zmziR1Ej#xLdi7X{X6cGwfJ@>py1SadUMuwRqX{K@l+S?svl_FQv$hHYz` zqivl1PAvIkuI7rIyUP6b3`?*s8Ae?8-gDaIwbZi3rqeLLl$AfV^<>R{@y2ppsnY#LFtMQt=&)&Nqy8l7D?|pFjUz-}JPXGV_ literal 0 HcmV?d00001 diff --git a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs index 6782c4324a..dcf5eb4da9 100644 --- a/osu.Game/Rulesets/Edit/BeatmapVerifier.cs +++ b/osu.Game/Rulesets/Edit/BeatmapVerifier.cs @@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Edit new CheckFewHitsounds(), new CheckTooShortAudioFiles(), new CheckAudioInVideo(), + new CheckDelayedHitsounds(), // Files new CheckZeroByteFiles(), diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs new file mode 100644 index 0000000000..b4fe6c70ef --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -0,0 +1,138 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.IO; +using System.Linq; +using osu.Framework.Audio.Track; +using osu.Game.Audio; +using osu.Game.Extensions; +using osu.Game.Rulesets.Edit.Checks.Components; + +namespace osu.Game.Rulesets.Edit.Checks +{ + public class CheckDelayedHitsounds : ICheck + { + ///

+ /// Threshold at which point the sample is considered silent. + /// + private const float silence_threshold = 0.001f; + + private const float falloff_factor = 0.95f; + private const int delay_threshold = 5; + private const int delay_threshold_negligible = 1; + + private readonly string[] audioExtensions = { "mp3", "ogg", "wav" }; + private readonly string[] sampleBankPrefixes = { HitSampleInfo.BANK_NORMAL, HitSampleInfo.BANK_SOFT, HitSampleInfo.BANK_DRUM }; + + public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Delayed hit sounds."); + + public IEnumerable PossibleTemplates => new IssueTemplate[] + { + new IssueTemplateConsequentDelay(this), + new IssueTemplateDelay(this), + new IssuTemplateMinorDelay(this) + }; + + private float getAverageAmplitude(Waveform.Point point) => (point.AmplitudeLeft + point.AmplitudeRight) / 2; + + public IEnumerable Run(BeatmapVerifierContext context) + { + var beatmapSet = context.Beatmap.BeatmapInfo.BeatmapSet; + + if (beatmapSet == null) + yield break; + + foreach (var file in beatmapSet.Files) + { + using (Stream stream = context.WorkingBeatmap.GetStream(file.File.GetStoragePath())) + { + if (stream == null) + continue; + + if (!hasAudioExtension(file.Filename)) + continue; + + if (!isHitSound(file.Filename)) + continue; + + Waveform waveform = new Waveform(stream); + + var points = waveform.GetPoints(); + + // Skip muted samples + if (points.Length == 0 || points.Sum(getAverageAmplitude) <= silence_threshold) + continue; + + float maxAmplitude = points.Select(getAverageAmplitude).Max(); + + int consequentDelay = 0; + int delay = 0; + float amplitude = 0; + + while (delay + consequentDelay < points.Length) + { + amplitude += getAverageAmplitude(points[delay]); + + // Reached peak amplitude/transient + if (amplitude >= maxAmplitude) + break; + + amplitude *= falloff_factor; + + if (amplitude < silence_threshold) + { + amplitude = 0; + consequentDelay++; + } + + delay++; + } + + if (consequentDelay >= delay_threshold) + yield return new IssueTemplateConsequentDelay(this).Create(file.Filename, consequentDelay); + else if (consequentDelay + delay >= delay_threshold) + yield return new IssueTemplateDelay(this).Create(file.Filename, consequentDelay, delay); + else if (consequentDelay + delay >= delay_threshold_negligible) + yield return new IssuTemplateMinorDelay(this).Create(file.Filename, consequentDelay, delay); + } + } + } + + private bool hasAudioExtension(string filename) => audioExtensions.Any(filename.ToLowerInvariant().EndsWith); + private bool isHitSound(string filename) => sampleBankPrefixes.Select(p => p + "-").Any(filename.ToLowerInvariant().StartsWith); + + public class IssueTemplateConsequentDelay : IssueTemplate + { + public IssueTemplateConsequentDelay(ICheck check) + : base(check, IssueType.Error, + "\"{0}\" has a {1:0.##} ms period of complete silence at the start.") + { + } + + public Issue Create(string filename, int pureDelay) => new Issue(this, filename, pureDelay); + } + + public class IssueTemplateDelay : IssueTemplate + { + public IssueTemplateDelay(ICheck check) + : base(check, IssueType.Warning, + "\"{0}\" has a transient delay of ~{1:0.##} ms, of which {2:0.##} ms is complete silence.") + { + } + + public Issue Create(string filename, int pureDelay, int delay) => new Issue(this, filename, delay, pureDelay); + } + + public class IssuTemplateMinorDelay : IssueTemplate + { + public IssuTemplateMinorDelay(ICheck check) + : base(check, IssueType.Negligible, + "\"{0}\"has a transient delay of ~{1:0.##} ms, of which {2:0.##} ms is complete silence.") + { + } + + public Issue Create(string filename, int pureDelay, int delay) => new Issue(this, filename, delay, pureDelay); + } + } +} From df55a2f6839e399fb530476327086713c840f177 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 24 Aug 2023 21:31:04 +0200 Subject: [PATCH 0013/1118] style(editor/checks): fix inconsistent argument names --- osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index b4fe6c70ef..0591ad866d 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -105,7 +105,7 @@ namespace osu.Game.Rulesets.Edit.Checks public class IssueTemplateConsequentDelay : IssueTemplate { public IssueTemplateConsequentDelay(ICheck check) - : base(check, IssueType.Error, + : base(check, IssueType.Problem, "\"{0}\" has a {1:0.##} ms period of complete silence at the start.") { } @@ -121,7 +121,7 @@ namespace osu.Game.Rulesets.Edit.Checks { } - public Issue Create(string filename, int pureDelay, int delay) => new Issue(this, filename, delay, pureDelay); + public Issue Create(string filename, int consequentDelay, int delay) => new Issue(this, filename, delay, consequentDelay); } public class IssuTemplateMinorDelay : IssueTemplate @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Edit.Checks { } - public Issue Create(string filename, int pureDelay, int delay) => new Issue(this, filename, delay, pureDelay); + public Issue Create(string filename, int consequentDelay, int delay) => new Issue(this, filename, delay, consequentDelay); } } } From 208e94e9be533129024b500b996bb48b41931368 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 24 Aug 2023 21:33:54 +0200 Subject: [PATCH 0014/1118] style(editor/checks): add type nullability for `Stream` --- osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index 0591ad866d..6afc8d7a5d 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -45,7 +45,7 @@ namespace osu.Game.Rulesets.Edit.Checks foreach (var file in beatmapSet.Files) { - using (Stream stream = context.WorkingBeatmap.GetStream(file.File.GetStoragePath())) + using (Stream? stream = context.WorkingBeatmap.GetStream(file.File.GetStoragePath())) { if (stream == null) continue; From 273593a0eefecf091ed64674ceacc286d965023b Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 24 Aug 2023 23:25:42 +0200 Subject: [PATCH 0015/1118] style: typo --- osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index 6afc8d7a5d..1c2ef94815 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Edit.Checks { public IssuTemplateMinorDelay(ICheck check) : base(check, IssueType.Negligible, - "\"{0}\"has a transient delay of ~{1:0.##} ms, of which {2:0.##} ms is complete silence.") + "\"{0}\" has a transient delay of ~{1:0.##} ms, of which {2:0.##} ms is complete silence.") { } From f7599c5bc4d89a96b4d204e091098ea7b3caff77 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 24 Aug 2023 23:41:56 +0200 Subject: [PATCH 0016/1118] style: reword --- osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index 1c2ef94815..3064a41eef 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Edit.Checks { public IssueTemplateDelay(ICheck check) : base(check, IssueType.Warning, - "\"{0}\" has a transient delay of ~{1:0.##} ms, of which {2:0.##} ms is complete silence.") + "\"{0}\" has its transient delayed by ~{1:0.##} ms, preceded by {2:0.##} ms of complete silence.") { } @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Edit.Checks { public IssuTemplateMinorDelay(ICheck check) : base(check, IssueType.Negligible, - "\"{0}\" has a transient delay of ~{1:0.##} ms, of which {2:0.##} ms is complete silence.") + "\"{0}\" has its transient delayed by ~{1:0.##} ms, preceded by {2:0.##} ms of complete silence.") { } From 50dbf564a58d75b096fa0e76673f0c845db12168 Mon Sep 17 00:00:00 2001 From: tsrk Date: Thu, 24 Aug 2023 23:51:34 +0200 Subject: [PATCH 0017/1118] revert: use old wording This makes more sense wrt to the complete silence thing. Refs: f7599c5b --- osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index 3064a41eef..1c2ef94815 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -117,7 +117,7 @@ namespace osu.Game.Rulesets.Edit.Checks { public IssueTemplateDelay(ICheck check) : base(check, IssueType.Warning, - "\"{0}\" has its transient delayed by ~{1:0.##} ms, preceded by {2:0.##} ms of complete silence.") + "\"{0}\" has a transient delay of ~{1:0.##} ms, of which {2:0.##} ms is complete silence.") { } @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Edit.Checks { public IssuTemplateMinorDelay(ICheck check) : base(check, IssueType.Negligible, - "\"{0}\" has its transient delayed by ~{1:0.##} ms, preceded by {2:0.##} ms of complete silence.") + "\"{0}\" has a transient delay of ~{1:0.##} ms, of which {2:0.##} ms is complete silence.") { } From 17b9b1649a0434b41df43a0ffe24363efbb9ac2e Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 25 Aug 2023 00:58:26 +0300 Subject: [PATCH 0018/1118] Fixed "works only for std" problem 1) Now it use AR/OD calculation from Ruleset class 2) Implemented needed functions in each of default rulesets --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 ++++ osu.Game.Rulesets.Mania/ManiaRuleset.cs | 4 ++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 7 +++++++ osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 3 +++ osu.Game/Rulesets/Ruleset.cs | 8 ++++++++ osu.Game/Screens/Select/Details/AdvancedStats.cs | 8 +++----- 6 files changed, 29 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index bd6b857fe8..a63e604204 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -232,5 +232,9 @@ namespace osu.Game.Rulesets.Catch }), }; } + + public override double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); + public override float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; + public override float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 4507015169..181e932788 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -427,6 +427,10 @@ namespace osu.Game.Rulesets.Mania public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection(); public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection(); + + public override double HitwindowFromOd(float OD) => 64.0 - 3 * OD; + public override float OdFromHitwindow(double hitwindow300) => (float)(64.0 - hitwindow300) / 3; + public override float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); } public enum PlayfieldType diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 220dc53705..fa575192c6 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -328,5 +328,12 @@ namespace osu.Game.Rulesets.Osu } public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection(); + + public override double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); + public override float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; + public override double HitwindowFromOd(float OD) => 80.0 - 6 * OD; + public override float OdFromHitwindow(double hitwindow300) => (float)(80.0 - hitwindow300) / 6; + public override float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); + public override float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); } } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 584a91bfdc..23d732645e 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -263,5 +263,8 @@ namespace osu.Game.Rulesets.Taiko }), true) }; } + public override double HitwindowFromOd(float OD) => 35.0 - 15.0 * (OD - 5) / 5; + public override float OdFromHitwindow(double hitwindow300) => (float)(5 * (35 - hitwindow300) / 15 + 5); + public override float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index be0d757e06..ed4bc0ea7a 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -389,5 +389,13 @@ namespace osu.Game.Rulesets /// Can be overridden to alter the difficulty section to the editor beatmap setup screen. ///
public virtual DifficultySection? CreateEditorDifficultySection() => null; + + public virtual double PreemptFromAr(float AR) => 0; + public virtual float ArFromPreempt(double preempt) => 5; + public virtual double HitwindowFromOd(float OD) => 0; + public virtual float OdFromHitwindow(double hitwindow300) => 0; + + public virtual float ChangeArFromRate(float AR, double rate) => AR; + public virtual float ChangeOdFromRate(float OD, double rate) => OD; } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index c88bb2cb77..2f97c9ddf8 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -133,12 +133,10 @@ namespace osu.Game.Screens.Select.Details foreach (var mod in mods.Value.OfType()) { double speedChange = (float)mod.SpeedChange.Value; + Ruleset ruleset = gameRuleset.Value.CreateInstance(); - double preempt = (int)IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, 1800, 1200, 450) / speedChange; - adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); - - float hitwindow300 = (80.0f - 6 * adjustedDifficulty.OverallDifficulty) / (float)speedChange; - adjustedDifficulty.OverallDifficulty = (80.0f - hitwindow300) / 6; + adjustedDifficulty.ApproachRate = ruleset.ChangeArFromRate(adjustedDifficulty.ApproachRate, speedChange); + adjustedDifficulty.OverallDifficulty = ruleset.ChangeOdFromRate(adjustedDifficulty.OverallDifficulty, speedChange); } } From 772633178ca95956fbc75ceea4870f1bc755c9a0 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 25 Aug 2023 01:02:12 +0300 Subject: [PATCH 0019/1118] Moved ruleset decl in right place --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 2f97c9ddf8..5e698204ae 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -129,11 +129,11 @@ namespace osu.Game.Screens.Select.Details if (baseDifficulty != null && mods.Value.Any(m => m is ModRateAdjust)) { adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); + Ruleset ruleset = gameRuleset.Value.CreateInstance(); foreach (var mod in mods.Value.OfType()) { double speedChange = (float)mod.SpeedChange.Value; - Ruleset ruleset = gameRuleset.Value.CreateInstance(); adjustedDifficulty.ApproachRate = ruleset.ChangeArFromRate(adjustedDifficulty.ApproachRate, speedChange); adjustedDifficulty.OverallDifficulty = ruleset.ChangeOdFromRate(adjustedDifficulty.OverallDifficulty, speedChange); From f05659a1c0e3f542c9aa9cd5975fe0c4fb7f6b5b Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 25 Aug 2023 00:10:21 +0200 Subject: [PATCH 0020/1118] test: explicit samples timings --- .../Editing/Checks/CheckDelayedHitsoundsTest.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs b/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs index 5be76327b3..912b916a84 100644 --- a/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs @@ -55,6 +55,9 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestMinorDelayedHitsounds() { + // 1 ms of silence -> 1 ms of noise at 0.3 amplitude -> hitsound transient + // => The transient is delayed by 2 ms + // Waveform: https://github.com/ppy/osu/assets/39100084/d5b9edbe-0ba2-401d-94b0-6d57228bdbd3 using (var resourceStream = TestResources.OpenResource("Samples/hitsound-minor-delay.wav")) { var issues = check.Run(getContext(resourceStream)).ToList(); @@ -67,6 +70,9 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestDelayedHitsounds() { + // 3 ms of silence -> 3 ms of noise at 0.3 amplitude -> hitsound transient + // => The transient is delayed by 6 ms + // Waveform: https://github.com/ppy/osu/assets/39100084/2509ff35-d908-414b-b7b9-583681348772 using var resourceStream = TestResources.OpenResource("Samples/hitsound-delay.wav"); var issues = check.Run(getContext(resourceStream)).ToList(); @@ -78,6 +84,8 @@ namespace osu.Game.Tests.Editing.Checks [Test] public void TestConsequentlyDelayedHitsounds() { + // The hitsound is delayed by 10 ms + // Waveform: https://github.com/ppy/osu/assets/39100084/3a7ede0d-8523-4b99-a222-3624cd208267 using var resourceStream = TestResources.OpenResource("Samples/hitsound-consequent-delay.wav"); var issues = check.Run(getContext(resourceStream)).ToList(); From d5ac93f631dd5b9ef7e4f753a21224921efddc9a Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 25 Aug 2023 01:25:12 +0300 Subject: [PATCH 0021/1118] Added "value is rate changed" indication Now if AR or OD value is not "true" and changed through rate - it will appear with symbol `*` Instead of `11` it wil show as `11*` --- .../Screens/Select/Details/AdvancedStats.cs | 34 +++++++++++++------ 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 5e698204ae..4b7eee380d 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -44,6 +44,9 @@ namespace osu.Game.Screens.Select.Details protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; + private bool isArRateAdjusted { get; set; } + private bool isOdRateAdjusted { get; set; } + private IBeatmapInfo beatmapInfo; public IBeatmapInfo BeatmapInfo @@ -117,6 +120,8 @@ namespace osu.Game.Screens.Select.Details { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; BeatmapDifficulty adjustedDifficulty = null; + isArRateAdjusted = false; + isOdRateAdjusted = false; if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty)) { @@ -135,8 +140,14 @@ namespace osu.Game.Screens.Select.Details { double speedChange = (float)mod.SpeedChange.Value; - adjustedDifficulty.ApproachRate = ruleset.ChangeArFromRate(adjustedDifficulty.ApproachRate, speedChange); - adjustedDifficulty.OverallDifficulty = ruleset.ChangeOdFromRate(adjustedDifficulty.OverallDifficulty, speedChange); + float ar = adjustedDifficulty.ApproachRate; + float od = adjustedDifficulty.OverallDifficulty; + + adjustedDifficulty.ApproachRate = ruleset.ChangeArFromRate(ar, speedChange); + adjustedDifficulty.OverallDifficulty = ruleset.ChangeOdFromRate(od, speedChange); + + if (adjustedDifficulty.ApproachRate != ar) isArRateAdjusted = true; + if (adjustedDifficulty.OverallDifficulty != od) isOdRateAdjusted = true; } } @@ -146,18 +157,18 @@ namespace osu.Game.Screens.Select.Details // Account for mania differences locally for now // Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania; - FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, null); + FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, null, false); break; default: FirstValue.Title = BeatmapsetsStrings.ShowStatsCs; - FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize); + FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize, false); break; } - HpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate); - Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty); - ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate); + HpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate, false); + Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty, isOdRateAdjusted); + ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate, isArRateAdjusted); updateStarDifficulty(); } @@ -191,7 +202,7 @@ namespace osu.Game.Screens.Select.Details if (normalDifficulty == null || moddedDifficulty == null) return; - starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); + starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars, false); }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); }); @@ -222,11 +233,11 @@ namespace osu.Game.Screens.Select.Details set => name.Text = value; } - private (float baseValue, float? adjustedValue)? value; + private (float baseValue, float? adjustedValue, bool isRateAdjusted)? value; - public (float baseValue, float? adjustedValue) Value + public (float baseValue, float? adjustedValue, bool isRateAdjusted) Value { - get => value ?? (0, null); + get => value ?? (0, null, false); set { if (value == this.value) @@ -237,6 +248,7 @@ namespace osu.Game.Screens.Select.Details bar.Length = value.baseValue / maxValue; valueText.Text = (value.adjustedValue ?? value.baseValue).ToString(forceDecimalPlaces ? "0.00" : "0.##"); + if (value.isRateAdjusted) valueText.Text += "*"; ModBar.Length = (value.adjustedValue ?? 0) / maxValue; if (Precision.AlmostEquals(value.baseValue, value.adjustedValue ?? value.baseValue, 0.05f)) From 4c32e9a479b411df17649b52a7f0f4c6f528dff1 Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 25 Aug 2023 00:37:45 +0200 Subject: [PATCH 0022/1118] style: use more rigorous hitsound filtering --- .../Edit/Checks/CheckDelayedHitsounds.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index 1c2ef94815..124a549088 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -23,7 +23,8 @@ namespace osu.Game.Rulesets.Edit.Checks private const int delay_threshold_negligible = 1; private readonly string[] audioExtensions = { "mp3", "ogg", "wav" }; - private readonly string[] sampleBankPrefixes = { HitSampleInfo.BANK_NORMAL, HitSampleInfo.BANK_SOFT, HitSampleInfo.BANK_DRUM }; + private readonly string[] bankNames = { HitSampleInfo.BANK_NORMAL, HitSampleInfo.BANK_SOFT, HitSampleInfo.BANK_DRUM }; + private readonly string[] sampleSets = { HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_WHISTLE, HitSampleInfo.HIT_FINISH, HitSampleInfo.HIT_CLAP }; public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Delayed hit sounds."); @@ -100,7 +101,20 @@ namespace osu.Game.Rulesets.Edit.Checks } private bool hasAudioExtension(string filename) => audioExtensions.Any(filename.ToLowerInvariant().EndsWith); - private bool isHitSound(string filename) => sampleBankPrefixes.Select(p => p + "-").Any(filename.ToLowerInvariant().StartsWith); + + private bool isHitSound(string filename) + { + // - + string[] parts = filename.ToLowerInvariant().Split('-'); + + if (parts.Length != 2) + return false; + + string bank = parts[0]; + string sampleSet = parts[1]; + + return bankNames.Contains(bank) && sampleSets.Any(sampleSet.StartsWith); + } public class IssueTemplateConsequentDelay : IssueTemplate { From 29a46f5b368273e8acf874a8c7182a93293b35fd Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 25 Aug 2023 11:46:09 +0200 Subject: [PATCH 0023/1118] style(editor/checks): add issues for delays with no silence --- .../Edit/Checks/CheckDelayedHitsounds.cs | 40 +++++++++++++++++-- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index 124a549088..323888a450 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -32,7 +32,9 @@ namespace osu.Game.Rulesets.Edit.Checks { new IssueTemplateConsequentDelay(this), new IssueTemplateDelay(this), - new IssuTemplateMinorDelay(this) + new IssueTemplateDelayNoSilence(this), + new IssuTemplateMinorDelay(this), + new IssuTemplateMinorDelayNoSilence(this), }; private float getAverageAmplitude(Waveform.Point point) => (point.AmplitudeLeft + point.AmplitudeRight) / 2; @@ -93,9 +95,19 @@ namespace osu.Game.Rulesets.Edit.Checks if (consequentDelay >= delay_threshold) yield return new IssueTemplateConsequentDelay(this).Create(file.Filename, consequentDelay); else if (consequentDelay + delay >= delay_threshold) - yield return new IssueTemplateDelay(this).Create(file.Filename, consequentDelay, delay); + { + if (consequentDelay > 0) + yield return new IssueTemplateDelay(this).Create(file.Filename, consequentDelay, delay); + else + yield return new IssueTemplateDelayNoSilence(this).Create(file.Filename, delay); + } else if (consequentDelay + delay >= delay_threshold_negligible) - yield return new IssuTemplateMinorDelay(this).Create(file.Filename, consequentDelay, delay); + { + if (consequentDelay > 0) + yield return new IssuTemplateMinorDelay(this).Create(file.Filename, consequentDelay, delay); + else + yield return new IssuTemplateMinorDelayNoSilence(this).Create(file.Filename, delay); + } } } } @@ -138,6 +150,17 @@ namespace osu.Game.Rulesets.Edit.Checks public Issue Create(string filename, int consequentDelay, int delay) => new Issue(this, filename, delay, consequentDelay); } + public class IssueTemplateDelayNoSilence : IssueTemplate + { + public IssueTemplateDelayNoSilence(ICheck check) + : base(check, IssueType.Warning, + "\"{0}\" has a transient delay of ~{1:0.##} ms.") + { + } + + public Issue Create(string filename, int delay) => new Issue(this, filename, delay); + } + public class IssuTemplateMinorDelay : IssueTemplate { public IssuTemplateMinorDelay(ICheck check) @@ -148,5 +171,16 @@ namespace osu.Game.Rulesets.Edit.Checks public Issue Create(string filename, int consequentDelay, int delay) => new Issue(this, filename, delay, consequentDelay); } + + public class IssuTemplateMinorDelayNoSilence : IssueTemplate + { + public IssuTemplateMinorDelayNoSilence(ICheck check) + : base(check, IssueType.Negligible, + "\"{0}\" has a transient delay of ~{1:0.##} ms.") + { + } + + public Issue Create(string filename, int delay) => new Issue(this, filename, delay); + } } } From 290f8db341c7dc168bf7a93a80a364199bc19dcc Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 26 Aug 2023 02:13:14 +0300 Subject: [PATCH 0024/1118] fixed stated problems removed functions from `Ruleset.cs` and added a `GetEffectiveDifficulty()` instead --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 40 ++++++++++++++-- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 38 +++++++++++++-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 46 ++++++++++++++++--- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 37 +++++++++++++-- osu.Game/Rulesets/Ruleset.cs | 9 +--- .../Screens/Select/Details/AdvancedStats.cs | 37 ++------------- 6 files changed, 152 insertions(+), 55 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index a63e604204..4f499cee62 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; @@ -233,8 +234,41 @@ namespace osu.Game.Rulesets.Catch }; } - public override double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); - public override float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; - public override float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); + public double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); + public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; + public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); + + public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) + { + BeatmapDifficulty? adjustedDifficulty = null; + isRateAdjusted = (false, false); + + if (mods.Any(m => m is IApplicableToDifficulty)) + { + adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.OfType()) + mod.ApplyToDifficulty(adjustedDifficulty); + } + + if (mods.Any(m => m is ModRateAdjust)) + { + adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.OfType()) + { + double speedChange = (float)mod.SpeedChange.Value; + + float ar = adjustedDifficulty.ApproachRate; + float od = adjustedDifficulty.OverallDifficulty; + + adjustedDifficulty.ApproachRate = ChangeArFromRate(ar, speedChange); + + if (adjustedDifficulty.ApproachRate != ar) isRateAdjusted.AR = true; + } + } + + return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; + } } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 181e932788..7d16806397 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -428,9 +428,41 @@ namespace osu.Game.Rulesets.Mania public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection(); - public override double HitwindowFromOd(float OD) => 64.0 - 3 * OD; - public override float OdFromHitwindow(double hitwindow300) => (float)(64.0 - hitwindow300) / 3; - public override float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); + public double HitwindowFromOd(float OD) => 64.0 - 3 * OD; + public float OdFromHitwindow(double hitwindow300) => (float)(64.0 - hitwindow300) / 3; + public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); + public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) + { + BeatmapDifficulty? adjustedDifficulty = null; + isRateAdjusted = (false, false); + + if (mods.Any(m => m is IApplicableToDifficulty)) + { + adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.OfType()) + mod.ApplyToDifficulty(adjustedDifficulty); + } + + if (mods.Any(m => m is ModRateAdjust)) + { + adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.OfType()) + { + double speedChange = (float)mod.SpeedChange.Value; + + float od = adjustedDifficulty.OverallDifficulty; + + adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(od, speedChange); + + if (adjustedDifficulty.OverallDifficulty != od) isRateAdjusted.OD = true; + } + } + + return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; + } + } public enum PlayfieldType diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index fa575192c6..250685ec0f 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -329,11 +329,45 @@ namespace osu.Game.Rulesets.Osu public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection(); - public override double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); - public override float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; - public override double HitwindowFromOd(float OD) => 80.0 - 6 * OD; - public override float OdFromHitwindow(double hitwindow300) => (float)(80.0 - hitwindow300) / 6; - public override float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); - public override float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); + public double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); + public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; + public double HitwindowFromOd(float OD) => 80.0 - 6 * OD; + public float OdFromHitwindow(double hitwindow300) => (float)(80.0 - hitwindow300) / 6; + public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); + public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); + public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) + { + BeatmapDifficulty? adjustedDifficulty = null; + isRateAdjusted = (false, false); + + if (mods.Any(m => m is IApplicableToDifficulty)) + { + adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.OfType()) + mod.ApplyToDifficulty(adjustedDifficulty); + } + + if (mods.Any(m => m is ModRateAdjust)) + { + adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.OfType()) + { + double speedChange = (float)mod.SpeedChange.Value; + + float ar = adjustedDifficulty.ApproachRate; + float od = adjustedDifficulty.OverallDifficulty; + + adjustedDifficulty.ApproachRate = ChangeArFromRate(ar, speedChange); + adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(od, speedChange); + + if (adjustedDifficulty.ApproachRate != ar) isRateAdjusted.AR = true; + if (adjustedDifficulty.OverallDifficulty != od) isRateAdjusted.OD = true; + } + } + + return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; + } } } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 23d732645e..801e46f9c3 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -263,8 +263,39 @@ namespace osu.Game.Rulesets.Taiko }), true) }; } - public override double HitwindowFromOd(float OD) => 35.0 - 15.0 * (OD - 5) / 5; - public override float OdFromHitwindow(double hitwindow300) => (float)(5 * (35 - hitwindow300) / 15 + 5); - public override float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); + public double HitwindowFromOd(float OD) => 35.0 - 15.0 * (OD - 5) / 5; + public float OdFromHitwindow(double hitwindow300) => (float)(5 * (35 - hitwindow300) / 15 + 5); + public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); + public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) + { + BeatmapDifficulty? adjustedDifficulty = null; + isRateAdjusted = (false, false); + + if (mods.Any(m => m is IApplicableToDifficulty)) + { + adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.OfType()) + mod.ApplyToDifficulty(adjustedDifficulty); + } + + if (mods.Any(m => m is ModRateAdjust)) + { + adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.OfType()) + { + double speedChange = (float)mod.SpeedChange.Value; + + float od = adjustedDifficulty.OverallDifficulty; + + adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(od, speedChange); + + if (adjustedDifficulty.OverallDifficulty != od) isRateAdjusted.OD = true; + } + } + + return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; + } } } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index ed4bc0ea7a..0f7655812a 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -389,13 +389,6 @@ namespace osu.Game.Rulesets /// Can be overridden to alter the difficulty section to the editor beatmap setup screen. ///
public virtual DifficultySection? CreateEditorDifficultySection() => null; - - public virtual double PreemptFromAr(float AR) => 0; - public virtual float ArFromPreempt(double preempt) => 5; - public virtual double HitwindowFromOd(float OD) => 0; - public virtual float OdFromHitwindow(double hitwindow300) => 0; - - public virtual float ChangeArFromRate(float AR, double rate) => AR; - public virtual float ChangeOdFromRate(float OD, double rate) => OD; + public virtual BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) => (BeatmapDifficulty)baseDifficulty; } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 4b7eee380d..4a0eed86f8 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -15,7 +15,6 @@ using osu.Game.Beatmaps; using osu.Framework.Bindables; using System.Collections.Generic; using osu.Game.Rulesets.Mods; -using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Extensions; @@ -44,9 +43,6 @@ namespace osu.Game.Screens.Select.Details protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; - private bool isArRateAdjusted { get; set; } - private bool isOdRateAdjusted { get; set; } - private IBeatmapInfo beatmapInfo; public IBeatmapInfo BeatmapInfo @@ -120,35 +116,12 @@ namespace osu.Game.Screens.Select.Details { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; BeatmapDifficulty adjustedDifficulty = null; - isArRateAdjusted = false; - isOdRateAdjusted = false; + (bool AR, bool OD) isRateAdjusted = (false, false); - if (baseDifficulty != null && mods.Value.Any(m => m is IApplicableToDifficulty)) + if (baseDifficulty != null) { - adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - - foreach (var mod in mods.Value.OfType()) - mod.ApplyToDifficulty(adjustedDifficulty); - } - - if (baseDifficulty != null && mods.Value.Any(m => m is ModRateAdjust)) - { - adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); Ruleset ruleset = gameRuleset.Value.CreateInstance(); - - foreach (var mod in mods.Value.OfType()) - { - double speedChange = (float)mod.SpeedChange.Value; - - float ar = adjustedDifficulty.ApproachRate; - float od = adjustedDifficulty.OverallDifficulty; - - adjustedDifficulty.ApproachRate = ruleset.ChangeArFromRate(ar, speedChange); - adjustedDifficulty.OverallDifficulty = ruleset.ChangeOdFromRate(od, speedChange); - - if (adjustedDifficulty.ApproachRate != ar) isArRateAdjusted = true; - if (adjustedDifficulty.OverallDifficulty != od) isOdRateAdjusted = true; - } + adjustedDifficulty = ruleset.GetEffectiveDifficulty(baseDifficulty, mods.Value, ref isRateAdjusted); } switch (BeatmapInfo?.Ruleset.OnlineID) @@ -167,8 +140,8 @@ namespace osu.Game.Screens.Select.Details } HpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate, false); - Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty, isOdRateAdjusted); - ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate, isArRateAdjusted); + Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty, isRateAdjusted.OD); + ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate, isRateAdjusted.AR); updateStarDifficulty(); } From a04333c17bdc8ed82b99a57ce04b609b6f9a6b5b Mon Sep 17 00:00:00 2001 From: tsrk Date: Tue, 29 Aug 2023 19:42:37 +0200 Subject: [PATCH 0025/1118] style: use `HitSampleInfo.All` properties --- osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index 323888a450..bd4649f065 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -23,8 +23,6 @@ namespace osu.Game.Rulesets.Edit.Checks private const int delay_threshold_negligible = 1; private readonly string[] audioExtensions = { "mp3", "ogg", "wav" }; - private readonly string[] bankNames = { HitSampleInfo.BANK_NORMAL, HitSampleInfo.BANK_SOFT, HitSampleInfo.BANK_DRUM }; - private readonly string[] sampleSets = { HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_WHISTLE, HitSampleInfo.HIT_FINISH, HitSampleInfo.HIT_CLAP }; public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Delayed hit sounds."); @@ -125,7 +123,8 @@ namespace osu.Game.Rulesets.Edit.Checks string bank = parts[0]; string sampleSet = parts[1]; - return bankNames.Contains(bank) && sampleSets.Any(sampleSet.StartsWith); + return HitSampleInfo.AllBanks.Contains(bank) + && HitSampleInfo.AllAdditions.Concat(new[] { HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FLOURISH }).Any(sampleSet.StartsWith); } public class IssueTemplateConsequentDelay : IssueTemplate From a7a64b57e93280624ac39481568050a9bf6dc8de Mon Sep 17 00:00:00 2001 From: tsrk Date: Tue, 29 Aug 2023 19:45:11 +0200 Subject: [PATCH 0026/1118] fix: use `using` statement for waveform auto-disposal --- .../Edit/Checks/CheckDelayedHitsounds.cs | 83 ++++++++++--------- 1 file changed, 42 insertions(+), 41 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index bd4649f065..e4be2e7198 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -57,54 +57,55 @@ namespace osu.Game.Rulesets.Edit.Checks if (!isHitSound(file.Filename)) continue; - Waveform waveform = new Waveform(stream); - - var points = waveform.GetPoints(); - - // Skip muted samples - if (points.Length == 0 || points.Sum(getAverageAmplitude) <= silence_threshold) - continue; - - float maxAmplitude = points.Select(getAverageAmplitude).Max(); - - int consequentDelay = 0; - int delay = 0; - float amplitude = 0; - - while (delay + consequentDelay < points.Length) + using (Waveform waveform = new Waveform(stream)) { - amplitude += getAverageAmplitude(points[delay]); + var points = waveform.GetPoints(); - // Reached peak amplitude/transient - if (amplitude >= maxAmplitude) - break; + // Skip muted samples + if (points.Length == 0 || points.Sum(getAverageAmplitude) <= silence_threshold) + continue; - amplitude *= falloff_factor; + float maxAmplitude = points.Select(getAverageAmplitude).Max(); - if (amplitude < silence_threshold) + int consequentDelay = 0; + int delay = 0; + float amplitude = 0; + + while (delay + consequentDelay < points.Length) { - amplitude = 0; - consequentDelay++; + amplitude += getAverageAmplitude(points[delay]); + + // Reached peak amplitude/transient + if (amplitude >= maxAmplitude) + break; + + amplitude *= falloff_factor; + + if (amplitude < silence_threshold) + { + amplitude = 0; + consequentDelay++; + } + + delay++; } - delay++; - } - - if (consequentDelay >= delay_threshold) - yield return new IssueTemplateConsequentDelay(this).Create(file.Filename, consequentDelay); - else if (consequentDelay + delay >= delay_threshold) - { - if (consequentDelay > 0) - yield return new IssueTemplateDelay(this).Create(file.Filename, consequentDelay, delay); - else - yield return new IssueTemplateDelayNoSilence(this).Create(file.Filename, delay); - } - else if (consequentDelay + delay >= delay_threshold_negligible) - { - if (consequentDelay > 0) - yield return new IssuTemplateMinorDelay(this).Create(file.Filename, consequentDelay, delay); - else - yield return new IssuTemplateMinorDelayNoSilence(this).Create(file.Filename, delay); + if (consequentDelay >= delay_threshold) + yield return new IssueTemplateConsequentDelay(this).Create(file.Filename, consequentDelay); + else if (consequentDelay + delay >= delay_threshold) + { + if (consequentDelay > 0) + yield return new IssueTemplateDelay(this).Create(file.Filename, consequentDelay, delay); + else + yield return new IssueTemplateDelayNoSilence(this).Create(file.Filename, delay); + } + else if (consequentDelay + delay >= delay_threshold_negligible) + { + if (consequentDelay > 0) + yield return new IssuTemplateMinorDelay(this).Create(file.Filename, consequentDelay, delay); + else + yield return new IssuTemplateMinorDelayNoSilence(this).Create(file.Filename, delay); + } } } } From c1a7ad09b4cf9f47b2fb559c91a1620481cf99d7 Mon Sep 17 00:00:00 2001 From: tsrk Date: Tue, 29 Aug 2023 20:10:35 +0200 Subject: [PATCH 0027/1118] style: move common audio utils to separate static class --- .../Rulesets/Edit/Checks/CheckDelayedHitsounds.cs | 10 +++------- .../Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs | 6 +----- .../Edit/Checks/Components/AudioCheckUtils.cs | 11 +++++++++++ 3 files changed, 15 insertions(+), 12 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index e4be2e7198..fab7cdb7e4 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -22,8 +22,6 @@ namespace osu.Game.Rulesets.Edit.Checks private const int delay_threshold = 5; private const int delay_threshold_negligible = 1; - private readonly string[] audioExtensions = { "mp3", "ogg", "wav" }; - public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Delayed hit sounds."); public IEnumerable PossibleTemplates => new IssueTemplate[] @@ -51,9 +49,6 @@ namespace osu.Game.Rulesets.Edit.Checks if (stream == null) continue; - if (!hasAudioExtension(file.Filename)) - continue; - if (!isHitSound(file.Filename)) continue; @@ -111,10 +106,11 @@ namespace osu.Game.Rulesets.Edit.Checks } } - private bool hasAudioExtension(string filename) => audioExtensions.Any(filename.ToLowerInvariant().EndsWith); - private bool isHitSound(string filename) { + if (!AudioCheckUtils.HasAudioExtension(filename)) + return false; + // - string[] parts = filename.ToLowerInvariant().Split('-'); diff --git a/osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs b/osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs index 1c2ea36948..32a3aa5ad9 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckTooShortAudioFiles.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.IO; -using System.Linq; using ManagedBass; using osu.Framework.Audio.Callbacks; using osu.Game.Extensions; @@ -16,8 +15,6 @@ namespace osu.Game.Rulesets.Edit.Checks private const int ms_threshold = 25; private const int min_bytes_threshold = 100; - private readonly string[] audioExtensions = { "mp3", "ogg", "wav" }; - public CheckMetadata Metadata => new CheckMetadata(CheckCategory.Audio, "Too short audio files"); public IEnumerable PossibleTemplates => new IssueTemplate[] @@ -46,7 +43,7 @@ namespace osu.Game.Rulesets.Edit.Checks { // If the file is not likely to be properly parsed by Bass, we don't produce Error issues about it. // Image files and audio files devoid of audio data both fail, for example, but neither would be issues in this check. - if (hasAudioExtension(file.Filename) && probablyHasAudioData(data)) + if (AudioCheckUtils.HasAudioExtension(file.Filename) && probablyHasAudioData(data)) yield return new IssueTemplateBadFormat(this).Create(file.Filename); continue; @@ -63,7 +60,6 @@ namespace osu.Game.Rulesets.Edit.Checks } } - private bool hasAudioExtension(string filename) => audioExtensions.Any(filename.ToLowerInvariant().EndsWith); private bool probablyHasAudioData(Stream data) => data.Length > min_bytes_threshold; public class IssueTemplateTooShort : IssueTemplate diff --git a/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs b/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs new file mode 100644 index 0000000000..b2cc950b94 --- /dev/null +++ b/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs @@ -0,0 +1,11 @@ +using System.Linq; + +namespace osu.Game.Rulesets.Edit.Checks.Components +{ + public static class AudioCheckUtils + { + public static readonly string[] AUDIO_EXTENSIONS = { "mp3", "ogg", "wav" }; + + public static bool HasAudioExtension(string filename) => AUDIO_EXTENSIONS.Any(filename.ToLowerInvariant().EndsWith); + } +} \ No newline at end of file From b3d432b0d520c3b4be2cb941d9f772842e6a37b7 Mon Sep 17 00:00:00 2001 From: tsrk Date: Tue, 29 Aug 2023 20:15:33 +0200 Subject: [PATCH 0028/1118] fix: issue template typo in `CheckDelayedHitsounds` --- .../Editing/Checks/CheckDelayedHitsoundsTest.cs | 2 +- .../Edit/Checks/CheckDelayedHitsounds.cs | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs b/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs index 912b916a84..20b9643ab4 100644 --- a/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs +++ b/osu.Game.Tests/Editing/Checks/CheckDelayedHitsoundsTest.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Editing.Checks var issues = check.Run(getContext(resourceStream)).ToList(); Assert.That(issues, Has.Count.EqualTo(1)); - Assert.That(issues.Single().Template is CheckDelayedHitsounds.IssuTemplateMinorDelay); + Assert.That(issues.Single().Template is CheckDelayedHitsounds.IssueTemplateMinorDelay); } } diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index fab7cdb7e4..5d12c3fea8 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -29,8 +29,8 @@ namespace osu.Game.Rulesets.Edit.Checks new IssueTemplateConsequentDelay(this), new IssueTemplateDelay(this), new IssueTemplateDelayNoSilence(this), - new IssuTemplateMinorDelay(this), - new IssuTemplateMinorDelayNoSilence(this), + new IssueTemplateMinorDelay(this), + new IssueTemplateMinorDelayNoSilence(this), }; private float getAverageAmplitude(Waveform.Point point) => (point.AmplitudeLeft + point.AmplitudeRight) / 2; @@ -97,9 +97,9 @@ namespace osu.Game.Rulesets.Edit.Checks else if (consequentDelay + delay >= delay_threshold_negligible) { if (consequentDelay > 0) - yield return new IssuTemplateMinorDelay(this).Create(file.Filename, consequentDelay, delay); + yield return new IssueTemplateMinorDelay(this).Create(file.Filename, consequentDelay, delay); else - yield return new IssuTemplateMinorDelayNoSilence(this).Create(file.Filename, delay); + yield return new IssueTemplateMinorDelayNoSilence(this).Create(file.Filename, delay); } } } @@ -157,9 +157,9 @@ namespace osu.Game.Rulesets.Edit.Checks public Issue Create(string filename, int delay) => new Issue(this, filename, delay); } - public class IssuTemplateMinorDelay : IssueTemplate + public class IssueTemplateMinorDelay : IssueTemplate { - public IssuTemplateMinorDelay(ICheck check) + public IssueTemplateMinorDelay(ICheck check) : base(check, IssueType.Negligible, "\"{0}\" has a transient delay of ~{1:0.##} ms, of which {2:0.##} ms is complete silence.") { @@ -168,9 +168,9 @@ namespace osu.Game.Rulesets.Edit.Checks public Issue Create(string filename, int consequentDelay, int delay) => new Issue(this, filename, delay, consequentDelay); } - public class IssuTemplateMinorDelayNoSilence : IssueTemplate + public class IssueTemplateMinorDelayNoSilence : IssueTemplate { - public IssuTemplateMinorDelayNoSilence(ICheck check) + public IssueTemplateMinorDelayNoSilence(ICheck check) : base(check, IssueType.Negligible, "\"{0}\" has a transient delay of ~{1:0.##} ms.") { From 5ecd5142d8ddc61d1394868e0627ceebc702e1ad Mon Sep 17 00:00:00 2001 From: tsrk Date: Tue, 29 Aug 2023 23:27:15 +0200 Subject: [PATCH 0029/1118] fix(AudioCheckUtils): add license header --- osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs b/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs index b2cc950b94..782369d72a 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System.Linq; namespace osu.Game.Rulesets.Edit.Checks.Components @@ -8,4 +11,4 @@ namespace osu.Game.Rulesets.Edit.Checks.Components public static bool HasAudioExtension(string filename) => AUDIO_EXTENSIONS.Any(filename.ToLowerInvariant().EndsWith); } -} \ No newline at end of file +} From 5f00d3e2a44a32bac0a2a42366b84c4fa0a797a1 Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 1 Sep 2023 20:54:02 +0200 Subject: [PATCH 0030/1118] style: convert to `using` declaration for less nesting --- .../Edit/Checks/CheckDelayedHitsounds.cs | 83 +++++++++---------- 1 file changed, 41 insertions(+), 42 deletions(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index 5d12c3fea8..0040deec80 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -52,55 +52,54 @@ namespace osu.Game.Rulesets.Edit.Checks if (!isHitSound(file.Filename)) continue; - using (Waveform waveform = new Waveform(stream)) + using Waveform waveform = new Waveform(stream); + + var points = waveform.GetPoints(); + + // Skip muted samples + if (points.Length == 0 || points.Sum(getAverageAmplitude) <= silence_threshold) + continue; + + float maxAmplitude = points.Select(getAverageAmplitude).Max(); + + int consequentDelay = 0; + int delay = 0; + float amplitude = 0; + + while (delay + consequentDelay < points.Length) { - var points = waveform.GetPoints(); + amplitude += getAverageAmplitude(points[delay]); - // Skip muted samples - if (points.Length == 0 || points.Sum(getAverageAmplitude) <= silence_threshold) - continue; + // Reached peak amplitude/transient + if (amplitude >= maxAmplitude) + break; - float maxAmplitude = points.Select(getAverageAmplitude).Max(); + amplitude *= falloff_factor; - int consequentDelay = 0; - int delay = 0; - float amplitude = 0; - - while (delay + consequentDelay < points.Length) + if (amplitude < silence_threshold) { - amplitude += getAverageAmplitude(points[delay]); - - // Reached peak amplitude/transient - if (amplitude >= maxAmplitude) - break; - - amplitude *= falloff_factor; - - if (amplitude < silence_threshold) - { - amplitude = 0; - consequentDelay++; - } - - delay++; + amplitude = 0; + consequentDelay++; } - if (consequentDelay >= delay_threshold) - yield return new IssueTemplateConsequentDelay(this).Create(file.Filename, consequentDelay); - else if (consequentDelay + delay >= delay_threshold) - { - if (consequentDelay > 0) - yield return new IssueTemplateDelay(this).Create(file.Filename, consequentDelay, delay); - else - yield return new IssueTemplateDelayNoSilence(this).Create(file.Filename, delay); - } - else if (consequentDelay + delay >= delay_threshold_negligible) - { - if (consequentDelay > 0) - yield return new IssueTemplateMinorDelay(this).Create(file.Filename, consequentDelay, delay); - else - yield return new IssueTemplateMinorDelayNoSilence(this).Create(file.Filename, delay); - } + delay++; + } + + if (consequentDelay >= delay_threshold) + yield return new IssueTemplateConsequentDelay(this).Create(file.Filename, consequentDelay); + else if (consequentDelay + delay >= delay_threshold) + { + if (consequentDelay > 0) + yield return new IssueTemplateDelay(this).Create(file.Filename, consequentDelay, delay); + else + yield return new IssueTemplateDelayNoSilence(this).Create(file.Filename, delay); + } + else if (consequentDelay + delay >= delay_threshold_negligible) + { + if (consequentDelay > 0) + yield return new IssueTemplateMinorDelay(this).Create(file.Filename, consequentDelay, delay); + else + yield return new IssueTemplateMinorDelayNoSilence(this).Create(file.Filename, delay); } } } From 2e2dcd99a6230aee8344fee3f45f4c33a0804d32 Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 1 Sep 2023 20:56:42 +0200 Subject: [PATCH 0031/1118] fix(AudioCheckUtils): use `Path.GetExtension()` To avoid filename escaping --- osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs b/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs index 782369d72a..b8cbe63c1e 100644 --- a/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs +++ b/osu.Game/Rulesets/Edit/Checks/Components/AudioCheckUtils.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.IO; using System.Linq; namespace osu.Game.Rulesets.Edit.Checks.Components @@ -9,6 +10,6 @@ namespace osu.Game.Rulesets.Edit.Checks.Components { public static readonly string[] AUDIO_EXTENSIONS = { "mp3", "ogg", "wav" }; - public static bool HasAudioExtension(string filename) => AUDIO_EXTENSIONS.Any(filename.ToLowerInvariant().EndsWith); + public static bool HasAudioExtension(string filename) => AUDIO_EXTENSIONS.Any(Path.GetExtension(filename).ToLowerInvariant().EndsWith); } } From a4e146ad9b8d01da1d4981ec59e87d6c7c284466 Mon Sep 17 00:00:00 2001 From: tsrk Date: Fri, 1 Sep 2023 21:02:43 +0200 Subject: [PATCH 0032/1118] style: remove usage of `HIT_FLOURISH` Unaware clueless usage --- osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs index 0040deec80..d6cd4f4caa 100644 --- a/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs +++ b/osu.Game/Rulesets/Edit/Checks/CheckDelayedHitsounds.cs @@ -120,7 +120,7 @@ namespace osu.Game.Rulesets.Edit.Checks string sampleSet = parts[1]; return HitSampleInfo.AllBanks.Contains(bank) - && HitSampleInfo.AllAdditions.Concat(new[] { HitSampleInfo.HIT_NORMAL, HitSampleInfo.HIT_FLOURISH }).Any(sampleSet.StartsWith); + && HitSampleInfo.AllAdditions.Append(HitSampleInfo.HIT_NORMAL).Any(sampleSet.StartsWith); } public class IssueTemplateConsequentDelay : IssueTemplate From 1a70110a4eed3e663ae6bbba81a9ed8a4b15cbf5 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Tue, 12 Sep 2023 16:44:44 +0300 Subject: [PATCH 0033/1118] Added BeatmapAttributesDisplay support --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 6 +-- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 6 +-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 8 ++-- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 6 +-- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 17 +++++-- .../Overlays/Mods/VerticalAttributeDisplay.cs | 46 +++++++++++++++++-- osu.Game/Rulesets/Ruleset.cs | 17 ++++++- .../Screens/Select/Details/AdvancedStats.cs | 8 ++-- 8 files changed, 89 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 4f499cee62..f0b20c68c4 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -238,10 +238,10 @@ namespace osu.Game.Rulesets.Catch public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); - public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) + public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) { BeatmapDifficulty? adjustedDifficulty = null; - isRateAdjusted = (false, false); + rateAdjustedInfo = (RateAdjustType.NotChanged, RateAdjustType.NotChanged); if (mods.Any(m => m is IApplicableToDifficulty)) { @@ -264,7 +264,7 @@ namespace osu.Game.Rulesets.Catch adjustedDifficulty.ApproachRate = ChangeArFromRate(ar, speedChange); - if (adjustedDifficulty.ApproachRate != ar) isRateAdjusted.AR = true; + rateAdjustedInfo.AR = GetRateAdjustType(ar, adjustedDifficulty.ApproachRate); } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 7d16806397..a8a54ebf05 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -431,10 +431,10 @@ namespace osu.Game.Rulesets.Mania public double HitwindowFromOd(float OD) => 64.0 - 3 * OD; public float OdFromHitwindow(double hitwindow300) => (float)(64.0 - hitwindow300) / 3; public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); - public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) + public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) { BeatmapDifficulty? adjustedDifficulty = null; - isRateAdjusted = (false, false); + rateAdjustedInfo = (RateAdjustType.NotChanged, RateAdjustType.NotChanged); if (mods.Any(m => m is IApplicableToDifficulty)) { @@ -456,7 +456,7 @@ namespace osu.Game.Rulesets.Mania adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(od, speedChange); - if (adjustedDifficulty.OverallDifficulty != od) isRateAdjusted.OD = true; + rateAdjustedInfo.OD = GetRateAdjustType(od, adjustedDifficulty.OverallDifficulty); } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 250685ec0f..4810be5db2 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -335,10 +335,10 @@ namespace osu.Game.Rulesets.Osu public float OdFromHitwindow(double hitwindow300) => (float)(80.0 - hitwindow300) / 6; public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); - public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) + public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) { BeatmapDifficulty? adjustedDifficulty = null; - isRateAdjusted = (false, false); + rateAdjustedInfo = (RateAdjustType.NotChanged, RateAdjustType.NotChanged); if (mods.Any(m => m is IApplicableToDifficulty)) { @@ -362,8 +362,8 @@ namespace osu.Game.Rulesets.Osu adjustedDifficulty.ApproachRate = ChangeArFromRate(ar, speedChange); adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(od, speedChange); - if (adjustedDifficulty.ApproachRate != ar) isRateAdjusted.AR = true; - if (adjustedDifficulty.OverallDifficulty != od) isRateAdjusted.OD = true; + rateAdjustedInfo.AR = GetRateAdjustType(ar, adjustedDifficulty.ApproachRate); + rateAdjustedInfo.OD = GetRateAdjustType(od, adjustedDifficulty.OverallDifficulty); } } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 801e46f9c3..a9abbea251 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -266,10 +266,10 @@ namespace osu.Game.Rulesets.Taiko public double HitwindowFromOd(float OD) => 35.0 - 15.0 * (OD - 5) / 5; public float OdFromHitwindow(double hitwindow300) => (float)(5 * (35 - hitwindow300) / 15 + 5); public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); - public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) + public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) { BeatmapDifficulty? adjustedDifficulty = null; - isRateAdjusted = (false, false); + rateAdjustedInfo = (RateAdjustType.NotChanged, RateAdjustType.NotChanged); if (mods.Any(m => m is IApplicableToDifficulty)) { @@ -291,7 +291,7 @@ namespace osu.Game.Rulesets.Taiko adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(od, speedChange); - if (adjustedDifficulty.OverallDifficulty != od) isRateAdjusted.OD = true; + rateAdjustedInfo.OD = GetRateAdjustType(od, adjustedDifficulty.OverallDifficulty); } } diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 2aea53090a..d0ee9750b2 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -16,6 +16,7 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Graphics; @@ -63,6 +64,10 @@ namespace osu.Game.Overlays.Mods [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } = null!; + [Resolved] + private OsuGameBase game { get; set; } = null!; + private IBindable gameRuleset = null!; + private CancellationTokenSource? cancellationSource; private IBindable starDifficulty = null!; @@ -195,6 +200,9 @@ namespace osu.Game.Overlays.Mods updateCollapsedState(); }); + gameRuleset = game.Ruleset.GetBoundCopy(); + gameRuleset.BindValueChanged(_ => updateValues()); + BeatmapInfo.BindValueChanged(_ => updateValues(), true); } @@ -243,9 +251,12 @@ namespace osu.Game.Overlays.Mods bpmDisplay.Current.Value = BeatmapInfo.Value.BPM * rate; - BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); - foreach (var mod in mods.Value.OfType()) - mod.ApplyToDifficulty(adjustedDifficulty); + (Ruleset.RateAdjustType AR, Ruleset.RateAdjustType OD) rateAdjustType = (Ruleset.RateAdjustType.NotChanged, Ruleset.RateAdjustType.NotChanged); + + Ruleset ruleset = gameRuleset.Value.CreateInstance(); + BeatmapDifficulty adjustedDifficulty = ruleset.GetEffectiveDifficulty(BeatmapInfo.Value.Difficulty, mods.Value, ref rateAdjustType); + approachRateDisplay.RateChangeType.Value = rateAdjustType.AR; + overallDifficultyDisplay.RateChangeType.Value = rateAdjustType.OD; circleSizeDisplay.Current.Value = adjustedDifficulty.CircleSize; drainRateDisplay.Current.Value = adjustedDifficulty.DrainRate; diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs index 60cc875dbb..16d75cd448 100644 --- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; @@ -10,6 +12,9 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osuTK.Graphics; namespace osu.Game.Overlays.Mods { @@ -23,11 +28,43 @@ namespace osu.Game.Overlays.Mods private readonly BindableWithCurrent current = new BindableWithCurrent(); + public Bindable RateChangeType = new Bindable(Ruleset.RateAdjustType.NotChanged); + /// /// Text to display in the top area of the display. /// public LocalisableString Label { get; protected set; } + private EffectCounter counter; + private OsuSpriteText text; + + [Resolved] + private OsuColour colours { get; set; } = null!; + private void updateTextColor() + { + Color4 newColor; + switch (RateChangeType.Value) + { + case Ruleset.RateAdjustType.NotChanged: + newColor = Color4.White; + break; + + case Ruleset.RateAdjustType.DifficultyReduction: + newColor = colours.ForModType(ModType.DifficultyReduction); + break; + + case Ruleset.RateAdjustType.DifficultyIncrease: + newColor = colours.ForModType(ModType.DifficultyIncrease); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(RateChangeType.Value)); + } + + text.Colour = newColor; + counter.Colour = newColor; + } + public VerticalAttributeDisplay(LocalisableString label) { Label = label; @@ -37,6 +74,8 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.CentreLeft; Anchor = Anchor.CentreLeft; + RateChangeType.BindValueChanged(_ => updateTextColor()); + InternalChild = new FillFlowContainer { Origin = Anchor.CentreLeft, @@ -45,7 +84,7 @@ namespace osu.Game.Overlays.Mods Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuSpriteText + text = new OsuSpriteText { Origin = Anchor.Centre, Anchor = Anchor.Centre, @@ -53,7 +92,7 @@ namespace osu.Game.Overlays.Mods Margin = new MarginPadding { Horizontal = 15 }, // to reserve space for 0.XX value Font = OsuFont.Default.With(size: 20, weight: FontWeight.Bold) }, - new EffectCounter + counter = new EffectCounter { Origin = Anchor.Centre, Anchor = Anchor.Centre, @@ -62,12 +101,11 @@ namespace osu.Game.Overlays.Mods } }; } - private partial class EffectCounter : RollingCounter { protected override double RollingDuration => 500; - protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0.0"); + protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0.0#"); protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText { diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 0f7655812a..7a09bff4aa 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -389,6 +389,21 @@ namespace osu.Game.Rulesets /// Can be overridden to alter the difficulty section to the editor beatmap setup screen. /// public virtual DifficultySection? CreateEditorDifficultySection() => null; - public virtual BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (bool AR, bool OD) isRateAdjusted) => (BeatmapDifficulty)baseDifficulty; + public virtual BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) => (BeatmapDifficulty)baseDifficulty; + public enum RateAdjustType + { + NotChanged, + DifficultyReduction, + DifficultyIncrease + } + + protected RateAdjustType GetRateAdjustType(float baseValue, float adjustedValue) + { + if (adjustedValue > baseValue) + return RateAdjustType.DifficultyIncrease; + if (adjustedValue < baseValue) + return RateAdjustType.DifficultyReduction; + return RateAdjustType.NotChanged; + } } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 4a0eed86f8..867d86a69b 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -116,12 +116,12 @@ namespace osu.Game.Screens.Select.Details { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; BeatmapDifficulty adjustedDifficulty = null; - (bool AR, bool OD) isRateAdjusted = (false, false); + (Ruleset.RateAdjustType AR, Ruleset.RateAdjustType OD) rateAdjustInfo = (Ruleset.RateAdjustType.NotChanged, Ruleset.RateAdjustType.NotChanged); if (baseDifficulty != null) { Ruleset ruleset = gameRuleset.Value.CreateInstance(); - adjustedDifficulty = ruleset.GetEffectiveDifficulty(baseDifficulty, mods.Value, ref isRateAdjusted); + adjustedDifficulty = ruleset.GetEffectiveDifficulty(baseDifficulty, mods.Value, ref rateAdjustInfo); } switch (BeatmapInfo?.Ruleset.OnlineID) @@ -140,8 +140,8 @@ namespace osu.Game.Screens.Select.Details } HpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate, false); - Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty, isRateAdjusted.OD); - ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate, isRateAdjusted.AR); + Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty, rateAdjustInfo.OD != Ruleset.RateAdjustType.NotChanged); + ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate, rateAdjustInfo.OD != Ruleset.RateAdjustType.NotChanged); updateStarDifficulty(); } From 55064c387d4558e342e0287368bfb26178167062 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Sep 2023 08:11:53 +0200 Subject: [PATCH 0034/1118] Add mod controls to scoring test scene --- .../Tests/Visual/Gameplay/ScoringTestScene.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs b/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs index de4688a6fe..879a5e8a2b 100644 --- a/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs +++ b/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs @@ -18,6 +18,9 @@ using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Overlays; +using osu.Game.Overlays.Mods; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -53,6 +56,10 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly BindableBool scoreV1Visible = new BindableBool(true); private readonly BindableBool scoreV2Visible = new BindableBool(true); + private RoundedButton changeModsButton = null!; + private OsuSpriteText modsText = null!; + private TestModSelectOverlay modSelect = null!; + [Resolved] private OsuColour colours { get; set; } = null!; @@ -83,6 +90,7 @@ namespace osu.Game.Tests.Visual.Gameplay new Dimension(), new Dimension(GridSizeMode.AutoSize), new Dimension(GridSizeMode.AutoSize), + new Dimension(GridSizeMode.AutoSize), }, Content = new[] { @@ -104,6 +112,47 @@ namespace osu.Game.Tests.Visual.Gameplay }, }, new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 20 }, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "Selected mods", + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + new FillFlowContainer + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(10), + Children = new Drawable[] + { + changeModsButton = new RoundedButton + { + Text = "Change", + Width = 100, + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + }, + modsText = new OsuSpriteText + { + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + }, + } + } + } + } + }, + new Drawable[] { new FillFlowContainer { @@ -139,6 +188,11 @@ namespace osu.Game.Tests.Visual.Gameplay }, }, } + }, + modSelect = new TestModSelectOverlay + { + RelativeSizeAxes = Axes.Both, + SelectedMods = { BindTarget = SelectedMods } } }; @@ -159,6 +213,9 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.MaxCombo.BindTo(sliderMaxCombo.Current); + changeModsButton.Action = () => modSelect.Show(); + SelectedMods.BindValueChanged(mods => Rerun()); + Rerun(); }); } @@ -168,6 +225,10 @@ namespace osu.Game.Tests.Visual.Gameplay graphs.Clear(); legend.Clear(); + modsText.Text = SelectedMods.Value.Any() + ? string.Join(", ", SelectedMods.Value.Select(mod => mod.Acronym)) + : "(none)"; + runForProcessor("lazer-standardised", colours.Green1, ScoringMode.Standardised, standardisedVisible); runForProcessor("lazer-classic", colours.Blue1, ScoringMode.Classic, classicVisible); @@ -592,5 +653,16 @@ namespace osu.Game.Tests.Visual.Gameplay lineGraph.Alpha = Visible.Value ? 1 : 0; } } + + private partial class TestModSelectOverlay : UserModSelectOverlay + { + protected override bool ShowModEffects => true; + protected override bool ShowPresets => false; + + public TestModSelectOverlay() + : base(OverlayColourScheme.Aquamarine) + { + } + } } } From 419cc8784a5f3cc10849a5c202250529933693ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Sep 2023 08:20:09 +0200 Subject: [PATCH 0035/1118] Apply mods to processor-based score algorithms --- osu.Game.Rulesets.Catch.Tests/TestSceneScoring.cs | 9 ++++++--- osu.Game.Rulesets.Mania.Tests/TestSceneScoring.cs | 10 +++++++--- osu.Game.Rulesets.Osu.Tests/TestSceneScoring.cs | 10 +++++++--- osu.Game.Rulesets.Taiko.Tests/TestSceneScoring.cs | 9 ++++++--- osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs | 8 +++++--- 5 files changed, 31 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneScoring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneScoring.cs index dfdde0a325..886fbe7222 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneScoring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneScoring.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Bindables; using osu.Game.Beatmaps; @@ -10,6 +11,7 @@ using osu.Game.Rulesets.Catch.Judgements; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Tests.Visual.Gameplay; @@ -41,7 +43,8 @@ namespace osu.Game.Rulesets.Catch.Tests protected override IScoringAlgorithm CreateScoreV2(int maxCombo) => new ScoreV2(maxCombo); - protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode) => new CatchProcessorBasedScoringAlgorithm(beatmap, mode); + protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) + => new CatchProcessorBasedScoringAlgorithm(beatmap, mode, selectedMods); [Test] public void TestBasicScenarios() @@ -140,8 +143,8 @@ namespace osu.Game.Rulesets.Catch.Tests private class CatchProcessorBasedScoringAlgorithm : ProcessorBasedScoringAlgorithm { - public CatchProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode) - : base(beatmap, mode) + public CatchProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) + : base(beatmap, mode, selectedMods) { } diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneScoring.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneScoring.cs index ae3ea861ea..f37d130b87 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneScoring.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneScoring.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; @@ -9,6 +10,7 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Judgements; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Scoring; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Tests.Visual.Gameplay; @@ -27,7 +29,9 @@ namespace osu.Game.Rulesets.Mania.Tests protected override IScoringAlgorithm CreateScoreV1() => new ScoreV1(MaxCombo.Value); protected override IScoringAlgorithm CreateScoreV2(int maxCombo) => new ScoreV2(maxCombo); - protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode) => new ManiaProcessorBasedScoringAlgorithm(beatmap, mode); + + protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) + => new ManiaProcessorBasedScoringAlgorithm(beatmap, mode, selectedMods); [Test] public void TestBasicScenarios() @@ -158,8 +162,8 @@ namespace osu.Game.Rulesets.Mania.Tests private class ManiaProcessorBasedScoringAlgorithm : ProcessorBasedScoringAlgorithm { - public ManiaProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode) - : base(beatmap, mode) + public ManiaProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) + : base(beatmap, mode, selectedMods) { } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneScoring.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneScoring.cs index bb09328ab7..671817d30f 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneScoring.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneScoring.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; @@ -34,7 +36,9 @@ namespace osu.Game.Rulesets.Osu.Tests protected override IScoringAlgorithm CreateScoreV1() => new ScoreV1 { ScoreMultiplier = { BindTarget = scoreMultiplier } }; protected override IScoringAlgorithm CreateScoreV2(int maxCombo) => new ScoreV2(maxCombo); - protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode) => new OsuProcessorBasedScoringAlgorithm(beatmap, mode); + + protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList mods) + => new OsuProcessorBasedScoringAlgorithm(beatmap, mode, mods); [Test] public void TestBasicScenarios() @@ -162,8 +166,8 @@ namespace osu.Game.Rulesets.Osu.Tests private class OsuProcessorBasedScoringAlgorithm : ProcessorBasedScoringAlgorithm { - public OsuProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode) - : base(beatmap, mode) + public OsuProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) + : base(beatmap, mode, selectedMods) { } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneScoring.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneScoring.cs index e065070822..90c866acdb 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneScoring.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneScoring.cs @@ -2,10 +2,12 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using NUnit.Framework; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Judgements; @@ -35,7 +37,8 @@ namespace osu.Game.Rulesets.Taiko.Tests protected override IScoringAlgorithm CreateScoreV1() => new ScoreV1 { ScoreMultiplier = { BindTarget = scoreMultiplier } }; protected override IScoringAlgorithm CreateScoreV2(int maxCombo) => new ScoreV2(maxCombo); - protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode) => new TaikoProcessorBasedScoringAlgorithm(beatmap, mode); + protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) + => new TaikoProcessorBasedScoringAlgorithm(beatmap, mode, selectedMods); [Test] public void TestBasicScenarios() @@ -171,8 +174,8 @@ namespace osu.Game.Rulesets.Taiko.Tests private class TaikoProcessorBasedScoringAlgorithm : ProcessorBasedScoringAlgorithm { - public TaikoProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode) - : base(beatmap, mode) + public TaikoProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) + : base(beatmap, mode, selectedMods) { } diff --git a/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs b/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs index 879a5e8a2b..8a2a81ce96 100644 --- a/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs +++ b/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs @@ -23,6 +23,7 @@ using osu.Game.Overlays; using osu.Game.Overlays.Mods; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring.Legacy; using osuTK; @@ -37,7 +38,7 @@ namespace osu.Game.Tests.Visual.Gameplay protected abstract IScoringAlgorithm CreateScoreV1(); protected abstract IScoringAlgorithm CreateScoreV2(int maxCombo); - protected abstract ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode); + protected abstract ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList mods); protected Bindable MaxCombo => sliderMaxCombo.Current; protected BindableList NonPerfectLocations => graphs.NonPerfectLocations; @@ -270,7 +271,7 @@ namespace osu.Game.Tests.Visual.Gameplay { int maxCombo = sliderMaxCombo.Current.Value; var beatmap = CreateBeatmap(maxCombo); - var algorithm = CreateScoreAlgorithm(beatmap, scoringMode); + var algorithm = CreateScoreAlgorithm(beatmap, scoringMode, SelectedMods.Value); runForAlgorithm(new ScoringAlgorithmInfo { @@ -343,11 +344,12 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly ScoreProcessor scoreProcessor; private readonly ScoringMode mode; - protected ProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode) + protected ProcessorBasedScoringAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) { this.mode = mode; scoreProcessor = CreateScoreProcessor(); scoreProcessor.ApplyBeatmap(beatmap); + scoreProcessor.Mods.Value = selectedMods; } public void ApplyHit() => scoreProcessor.ApplyResult(CreatePerfectJudgementResult()); From d7e891140de52b8c8c0eb623040fd60df729cf17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Sep 2023 08:44:32 +0200 Subject: [PATCH 0036/1118] Apply mod multipliers to local score V1/V2 reimplementations --- .../TestSceneScoring.cs | 37 +++++++++++++--- .../TestSceneScoring.cs | 31 ++++++++++---- .../TestSceneScoring.cs | 42 +++++++++++++++---- .../TestSceneScoring.cs | 40 +++++++++++++++--- .../Tests/Visual/Gameplay/ScoringTestScene.cs | 8 ++-- 5 files changed, 128 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneScoring.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneScoring.cs index 886fbe7222..9f667358db 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneScoring.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneScoring.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Game.Beatmaps; @@ -13,6 +14,7 @@ using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Tests.Visual.Gameplay; namespace osu.Game.Rulesets.Catch.Tests @@ -39,9 +41,11 @@ namespace osu.Game.Rulesets.Catch.Tests return beatmap; } - protected override IScoringAlgorithm CreateScoreV1() => new ScoreV1 { ScoreMultiplier = { BindTarget = scoreMultiplier } }; + protected override IScoringAlgorithm CreateScoreV1(IReadOnlyList selectedMods) + => new ScoreV1(selectedMods) { ScoreMultiplier = { BindTarget = scoreMultiplier } }; - protected override IScoringAlgorithm CreateScoreV2(int maxCombo) => new ScoreV2(maxCombo); + protected override IScoringAlgorithm CreateScoreV2(int maxCombo, IReadOnlyList selectedMods) + => new ScoreV2(maxCombo, selectedMods); protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) => new CatchProcessorBasedScoringAlgorithm(beatmap, mode, selectedMods); @@ -72,10 +76,21 @@ namespace osu.Game.Rulesets.Catch.Tests private class ScoreV1 : IScoringAlgorithm { - private int currentCombo; + private readonly double modMultiplier; public BindableDouble ScoreMultiplier { get; } = new BindableDouble(); + private int currentCombo; + + public ScoreV1(IReadOnlyList selectedMods) + { + var ruleset = new CatchRuleset(); + modMultiplier = ruleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier(selectedMods, new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = ruleset.RulesetInfo + }); + } + public void ApplyHit() => applyHitV1(base_great); public void ApplyNonPerfect() => throw new NotSupportedException("catch does not have \"non-perfect\" judgements."); @@ -94,7 +109,7 @@ namespace osu.Game.Rulesets.Catch.Tests // combo multiplier // ReSharper disable once PossibleLossOfFraction - TotalScore += (int)(Math.Max(0, currentCombo - 1) * (baseScore / 25 * ScoreMultiplier.Value)); + TotalScore += (int)(Math.Max(0, currentCombo - 1) * (baseScore / 25 * (ScoreMultiplier.Value * modMultiplier))); currentCombo++; } @@ -107,13 +122,23 @@ namespace osu.Game.Rulesets.Catch.Tests private int currentCombo; private double comboPortion; + private readonly double modMultiplier; + private readonly double comboPortionMax; private const double combo_base = 4; private const int combo_cap = 200; - public ScoreV2(int maxCombo) + public ScoreV2(int maxCombo, IReadOnlyList selectedMods) { + var ruleset = new CatchRuleset(); + modMultiplier = ruleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier( + selectedMods.Append(new ModScoreV2()).ToList(), + new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = ruleset.RulesetInfo + }); + for (int i = 0; i < maxCombo; i++) ApplyHit(); @@ -138,7 +163,7 @@ namespace osu.Game.Rulesets.Catch.Tests } public long TotalScore - => (int)Math.Round(1000000 * comboPortion / comboPortionMax); // vast simplification, as we're not doing ticks here. + => (int)Math.Round((1000000 * comboPortion / comboPortionMax) * modMultiplier); // vast simplification, as we're not doing ticks here. } private class CatchProcessorBasedScoringAlgorithm : ProcessorBasedScoringAlgorithm diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneScoring.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneScoring.cs index f37d130b87..19d90e0551 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneScoring.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneScoring.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; @@ -12,6 +13,7 @@ using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Tests.Visual.Gameplay; namespace osu.Game.Rulesets.Mania.Tests @@ -27,8 +29,8 @@ namespace osu.Game.Rulesets.Mania.Tests return beatmap; } - protected override IScoringAlgorithm CreateScoreV1() => new ScoreV1(MaxCombo.Value); - protected override IScoringAlgorithm CreateScoreV2(int maxCombo) => new ScoreV2(maxCombo); + protected override IScoringAlgorithm CreateScoreV1(IReadOnlyList selectedMods) => new ScoreV1(MaxCombo.Value, selectedMods); + protected override IScoringAlgorithm CreateScoreV2(int maxCombo, IReadOnlyList selectedMods) => new ScoreV2(maxCombo, selectedMods); protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) => new ManiaProcessorBasedScoringAlgorithm(beatmap, mode, selectedMods); @@ -63,11 +65,17 @@ namespace osu.Game.Rulesets.Mania.Tests private int currentCombo; private double comboAddition = 100; private double totalScoreDouble; + private readonly double scoreMultiplier; - public ScoreV1(int maxCombo) + public ScoreV1(int maxCombo, IReadOnlyList selectedMods) { - scoreMultiplier = 500000d / maxCombo; + var ruleset = new ManiaRuleset(); + + scoreMultiplier = 500000d / maxCombo * ruleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier(selectedMods, new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = ruleset.RulesetInfo + }); } public void ApplyHit() => applyHitV1(320, add => add + 2, 32); @@ -107,13 +115,22 @@ namespace osu.Game.Rulesets.Mania.Tests private readonly double comboPortionMax; private readonly int maxCombo; + private readonly double modMultiplier; private const double combo_base = 4; - public ScoreV2(int maxCombo) + public ScoreV2(int maxCombo, IReadOnlyList selectedMods) { this.maxCombo = maxCombo; + var ruleset = new ManiaRuleset(); + modMultiplier = new ManiaRuleset().CreateLegacyScoreSimulator().GetLegacyScoreMultiplier( + selectedMods.Append(new ModScoreV2()).ToArray(), + new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = ruleset.RulesetInfo + }); + for (int i = 0; i < this.maxCombo; i++) ApplyHit(); @@ -152,10 +169,10 @@ namespace osu.Game.Rulesets.Mania.Tests float accuracy = (float)(currentBaseScore / maxBaseScore); return (int)Math.Round - ( + (( 200000 * comboPortion / comboPortionMax + 800000 * Math.Pow(accuracy, 2 + 2 * accuracy) * ((double)currentHits / maxCombo) - ); + ) * modMultiplier); } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneScoring.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneScoring.cs index 671817d30f..627c8f416e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneScoring.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneScoring.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Game.Beatmaps; @@ -13,6 +14,7 @@ using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Tests.Visual.Gameplay; namespace osu.Game.Rulesets.Osu.Tests @@ -34,8 +36,14 @@ namespace osu.Game.Rulesets.Osu.Tests return beatmap; } - protected override IScoringAlgorithm CreateScoreV1() => new ScoreV1 { ScoreMultiplier = { BindTarget = scoreMultiplier } }; - protected override IScoringAlgorithm CreateScoreV2(int maxCombo) => new ScoreV2(maxCombo); + protected override IScoringAlgorithm CreateScoreV1(IReadOnlyList selectedMods) + => new ScoreV1(selectedMods) + { + ScoreMultiplier = { BindTarget = scoreMultiplier } + }; + + protected override IScoringAlgorithm CreateScoreV2(int maxCombo, IReadOnlyList selectedMods) + => new ScoreV2(maxCombo, selectedMods); protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList mods) => new OsuProcessorBasedScoringAlgorithm(beatmap, mode, mods); @@ -75,9 +83,19 @@ namespace osu.Game.Rulesets.Osu.Tests private class ScoreV1 : IScoringAlgorithm { + private readonly double modMultiplier; + public BindableDouble ScoreMultiplier { get; } = new BindableDouble(); + private int currentCombo; - public BindableDouble ScoreMultiplier { get; } = new BindableDouble(); + public ScoreV1(IReadOnlyList selectedMods) + { + var ruleset = new OsuRuleset(); + modMultiplier = ruleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier(selectedMods, new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = ruleset.RulesetInfo + }); + } public void ApplyHit() => applyHitV1(base_great); public void ApplyNonPerfect() => applyHitV1(base_ok); @@ -95,7 +113,7 @@ namespace osu.Game.Rulesets.Osu.Tests // combo multiplier // ReSharper disable once PossibleLossOfFraction - TotalScore += (int)(Math.Max(0, currentCombo - 1) * (baseScore / 25 * ScoreMultiplier.Value)); + TotalScore += (int)(Math.Max(0, currentCombo - 1) * (baseScore / 25 * (ScoreMultiplier.Value * modMultiplier))); currentCombo++; } @@ -111,13 +129,23 @@ namespace osu.Game.Rulesets.Osu.Tests private double maxBaseScore; private int currentHits; + private readonly double modMultiplier; + private readonly double comboPortionMax; private readonly int maxCombo; - public ScoreV2(int maxCombo) + public ScoreV2(int maxCombo, IReadOnlyList selectedMods) { this.maxCombo = maxCombo; + var ruleset = new OsuRuleset(); + modMultiplier = ruleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier( + selectedMods.Append(new ModScoreV2()).ToList(), + new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = ruleset.RulesetInfo + }); + for (int i = 0; i < this.maxCombo; i++) ApplyHit(); @@ -156,10 +184,10 @@ namespace osu.Game.Rulesets.Osu.Tests double accuracy = currentBaseScore / maxBaseScore; return (int)Math.Round - ( + (( 700000 * comboPortion / comboPortionMax + 300000 * Math.Pow(accuracy, 10) * ((double)currentHits / maxCombo) - ); + ) * modMultiplier); } } } diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneScoring.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneScoring.cs index 90c866acdb..cf8e3767da 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneScoring.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneScoring.cs @@ -3,12 +3,14 @@ using System; using System.Collections.Generic; +using System.Linq; using NUnit.Framework; using osu.Framework.Bindables; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Judgements; using osu.Game.Rulesets.Taiko.Objects; @@ -34,8 +36,14 @@ namespace osu.Game.Rulesets.Taiko.Tests return beatmap; } - protected override IScoringAlgorithm CreateScoreV1() => new ScoreV1 { ScoreMultiplier = { BindTarget = scoreMultiplier } }; - protected override IScoringAlgorithm CreateScoreV2(int maxCombo) => new ScoreV2(maxCombo); + protected override IScoringAlgorithm CreateScoreV1(IReadOnlyList selectedMods) + => new ScoreV1(selectedMods) + { + ScoreMultiplier = { BindTarget = scoreMultiplier } + }; + + protected override IScoringAlgorithm CreateScoreV2(int maxCombo, IReadOnlyList selectedMods) + => new ScoreV2(maxCombo, selectedMods); protected override ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList selectedMods) => new TaikoProcessorBasedScoringAlgorithm(beatmap, mode, selectedMods); @@ -75,8 +83,19 @@ namespace osu.Game.Rulesets.Taiko.Tests private class ScoreV1 : IScoringAlgorithm { + private readonly double modMultiplier; + private int currentCombo; + public ScoreV1(IReadOnlyList selectedMods) + { + var ruleset = new TaikoRuleset(); + modMultiplier = ruleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier(selectedMods, new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = ruleset.RulesetInfo + }); + } + public BindableDouble ScoreMultiplier { get; } = new BindableDouble(); public void ApplyHit() => applyHitV1(base_great); @@ -97,7 +116,7 @@ namespace osu.Game.Rulesets.Taiko.Tests // combo multiplier // ReSharper disable once PossibleLossOfFraction - TotalScore += (int)((baseScore / 35) * 2 * (ScoreMultiplier.Value + 1)) * (Math.Min(100, currentCombo) / 10); + TotalScore += (int)((baseScore / 35) * 2 * (ScoreMultiplier.Value + 1) * modMultiplier) * (Math.Min(100, currentCombo) / 10); currentCombo++; } @@ -113,15 +132,24 @@ namespace osu.Game.Rulesets.Taiko.Tests private double maxBaseScore; private int currentHits; + private readonly double modMultiplier; private readonly double comboPortionMax; private readonly int maxCombo; private const double combo_base = 4; - public ScoreV2(int maxCombo) + public ScoreV2(int maxCombo, IReadOnlyList selectedMods) { this.maxCombo = maxCombo; + var ruleset = new TaikoRuleset(); + modMultiplier = ruleset.CreateLegacyScoreSimulator().GetLegacyScoreMultiplier( + selectedMods.Append(new ModScoreV2()).ToArray(), + new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = ruleset.RulesetInfo + }); + for (int i = 0; i < this.maxCombo; i++) ApplyHit(); @@ -164,10 +192,10 @@ namespace osu.Game.Rulesets.Taiko.Tests double accuracy = currentBaseScore / maxBaseScore; return (int)Math.Round - ( + (( 250000 * comboPortion / comboPortionMax + 750000 * Math.Pow(accuracy, 3.6) * ((double)currentHits / maxCombo) - ); + ) * modMultiplier); } } } diff --git a/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs b/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs index 8a2a81ce96..e7053e4202 100644 --- a/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs +++ b/osu.Game/Tests/Visual/Gameplay/ScoringTestScene.cs @@ -36,8 +36,8 @@ namespace osu.Game.Tests.Visual.Gameplay { protected abstract IBeatmap CreateBeatmap(int maxCombo); - protected abstract IScoringAlgorithm CreateScoreV1(); - protected abstract IScoringAlgorithm CreateScoreV2(int maxCombo); + protected abstract IScoringAlgorithm CreateScoreV1(IReadOnlyList selectedMods); + protected abstract IScoringAlgorithm CreateScoreV2(int maxCombo, IReadOnlyList selectedMods); protected abstract ProcessorBasedScoringAlgorithm CreateScoreAlgorithm(IBeatmap beatmap, ScoringMode mode, IReadOnlyList mods); protected Bindable MaxCombo => sliderMaxCombo.Current; @@ -237,14 +237,14 @@ namespace osu.Game.Tests.Visual.Gameplay { Name = "ScoreV1 (classic)", Colour = colours.Purple1, - Algorithm = CreateScoreV1(), + Algorithm = CreateScoreV1(SelectedMods.Value), Visible = scoreV1Visible }); runForAlgorithm(new ScoringAlgorithmInfo { Name = "ScoreV2", Colour = colours.Red1, - Algorithm = CreateScoreV2(sliderMaxCombo.Current.Value), + Algorithm = CreateScoreV2(sliderMaxCombo.Current.Value, SelectedMods.Value), Visible = scoreV2Visible }); From 43ab7f49426626b3d9536af481221c98e7dca86a Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sat, 4 Nov 2023 02:01:18 +0100 Subject: [PATCH 0037/1118] Added OpenEditorTimestamp base implementation --- osu.Game/Localisation/EditorStrings.cs | 15 +++ osu.Game/OsuGame.cs | 48 +++++++++ osu.Game/Screens/Edit/Editor.cs | 34 ++++++ .../Screens/Edit/EditorTimestampParser.cs | 101 ++++++++++++++++++ 4 files changed, 198 insertions(+) create mode 100644 osu.Game/Screens/Edit/EditorTimestampParser.cs diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index 93e52746c5..227dbc5e0c 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -119,6 +119,21 @@ namespace osu.Game.Localisation /// public static LocalisableString LimitedDistanceSnap => new TranslatableString(getKey(@"limited_distance_snap_grid"), @"Limit distance snap placement to current time"); + /// + /// "Must be in edit mode to handle editor links" + /// + public static LocalisableString MustBeInEdit => new TranslatableString(getKey(@"must_be_in_edit"), @"Must be in edit mode to handle editor links"); + + /// + /// "Failed to process timestamp" + /// + public static LocalisableString FailedToProcessTimestamp => new TranslatableString(getKey(@"failed_to_process_timestamp"), @"Failed to process timestamp"); + + /// + /// "The timestamp was too long to process" + /// + public static LocalisableString TooLongTimestamp => new TranslatableString(getKey(@"too_long_timestamp"), @"The timestamp was too long to process"); + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2f11964f6a..439e112bd3 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -58,6 +58,7 @@ using osu.Game.Performance; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens; +using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.OnlinePlay.Multiplayer; using osu.Game.Screens.Play; @@ -433,6 +434,9 @@ namespace osu.Game break; case LinkAction.OpenEditorTimestamp: + SeekToTimestamp(argString); + break; + case LinkAction.JoinMultiplayerMatch: case LinkAction.Spectate: waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification @@ -550,6 +554,50 @@ namespace osu.Game /// The build version of the update stream public void ShowChangelogBuild(string updateStream, string version) => waitForReady(() => changelogOverlay, _ => changelogOverlay.ShowBuild(updateStream, version)); + /// + /// Seek to a given timestamp in the Editor and select relevant HitObjects if needed + /// + /// The timestamp and the selected objects + public void SeekToTimestamp(string timestamp) + { + if (ScreenStack.CurrentScreen is not Editor editor) + { + waitForReady(() => Notifications, _ => Notifications.Post(new SimpleErrorNotification + { + Text = EditorStrings.MustBeInEdit, + })); + return; + } + + string[] groups = EditorTimestampParser.GetRegexGroups(timestamp); + + if (groups.Length != 2 || string.IsNullOrEmpty(groups[0])) + { + waitForReady(() => Notifications, _ => Notifications.Post(new SimpleErrorNotification + { + Text = EditorStrings.FailedToProcessTimestamp + })); + return; + } + + string timeGroup = groups[0]; + string objectsGroup = groups[1]; + string timeMinutes = timeGroup.Split(':').FirstOrDefault() ?? string.Empty; + + // Currently, lazer chat highlights infinite-long editor links like `10000000000:00:000 (1)` + // Limit timestamp link length at 30000 min (50 hr) to avoid parsing issues + if (timeMinutes.Length > 5 || double.Parse(timeMinutes) > 30_000) + { + waitForReady(() => Notifications, _ => Notifications.Post(new SimpleErrorNotification + { + Text = EditorStrings.TooLongTimestamp + })); + return; + } + + editor.SeekToTimestamp(timeGroup, objectsGroup); + } + /// /// Present a skin select immediately. /// diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 91c3c98f01..b2fad55fed 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -39,6 +39,7 @@ using osu.Game.Overlays.Notifications; using osu.Game.Overlays.OSD; using osu.Game.Rulesets; using osu.Game.Rulesets.Edit; +using osu.Game.Rulesets.Objects; using osu.Game.Screens.Edit.Components.Menus; using osu.Game.Screens.Edit.Compose; using osu.Game.Screens.Edit.Compose.Components.Timeline; @@ -1137,6 +1138,39 @@ namespace osu.Game.Screens.Edit loader?.CancelPendingDifficultySwitch(); } + public void SeekToTimestamp(string timeGroup, string objectsGroup) + { + double position = EditorTimestampParser.GetTotalMilliseconds(timeGroup); + editorBeatmap.SelectedHitObjects.Clear(); + + if (string.IsNullOrEmpty(objectsGroup)) + { + if (clock.IsRunning) + clock.Stop(); + + clock.Seek(position); + return; + } + + if (Mode.Value != EditorScreenMode.Compose) + Mode.Value = EditorScreenMode.Compose; + + // Seek to the next closest HitObject's position + HitObject nextObject = editorBeatmap.HitObjects.FirstOrDefault(x => x.StartTime >= position); + if (nextObject != null && nextObject.StartTime > 0) + position = nextObject.StartTime; + + List selected = EditorTimestampParser.GetSelectedHitObjects(editorBeatmap.HitObjects.ToList(), objectsGroup, position); + + if (selected.Any()) + editorBeatmap.SelectedHitObjects.AddRange(selected); + + if (clock.IsRunning) + clock.Stop(); + + clock.Seek(position); + } + public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); public double GetBeatLengthAtTime(double referenceTime) => editorBeatmap.GetBeatLengthAtTime(referenceTime); diff --git a/osu.Game/Screens/Edit/EditorTimestampParser.cs b/osu.Game/Screens/Edit/EditorTimestampParser.cs new file mode 100644 index 0000000000..44d614ca70 --- /dev/null +++ b/osu.Game/Screens/Edit/EditorTimestampParser.cs @@ -0,0 +1,101 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text.RegularExpressions; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; + +namespace osu.Game.Screens.Edit +{ + public static class EditorTimestampParser + { + private static readonly Regex timestamp_regex = new Regex(@"^(\d+:\d+:\d+)(?: \((\d+(?:[|,]\d+)*)\))?$", RegexOptions.Compiled); + + public static string[] GetRegexGroups(string timestamp) + { + Match match = timestamp_regex.Match(timestamp); + return match.Success + ? match.Groups.Values.Where(x => x is not Match).Select(x => x.Value).ToArray() + : Array.Empty(); + } + + public static double GetTotalMilliseconds(string timeGroup) + { + int[] times = timeGroup.Split(':').Select(int.Parse).ToArray(); + + Debug.Assert(times.Length == 3); + + return (times[0] * 60 + times[1]) * 1_000 + times[2]; + } + + public static List GetSelectedHitObjects(IEnumerable editorHitObjects, string objectsGroup, double position) + { + List hitObjects = editorHitObjects.Where(x => x.StartTime >= position).ToList(); + List selectedObjects = new List(); + + string[] objectsToSelect = objectsGroup.Split(',').ToArray(); + + foreach (string objectInfo in objectsToSelect) + { + HitObject? current = hitObjects.FirstOrDefault(x => shouldHitObjectBeSelected(x, objectInfo)); + + if (current == null) + continue; + + selectedObjects.Add(current); + hitObjects = hitObjects.Where(x => x != current && x.StartTime >= current.StartTime).ToList(); + } + + // Stable behavior + // - always selects next closest object when `objectsGroup` only has one, non-Column item + if (objectsToSelect.Length != 1 || objectsGroup.Contains('|')) + return selectedObjects; + + HitObject? nextClosest = editorHitObjects.FirstOrDefault(x => x.StartTime >= position); + if (nextClosest == null) + return selectedObjects; + + if (nextClosest.StartTime <= (selectedObjects.FirstOrDefault()?.StartTime ?? position)) + { + selectedObjects.Clear(); + selectedObjects.Add(nextClosest); + } + + return selectedObjects; + } + + private static bool shouldHitObjectBeSelected(HitObject hitObject, string objectInfo) + { + switch (hitObject) + { + // (combo) + case IHasComboInformation comboInfo: + { + if (!double.TryParse(objectInfo, out double comboValue) || comboValue < 1) + return false; + + return comboInfo.IndexInCurrentCombo + 1 == comboValue; + } + + // (time|column) + case IHasColumn column: + { + double[] split = objectInfo.Split('|').Select(double.Parse).ToArray(); + if (split.Length != 2) + return false; + + double timeValue = split[0]; + double columnValue = split[1]; + return hitObject.StartTime == timeValue && column.Column == columnValue; + } + + default: + return false; + } + } + } +} From f854e78bb03f6117b1cfb8b0579e867d1aae093f Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sat, 4 Nov 2023 03:29:05 +0100 Subject: [PATCH 0038/1118] Added ExclamationTriangle Icon to notifications --- osu.Game/OsuGame.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 439e112bd3..5acd958568 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -562,8 +562,9 @@ namespace osu.Game { if (ScreenStack.CurrentScreen is not Editor editor) { - waitForReady(() => Notifications, _ => Notifications.Post(new SimpleErrorNotification + waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification { + Icon = FontAwesome.Solid.ExclamationTriangle, Text = EditorStrings.MustBeInEdit, })); return; @@ -573,8 +574,9 @@ namespace osu.Game if (groups.Length != 2 || string.IsNullOrEmpty(groups[0])) { - waitForReady(() => Notifications, _ => Notifications.Post(new SimpleErrorNotification + waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification { + Icon = FontAwesome.Solid.ExclamationTriangle, Text = EditorStrings.FailedToProcessTimestamp })); return; @@ -588,8 +590,9 @@ namespace osu.Game // Limit timestamp link length at 30000 min (50 hr) to avoid parsing issues if (timeMinutes.Length > 5 || double.Parse(timeMinutes) > 30_000) { - waitForReady(() => Notifications, _ => Notifications.Post(new SimpleErrorNotification + waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification { + Icon = FontAwesome.Solid.ExclamationTriangle, Text = EditorStrings.TooLongTimestamp })); return; From 60f62faec3712e1086af4d0a7462c60d0efbc257 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sat, 4 Nov 2023 03:30:38 +0100 Subject: [PATCH 0039/1118] Renamed Editor method --- osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 5acd958568..a6bb6cc120 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -598,7 +598,7 @@ namespace osu.Game return; } - editor.SeekToTimestamp(timeGroup, objectsGroup); + editor.SeekAndSelectHitObjects(timeGroup, objectsGroup); } /// diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index b2fad55fed..80e01d4eb7 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1138,7 +1138,7 @@ namespace osu.Game.Screens.Edit loader?.CancelPendingDifficultySwitch(); } - public void SeekToTimestamp(string timeGroup, string objectsGroup) + public void SeekAndSelectHitObjects(string timeGroup, string objectsGroup) { double position = EditorTimestampParser.GetTotalMilliseconds(timeGroup); editorBeatmap.SelectedHitObjects.Clear(); From f867cff8c79c55f75708ea41c699c6d7a557485c Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sat, 4 Nov 2023 03:42:08 +0100 Subject: [PATCH 0040/1118] Added OpenEditorTimestamp Tests --- .../Editing/TestSceneOpenEditorTimestamp.cs | 377 ++++++++++++++++++ 1 file changed, 377 insertions(+) create mode 100644 osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs new file mode 100644 index 0000000000..5ae0a20fd2 --- /dev/null +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -0,0 +1,377 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Extensions; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Database; +using osu.Game.Localisation; +using osu.Game.Online.Chat; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu; +using osu.Game.Screens.Edit; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Select; +using osu.Game.Tests.Resources; + +namespace osu.Game.Tests.Visual.Editing +{ + public partial class TestSceneOpenEditorTimestamp : OsuGameTestScene + { + protected Editor Editor => (Editor)Game.ScreenStack.CurrentScreen; + protected EditorBeatmap EditorBeatmap => Editor.ChildrenOfType().Single(); + protected EditorClock EditorClock => Editor.ChildrenOfType().Single(); + + protected void AddStepClickLink(string timestamp, string step = "") + { + AddStep(step + timestamp, () => + Game.HandleLink(new LinkDetails(LinkAction.OpenEditorTimestamp, timestamp)) + ); + } + + protected void AddStepScreenModeTo(EditorScreenMode screenMode) + { + AddStep("change screen to " + screenMode, () => Editor.Mode.Value = screenMode); + } + + protected void AssertOnScreenAt(EditorScreenMode screen, double time, string text = "stays in") + { + AddAssert($"{text} {screen} at {time}", () => + Editor.Mode.Value == screen + && EditorClock.CurrentTime == time + ); + } + + protected bool HasCombosInOrder(IEnumerable selected, params int[] comboNumbers) + { + List hitObjects = selected.ToList(); + if (hitObjects.Count != comboNumbers.Length) + return false; + + return !hitObjects.Select(x => (IHasComboInformation)x) + .Where((combo, i) => combo.IndexInCurrentCombo + 1 != comboNumbers[i]) + .Any(); + } + + protected bool IsNoteAt(HitObject hitObject, double time, int column) + { + return hitObject is IHasColumn columnInfo + && hitObject.StartTime == time + && columnInfo.Column == column; + } + + public void SetUpEditor(RulesetInfo ruleset) + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("Import test beatmap", () => + Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely() + ); + AddStep("Retrieve beatmap", () => + beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach() + ); + AddStep("Present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("Wait for song select", () => + Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded + ); + AddStep("Switch ruleset", () => Game.Ruleset.Value = ruleset); + AddStep("Open editor for ruleset", () => + ((PlaySongSelect)Game.ScreenStack.CurrentScreen) + .Edit(beatmapSet.Beatmaps.Last(beatmap => beatmap.Ruleset.Name == ruleset.Name)) + ); + AddUntilStep("Wait for editor open", () => Editor.ReadyForUse); + } + + [Test] + public void TestErrorNotifications() + { + RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; + + AddStepClickLink("00:00:000"); + AddAssert("recieved 'must be in edit'", () => + Game.Notifications.UnreadCount.Value == 1 + && Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 1 + ); + + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); + AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + + AddStepClickLink("00:00:000 (1)"); + AddAssert("recieved 'must be in edit'", () => + Game.Notifications.UnreadCount.Value == 2 + && Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 2 + ); + + SetUpEditor(rulesetInfo); + AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + + AddStepClickLink("00:000", "invalid link "); + AddAssert("recieved 'failed to process'", () => + Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 1 + ); + + AddStepClickLink("00:00:00:000", "invalid link "); + AddAssert("recieved 'failed to process'", () => + Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 2 + ); + + AddStepClickLink("00:00:000 ()", "invalid link "); + AddAssert("recieved 'failed to process'", () => + Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 3 + ); + + AddStepClickLink("00:00:000 (-1)", "invalid link "); + AddAssert("recieved 'failed to process'", () => + Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 4 + ); + + AddStepClickLink("50000:00:000", "too long link "); + AddAssert("recieved 'too long'", () => + EditorClock.CurrentTime == 0 + && Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.TooLongTimestamp) == 1 + ); + } + + [Test] + public void TestHandleCurrentScreenChanges() + { + const long long_link_value = 1_000 * 60 * 1_000; + RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; + + SetUpEditor(rulesetInfo); + AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + + AddStepClickLink("1000:00:000", "long link "); + AddAssert("moved to end of track", () => + EditorClock.CurrentTime == long_link_value + || (EditorClock.TrackLength < long_link_value && EditorClock.CurrentTime == EditorClock.TrackLength) + ); + + AddStepScreenModeTo(EditorScreenMode.SongSetup); + AddStepClickLink("00:00:000"); + AssertOnScreenAt(EditorScreenMode.SongSetup, 0); + + AddStepClickLink("00:05:000 (0|0)"); + AddAssert("seek and change screen", () => + Editor.Mode.Value == EditorScreenMode.Compose + && EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 5_000).StartTime + ); + + AddStepScreenModeTo(EditorScreenMode.Design); + AddStepClickLink("00:10:000"); + AssertOnScreenAt(EditorScreenMode.Design, 10_000); + + AddStepClickLink("00:15:000 (1)"); + AddAssert("seek and change screen", () => + Editor.Mode.Value == EditorScreenMode.Compose + && EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 15_000).StartTime + ); + + AddStepScreenModeTo(EditorScreenMode.Timing); + AddStepClickLink("00:20:000"); + AssertOnScreenAt(EditorScreenMode.Timing, 20_000); + + AddStepClickLink("00:25:000 (0,1)"); + AddAssert("seek and change screen", () => + Editor.Mode.Value == EditorScreenMode.Compose + && EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 25_000).StartTime + ); + + AddStepScreenModeTo(EditorScreenMode.Verify); + AddStepClickLink("00:30:000"); + AssertOnScreenAt(EditorScreenMode.Verify, 30_000); + + AddStepClickLink("00:35:000 (0,1)"); + AddAssert("seek and change screen", () => + Editor.Mode.Value == EditorScreenMode.Compose + && EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 35_000).StartTime + ); + + AddStepClickLink("00:00:000"); + AssertOnScreenAt(EditorScreenMode.Compose, 0); + } + + [Test] + public void TestSelectionForOsu() + { + HitObject firstObject = null!; + RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; + + SetUpEditor(rulesetInfo); + AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + + AddStepClickLink("00:00:956 (1,2,3)"); + AddAssert("snap and select 1-2-3", () => + { + firstObject = EditorBeatmap.HitObjects.First(); + return EditorClock.CurrentTime == firstObject.StartTime + && EditorBeatmap.SelectedHitObjects.Count == 3 + && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1, 2, 3); + }); + + AddStepClickLink("00:01:450 (2,3,4,1,2)"); + AddAssert("snap and select 2-3-4-1-2", () => + EditorClock.CurrentTime == 1_450 + && EditorBeatmap.SelectedHitObjects.Count == 5 + && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 2, 3, 4, 1, 2) + ); + + AddStepClickLink("00:00:956 (1,1,1)"); + AddAssert("snap and select 1-1-1", () => + EditorClock.CurrentTime == firstObject.StartTime + && EditorBeatmap.SelectedHitObjects.Count == 3 + && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1, 1, 1) + ); + } + + [Test] + public void TestUnusualSelectionForOsu() + { + HitObject firstObject = null!; + RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; + + SetUpEditor(rulesetInfo); + AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + + AddStepClickLink("00:00:000 (1,2,3)", "invalid offset "); + AddAssert("snap to next, select 1-2-3", () => + { + firstObject = EditorBeatmap.HitObjects.First(); + return EditorClock.CurrentTime == firstObject.StartTime + && EditorBeatmap.SelectedHitObjects.Count == 3 + && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1, 2, 3); + }); + + AddStepClickLink("00:00:956 (2,3,4)", "invalid offset "); + AddAssert("snap to next, select 2-3-4", () => + EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 956).StartTime + && EditorBeatmap.SelectedHitObjects.Count == 3 + && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 2, 3, 4) + ); + + AddStepClickLink("00:00:000 (0)", "invalid combo "); + AddAssert("snap to 1, select 1", () => + EditorClock.CurrentTime == firstObject.StartTime + && EditorBeatmap.SelectedHitObjects.Count == 1 + && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1) + ); + + AddStepClickLink("00:00:000 (1)", "invalid offset "); + AddAssert("snap and select 1", () => + EditorClock.CurrentTime == firstObject.StartTime + && EditorBeatmap.SelectedHitObjects.Count == 1 + && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1) + ); + + AddStepClickLink("00:00:000 (2)", "invalid offset "); + AddAssert("snap and select 1", () => + EditorClock.CurrentTime == firstObject.StartTime + && EditorBeatmap.SelectedHitObjects.Count == 1 + && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1) + ); + + AddStepClickLink("00:00:000 (2,3)", "invalid offset "); + AddAssert("snap to 1, select 2-3", () => + EditorClock.CurrentTime == firstObject.StartTime + && EditorBeatmap.SelectedHitObjects.Count == 2 + && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 2, 3) + ); + + AddStepClickLink("00:00:956 (956|1,956|2)", "mania link "); + AddAssert("snap to next, select none", () => + EditorClock.CurrentTime == firstObject?.StartTime + && !EditorBeatmap.SelectedHitObjects.Any() + ); + + AddStepClickLink("00:00:000 (0|1)", "mania link "); + AddAssert("snap to 1, select none", () => + EditorClock.CurrentTime == firstObject.StartTime + && !EditorBeatmap.SelectedHitObjects.Any() + ); + } + + [Test] + public void TestSelectionForMania() + { + RulesetInfo rulesetInfo = new ManiaRuleset().RulesetInfo; + + SetUpEditor(rulesetInfo); + AddAssert("is editor Mania", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + + AddStepClickLink("00:11:010 (11010|1,11175|5,11258|3,11340|5,11505|1)"); + AddAssert("selected group", () => + EditorClock.CurrentTime == 11010 + && EditorBeatmap.SelectedHitObjects.Count == 5 + && EditorBeatmap.SelectedHitObjects.All(x => + IsNoteAt(x, 11010, 1) || IsNoteAt(x, 11175, 5) || + IsNoteAt(x, 11258, 3) || IsNoteAt(x, 11340, 5) || + IsNoteAt(x, 11505, 1)) + ); + + AddStepClickLink("00:00:956 (956|1,956|6,1285|3,1780|4)"); + AddAssert("selected ungrouped", () => + EditorClock.CurrentTime == 956 + && EditorBeatmap.SelectedHitObjects.Count == 4 + && EditorBeatmap.SelectedHitObjects.All(x => + IsNoteAt(x, 956, 1) || IsNoteAt(x, 956, 6) || + IsNoteAt(x, 1285, 3) || IsNoteAt(x, 1780, 4)) + ); + + AddStepClickLink("02:36:560 (156560|1,156560|4,156560|6)"); + AddAssert("selected in row", () => + EditorClock.CurrentTime == 156560 + && EditorBeatmap.SelectedHitObjects.Count == 3 + && EditorBeatmap.SelectedHitObjects.All(x => + IsNoteAt(x, 156560, 1) || IsNoteAt(x, 156560, 4) || + IsNoteAt(x, 156560, 6)) + ); + + AddStepClickLink("00:35:736 (35736|3,36395|3,36725|3,37384|3)"); + AddAssert("selected in column", () => + EditorClock.CurrentTime == 35736 + && EditorBeatmap.SelectedHitObjects.Count == 4 + && EditorBeatmap.SelectedHitObjects.All(x => + IsNoteAt(x, 35736, 3) || IsNoteAt(x, 36395, 3) || + IsNoteAt(x, 36725, 3) || IsNoteAt(x, 37384, 3)) + ); + } + + [Test] + public void TestUnusualSelectionForMania() + { + RulesetInfo rulesetInfo = new ManiaRuleset().RulesetInfo; + + SetUpEditor(rulesetInfo); + AddAssert("is editor Mania", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + + AddStepClickLink("00:00:000 (0|1)", "invalid link "); + AddAssert("snap to 1, select none", () => + EditorClock.CurrentTime == 956 + && !EditorBeatmap.SelectedHitObjects.Any() + ); + + AddStepClickLink("00:00:000 (0)", "std link "); + AddAssert("snap and select 1", () => + EditorClock.CurrentTime == 956 + && EditorBeatmap.SelectedHitObjects.All(x => IsNoteAt(x, 956, 1)) + ); + + AddStepClickLink("00:00:000 (1,2)", "std link "); + AddAssert("snap to 1, select none", () => + EditorClock.CurrentTime == 956 + && !EditorBeatmap.SelectedHitObjects.Any() + ); + } + } +} From 19cdf99df8f101b5c676528826a39cb905c1efe5 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sat, 4 Nov 2023 12:04:58 +0100 Subject: [PATCH 0041/1118] Fixed up tests --- .../Editing/TestSceneOpenEditorTimestamp.cs | 216 +++++++----------- 1 file changed, 83 insertions(+), 133 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index 5ae0a20fd2..fea9334ff8 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -33,7 +33,7 @@ namespace osu.Game.Tests.Visual.Editing protected void AddStepClickLink(string timestamp, string step = "") { - AddStep(step + timestamp, () => + AddStep($"{step} {timestamp}", () => Game.HandleLink(new LinkDetails(LinkAction.OpenEditorTimestamp, timestamp)) ); } @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("change screen to " + screenMode, () => Editor.Mode.Value = screenMode); } - protected void AssertOnScreenAt(EditorScreenMode screen, double time, string text = "stays in") + protected void AssertOnScreenAt(EditorScreenMode screen, double time, string text = "stayed in") { AddAssert($"{text} {screen} at {time}", () => Editor.Mode.Value == screen @@ -51,7 +51,34 @@ namespace osu.Game.Tests.Visual.Editing ); } - protected bool HasCombosInOrder(IEnumerable selected, params int[] comboNumbers) + protected void AssertMovedScreenTo(EditorScreenMode screen, string text = "moved to") + { + AddAssert($"{text} {screen}", () => Editor.Mode.Value == screen); + } + + private bool checkSnapAndSelectCombo(double startTime, params int[] comboNumbers) + { + bool checkCombos = comboNumbers.Any() + ? hasCombosInOrder(EditorBeatmap.SelectedHitObjects, comboNumbers) + : !EditorBeatmap.SelectedHitObjects.Any(); + + return EditorClock.CurrentTime == startTime + && EditorBeatmap.SelectedHitObjects.Count == comboNumbers.Length + && checkCombos; + } + + private bool checkSnapAndSelectColumn(double startTime, List<(int, int)> columnPairs = null) + { + bool checkColumns = columnPairs != null + ? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2))) + : !EditorBeatmap.SelectedHitObjects.Any(); + + return EditorClock.CurrentTime == startTime + && EditorBeatmap.SelectedHitObjects.Count == (columnPairs?.Count ?? 0) + && checkColumns; + } + + private bool hasCombosInOrder(IEnumerable selected, params int[] comboNumbers) { List hitObjects = selected.ToList(); if (hitObjects.Count != comboNumbers.Length) @@ -62,7 +89,7 @@ namespace osu.Game.Tests.Visual.Editing .Any(); } - protected bool IsNoteAt(HitObject hitObject, double time, int column) + private bool isNoteAt(HitObject hitObject, double time, int column) { return hitObject is IHasColumn columnInfo && hitObject.StartTime == time @@ -100,8 +127,7 @@ namespace osu.Game.Tests.Visual.Editing AddStepClickLink("00:00:000"); AddAssert("recieved 'must be in edit'", () => - Game.Notifications.UnreadCount.Value == 1 - && Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 1 + Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 1 ); AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); @@ -109,37 +135,35 @@ namespace osu.Game.Tests.Visual.Editing AddStepClickLink("00:00:000 (1)"); AddAssert("recieved 'must be in edit'", () => - Game.Notifications.UnreadCount.Value == 2 - && Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 2 + Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 2 ); SetUpEditor(rulesetInfo); AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - AddStepClickLink("00:000", "invalid link "); + AddStepClickLink("00:000", "invalid link"); AddAssert("recieved 'failed to process'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 1 ); - AddStepClickLink("00:00:00:000", "invalid link "); + AddStepClickLink("00:00:00:000", "invalid link"); AddAssert("recieved 'failed to process'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 2 ); - AddStepClickLink("00:00:000 ()", "invalid link "); + AddStepClickLink("00:00:000 ()", "invalid link"); AddAssert("recieved 'failed to process'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 3 ); - AddStepClickLink("00:00:000 (-1)", "invalid link "); + AddStepClickLink("00:00:000 (-1)", "invalid link"); AddAssert("recieved 'failed to process'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 4 ); - AddStepClickLink("50000:00:000", "too long link "); + AddStepClickLink("50000:00:000", "too long link"); AddAssert("recieved 'too long'", () => - EditorClock.CurrentTime == 0 - && Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.TooLongTimestamp) == 1 + Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.TooLongTimestamp) == 1 ); } @@ -152,7 +176,7 @@ namespace osu.Game.Tests.Visual.Editing SetUpEditor(rulesetInfo); AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - AddStepClickLink("1000:00:000", "long link "); + AddStepClickLink("1000:00:000", "long link"); AddAssert("moved to end of track", () => EditorClock.CurrentTime == long_link_value || (EditorClock.TrackLength < long_link_value && EditorClock.CurrentTime == EditorClock.TrackLength) @@ -163,40 +187,28 @@ namespace osu.Game.Tests.Visual.Editing AssertOnScreenAt(EditorScreenMode.SongSetup, 0); AddStepClickLink("00:05:000 (0|0)"); - AddAssert("seek and change screen", () => - Editor.Mode.Value == EditorScreenMode.Compose - && EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 5_000).StartTime - ); + AssertMovedScreenTo(EditorScreenMode.Compose); AddStepScreenModeTo(EditorScreenMode.Design); AddStepClickLink("00:10:000"); AssertOnScreenAt(EditorScreenMode.Design, 10_000); AddStepClickLink("00:15:000 (1)"); - AddAssert("seek and change screen", () => - Editor.Mode.Value == EditorScreenMode.Compose - && EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 15_000).StartTime - ); + AssertMovedScreenTo(EditorScreenMode.Compose); AddStepScreenModeTo(EditorScreenMode.Timing); AddStepClickLink("00:20:000"); AssertOnScreenAt(EditorScreenMode.Timing, 20_000); AddStepClickLink("00:25:000 (0,1)"); - AddAssert("seek and change screen", () => - Editor.Mode.Value == EditorScreenMode.Compose - && EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 25_000).StartTime - ); + AssertMovedScreenTo(EditorScreenMode.Compose); AddStepScreenModeTo(EditorScreenMode.Verify); AddStepClickLink("00:30:000"); AssertOnScreenAt(EditorScreenMode.Verify, 30_000); AddStepClickLink("00:35:000 (0,1)"); - AddAssert("seek and change screen", () => - Editor.Mode.Value == EditorScreenMode.Compose - && EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 35_000).StartTime - ); + AssertMovedScreenTo(EditorScreenMode.Compose); AddStepClickLink("00:00:000"); AssertOnScreenAt(EditorScreenMode.Compose, 0); @@ -215,24 +227,14 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("snap and select 1-2-3", () => { firstObject = EditorBeatmap.HitObjects.First(); - return EditorClock.CurrentTime == firstObject.StartTime - && EditorBeatmap.SelectedHitObjects.Count == 3 - && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1, 2, 3); + return checkSnapAndSelectCombo(firstObject.StartTime, 1, 2, 3); }); AddStepClickLink("00:01:450 (2,3,4,1,2)"); - AddAssert("snap and select 2-3-4-1-2", () => - EditorClock.CurrentTime == 1_450 - && EditorBeatmap.SelectedHitObjects.Count == 5 - && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 2, 3, 4, 1, 2) - ); + AddAssert("snap and select 2-3-4-1-2", () => checkSnapAndSelectCombo(1_450, 2, 3, 4, 1, 2)); AddStepClickLink("00:00:956 (1,1,1)"); - AddAssert("snap and select 1-1-1", () => - EditorClock.CurrentTime == firstObject.StartTime - && EditorBeatmap.SelectedHitObjects.Count == 3 - && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1, 1, 1) - ); + AddAssert("snap and select 1-1-1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1, 1, 1)); } [Test] @@ -244,61 +246,33 @@ namespace osu.Game.Tests.Visual.Editing SetUpEditor(rulesetInfo); AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - AddStepClickLink("00:00:000 (1,2,3)", "invalid offset "); + AddStepClickLink("00:00:000 (1,2,3)", "invalid offset"); AddAssert("snap to next, select 1-2-3", () => { firstObject = EditorBeatmap.HitObjects.First(); - return EditorClock.CurrentTime == firstObject.StartTime - && EditorBeatmap.SelectedHitObjects.Count == 3 - && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1, 2, 3); + return checkSnapAndSelectCombo(firstObject.StartTime, 1, 2, 3); }); - AddStepClickLink("00:00:956 (2,3,4)", "invalid offset "); - AddAssert("snap to next, select 2-3-4", () => - EditorClock.CurrentTime == EditorBeatmap.HitObjects.First(x => x.StartTime >= 956).StartTime - && EditorBeatmap.SelectedHitObjects.Count == 3 - && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 2, 3, 4) - ); + AddStepClickLink("00:00:956 (2,3,4)", "invalid offset"); + AddAssert("snap to next, select 2-3-4", () => checkSnapAndSelectCombo(firstObject.StartTime, 2, 3, 4)); - AddStepClickLink("00:00:000 (0)", "invalid combo "); - AddAssert("snap to 1, select 1", () => - EditorClock.CurrentTime == firstObject.StartTime - && EditorBeatmap.SelectedHitObjects.Count == 1 - && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1) - ); + AddStepClickLink("00:00:000 (0)", "invalid offset"); + AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); - AddStepClickLink("00:00:000 (1)", "invalid offset "); - AddAssert("snap and select 1", () => - EditorClock.CurrentTime == firstObject.StartTime - && EditorBeatmap.SelectedHitObjects.Count == 1 - && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1) - ); + AddStepClickLink("00:00:000 (1)", "invalid offset"); + AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); - AddStepClickLink("00:00:000 (2)", "invalid offset "); - AddAssert("snap and select 1", () => - EditorClock.CurrentTime == firstObject.StartTime - && EditorBeatmap.SelectedHitObjects.Count == 1 - && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 1) - ); + AddStepClickLink("00:00:000 (2)", "invalid offset"); + AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); - AddStepClickLink("00:00:000 (2,3)", "invalid offset "); - AddAssert("snap to 1, select 2-3", () => - EditorClock.CurrentTime == firstObject.StartTime - && EditorBeatmap.SelectedHitObjects.Count == 2 - && HasCombosInOrder(EditorBeatmap.SelectedHitObjects, 2, 3) - ); + AddStepClickLink("00:00:000 (2,3)", "invalid offset"); + AddAssert("snap to 1, select 2-3", () => checkSnapAndSelectCombo(firstObject.StartTime, 2, 3)); - AddStepClickLink("00:00:956 (956|1,956|2)", "mania link "); - AddAssert("snap to next, select none", () => - EditorClock.CurrentTime == firstObject?.StartTime - && !EditorBeatmap.SelectedHitObjects.Any() - ); + AddStepClickLink("00:00:956 (956|1,956|2)", "mania link"); + AddAssert("snap to next, select none", () => checkSnapAndSelectCombo(firstObject.StartTime)); - AddStepClickLink("00:00:000 (0|1)", "mania link "); - AddAssert("snap to 1, select none", () => - EditorClock.CurrentTime == firstObject.StartTime - && !EditorBeatmap.SelectedHitObjects.Any() - ); + AddStepClickLink("00:00:000 (0|1)", "mania link"); + AddAssert("snap to 1, select none", () => checkSnapAndSelectCombo(firstObject.StartTime)); } [Test] @@ -310,41 +284,24 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("is editor Mania", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); AddStepClickLink("00:11:010 (11010|1,11175|5,11258|3,11340|5,11505|1)"); - AddAssert("selected group", () => - EditorClock.CurrentTime == 11010 - && EditorBeatmap.SelectedHitObjects.Count == 5 - && EditorBeatmap.SelectedHitObjects.All(x => - IsNoteAt(x, 11010, 1) || IsNoteAt(x, 11175, 5) || - IsNoteAt(x, 11258, 3) || IsNoteAt(x, 11340, 5) || - IsNoteAt(x, 11505, 1)) - ); + AddAssert("selected group", () => checkSnapAndSelectColumn(11010, new List<(int, int)> + { (11010, 1), (11175, 5), (11258, 3), (11340, 5), (11505, 1) } + )); AddStepClickLink("00:00:956 (956|1,956|6,1285|3,1780|4)"); - AddAssert("selected ungrouped", () => - EditorClock.CurrentTime == 956 - && EditorBeatmap.SelectedHitObjects.Count == 4 - && EditorBeatmap.SelectedHitObjects.All(x => - IsNoteAt(x, 956, 1) || IsNoteAt(x, 956, 6) || - IsNoteAt(x, 1285, 3) || IsNoteAt(x, 1780, 4)) - ); + AddAssert("selected ungrouped", () => checkSnapAndSelectColumn(956, new List<(int, int)> + { (956, 1), (956, 6), (1285, 3), (1780, 4) } + )); AddStepClickLink("02:36:560 (156560|1,156560|4,156560|6)"); - AddAssert("selected in row", () => - EditorClock.CurrentTime == 156560 - && EditorBeatmap.SelectedHitObjects.Count == 3 - && EditorBeatmap.SelectedHitObjects.All(x => - IsNoteAt(x, 156560, 1) || IsNoteAt(x, 156560, 4) || - IsNoteAt(x, 156560, 6)) - ); + AddAssert("selected in row", () => checkSnapAndSelectColumn(156560, new List<(int, int)> + { (156560, 1), (156560, 4), (156560, 6) } + )); AddStepClickLink("00:35:736 (35736|3,36395|3,36725|3,37384|3)"); - AddAssert("selected in column", () => - EditorClock.CurrentTime == 35736 - && EditorBeatmap.SelectedHitObjects.Count == 4 - && EditorBeatmap.SelectedHitObjects.All(x => - IsNoteAt(x, 35736, 3) || IsNoteAt(x, 36395, 3) || - IsNoteAt(x, 36725, 3) || IsNoteAt(x, 37384, 3)) - ); + AddAssert("selected in column", () => checkSnapAndSelectColumn(35736, new List<(int, int)> + { (35736, 3), (36395, 3), (36725, 3), (37384, 3) } + )); } [Test] @@ -355,23 +312,16 @@ namespace osu.Game.Tests.Visual.Editing SetUpEditor(rulesetInfo); AddAssert("is editor Mania", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - AddStepClickLink("00:00:000 (0|1)", "invalid link "); - AddAssert("snap to 1, select none", () => - EditorClock.CurrentTime == 956 - && !EditorBeatmap.SelectedHitObjects.Any() + AddStepClickLink("00:00:000 (0|1)", "invalid link"); + AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(956)); + + AddStepClickLink("00:00:000 (0)", "std link"); + AddAssert("snap and select 1", () => checkSnapAndSelectColumn(956, new List<(int, int)> + { (956, 1) }) ); - AddStepClickLink("00:00:000 (0)", "std link "); - AddAssert("snap and select 1", () => - EditorClock.CurrentTime == 956 - && EditorBeatmap.SelectedHitObjects.All(x => IsNoteAt(x, 956, 1)) - ); - - AddStepClickLink("00:00:000 (1,2)", "std link "); - AddAssert("snap to 1, select none", () => - EditorClock.CurrentTime == 956 - && !EditorBeatmap.SelectedHitObjects.Any() - ); + AddStepClickLink("00:00:000 (1,2)", "std link"); + AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(956)); } } } From 57170501cdadfa786d8d59bd67efa4f45a9ce81a Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 4 Nov 2023 17:25:09 +0200 Subject: [PATCH 0042/1118] Improve code quality --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 31 +++------------- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 30 +++------------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 33 +++-------------- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 30 +++------------- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 20 ++++++----- .../Overlays/Mods/VerticalAttributeDisplay.cs | 33 ++++++++++++----- osu.Game/Rulesets/Ruleset.cs | 17 +-------- .../Screens/Select/Details/AdvancedStats.cs | 35 ++++++++++++------- 8 files changed, 76 insertions(+), 153 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index f0b20c68c4..2d0e34431d 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -238,37 +238,14 @@ namespace osu.Game.Rulesets.Catch public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); - public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) + public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) { - BeatmapDifficulty? adjustedDifficulty = null; - rateAdjustedInfo = (RateAdjustType.NotChanged, RateAdjustType.NotChanged); + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - if (mods.Any(m => m is IApplicableToDifficulty)) - { - adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - - foreach (var mod in mods.OfType()) - mod.ApplyToDifficulty(adjustedDifficulty); - } - - if (mods.Any(m => m is ModRateAdjust)) - { - adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); - - foreach (var mod in mods.OfType()) - { - double speedChange = (float)mod.SpeedChange.Value; - - float ar = adjustedDifficulty.ApproachRate; - float od = adjustedDifficulty.OverallDifficulty; - - adjustedDifficulty.ApproachRate = ChangeArFromRate(ar, speedChange); - - rateAdjustedInfo.AR = GetRateAdjustType(ar, adjustedDifficulty.ApproachRate); - } - } + adjustedDifficulty.ApproachRate = ChangeArFromRate(adjustedDifficulty.ApproachRate, rate); return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; } + } } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index a8a54ebf05..cfdfcbadee 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -431,34 +431,12 @@ namespace osu.Game.Rulesets.Mania public double HitwindowFromOd(float OD) => 64.0 - 3 * OD; public float OdFromHitwindow(double hitwindow300) => (float)(64.0 - hitwindow300) / 3; public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); - public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) + + public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) { - BeatmapDifficulty? adjustedDifficulty = null; - rateAdjustedInfo = (RateAdjustType.NotChanged, RateAdjustType.NotChanged); + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - if (mods.Any(m => m is IApplicableToDifficulty)) - { - adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - - foreach (var mod in mods.OfType()) - mod.ApplyToDifficulty(adjustedDifficulty); - } - - if (mods.Any(m => m is ModRateAdjust)) - { - adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); - - foreach (var mod in mods.OfType()) - { - double speedChange = (float)mod.SpeedChange.Value; - - float od = adjustedDifficulty.OverallDifficulty; - - adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(od, speedChange); - - rateAdjustedInfo.OD = GetRateAdjustType(od, adjustedDifficulty.OverallDifficulty); - } - } + adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(adjustedDifficulty.OverallDifficulty, rate); return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 4810be5db2..3a8f589c9f 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -335,37 +335,12 @@ namespace osu.Game.Rulesets.Osu public float OdFromHitwindow(double hitwindow300) => (float)(80.0 - hitwindow300) / 6; public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); - public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) + public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) { - BeatmapDifficulty? adjustedDifficulty = null; - rateAdjustedInfo = (RateAdjustType.NotChanged, RateAdjustType.NotChanged); + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - if (mods.Any(m => m is IApplicableToDifficulty)) - { - adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - - foreach (var mod in mods.OfType()) - mod.ApplyToDifficulty(adjustedDifficulty); - } - - if (mods.Any(m => m is ModRateAdjust)) - { - adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); - - foreach (var mod in mods.OfType()) - { - double speedChange = (float)mod.SpeedChange.Value; - - float ar = adjustedDifficulty.ApproachRate; - float od = adjustedDifficulty.OverallDifficulty; - - adjustedDifficulty.ApproachRate = ChangeArFromRate(ar, speedChange); - adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(od, speedChange); - - rateAdjustedInfo.AR = GetRateAdjustType(ar, adjustedDifficulty.ApproachRate); - rateAdjustedInfo.OD = GetRateAdjustType(od, adjustedDifficulty.OverallDifficulty); - } - } + adjustedDifficulty.ApproachRate = ChangeArFromRate(adjustedDifficulty.ApproachRate, rate); + adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(adjustedDifficulty.OverallDifficulty, rate); return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index a9abbea251..026ac4f42e 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -266,34 +266,12 @@ namespace osu.Game.Rulesets.Taiko public double HitwindowFromOd(float OD) => 35.0 - 15.0 * (OD - 5) / 5; public float OdFromHitwindow(double hitwindow300) => (float)(5 * (35 - hitwindow300) / 15 + 5); public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); - public override BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) + + public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) { - BeatmapDifficulty? adjustedDifficulty = null; - rateAdjustedInfo = (RateAdjustType.NotChanged, RateAdjustType.NotChanged); + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - if (mods.Any(m => m is IApplicableToDifficulty)) - { - adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - - foreach (var mod in mods.OfType()) - mod.ApplyToDifficulty(adjustedDifficulty); - } - - if (mods.Any(m => m is ModRateAdjust)) - { - adjustedDifficulty ??= new BeatmapDifficulty(baseDifficulty); - - foreach (var mod in mods.OfType()) - { - double speedChange = (float)mod.SpeedChange.Value; - - float od = adjustedDifficulty.OverallDifficulty; - - adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(od, speedChange); - - rateAdjustedInfo.OD = GetRateAdjustType(od, adjustedDifficulty.OverallDifficulty); - } - } + adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(adjustedDifficulty.OverallDifficulty, rate); return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; } diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index d0ee9750b2..fd9ce8f5d9 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -251,17 +251,21 @@ namespace osu.Game.Overlays.Mods bpmDisplay.Current.Value = BeatmapInfo.Value.BPM * rate; - (Ruleset.RateAdjustType AR, Ruleset.RateAdjustType OD) rateAdjustType = (Ruleset.RateAdjustType.NotChanged, Ruleset.RateAdjustType.NotChanged); + var moddedDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); + + foreach (var mod in mods.Value.OfType()) + mod.ApplyToDifficulty(moddedDifficulty); Ruleset ruleset = gameRuleset.Value.CreateInstance(); - BeatmapDifficulty adjustedDifficulty = ruleset.GetEffectiveDifficulty(BeatmapInfo.Value.Difficulty, mods.Value, ref rateAdjustType); - approachRateDisplay.RateChangeType.Value = rateAdjustType.AR; - overallDifficultyDisplay.RateChangeType.Value = rateAdjustType.OD; + var rateAdjustedDifficulty = ruleset.GetRateAdjustedDifficulty(moddedDifficulty, rate); - circleSizeDisplay.Current.Value = adjustedDifficulty.CircleSize; - drainRateDisplay.Current.Value = adjustedDifficulty.DrainRate; - approachRateDisplay.Current.Value = adjustedDifficulty.ApproachRate; - overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty; + approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(moddedDifficulty.ApproachRate, rateAdjustedDifficulty.ApproachRate); + overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(moddedDifficulty.OverallDifficulty, rateAdjustedDifficulty.OverallDifficulty); + + circleSizeDisplay.Current.Value = rateAdjustedDifficulty.CircleSize; + drainRateDisplay.Current.Value = rateAdjustedDifficulty.DrainRate; + approachRateDisplay.Current.Value = rateAdjustedDifficulty.ApproachRate; + overallDifficultyDisplay.Current.Value = rateAdjustedDifficulty.OverallDifficulty; }); private void updateCollapsedState() diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs index 16d75cd448..0b0c49bc1b 100644 --- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -12,7 +12,6 @@ using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; -using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osuTK.Graphics; @@ -28,7 +27,7 @@ namespace osu.Game.Overlays.Mods private readonly BindableWithCurrent current = new BindableWithCurrent(); - public Bindable RateChangeType = new Bindable(Ruleset.RateAdjustType.NotChanged); + public Bindable AdjustType = new Bindable(); /// /// Text to display in the top area of the display. @@ -43,22 +42,22 @@ namespace osu.Game.Overlays.Mods private void updateTextColor() { Color4 newColor; - switch (RateChangeType.Value) + switch (AdjustType.Value) { - case Ruleset.RateAdjustType.NotChanged: + case ModEffect.NotChanged: newColor = Color4.White; break; - case Ruleset.RateAdjustType.DifficultyReduction: + case ModEffect.DifficultyReduction: newColor = colours.ForModType(ModType.DifficultyReduction); break; - case Ruleset.RateAdjustType.DifficultyIncrease: + case ModEffect.DifficultyIncrease: newColor = colours.ForModType(ModType.DifficultyIncrease); break; default: - throw new ArgumentOutOfRangeException(nameof(RateChangeType.Value)); + throw new ArgumentOutOfRangeException(nameof(AdjustType.Value)); } text.Colour = newColor; @@ -74,7 +73,7 @@ namespace osu.Game.Overlays.Mods Origin = Anchor.CentreLeft; Anchor = Anchor.CentreLeft; - RateChangeType.BindValueChanged(_ => updateTextColor()); + AdjustType.BindValueChanged(_ => updateTextColor()); InternalChild = new FillFlowContainer { @@ -101,6 +100,24 @@ namespace osu.Game.Overlays.Mods } }; } + + public static ModEffect CalculateEffect(double oldValue, double newValue) + { + if (newValue == oldValue) + return ModEffect.NotChanged; + if (newValue < oldValue) + return ModEffect.DifficultyReduction; + + return ModEffect.DifficultyIncrease; + } + + public enum ModEffect + { + NotChanged, + DifficultyReduction, + DifficultyIncrease, + } + private partial class EffectCounter : RollingCounter { protected override double RollingDuration => 500; diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 7a09bff4aa..fbeec5ff4b 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -389,21 +389,6 @@ namespace osu.Game.Rulesets /// Can be overridden to alter the difficulty section to the editor beatmap setup screen. /// public virtual DifficultySection? CreateEditorDifficultySection() => null; - public virtual BeatmapDifficulty GetEffectiveDifficulty(IBeatmapDifficultyInfo baseDifficulty, IReadOnlyList mods, ref (RateAdjustType AR, RateAdjustType OD) rateAdjustedInfo) => (BeatmapDifficulty)baseDifficulty; - public enum RateAdjustType - { - NotChanged, - DifficultyReduction, - DifficultyIncrease - } - - protected RateAdjustType GetRateAdjustType(float baseValue, float adjustedValue) - { - if (adjustedValue > baseValue) - return RateAdjustType.DifficultyIncrease; - if (adjustedValue < baseValue) - return RateAdjustType.DifficultyReduction; - return RateAdjustType.NotChanged; - } + public virtual BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) => new BeatmapDifficulty(baseDifficulty); } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 867d86a69b..d143e12667 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -24,6 +24,7 @@ using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; +using System.Linq; namespace osu.Game.Screens.Select.Details { @@ -116,12 +117,21 @@ namespace osu.Game.Screens.Select.Details { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; BeatmapDifficulty adjustedDifficulty = null; - (Ruleset.RateAdjustType AR, Ruleset.RateAdjustType OD) rateAdjustInfo = (Ruleset.RateAdjustType.NotChanged, Ruleset.RateAdjustType.NotChanged); if (baseDifficulty != null) { - Ruleset ruleset = gameRuleset.Value.CreateInstance(); - adjustedDifficulty = ruleset.GetEffectiveDifficulty(baseDifficulty, mods.Value, ref rateAdjustInfo); + adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + + foreach (var mod in mods.Value.OfType()) + mod.ApplyToDifficulty(adjustedDifficulty); + + // For now we not using rate adjusted difficulty here + + //Ruleset ruleset = gameRuleset.Value.CreateInstance(); + //double rate = 1; + //foreach (var mod in mods.Value.OfType()) + // rate = mod.ApplyToRate(0, rate); + //adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(adjustedDifficulty, rate); } switch (BeatmapInfo?.Ruleset.OnlineID) @@ -130,18 +140,18 @@ namespace osu.Game.Screens.Select.Details // Account for mania differences locally for now // Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania; - FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, null, false); + FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, null); break; default: FirstValue.Title = BeatmapsetsStrings.ShowStatsCs; - FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize, false); + FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, adjustedDifficulty?.CircleSize); break; } - HpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate, false); - Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty, rateAdjustInfo.OD != Ruleset.RateAdjustType.NotChanged); - ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate, rateAdjustInfo.OD != Ruleset.RateAdjustType.NotChanged); + HpDrain.Value = (baseDifficulty?.DrainRate ?? 0, adjustedDifficulty?.DrainRate); + Accuracy.Value = (baseDifficulty?.OverallDifficulty ?? 0, adjustedDifficulty?.OverallDifficulty); + ApproachRate.Value = (baseDifficulty?.ApproachRate ?? 0, adjustedDifficulty?.ApproachRate); updateStarDifficulty(); } @@ -175,7 +185,7 @@ namespace osu.Game.Screens.Select.Details if (normalDifficulty == null || moddedDifficulty == null) return; - starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars, false); + starDifficulty.Value = ((float)normalDifficulty.Value.Stars, (float)moddedDifficulty.Value.Stars); }), starDifficultyCancellationSource.Token, TaskContinuationOptions.OnlyOnRanToCompletion, TaskScheduler.Current); }); @@ -206,11 +216,11 @@ namespace osu.Game.Screens.Select.Details set => name.Text = value; } - private (float baseValue, float? adjustedValue, bool isRateAdjusted)? value; + private (float baseValue, float? adjustedValue)? value; - public (float baseValue, float? adjustedValue, bool isRateAdjusted) Value + public (float baseValue, float? adjustedValue) Value { - get => value ?? (0, null, false); + get => value ?? (0, null); set { if (value == this.value) @@ -221,7 +231,6 @@ namespace osu.Game.Screens.Select.Details bar.Length = value.baseValue / maxValue; valueText.Text = (value.adjustedValue ?? value.baseValue).ToString(forceDecimalPlaces ? "0.00" : "0.##"); - if (value.isRateAdjusted) valueText.Text += "*"; ModBar.Length = (value.adjustedValue ?? 0) / maxValue; if (Precision.AlmostEquals(value.baseValue, value.adjustedValue ?? value.baseValue, 0.05f)) From 440d57fb48ec4fc029452ceda9f54aad381fd32a Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 4 Nov 2023 17:47:02 +0200 Subject: [PATCH 0043/1118] Basic rate-adjust tooltip --- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index fd9ce8f5d9..fc0ef989e6 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Colour; +using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; using osu.Game.Beatmaps; @@ -23,6 +24,7 @@ using osuTK.Graphics; using System.Threading; using osu.Framework.Input.Events; using osu.Game.Configuration; +using osu.Game.Rulesets.Difficulty; namespace osu.Game.Overlays.Mods { @@ -30,7 +32,7 @@ namespace osu.Game.Overlays.Mods /// On the mod select overlay, this provides a local updating view of BPM, star rating and other /// difficulty attributes so the user can have a better insight into what mods are changing. /// - public partial class BeatmapAttributesDisplay : CompositeDrawable + public partial class BeatmapAttributesDisplay : CompositeDrawable, IHasTooltip { private Container content = null!; private Container innerContent = null!; @@ -71,6 +73,10 @@ namespace osu.Game.Overlays.Mods private CancellationTokenSource? cancellationSource; private IBindable starDifficulty = null!; + private BeatmapDifficulty? baseDifficultyAttributes = null; + + private bool haveRateChangedValues = false; + [BackgroundDependencyLoader] private void load() { @@ -256,9 +262,13 @@ namespace osu.Game.Overlays.Mods foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(moddedDifficulty); + baseDifficultyAttributes = moddedDifficulty; + Ruleset ruleset = gameRuleset.Value.CreateInstance(); var rateAdjustedDifficulty = ruleset.GetRateAdjustedDifficulty(moddedDifficulty, rate); + haveRateChangedValues = !haveEqualDifficulties(rateAdjustedDifficulty, moddedDifficulty); + approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(moddedDifficulty.ApproachRate, rateAdjustedDifficulty.ApproachRate); overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(moddedDifficulty.OverallDifficulty, rateAdjustedDifficulty.OverallDifficulty); @@ -268,6 +278,19 @@ namespace osu.Game.Overlays.Mods overallDifficultyDisplay.Current.Value = rateAdjustedDifficulty.OverallDifficulty; }); + private bool haveEqualDifficulties(BeatmapDifficulty? a, BeatmapDifficulty? b) + { + if (a == null && b == null) return true; + if (a == null || b == null) return false; + + if (a.ApproachRate != b.ApproachRate) return false; + if (a.OverallDifficulty != b.OverallDifficulty) return false; + if (a.DrainRate != b.DrainRate) return false; + if (a.CircleSize != b.CircleSize) return false; + + return true; + } + private void updateCollapsedState() { outerContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint); @@ -285,5 +308,17 @@ namespace osu.Game.Overlays.Mods UseFullGlyphHeight = false, }; } + + public LocalisableString TooltipText + { + get + { + if (haveRateChangedValues) + { + return "Some of the values are Rate-Adjusted."; + } + return ""; + } + } } } From 5597e819be1e400fb481f316b1b6fc47c76b94d7 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 4 Nov 2023 18:01:10 +0200 Subject: [PATCH 0044/1118] fixed bug in AR formula --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 +- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 2d0e34431d..da84d12f59 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -235,7 +235,7 @@ namespace osu.Game.Rulesets.Catch } public double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); - public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; + public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 3a8f589c9f..bdbedf1409 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -330,7 +330,7 @@ namespace osu.Game.Rulesets.Osu public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection(); public double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); - public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150)) + 5; + public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); public double HitwindowFromOd(float OD) => 80.0 - 6 * OD; public float OdFromHitwindow(double hitwindow300) => (float)(80.0 - hitwindow300) / 6; public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); From 820519c37dcfa271235e75cdc2f9ad10d2f6f91e Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 4 Nov 2023 21:55:46 +0200 Subject: [PATCH 0045/1118] improved tooltip --- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 44 +++++++++---------- .../Overlays/Mods/VerticalAttributeDisplay.cs | 3 +- .../Screens/Select/Details/AdvancedStats.cs | 44 ++++++++++++++++--- 3 files changed, 60 insertions(+), 31 deletions(-) diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index fc0ef989e6..c921e3e075 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Threading; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -11,9 +12,12 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -21,10 +25,6 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osuTK; using osuTK.Graphics; -using System.Threading; -using osu.Framework.Input.Events; -using osu.Game.Configuration; -using osu.Game.Rulesets.Difficulty; namespace osu.Game.Overlays.Mods { @@ -73,8 +73,7 @@ namespace osu.Game.Overlays.Mods private CancellationTokenSource? cancellationSource; private IBindable starDifficulty = null!; - private BeatmapDifficulty? baseDifficultyAttributes = null; - + private BeatmapDifficulty? originalDifficulty = null; private bool haveRateChangedValues = false; [BackgroundDependencyLoader] @@ -257,36 +256,34 @@ namespace osu.Game.Overlays.Mods bpmDisplay.Current.Value = BeatmapInfo.Value.BPM * rate; - var moddedDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); + var adjustedDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); foreach (var mod in mods.Value.OfType()) - mod.ApplyToDifficulty(moddedDifficulty); + mod.ApplyToDifficulty(adjustedDifficulty); - baseDifficultyAttributes = moddedDifficulty; + originalDifficulty = adjustedDifficulty; Ruleset ruleset = gameRuleset.Value.CreateInstance(); - var rateAdjustedDifficulty = ruleset.GetRateAdjustedDifficulty(moddedDifficulty, rate); + adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(adjustedDifficulty, rate); - haveRateChangedValues = !haveEqualDifficulties(rateAdjustedDifficulty, moddedDifficulty); + haveRateChangedValues = !isDifferentArOd(originalDifficulty, adjustedDifficulty); - approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(moddedDifficulty.ApproachRate, rateAdjustedDifficulty.ApproachRate); - overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(moddedDifficulty.OverallDifficulty, rateAdjustedDifficulty.OverallDifficulty); + approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); + overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); - circleSizeDisplay.Current.Value = rateAdjustedDifficulty.CircleSize; - drainRateDisplay.Current.Value = rateAdjustedDifficulty.DrainRate; - approachRateDisplay.Current.Value = rateAdjustedDifficulty.ApproachRate; - overallDifficultyDisplay.Current.Value = rateAdjustedDifficulty.OverallDifficulty; + circleSizeDisplay.Current.Value = adjustedDifficulty.CircleSize; + drainRateDisplay.Current.Value = adjustedDifficulty.DrainRate; + approachRateDisplay.Current.Value = adjustedDifficulty.ApproachRate; + overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty; }); - private bool haveEqualDifficulties(BeatmapDifficulty? a, BeatmapDifficulty? b) + private bool isDifferentArOd(BeatmapDifficulty? a, BeatmapDifficulty? b) { if (a == null && b == null) return true; if (a == null || b == null) return false; - if (a.ApproachRate != b.ApproachRate) return false; - if (a.OverallDifficulty != b.OverallDifficulty) return false; - if (a.DrainRate != b.DrainRate) return false; - if (a.CircleSize != b.CircleSize) return false; + if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate, 0.01)) return false; + if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty, 0.01)) return false; return true; } @@ -315,7 +312,8 @@ namespace osu.Game.Overlays.Mods { if (haveRateChangedValues) { - return "Some of the values are Rate-Adjusted."; + return LocalisableString.Format("Values are changed by mods that change speed.\n" + + "Original values: AR = {0}, OD = {1}", originalDifficulty?.ApproachRate ?? 0, originalDifficulty?.OverallDifficulty ?? 0); } return ""; } diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs index 0b0c49bc1b..6ac7ebf159 100644 --- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -103,7 +104,7 @@ namespace osu.Game.Overlays.Mods public static ModEffect CalculateEffect(double oldValue, double newValue) { - if (newValue == oldValue) + if (Precision.AlmostEquals(newValue, oldValue, 0.01)) return ModEffect.NotChanged; if (newValue < oldValue) return ModEffect.DifficultyReduction; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index d143e12667..f9c6e95154 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -8,6 +8,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -28,7 +29,7 @@ using System.Linq; namespace osu.Game.Screens.Select.Details { - public partial class AdvancedStats : Container + public partial class AdvancedStats : Container, IHasTooltip { [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -44,6 +45,9 @@ namespace osu.Game.Screens.Select.Details protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; + private BeatmapDifficulty originalDifficulty = null; + private bool haveRateChangedValues = false; + private IBeatmapInfo beatmapInfo; public IBeatmapInfo BeatmapInfo @@ -125,13 +129,15 @@ namespace osu.Game.Screens.Select.Details foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(adjustedDifficulty); - // For now we not using rate adjusted difficulty here + originalDifficulty = adjustedDifficulty; - //Ruleset ruleset = gameRuleset.Value.CreateInstance(); - //double rate = 1; - //foreach (var mod in mods.Value.OfType()) - // rate = mod.ApplyToRate(0, rate); - //adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(adjustedDifficulty, rate); + Ruleset ruleset = gameRuleset.Value.CreateInstance(); + double rate = 1; + foreach (var mod in mods.Value.OfType()) + rate = mod.ApplyToRate(0, rate); + adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(adjustedDifficulty, rate); + + haveRateChangedValues = !isDifferentArOd(originalDifficulty, adjustedDifficulty); } switch (BeatmapInfo?.Ruleset.OnlineID) @@ -196,6 +202,30 @@ namespace osu.Game.Screens.Select.Details starDifficultyCancellationSource?.Cancel(); } + private bool isDifferentArOd(BeatmapDifficulty a, BeatmapDifficulty b) + { + if (a == null && b == null) return true; + if (a == null || b == null) return false; + + if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate, 0.01)) return false; + if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty, 0.01)) return false; + + return true; + } + + public LocalisableString TooltipText + { + get + { + if (haveRateChangedValues) + { + return LocalisableString.Format("Values are changed by mods that change speed.\n" + + "Original values: AR = {0}, OD = {1}", originalDifficulty?.ApproachRate ?? 0, originalDifficulty?.OverallDifficulty ?? 0); + } + return ""; + } + } + public partial class StatisticRow : Container, IHasAccentColour { private const float value_width = 25; From 97caf18036efce177232e4ecfa3e36a5dba69fa0 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 4 Nov 2023 21:57:17 +0200 Subject: [PATCH 0046/1118] Update CatchRuleset.cs --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index da84d12f59..816638ca87 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; From d0b2b4f7b9eaf2bf93cc92532ec683e095707561 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sat, 4 Nov 2023 21:57:42 +0200 Subject: [PATCH 0047/1118] Update AdvancedStats.cs --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index f9c6e95154..54b76c48a4 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -16,6 +16,7 @@ using osu.Game.Beatmaps; using osu.Framework.Bindables; using System.Collections.Generic; using osu.Game.Rulesets.Mods; +using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Extensions; @@ -25,7 +26,6 @@ using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; -using System.Linq; namespace osu.Game.Screens.Select.Details { From 7492d953aee303732df79ba0267f34dbacfbb3e9 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sat, 4 Nov 2023 21:17:58 +0100 Subject: [PATCH 0048/1118] Moved error checks into Editor - Invoke Action on error to Notify user - added some comments --- .../Editing/TestSceneOpenEditorTimestamp.cs | 25 ++++++------ osu.Game/OsuGame.cs | 39 ++++--------------- osu.Game/Screens/Edit/Editor.cs | 27 ++++++++++++- .../Screens/Edit/EditorTimestampParser.cs | 4 +- 4 files changed, 47 insertions(+), 48 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index fea9334ff8..f65aff922e 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -67,17 +67,6 @@ namespace osu.Game.Tests.Visual.Editing && checkCombos; } - private bool checkSnapAndSelectColumn(double startTime, List<(int, int)> columnPairs = null) - { - bool checkColumns = columnPairs != null - ? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2))) - : !EditorBeatmap.SelectedHitObjects.Any(); - - return EditorClock.CurrentTime == startTime - && EditorBeatmap.SelectedHitObjects.Count == (columnPairs?.Count ?? 0) - && checkColumns; - } - private bool hasCombosInOrder(IEnumerable selected, params int[] comboNumbers) { List hitObjects = selected.ToList(); @@ -89,6 +78,17 @@ namespace osu.Game.Tests.Visual.Editing .Any(); } + private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(int, int)> columnPairs = null) + { + bool checkColumns = columnPairs != null + ? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2))) + : !EditorBeatmap.SelectedHitObjects.Any(); + + return EditorClock.CurrentTime == startTime + && EditorBeatmap.SelectedHitObjects.Count == (columnPairs?.Count ?? 0) + && checkColumns; + } + private bool isNoteAt(HitObject hitObject, double time, int column) { return hitObject is IHasColumn columnInfo @@ -96,7 +96,7 @@ namespace osu.Game.Tests.Visual.Editing && columnInfo.Column == column; } - public void SetUpEditor(RulesetInfo ruleset) + protected void SetUpEditor(RulesetInfo ruleset) { BeatmapSetInfo beatmapSet = null!; @@ -320,6 +320,7 @@ namespace osu.Game.Tests.Visual.Editing { (956, 1) }) ); + // TODO: discuss - this selects the first 2 objects on Stable, do we want that or is this fine? AddStepClickLink("00:00:000 (1,2)", "std link"); AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(956)); } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a6bb6cc120..a9d4927e33 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -562,43 +562,18 @@ namespace osu.Game { if (ScreenStack.CurrentScreen is not Editor editor) { - waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification - { - Icon = FontAwesome.Solid.ExclamationTriangle, - Text = EditorStrings.MustBeInEdit, - })); + postNotification(EditorStrings.MustBeInEdit); return; } - string[] groups = EditorTimestampParser.GetRegexGroups(timestamp); + editor.SeekAndSelectHitObjects(timestamp, onError: postNotification); + return; - if (groups.Length != 2 || string.IsNullOrEmpty(groups[0])) + void postNotification(LocalisableString message) => Schedule(() => Notifications.Post(new SimpleNotification { - waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification - { - Icon = FontAwesome.Solid.ExclamationTriangle, - Text = EditorStrings.FailedToProcessTimestamp - })); - return; - } - - string timeGroup = groups[0]; - string objectsGroup = groups[1]; - string timeMinutes = timeGroup.Split(':').FirstOrDefault() ?? string.Empty; - - // Currently, lazer chat highlights infinite-long editor links like `10000000000:00:000 (1)` - // Limit timestamp link length at 30000 min (50 hr) to avoid parsing issues - if (timeMinutes.Length > 5 || double.Parse(timeMinutes) > 30_000) - { - waitForReady(() => Notifications, _ => Notifications.Post(new SimpleNotification - { - Icon = FontAwesome.Solid.ExclamationTriangle, - Text = EditorStrings.TooLongTimestamp - })); - return; - } - - editor.SeekAndSelectHitObjects(timeGroup, objectsGroup); + Icon = FontAwesome.Solid.ExclamationTriangle, + Text = message + })); } /// diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 80e01d4eb7..60d26d9ec0 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1138,11 +1138,33 @@ namespace osu.Game.Screens.Edit loader?.CancelPendingDifficultySwitch(); } - public void SeekAndSelectHitObjects(string timeGroup, string objectsGroup) + public void SeekAndSelectHitObjects(string timestamp, Action onError) { + string[] groups = EditorTimestampParser.GetRegexGroups(timestamp); + + if (groups.Length != 2 || string.IsNullOrEmpty(groups[0])) + { + onError.Invoke(EditorStrings.FailedToProcessTimestamp); + return; + } + + string timeGroup = groups[0]; + string objectsGroup = groups[1]; + string timeMinutes = timeGroup.Split(':').FirstOrDefault() ?? string.Empty; + + // Currently, lazer chat highlights infinite-long editor links like `10000000000:00:000 (1)` + // Limit timestamp link length at 30000 min (50 hr) to avoid parsing issues + if (timeMinutes.Length > 5 || double.Parse(timeMinutes) > 30_000) + { + onError.Invoke(EditorStrings.TooLongTimestamp); + return; + } + double position = EditorTimestampParser.GetTotalMilliseconds(timeGroup); + editorBeatmap.SelectedHitObjects.Clear(); + // Only seeking is necessary if (string.IsNullOrEmpty(objectsGroup)) { if (clock.IsRunning) @@ -1155,8 +1177,9 @@ namespace osu.Game.Screens.Edit if (Mode.Value != EditorScreenMode.Compose) Mode.Value = EditorScreenMode.Compose; - // Seek to the next closest HitObject's position + // Seek to the next closest HitObject HitObject nextObject = editorBeatmap.HitObjects.FirstOrDefault(x => x.StartTime >= position); + if (nextObject != null && nextObject.StartTime > 0) position = nextObject.StartTime; diff --git a/osu.Game/Screens/Edit/EditorTimestampParser.cs b/osu.Game/Screens/Edit/EditorTimestampParser.cs index 44d614ca70..2d8f8a8f4c 100644 --- a/osu.Game/Screens/Edit/EditorTimestampParser.cs +++ b/osu.Game/Screens/Edit/EditorTimestampParser.cs @@ -32,7 +32,7 @@ namespace osu.Game.Screens.Edit return (times[0] * 60 + times[1]) * 1_000 + times[2]; } - public static List GetSelectedHitObjects(IEnumerable editorHitObjects, string objectsGroup, double position) + public static List GetSelectedHitObjects(IReadOnlyList editorHitObjects, string objectsGroup, double position) { List hitObjects = editorHitObjects.Where(x => x.StartTime >= position).ToList(); List selectedObjects = new List(); @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Edit } // Stable behavior - // - always selects next closest object when `objectsGroup` only has one, non-Column item + // - always selects the next closest object when `objectsGroup` only has one (combo) item if (objectsToSelect.Length != 1 || objectsGroup.Contains('|')) return selectedObjects; From 277cf7dc127cfacaf62f51bc88e0a0a3c1474382 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sun, 5 Nov 2023 18:26:51 +0100 Subject: [PATCH 0049/1118] Ensure every SelectedItem is alive and has Blueprint --- .../Components/EditorBlueprintContainer.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index ad0e8b124b..60959ca27a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; +using System.Collections.Specialized; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -51,6 +52,10 @@ namespace osu.Game.Screens.Edit.Compose.Components Beatmap.HitObjectAdded += AddBlueprintFor; Beatmap.HitObjectRemoved += RemoveBlueprintFor; + // This makes sure HitObjects will have active Blueprints ready to display + // after clicking on an Editor Timestamp/Link + Beatmap.SelectedHitObjects.CollectionChanged += SetHitObjectsAlive; + if (Composer != null) { foreach (var obj in Composer.HitObjects) @@ -144,6 +149,15 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).ToArray()); } + protected void SetHitObjectsAlive(object sender, NotifyCollectionChangedEventArgs e) + { + if (e == null || e.Action != NotifyCollectionChangedAction.Add || e.NewItems == null) + return; + + foreach (HitObject item in e.NewItems) + Composer.Playfield.SetKeepAlive(item, true); + } + protected override void OnBlueprintSelected(SelectionBlueprint blueprint) { base.OnBlueprintSelected(blueprint); @@ -166,6 +180,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { Beatmap.HitObjectAdded -= AddBlueprintFor; Beatmap.HitObjectRemoved -= RemoveBlueprintFor; + Beatmap.SelectedHitObjects.CollectionChanged -= SetHitObjectsAlive; } usageEventBuffer?.Dispose(); From 8e8a88cfaf4852d4e80611677cb5948806096704 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Tue, 7 Nov 2023 00:54:15 +0100 Subject: [PATCH 0050/1118] Removed nullable line from Test --- osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index f65aff922e..77dcbf069b 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Collections.Generic; using System.Linq; using NUnit.Framework; From 0834b79cc7fcb78873e381754f709deba0be4693 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Tue, 7 Nov 2023 00:56:24 +0100 Subject: [PATCH 0051/1118] Renamed method and moved Notifications inside --- osu.Game/OsuGame.cs | 15 ++++++--------- osu.Game/Screens/Edit/Editor.cs | 16 ++++++++++++---- 2 files changed, 18 insertions(+), 13 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index a9d4927e33..be1776a330 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -562,18 +562,15 @@ namespace osu.Game { if (ScreenStack.CurrentScreen is not Editor editor) { - postNotification(EditorStrings.MustBeInEdit); + Schedule(() => Notifications.Post(new SimpleNotification + { + Icon = FontAwesome.Solid.ExclamationTriangle, + Text = EditorStrings.MustBeInEdit + })); return; } - editor.SeekAndSelectHitObjects(timestamp, onError: postNotification); - return; - - void postNotification(LocalisableString message) => Schedule(() => Notifications.Post(new SimpleNotification - { - Icon = FontAwesome.Solid.ExclamationTriangle, - Text = message - })); + editor.HandleTimestamp(timestamp); } /// diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 60d26d9ec0..9e0671e91d 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable @@ -1138,13 +1138,17 @@ namespace osu.Game.Screens.Edit loader?.CancelPendingDifficultySwitch(); } - public void SeekAndSelectHitObjects(string timestamp, Action onError) + public void HandleTimestamp(string timestamp) { string[] groups = EditorTimestampParser.GetRegexGroups(timestamp); if (groups.Length != 2 || string.IsNullOrEmpty(groups[0])) { - onError.Invoke(EditorStrings.FailedToProcessTimestamp); + Schedule(() => notifications.Post(new SimpleNotification + { + Icon = FontAwesome.Solid.ExclamationTriangle, + Text = EditorStrings.FailedToProcessTimestamp + })); return; } @@ -1156,7 +1160,11 @@ namespace osu.Game.Screens.Edit // Limit timestamp link length at 30000 min (50 hr) to avoid parsing issues if (timeMinutes.Length > 5 || double.Parse(timeMinutes) > 30_000) { - onError.Invoke(EditorStrings.TooLongTimestamp); + Schedule(() => notifications.Post(new SimpleNotification + { + Icon = FontAwesome.Solid.ExclamationTriangle, + Text = EditorStrings.TooLongTimestamp + })); return; } From 44f127c8a86c9fa381118571c5af5e26f8cf141d Mon Sep 17 00:00:00 2001 From: ratinfx Date: Tue, 7 Nov 2023 01:02:45 +0100 Subject: [PATCH 0052/1118] Renamed method and made private --- .../Edit/Compose/Components/EditorBlueprintContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index 60959ca27a..b68a690097 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // This makes sure HitObjects will have active Blueprints ready to display // after clicking on an Editor Timestamp/Link - Beatmap.SelectedHitObjects.CollectionChanged += SetHitObjectsAlive; + Beatmap.SelectedHitObjects.CollectionChanged += keepHitObjectsAlive; if (Composer != null) { @@ -149,7 +149,7 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).ToArray()); } - protected void SetHitObjectsAlive(object sender, NotifyCollectionChangedEventArgs e) + private void keepHitObjectsAlive(object sender, NotifyCollectionChangedEventArgs e) { if (e == null || e.Action != NotifyCollectionChangedAction.Add || e.NewItems == null) return; @@ -180,7 +180,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { Beatmap.HitObjectAdded -= AddBlueprintFor; Beatmap.HitObjectRemoved -= RemoveBlueprintFor; - Beatmap.SelectedHitObjects.CollectionChanged -= SetHitObjectsAlive; + Beatmap.SelectedHitObjects.CollectionChanged -= keepHitObjectsAlive; } usageEventBuffer?.Dispose(); From aa87e0a44d469bbdfb9abe9bca13ea0323eda774 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Tue, 7 Nov 2023 01:36:58 +0100 Subject: [PATCH 0053/1118] HitObject Selection logic and separation for gamemodes + moved time_regex into EditorTimestampParser --- .../Edit/ManiaHitObjectComposer.cs | 19 ++++++- .../Edit/OsuHitObjectComposer.cs | 13 ++++- .../Editing/TestSceneOpenEditorTimestamp.cs | 30 ++++------- osu.Game/Online/Chat/MessageFormatter.cs | 7 +-- osu.Game/OsuGame.cs | 6 +-- .../Edit/EditorTimestampParser.cs | 54 +++++-------------- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 13 +++++ osu.Game/Screens/Edit/Editor.cs | 50 ++++++++--------- 8 files changed, 95 insertions(+), 97 deletions(-) rename osu.Game/{Screens => Rulesets}/Edit/EditorTimestampParser.cs (50%) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index b9db4168f4..d217f04651 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; @@ -11,6 +12,7 @@ using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit.Compose.Components; @@ -49,6 +51,21 @@ namespace osu.Game.Rulesets.Mania.Edit }; public override string ConvertSelectionToString() - => string.Join(',', EditorBeatmap.SelectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => $"{h.StartTime}|{h.Column}")); + => string.Join(ObjectSeparator, EditorBeatmap.SelectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => $"{h.StartTime}|{h.Column}")); + + public override bool HandleHitObjectSelection(HitObject hitObject, string objectInfo) + { + if (hitObject is not ManiaHitObject maniaHitObject) + return false; + + double[] split = objectInfo.Split('|').Select(double.Parse).ToArray(); + if (split.Length != 2) + return false; + + double timeValue = split[0]; + double columnValue = split[1]; + return Math.Abs(maniaHitObject.StartTime - timeValue) < 0.5 + && Math.Abs(maniaHitObject.Column - columnValue) < 0.5; + } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 0f8c960b65..0c63cf71d8 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -104,7 +104,18 @@ namespace osu.Game.Rulesets.Osu.Edit => new OsuBlueprintContainer(this); public override string ConvertSelectionToString() - => string.Join(',', selectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString())); + => string.Join(ObjectSeparator, selectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString())); + + public override bool HandleHitObjectSelection(HitObject hitObject, string objectInfo) + { + if (hitObject is not OsuHitObject osuHitObject) + return false; + + if (!int.TryParse(objectInfo, out int comboValue) || comboValue < 1) + return false; + + return osuHitObject.IndexInCurrentCombo + 1 == comboValue; + } private DistanceSnapGrid distanceSnapGrid; private Container distanceSnapGridContainer; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index 77dcbf069b..f7b976702a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -29,11 +29,14 @@ namespace osu.Game.Tests.Visual.Editing protected EditorBeatmap EditorBeatmap => Editor.ChildrenOfType().Single(); protected EditorClock EditorClock => Editor.ChildrenOfType().Single(); - protected void AddStepClickLink(string timestamp, string step = "") + protected void AddStepClickLink(string timestamp, string step = "", bool waitForSeek = true) { AddStep($"{step} {timestamp}", () => Game.HandleLink(new LinkDetails(LinkAction.OpenEditorTimestamp, timestamp)) ); + + if (waitForSeek) + AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value); } protected void AddStepScreenModeTo(EditorScreenMode screenMode) @@ -76,7 +79,7 @@ namespace osu.Game.Tests.Visual.Editing .Any(); } - private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(int, int)> columnPairs = null) + private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(int, int)>? columnPairs = null) { bool checkColumns = columnPairs != null ? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2))) @@ -123,7 +126,7 @@ namespace osu.Game.Tests.Visual.Editing { RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; - AddStepClickLink("00:00:000"); + AddStepClickLink("00:00:000", waitForSeek: false); AddAssert("recieved 'must be in edit'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 1 ); @@ -131,7 +134,7 @@ namespace osu.Game.Tests.Visual.Editing AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); - AddStepClickLink("00:00:000 (1)"); + AddStepClickLink("00:00:000 (1)", waitForSeek: false); AddAssert("recieved 'must be in edit'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 2 ); @@ -139,27 +142,12 @@ namespace osu.Game.Tests.Visual.Editing SetUpEditor(rulesetInfo); AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - AddStepClickLink("00:000", "invalid link"); + AddStepClickLink("00:000", "invalid link", waitForSeek: false); AddAssert("recieved 'failed to process'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 1 ); - AddStepClickLink("00:00:00:000", "invalid link"); - AddAssert("recieved 'failed to process'", () => - Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 2 - ); - - AddStepClickLink("00:00:000 ()", "invalid link"); - AddAssert("recieved 'failed to process'", () => - Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 3 - ); - - AddStepClickLink("00:00:000 (-1)", "invalid link"); - AddAssert("recieved 'failed to process'", () => - Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 4 - ); - - AddStepClickLink("50000:00:000", "too long link"); + AddStepClickLink("50000:00:000", "too long link", waitForSeek: false); AddAssert("recieved 'too long'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.TooLongTimestamp) == 1 ); diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 667175117f..9a194dba47 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using System.Text.RegularExpressions; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets.Edit; namespace osu.Game.Online.Chat { @@ -41,10 +42,6 @@ namespace osu.Game.Online.Chat @"(?:#(?:[a-z0-9$_\+!\*\',;:\(\)@&=\/~-]|%[0-9a-f]{2})*)?)?)", RegexOptions.IgnoreCase); - // 00:00:000 (1,2,3) - test - // regex from https://github.com/ppy/osu-web/blob/651a9bac2b60d031edd7e33b8073a469bf11edaa/resources/assets/coffee/_classes/beatmap-discussion-helper.coffee#L10 - private static readonly Regex time_regex = new Regex(@"\b(((\d{2,}):([0-5]\d)[:.](\d{3}))(\s\((?:\d+[,|])*\d+\))?)"); - // #osu private static readonly Regex channel_regex = new Regex(@"(#[a-zA-Z]+[a-zA-Z0-9]+)"); @@ -274,7 +271,7 @@ namespace osu.Game.Online.Chat handleAdvanced(advanced_link_regex, result, startIndex); // handle editor times - handleMatches(time_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}edit/{{0}}", result, startIndex, LinkAction.OpenEditorTimestamp); + handleMatches(EditorTimestampParser.TIME_REGEX, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}edit/{{0}}", result, startIndex, LinkAction.OpenEditorTimestamp); // handle channels handleMatches(channel_regex, "{0}", $@"{OsuGameBase.OSU_PROTOCOL}chan/{{0}}", result, startIndex, LinkAction.OpenChannel); diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index be1776a330..cde8ee1457 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -563,10 +563,10 @@ namespace osu.Game if (ScreenStack.CurrentScreen is not Editor editor) { Schedule(() => Notifications.Post(new SimpleNotification - { - Icon = FontAwesome.Solid.ExclamationTriangle, + { + Icon = FontAwesome.Solid.ExclamationTriangle, Text = EditorStrings.MustBeInEdit - })); + })); return; } diff --git a/osu.Game/Screens/Edit/EditorTimestampParser.cs b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs similarity index 50% rename from osu.Game/Screens/Edit/EditorTimestampParser.cs rename to osu.Game/Rulesets/Edit/EditorTimestampParser.cs index 2d8f8a8f4c..4e5a696102 100644 --- a/osu.Game/Screens/Edit/EditorTimestampParser.cs +++ b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs @@ -7,41 +7,43 @@ using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -namespace osu.Game.Screens.Edit +namespace osu.Game.Rulesets.Edit { public static class EditorTimestampParser { - private static readonly Regex timestamp_regex = new Regex(@"^(\d+:\d+:\d+)(?: \((\d+(?:[|,]\d+)*)\))?$", RegexOptions.Compiled); + // 00:00:000 (1,2,3) - test + // regex from https://github.com/ppy/osu-web/blob/651a9bac2b60d031edd7e33b8073a469bf11edaa/resources/assets/coffee/_classes/beatmap-discussion-helper.coffee#L10 + public static readonly Regex TIME_REGEX = new Regex(@"\b(((\d{2,}):([0-5]\d)[:.](\d{3}))(\s\((?:\d+[,|])*\d+\))?)"); public static string[] GetRegexGroups(string timestamp) { - Match match = timestamp_regex.Match(timestamp); - return match.Success - ? match.Groups.Values.Where(x => x is not Match).Select(x => x.Value).ToArray() + Match match = TIME_REGEX.Match(timestamp); + string[] result = match.Success + ? match.Groups.Values.Where(x => x is not Match && !x.Value.Contains(':')).Select(x => x.Value).ToArray() : Array.Empty(); + return result; } - public static double GetTotalMilliseconds(string timeGroup) + public static double GetTotalMilliseconds(params string[] timesGroup) { - int[] times = timeGroup.Split(':').Select(int.Parse).ToArray(); + int[] times = timesGroup.Select(int.Parse).ToArray(); Debug.Assert(times.Length == 3); return (times[0] * 60 + times[1]) * 1_000 + times[2]; } - public static List GetSelectedHitObjects(IReadOnlyList editorHitObjects, string objectsGroup, double position) + public static List GetSelectedHitObjects(HitObjectComposer composer, IReadOnlyList editorHitObjects, string objectsGroup, double position) { List hitObjects = editorHitObjects.Where(x => x.StartTime >= position).ToList(); List selectedObjects = new List(); - string[] objectsToSelect = objectsGroup.Split(',').ToArray(); + string[] objectsToSelect = objectsGroup.Split(composer.ObjectSeparator).ToArray(); foreach (string objectInfo in objectsToSelect) { - HitObject? current = hitObjects.FirstOrDefault(x => shouldHitObjectBeSelected(x, objectInfo)); + HitObject? current = hitObjects.FirstOrDefault(x => composer.HandleHitObjectSelection(x, objectInfo)); if (current == null) continue; @@ -67,35 +69,5 @@ namespace osu.Game.Screens.Edit return selectedObjects; } - - private static bool shouldHitObjectBeSelected(HitObject hitObject, string objectInfo) - { - switch (hitObject) - { - // (combo) - case IHasComboInformation comboInfo: - { - if (!double.TryParse(objectInfo, out double comboValue) || comboValue < 1) - return false; - - return comboInfo.IndexInCurrentCombo + 1 == comboValue; - } - - // (time|column) - case IHasColumn column: - { - double[] split = objectInfo.Split('|').Select(double.Parse).ToArray(); - if (split.Length != 2) - return false; - - double timeValue = split[0]; - double columnValue = split[1]; - return hitObject.StartTime == timeValue && column.Column == columnValue; - } - - default: - return false; - } - } } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 07e5869e28..f6cddcc0d2 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -528,6 +528,19 @@ namespace osu.Game.Rulesets.Edit public virtual string ConvertSelectionToString() => string.Empty; + /// + /// The custom logic that decides whether a HitObject should be selected when clicking an editor timestamp link + /// + /// The hitObject being checked + /// A single hitObject's information created with + /// Whether a HitObject should be selected or not + public virtual bool HandleHitObjectSelection(HitObject hitObject, string objectInfo) => false; + + /// + /// A character that separates the selection in + /// + public virtual char ObjectSeparator => ','; + #region IPositionSnapProvider public abstract SnapResult FindSnappedPositionAndTime(Vector2 screenSpacePosition, SnapType snapType = SnapType.All); diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 9e0671e91d..592e6625cc 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. #nullable disable @@ -14,6 +14,7 @@ using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Input.Bindings; @@ -1142,7 +1143,7 @@ namespace osu.Game.Screens.Edit { string[] groups = EditorTimestampParser.GetRegexGroups(timestamp); - if (groups.Length != 2 || string.IsNullOrEmpty(groups[0])) + if (groups.Length != 4 || string.IsNullOrEmpty(groups[0])) { Schedule(() => notifications.Post(new SimpleNotification { @@ -1152,13 +1153,14 @@ namespace osu.Game.Screens.Edit return; } - string timeGroup = groups[0]; - string objectsGroup = groups[1]; - string timeMinutes = timeGroup.Split(':').FirstOrDefault() ?? string.Empty; + string timeMin = groups[0]; + string timeSec = groups[1]; + string timeMss = groups[2]; + string objectsGroup = groups[3].Replace("(", "").Replace(")", "").Trim(); // Currently, lazer chat highlights infinite-long editor links like `10000000000:00:000 (1)` // Limit timestamp link length at 30000 min (50 hr) to avoid parsing issues - if (timeMinutes.Length > 5 || double.Parse(timeMinutes) > 30_000) + if (string.IsNullOrEmpty(timeMin) || timeMin.Length > 5 || double.Parse(timeMin) > 30_000) { Schedule(() => notifications.Post(new SimpleNotification { @@ -1168,38 +1170,36 @@ namespace osu.Game.Screens.Edit return; } - double position = EditorTimestampParser.GetTotalMilliseconds(timeGroup); - editorBeatmap.SelectedHitObjects.Clear(); - // Only seeking is necessary + double position = EditorTimestampParser.GetTotalMilliseconds(timeMin, timeSec, timeMss); + if (string.IsNullOrEmpty(objectsGroup)) { - if (clock.IsRunning) - clock.Stop(); - - clock.Seek(position); + clock.SeekSmoothlyTo(position); return; } + // Seek to the next closest HitObject instead + HitObject nextObject = editorBeatmap.HitObjects.FirstOrDefault(x => x.StartTime >= position); + + if (nextObject != null) + position = nextObject.StartTime; + + clock.SeekSmoothlyTo(position); + if (Mode.Value != EditorScreenMode.Compose) Mode.Value = EditorScreenMode.Compose; - // Seek to the next closest HitObject - HitObject nextObject = editorBeatmap.HitObjects.FirstOrDefault(x => x.StartTime >= position); - - if (nextObject != null && nextObject.StartTime > 0) - position = nextObject.StartTime; - - List selected = EditorTimestampParser.GetSelectedHitObjects(editorBeatmap.HitObjects.ToList(), objectsGroup, position); + List selected = EditorTimestampParser.GetSelectedHitObjects( + currentScreen.Dependencies.Get(), + editorBeatmap.HitObjects.ToList(), + objectsGroup, + position + ); if (selected.Any()) editorBeatmap.SelectedHitObjects.AddRange(selected); - - if (clock.IsRunning) - clock.Stop(); - - clock.Seek(position); } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); From bdbeb2bce4286122584e0c7f9dff61bdc5d57ca1 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Tue, 7 Nov 2023 11:11:32 +0100 Subject: [PATCH 0054/1118] Renamed CollectionChanged event handler --- .../Edit/Compose/Components/EditorBlueprintContainer.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index b68a690097..a311054ffc 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -54,7 +54,7 @@ namespace osu.Game.Screens.Edit.Compose.Components // This makes sure HitObjects will have active Blueprints ready to display // after clicking on an Editor Timestamp/Link - Beatmap.SelectedHitObjects.CollectionChanged += keepHitObjectsAlive; + Beatmap.SelectedHitObjects.CollectionChanged += selectionChanged; if (Composer != null) { @@ -149,7 +149,7 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).ToArray()); } - private void keepHitObjectsAlive(object sender, NotifyCollectionChangedEventArgs e) + private void selectionChanged(object sender, NotifyCollectionChangedEventArgs e) { if (e == null || e.Action != NotifyCollectionChangedAction.Add || e.NewItems == null) return; @@ -180,7 +180,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { Beatmap.HitObjectAdded -= AddBlueprintFor; Beatmap.HitObjectRemoved -= RemoveBlueprintFor; - Beatmap.SelectedHitObjects.CollectionChanged -= keepHitObjectsAlive; + Beatmap.SelectedHitObjects.CollectionChanged -= selectionChanged; } usageEventBuffer?.Dispose(); From 544d5d1d86416a179de9c6badd7833a8e53ca518 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Tue, 7 Nov 2023 12:23:22 +0100 Subject: [PATCH 0055/1118] Forgot a clock.Stop call --- osu.Game/Screens/Edit/Editor.cs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 592e6625cc..58c3ae809c 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1145,7 +1145,7 @@ namespace osu.Game.Screens.Edit if (groups.Length != 4 || string.IsNullOrEmpty(groups[0])) { - Schedule(() => notifications.Post(new SimpleNotification + Schedule(() => notifications?.Post(new SimpleNotification { Icon = FontAwesome.Solid.ExclamationTriangle, Text = EditorStrings.FailedToProcessTimestamp @@ -1162,7 +1162,7 @@ namespace osu.Game.Screens.Edit // Limit timestamp link length at 30000 min (50 hr) to avoid parsing issues if (string.IsNullOrEmpty(timeMin) || timeMin.Length > 5 || double.Parse(timeMin) > 30_000) { - Schedule(() => notifications.Post(new SimpleNotification + Schedule(() => notifications?.Post(new SimpleNotification { Icon = FontAwesome.Solid.ExclamationTriangle, Text = EditorStrings.TooLongTimestamp @@ -1172,6 +1172,9 @@ namespace osu.Game.Screens.Edit editorBeatmap.SelectedHitObjects.Clear(); + if (clock.IsRunning) + clock.Stop(); + double position = EditorTimestampParser.GetTotalMilliseconds(timeMin, timeSec, timeMss); if (string.IsNullOrEmpty(objectsGroup)) From 81caa854e6a7ba3251658f1cd174bf6bad62b1ea Mon Sep 17 00:00:00 2001 From: ratinfx Date: Tue, 7 Nov 2023 13:02:46 +0100 Subject: [PATCH 0056/1118] Separate Test cases by relevant rulesets --- .../TestSceneOpenEditorTimestampInMania.cs | 102 +++++++ .../TestSceneOpenEditorTimestampInOsu.cs | 110 ++++++++ .../Editing/TestSceneOpenEditorTimestamp.cs | 249 ++++-------------- 3 files changed, 259 insertions(+), 202 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs new file mode 100644 index 0000000000..6ec5dcee4c --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs @@ -0,0 +1,102 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests.Editor +{ + public partial class TestSceneOpenEditorTimestampInMania : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new ManiaRuleset(); + + private void addStepClickLink(string timestamp, string step = "", bool displayTimestamp = true) + { + AddStep(displayTimestamp ? $"{step} {timestamp}" : step, () => Editor.HandleTimestamp(timestamp)); + AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value); + } + + private void addReset() + { + addStepClickLink("00:00:000", "reset", false); + } + + private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(int, int)>? columnPairs = null) + { + bool checkColumns = columnPairs != null + ? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2))) + : !EditorBeatmap.SelectedHitObjects.Any(); + + return EditorClock.CurrentTime == startTime + && EditorBeatmap.SelectedHitObjects.Count == (columnPairs?.Count ?? 0) + && checkColumns; + } + + private bool isNoteAt(HitObject hitObject, double time, int column) + { + return hitObject is ManiaHitObject maniaHitObject + && maniaHitObject.StartTime == time + && maniaHitObject.Column == column; + } + + [Test] + public void TestNormalSelection() + { + addStepClickLink("00:05:920 (5920|3,6623|3,6857|2,7326|1)"); + AddAssert("selected group", () => checkSnapAndSelectColumn(5_920, new List<(int, int)> + { (5_920, 3), (6_623, 3), (6_857, 2), (7_326, 1) } + )); + + addReset(); + addStepClickLink("00:42:716 (42716|3,43420|2,44123|0,44357|1,45295|1)"); + AddAssert("selected ungrouped", () => checkSnapAndSelectColumn(42_716, new List<(int, int)> + { (42_716, 3), (43_420, 2), (44_123, 0), (44_357, 1), (45_295, 1) } + )); + + addReset(); + AddStep("add notes to row", () => + { + if (EditorBeatmap.HitObjects.Any(x => x is ManiaHitObject m && m.StartTime == 11_545 && m.Column is 1 or 2 or 3)) + return; + + ManiaHitObject first = (ManiaHitObject)EditorBeatmap.HitObjects.First(x => x is ManiaHitObject m && m.StartTime == 11_545 && m.Column == 0); + ManiaHitObject second = new Note { Column = 1, StartTime = first.StartTime }; + ManiaHitObject third = new Note { Column = 2, StartTime = first.StartTime }; + ManiaHitObject forth = new Note { Column = 3, StartTime = first.StartTime }; + EditorBeatmap.AddRange(new[] { second, third, forth }); + }); + addStepClickLink("00:11:545 (11545|0,11545|1,11545|2,11545|3)"); + AddAssert("selected in row", () => checkSnapAndSelectColumn(11_545, new List<(int, int)> + { (11_545, 0), (11_545, 1), (11_545, 2), (11_545, 3) } + )); + + addReset(); + addStepClickLink("01:36:623 (96623|1,97560|1,97677|1,97795|1,98966|1)"); + AddAssert("selected in column", () => checkSnapAndSelectColumn(96_623, new List<(int, int)> + { (96_623, 1), (97_560, 1), (97_677, 1), (97_795, 1), (98_966, 1) } + )); + } + + [Test] + public void TestUnusualSelection() + { + addStepClickLink("00:00:000 (0|1)", "invalid link"); + AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(2_170)); + + addReset(); + addStepClickLink("00:00:000 (0)", "std link"); + AddAssert("snap and select 1", () => checkSnapAndSelectColumn(2_170, new List<(int, int)> + { (2_170, 2) }) + ); + + addReset(); + // TODO: discuss - this selects the first 2 objects on Stable, do we want that or is this fine? + addStepClickLink("00:00:000 (1,2)", "std link"); + AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(2_170)); + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs new file mode 100644 index 0000000000..d69f482d29 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs @@ -0,0 +1,110 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests.Editor +{ + public partial class TestSceneOpenEditorTimestampInOsu : EditorTestScene + { + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + private void addStepClickLink(string timestamp, string step = "", bool displayTimestamp = true) + { + AddStep(displayTimestamp ? $"{step} {timestamp}" : step, () => Editor.HandleTimestamp(timestamp)); + AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value); + } + + private void addReset() + { + addStepClickLink("00:00:000", "reset", false); + } + + private bool checkSnapAndSelectCombo(double startTime, params int[] comboNumbers) + { + bool checkCombos = comboNumbers.Any() + ? hasCombosInOrder(EditorBeatmap.SelectedHitObjects, comboNumbers) + : !EditorBeatmap.SelectedHitObjects.Any(); + + return EditorClock.CurrentTime == startTime + && EditorBeatmap.SelectedHitObjects.Count == comboNumbers.Length + && checkCombos; + } + + private bool hasCombosInOrder(IEnumerable selected, params int[] comboNumbers) + { + List hitObjects = selected.ToList(); + if (hitObjects.Count != comboNumbers.Length) + return false; + + return !hitObjects.Select(x => (OsuHitObject)x) + .Where((x, i) => x.IndexInCurrentCombo + 1 != comboNumbers[i]) + .Any(); + } + + [Test] + public void TestNormalSelection() + { + addStepClickLink("00:02:170 (1,2,3)"); + AddAssert("snap and select 1-2-3", () => checkSnapAndSelectCombo(2_170, 1, 2, 3)); + + addReset(); + addStepClickLink("00:04:748 (2,3,4,1,2)"); + AddAssert("snap and select 2-3-4-1-2", () => checkSnapAndSelectCombo(4_748, 2, 3, 4, 1, 2)); + + addReset(); + addStepClickLink("00:02:170 (1,1,1)"); + AddAssert("snap and select 1-1-1", () => checkSnapAndSelectCombo(2_170, 1, 1, 1)); + + addReset(); + addStepClickLink("00:02:873 (2,2,2,2)"); + AddAssert("snap and select 2-2-2-2", () => checkSnapAndSelectCombo(2_873, 2, 2, 2, 2)); + } + + [Test] + public void TestUnusualSelection() + { + HitObject firstObject = null!; + + addStepClickLink("00:00:000 (1,2,3)", "invalid offset"); + AddAssert("snap to next, select 1-2-3", () => + { + firstObject = EditorBeatmap.HitObjects.First(); + return checkSnapAndSelectCombo(firstObject.StartTime, 1, 2, 3); + }); + + addReset(); + addStepClickLink("00:00:956 (2,3,4)", "invalid offset"); + AddAssert("snap to next, select 2-3-4", () => checkSnapAndSelectCombo(firstObject.StartTime, 2, 3, 4)); + + addReset(); + addStepClickLink("00:00:000 (0)", "invalid offset"); + AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); + + addReset(); + addStepClickLink("00:00:000 (1)", "invalid offset"); + AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); + + addReset(); + addStepClickLink("00:00:000 (2)", "invalid offset"); + AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); + + addReset(); + addStepClickLink("00:00:000 (2,3)", "invalid offset"); + AddAssert("snap to 1, select 2-3", () => checkSnapAndSelectCombo(firstObject.StartTime, 2, 3)); + + addReset(); + addStepClickLink("00:00:956 (956|1,956|2)", "mania link"); + AddAssert("snap to next, select none", () => checkSnapAndSelectCombo(firstObject.StartTime)); + + addReset(); + addStepClickLink("00:00:000 (0|1)", "mania link"); + AddAssert("snap to 1, select none", () => checkSnapAndSelectCombo(firstObject.StartTime)); + } + } +} diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index f7b976702a..bc31924e2c 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Extensions; @@ -12,9 +11,6 @@ using osu.Game.Database; using osu.Game.Localisation; using osu.Game.Online.Chat; using osu.Game.Rulesets; -using osu.Game.Rulesets.Mania; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; @@ -25,79 +21,39 @@ namespace osu.Game.Tests.Visual.Editing { public partial class TestSceneOpenEditorTimestamp : OsuGameTestScene { - protected Editor Editor => (Editor)Game.ScreenStack.CurrentScreen; - protected EditorBeatmap EditorBeatmap => Editor.ChildrenOfType().Single(); - protected EditorClock EditorClock => Editor.ChildrenOfType().Single(); + private Editor editor => (Editor)Game.ScreenStack.CurrentScreen; + private EditorBeatmap editorBeatmap => editor.ChildrenOfType().Single(); + private EditorClock editorClock => editor.ChildrenOfType().Single(); - protected void AddStepClickLink(string timestamp, string step = "", bool waitForSeek = true) + private void addStepClickLink(string timestamp, string step = "", bool waitForSeek = true) { AddStep($"{step} {timestamp}", () => Game.HandleLink(new LinkDetails(LinkAction.OpenEditorTimestamp, timestamp)) ); if (waitForSeek) - AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value); + AddUntilStep("wait for seek", () => editorClock.SeekingOrStopped.Value); } - protected void AddStepScreenModeTo(EditorScreenMode screenMode) + private void addStepScreenModeTo(EditorScreenMode screenMode) { - AddStep("change screen to " + screenMode, () => Editor.Mode.Value = screenMode); + AddStep("change screen to " + screenMode, () => editor.Mode.Value = screenMode); } - protected void AssertOnScreenAt(EditorScreenMode screen, double time, string text = "stayed in") + private void assertOnScreenAt(EditorScreenMode screen, double time, string text = "stayed in") { AddAssert($"{text} {screen} at {time}", () => - Editor.Mode.Value == screen - && EditorClock.CurrentTime == time + editor.Mode.Value == screen + && editorClock.CurrentTime == time ); } - protected void AssertMovedScreenTo(EditorScreenMode screen, string text = "moved to") + private void assertMovedScreenTo(EditorScreenMode screen, string text = "moved to") { - AddAssert($"{text} {screen}", () => Editor.Mode.Value == screen); + AddAssert($"{text} {screen}", () => editor.Mode.Value == screen); } - private bool checkSnapAndSelectCombo(double startTime, params int[] comboNumbers) - { - bool checkCombos = comboNumbers.Any() - ? hasCombosInOrder(EditorBeatmap.SelectedHitObjects, comboNumbers) - : !EditorBeatmap.SelectedHitObjects.Any(); - - return EditorClock.CurrentTime == startTime - && EditorBeatmap.SelectedHitObjects.Count == comboNumbers.Length - && checkCombos; - } - - private bool hasCombosInOrder(IEnumerable selected, params int[] comboNumbers) - { - List hitObjects = selected.ToList(); - if (hitObjects.Count != comboNumbers.Length) - return false; - - return !hitObjects.Select(x => (IHasComboInformation)x) - .Where((combo, i) => combo.IndexInCurrentCombo + 1 != comboNumbers[i]) - .Any(); - } - - private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(int, int)>? columnPairs = null) - { - bool checkColumns = columnPairs != null - ? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2))) - : !EditorBeatmap.SelectedHitObjects.Any(); - - return EditorClock.CurrentTime == startTime - && EditorBeatmap.SelectedHitObjects.Count == (columnPairs?.Count ?? 0) - && checkColumns; - } - - private bool isNoteAt(HitObject hitObject, double time, int column) - { - return hitObject is IHasColumn columnInfo - && hitObject.StartTime == time - && columnInfo.Column == column; - } - - protected void SetUpEditor(RulesetInfo ruleset) + private void setUpEditor(RulesetInfo ruleset) { BeatmapSetInfo beatmapSet = null!; @@ -118,7 +74,7 @@ namespace osu.Game.Tests.Visual.Editing ((PlaySongSelect)Game.ScreenStack.CurrentScreen) .Edit(beatmapSet.Beatmaps.Last(beatmap => beatmap.Ruleset.Name == ruleset.Name)) ); - AddUntilStep("Wait for editor open", () => Editor.ReadyForUse); + AddUntilStep("Wait for editor open", () => editor.ReadyForUse); } [Test] @@ -126,7 +82,7 @@ namespace osu.Game.Tests.Visual.Editing { RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; - AddStepClickLink("00:00:000", waitForSeek: false); + addStepClickLink("00:00:000", waitForSeek: false); AddAssert("recieved 'must be in edit'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 1 ); @@ -134,20 +90,20 @@ namespace osu.Game.Tests.Visual.Editing AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); - AddStepClickLink("00:00:000 (1)", waitForSeek: false); + addStepClickLink("00:00:000 (1)", waitForSeek: false); AddAssert("recieved 'must be in edit'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 2 ); - SetUpEditor(rulesetInfo); - AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + setUpEditor(rulesetInfo); + AddAssert("is editor Osu", () => editorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - AddStepClickLink("00:000", "invalid link", waitForSeek: false); + addStepClickLink("00:000", "invalid link", waitForSeek: false); AddAssert("recieved 'failed to process'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 1 ); - AddStepClickLink("50000:00:000", "too long link", waitForSeek: false); + addStepClickLink("50000:00:000", "too long link", waitForSeek: false); AddAssert("recieved 'too long'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.TooLongTimestamp) == 1 ); @@ -159,156 +115,45 @@ namespace osu.Game.Tests.Visual.Editing const long long_link_value = 1_000 * 60 * 1_000; RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; - SetUpEditor(rulesetInfo); - AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + setUpEditor(rulesetInfo); + AddAssert("is editor Osu", () => editorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - AddStepClickLink("1000:00:000", "long link"); + addStepClickLink("1000:00:000", "long link"); AddAssert("moved to end of track", () => - EditorClock.CurrentTime == long_link_value - || (EditorClock.TrackLength < long_link_value && EditorClock.CurrentTime == EditorClock.TrackLength) + editorClock.CurrentTime == long_link_value + || (editorClock.TrackLength < long_link_value && editorClock.CurrentTime == editorClock.TrackLength) ); - AddStepScreenModeTo(EditorScreenMode.SongSetup); - AddStepClickLink("00:00:000"); - AssertOnScreenAt(EditorScreenMode.SongSetup, 0); + addStepScreenModeTo(EditorScreenMode.SongSetup); + addStepClickLink("00:00:000"); + assertOnScreenAt(EditorScreenMode.SongSetup, 0); - AddStepClickLink("00:05:000 (0|0)"); - AssertMovedScreenTo(EditorScreenMode.Compose); + addStepClickLink("00:05:000 (0|0)"); + assertMovedScreenTo(EditorScreenMode.Compose); - AddStepScreenModeTo(EditorScreenMode.Design); - AddStepClickLink("00:10:000"); - AssertOnScreenAt(EditorScreenMode.Design, 10_000); + addStepScreenModeTo(EditorScreenMode.Design); + addStepClickLink("00:10:000"); + assertOnScreenAt(EditorScreenMode.Design, 10_000); - AddStepClickLink("00:15:000 (1)"); - AssertMovedScreenTo(EditorScreenMode.Compose); + addStepClickLink("00:15:000 (1)"); + assertMovedScreenTo(EditorScreenMode.Compose); - AddStepScreenModeTo(EditorScreenMode.Timing); - AddStepClickLink("00:20:000"); - AssertOnScreenAt(EditorScreenMode.Timing, 20_000); + addStepScreenModeTo(EditorScreenMode.Timing); + addStepClickLink("00:20:000"); + assertOnScreenAt(EditorScreenMode.Timing, 20_000); - AddStepClickLink("00:25:000 (0,1)"); - AssertMovedScreenTo(EditorScreenMode.Compose); + addStepClickLink("00:25:000 (0,1)"); + assertMovedScreenTo(EditorScreenMode.Compose); - AddStepScreenModeTo(EditorScreenMode.Verify); - AddStepClickLink("00:30:000"); - AssertOnScreenAt(EditorScreenMode.Verify, 30_000); + addStepScreenModeTo(EditorScreenMode.Verify); + addStepClickLink("00:30:000"); + assertOnScreenAt(EditorScreenMode.Verify, 30_000); - AddStepClickLink("00:35:000 (0,1)"); - AssertMovedScreenTo(EditorScreenMode.Compose); + addStepClickLink("00:35:000 (0,1)"); + assertMovedScreenTo(EditorScreenMode.Compose); - AddStepClickLink("00:00:000"); - AssertOnScreenAt(EditorScreenMode.Compose, 0); - } - - [Test] - public void TestSelectionForOsu() - { - HitObject firstObject = null!; - RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; - - SetUpEditor(rulesetInfo); - AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - - AddStepClickLink("00:00:956 (1,2,3)"); - AddAssert("snap and select 1-2-3", () => - { - firstObject = EditorBeatmap.HitObjects.First(); - return checkSnapAndSelectCombo(firstObject.StartTime, 1, 2, 3); - }); - - AddStepClickLink("00:01:450 (2,3,4,1,2)"); - AddAssert("snap and select 2-3-4-1-2", () => checkSnapAndSelectCombo(1_450, 2, 3, 4, 1, 2)); - - AddStepClickLink("00:00:956 (1,1,1)"); - AddAssert("snap and select 1-1-1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1, 1, 1)); - } - - [Test] - public void TestUnusualSelectionForOsu() - { - HitObject firstObject = null!; - RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; - - SetUpEditor(rulesetInfo); - AddAssert("is editor Osu", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - - AddStepClickLink("00:00:000 (1,2,3)", "invalid offset"); - AddAssert("snap to next, select 1-2-3", () => - { - firstObject = EditorBeatmap.HitObjects.First(); - return checkSnapAndSelectCombo(firstObject.StartTime, 1, 2, 3); - }); - - AddStepClickLink("00:00:956 (2,3,4)", "invalid offset"); - AddAssert("snap to next, select 2-3-4", () => checkSnapAndSelectCombo(firstObject.StartTime, 2, 3, 4)); - - AddStepClickLink("00:00:000 (0)", "invalid offset"); - AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); - - AddStepClickLink("00:00:000 (1)", "invalid offset"); - AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); - - AddStepClickLink("00:00:000 (2)", "invalid offset"); - AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); - - AddStepClickLink("00:00:000 (2,3)", "invalid offset"); - AddAssert("snap to 1, select 2-3", () => checkSnapAndSelectCombo(firstObject.StartTime, 2, 3)); - - AddStepClickLink("00:00:956 (956|1,956|2)", "mania link"); - AddAssert("snap to next, select none", () => checkSnapAndSelectCombo(firstObject.StartTime)); - - AddStepClickLink("00:00:000 (0|1)", "mania link"); - AddAssert("snap to 1, select none", () => checkSnapAndSelectCombo(firstObject.StartTime)); - } - - [Test] - public void TestSelectionForMania() - { - RulesetInfo rulesetInfo = new ManiaRuleset().RulesetInfo; - - SetUpEditor(rulesetInfo); - AddAssert("is editor Mania", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - - AddStepClickLink("00:11:010 (11010|1,11175|5,11258|3,11340|5,11505|1)"); - AddAssert("selected group", () => checkSnapAndSelectColumn(11010, new List<(int, int)> - { (11010, 1), (11175, 5), (11258, 3), (11340, 5), (11505, 1) } - )); - - AddStepClickLink("00:00:956 (956|1,956|6,1285|3,1780|4)"); - AddAssert("selected ungrouped", () => checkSnapAndSelectColumn(956, new List<(int, int)> - { (956, 1), (956, 6), (1285, 3), (1780, 4) } - )); - - AddStepClickLink("02:36:560 (156560|1,156560|4,156560|6)"); - AddAssert("selected in row", () => checkSnapAndSelectColumn(156560, new List<(int, int)> - { (156560, 1), (156560, 4), (156560, 6) } - )); - - AddStepClickLink("00:35:736 (35736|3,36395|3,36725|3,37384|3)"); - AddAssert("selected in column", () => checkSnapAndSelectColumn(35736, new List<(int, int)> - { (35736, 3), (36395, 3), (36725, 3), (37384, 3) } - )); - } - - [Test] - public void TestUnusualSelectionForMania() - { - RulesetInfo rulesetInfo = new ManiaRuleset().RulesetInfo; - - SetUpEditor(rulesetInfo); - AddAssert("is editor Mania", () => EditorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - - AddStepClickLink("00:00:000 (0|1)", "invalid link"); - AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(956)); - - AddStepClickLink("00:00:000 (0)", "std link"); - AddAssert("snap and select 1", () => checkSnapAndSelectColumn(956, new List<(int, int)> - { (956, 1) }) - ); - - // TODO: discuss - this selects the first 2 objects on Stable, do we want that or is this fine? - AddStepClickLink("00:00:000 (1,2)", "std link"); - AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(956)); + addStepClickLink("00:00:000"); + assertOnScreenAt(EditorScreenMode.Compose, 0); } } } From e451b2197c19503d357fc021984844d75d908c1f Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Thu, 9 Nov 2023 18:23:53 +0200 Subject: [PATCH 0057/1118] Delete util functions from rulesets --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 10 +++++----- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 18 +++++++++--------- osu.Game.Rulesets.Osu/OsuRuleset.cs | 17 +++++++++-------- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 7 +++---- 4 files changed, 26 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 816638ca87..421714e3b2 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -233,15 +233,15 @@ namespace osu.Game.Rulesets.Catch }; } - public double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); - public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); - public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); - public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - adjustedDifficulty.ApproachRate = ChangeArFromRate(adjustedDifficulty.ApproachRate, rate); + double preempt = adjustedDifficulty.ApproachRate < 5 ? + (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : + (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); + preempt /= rate; + adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; } diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index cfdfcbadee..b8324b3f2f 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -428,18 +428,18 @@ namespace osu.Game.Rulesets.Mania public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection(); - public double HitwindowFromOd(float OD) => 64.0 - 3 * OD; - public float OdFromHitwindow(double hitwindow300) => (float)(64.0 - hitwindow300) / 3; - public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); + // Mania doesn't have rate-adjusted attributes anymore? - public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) - { - BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + //public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) + //{ + // BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(adjustedDifficulty.OverallDifficulty, rate); + // double hitwindow = 64.0 - 3 * adjustedDifficulty.OverallDifficulty; + // hitwindow /= rate; + // adjustedDifficulty.OverallDifficulty = (float)(64.0 - hitwindow) / 3; - return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; - } + // return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; + //} } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index bdbedf1409..26a51a0c48 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -329,18 +329,19 @@ namespace osu.Game.Rulesets.Osu public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection(); - public double PreemptFromAr(float AR) => AR < 5 ? (1200.0 + 600.0 * (5 - AR) / 5) : (1200.0 - 750.0 * (AR - 5) / 5); - public float ArFromPreempt(double preempt) => (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); - public double HitwindowFromOd(float OD) => 80.0 - 6 * OD; - public float OdFromHitwindow(double hitwindow300) => (float)(80.0 - hitwindow300) / 6; - public float ChangeArFromRate(float AR, double rate) => ArFromPreempt(PreemptFromAr(AR) / rate); - public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - adjustedDifficulty.ApproachRate = ChangeArFromRate(adjustedDifficulty.ApproachRate, rate); - adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(adjustedDifficulty.OverallDifficulty, rate); + double preempt = adjustedDifficulty.ApproachRate < 5 ? + (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : + (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); + preempt /= rate; + adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); + + double hitwindow = 80.0 - 6 * adjustedDifficulty.OverallDifficulty; + hitwindow /= rate; + adjustedDifficulty.OverallDifficulty = (float)(80.0 - hitwindow) / 6; return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 026ac4f42e..3f9f939245 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -263,15 +263,14 @@ namespace osu.Game.Rulesets.Taiko }), true) }; } - public double HitwindowFromOd(float OD) => 35.0 - 15.0 * (OD - 5) / 5; - public float OdFromHitwindow(double hitwindow300) => (float)(5 * (35 - hitwindow300) / 15 + 5); - public float ChangeOdFromRate(float OD, double rate) => OdFromHitwindow(HitwindowFromOd(OD) / rate); public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - adjustedDifficulty.OverallDifficulty = ChangeOdFromRate(adjustedDifficulty.OverallDifficulty, rate); + double hitwindow = 35.0 - 15.0 * (adjustedDifficulty.OverallDifficulty - 5) / 5; + hitwindow /= rate; + adjustedDifficulty.OverallDifficulty = (float)(5 * (35 - hitwindow) / 15 + 5); return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; } From fecc6f580bf309357682d1ee725f0a3796d9261a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 23 Oct 2023 16:37:26 +0900 Subject: [PATCH 0058/1118] Add legacy reference health processor --- .../Scoring/LegacyOsuHealthProcessor.cs | 197 ++++++++++++++++++ .../Scoring/DrainingHealthProcessor.cs | 1 - .../Scoring/LegacyDrainingHealthProcessor.cs | 90 ++++++++ 3 files changed, 287 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs create mode 100644 osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs diff --git a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs new file mode 100644 index 0000000000..103569ffc3 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs @@ -0,0 +1,197 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Scoring +{ + // Reference implementation for osu!stable's HP drain. + public partial class LegacyOsuHealthProcessor : LegacyDrainingHealthProcessor + { + private const double hp_bar_maximum = 200; + private const double hp_combo_geki = 14; + private const double hp_hit_300 = 6; + private const double hp_slider_repeat = 4; + private const double hp_slider_tick = 3; + + private double lowestHpEver; + private double lowestHpEnd; + private double lowestHpComboEnd; + private double hpRecoveryAvailable; + private double hpMultiplierNormal; + private double hpMultiplierComboEnd; + + public LegacyOsuHealthProcessor(double drainStartTime) + : base(drainStartTime) + { + } + + public override void ApplyBeatmap(IBeatmap beatmap) + { + lowestHpEver = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 195, 160, 60); + lowestHpComboEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 198, 170, 80); + lowestHpEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 198, 180, 80); + hpRecoveryAvailable = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 8, 4, 0); + + base.ApplyBeatmap(beatmap); + } + + protected override void Reset(bool storeResults) + { + hpMultiplierNormal = 1; + hpMultiplierComboEnd = 1; + + base.Reset(storeResults); + } + + protected override double ComputeDrainRate() + { + double testDrop = 0.05; + double currentHp; + double currentHpUncapped; + + do + { + currentHp = hp_bar_maximum; + currentHpUncapped = hp_bar_maximum; + + double lowestHp = currentHp; + double lastTime = DrainStartTime; + int currentBreak = 0; + bool fail = false; + int comboTooLowCount = 0; + string failReason = string.Empty; + + for (int i = 0; i < Beatmap.HitObjects.Count; i++) + { + HitObject h = Beatmap.HitObjects[i]; + + // Find active break (between current and lastTime) + double localLastTime = lastTime; + double breakTime = 0; + + // Subtract any break time from the duration since the last object + if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) + { + BreakPeriod e = Beatmap.Breaks[currentBreak]; + + if (e.StartTime >= localLastTime && e.EndTime <= h.StartTime) + { + // consider break start equal to object end time for version 8+ since drain stops during this time + breakTime = (Beatmap.BeatmapInfo.BeatmapVersion < 8) ? (e.EndTime - e.StartTime) : e.EndTime - localLastTime; + currentBreak++; + } + } + + reduceHp(testDrop * (h.StartTime - lastTime - breakTime)); + + lastTime = h.GetEndTime(); + + if (currentHp < lowestHp) + lowestHp = currentHp; + + if (currentHp <= lowestHpEver) + { + fail = true; + testDrop *= 0.96; + failReason = $"hp too low ({currentHp / hp_bar_maximum} < {lowestHpEver / hp_bar_maximum})"; + break; + } + + double hpReduction = testDrop * (h.GetEndTime() - h.StartTime); + double hpOverkill = Math.Max(0, hpReduction - currentHp); + reduceHp(hpReduction); + + if (h is Slider slider) + { + for (int j = 0; j < slider.RepeatCount + 2; j++) + increaseHp(hpMultiplierNormal * hp_slider_repeat); + foreach (var _ in slider.NestedHitObjects.OfType()) + increaseHp(hpMultiplierNormal * hp_slider_tick); + } + else if (h is Spinner spinner) + { + foreach (var _ in spinner.NestedHitObjects.Where(t => t is not SpinnerBonusTick)) + increaseHp(hpMultiplierNormal * 1.7); + } + + if (hpOverkill > 0 && currentHp - hpOverkill <= lowestHpEver) + { + fail = true; + testDrop *= 0.96; + failReason = $"overkill ({currentHp / hp_bar_maximum} - {hpOverkill / hp_bar_maximum} <= {lowestHpEver / hp_bar_maximum})"; + break; + } + + if (i == Beatmap.HitObjects.Count - 1 || ((OsuHitObject)Beatmap.HitObjects[i + 1]).NewCombo) + { + increaseHp(hpMultiplierComboEnd * hp_combo_geki + hpMultiplierNormal * hp_hit_300); + + if (currentHp < lowestHpComboEnd) + { + if (++comboTooLowCount > 2) + { + hpMultiplierComboEnd *= 1.07; + hpMultiplierNormal *= 1.03; + fail = true; + failReason = $"combo end hp too low ({currentHp / hp_bar_maximum} < {lowestHpComboEnd / hp_bar_maximum})"; + break; + } + } + } + else + increaseHp(hpMultiplierNormal * hp_hit_300); + } + + if (!fail && currentHp < lowestHpEnd) + { + fail = true; + testDrop *= 0.94; + hpMultiplierComboEnd *= 1.01; + hpMultiplierNormal *= 1.01; + failReason = $"end hp too low ({currentHp / hp_bar_maximum} < {lowestHpEnd / hp_bar_maximum})"; + } + + double recovery = (currentHpUncapped - hp_bar_maximum) / Beatmap.HitObjects.Count; + + if (!fail && recovery < hpRecoveryAvailable) + { + fail = true; + testDrop *= 0.96; + hpMultiplierComboEnd *= 1.02; + hpMultiplierNormal *= 1.01; + failReason = $"recovery too low ({recovery / hp_bar_maximum} < {hpRecoveryAvailable / hp_bar_maximum})"; + } + + if (fail) + { + if (Log) + Console.WriteLine($"FAILED drop {testDrop / hp_bar_maximum}: {failReason}"); + continue; + } + + if (Log) + Console.WriteLine($"PASSED drop {testDrop / hp_bar_maximum}"); + return testDrop / hp_bar_maximum; + } while (true); + + void reduceHp(double amount) + { + currentHpUncapped = Math.Max(0, currentHpUncapped - amount); + currentHp = Math.Max(0, currentHp - amount); + } + + void increaseHp(double amount) + { + currentHpUncapped += amount; + currentHp = Math.Max(0, Math.Min(hp_bar_maximum, currentHp + amount)); + } + } + } +} diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index 592dcbfeb8..2f81aa735e 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -42,7 +42,6 @@ namespace osu.Game.Rulesets.Scoring private const double max_health_target = 0.4; private IBeatmap beatmap; - private double gameplayEndTime; private readonly double drainStartTime; diff --git a/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs new file mode 100644 index 0000000000..5d2426e4b7 --- /dev/null +++ b/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs @@ -0,0 +1,90 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + +using System; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Utils; + +namespace osu.Game.Rulesets.Scoring +{ + /// + /// A which continuously drains health.
+ /// At HP=0, the minimum health reached for a perfect play is 95%.
+ /// At HP=5, the minimum health reached for a perfect play is 70%.
+ /// At HP=10, the minimum health reached for a perfect play is 30%. + ///
+ public abstract partial class LegacyDrainingHealthProcessor : HealthProcessor + { + protected double DrainStartTime { get; } + protected double GameplayEndTime { get; private set; } + + protected IBeatmap Beatmap { get; private set; } + protected PeriodTracker NoDrainPeriodTracker { get; private set; } + + public bool Log { get; set; } + + public double DrainRate { get; private set; } + + /// + /// Creates a new . + /// + /// The time after which draining should begin. + protected LegacyDrainingHealthProcessor(double drainStartTime) + { + DrainStartTime = drainStartTime; + } + + protected override void Update() + { + base.Update(); + + if (NoDrainPeriodTracker?.IsInAny(Time.Current) == true) + return; + + // When jumping in and out of gameplay time within a single frame, health should only be drained for the period within the gameplay time + double lastGameplayTime = Math.Clamp(Time.Current - Time.Elapsed, DrainStartTime, GameplayEndTime); + double currentGameplayTime = Math.Clamp(Time.Current, DrainStartTime, GameplayEndTime); + + Health.Value -= DrainRate * (currentGameplayTime - lastGameplayTime); + } + + public override void ApplyBeatmap(IBeatmap beatmap) + { + Beatmap = beatmap; + + if (beatmap.HitObjects.Count > 0) + GameplayEndTime = beatmap.HitObjects[^1].GetEndTime(); + + NoDrainPeriodTracker = new PeriodTracker(beatmap.Breaks.Select(breakPeriod => new Period( + beatmap.HitObjects + .Select(hitObject => hitObject.GetEndTime()) + .Where(endTime => endTime <= breakPeriod.StartTime) + .DefaultIfEmpty(double.MinValue) + .Last(), + beatmap.HitObjects + .Select(hitObject => hitObject.StartTime) + .Where(startTime => startTime >= breakPeriod.EndTime) + .DefaultIfEmpty(double.MaxValue) + .First() + ))); + + base.ApplyBeatmap(beatmap); + } + + protected override void Reset(bool storeResults) + { + base.Reset(storeResults); + + DrainRate = 1; + + if (storeResults) + DrainRate = ComputeDrainRate(); + } + + protected abstract double ComputeDrainRate(); + } +} From 12e5766d5073374b463ad6752a98fd8ed937292c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 10 Nov 2023 16:35:03 +0900 Subject: [PATCH 0059/1118] Implement OsuHealthProcessor following osu-stable --- .../Scoring/OsuHealthProcessor.cs | 229 ++++++++++++++++++ 1 file changed, 229 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs new file mode 100644 index 0000000000..489b8408ea --- /dev/null +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -0,0 +1,229 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Scoring +{ + public partial class OsuHealthProcessor : LegacyDrainingHealthProcessor + { + private double lowestHpEver; + private double lowestHpEnd; + private double hpRecoveryAvailable; + private double hpMultiplierNormal; + + public OsuHealthProcessor(double drainStartTime) + : base(drainStartTime) + { + } + + public override void ApplyBeatmap(IBeatmap beatmap) + { + lowestHpEver = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.975, 0.8, 0.3); + lowestHpEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.99, 0.9, 0.4); + hpRecoveryAvailable = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.04, 0.02, 0); + + base.ApplyBeatmap(beatmap); + } + + protected override void Reset(bool storeResults) + { + hpMultiplierNormal = 1; + base.Reset(storeResults); + } + + protected override double ComputeDrainRate() + { + double testDrop = 0.00025; + double currentHp; + double currentHpUncapped; + + do + { + currentHp = 1; + currentHpUncapped = 1; + + double lowestHp = currentHp; + double lastTime = DrainStartTime; + int currentBreak = 0; + bool fail = false; + string failReason = string.Empty; + + for (int i = 0; i < Beatmap.HitObjects.Count; i++) + { + HitObject h = Beatmap.HitObjects[i]; + + // Find active break (between current and lastTime) + double localLastTime = lastTime; + double breakTime = 0; + + // Subtract any break time from the duration since the last object + if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) + { + BreakPeriod e = Beatmap.Breaks[currentBreak]; + + if (e.StartTime >= localLastTime && e.EndTime <= h.StartTime) + { + // consider break start equal to object end time for version 8+ since drain stops during this time + breakTime = (Beatmap.BeatmapInfo.BeatmapVersion < 8) ? (e.EndTime - e.StartTime) : e.EndTime - localLastTime; + currentBreak++; + } + } + + reduceHp(testDrop * (h.StartTime - lastTime - breakTime)); + + lastTime = h.GetEndTime(); + + if (currentHp < lowestHp) + lowestHp = currentHp; + + if (currentHp <= lowestHpEver) + { + fail = true; + testDrop *= 0.96; + failReason = $"hp too low ({currentHp} < {lowestHpEver})"; + break; + } + + double hpReduction = testDrop * (h.GetEndTime() - h.StartTime); + double hpOverkill = Math.Max(0, hpReduction - currentHp); + reduceHp(hpReduction); + + if (h is Slider slider) + { + foreach (var nested in slider.NestedHitObjects) + increaseHp(nested); + } + else if (h is Spinner spinner) + { + foreach (var nested in spinner.NestedHitObjects.Where(t => t is not SpinnerBonusTick)) + increaseHp(nested); + } + + if (hpOverkill > 0 && currentHp - hpOverkill <= lowestHpEver) + { + fail = true; + testDrop *= 0.96; + failReason = $"overkill ({currentHp} - {hpOverkill} <= {lowestHpEver})"; + break; + } + + increaseHp(h); + } + + if (!fail && currentHp < lowestHpEnd) + { + fail = true; + testDrop *= 0.94; + hpMultiplierNormal *= 1.01; + failReason = $"end hp too low ({currentHp} < {lowestHpEnd})"; + } + + double recovery = (currentHpUncapped - 1) / Beatmap.HitObjects.Count; + + if (!fail && recovery < hpRecoveryAvailable) + { + fail = true; + testDrop *= 0.96; + hpMultiplierNormal *= 1.01; + failReason = $"recovery too low ({recovery} < {hpRecoveryAvailable})"; + } + + if (fail) + { + if (Log) + Console.WriteLine($"FAILED drop {testDrop}: {failReason}"); + continue; + } + + if (Log) + Console.WriteLine($"PASSED drop {testDrop}"); + return testDrop; + } while (true); + + void reduceHp(double amount) + { + currentHpUncapped = Math.Max(0, currentHpUncapped - amount); + currentHp = Math.Max(0, currentHp - amount); + } + + void increaseHp(HitObject hitObject) + { + double amount = healthIncreaseFor(hitObject, hitObject.CreateJudgement().MaxResult); + currentHpUncapped += amount; + currentHp = Math.Max(0, Math.Min(1, currentHp + amount)); + } + } + + protected override double GetHealthIncreaseFor(JudgementResult result) => healthIncreaseFor(result.HitObject, result.Type); + + private double healthIncreaseFor(HitObject hitObject, HitResult result) + { + double increase; + + switch (result) + { + case HitResult.SmallTickMiss: + return IBeatmapDifficultyInfo.DifficultyRange(Beatmap.Difficulty.DrainRate, -0.02, -0.075, -0.14); + + case HitResult.LargeTickMiss: + return IBeatmapDifficultyInfo.DifficultyRange(Beatmap.Difficulty.DrainRate, -0.02, -0.075, -0.14); + + case HitResult.Miss: + return IBeatmapDifficultyInfo.DifficultyRange(Beatmap.Difficulty.DrainRate, -0.03, -0.125, -0.2); + + case HitResult.SmallTickHit: + // This result is always as a result of the slider tail. + increase = 0.02; + break; + + case HitResult.LargeTickHit: + // This result is either a result of a slider tick or a repeat. + increase = hitObject is SliderTick ? 0.015 : 0.02; + break; + + case HitResult.Meh: + increase = 0.002; + break; + + case HitResult.Ok: + increase = 0.011; + break; + + case HitResult.Good: + increase = 0.024; + break; + + case HitResult.Great: + increase = 0.03; + break; + + case HitResult.Perfect: + // 1.1 * Great. Unused. + increase = 0.033; + break; + + case HitResult.SmallBonus: + increase = 0.0085; + break; + + case HitResult.LargeBonus: + increase = 0.01; + break; + + default: + increase = 0; + break; + } + + return hpMultiplierNormal * increase; + } + } +} From 793d90e396bb3268af6117f1fff2a5b474e80e2d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 10 Nov 2023 19:09:09 +0900 Subject: [PATCH 0060/1118] Add some notes --- osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 489b8408ea..2266cf9d33 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -64,6 +64,7 @@ namespace osu.Game.Rulesets.Osu.Scoring double localLastTime = lastTime; double breakTime = 0; + // TODO: This doesn't handle overlapping/sequential breaks correctly (/b/614). // Subtract any break time from the duration since the last object if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) { @@ -107,6 +108,8 @@ namespace osu.Game.Rulesets.Osu.Scoring increaseHp(nested); } + // Note: Because HP is capped during the above increases, long sliders (with many ticks) or spinners + // will appear to overkill at lower drain levels than they should. However, it is also not correct to simply use the uncapped version. if (hpOverkill > 0 && currentHp - hpOverkill <= lowestHpEver) { fail = true; From 9ef34fa51a97e41f47dc8be0fc40c1495442c5fe Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 10 Nov 2023 15:02:15 +0200 Subject: [PATCH 0061/1118] FIxed stated problems --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 13 ------------- osu.Game/Rulesets/Ruleset.cs | 8 ++++++++ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index b8324b3f2f..7fcf400409 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -428,19 +428,6 @@ namespace osu.Game.Rulesets.Mania public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection(); - // Mania doesn't have rate-adjusted attributes anymore? - - //public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) - //{ - // BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - - // double hitwindow = 64.0 - 3 * adjustedDifficulty.OverallDifficulty; - // hitwindow /= rate; - // adjustedDifficulty.OverallDifficulty = (float)(64.0 - hitwindow) / 3; - - // return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; - //} - } public enum PlayfieldType diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index fbeec5ff4b..8896517a1a 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -389,6 +389,14 @@ namespace osu.Game.Rulesets /// Can be overridden to alter the difficulty section to the editor beatmap setup screen. ///
public virtual DifficultySection? CreateEditorDifficultySection() => null; + + /// + /// Changes difficulty after they're adjusted according to rate. + /// Doesn't change any attributes by default. + /// + /// Difficulty attributes that will be changed + /// Rate of the gameplay. For example 1.5 for DT. + /// Copy of difficulty info with values changed according to rate and ruleset-specific behaviour. public virtual BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) => new BeatmapDifficulty(baseDifficulty); } } From d0d334a3715d98405c9bcd58cf5d21d25895fd98 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 10 Nov 2023 15:05:26 +0200 Subject: [PATCH 0062/1118] Update Ruleset.cs --- osu.Game/Rulesets/Ruleset.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index 8896517a1a..e0e0c1295b 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -391,10 +391,10 @@ namespace osu.Game.Rulesets public virtual DifficultySection? CreateEditorDifficultySection() => null; /// - /// Changes difficulty after they're adjusted according to rate. + /// Changes after they're adjusted according to rate. /// Doesn't change any attributes by default. /// - /// Difficulty attributes that will be changed + /// >The that will be adjusted. /// Rate of the gameplay. For example 1.5 for DT. /// Copy of difficulty info with values changed according to rate and ruleset-specific behaviour. public virtual BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) => new BeatmapDifficulty(baseDifficulty); From c5feefc2ad186371431c08f593d3ad8b032168a6 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 10 Nov 2023 15:06:32 +0200 Subject: [PATCH 0063/1118] Update ManiaRuleset.cs --- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 7fcf400409..4507015169 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -427,7 +427,6 @@ namespace osu.Game.Rulesets.Mania public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection(); public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection(); - } public enum PlayfieldType From 60c3e7250bbc406f7ea82c446b9c2b3e2511e384 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 10 Nov 2023 15:13:40 +0200 Subject: [PATCH 0064/1118] fixed naming incconvinence --- osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs | 12 ++++++------ osu.Game/Screens/Select/Details/AdvancedStats.cs | 12 ++++++------ 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index c921e3e075..74bf5e5bd3 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -266,7 +266,7 @@ namespace osu.Game.Overlays.Mods Ruleset ruleset = gameRuleset.Value.CreateInstance(); adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(adjustedDifficulty, rate); - haveRateChangedValues = !isDifferentArOd(originalDifficulty, adjustedDifficulty); + haveRateChangedValues = isDifferentArOd(originalDifficulty, adjustedDifficulty); approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); @@ -279,13 +279,13 @@ namespace osu.Game.Overlays.Mods private bool isDifferentArOd(BeatmapDifficulty? a, BeatmapDifficulty? b) { - if (a == null && b == null) return true; - if (a == null || b == null) return false; + if (a == null && b == null) return false; + if (a == null || b == null) return true; - if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate, 0.01)) return false; - if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty, 0.01)) return false; + if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate, 0.01)) return true; + if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty, 0.01)) return true; - return true; + return false; } private void updateCollapsedState() diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 54b76c48a4..802c1901eb 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Select.Details rate = mod.ApplyToRate(0, rate); adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(adjustedDifficulty, rate); - haveRateChangedValues = !isDifferentArOd(originalDifficulty, adjustedDifficulty); + haveRateChangedValues = isDifferentArOd(originalDifficulty, adjustedDifficulty); } switch (BeatmapInfo?.Ruleset.OnlineID) @@ -204,13 +204,13 @@ namespace osu.Game.Screens.Select.Details private bool isDifferentArOd(BeatmapDifficulty a, BeatmapDifficulty b) { - if (a == null && b == null) return true; - if (a == null || b == null) return false; + if (a == null && b == null) return false; + if (a == null || b == null) return true; - if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate, 0.01)) return false; - if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty, 0.01)) return false; + if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate, 0.01)) return true; + if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty, 0.01)) return true; - return true; + return false; } public LocalisableString TooltipText From 064857c40b7cd18343672fdf103083ba3cb0fdba Mon Sep 17 00:00:00 2001 From: Poyo Date: Fri, 10 Nov 2023 19:57:44 -0800 Subject: [PATCH 0065/1118] Calculate unstable rate using rate-adjusted offsets --- osu.Game/Rulesets/Judgements/JudgementResult.cs | 5 +++++ .../Rulesets/Objects/Drawables/DrawableHitObject.cs | 1 + osu.Game/Rulesets/Scoring/HitEvent.cs | 11 +++++++++-- osu.Game/Rulesets/Scoring/HitEventExtensions.cs | 3 ++- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 2 +- osu.Game/Rulesets/UI/Playfield.cs | 2 +- osu.Game/Screens/Utility/CircleGameplay.cs | 2 +- osu.Game/Screens/Utility/ScrollingGameplay.cs | 2 +- 8 files changed, 21 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index c67f8b9fd5..603d470954 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -54,6 +54,11 @@ namespace osu.Game.Rulesets.Judgements /// public double TimeAbsolute => RawTime != null ? Math.Min(RawTime.Value, HitObject.GetEndTime() + HitObject.MaximumJudgementOffset) : HitObject.GetEndTime(); + /// + /// The gameplay rate at the time this occurred. + /// + public double GameplayRate { get; internal set; } + /// /// The combo prior to this occurring. /// diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index ce6475d3ce..0843fd5bdc 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -704,6 +704,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } Result.RawTime = Time.Current; + Result.GameplayRate = Clock.Rate; if (Result.HasResult) updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss); diff --git a/osu.Game/Rulesets/Scoring/HitEvent.cs b/osu.Game/Rulesets/Scoring/HitEvent.cs index cabbf40a7d..afa654318b 100644 --- a/osu.Game/Rulesets/Scoring/HitEvent.cs +++ b/osu.Game/Rulesets/Scoring/HitEvent.cs @@ -19,6 +19,11 @@ namespace osu.Game.Rulesets.Scoring ///
public readonly double TimeOffset; + /// + /// The true gameplay rate at the time of the event. + /// + public readonly double GameplayRate; + /// /// The hit result. /// @@ -46,12 +51,14 @@ namespace osu.Game.Rulesets.Scoring /// /// The time offset from the end of at which the event occurs. /// The . + /// The true gameplay rate at the time of the event. /// The that triggered the event. /// The previous . /// A position corresponding to the event. - public HitEvent(double timeOffset, HitResult result, HitObject hitObject, [CanBeNull] HitObject lastHitObject, [CanBeNull] Vector2? position) + public HitEvent(double timeOffset, double gameplayRate, HitResult result, HitObject hitObject, [CanBeNull] HitObject lastHitObject, [CanBeNull] Vector2? position) { TimeOffset = timeOffset; + GameplayRate = gameplayRate; Result = result; HitObject = hitObject; LastHitObject = lastHitObject; @@ -63,6 +70,6 @@ namespace osu.Game.Rulesets.Scoring /// /// The positional offset. /// The new . - public HitEvent With(Vector2? positionOffset) => new HitEvent(TimeOffset, Result, HitObject, LastHitObject, positionOffset); + public HitEvent With(Vector2? positionOffset) => new HitEvent(TimeOffset, GameplayRate, Result, HitObject, LastHitObject, positionOffset); } } diff --git a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs index b4bdd8a1ea..a93385ef43 100644 --- a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs +++ b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs @@ -18,7 +18,8 @@ namespace osu.Game.Rulesets.Scoring /// public static double? CalculateUnstableRate(this IEnumerable hitEvents) { - double[] timeOffsets = hitEvents.Where(affectsUnstableRate).Select(ev => ev.TimeOffset).ToArray(); + // Division by gameplay rate is to account for TimeOffset scaling with gameplay rate. + double[] timeOffsets = hitEvents.Where(affectsUnstableRate).Select(ev => ev.TimeOffset / ev.GameplayRate).ToArray(); return 10 * standardDeviation(timeOffsets); } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 35a7dfe369..4e899479bd 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -252,7 +252,7 @@ namespace osu.Game.Rulesets.Scoring /// The to describe. /// The . protected virtual HitEvent CreateHitEvent(JudgementResult result) - => new HitEvent(result.TimeOffset, result.Type, result.HitObject, lastHitObject, null); + => new HitEvent(result.TimeOffset, result.GameplayRate, result.Type, result.HitObject, lastHitObject, null); protected sealed override void RevertResultInternal(JudgementResult result) { diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index e9c35555c8..17baf8838c 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -473,7 +473,7 @@ namespace osu.Game.Rulesets.UI private void onNewResult(DrawableHitObject drawable, JudgementResult result) { - Debug.Assert(result != null && drawable.Entry?.Result == result && result.RawTime != null); + Debug.Assert(result != null && drawable.Entry?.Result == result && result.RawTime != null && result.GameplayRate != 0.0); judgedEntries.Push(drawable.Entry.AsNonNull()); NewResult?.Invoke(drawable, result); diff --git a/osu.Game/Screens/Utility/CircleGameplay.cs b/osu.Game/Screens/Utility/CircleGameplay.cs index d97812acb4..1f970c5121 100644 --- a/osu.Game/Screens/Utility/CircleGameplay.cs +++ b/osu.Game/Screens/Utility/CircleGameplay.cs @@ -224,7 +224,7 @@ namespace osu.Game.Screens.Utility .FadeOut(duration) .ScaleTo(1.5f, duration); - HitEvent = new HitEvent(Clock.CurrentTime - HitTime, HitResult.Good, new HitObject + HitEvent = new HitEvent(Clock.CurrentTime - HitTime, 1.0, HitResult.Good, new HitObject { HitWindows = new HitWindows(), }, null, null); diff --git a/osu.Game/Screens/Utility/ScrollingGameplay.cs b/osu.Game/Screens/Utility/ScrollingGameplay.cs index f1331d8fb2..5038c53b4a 100644 --- a/osu.Game/Screens/Utility/ScrollingGameplay.cs +++ b/osu.Game/Screens/Utility/ScrollingGameplay.cs @@ -186,7 +186,7 @@ namespace osu.Game.Screens.Utility .FadeOut(duration / 2) .ScaleTo(1.5f, duration / 2); - HitEvent = new HitEvent(Clock.CurrentTime - HitTime, HitResult.Good, new HitObject + HitEvent = new HitEvent(Clock.CurrentTime - HitTime, 1.0, HitResult.Good, new HitObject { HitWindows = new HitWindows(), }, null, null); From 926636cc035a17543d987ba9a9ba9c47fa5e7f7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Wed, 8 Nov 2023 19:43:54 +0900 Subject: [PATCH 0066/1118] Generalize Bezier curves to BSplines of Nth degree --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 8 +- .../JuiceStreamPathTest.cs | 4 +- .../Mods/TestSceneCatchModPerfect.cs | 2 +- .../Mods/TestSceneCatchModRelax.cs | 2 +- .../TestSceneAutoJuiceStream.cs | 2 +- .../TestSceneCatchModHidden.cs | 2 +- .../TestSceneDrawableHitObjects.cs | 2 +- .../TestSceneJuiceStream.cs | 2 +- .../Blueprints/Components/EditablePath.cs | 2 +- .../Objects/JuiceStreamPath.cs | 2 +- .../Checks/CheckOffscreenObjectsTest.cs | 12 +-- .../Editor/TestSceneObjectMerging.cs | 10 +-- .../TestSceneOsuEditorSelectInvalidPath.cs | 2 +- .../TestScenePathControlPointVisualiser.cs | 36 ++++----- .../TestSceneSliderControlPointPiece.cs | 28 +++---- .../Editor/TestSceneSliderLengthValidity.cs | 8 +- .../TestSceneSliderPlacementBlueprint.cs | 42 +++++------ .../Editor/TestSceneSliderReversal.cs | 4 +- .../TestSceneSliderSelectionBlueprint.cs | 2 +- .../Editor/TestSceneSliderSnapping.cs | 10 +-- .../Editor/TestSceneSliderSplitting.cs | 28 +++---- .../Editor/TestSliderScaling.cs | 2 +- .../Mods/TestSceneOsuModHidden.cs | 10 +-- .../Mods/TestSceneOsuModPerfect.cs | 2 +- .../OsuHitObjectGenerationUtilsTest.cs | 6 +- .../TestSceneHitCircleLateFade.cs | 2 +- .../TestSceneLegacyHitPolicy.cs | 18 ++--- .../TestSceneSlider.cs | 14 ++-- .../TestSceneSliderApplication.cs | 6 +- .../TestSceneSliderFollowCircleInput.cs | 2 +- .../TestSceneSliderInput.cs | 8 +- .../TestSceneSliderSnaking.cs | 6 +- .../TestSceneStartTimeOrderedHitPolicy.cs | 10 +-- .../Components/PathControlPointPiece.cs | 20 +++-- .../Components/PathControlPointVisualiser.cs | 26 +++---- .../Sliders/SliderPlacementBlueprint.cs | 14 ++-- .../Edit/OsuSelectionHandler.cs | 4 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- .../Formats/LegacyBeatmapDecoderTest.cs | 36 ++++----- .../Formats/LegacyBeatmapEncoderTest.cs | 6 +- .../Editing/LegacyEditorBeatmapPatcherTest.cs | 4 +- .../Editing/TestSceneEditorClipboard.cs | 2 +- .../Editing/TestSceneHitObjectComposer.cs | 2 +- .../Gameplay/TestSceneBezierConverter.cs | 40 +++++----- .../TestSceneGameplaySampleTriggerSource.cs | 2 +- .../Visual/Gameplay/TestSceneSliderPath.cs | 73 +++++++++++-------- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 15 ++-- osu.Game/Database/LegacyBeatmapExporter.cs | 4 +- osu.Game/Rulesets/Objects/BezierConverter.cs | 32 ++++---- .../Objects/Legacy/ConvertHitObjectParser.cs | 20 ++--- osu.Game/Rulesets/Objects/SliderPath.cs | 13 ++-- .../Rulesets/Objects/SliderPathExtensions.cs | 6 +- osu.Game/Rulesets/Objects/Types/PathType.cs | 50 ++++++++++++- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 10 +-- 54 files changed, 372 insertions(+), 305 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 05d7a38a95..16b51d414a 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddStep("update hit object path", () => { - hitObject.Path = new SliderPath(PathType.PerfectCurve, new[] + hitObject.Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(100, 100), @@ -190,16 +190,16 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor [Test] public void TestVertexResampling() { - addBlueprintStep(100, 100, new SliderPath(PathType.PerfectCurve, new[] + addBlueprintStep(100, 100, new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(100, 100), new Vector2(50, 200), }), 0.5); AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count); - AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); addAddVertexSteps(150, 150); - AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.Linear); + AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.LINEAR); } private void addBlueprintStep(double time, float x, SliderPath sliderPath, double velocity) => AddStep("add selection blueprint", () => diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs index 95b4fdc07e..82f24633b5 100644 --- a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests } while (rng.Next(2) != 0); int length = sliderPath.ControlPoints.Count - start + 1; - sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.Linear : length == 3 ? PathType.PerfectCurve : PathType.Bezier; + sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.LINEAR : length == 3 ? PathType.PERFECTCURVE : PathType.BEZIER; } while (rng.Next(3) != 0); if (rng.Next(5) == 0) @@ -215,7 +215,7 @@ namespace osu.Game.Rulesets.Catch.Tests foreach (var point in sliderPath.ControlPoints) { - Assert.That(point.Type, Is.EqualTo(PathType.Linear).Or.Null); + Assert.That(point.Type, Is.EqualTo(PathType.LINEAR).Or.Null); Assert.That(sliderStartY + point.Position.Y, Is.InRange(0, JuiceStreamPath.OSU_PLAYFIELD_HEIGHT)); } diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs index 71df523951..45e7d7aa28 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs @@ -35,7 +35,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods var stream = new JuiceStream { StartTime = 1000, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs index 5835ccaf78..a161615579 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModRelax.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Mods { X = CatchPlayfield.CENTER_X, StartTime = 3000, - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }) + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, Vector2.UnitY * 200 }) } } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs index 3261fb656e..202f010680 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneAutoJuiceStream.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.HitObjects.Add(new JuiceStream { X = CatchPlayfield.CENTER_X - width / 2, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(width, 0) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs index a44575a46e..419a846ec3 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Catch.Tests new JuiceStream { StartTime = 1000, - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(0, -192) }), + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(0, -192) }), X = CatchPlayfield.WIDTH / 2 } } diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs index 11d6419507..9c5cd68201 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneDrawableHitObjects.cs @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Catch.Tests { X = xCoords, StartTime = playfieldTime + 1000, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(0, 200) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs index c31a7ca99f..9a923adaab 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneJuiceStream.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Catch.Tests new JuiceStream { X = CatchPlayfield.CENTER_X, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(0, 100) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs index df76bf0a8c..86f92d16ca 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/Components/EditablePath.cs @@ -74,7 +74,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints.Components path.ConvertFromSliderPath(sliderPath, hitObject.Velocity); // If the original slider path has non-linear type segments, resample the vertices at nested hit object times to reduce the number of vertices. - if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.Linear)) + if (sliderPath.ControlPoints.Any(p => p.Type != null && p.Type != PathType.LINEAR)) { path.ResampleVertices(hitObject.NestedHitObjects .Skip(1).TakeWhile(h => !(h is Fruit)) // Only droplets in the first span are used. diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs index 0633151ddd..57acf7cee2 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStreamPath.cs @@ -236,7 +236,7 @@ namespace osu.Game.Rulesets.Catch.Objects for (int i = 1; i < vertices.Count; i++) { - sliderPath.ControlPoints[^1].Type = PathType.Linear; + sliderPath.ControlPoints[^1].Type = PathType.LINEAR; float deltaX = vertices[i].X - lastPosition.X; double length = (vertices[i].Time - currentTime) * velocity; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index a72aaa966c..8612a8eb57 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -107,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = new Vector2(420, 240), Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(new Vector2(-100, 0)) }), } @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = playfield_centre, Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5)) }), } @@ -149,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = playfield_centre, Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(new Vector2(0, -playfield_centre.Y + 5)) }), StackHeight = 5 @@ -171,7 +171,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = new Vector2(0, 0), Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(playfield_centre) }), } @@ -192,7 +192,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Position = playfield_centre, Path = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), new PathControlPoint(-playfield_centre) }), } @@ -214,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Path = new SliderPath(new[] { // Circular arc shoots over the top of the screen. - new PathControlPoint(new Vector2(0, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(0, 0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(-100, -200)), new PathControlPoint(new Vector2(100, -200)) }), diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs index 8d8386cae1..3d35ab79f7 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneObjectMerging.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor mergeSelection(); AddAssert("slider created", () => circle1 is not null && circle2 is not null && sliderCreatedFor( - (pos: circle1.Position, pathType: PathType.Linear), + (pos: circle1.Position, pathType: PathType.LINEAR), (pos: circle2.Position, pathType: null))); AddStep("undo", () => Editor.Undo()); @@ -73,11 +73,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor var controlPoints = slider.Path.ControlPoints; (Vector2, PathType?)[] args = new (Vector2, PathType?)[controlPoints.Count + 2]; - args[0] = (circle1.Position, PathType.Linear); + args[0] = (circle1.Position, PathType.LINEAR); for (int i = 0; i < controlPoints.Count; i++) { - args[i + 1] = (controlPoints[i].Position + slider.Position, i == controlPoints.Count - 1 ? PathType.Linear : controlPoints[i].Type); + args[i + 1] = (controlPoints[i].Position + slider.Position, i == controlPoints.Count - 1 ? PathType.LINEAR : controlPoints[i].Type); } args[^1] = (circle2.Position, null); @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor mergeSelection(); AddAssert("slider created", () => circle1 is not null && circle2 is not null && sliderCreatedFor( - (pos: circle1.Position, pathType: PathType.Linear), + (pos: circle1.Position, pathType: PathType.LINEAR), (pos: circle2.Position, pathType: null))); AddAssert("samples exist", sliderSampleExist); @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor mergeSelection(); AddAssert("slider created", () => circle1 is not null && circle2 is not null && sliderCreatedFor( - (pos: circle1.Position, pathType: PathType.Linear), + (pos: circle1.Position, pathType: PathType.LINEAR), (pos: circle2.Position, pathType: null))); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs index 37a109de18..7ea4d40b90 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(-100, 0)), new PathControlPoint(new Vector2(100, 20)) }; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index c267cd1f63..16800997f4 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Bezier); + addControlPointStep(new Vector2(200), PathType.BEZIER); addControlPointStep(new Vector2(300)); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200)); @@ -63,9 +63,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true); addContextMenuItemStep("Perfect curve"); - assertControlPointPathType(0, PathType.Bezier); - assertControlPointPathType(1, PathType.PerfectCurve); - assertControlPointPathType(3, PathType.Bezier); + assertControlPointPathType(0, PathType.BEZIER); + assertControlPointPathType(1, PathType.PERFECTCURVE); + assertControlPointPathType(3, PathType.BEZIER); } [Test] @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Bezier); + addControlPointStep(new Vector2(200), PathType.BEZIER); addControlPointStep(new Vector2(300)); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200)); @@ -83,8 +83,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("select control point", () => visualiser.Pieces[2].IsSelected.Value = true); addContextMenuItemStep("Perfect curve"); - assertControlPointPathType(0, PathType.Bezier); - assertControlPointPathType(2, PathType.PerfectCurve); + assertControlPointPathType(0, PathType.BEZIER); + assertControlPointPathType(2, PathType.PERFECTCURVE); assertControlPointPathType(4, null); } @@ -93,7 +93,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Bezier); + addControlPointStep(new Vector2(200), PathType.BEZIER); addControlPointStep(new Vector2(300)); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200)); @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true); addContextMenuItemStep("Perfect curve"); - assertControlPointPathType(0, PathType.Bezier); + assertControlPointPathType(0, PathType.BEZIER); AddAssert("point 3 is not inherited", () => slider.Path.ControlPoints[3].Type != null); } @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Linear); + addControlPointStep(new Vector2(200), PathType.LINEAR); addControlPointStep(new Vector2(300)); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200)); @@ -123,9 +123,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("select control point", () => visualiser.Pieces[1].IsSelected.Value = true); addContextMenuItemStep("Perfect curve"); - assertControlPointPathType(0, PathType.Linear); - assertControlPointPathType(1, PathType.PerfectCurve); - assertControlPointPathType(3, PathType.Linear); + assertControlPointPathType(0, PathType.LINEAR); + assertControlPointPathType(1, PathType.PERFECTCURVE); + assertControlPointPathType(3, PathType.LINEAR); } [Test] @@ -133,18 +133,18 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { createVisualiser(true); - addControlPointStep(new Vector2(200), PathType.Bezier); - addControlPointStep(new Vector2(300), PathType.PerfectCurve); + addControlPointStep(new Vector2(200), PathType.BEZIER); + addControlPointStep(new Vector2(300), PathType.PERFECTCURVE); addControlPointStep(new Vector2(500, 300)); - addControlPointStep(new Vector2(700, 200), PathType.Bezier); + addControlPointStep(new Vector2(700, 200), PathType.BEZIER); addControlPointStep(new Vector2(500, 100)); moveMouseToControlPoint(3); AddStep("select control point", () => visualiser.Pieces[3].IsSelected.Value = true); addContextMenuItemStep("Inherit"); - assertControlPointPathType(0, PathType.Bezier); - assertControlPointPathType(1, PathType.Bezier); + assertControlPointPathType(0, PathType.BEZIER); + assertControlPointPathType(1, PathType.BEZIER); assertControlPointPathType(3, null); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs index 408205d6b2..1d8d2cf01a 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs @@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(256, 192), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(400, 0)), new PathControlPoint(new Vector2(400, 150)) }) @@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(1, new Vector2(150, 50)); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("three control point pieces selected", () => this.ChildrenOfType>().Count(piece => piece.IsSelected.Value) == 3); assertControlPointPosition(2, new Vector2(450, 50)); - assertControlPointType(2, PathType.PerfectCurve); + assertControlPointType(2, PathType.PERFECTCURVE); assertControlPointPosition(3, new Vector2(550, 50)); @@ -249,7 +249,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider moved", () => Precision.AlmostEquals(slider.Position, new Vector2(256, 192) + new Vector2(150, 50))); assertControlPointPosition(0, Vector2.Zero); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); assertControlPointPosition(1, new Vector2(0, 100)); @@ -272,7 +272,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(1, new Vector2(400, 0.01f)); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -282,13 +282,13 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("hold", () => InputManager.PressButton(MouseButton.Left)); addMovementStep(new Vector2(400, 0.01f)); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); addMovementStep(new Vector2(150, 50)); AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(1, new Vector2(150, 50)); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -298,32 +298,32 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("hold", () => InputManager.PressButton(MouseButton.Left)); addMovementStep(new Vector2(350, 0.01f)); - assertControlPointType(2, PathType.Bezier); + assertControlPointType(2, PathType.BEZIER); addMovementStep(new Vector2(150, 150)); AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(4, new Vector2(150, 150)); - assertControlPointType(2, PathType.PerfectCurve); + assertControlPointType(2, PathType.PERFECTCURVE); } [Test] public void TestDragControlPointPathAfterChangingType() { - AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.Bezier); + AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.BEZIER); AddStep("add point", () => slider.Path.ControlPoints.Add(new PathControlPoint(new Vector2(500, 10)))); - AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PerfectCurve); + AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PERFECTCURVE); moveMouseToControlPoint(4); AddStep("hold", () => InputManager.PressButton(MouseButton.Left)); - assertControlPointType(3, PathType.PerfectCurve); + assertControlPointType(3, PathType.PERFECTCURVE); addMovementStep(new Vector2(350, 0.01f)); AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(4, new Vector2(350, 0.01f)); - assertControlPointType(3, PathType.Bezier); + assertControlPointType(3, PathType.BEZIER); } private void addMovementStep(Vector2 relativePosition) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs index 77e828e80a..38ebeb7e8f 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.Linear), + new PathControlPoint(new Vector2(0), PathType.LINEAR), new PathControlPoint(new Vector2(100, 0)), }; @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.Linear), + new PathControlPoint(new Vector2(0), PathType.LINEAR), new PathControlPoint(new Vector2(100, 0)), }; @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(100, 0)), new PathControlPoint(new Vector2(0, 10)) }; @@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.Linear), + new PathControlPoint(new Vector2(0), PathType.LINEAR), new PathControlPoint(new Vector2(0, 50)), new PathControlPoint(new Vector2(0, 100)) }; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 7d29670daa..4b120c1a3f 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -58,7 +58,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertLength(200); assertControlPointCount(2); - assertControlPointType(0, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); } [Test] @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(2); - assertControlPointType(0, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); } [Test] @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); assertControlPointPosition(1, new Vector2(100, 0)); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -112,7 +112,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointCount(4); assertControlPointPosition(1, new Vector2(100, 0)); assertControlPointPosition(2, new Vector2(100, 100)); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -131,8 +131,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); assertControlPointPosition(1, new Vector2(100, 0)); - assertControlPointType(0, PathType.Linear); - assertControlPointType(1, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); + assertControlPointType(1, PathType.LINEAR); } [Test] @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(2); - assertControlPointType(0, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); assertLength(100); } @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -196,7 +196,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(4); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -216,8 +216,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointCount(3); assertControlPointPosition(1, new Vector2(100, 0)); assertControlPointPosition(2, new Vector2(100)); - assertControlPointType(0, PathType.Linear); - assertControlPointType(1, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); + assertControlPointType(1, PathType.LINEAR); } [Test] @@ -240,8 +240,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointCount(4); assertControlPointPosition(1, new Vector2(100, 0)); assertControlPointPosition(2, new Vector2(100)); - assertControlPointType(0, PathType.Linear); - assertControlPointType(1, PathType.PerfectCurve); + assertControlPointType(0, PathType.LINEAR); + assertControlPointType(1, PathType.PERFECTCURVE); } [Test] @@ -269,8 +269,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointPosition(2, new Vector2(100)); assertControlPointPosition(3, new Vector2(200, 100)); assertControlPointPosition(4, new Vector2(200)); - assertControlPointType(0, PathType.PerfectCurve); - assertControlPointType(2, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(2, PathType.PERFECTCURVE); } [Test] @@ -287,7 +287,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertLength(200); assertControlPointCount(2); - assertControlPointType(0, PathType.Linear); + assertControlPointType(0, PathType.LINEAR); } [Test] @@ -306,7 +306,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -326,7 +326,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -347,7 +347,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } [Test] @@ -368,7 +368,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.Bezier); + assertControlPointType(0, PathType.BEZIER); } [Test] @@ -385,7 +385,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PerfectCurve); + assertControlPointType(0, PathType.PERFECTCURVE); } private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position))); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs index 9c5eb83e3c..0ddfc40946 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs @@ -22,12 +22,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private readonly PathControlPoint[][] paths = { createPathSegment( - PathType.PerfectCurve, + PathType.PERFECTCURVE, new Vector2(200, -50), new Vector2(250, 0) ), createPathSegment( - PathType.Linear, + PathType.LINEAR, new Vector2(100, 0), new Vector2(100, 100) ) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs index 413a3c3dfd..d4d99e1019 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSelectionBlueprint.cs @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor slider = new Slider { Position = new Vector2(256, 192), - Path = new SliderPath(PathType.Bezier, new[] + Path = new SliderPath(PathType.BEZIER, new[] { Vector2.Zero, new Vector2(150, 150), diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index 0ae14bdde8..c984d9168e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { ControlPoints = { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(136, 205)), new PathControlPoint(new Vector2(-4, 226)) } @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { OsuSelectionHandler selectionHandler; - AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); AddStep("rotate 90 degrees ccw", () => @@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor selectionHandler.HandleRotation(-90); }); - AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); } [Test] @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { OsuSelectionHandler selectionHandler; - AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); AddStep("flip slider horizontally", () => @@ -232,7 +232,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor selectionHandler.OnPressed(new KeyBindingPressEvent(InputManager.CurrentState, GlobalAction.EditorFlipVertically)); }); - AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PerfectCurve); + AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); } [Test] diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs index ad37258c9b..cded9165f4 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs @@ -45,9 +45,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(0, 50), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(400, 0)), new PathControlPoint(new Vector2(400, 150)) }) @@ -73,20 +73,20 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 2 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap, - (new Vector2(0, 50), PathType.PerfectCurve), + (new Vector2(0, 50), PathType.PERFECTCURVE), (new Vector2(150, 200), null), (new Vector2(300, 50), null) ) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], slider.StartTime, endTime + split_gap, - (new Vector2(300, 50), PathType.PerfectCurve), + (new Vector2(300, 50), PathType.PERFECTCURVE), (new Vector2(400, 50), null), (new Vector2(400, 200), null) )); AddStep("undo", () => Editor.Undo()); AddAssert("original slider restored", () => EditorBeatmap.HitObjects.Count == 1 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, endTime, - (new Vector2(0, 50), PathType.PerfectCurve), + (new Vector2(0, 50), PathType.PERFECTCURVE), (new Vector2(150, 200), null), - (new Vector2(300, 50), PathType.PerfectCurve), + (new Vector2(300, 50), PathType.PERFECTCURVE), (new Vector2(400, 50), null), (new Vector2(400, 200), null) )); @@ -104,11 +104,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(0, 50), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.Bezier), + new PathControlPoint(new Vector2(300, 0), PathType.BEZIER), new PathControlPoint(new Vector2(400, 0)), - new PathControlPoint(new Vector2(400, 150), PathType.Catmull), + new PathControlPoint(new Vector2(400, 150), PathType.CATMULL), new PathControlPoint(new Vector2(300, 200)), new PathControlPoint(new Vector2(400, 250)) }) @@ -139,15 +139,15 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 3 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap, - (new Vector2(0, 50), PathType.PerfectCurve), + (new Vector2(0, 50), PathType.PERFECTCURVE), (new Vector2(150, 200), null), (new Vector2(300, 50), null) ) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], EditorBeatmap.HitObjects[0].GetEndTime() + split_gap, slider.StartTime - split_gap, - (new Vector2(300, 50), PathType.Bezier), + (new Vector2(300, 50), PathType.BEZIER), (new Vector2(400, 50), null), (new Vector2(400, 200), null) ) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[2], EditorBeatmap.HitObjects[1].GetEndTime() + split_gap, endTime + split_gap * 2, - (new Vector2(400, 200), PathType.Catmull), + (new Vector2(400, 200), PathType.CATMULL), (new Vector2(300, 250), null), (new Vector2(400, 300), null) )); @@ -165,9 +165,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(0, 50), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PerfectCurve), + new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.PerfectCurve), + new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE), new PathControlPoint(new Vector2(400, 0)), new PathControlPoint(new Vector2(400, 150)) }) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSliderScaling.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSliderScaling.cs index 64d23090d0..021fdba225 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSliderScaling.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSliderScaling.cs @@ -43,7 +43,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.Linear), + new PathControlPoint(new Vector2(0), PathType.LINEAR), new PathControlPoint(new Vector2(100, 0)), }; diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs index 3f84ac6935..58bdd805c1 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModHidden.cs @@ -81,12 +81,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods new Slider { StartTime = 3200, - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), }) + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), }) }, new Slider { StartTime = 5200, - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), }) + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), }) } } }, @@ -105,12 +105,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods new Slider { StartTime = 1000, - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), }) + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), }) }, new Slider { StartTime = 4000, - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), }) + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), }) }, } }, @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { StartTime = 3000, Position = new Vector2(156, 242), - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(200, 0), }) + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(200, 0), }) }, new Spinner { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs index f0496efc19..26c4133bc4 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods var slider = new Slider { StartTime = 1000, - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(100, 0), }) + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), }) }; CreateHitObjectTest(new HitObjectTestData(slider), shouldMiss); diff --git a/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs index daa914cac2..d78c32aa6a 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuHitObjectGenerationUtilsTest.cs @@ -26,9 +26,9 @@ namespace osu.Game.Rulesets.Osu.Tests { ControlPoints = { - new PathControlPoint(new Vector2(), PathType.Linear), - new PathControlPoint(new Vector2(-64, -128), PathType.Linear), // absolute position: (64, 0) - new PathControlPoint(new Vector2(-128, 0), PathType.Linear) // absolute position: (0, 128) + new PathControlPoint(new Vector2(), PathType.LINEAR), + new PathControlPoint(new Vector2(-64, -128), PathType.LINEAR), // absolute position: (64, 0) + new PathControlPoint(new Vector2(-128, 0), PathType.LINEAR) // absolute position: (0, 128) } }, RepeatCount = 1 diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs index 483155e646..7824f26251 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneHitCircleLateFade.cs @@ -167,7 +167,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + 500, Position = new Vector2(250), - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(0, 100), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs index fa6aa580a3..e460da9bd5 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneLegacyHitPolicy.cs @@ -264,7 +264,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_slider, Position = positionSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(50, 0), @@ -308,7 +308,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_slider, Position = positionSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(50, 0), @@ -391,7 +391,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_slider, Position = positionSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -428,7 +428,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_first_slider, Position = positionFirstSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -438,7 +438,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_second_slider, Position = positionSecondSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -521,7 +521,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_first_slider, Position = positionFirstSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -531,7 +531,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_second_slider, Position = positionSecondSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -571,7 +571,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_first_slider, Position = positionFirstSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -581,7 +581,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_second_slider, Position = positionSecondSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index b805e7ed63..60003e7950 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -219,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(239, 176), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(154, 28), @@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Osu.Tests SliderVelocityMultiplier = speedMultiplier, StartTime = Time.Current + time_offset, Position = new Vector2(0, -(distance / 2)), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(0, distance), @@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(-max_length / 2, 0), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(max_length / 2, max_length / 2), @@ -293,7 +293,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(-max_length / 2, 0), - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(max_length * 0.375f, max_length * 0.18f), @@ -316,7 +316,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(-max_length / 2, 0), - Path = new SliderPath(PathType.Bezier, new[] + Path = new SliderPath(PathType.BEZIER, new[] { Vector2.Zero, new Vector2(max_length * 0.375f, max_length * 0.18f), @@ -338,7 +338,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(0, 0), - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(-max_length / 2, 0), @@ -365,7 +365,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(-max_length / 4, 0), - Path = new SliderPath(PathType.Catmull, new[] + Path = new SliderPath(PathType.CATMULL, new[] { Vector2.Zero, new Vector2(max_length * 0.125f, max_length * 0.125f), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index 88b70a8836..f41dd913ab 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -37,7 +37,7 @@ namespace osu.Game.Rulesets.Osu.Tests Position = new Vector2(256, 192), IndexInCurrentCombo = 0, StartTime = Time.Current, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(150, 100), @@ -52,7 +52,7 @@ namespace osu.Game.Rulesets.Osu.Tests Position = new Vector2(256, 192), ComboIndex = 1, StartTime = dho.HitObject.StartTime, - Path = new SliderPath(PathType.Bezier, new[] + Path = new SliderPath(PathType.BEZIER, new[] { Vector2.Zero, new Vector2(150, 100), @@ -80,7 +80,7 @@ namespace osu.Game.Rulesets.Osu.Tests Position = new Vector2(256, 192), IndexInCurrentCombo = 0, StartTime = Time.Current, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(150, 100), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs index d4bb789a12..fc9bb16cb7 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderFollowCircleInput.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = time_slider_start, Position = new Vector2(0, 0), SliderVelocityMultiplier = velocity, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(followCircleRadius, 0), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index f718a5069f..08836ef819 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -62,7 +62,7 @@ namespace osu.Game.Rulesets.Osu.Tests Position = new Vector2(0, 0), SliderVelocityMultiplier = 10f, RepeatCount = repeatCount, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(sliderLength, 0), @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Tests Position = new Vector2(0, 0), SliderVelocityMultiplier = 10f, RepeatCount = repeatCount, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(sliderLength, 0), @@ -145,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = time_slider_start, Position = new Vector2(0, 0), SliderVelocityMultiplier = 10f, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(slider_path_length * 10, 0), @@ -478,7 +478,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = time_slider_start, Position = new Vector2(0, 0), SliderVelocityMultiplier = 0.1f, - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(slider_path_length, 0), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs index 13166c2b6b..ebc5143aed 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs @@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = 3000, Position = new Vector2(100, 100), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(300, 200) @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = 13000, Position = new Vector2(100, 100), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(300, 200) @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = 23000, Position = new Vector2(100, 100), - Path = new SliderPath(PathType.PerfectCurve, new[] + Path = new SliderPath(PathType.PERFECTCURVE, new[] { Vector2.Zero, new Vector2(300, 200) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs index 3475680c71..895e9bbdee 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneStartTimeOrderedHitPolicy.cs @@ -196,7 +196,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_slider, Position = positionSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_slider, Position = positionSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -318,7 +318,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_slider, Position = positionSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -352,7 +352,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_first_slider, Position = positionFirstSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), @@ -362,7 +362,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = time_second_slider, Position = positionSecondSlider, - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(25, 0), diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 12e5ca0236..9658e5f6c3 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -221,11 +221,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updatePathType() { - if (ControlPoint.Type != PathType.PerfectCurve) + if (ControlPoint.Type != PathType.PERFECTCURVE) return; if (PointsInSegment.Count > 3) - ControlPoint.Type = PathType.Bezier; + ControlPoint.Type = PathType.BEZIER; if (PointsInSegment.Count != 3) return; @@ -233,7 +233,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components ReadOnlySpan points = PointsInSegment.Select(p => p.Position).ToArray(); RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points); if (boundingBox.Width >= 640 || boundingBox.Height >= 480) - ControlPoint.Type = PathType.Bezier; + ControlPoint.Type = PathType.BEZIER; } /// @@ -256,18 +256,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private Color4 getColourFromNodeType() { - if (!(ControlPoint.Type is PathType pathType)) + if (ControlPoint.Type is not PathType pathType) return colours.Yellow; switch (pathType) { - case PathType.Catmull: + case { SplineType: SplineType.Catmull }: return colours.SeaFoam; - case PathType.Bezier: - return colours.Pink; + case { SplineType: SplineType.BSpline, Degree: null }: + return colours.PinkLighter; - case PathType.PerfectCurve: + case { SplineType: SplineType.BSpline, Degree: >= 1 }: + int idx = Math.Clamp(pathType.Degree.Value, 0, 3); + return new[] { colours.PinkDarker, colours.PinkDark, colours.Pink, colours.PinkLight }[idx]; + + case { SplineType: SplineType.PerfectCurve }: return colours.PurpleDark; default: diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index f891d23bbd..b5c9016538 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -242,18 +242,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint); - switch (type) + if (type.HasValue && type.Value.SplineType == SplineType.PerfectCurve) { - case PathType.PerfectCurve: - // Can't always create a circular arc out of 4 or more points, - // so we split the segment into one 3-point circular arc segment - // and one segment of the previous type. - int thirdPointIndex = indexInSegment + 2; + // Can't always create a circular arc out of 4 or more points, + // so we split the segment into one 3-point circular arc segment + // and one segment of the previous type. + int thirdPointIndex = indexInSegment + 2; - if (piece.PointsInSegment.Count > thirdPointIndex + 1) - piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type; - - break; + if (piece.PointsInSegment.Count > thirdPointIndex + 1) + piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type; } hitObject.Path.ExpectedDistance.Value = null; @@ -370,10 +367,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components curveTypeItems.Add(createMenuItemForPathType(null)); // todo: hide/disable items which aren't valid for selected points - curveTypeItems.Add(createMenuItemForPathType(PathType.Linear)); - curveTypeItems.Add(createMenuItemForPathType(PathType.PerfectCurve)); - curveTypeItems.Add(createMenuItemForPathType(PathType.Bezier)); - curveTypeItems.Add(createMenuItemForPathType(PathType.Catmull)); + curveTypeItems.Add(createMenuItemForPathType(PathType.LINEAR)); + curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECTCURVE)); + curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER)); + curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3))); + curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); var menuItems = new List { diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 9b6adc04cf..8f0a2ee781 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { RelativeSizeAxes = Axes.Both; - HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.Linear)); + HitObject.Path.ControlPoints.Add(segmentStart = new PathControlPoint(Vector2.Zero, PathType.LINEAR)); currentSegmentLength = 1; } @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders Debug.Assert(lastPoint != null); segmentStart = lastPoint; - segmentStart.Type = PathType.Linear; + segmentStart.Type = PathType.LINEAR; currentSegmentLength = 1; } @@ -173,15 +173,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { case 1: case 2: - segmentStart.Type = PathType.Linear; + segmentStart.Type = PathType.LINEAR; break; case 3: - segmentStart.Type = PathType.PerfectCurve; + segmentStart.Type = PathType.PERFECTCURVE; break; default: - segmentStart.Type = PathType.Bezier; + segmentStart.Type = PathType.BEZIER; break; } } @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { HitObject.Path.ControlPoints.Add(cursor = new PathControlPoint { Position = Vector2.Zero }); - // The path type should be adjusted in the progression of updatePathType() (Linear -> PC -> Bezier). + // The path type should be adjusted in the progression of updatePathType() (LINEAR -> PC -> BEZIER). currentSegmentLength++; updatePathType(); } @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders HitObject.Path.ControlPoints.Remove(cursor); cursor = null; - // The path type should be adjusted in the reverse progression of updatePathType() (Bezier -> PC -> Linear). + // The path type should be adjusted in the reverse progression of updatePathType() (BEZIER -> PC -> LINEAR). currentSegmentLength--; updatePathType(); } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs index e81941d254..b972f09136 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSelectionHandler.cs @@ -320,7 +320,7 @@ namespace osu.Game.Rulesets.Osu.Edit if (mergedHitObject.Path.ControlPoints.Count == 0) { - mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(Vector2.Zero, PathType.Linear)); + mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(Vector2.Zero, PathType.LINEAR)); } // Merge all the selected hit objects into one slider path. @@ -350,7 +350,7 @@ namespace osu.Game.Rulesets.Osu.Edit // Turn the last control point into a linear type if this is the first merging circle in a sequence, so the subsequent control points can be inherited path type. if (!lastCircle) { - mergedHitObject.Path.ControlPoints.Last().Type = PathType.Linear; + mergedHitObject.Path.ControlPoints.Last().Type = PathType.LINEAR; } mergedHitObject.Path.ControlPoints.Add(new PathControlPoint(selectedMergeableObject.Position - mergedHitObject.Position)); diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 5f47d486e6..2a76782a08 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Taiko.Objects double IHasDistance.Distance => Duration * Velocity; SliderPath IHasPath.Path - => new SliderPath(PathType.Linear, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER); + => new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER); #endregion } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 66151a51e6..18c21046fb 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -663,7 +663,7 @@ namespace osu.Game.Tests.Beatmaps.Formats assertObjectHasBanks(hitObjects[9], HitSampleInfo.BANK_DRUM, HitSampleInfo.BANK_NORMAL); } - void assertObjectHasBanks(HitObject hitObject, string normalBank, string? additionsBank = null) + static void assertObjectHasBanks(HitObject hitObject, string normalBank, string? additionsBank = null) { Assert.AreEqual(normalBank, hitObject.Samples[0].Bank); @@ -808,14 +808,14 @@ namespace osu.Game.Tests.Beatmaps.Formats var first = ((IHasPath)decoded.HitObjects[0]).Path; Assert.That(first.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve)); + Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECTCURVE)); Assert.That(first.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); Assert.That(first.ControlPoints[1].Type, Is.EqualTo(null)); // ReSharper disable once HeuristicUnreachableCode // weird one, see https://youtrack.jetbrains.com/issue/RIDER-70159. Assert.That(first.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3))); - Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(first.ControlPoints[2].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(first.ControlPoints[3].Position, Is.EqualTo(new Vector2(68, 15))); Assert.That(first.ControlPoints[3].Type, Is.EqualTo(null)); Assert.That(first.ControlPoints[4].Position, Is.EqualTo(new Vector2(259, -132))); @@ -827,7 +827,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var second = ((IHasPath)decoded.HitObjects[1]).Path; Assert.That(second.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PerfectCurve)); + Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECTCURVE)); Assert.That(second.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); Assert.That(second.ControlPoints[1].Type, Is.EqualTo(null)); Assert.That(second.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3))); @@ -837,14 +837,14 @@ namespace osu.Game.Tests.Beatmaps.Formats var third = ((IHasPath)decoded.HitObjects[2]).Path; Assert.That(third.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(third.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(third.ControlPoints[1].Position, Is.EqualTo(new Vector2(0, 192))); Assert.That(third.ControlPoints[1].Type, Is.EqualTo(null)); Assert.That(third.ControlPoints[2].Position, Is.EqualTo(new Vector2(224, 192))); Assert.That(third.ControlPoints[2].Type, Is.EqualTo(null)); Assert.That(third.ControlPoints[3].Position, Is.EqualTo(new Vector2(224, 0))); - Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(third.ControlPoints[3].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(third.ControlPoints[4].Position, Is.EqualTo(new Vector2(224, -192))); Assert.That(third.ControlPoints[4].Type, Is.EqualTo(null)); Assert.That(third.ControlPoints[5].Position, Is.EqualTo(new Vector2(480, -192))); @@ -856,7 +856,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var fourth = ((IHasPath)decoded.HitObjects[3]).Path; Assert.That(fourth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fourth.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(fourth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1))); Assert.That(fourth.ControlPoints[1].Type, Is.EqualTo(null)); Assert.That(fourth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2))); @@ -870,7 +870,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var fifth = ((IHasPath)decoded.HitObjects[4]).Path; Assert.That(fifth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fifth.ControlPoints[0].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(fifth.ControlPoints[1].Position, Is.EqualTo(new Vector2(1, 1))); Assert.That(fifth.ControlPoints[1].Type, Is.EqualTo(null)); Assert.That(fifth.ControlPoints[2].Position, Is.EqualTo(new Vector2(2, 2))); @@ -881,7 +881,7 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(fifth.ControlPoints[4].Type, Is.EqualTo(null)); Assert.That(fifth.ControlPoints[5].Position, Is.EqualTo(new Vector2(4, 4))); - Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.Bezier)); + Assert.That(fifth.ControlPoints[5].Type, Is.EqualTo(PathType.BEZIER)); Assert.That(fifth.ControlPoints[6].Position, Is.EqualTo(new Vector2(5, 5))); Assert.That(fifth.ControlPoints[6].Type, Is.EqualTo(null)); @@ -889,12 +889,12 @@ namespace osu.Game.Tests.Beatmaps.Formats var sixth = ((IHasPath)decoded.HitObjects[5]).Path; Assert.That(sixth.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(sixth.ControlPoints[0].Type == PathType.Bezier); + Assert.That(sixth.ControlPoints[0].Type == PathType.BEZIER); Assert.That(sixth.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145))); Assert.That(sixth.ControlPoints[1].Type == null); Assert.That(sixth.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75))); - Assert.That(sixth.ControlPoints[2].Type == PathType.Bezier); + Assert.That(sixth.ControlPoints[2].Type == PathType.BEZIER); Assert.That(sixth.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145))); Assert.That(sixth.ControlPoints[3].Type == null); Assert.That(sixth.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20))); @@ -904,12 +904,12 @@ namespace osu.Game.Tests.Beatmaps.Formats var seventh = ((IHasPath)decoded.HitObjects[6]).Path; Assert.That(seventh.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(seventh.ControlPoints[0].Type == PathType.PerfectCurve); + Assert.That(seventh.ControlPoints[0].Type == PathType.PERFECTCURVE); Assert.That(seventh.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145))); Assert.That(seventh.ControlPoints[1].Type == null); Assert.That(seventh.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75))); - Assert.That(seventh.ControlPoints[2].Type == PathType.PerfectCurve); + Assert.That(seventh.ControlPoints[2].Type == PathType.PERFECTCURVE); Assert.That(seventh.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145))); Assert.That(seventh.ControlPoints[3].Type == null); Assert.That(seventh.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20))); @@ -1016,7 +1016,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints; Assert.That(controlPoints.Count, Is.EqualTo(6)); - Assert.That(controlPoints.Single(c => c.Type != null).Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints.Single(c => c.Type != null).Type, Is.EqualTo(PathType.CATMULL)); } } @@ -1032,9 +1032,9 @@ namespace osu.Game.Tests.Beatmaps.Formats var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints; Assert.That(controlPoints.Count, Is.EqualTo(4)); - Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull)); - Assert.That(controlPoints[1].Type, Is.EqualTo(PathType.Catmull)); - Assert.That(controlPoints[2].Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.CATMULL)); + Assert.That(controlPoints[1].Type, Is.EqualTo(PathType.CATMULL)); + Assert.That(controlPoints[2].Type, Is.EqualTo(PathType.CATMULL)); Assert.That(controlPoints[3].Type, Is.Null); } } @@ -1051,7 +1051,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var controlPoints = ((IHasPath)decoded.HitObjects[0]).Path.ControlPoints; Assert.That(controlPoints.Count, Is.EqualTo(4)); - Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.Catmull)); + Assert.That(controlPoints[0].Type, Is.EqualTo(PathType.CATMULL)); Assert.That(controlPoints[0].Position, Is.EqualTo(Vector2.Zero)); Assert.That(controlPoints[1].Type, Is.Null); Assert.That(controlPoints[1].Position, Is.Not.EqualTo(Vector2.Zero)); diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 5d9049ead7..db50273f27 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Beatmaps.Formats compareBeatmaps(decoded, decodedAfterEncode); - ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo) + static ControlPointInfo removeLegacyControlPointTypes(ControlPointInfo controlPointInfo) { // emulate non-legacy control points by cloning the non-legacy portion. // the assertion is that the encoder can recreate this losslessly from hitobject data. @@ -125,10 +125,10 @@ namespace osu.Game.Tests.Beatmaps.Formats Position = new Vector2(0.6f), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.Bezier), + new PathControlPoint(Vector2.Zero, PathType.BEZIER), new PathControlPoint(new Vector2(0.5f)), new PathControlPoint(new Vector2(0.51f)), // This is actually on the same position as the previous one in legacy beatmaps (truncated to int). - new PathControlPoint(new Vector2(1f), PathType.Bezier), + new PathControlPoint(new Vector2(1f), PathType.BEZIER), new PathControlPoint(new Vector2(2f)) }) }, diff --git a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs index 5af0366e6e..21d8a165ff 100644 --- a/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs +++ b/osu.Game.Tests/Editing/LegacyEditorBeatmapPatcherTest.cs @@ -162,7 +162,7 @@ namespace osu.Game.Tests.Editing { new PathControlPoint(Vector2.Zero), new PathControlPoint(Vector2.One), - new PathControlPoint(new Vector2(2), PathType.Bezier), + new PathControlPoint(new Vector2(2), PathType.BEZIER), new PathControlPoint(new Vector2(3)), }, 50) }, @@ -179,7 +179,7 @@ namespace osu.Game.Tests.Editing StartTime = 2000, Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.Bezier), + new PathControlPoint(Vector2.Zero, PathType.BEZIER), new PathControlPoint(new Vector2(4)), new PathControlPoint(new Vector2(5)), }, 100) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs index c4c05278b5..a766b253aa 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorClipboard.cs @@ -72,7 +72,7 @@ namespace osu.Game.Tests.Visual.Editing ControlPoints = { new PathControlPoint(), - new PathControlPoint(new Vector2(100, 0), PathType.Bezier) + new PathControlPoint(new Vector2(100, 0), PathType.BEZIER) } } }; diff --git a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs index ed3bffe5c2..f392841ac7 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneHitObjectComposer.cs @@ -57,7 +57,7 @@ namespace osu.Game.Tests.Visual.Editing new Slider { Position = new Vector2(128, 256), - Path = new SliderPath(PathType.Linear, new[] + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(216, 0), diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs index a40eab5948..5eb82ccbdc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs @@ -114,23 +114,25 @@ namespace osu.Game.Tests.Visual.Gameplay { } - [TestCase(PathType.Linear)] - [TestCase(PathType.Bezier)] - [TestCase(PathType.Catmull)] - [TestCase(PathType.PerfectCurve)] - public void TestSingleSegment(PathType type) - => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + [TestCase(SplineType.Linear, null)] + [TestCase(SplineType.BSpline, null)] + [TestCase(SplineType.BSpline, 3)] + [TestCase(SplineType.Catmull, null)] + [TestCase(SplineType.PerfectCurve, null)] + public void TestSingleSegment(SplineType splineType, int? degree) + => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); - [TestCase(PathType.Linear)] - [TestCase(PathType.Bezier)] - [TestCase(PathType.Catmull)] - [TestCase(PathType.PerfectCurve)] - public void TestMultipleSegment(PathType type) + [TestCase(SplineType.Linear, null)] + [TestCase(SplineType.BSpline, null)] + [TestCase(SplineType.BSpline, 3)] + [TestCase(SplineType.Catmull, null)] + [TestCase(SplineType.PerfectCurve, null)] + public void TestMultipleSegment(SplineType splineType, int? degree) { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero)); - path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); }); } @@ -139,9 +141,9 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100, 0))); - path.ControlPoints.AddRange(createSegment(PathType.Bezier, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100))); - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(100, 0))); + path.ControlPoints.AddRange(createSegment(PathType.BEZIER, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero)); }); } @@ -157,7 +159,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0))); }); } @@ -170,11 +172,11 @@ namespace osu.Game.Tests.Visual.Gameplay switch (points) { case 2: - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100))); break; case 4: - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); break; } }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs index 0f16d3f394..3cbd5eefac 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneGameplaySampleTriggerSource.cs @@ -88,7 +88,7 @@ namespace osu.Game.Tests.Visual.Gameplay { HitWindows = new HitWindows(), StartTime = t += spacing, - Path = new SliderPath(PathType.Linear, new[] { Vector2.Zero, Vector2.UnitY * 200 }), + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, Vector2.UnitY * 200 }), Samples = new[] { new HitSampleInfo(HitSampleInfo.HIT_WHISTLE, HitSampleInfo.BANK_SOFT) }, }, }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index 635d9f9604..e4d99f6741 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -52,59 +52,68 @@ namespace osu.Game.Tests.Visual.Gameplay { } - [TestCase(PathType.Linear)] - [TestCase(PathType.Bezier)] - [TestCase(PathType.Catmull)] - [TestCase(PathType.PerfectCurve)] - public void TestSingleSegment(PathType type) - => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(type, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + [TestCase(SplineType.Linear, null)] + [TestCase(SplineType.BSpline, null)] + [TestCase(SplineType.BSpline, 3)] + [TestCase(SplineType.Catmull, null)] + [TestCase(SplineType.PerfectCurve, null)] + public void TestSingleSegment(SplineType splineType, int? degree) + => AddStep("create path", () => path.ControlPoints.AddRange(createSegment( + new PathType { SplineType = splineType, Degree = degree }, + Vector2.Zero, + new Vector2(0, 100), + new Vector2(100), + new Vector2(0, 200), + new Vector2(200) + ))); - [TestCase(PathType.Linear)] - [TestCase(PathType.Bezier)] - [TestCase(PathType.Catmull)] - [TestCase(PathType.PerfectCurve)] - public void TestMultipleSegment(PathType type) + [TestCase(SplineType.Linear, null)] + [TestCase(SplineType.BSpline, null)] + [TestCase(SplineType.BSpline, 3)] + [TestCase(SplineType.Catmull, null)] + [TestCase(SplineType.PerfectCurve, null)] + public void TestMultipleSegment(SplineType splineType, int? degree) { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero)); - path.ControlPoints.AddRange(createSegment(type, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); }); } [Test] public void TestAddControlPoint() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100)))); AddStep("add point", () => path.ControlPoints.Add(new PathControlPoint { Position = new Vector2(100) })); } [Test] public void TestInsertControlPoint() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(100)))); AddStep("insert point", () => path.ControlPoints.Insert(1, new PathControlPoint { Position = new Vector2(0, 100) })); } [Test] public void TestRemoveControlPoint() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("remove second point", () => path.ControlPoints.RemoveAt(1)); } [Test] public void TestChangePathType() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); - AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.Bezier); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("change type to bezier", () => path.ControlPoints[0].Type = PathType.BEZIER); } [Test] public void TestAddSegmentByChangingType() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)))); - AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.Bezier); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0)))); + AddStep("change second point type to bezier", () => path.ControlPoints[1].Type = PathType.BEZIER); } [Test] @@ -112,8 +121,8 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); - path.ControlPoints[1].Type = PathType.Bezier; + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints[1].Type = PathType.BEZIER; }); AddStep("change second point type to null", () => path.ControlPoints[1].Type = null); @@ -124,8 +133,8 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); - path.ControlPoints[1].Type = PathType.Bezier; + path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints[1].Type = PathType.BEZIER; }); AddStep("remove second point", () => path.ControlPoints.RemoveAt(1)); @@ -140,11 +149,11 @@ namespace osu.Game.Tests.Visual.Gameplay switch (points) { case 2: - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100))); break; case 4: - path.ControlPoints.AddRange(createSegment(PathType.PerfectCurve, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); break; } }); @@ -153,35 +162,35 @@ namespace osu.Game.Tests.Visual.Gameplay [Test] public void TestLengthenLastSegment() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("lengthen last segment", () => path.ExpectedDistance.Value = 300); } [Test] public void TestShortenLastSegment() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("shorten last segment", () => path.ExpectedDistance.Value = 150); } [Test] public void TestShortenFirstSegment() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("shorten first segment", () => path.ExpectedDistance.Value = 50); } [Test] public void TestShortenToZeroLength() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("shorten to 0 length", () => path.ExpectedDistance.Value = 0); } [Test] public void TestShortenToNegativeLength() { - AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.Linear, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + AddStep("create path", () => path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); AddStep("shorten to -10 length", () => path.ExpectedDistance.Value = -10); } @@ -197,7 +206,7 @@ namespace osu.Game.Tests.Visual.Gameplay }; double[] distances = { 100d, 200d, 300d }; - AddStep("create path", () => path.ControlPoints.AddRange(positions.Select(p => new PathControlPoint(p, PathType.Linear)))); + AddStep("create path", () => path.ControlPoints.AddRange(positions.Select(p => new PathControlPoint(p, PathType.LINEAR)))); AddAssert("segment ends are correct", () => path.GetSegmentEnds(), () => Is.EqualTo(distances.Select(d => d / 300))); AddAssert("segment end positions recovered", () => path.GetSegmentEnds().Select(p => path.PositionAt(p)), () => Is.EqualTo(positions.Skip(1))); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 4f8e935ee4..7029f61459 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -437,7 +438,7 @@ namespace osu.Game.Beatmaps.Formats // Explicit segments have a new format in which the type is injected into the middle of the control point string. // To preserve compatibility with osu-stable as much as possible, explicit segments with the same type are converted to use implicit segments by duplicating the control point. // One exception are consecutive perfect curves, which aren't supported in osu!stable and can lead to decoding issues if encoded as implicit segments - bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PerfectCurve; + bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PERFECTCURVE; // Another exception to this is when the last two control points of the last segment were duplicated. This is not a scenario supported by osu!stable. // Lazer does not add implicit segments for the last two control points of _any_ explicit segment, so an explicit segment is forced in order to maintain consistency with the decoder. @@ -455,19 +456,23 @@ namespace osu.Game.Beatmaps.Formats { switch (point.Type) { - case PathType.Bezier: + case { SplineType: SplineType.BSpline, Degree: > 0 }: + writer.Write($"B{point.Type.Value.Degree}|"); + break; + + case { SplineType: SplineType.BSpline, Degree: <= 0 }: writer.Write("B|"); break; - case PathType.Catmull: + case { SplineType: SplineType.Catmull }: writer.Write("C|"); break; - case PathType.PerfectCurve: + case { SplineType: SplineType.PerfectCurve }: writer.Write("P|"); break; - case PathType.Linear: + case { SplineType: SplineType.Linear }: writer.Write("L|"); break; } diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index ece705f685..9ca12a79dd 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -78,10 +78,10 @@ namespace osu.Game.Database // wherein the last control point of an otherwise-single-segment slider path has a different type than previous, // which would lead to sliders being mangled when exported back to stable. // normally, that would be handled by the `BezierConverter.ConvertToModernBezier()` call below, - // which outputs a slider path containing only Bezier control points, + // which outputs a slider path containing only BEZIER control points, // but a non-inherited last control point is (rightly) not considered to be starting a new segment, // therefore it would fail to clear the `CountSegments() <= 1` check. - // by clearing explicitly we both fix the issue and avoid unnecessary conversions to Bezier. + // by clearing explicitly we both fix the issue and avoid unnecessary conversions to BEZIER. if (hasPath.Path.ControlPoints.Count > 1) hasPath.Path.ControlPoints[^1].Type = null; diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index 0c878fa1fd..74fbe7d8f9 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -68,26 +68,26 @@ namespace osu.Game.Rulesets.Objects // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = controlPoints[start].Type ?? PathType.Linear; + var segmentType = controlPoints[start].Type ?? PathType.LINEAR; switch (segmentType) { - case PathType.Catmull: + case { SplineType: SplineType.Catmull }: result.AddRange(from segment in ConvertCatmullToBezierAnchors(segmentVertices) from v in segment select v + position); - break; - case PathType.Linear: + case { SplineType: SplineType.Linear }: result.AddRange(from segment in ConvertLinearToBezierAnchors(segmentVertices) from v in segment select v + position); - break; - case PathType.PerfectCurve: + case { SplineType: SplineType.PerfectCurve }: result.AddRange(ConvertCircleToBezierAnchors(segmentVertices).Select(v => v + position)); - break; default: + if (segmentType.Degree != null) + throw new NotImplementedException("BSpline conversion of arbitrary degree is not implemented."); + foreach (Vector2 v in segmentVertices) { result.Add(v + position); @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Objects } /// - /// Converts a path of control points to an identical path using only Bezier type control points. + /// Converts a path of control points to an identical path using only BEZIER type control points. /// /// The control points of the path. /// The list of bezier control points. @@ -124,38 +124,38 @@ namespace osu.Game.Rulesets.Objects // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = controlPoints[start].Type ?? PathType.Linear; + var segmentType = controlPoints[start].Type ?? PathType.LINEAR; switch (segmentType) { - case PathType.Catmull: + case { SplineType: SplineType.Catmull }: foreach (var segment in ConvertCatmullToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) { - result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.Bezier : null)); + result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.BEZIER : null)); } } break; - case PathType.Linear: + case { SplineType: SplineType.Linear }: foreach (var segment in ConvertLinearToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) { - result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.Bezier : null)); + result.Add(new PathControlPoint(segment[j], j == 0 ? PathType.BEZIER : null)); } } break; - case PathType.PerfectCurve: + case { SplineType: SplineType.PerfectCurve }: var circleResult = ConvertCircleToBezierAnchors(segmentVertices); for (int j = 0; j < circleResult.Length - 1; j++) { - result.Add(new PathControlPoint(circleResult[j], j == 0 ? PathType.Bezier : null)); + result.Add(new PathControlPoint(circleResult[j], j == 0 ? PathType.BEZIER : null)); } break; @@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Objects default: for (int j = 0; j < segmentVertices.Length - 1; j++) { - result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? PathType.Bezier : null)); + result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? segmentType : null)); } break; diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index d20f2d31bb..30f4c092d9 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -224,16 +224,18 @@ namespace osu.Game.Rulesets.Objects.Legacy { default: case 'C': - return PathType.Catmull; + return new PathType(SplineType.Catmull); case 'B': - return PathType.Bezier; + if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0) + return new PathType { SplineType = SplineType.BSpline, Degree = degree }; + return new PathType(SplineType.BSpline); case 'L': - return PathType.Linear; + return new PathType(SplineType.Linear); case 'P': - return PathType.PerfectCurve; + return new PathType(SplineType.PerfectCurve); } } @@ -320,14 +322,14 @@ namespace osu.Game.Rulesets.Objects.Legacy readPoint(endPoint, offset, out vertices[^1]); // Edge-case rules (to match stable). - if (type == PathType.PerfectCurve) + if (type == PathType.PERFECTCURVE) { if (vertices.Length != 3) - type = PathType.Bezier; + type = PathType.BEZIER; else if (isLinear(vertices)) { // osu-stable special-cased colinear perfect curves to a linear path - type = PathType.Linear; + type = PathType.LINEAR; } } @@ -349,10 +351,10 @@ namespace osu.Game.Rulesets.Objects.Legacy if (vertices[endIndex].Position != vertices[endIndex - 1].Position) continue; - // Legacy Catmull sliders don't support multiple segments, so adjacent Catmull segments should be treated as a single one. + // Legacy CATMULL sliders don't support multiple segments, so adjacent CATMULL segments should be treated as a single one. // Importantly, this is not applied to the first control point, which may duplicate the slider path's position // resulting in a duplicate (0,0) control point in the resultant list. - if (type == PathType.Catmull && endIndex > 1 && FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION) + if (type == PathType.CATMULL && endIndex > 1 && FormatVersion < LegacyBeatmapEncoder.FIRST_LAZER_VERSION) continue; // The last control point of each segment is not allowed to start a new implicit segment. diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 0ac057578b..4c24c111be 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; +using Microsoft.Toolkit.HighPerformance; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Caching; @@ -260,7 +261,7 @@ namespace osu.Game.Rulesets.Objects // The current vertex ends the segment var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); - var segmentType = ControlPoints[start].Type ?? PathType.Linear; + var segmentType = ControlPoints[start].Type ?? PathType.LINEAR; // No need to calculate path when there is only 1 vertex if (segmentVertices.Length == 1) @@ -288,12 +289,12 @@ namespace osu.Game.Rulesets.Objects private List calculateSubPath(ReadOnlySpan subControlPoints, PathType type) { - switch (type) + switch (type.SplineType) { - case PathType.Linear: + case SplineType.Linear: return PathApproximator.ApproximateLinear(subControlPoints); - case PathType.PerfectCurve: + case SplineType.PerfectCurve: if (subControlPoints.Length != 3) break; @@ -305,11 +306,11 @@ namespace osu.Game.Rulesets.Objects return subPath; - case PathType.Catmull: + case SplineType.Catmull: return PathApproximator.ApproximateCatmull(subControlPoints); } - return PathApproximator.ApproximateBezier(subControlPoints); + return PathApproximator.ApproximateBSpline(subControlPoints, type.Degree ?? subControlPoints.Length); } private void calculateLength() diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index 6c88f01249..d7e5e4574d 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -29,11 +29,11 @@ namespace osu.Game.Rulesets.Objects { var controlPoints = sliderPath.ControlPoints; - var inheritedLinearPoints = controlPoints.Where(p => sliderPath.PointsInSegment(p)[0].Type == PathType.Linear && p.Type is null).ToList(); + var inheritedLinearPoints = controlPoints.Where(p => sliderPath.PointsInSegment(p)[0].Type == PathType.LINEAR && p.Type is null).ToList(); // Inherited points after a linear point, as well as the first control point if it inherited, // should be treated as linear points, so their types are temporarily changed to linear. - inheritedLinearPoints.ForEach(p => p.Type = PathType.Linear); + inheritedLinearPoints.ForEach(p => p.Type = PathType.LINEAR); double[] segmentEnds = sliderPath.GetSegmentEnds().ToArray(); @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Objects inheritedLinearPoints.ForEach(p => p.Type = null); // Recalculate middle perfect curve control points at the end of the slider path. - if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PerfectCurve && controlPoints[^2].Type is null && segmentEnds.Any()) + if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PERFECTCURVE && controlPoints[^2].Type is null && segmentEnds.Any()) { double lastSegmentStart = segmentEnds.Length > 1 ? segmentEnds[^2] : 0; double lastSegmentEnd = segmentEnds[^1]; diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 923ce9eba4..41472fd8b5 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -1,13 +1,59 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Diagnostics; + namespace osu.Game.Rulesets.Objects.Types { - public enum PathType + public enum SplineType { Catmull, - Bezier, + BSpline, Linear, PerfectCurve } + + public struct PathType + { + public static readonly PathType CATMULL = new PathType(SplineType.Catmull); + public static readonly PathType BEZIER = new PathType(SplineType.BSpline); + public static readonly PathType LINEAR = new PathType(SplineType.Linear); + public static readonly PathType PERFECTCURVE = new PathType(SplineType.PerfectCurve); + + /// + /// The type of the spline that should be used to interpret the control points of the path. + /// + public SplineType SplineType { get; init; } + + /// + /// The degree of a BSpline. Unused if is not . + /// Null means the degree is equal to the number of control points, 1 means linear, 2 means quadratic, etc. + /// + public int? Degree { get; init; } + + public PathType(SplineType splineType) + { + SplineType = splineType; + Degree = null; + } + + public override int GetHashCode() + => HashCode.Combine(SplineType, Degree); + + public override bool Equals(object? obj) + => obj is PathType pathType && this == pathType; + + public static bool operator ==(PathType a, PathType b) + => a.SplineType == b.SplineType && a.Degree == b.Degree; + + public static bool operator !=(PathType a, PathType b) + => a.SplineType != b.SplineType || a.Degree != b.Degree; + + public static PathType BSpline(int degree) + { + Debug.Assert(degree > 0); + return new PathType { SplineType = SplineType.BSpline, Degree = degree }; + } + } } diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 793d43f7ef..a114529bf9 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -246,13 +246,13 @@ namespace osu.Game.Screens.Play.HUD barPath = new SliderPath(new[] { - new PathControlPoint(new Vector2(0, 0), PathType.Linear), - new PathControlPoint(new Vector2(curveStart - curve_smoothness, 0), PathType.Bezier), + new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), + new PathControlPoint(new Vector2(curveStart - curve_smoothness, 0), PathType.BEZIER), new PathControlPoint(new Vector2(curveStart, 0)), - new PathControlPoint(new Vector2(curveStart, 0) + diagonalDir * curve_smoothness, PathType.Linear), - new PathControlPoint(new Vector2(curveEnd, BarHeight.Value) - diagonalDir * curve_smoothness, PathType.Bezier), + new PathControlPoint(new Vector2(curveStart, 0) + diagonalDir * curve_smoothness, PathType.LINEAR), + new PathControlPoint(new Vector2(curveEnd, BarHeight.Value) - diagonalDir * curve_smoothness, PathType.BEZIER), new PathControlPoint(new Vector2(curveEnd, BarHeight.Value)), - new PathControlPoint(new Vector2(curveEnd + curve_smoothness, BarHeight.Value), PathType.Linear), + new PathControlPoint(new Vector2(curveEnd + curve_smoothness, BarHeight.Value), PathType.LINEAR), new PathControlPoint(new Vector2(barLength, BarHeight.Value)), }); From 3f85aa79c5b4402b714da14295b6b796ffa8d6e6 Mon Sep 17 00:00:00 2001 From: cs Date: Sat, 11 Nov 2023 10:45:22 +0100 Subject: [PATCH 0067/1118] Add free-hand drawing of sliders to the editor --- .../Sliders/SliderPlacementBlueprint.cs | 86 +++++++++++++++++-- .../Edit/ISliderDrawingSettingsProvider.cs | 12 +++ .../Edit/OsuHitObjectComposer.cs | 8 +- .../Edit/OsuSliderDrawingSettingsProvider.cs | 68 +++++++++++++++ osu.Game/Rulesets/Objects/SliderPath.cs | 8 +- 5 files changed, 171 insertions(+), 11 deletions(-) create mode 100644 osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs create mode 100644 osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 8f0a2ee781..a5c6ae9465 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -10,6 +10,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -44,6 +45,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders [Resolved(CanBeNull = true)] private IDistanceSnapProvider distanceSnapProvider { get; set; } + [Resolved(CanBeNull = true)] + private ISliderDrawingSettingsProvider drawingSettingsProvider { get; set; } + + private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder(); + protected override bool IsValidForPlacement => HitObject.Path.HasValidLength; public SliderPlacementBlueprint() @@ -73,6 +79,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { base.LoadComplete(); inputManager = GetContainingInputManager(); + + drawingSettingsProvider.Tolerance.BindValueChanged(e => + { + if (bSplineBuilder.Tolerance != e.NewValue) + bSplineBuilder.Tolerance = e.NewValue; + updateSliderPathFromBSplineBuilder(); + }, true); } [Resolved] @@ -98,7 +111,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders ApplyDefaultsToHitObject(); break; - case SliderPlacementState.Body: + case SliderPlacementState.ControlPoints: updateCursor(); break; } @@ -115,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders beginCurve(); break; - case SliderPlacementState.Body: + case SliderPlacementState.ControlPoints: if (canPlaceNewControlPoint(out var lastPoint)) { // Place a new point by detatching the current cursor. @@ -139,9 +152,62 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return true; } + protected override bool OnDragStart(DragStartEvent e) + { + if (e.Button == MouseButton.Left) + { + switch (state) + { + case SliderPlacementState.Initial: + return true; + + case SliderPlacementState.ControlPoints: + if (HitObject.Path.ControlPoints.Count < 3) + { + var lastCp = HitObject.Path.ControlPoints.LastOrDefault(); + if (lastCp != cursor) + return false; + + bSplineBuilder.Clear(); + bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); + setState(SliderPlacementState.Drawing); + return true; + } + return false; + } + } + return base.OnDragStart(e); + } + + protected override void OnDrag(DragEvent e) + { + base.OnDrag(e); + + bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); + updateSliderPathFromBSplineBuilder(); + } + + private void updateSliderPathFromBSplineBuilder() + { + Scheduler.AddOnce(static self => + { + var cps = self.bSplineBuilder.GetControlPoints(); + self.HitObject.Path.ControlPoints.RemoveRange(1, self.HitObject.Path.ControlPoints.Count - 1); + self.HitObject.Path.ControlPoints.AddRange(cps.Select(v => new PathControlPoint(v))); + }, this); + } + + protected override void OnDragEnd(DragEndEvent e) + { + base.OnDragEnd(e); + + if (state == SliderPlacementState.Drawing) + endCurve(); + } + protected override void OnMouseUp(MouseUpEvent e) { - if (state == SliderPlacementState.Body && e.Button == MouseButton.Right) + if (state == SliderPlacementState.ControlPoints && e.Button == MouseButton.Right) endCurve(); base.OnMouseUp(e); } @@ -149,7 +215,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void beginCurve() { BeginPlacement(commitStart: true); - setState(SliderPlacementState.Body); + setState(SliderPlacementState.ControlPoints); } private void endCurve() @@ -169,6 +235,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updatePathType() { + if (state == SliderPlacementState.Drawing) + { + segmentStart.Type = PathType.BSpline(3); + return; + } + switch (currentSegmentLength) { case 1: @@ -201,7 +273,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } // Update the cursor position. - var result = positionSnapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.Body ? SnapType.GlobalGrids : SnapType.All); + var result = positionSnapProvider?.FindSnappedPositionAndTime(inputManager.CurrentState.Mouse.Position, state == SliderPlacementState.ControlPoints ? SnapType.GlobalGrids : SnapType.All); cursor.Position = ToLocalSpace(result?.ScreenSpacePosition ?? inputManager.CurrentState.Mouse.Position) - HitObject.Position; } else if (cursor != null) @@ -248,7 +320,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private enum SliderPlacementState { Initial, - Body, + ControlPoints, + Drawing, + DrawingFinalization } } } diff --git a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs new file mode 100644 index 0000000000..1138588259 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs @@ -0,0 +1,12 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Bindables; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public interface ISliderDrawingSettingsProvider + { + BindableFloat Tolerance { get; } + } +} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 0f8c960b65..d958b558cf 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -63,6 +63,9 @@ namespace osu.Game.Rulesets.Osu.Edit [Cached(typeof(IDistanceSnapProvider))] protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); + [Cached(typeof(ISliderDrawingSettingsProvider))] + protected readonly OsuSliderDrawingSettingsProvider SliderDrawingSettingsProvider = new OsuSliderDrawingSettingsProvider(); + [BackgroundDependencyLoader] private void load() { @@ -96,8 +99,11 @@ namespace osu.Game.Rulesets.Osu.Edit RightToolbox.Add(new TransformToolboxGroup { - RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler + RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }); + + AddInternal(SliderDrawingSettingsProvider); + SliderDrawingSettingsProvider.AttachToToolbox(RightToolbox); } protected override ComposeBlueprintContainer CreateBlueprintContainer() diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs new file mode 100644 index 0000000000..ba2c39e1b5 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Utils; +using osu.Game.Graphics.UserInterface; +using osu.Game.Rulesets.Edit; + +namespace osu.Game.Rulesets.Osu.Edit +{ + public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider + { + public BindableFloat Tolerance { get; } = new BindableFloat(0.1f) + { + MinValue = 0.05f, + MaxValue = 1f, + Precision = 0.01f + }; + + private BindableInt sliderTolerance = new BindableInt(10) + { + MinValue = 5, + MaxValue = 100 + }; + + private ExpandableSlider toleranceSlider = null!; + + private EditorToolboxGroup? toolboxGroup; + + public OsuSliderDrawingSettingsProvider() + { + sliderTolerance.BindValueChanged(v => + { + float newValue = v.NewValue / 100f; + if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f)) + Tolerance.Value = newValue; + }); + Tolerance.BindValueChanged(v => + { + int newValue = (int)Math.Round(v.NewValue * 100f); + if (sliderTolerance.Value != newValue) + sliderTolerance.Value = newValue; + }); + } + + public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) + { + toolboxContainer.Add(toolboxGroup = new EditorToolboxGroup("drawing") + { + Children = new Drawable[] + { + toleranceSlider = new ExpandableSlider + { + Current = sliderTolerance + } + } + }); + + sliderTolerance.BindValueChanged(e => + { + toleranceSlider.ContractedLabelText = $"Tolerance: {e.NewValue:N0}"; + toleranceSlider.ExpandedLabelText = $"Tolerance: {e.NewValue:N0}"; + }, true); + } + } +} diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 4c24c111be..75f1ab868d 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -292,13 +292,13 @@ namespace osu.Game.Rulesets.Objects switch (type.SplineType) { case SplineType.Linear: - return PathApproximator.ApproximateLinear(subControlPoints); + return PathApproximator.LinearToPiecewiseLinear(subControlPoints); case SplineType.PerfectCurve: if (subControlPoints.Length != 3) break; - List subPath = PathApproximator.ApproximateCircularArc(subControlPoints); + List subPath = PathApproximator.CircularArcToPiecewiseLinear(subControlPoints); // If for some reason a circular arc could not be fit to the 3 given points, fall back to a numerically stable bezier approximation. if (subPath.Count == 0) @@ -307,10 +307,10 @@ namespace osu.Game.Rulesets.Objects return subPath; case SplineType.Catmull: - return PathApproximator.ApproximateCatmull(subControlPoints); + return PathApproximator.CatmullToPiecewiseLinear(subControlPoints); } - return PathApproximator.ApproximateBSpline(subControlPoints, type.Degree ?? subControlPoints.Length); + return PathApproximator.BSplineToPiecewiseLinear(subControlPoints, type.Degree ?? subControlPoints.Length); } private void calculateLength() From 4e1e19728cb8ff9e11f18ab9c0e8635c2cc2ba9a Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sat, 11 Nov 2023 14:02:42 +0100 Subject: [PATCH 0068/1118] Refactor HitObject selection in Composer --- .../Edit/ManiaHitObjectComposer.cs | 43 ++++++++++++++----- .../Edit/OsuHitObjectComposer.cs | 36 +++++++++++++--- .../Rulesets/Edit/EditorTimestampParser.cs | 40 +---------------- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 14 ++---- osu.Game/Screens/Edit/Editor.cs | 11 +---- 5 files changed, 68 insertions(+), 76 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index d217f04651..b04d3f895d 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -3,16 +3,15 @@ #nullable disable -using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using osu.Game.Beatmaps; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.UI; using osu.Game.Rulesets.Mods; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit.Compose.Components; @@ -50,22 +49,44 @@ namespace osu.Game.Rulesets.Mania.Edit new HoldNoteCompositionTool() }; + private static readonly Regex selection_regex = new Regex(@"^\d+\|\d+(,\d+\|\d+)*$"); + public override string ConvertSelectionToString() - => string.Join(ObjectSeparator, EditorBeatmap.SelectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => $"{h.StartTime}|{h.Column}")); + => string.Join(',', EditorBeatmap.SelectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => $"{h.StartTime}|{h.Column}")); - public override bool HandleHitObjectSelection(HitObject hitObject, string objectInfo) + public override void SelectHitObjects(double timestamp, string objectDescription) { - if (hitObject is not ManiaHitObject maniaHitObject) - return false; + if (!selection_regex.IsMatch(objectDescription)) + return; - double[] split = objectInfo.Split('|').Select(double.Parse).ToArray(); + List remainingHitObjects = EditorBeatmap.HitObjects.Cast().Where(h => h.StartTime >= timestamp).ToList(); + string[] split = objectDescription.Split(',').ToArray(); + + for (int i = 0; i < split.Length; i++) + { + ManiaHitObject current = remainingHitObjects.FirstOrDefault(h => shouldBeSelected(h, split[i])); + + if (current == null) + continue; + + EditorBeatmap.SelectedHitObjects.Add(current); + + if (i < split.Length - 1) + remainingHitObjects = remainingHitObjects.Where(h => h != current && h.StartTime >= current.StartTime).ToList(); + } + } + + private bool shouldBeSelected(ManiaHitObject hitObject, string objectInfo) + { + string[] split = objectInfo.Split('|').ToArray(); if (split.Length != 2) return false; - double timeValue = split[0]; - double columnValue = split[1]; - return Math.Abs(maniaHitObject.StartTime - timeValue) < 0.5 - && Math.Abs(maniaHitObject.Column - columnValue) < 0.5; + if (!double.TryParse(split[0], out double time) || !int.TryParse(split[1], out int column)) + return false; + + return hitObject.StartTime == time + && hitObject.Column == column; } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 0c63cf71d8..e9c222b0e7 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.RegularExpressions; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Caching; @@ -103,18 +104,39 @@ namespace osu.Game.Rulesets.Osu.Edit protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(this); + private static readonly Regex selection_regex = new Regex(@"^\d+(,\d+)*$"); + public override string ConvertSelectionToString() - => string.Join(ObjectSeparator, selectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString())); + => string.Join(',', selectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString())); - public override bool HandleHitObjectSelection(HitObject hitObject, string objectInfo) + public override void SelectHitObjects(double timestamp, string objectDescription) { - if (hitObject is not OsuHitObject osuHitObject) + if (!selection_regex.IsMatch(objectDescription)) + return; + + List remainingHitObjects = EditorBeatmap.HitObjects.Cast().Where(h => h.StartTime >= timestamp).ToList(); + string[] split = objectDescription.Split(',').ToArray(); + + for (int i = 0; i < split.Length; i++) + { + OsuHitObject current = remainingHitObjects.FirstOrDefault(h => shouldBeSelected(h, split[i])); + + if (current == null) + continue; + + EditorBeatmap.SelectedHitObjects.Add(current); + + if (i < split.Length - 1) + remainingHitObjects = remainingHitObjects.Where(h => h != current && h.StartTime >= current.StartTime).ToList(); + } + } + + private bool shouldBeSelected(OsuHitObject hitObject, string objectInfo) + { + if (!int.TryParse(objectInfo, out int combo) || combo < 1) return false; - if (!int.TryParse(objectInfo, out int comboValue) || comboValue < 1) - return false; - - return osuHitObject.IndexInCurrentCombo + 1 == comboValue; + return hitObject.IndexInCurrentCombo + 1 == combo; } private DistanceSnapGrid distanceSnapGrid; diff --git a/osu.Game/Rulesets/Edit/EditorTimestampParser.cs b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs index 4e5a696102..7d4247b269 100644 --- a/osu.Game/Rulesets/Edit/EditorTimestampParser.cs +++ b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs @@ -2,11 +2,9 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Text.RegularExpressions; -using osu.Game.Rulesets.Objects; namespace osu.Game.Rulesets.Edit { @@ -31,43 +29,7 @@ namespace osu.Game.Rulesets.Edit Debug.Assert(times.Length == 3); - return (times[0] * 60 + times[1]) * 1_000 + times[2]; - } - - public static List GetSelectedHitObjects(HitObjectComposer composer, IReadOnlyList editorHitObjects, string objectsGroup, double position) - { - List hitObjects = editorHitObjects.Where(x => x.StartTime >= position).ToList(); - List selectedObjects = new List(); - - string[] objectsToSelect = objectsGroup.Split(composer.ObjectSeparator).ToArray(); - - foreach (string objectInfo in objectsToSelect) - { - HitObject? current = hitObjects.FirstOrDefault(x => composer.HandleHitObjectSelection(x, objectInfo)); - - if (current == null) - continue; - - selectedObjects.Add(current); - hitObjects = hitObjects.Where(x => x != current && x.StartTime >= current.StartTime).ToList(); - } - - // Stable behavior - // - always selects the next closest object when `objectsGroup` only has one (combo) item - if (objectsToSelect.Length != 1 || objectsGroup.Contains('|')) - return selectedObjects; - - HitObject? nextClosest = editorHitObjects.FirstOrDefault(x => x.StartTime >= position); - if (nextClosest == null) - return selectedObjects; - - if (nextClosest.StartTime <= (selectedObjects.FirstOrDefault()?.StartTime ?? position)) - { - selectedObjects.Clear(); - selectedObjects.Add(nextClosest); - } - - return selectedObjects; + return (times[0] * 60 + times[1]) * 1000 + times[2]; } } } diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index f6cddcc0d2..52a525e84f 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -529,17 +529,11 @@ namespace osu.Game.Rulesets.Edit public virtual string ConvertSelectionToString() => string.Empty; /// - /// The custom logic that decides whether a HitObject should be selected when clicking an editor timestamp link + /// Each ruleset has it's own selection method /// - /// The hitObject being checked - /// A single hitObject's information created with - /// Whether a HitObject should be selected or not - public virtual bool HandleHitObjectSelection(HitObject hitObject, string objectInfo) => false; - - /// - /// A character that separates the selection in - /// - public virtual char ObjectSeparator => ','; + /// The given timestamp + /// The selected object information between the brackets + public virtual void SelectHitObjects(double timestamp, string objectDescription) { } #region IPositionSnapProvider diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 58c3ae809c..03d3e3a1f8 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1194,15 +1194,8 @@ namespace osu.Game.Screens.Edit if (Mode.Value != EditorScreenMode.Compose) Mode.Value = EditorScreenMode.Compose; - List selected = EditorTimestampParser.GetSelectedHitObjects( - currentScreen.Dependencies.Get(), - editorBeatmap.HitObjects.ToList(), - objectsGroup, - position - ); - - if (selected.Any()) - editorBeatmap.SelectedHitObjects.AddRange(selected); + // Let the Ruleset handle selection + currentScreen.Dependencies.Get().SelectHitObjects(position, objectsGroup); } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); From 6ddecfd8062d6b1b0d62a6064d7d0b0b2c8d4760 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sat, 11 Nov 2023 14:13:06 +0100 Subject: [PATCH 0069/1118] Update Test cases --- .../TestSceneOpenEditorTimestampInMania.cs | 9 ++---- .../TestSceneOpenEditorTimestampInOsu.cs | 32 +++++-------------- 2 files changed, 11 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs index 6ec5dcee4c..3c6a9f3b42 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs @@ -84,17 +84,14 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor [Test] public void TestUnusualSelection() { - addStepClickLink("00:00:000 (0|1)", "invalid link"); + addStepClickLink("00:00:000 (0|1)", "wrong offset"); AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(2_170)); addReset(); - addStepClickLink("00:00:000 (0)", "std link"); - AddAssert("snap and select 1", () => checkSnapAndSelectColumn(2_170, new List<(int, int)> - { (2_170, 2) }) - ); + addStepClickLink("00:00:000 (2)", "std link"); + AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(2_170)); addReset(); - // TODO: discuss - this selects the first 2 objects on Stable, do we want that or is this fine? addStepClickLink("00:00:000 (1,2)", "std link"); AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(2_170)); } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs index d69f482d29..93573b5ad8 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs @@ -71,40 +71,24 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { HitObject firstObject = null!; - addStepClickLink("00:00:000 (1,2,3)", "invalid offset"); - AddAssert("snap to next, select 1-2-3", () => + addStepClickLink("00:00:000 (0)", "invalid combo"); + AddAssert("snap to next, select none", () => { firstObject = EditorBeatmap.HitObjects.First(); - return checkSnapAndSelectCombo(firstObject.StartTime, 1, 2, 3); + return checkSnapAndSelectCombo(firstObject.StartTime); }); addReset(); - addStepClickLink("00:00:956 (2,3,4)", "invalid offset"); + addStepClickLink("00:00:000 (1)", "wrong offset"); + AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); + + addReset(); + addStepClickLink("00:00:956 (2,3,4)", "wrong offset"); AddAssert("snap to next, select 2-3-4", () => checkSnapAndSelectCombo(firstObject.StartTime, 2, 3, 4)); - addReset(); - addStepClickLink("00:00:000 (0)", "invalid offset"); - AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); - - addReset(); - addStepClickLink("00:00:000 (1)", "invalid offset"); - AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); - - addReset(); - addStepClickLink("00:00:000 (2)", "invalid offset"); - AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); - - addReset(); - addStepClickLink("00:00:000 (2,3)", "invalid offset"); - AddAssert("snap to 1, select 2-3", () => checkSnapAndSelectCombo(firstObject.StartTime, 2, 3)); - addReset(); addStepClickLink("00:00:956 (956|1,956|2)", "mania link"); AddAssert("snap to next, select none", () => checkSnapAndSelectCombo(firstObject.StartTime)); - - addReset(); - addStepClickLink("00:00:000 (0|1)", "mania link"); - AddAssert("snap to 1, select none", () => checkSnapAndSelectCombo(firstObject.StartTime)); } } } From 54b8244a18ad3e74d40962be20cab996ba63e90d Mon Sep 17 00:00:00 2001 From: cs Date: Sat, 11 Nov 2023 15:02:06 +0100 Subject: [PATCH 0070/1118] CI Fixup --- .../Sliders/Components/PathControlPointPiece.cs | 8 ++++---- .../Components/PathControlPointVisualiser.cs | 2 +- .../Sliders/SliderPlacementBlueprint.cs | 2 ++ .../Edit/OsuSliderDrawingSettingsProvider.cs | 10 ++++------ .../Visual/Gameplay/TestSceneBezierConverter.cs | 4 ++-- .../Visual/Gameplay/TestSceneSliderPath.cs | 4 ++-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 11 +++++------ osu.Game/Rulesets/Objects/BezierConverter.cs | 12 ++++++------ .../Objects/Legacy/ConvertHitObjectParser.cs | 3 ++- osu.Game/Rulesets/Objects/SliderPath.cs | 3 +-- osu.Game/Rulesets/Objects/Types/PathType.cs | 16 ++++++++-------- 11 files changed, 37 insertions(+), 38 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 9658e5f6c3..53228cff82 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -261,17 +261,17 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components switch (pathType) { - case { SplineType: SplineType.Catmull }: + case { Type: SplineType.Catmull }: return colours.SeaFoam; - case { SplineType: SplineType.BSpline, Degree: null }: + case { Type: SplineType.BSpline, Degree: null }: return colours.PinkLighter; - case { SplineType: SplineType.BSpline, Degree: >= 1 }: + case { Type: SplineType.BSpline, Degree: >= 1 }: int idx = Math.Clamp(pathType.Degree.Value, 0, 3); return new[] { colours.PinkDarker, colours.PinkDark, colours.Pink, colours.PinkLight }[idx]; - case { SplineType: SplineType.PerfectCurve }: + case { Type: SplineType.PerfectCurve }: return colours.PurpleDark; default: diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index b5c9016538..4e85835652 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -242,7 +242,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint); - if (type.HasValue && type.Value.SplineType == SplineType.PerfectCurve) + if (type.HasValue && type.Value.Type == SplineType.PerfectCurve) { // Can't always create a circular arc out of 4 or more points, // so we split the segment into one 3-point circular arc segment diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index a5c6ae9465..20f11c3585 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -173,9 +173,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders setState(SliderPlacementState.Drawing); return true; } + return false; } } + return base.OnDragStart(e); } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index ba2c39e1b5..ae772f53fc 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; - private BindableInt sliderTolerance = new BindableInt(10) + private readonly BindableInt sliderTolerance = new BindableInt(10) { MinValue = 5, MaxValue = 100 @@ -27,8 +27,6 @@ namespace osu.Game.Rulesets.Osu.Edit private ExpandableSlider toleranceSlider = null!; - private EditorToolboxGroup? toolboxGroup; - public OsuSliderDrawingSettingsProvider() { sliderTolerance.BindValueChanged(v => @@ -47,7 +45,7 @@ namespace osu.Game.Rulesets.Osu.Edit public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) { - toolboxContainer.Add(toolboxGroup = new EditorToolboxGroup("drawing") + toolboxContainer.Add(new EditorToolboxGroup("drawing") { Children = new Drawable[] { @@ -60,8 +58,8 @@ namespace osu.Game.Rulesets.Osu.Edit sliderTolerance.BindValueChanged(e => { - toleranceSlider.ContractedLabelText = $"Tolerance: {e.NewValue:N0}"; - toleranceSlider.ExpandedLabelText = $"Tolerance: {e.NewValue:N0}"; + toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; + toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; }, true); } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs index 5eb82ccbdc..e2333011c7 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs @@ -120,7 +120,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(SplineType.Catmull, null)] [TestCase(SplineType.PerfectCurve, null)] public void TestSingleSegment(SplineType splineType, int? degree) - => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); + => AddStep("create path", () => path.ControlPoints.AddRange(createSegment(new PathType { Type = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100)))); [TestCase(SplineType.Linear, null)] [TestCase(SplineType.BSpline, null)] @@ -132,7 +132,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create path", () => { path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero)); - path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(new PathType { Type = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); }); } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index e4d99f6741..d44af45fe4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -59,7 +59,7 @@ namespace osu.Game.Tests.Visual.Gameplay [TestCase(SplineType.PerfectCurve, null)] public void TestSingleSegment(SplineType splineType, int? degree) => AddStep("create path", () => path.ControlPoints.AddRange(createSegment( - new PathType { SplineType = splineType, Degree = degree }, + new PathType { Type = splineType, Degree = degree }, Vector2.Zero, new Vector2(0, 100), new Vector2(100), @@ -77,7 +77,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("create path", () => { path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero)); - path.ControlPoints.AddRange(createSegment(new PathType { SplineType = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(new PathType { Type = splineType, Degree = degree }, new Vector2(0, 100), new Vector2(100), Vector2.Zero)); }); } diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 7029f61459..ff446206ac 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Globalization; using System.IO; using System.Linq; @@ -456,23 +455,23 @@ namespace osu.Game.Beatmaps.Formats { switch (point.Type) { - case { SplineType: SplineType.BSpline, Degree: > 0 }: + case { Type: SplineType.BSpline, Degree: > 0 }: writer.Write($"B{point.Type.Value.Degree}|"); break; - case { SplineType: SplineType.BSpline, Degree: <= 0 }: + case { Type: SplineType.BSpline, Degree: <= 0 }: writer.Write("B|"); break; - case { SplineType: SplineType.Catmull }: + case { Type: SplineType.Catmull }: writer.Write("C|"); break; - case { SplineType: SplineType.PerfectCurve }: + case { Type: SplineType.PerfectCurve }: writer.Write("P|"); break; - case { SplineType: SplineType.Linear }: + case { Type: SplineType.Linear }: writer.Write("L|"); break; } diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index 74fbe7d8f9..ed86fc10e0 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -72,15 +72,15 @@ namespace osu.Game.Rulesets.Objects switch (segmentType) { - case { SplineType: SplineType.Catmull }: + case { Type: SplineType.Catmull }: result.AddRange(from segment in ConvertCatmullToBezierAnchors(segmentVertices) from v in segment select v + position); break; - case { SplineType: SplineType.Linear }: + case { Type: SplineType.Linear }: result.AddRange(from segment in ConvertLinearToBezierAnchors(segmentVertices) from v in segment select v + position); break; - case { SplineType: SplineType.PerfectCurve }: + case { Type: SplineType.PerfectCurve }: result.AddRange(ConvertCircleToBezierAnchors(segmentVertices).Select(v => v + position)); break; @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Objects switch (segmentType) { - case { SplineType: SplineType.Catmull }: + case { Type: SplineType.Catmull }: foreach (var segment in ConvertCatmullToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Objects break; - case { SplineType: SplineType.Linear }: + case { Type: SplineType.Linear }: foreach (var segment in ConvertLinearToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Objects break; - case { SplineType: SplineType.PerfectCurve }: + case { Type: SplineType.PerfectCurve }: var circleResult = ConvertCircleToBezierAnchors(segmentVertices); for (int j = 0; j < circleResult.Length - 1; j++) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 30f4c092d9..6a13a897c4 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -228,7 +228,8 @@ namespace osu.Game.Rulesets.Objects.Legacy case 'B': if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0) - return new PathType { SplineType = SplineType.BSpline, Degree = degree }; + return new PathType { Type = SplineType.BSpline, Degree = degree }; + return new PathType(SplineType.BSpline); case 'L': diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index 75f1ab868d..e9a192669f 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; using System.Linq; -using Microsoft.Toolkit.HighPerformance; using Newtonsoft.Json; using osu.Framework.Bindables; using osu.Framework.Caching; @@ -289,7 +288,7 @@ namespace osu.Game.Rulesets.Objects private List calculateSubPath(ReadOnlySpan subControlPoints, PathType type) { - switch (type.SplineType) + switch (type.Type) { case SplineType.Linear: return PathApproximator.LinearToPiecewiseLinear(subControlPoints); diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 41472fd8b5..a6e8e173d4 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -14,7 +14,7 @@ namespace osu.Game.Rulesets.Objects.Types PerfectCurve } - public struct PathType + public readonly struct PathType { public static readonly PathType CATMULL = new PathType(SplineType.Catmull); public static readonly PathType BEZIER = new PathType(SplineType.BSpline); @@ -24,36 +24,36 @@ namespace osu.Game.Rulesets.Objects.Types /// /// The type of the spline that should be used to interpret the control points of the path. /// - public SplineType SplineType { get; init; } + public SplineType Type { get; init; } /// - /// The degree of a BSpline. Unused if is not . + /// The degree of a BSpline. Unused if is not . /// Null means the degree is equal to the number of control points, 1 means linear, 2 means quadratic, etc. /// public int? Degree { get; init; } public PathType(SplineType splineType) { - SplineType = splineType; + Type = splineType; Degree = null; } public override int GetHashCode() - => HashCode.Combine(SplineType, Degree); + => HashCode.Combine(Type, Degree); public override bool Equals(object? obj) => obj is PathType pathType && this == pathType; public static bool operator ==(PathType a, PathType b) - => a.SplineType == b.SplineType && a.Degree == b.Degree; + => a.Type == b.Type && a.Degree == b.Degree; public static bool operator !=(PathType a, PathType b) - => a.SplineType != b.SplineType || a.Degree != b.Degree; + => a.Type != b.Type || a.Degree != b.Degree; public static PathType BSpline(int degree) { Debug.Assert(degree > 0); - return new PathType { SplineType = SplineType.BSpline, Degree = degree }; + return new PathType { Type = SplineType.BSpline, Degree = degree }; } } } From c367697559dc8981ce601e44afa45a3ce46a6dc9 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sat, 11 Nov 2023 16:14:26 +0100 Subject: [PATCH 0071/1118] Changed TIME_REGEX to accept everything in the parentheses - changed osu-web link to the current value --- osu.Game/Rulesets/Edit/EditorTimestampParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Edit/EditorTimestampParser.cs b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs index 7d4247b269..b1488d298f 100644 --- a/osu.Game/Rulesets/Edit/EditorTimestampParser.cs +++ b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Edit public static class EditorTimestampParser { // 00:00:000 (1,2,3) - test - // regex from https://github.com/ppy/osu-web/blob/651a9bac2b60d031edd7e33b8073a469bf11edaa/resources/assets/coffee/_classes/beatmap-discussion-helper.coffee#L10 - public static readonly Regex TIME_REGEX = new Regex(@"\b(((\d{2,}):([0-5]\d)[:.](\d{3}))(\s\((?:\d+[,|])*\d+\))?)"); + // osu-web regex: https://github.com/ppy/osu-web/blob/3b1698639244cfdaf0b41c68bfd651ea729ec2e3/resources/js/utils/beatmapset-discussion-helper.ts#L78 + public static readonly Regex TIME_REGEX = new Regex(@"\b(((\d{2,}):([0-5]\d)[:.](\d{3}))(\s\([^)]+\))?)"); public static string[] GetRegexGroups(string timestamp) { From 4df1eb1b378cc4d5641ef4160b299f226e93dddf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 12 Nov 2023 16:15:33 +0900 Subject: [PATCH 0072/1118] Refactor logic and tooltip formatting --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 6 +- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 60 +++++++++---------- 2 files changed, 31 insertions(+), 35 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 7a9250a60e..382ac29b41 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -238,14 +238,12 @@ namespace osu.Game.Rulesets.Catch { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - double preempt = adjustedDifficulty.ApproachRate < 5 ? - (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : - (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); + double preempt = adjustedDifficulty.ApproachRate < 5 ? (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); + preempt /= rate; adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; } - } } diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 0a6f22d74a..5531ed5461 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -59,6 +59,8 @@ namespace osu.Game.Overlays.Mods private IBindable starDifficulty = null!; private BeatmapDifficulty? originalDifficulty; + private BeatmapDifficulty? adjustedDifficulty; + private bool haveRateChangedValues; private const float transition_duration = 250; @@ -171,17 +173,15 @@ namespace osu.Game.Overlays.Mods bpmDisplay.Current.Value = BeatmapInfo.Value.BPM * rate; - var adjustedDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); + originalDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); foreach (var mod in mods.Value.OfType()) - mod.ApplyToDifficulty(adjustedDifficulty); - - originalDifficulty = adjustedDifficulty; + mod.ApplyToDifficulty(originalDifficulty); Ruleset ruleset = gameRuleset.Value.CreateInstance(); - adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(adjustedDifficulty, rate); + adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(originalDifficulty, rate); - haveRateChangedValues = isDifferentArOd(originalDifficulty, adjustedDifficulty); + haveRateChangedValues = hasRateAdjustedProperties(originalDifficulty, adjustedDifficulty); approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); @@ -192,22 +192,34 @@ namespace osu.Game.Overlays.Mods overallDifficultyDisplay.Current.Value = adjustedDifficulty.OverallDifficulty; }); - private bool isDifferentArOd(BeatmapDifficulty? a, BeatmapDifficulty? b) - { - if (a == null && b == null) return false; - if (a == null || b == null) return true; - - if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate, 0.01)) return true; - if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty, 0.01)) return true; - - return false; - } - private void updateCollapsedState() { RightContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint); } + public LocalisableString TooltipText + { + get + { + if (haveRateChangedValues) + { + return $"One or more values are being adjusted by mods that change speed." + + $" (AR {originalDifficulty?.ApproachRate ?? 0}→{adjustedDifficulty?.ApproachRate ?? 0}, " + + $"OD {originalDifficulty?.OverallDifficulty ?? 0}→{adjustedDifficulty?.OverallDifficulty ?? 0})"; + } + + return string.Empty; + } + } + + private static bool hasRateAdjustedProperties(BeatmapDifficulty a, BeatmapDifficulty b) + { + if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate)) return true; + if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty)) return true; + + return false; + } + private partial class BPMDisplay : RollingCounter { protected override double RollingDuration => 500; @@ -222,19 +234,5 @@ namespace osu.Game.Overlays.Mods UseFullGlyphHeight = false, }; } - - public LocalisableString TooltipText - { - get - { - if (haveRateChangedValues) - { - return LocalisableString.Format("Values are changed by mods that change speed.\n" + - "Original values: AR = {0}, OD = {1}", originalDifficulty?.ApproachRate ?? 0, originalDifficulty?.OverallDifficulty ?? 0); - } - - return ""; - } - } } } From a04f9aaef7870d8ee94b85598aaca1d25dc22840 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 12 Nov 2023 16:20:13 +0900 Subject: [PATCH 0073/1118] Apply various inspections --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 ++-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 6 ++---- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 2 +- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 382ac29b41..7327fb4513 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -238,12 +238,12 @@ namespace osu.Game.Rulesets.Catch { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - double preempt = adjustedDifficulty.ApproachRate < 5 ? (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); + double preempt = adjustedDifficulty.ApproachRate < 6 ? (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); preempt /= rate; adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); - return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; + return adjustedDifficulty; } } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 2d12e9ebc5..f4cf9409e8 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -334,9 +334,7 @@ namespace osu.Game.Rulesets.Osu { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); - double preempt = adjustedDifficulty.ApproachRate < 5 ? - (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : - (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); + double preempt = adjustedDifficulty.ApproachRate < 5 ? (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); preempt /= rate; adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); @@ -344,7 +342,7 @@ namespace osu.Game.Rulesets.Osu hitwindow /= rate; adjustedDifficulty.OverallDifficulty = (float)(80.0 - hitwindow) / 6; - return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; + return adjustedDifficulty; } } } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index aaf32d35f2..88d0087622 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Taiko hitwindow /= rate; adjustedDifficulty.OverallDifficulty = (float)(5 * (35 - hitwindow) / 15 + 5); - return adjustedDifficulty ?? (BeatmapDifficulty)baseDifficulty; + return adjustedDifficulty; } } } From 4e7c40f1d7392376d68989ad5fa26e53430c55c6 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sun, 12 Nov 2023 14:58:46 +0100 Subject: [PATCH 0074/1118] Do Split and Parse before checking HitObjects --- .../Edit/ManiaHitObjectComposer.cs | 28 ++++++++----------- .../Edit/OsuHitObjectComposer.cs | 19 +++++-------- 2 files changed, 18 insertions(+), 29 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index b04d3f895d..d580f2d025 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -60,33 +60,27 @@ namespace osu.Game.Rulesets.Mania.Edit return; List remainingHitObjects = EditorBeatmap.HitObjects.Cast().Where(h => h.StartTime >= timestamp).ToList(); - string[] split = objectDescription.Split(',').ToArray(); + string[] splitDescription = objectDescription.Split(',').ToArray(); - for (int i = 0; i < split.Length; i++) + for (int i = 0; i < splitDescription.Length; i++) { - ManiaHitObject current = remainingHitObjects.FirstOrDefault(h => shouldBeSelected(h, split[i])); + string[] split = splitDescription[i].Split('|').ToArray(); + if (split.Length != 2) + continue; + + if (!double.TryParse(split[0], out double time) || !int.TryParse(split[1], out int column)) + continue; + + ManiaHitObject current = remainingHitObjects.FirstOrDefault(h => h.StartTime == time && h.Column == column); if (current == null) continue; EditorBeatmap.SelectedHitObjects.Add(current); - if (i < split.Length - 1) + if (i < splitDescription.Length - 1) remainingHitObjects = remainingHitObjects.Where(h => h != current && h.StartTime >= current.StartTime).ToList(); } } - - private bool shouldBeSelected(ManiaHitObject hitObject, string objectInfo) - { - string[] split = objectInfo.Split('|').ToArray(); - if (split.Length != 2) - return false; - - if (!double.TryParse(split[0], out double time) || !int.TryParse(split[1], out int column)) - return false; - - return hitObject.StartTime == time - && hitObject.Column == column; - } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index e9c222b0e7..1dfa02fe83 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -115,30 +115,25 @@ namespace osu.Game.Rulesets.Osu.Edit return; List remainingHitObjects = EditorBeatmap.HitObjects.Cast().Where(h => h.StartTime >= timestamp).ToList(); - string[] split = objectDescription.Split(',').ToArray(); + string[] splitDescription = objectDescription.Split(',').ToArray(); - for (int i = 0; i < split.Length; i++) + for (int i = 0; i < splitDescription.Length; i++) { - OsuHitObject current = remainingHitObjects.FirstOrDefault(h => shouldBeSelected(h, split[i])); + if (!int.TryParse(splitDescription[i], out int combo) || combo < 1) + continue; + + OsuHitObject current = remainingHitObjects.FirstOrDefault(h => h.IndexInCurrentCombo + 1 == combo); if (current == null) continue; EditorBeatmap.SelectedHitObjects.Add(current); - if (i < split.Length - 1) + if (i < splitDescription.Length - 1) remainingHitObjects = remainingHitObjects.Where(h => h != current && h.StartTime >= current.StartTime).ToList(); } } - private bool shouldBeSelected(OsuHitObject hitObject, string objectInfo) - { - if (!int.TryParse(objectInfo, out int combo) || combo < 1) - return false; - - return hitObject.IndexInCurrentCombo + 1 == combo; - } - private DistanceSnapGrid distanceSnapGrid; private Container distanceSnapGridContainer; From fab6fc9adb633d82d88afe4c76b1313946032ab4 Mon Sep 17 00:00:00 2001 From: ratinfx Date: Sun, 12 Nov 2023 15:09:15 +0100 Subject: [PATCH 0075/1118] Updated comments, renamed method --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 1 + osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 1 + osu.Game/OsuGame.cs | 4 ++-- osu.Game/Rulesets/Edit/EditorTimestampParser.cs | 6 ++++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index d580f2d025..92ecea812c 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -49,6 +49,7 @@ namespace osu.Game.Rulesets.Mania.Edit new HoldNoteCompositionTool() }; + // 123|0,456|1,789|2 ... private static readonly Regex selection_regex = new Regex(@"^\d+\|\d+(,\d+\|\d+)*$"); public override string ConvertSelectionToString() diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 1dfa02fe83..2afbd83ce5 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -104,6 +104,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(this); + // 1,2,3,4 ... private static readonly Regex selection_regex = new Regex(@"^\d+(,\d+)*$"); public override string ConvertSelectionToString() diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index cde8ee1457..4e37481ba7 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -434,7 +434,7 @@ namespace osu.Game break; case LinkAction.OpenEditorTimestamp: - SeekToTimestamp(argString); + HandleTimestamp(argString); break; case LinkAction.JoinMultiplayerMatch: @@ -558,7 +558,7 @@ namespace osu.Game /// Seek to a given timestamp in the Editor and select relevant HitObjects if needed /// /// The timestamp and the selected objects - public void SeekToTimestamp(string timestamp) + public void HandleTimestamp(string timestamp) { if (ScreenStack.CurrentScreen is not Editor editor) { diff --git a/osu.Game/Rulesets/Edit/EditorTimestampParser.cs b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs index b1488d298f..e36822cc63 100644 --- a/osu.Game/Rulesets/Edit/EditorTimestampParser.cs +++ b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs @@ -10,16 +10,18 @@ namespace osu.Game.Rulesets.Edit { public static class EditorTimestampParser { - // 00:00:000 (1,2,3) - test - // osu-web regex: https://github.com/ppy/osu-web/blob/3b1698639244cfdaf0b41c68bfd651ea729ec2e3/resources/js/utils/beatmapset-discussion-helper.ts#L78 + // 00:00:000 (...) - test + // original osu-web regex: https://github.com/ppy/osu-web/blob/3b1698639244cfdaf0b41c68bfd651ea729ec2e3/resources/js/utils/beatmapset-discussion-helper.ts#L78 public static readonly Regex TIME_REGEX = new Regex(@"\b(((\d{2,}):([0-5]\d)[:.](\d{3}))(\s\([^)]+\))?)"); public static string[] GetRegexGroups(string timestamp) { Match match = TIME_REGEX.Match(timestamp); + string[] result = match.Success ? match.Groups.Values.Where(x => x is not Match && !x.Value.Contains(':')).Select(x => x.Value).ToArray() : Array.Empty(); + return result; } From 26d493986c318e2a390ecf02af10785f5ee760f1 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 12 Nov 2023 18:05:18 +0200 Subject: [PATCH 0076/1118] Fixed precision and updated AdvancedStats --- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 4 +- .../Screens/Select/Details/AdvancedStats.cs | 48 +++++++++---------- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 5531ed5461..bf39666f83 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -204,8 +204,8 @@ namespace osu.Game.Overlays.Mods if (haveRateChangedValues) { return $"One or more values are being adjusted by mods that change speed." + - $" (AR {originalDifficulty?.ApproachRate ?? 0}→{adjustedDifficulty?.ApproachRate ?? 0}, " + - $"OD {originalDifficulty?.OverallDifficulty ?? 0}→{adjustedDifficulty?.OverallDifficulty ?? 0})"; + $" (AR {originalDifficulty?.ApproachRate ?? 0}→{(adjustedDifficulty?.ApproachRate ?? 0):0.0#}, " + + $"OD {originalDifficulty?.OverallDifficulty ?? 0}→{(adjustedDifficulty?.OverallDifficulty ?? 0):0.0#})"; } return string.Empty; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index f84422ae65..25c18f7328 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -46,6 +46,7 @@ namespace osu.Game.Screens.Select.Details private readonly StatisticRow starDifficulty; private BeatmapDifficulty originalDifficulty; + private BeatmapDifficulty adjustedDifficulty; private bool haveRateChangedValues; private IBeatmapInfo beatmapInfo; @@ -120,24 +121,25 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; - BeatmapDifficulty adjustedDifficulty = null; if (baseDifficulty != null) { - adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + originalDifficulty = new BeatmapDifficulty(baseDifficulty); foreach (var mod in mods.Value.OfType()) - mod.ApplyToDifficulty(adjustedDifficulty); + mod.ApplyToDifficulty(originalDifficulty); - originalDifficulty = adjustedDifficulty; + if (gameRuleset != null) + { + Ruleset ruleset = gameRuleset.Value.CreateInstance(); - Ruleset ruleset = gameRuleset.Value.CreateInstance(); - double rate = 1; - foreach (var mod in mods.Value.OfType()) - rate = mod.ApplyToRate(0, rate); - adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(adjustedDifficulty, rate); + double rate = 1; + foreach (var mod in mods.Value.OfType()) + rate = mod.ApplyToRate(0, rate); - haveRateChangedValues = isDifferentArOd(originalDifficulty, adjustedDifficulty); + adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(originalDifficulty, rate); + haveRateChangedValues = hasRateAdjustedProperties(originalDifficulty, adjustedDifficulty); + } } switch (BeatmapInfo?.Ruleset.OnlineID) @@ -202,31 +204,29 @@ namespace osu.Game.Screens.Select.Details starDifficultyCancellationSource?.Cancel(); } - private bool isDifferentArOd(BeatmapDifficulty a, BeatmapDifficulty b) - { - if (a == null && b == null) return false; - if (a == null || b == null) return true; - - if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate, 0.01)) return true; - if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty, 0.01)) return true; - - return false; - } - public LocalisableString TooltipText { get { if (haveRateChangedValues) { - return LocalisableString.Format("Values are changed by mods that change speed.\n" + - "Original values: AR = {0}, OD = {1}", originalDifficulty?.ApproachRate ?? 0, originalDifficulty?.OverallDifficulty ?? 0); + return $"One or more values are being adjusted by mods that change speed." + + $" (AR {originalDifficulty?.ApproachRate ?? 0}→{(adjustedDifficulty?.ApproachRate ?? 0):0.0#}, " + + $"OD {originalDifficulty?.OverallDifficulty ?? 0}→{(adjustedDifficulty?.OverallDifficulty ?? 0):0.0#})"; } - return ""; + return string.Empty; } } + private static bool hasRateAdjustedProperties(BeatmapDifficulty a, BeatmapDifficulty b) + { + if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate)) return true; + if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty)) return true; + + return false; + } + public partial class StatisticRow : Container, IHasAccentColour { private const float value_width = 25; From d123ba5bcef07e6893cb8c2d633e5c35b987bccf Mon Sep 17 00:00:00 2001 From: Poyo Date: Sun, 12 Nov 2023 11:13:38 -0800 Subject: [PATCH 0077/1118] Fix broken tests --- .../NonVisual/Ranking/UnstableRateTest.cs | 8 +++--- .../Gameplay/TestSceneUnstableRateCounter.cs | 4 ++- ...estSceneHitEventTimingDistributionGraph.cs | 28 +++++++++---------- 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs b/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs index 27c8270f0f..bde3c37af7 100644 --- a/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs +++ b/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs @@ -20,7 +20,7 @@ namespace osu.Game.Tests.NonVisual.Ranking public void TestDistributedHits() { var events = Enumerable.Range(-5, 11) - .Select(t => new HitEvent(t - 5, HitResult.Great, new HitObject(), null, null)); + .Select(t => new HitEvent(t - 5, 1.0, HitResult.Great, new HitObject(), null, null)); var unstableRate = new UnstableRate(events); @@ -33,9 +33,9 @@ namespace osu.Game.Tests.NonVisual.Ranking { var events = new[] { - new HitEvent(-100, HitResult.Miss, new HitObject(), null, null), - new HitEvent(0, HitResult.Great, new HitObject(), null, null), - new HitEvent(200, HitResult.Meh, new HitObject { HitWindows = HitWindows.Empty }, null, null), + new HitEvent(-100, 1.0, HitResult.Miss, new HitObject(), null, null), + new HitEvent(0, 1.0, HitResult.Great, new HitObject(), null, null), + new HitEvent(200, 1.0, HitResult.Meh, new HitObject { HitWindows = HitWindows.Empty }, null, null), }; var unstableRate = new UnstableRate(events); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs index d0e516ed39..f6c819b329 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs @@ -56,6 +56,7 @@ namespace osu.Game.Tests.Visual.Gameplay scoreProcessor.RevertResult( new JudgementResult(new HitCircle { HitWindows = hitWindows }, new Judgement()) { + GameplayRate = 1.0, TimeOffset = 25, Type = HitResult.Perfect, }); @@ -92,7 +93,7 @@ namespace osu.Game.Tests.Visual.Gameplay }); } - private void applyJudgement(double offsetMs, bool alt) + private void applyJudgement(double offsetMs, bool alt, double gameplayRate = 1.0) { double placement = offsetMs; @@ -105,6 +106,7 @@ namespace osu.Game.Tests.Visual.Gameplay scoreProcessor.ApplyResult(new JudgementResult(new HitCircle { HitWindows = hitWindows }, new Judgement()) { TimeOffset = placement, + GameplayRate = gameplayRate, Type = HitResult.Perfect, }); } diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs index a40cb41e2c..325a535731 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneHitEventTimingDistributionGraph.cs @@ -49,7 +49,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestAroundCentre() { - createTest(Enumerable.Range(-150, 300).Select(i => new HitEvent(i / 50f, HitResult.Perfect, placeholder_object, placeholder_object, null)).ToList()); + createTest(Enumerable.Range(-150, 300).Select(i => new HitEvent(i / 50f, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null)).ToList()); } [Test] @@ -57,12 +57,12 @@ namespace osu.Game.Tests.Visual.Ranking { createTest(new List { - new HitEvent(-7, HitResult.Perfect, placeholder_object, placeholder_object, null), - new HitEvent(-6, HitResult.Perfect, placeholder_object, placeholder_object, null), - new HitEvent(-5, HitResult.Perfect, placeholder_object, placeholder_object, null), - new HitEvent(5, HitResult.Perfect, placeholder_object, placeholder_object, null), - new HitEvent(6, HitResult.Perfect, placeholder_object, placeholder_object, null), - new HitEvent(7, HitResult.Perfect, placeholder_object, placeholder_object, null), + new HitEvent(-7, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null), + new HitEvent(-6, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null), + new HitEvent(-5, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null), + new HitEvent(5, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null), + new HitEvent(6, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null), + new HitEvent(7, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null), }); } @@ -78,7 +78,7 @@ namespace osu.Game.Tests.Visual.Ranking : offset > 16 ? HitResult.Good : offset > 8 ? HitResult.Great : HitResult.Perfect; - return new HitEvent(h.TimeOffset, result, placeholder_object, placeholder_object, null); + return new HitEvent(h.TimeOffset, 1.0, result, placeholder_object, placeholder_object, null); }).ToList()); } @@ -95,7 +95,7 @@ namespace osu.Game.Tests.Visual.Ranking : offset > 8 ? HitResult.Great : HitResult.Perfect; - return new HitEvent(h.TimeOffset, result, placeholder_object, placeholder_object, null); + return new HitEvent(h.TimeOffset, 1.0, result, placeholder_object, placeholder_object, null); }); var narrow = CreateDistributedHitEvents(0, 50).Select(h => { @@ -106,7 +106,7 @@ namespace osu.Game.Tests.Visual.Ranking : offset > 10 ? HitResult.Good : offset > 5 ? HitResult.Great : HitResult.Perfect; - return new HitEvent(h.TimeOffset, result, placeholder_object, placeholder_object, null); + return new HitEvent(h.TimeOffset, 1.0, result, placeholder_object, placeholder_object, null); }); createTest(wide.Concat(narrow).ToList()); } @@ -114,7 +114,7 @@ namespace osu.Game.Tests.Visual.Ranking [Test] public void TestZeroTimeOffset() { - createTest(Enumerable.Range(0, 100).Select(_ => new HitEvent(0, HitResult.Perfect, placeholder_object, placeholder_object, null)).ToList()); + createTest(Enumerable.Range(0, 100).Select(_ => new HitEvent(0, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null)).ToList()); } [Test] @@ -129,9 +129,9 @@ namespace osu.Game.Tests.Visual.Ranking createTest(Enumerable.Range(0, 100).Select(i => { if (i % 2 == 0) - return new HitEvent(0, HitResult.Perfect, placeholder_object, placeholder_object, null); + return new HitEvent(0, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null); - return new HitEvent(30, HitResult.Miss, placeholder_object, placeholder_object, null); + return new HitEvent(30, 1.0, HitResult.Miss, placeholder_object, placeholder_object, null); }).ToList()); } @@ -162,7 +162,7 @@ namespace osu.Game.Tests.Visual.Ranking int count = (int)(Math.Pow(range - Math.Abs(i - range), 2)) / 10; for (int j = 0; j < count; j++) - hitEvents.Add(new HitEvent(centre + i - range, HitResult.Perfect, placeholder_object, placeholder_object, null)); + hitEvents.Add(new HitEvent(centre + i - range, 1.0, HitResult.Perfect, placeholder_object, placeholder_object, null)); } return hitEvents; From 8c36604e58e84265d623f0ec19e3eec85370ef4d Mon Sep 17 00:00:00 2001 From: Poyo Date: Sun, 12 Nov 2023 11:16:06 -0800 Subject: [PATCH 0078/1118] Add rate-change UR tests --- .../NonVisual/Ranking/UnstableRateTest.cs | 32 +++++++++++++++++++ .../Gameplay/TestSceneUnstableRateCounter.cs | 22 +++++++++++++ 2 files changed, 54 insertions(+) diff --git a/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs b/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs index bde3c37af7..5a416d05d7 100644 --- a/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs +++ b/osu.Game.Tests/NonVisual/Ranking/UnstableRateTest.cs @@ -42,5 +42,37 @@ namespace osu.Game.Tests.NonVisual.Ranking Assert.AreEqual(0, unstableRate.Value); } + + [Test] + public void TestStaticRateChange() + { + var events = new[] + { + new HitEvent(-150, 1.5, HitResult.Great, new HitObject(), null, null), + new HitEvent(-150, 1.5, HitResult.Great, new HitObject(), null, null), + new HitEvent(150, 1.5, HitResult.Great, new HitObject(), null, null), + new HitEvent(150, 1.5, HitResult.Great, new HitObject(), null, null), + }; + + var unstableRate = new UnstableRate(events); + + Assert.AreEqual(10 * 100, unstableRate.Value); + } + + [Test] + public void TestDynamicRateChange() + { + var events = new[] + { + new HitEvent(-50, 0.5, HitResult.Great, new HitObject(), null, null), + new HitEvent(75, 0.75, HitResult.Great, new HitObject(), null, null), + new HitEvent(-100, 1.0, HitResult.Great, new HitObject(), null, null), + new HitEvent(125, 1.25, HitResult.Great, new HitObject(), null, null), + }; + + var unstableRate = new UnstableRate(events); + + Assert.AreEqual(10 * 100, unstableRate.Value); + } } } diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs index f6c819b329..73ec6ea335 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneUnstableRateCounter.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -81,6 +82,27 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("UR = 250", () => counter.Current.Value == 250.0); } + [Test] + public void TestStaticRateChange() + { + AddStep("Create Display", recreateDisplay); + + AddRepeatStep("Set UR to 250 at 1.5x", () => applyJudgement(25, true, 1.5), 4); + + AddUntilStep("UR = 250/1.5", () => counter.Current.Value == Math.Round(250.0 / 1.5)); + } + + [Test] + public void TestDynamicRateChange() + { + AddStep("Create Display", recreateDisplay); + + AddRepeatStep("Set UR to 100 at 1.0x", () => applyJudgement(10, true, 1.0), 4); + AddRepeatStep("Bring UR to 100 at 1.5x", () => applyJudgement(15, true, 1.5), 4); + + AddUntilStep("UR = 100", () => counter.Current.Value == 100.0); + } + private void recreateDisplay() { Clear(); From e67725f5d6c9b6334c3e01a5717b06cd507a1f63 Mon Sep 17 00:00:00 2001 From: Poyo Date: Sun, 12 Nov 2023 12:14:19 -0800 Subject: [PATCH 0079/1118] Use IGameplayClock for rate --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 0843fd5bdc..4ae59dccb1 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -154,6 +154,9 @@ namespace osu.Game.Rulesets.Objects.Drawables [Resolved(CanBeNull = true)] private IPooledHitObjectProvider pooledObjectProvider { get; set; } + [Resolved] + private IGameplayClock gameplayClock { get; set; } = null!; + /// /// Whether the initialization logic in has applied. /// @@ -704,7 +707,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } Result.RawTime = Time.Current; - Result.GameplayRate = Clock.Rate; + Result.GameplayRate = gameplayClock.GetTrueGameplayRate(); if (Result.HasResult) updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss); From f794d4dc8384e0bba90d5063e5285a063adabcf8 Mon Sep 17 00:00:00 2001 From: Poyo Date: Sun, 12 Nov 2023 13:29:40 -0800 Subject: [PATCH 0080/1118] Allow gameplayClock to be null --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 4ae59dccb1..7cfb6aedf0 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -154,8 +154,8 @@ namespace osu.Game.Rulesets.Objects.Drawables [Resolved(CanBeNull = true)] private IPooledHitObjectProvider pooledObjectProvider { get; set; } - [Resolved] - private IGameplayClock gameplayClock { get; set; } = null!; + [Resolved(CanBeNull = true)] + private IGameplayClock gameplayClock { get; set; } /// /// Whether the initialization logic in has applied. @@ -707,7 +707,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } Result.RawTime = Time.Current; - Result.GameplayRate = gameplayClock.GetTrueGameplayRate(); + Result.GameplayRate = gameplayClock?.GetTrueGameplayRate() ?? 1.0; if (Result.HasResult) updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss); From c24b3543ab271536b2b98c3c4777a174bc0621c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Mon, 13 Nov 2023 12:47:12 +0900 Subject: [PATCH 0081/1118] Fix OnDragStart on macOS --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 20f11c3585..fe01b1b6d2 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (HitObject.Path.ControlPoints.Count < 3) { var lastCp = HitObject.Path.ControlPoints.LastOrDefault(); - if (lastCp != cursor) + if (lastCp != cursor && HitObject.Path.ControlPoints.Count == 2) return false; bSplineBuilder.Clear(); From e6b4dfba369ee790ee0cee427aa702a935ce341b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Mon, 13 Nov 2023 12:49:59 +0900 Subject: [PATCH 0082/1118] Fix doubled control point at beginning of drawn slider --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index fe01b1b6d2..9ac28fe82a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { var cps = self.bSplineBuilder.GetControlPoints(); self.HitObject.Path.ControlPoints.RemoveRange(1, self.HitObject.Path.ControlPoints.Count - 1); - self.HitObject.Path.ControlPoints.AddRange(cps.Select(v => new PathControlPoint(v))); + self.HitObject.Path.ControlPoints.AddRange(cps.Skip(1).Select(v => new PathControlPoint(v))); }, this); } From 5fd55e55c08130773e72805ce7785b6da69031b6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 13 Nov 2023 12:59:36 +0900 Subject: [PATCH 0083/1118] Add flag for combo end bonus to legacy processor --- osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs index 103569ffc3..e6db76e3b6 100644 --- a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs @@ -20,6 +20,8 @@ namespace osu.Game.Rulesets.Osu.Scoring private const double hp_slider_repeat = 4; private const double hp_slider_tick = 3; + public bool ComboEndBonus { get; set; } + private double lowestHpEver; private double lowestHpEnd; private double lowestHpComboEnd; @@ -129,7 +131,7 @@ namespace osu.Game.Rulesets.Osu.Scoring break; } - if (i == Beatmap.HitObjects.Count - 1 || ((OsuHitObject)Beatmap.HitObjects[i + 1]).NewCombo) + if (ComboEndBonus && (i == Beatmap.HitObjects.Count - 1 || ((OsuHitObject)Beatmap.HitObjects[i + 1]).NewCombo)) { increaseHp(hpMultiplierComboEnd * hp_combo_geki + hpMultiplierNormal * hp_hit_300); From 929570656d7a25fdea59141b1385a5a207b1bfc4 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 13 Nov 2023 13:12:46 +0900 Subject: [PATCH 0084/1118] Disallow legacy health processor from being used for gameplay --- .../Scoring/LegacyOsuHealthProcessor.cs | 22 +++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs index e6db76e3b6..aa9312500b 100644 --- a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs @@ -5,13 +5,17 @@ using System; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Scoring { - // Reference implementation for osu!stable's HP drain. + /// + /// Reference implementation for osu!stable's HP drain. + /// Cannot be used for gameplay. + /// public partial class LegacyOsuHealthProcessor : LegacyDrainingHealthProcessor { private const double hp_bar_maximum = 200; @@ -20,8 +24,6 @@ namespace osu.Game.Rulesets.Osu.Scoring private const double hp_slider_repeat = 4; private const double hp_slider_tick = 3; - public bool ComboEndBonus { get; set; } - private double lowestHpEver; private double lowestHpEnd; private double lowestHpComboEnd; @@ -44,6 +46,18 @@ namespace osu.Game.Rulesets.Osu.Scoring base.ApplyBeatmap(beatmap); } + protected override void ApplyResultInternal(JudgementResult result) + { + if (!IsSimulating) + throw new NotSupportedException("The legacy osu! health processor is not supported for gameplay."); + } + + protected override void RevertResultInternal(JudgementResult result) + { + if (!IsSimulating) + throw new NotSupportedException("The legacy osu! health processor is not supported for gameplay."); + } + protected override void Reset(bool storeResults) { hpMultiplierNormal = 1; @@ -131,7 +145,7 @@ namespace osu.Game.Rulesets.Osu.Scoring break; } - if (ComboEndBonus && (i == Beatmap.HitObjects.Count - 1 || ((OsuHitObject)Beatmap.HitObjects[i + 1]).NewCombo)) + if (i == Beatmap.HitObjects.Count - 1 || ((OsuHitObject)Beatmap.HitObjects[i + 1]).NewCombo) { increaseHp(hpMultiplierComboEnd * hp_combo_geki + hpMultiplierNormal * hp_hit_300); From 7713da499f6cb08b81cfcaf571ad79fb36f20752 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 13 Nov 2023 13:12:56 +0900 Subject: [PATCH 0085/1118] Make osu ruleset use the new health processor --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 607b83d379..aaf0ab41a0 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -48,6 +48,8 @@ namespace osu.Game.Rulesets.Osu public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(); + public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new OsuHealthProcessor(drainStartTime); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap, this); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); From 98e6b7744bf58db8db7e348a4935fe9c5fe06e78 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 13 Nov 2023 13:46:47 +0900 Subject: [PATCH 0086/1118] Cleanup --- .../Scoring/LegacyOsuHealthProcessor.cs | 11 +-- .../Scoring/OsuHealthProcessor.cs | 12 +-- .../Scoring/DrainingHealthProcessor.cs | 54 ++++++----- .../Scoring/LegacyDrainingHealthProcessor.cs | 90 ------------------- 4 files changed, 47 insertions(+), 120 deletions(-) delete mode 100644 osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs diff --git a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs index aa9312500b..f918868715 100644 --- a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Scoring /// Reference implementation for osu!stable's HP drain. /// Cannot be used for gameplay. /// - public partial class LegacyOsuHealthProcessor : LegacyDrainingHealthProcessor + public partial class LegacyOsuHealthProcessor : DrainingHealthProcessor { private const double hp_bar_maximum = 200; private const double hp_combo_geki = 14; @@ -24,6 +24,9 @@ namespace osu.Game.Rulesets.Osu.Scoring private const double hp_slider_repeat = 4; private const double hp_slider_tick = 3; + public Action? OnIterationFail; + public Action? OnIterationSuccess; + private double lowestHpEver; private double lowestHpEnd; private double lowestHpComboEnd; @@ -187,13 +190,11 @@ namespace osu.Game.Rulesets.Osu.Scoring if (fail) { - if (Log) - Console.WriteLine($"FAILED drop {testDrop / hp_bar_maximum}: {failReason}"); + OnIterationFail?.Invoke($"FAILED drop {testDrop / hp_bar_maximum}: {failReason}"); continue; } - if (Log) - Console.WriteLine($"PASSED drop {testDrop / hp_bar_maximum}"); + OnIterationSuccess?.Invoke($"PASSED drop {testDrop / hp_bar_maximum}"); return testDrop / hp_bar_maximum; } while (true); diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 2266cf9d33..3a096fca88 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Judgements; @@ -12,8 +13,11 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Scoring { - public partial class OsuHealthProcessor : LegacyDrainingHealthProcessor + public partial class OsuHealthProcessor : DrainingHealthProcessor { + public Action? OnIterationFail; + public Action? OnIterationSuccess; + private double lowestHpEver; private double lowestHpEnd; private double hpRecoveryAvailable; @@ -141,13 +145,11 @@ namespace osu.Game.Rulesets.Osu.Scoring if (fail) { - if (Log) - Console.WriteLine($"FAILED drop {testDrop}: {failReason}"); + OnIterationFail?.Invoke($"FAILED drop {testDrop}: {failReason}"); continue; } - if (Log) - Console.WriteLine($"PASSED drop {testDrop}"); + OnIterationSuccess?.Invoke($"PASSED drop {testDrop}"); return testDrop; } while (true); diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index 2f81aa735e..a8808d08e5 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -41,15 +41,29 @@ namespace osu.Game.Rulesets.Scoring /// private const double max_health_target = 0.4; - private IBeatmap beatmap; - private double gameplayEndTime; + /// + /// The drain rate as a proportion of the total health drained per millisecond. + /// + public double DrainRate { get; private set; } = 1; - private readonly double drainStartTime; - private readonly double drainLenience; + /// + /// The beatmap. + /// + protected IBeatmap Beatmap { get; private set; } + + /// + /// The time at which health starts draining. + /// + protected readonly double DrainStartTime; + + /// + /// An amount of lenience to apply to the drain rate. + /// + protected readonly double DrainLenience; private readonly List<(double time, double health)> healthIncreases = new List<(double, double)>(); + private double gameplayEndTime; private double targetMinimumHealth; - private double drainRate = 1; private PeriodTracker noDrainPeriodTracker; @@ -63,8 +77,8 @@ namespace osu.Game.Rulesets.Scoring /// A value of 1 completely removes drain. public DrainingHealthProcessor(double drainStartTime, double drainLenience = 0) { - this.drainStartTime = drainStartTime; - this.drainLenience = Math.Clamp(drainLenience, 0, 1); + DrainStartTime = drainStartTime; + DrainLenience = Math.Clamp(drainLenience, 0, 1); } protected override void Update() @@ -75,16 +89,16 @@ namespace osu.Game.Rulesets.Scoring return; // When jumping in and out of gameplay time within a single frame, health should only be drained for the period within the gameplay time - double lastGameplayTime = Math.Clamp(Time.Current - Time.Elapsed, drainStartTime, gameplayEndTime); - double currentGameplayTime = Math.Clamp(Time.Current, drainStartTime, gameplayEndTime); + double lastGameplayTime = Math.Clamp(Time.Current - Time.Elapsed, DrainStartTime, gameplayEndTime); + double currentGameplayTime = Math.Clamp(Time.Current, DrainStartTime, gameplayEndTime); - if (drainLenience < 1) - Health.Value -= drainRate * (currentGameplayTime - lastGameplayTime); + if (DrainLenience < 1) + Health.Value -= DrainRate * (currentGameplayTime - lastGameplayTime); } public override void ApplyBeatmap(IBeatmap beatmap) { - this.beatmap = beatmap; + Beatmap = beatmap; if (beatmap.HitObjects.Count > 0) gameplayEndTime = beatmap.HitObjects[^1].GetEndTime(); @@ -105,7 +119,7 @@ namespace osu.Game.Rulesets.Scoring targetMinimumHealth = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, min_health_target, mid_health_target, max_health_target); // Add back a portion of the amount of HP to be drained, depending on the lenience requested. - targetMinimumHealth += drainLenience * (1 - targetMinimumHealth); + targetMinimumHealth += DrainLenience * (1 - targetMinimumHealth); // Ensure the target HP is within an acceptable range. targetMinimumHealth = Math.Clamp(targetMinimumHealth, 0, 1); @@ -125,15 +139,15 @@ namespace osu.Game.Rulesets.Scoring { base.Reset(storeResults); - drainRate = 1; + DrainRate = 1; if (storeResults) - drainRate = computeDrainRate(); + DrainRate = ComputeDrainRate(); healthIncreases.Clear(); } - private double computeDrainRate() + protected virtual double ComputeDrainRate() { if (healthIncreases.Count <= 1) return 0; @@ -152,17 +166,17 @@ namespace osu.Game.Rulesets.Scoring for (int i = 0; i < healthIncreases.Count; i++) { double currentTime = healthIncreases[i].time; - double lastTime = i > 0 ? healthIncreases[i - 1].time : drainStartTime; + double lastTime = i > 0 ? healthIncreases[i - 1].time : DrainStartTime; // Subtract any break time from the duration since the last object - if (beatmap.Breaks.Count > 0) + if (Beatmap.Breaks.Count > 0) { // Advance the last break occuring before the current time - while (currentBreak + 1 < beatmap.Breaks.Count && beatmap.Breaks[currentBreak + 1].EndTime < currentTime) + while (currentBreak + 1 < Beatmap.Breaks.Count && Beatmap.Breaks[currentBreak + 1].EndTime < currentTime) currentBreak++; if (currentBreak >= 0) - lastTime = Math.Max(lastTime, beatmap.Breaks[currentBreak].EndTime); + lastTime = Math.Max(lastTime, Beatmap.Breaks[currentBreak].EndTime); } // Apply health adjustments diff --git a/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs deleted file mode 100644 index 5d2426e4b7..0000000000 --- a/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Objects; -using osu.Game.Utils; - -namespace osu.Game.Rulesets.Scoring -{ - /// - /// A which continuously drains health.
- /// At HP=0, the minimum health reached for a perfect play is 95%.
- /// At HP=5, the minimum health reached for a perfect play is 70%.
- /// At HP=10, the minimum health reached for a perfect play is 30%. - ///
- public abstract partial class LegacyDrainingHealthProcessor : HealthProcessor - { - protected double DrainStartTime { get; } - protected double GameplayEndTime { get; private set; } - - protected IBeatmap Beatmap { get; private set; } - protected PeriodTracker NoDrainPeriodTracker { get; private set; } - - public bool Log { get; set; } - - public double DrainRate { get; private set; } - - /// - /// Creates a new . - /// - /// The time after which draining should begin. - protected LegacyDrainingHealthProcessor(double drainStartTime) - { - DrainStartTime = drainStartTime; - } - - protected override void Update() - { - base.Update(); - - if (NoDrainPeriodTracker?.IsInAny(Time.Current) == true) - return; - - // When jumping in and out of gameplay time within a single frame, health should only be drained for the period within the gameplay time - double lastGameplayTime = Math.Clamp(Time.Current - Time.Elapsed, DrainStartTime, GameplayEndTime); - double currentGameplayTime = Math.Clamp(Time.Current, DrainStartTime, GameplayEndTime); - - Health.Value -= DrainRate * (currentGameplayTime - lastGameplayTime); - } - - public override void ApplyBeatmap(IBeatmap beatmap) - { - Beatmap = beatmap; - - if (beatmap.HitObjects.Count > 0) - GameplayEndTime = beatmap.HitObjects[^1].GetEndTime(); - - NoDrainPeriodTracker = new PeriodTracker(beatmap.Breaks.Select(breakPeriod => new Period( - beatmap.HitObjects - .Select(hitObject => hitObject.GetEndTime()) - .Where(endTime => endTime <= breakPeriod.StartTime) - .DefaultIfEmpty(double.MinValue) - .Last(), - beatmap.HitObjects - .Select(hitObject => hitObject.StartTime) - .Where(startTime => startTime >= breakPeriod.EndTime) - .DefaultIfEmpty(double.MaxValue) - .First() - ))); - - base.ApplyBeatmap(beatmap); - } - - protected override void Reset(bool storeResults) - { - base.Reset(storeResults); - - DrainRate = 1; - - if (storeResults) - DrainRate = ComputeDrainRate(); - } - - protected abstract double ComputeDrainRate(); - } -} From 8ad8764947835e9ce4070338b6e1ea81016df2b1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 13 Nov 2023 14:06:18 +0900 Subject: [PATCH 0087/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 2870696c03..1f6a65c450 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index f1159f58b9..70525a5c59 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 65b41138a347718297f71509e6a1dbc30d468543 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 13 Nov 2023 14:06:24 +0900 Subject: [PATCH 0088/1118] Add option to disable combo end --- osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs index f918868715..e92c3c9b97 100644 --- a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs @@ -26,6 +26,7 @@ namespace osu.Game.Rulesets.Osu.Scoring public Action? OnIterationFail; public Action? OnIterationSuccess; + public bool ApplyComboEndBonus { get; set; } = true; private double lowestHpEver; private double lowestHpEnd; @@ -148,7 +149,7 @@ namespace osu.Game.Rulesets.Osu.Scoring break; } - if (i == Beatmap.HitObjects.Count - 1 || ((OsuHitObject)Beatmap.HitObjects[i + 1]).NewCombo) + if (ApplyComboEndBonus && (i == Beatmap.HitObjects.Count - 1 || ((OsuHitObject)Beatmap.HitObjects[i + 1]).NewCombo)) { increaseHp(hpMultiplierComboEnd * hp_combo_geki + hpMultiplierNormal * hp_hit_300); From 35d4c483d78399fa3322b7a1db79110da36a759f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 13 Nov 2023 14:06:34 +0900 Subject: [PATCH 0089/1118] Improve commenting around small tick/large tick health --- osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 3a096fca88..5802f8fc0d 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -185,12 +185,12 @@ namespace osu.Game.Rulesets.Osu.Scoring return IBeatmapDifficultyInfo.DifficultyRange(Beatmap.Difficulty.DrainRate, -0.03, -0.125, -0.2); case HitResult.SmallTickHit: - // This result is always as a result of the slider tail. + // This result always comes from the slider tail, which is judged the same as a repeat. increase = 0.02; break; case HitResult.LargeTickHit: - // This result is either a result of a slider tick or a repeat. + // This result comes from either a slider tick or repeat. increase = hitObject is SliderTick ? 0.015 : 0.02; break; From fa976a5aa0eeb94dccbcd58b00d51a1c9d7e52df Mon Sep 17 00:00:00 2001 From: cs Date: Mon, 13 Nov 2023 08:24:09 +0100 Subject: [PATCH 0090/1118] Fix code style/quality issues --- .../TestSceneJuiceStreamSelectionBlueprint.cs | 6 +++--- .../JuiceStreamPathTest.cs | 2 +- .../Checks/CheckOffscreenObjectsTest.cs | 2 +- .../TestSceneOsuEditorSelectInvalidPath.cs | 2 +- .../TestScenePathControlPointVisualiser.cs | 8 ++++---- .../TestSceneSliderControlPointPiece.cs | 18 ++++++++--------- .../Editor/TestSceneSliderLengthValidity.cs | 2 +- .../TestSceneSliderPlacementBlueprint.cs | 16 +++++++-------- .../Editor/TestSceneSliderReversal.cs | 2 +- .../Editor/TestSceneSliderSnapping.cs | 10 +++++----- .../Editor/TestSceneSliderSplitting.cs | 20 +++++++++---------- .../TestSceneSlider.cs | 6 +++--- .../TestSceneSliderInput.cs | 2 +- .../TestSceneSliderSnaking.cs | 6 +++--- .../Components/PathControlPointPiece.cs | 14 ++++++------- .../Components/PathControlPointVisualiser.cs | 2 +- .../Sliders/SliderPlacementBlueprint.cs | 2 +- .../Edit/OsuSliderDrawingSettingsProvider.cs | 8 +++++--- .../Formats/LegacyBeatmapDecoderTest.cs | 8 ++++---- .../Gameplay/TestSceneBezierConverter.cs | 8 ++++---- .../Visual/Gameplay/TestSceneSliderPath.cs | 4 ++-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 18 +++++++---------- .../Edit/ComposerDistanceSnapProvider.cs | 2 +- osu.Game/Rulesets/Edit/IToolboxAttachment.cs | 10 ++++++++++ osu.Game/Rulesets/Objects/BezierConverter.cs | 8 ++++---- .../Objects/Legacy/ConvertHitObjectParser.cs | 10 +++++----- .../Rulesets/Objects/SliderPathExtensions.cs | 2 +- osu.Game/Rulesets/Objects/Types/PathType.cs | 12 +++++++---- osu.Game/osu.Game.csproj | 2 +- 29 files changed, 112 insertions(+), 100 deletions(-) create mode 100644 osu.Game/Rulesets/Edit/IToolboxAttachment.cs diff --git a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs index 16b51d414a..c96f32d87c 100644 --- a/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Catch.Tests/Editor/TestSceneJuiceStreamSelectionBlueprint.cs @@ -140,7 +140,7 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor AddStep("update hit object path", () => { - hitObject.Path = new SliderPath(PathType.PERFECTCURVE, new[] + hitObject.Path = new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(100, 100), @@ -190,14 +190,14 @@ namespace osu.Game.Rulesets.Catch.Tests.Editor [Test] public void TestVertexResampling() { - addBlueprintStep(100, 100, new SliderPath(PathType.PERFECTCURVE, new[] + addBlueprintStep(100, 100, new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(100, 100), new Vector2(50, 200), }), 0.5); AddAssert("1 vertex per 1 nested HO", () => getVertices().Count == hitObject.NestedHitObjects.Count); - AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); + AddAssert("slider path not yet changed", () => hitObject.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE); addAddVertexSteps(150, 150); AddAssert("slider path change to linear", () => hitObject.Path.ControlPoints[0].Type == PathType.LINEAR); } diff --git a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs index 82f24633b5..9fb55fc057 100644 --- a/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/JuiceStreamPathTest.cs @@ -154,7 +154,7 @@ namespace osu.Game.Rulesets.Catch.Tests } while (rng.Next(2) != 0); int length = sliderPath.ControlPoints.Count - start + 1; - sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.LINEAR : length == 3 ? PathType.PERFECTCURVE : PathType.BEZIER; + sliderPath.ControlPoints[start].Type = length <= 2 ? PathType.LINEAR : length == 3 ? PathType.PERFECT_CURVE : PathType.BEZIER; } while (rng.Next(3) != 0); if (rng.Next(5) == 0) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs index 8612a8eb57..5db6dc6cdd 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/Checks/CheckOffscreenObjectsTest.cs @@ -214,7 +214,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor.Checks Path = new SliderPath(new[] { // Circular arc shoots over the top of the screen. - new PathControlPoint(new Vector2(0, 0), PathType.PERFECTCURVE), + new PathControlPoint(new Vector2(0, 0), PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(-100, -200)), new PathControlPoint(new Vector2(100, -200)) }), diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs index 7ea4d40b90..43dae38004 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuEditorSelectInvalidPath.cs @@ -25,7 +25,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.PERFECTCURVE), + new PathControlPoint(new Vector2(0), PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(-100, 0)), new PathControlPoint(new Vector2(100, 20)) }; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 16800997f4..811ecf53e9 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor addContextMenuItemStep("Perfect curve"); assertControlPointPathType(0, PathType.BEZIER); - assertControlPointPathType(1, PathType.PERFECTCURVE); + assertControlPointPathType(1, PathType.PERFECT_CURVE); assertControlPointPathType(3, PathType.BEZIER); } @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor addContextMenuItemStep("Perfect curve"); assertControlPointPathType(0, PathType.BEZIER); - assertControlPointPathType(2, PathType.PERFECTCURVE); + assertControlPointPathType(2, PathType.PERFECT_CURVE); assertControlPointPathType(4, null); } @@ -124,7 +124,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor addContextMenuItemStep("Perfect curve"); assertControlPointPathType(0, PathType.LINEAR); - assertControlPointPathType(1, PathType.PERFECTCURVE); + assertControlPointPathType(1, PathType.PERFECT_CURVE); assertControlPointPathType(3, PathType.LINEAR); } @@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor createVisualiser(true); addControlPointStep(new Vector2(200), PathType.BEZIER); - addControlPointStep(new Vector2(300), PathType.PERFECTCURVE); + addControlPointStep(new Vector2(300), PathType.PERFECT_CURVE); addControlPointStep(new Vector2(500, 300)); addControlPointStep(new Vector2(700, 200), PathType.BEZIER); addControlPointStep(new Vector2(500, 100)); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs index 1d8d2cf01a..99ced30ffe 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderControlPointPiece.cs @@ -38,9 +38,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(256, 192), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), + new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE), + new PathControlPoint(new Vector2(300, 0), PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(400, 0)), new PathControlPoint(new Vector2(400, 150)) }) @@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(1, new Vector2(150, 50)); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("three control point pieces selected", () => this.ChildrenOfType>().Count(piece => piece.IsSelected.Value) == 3); assertControlPointPosition(2, new Vector2(450, 50)); - assertControlPointType(2, PathType.PERFECTCURVE); + assertControlPointType(2, PathType.PERFECT_CURVE); assertControlPointPosition(3, new Vector2(550, 50)); @@ -249,7 +249,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider moved", () => Precision.AlmostEquals(slider.Position, new Vector2(256, 192) + new Vector2(150, 50))); assertControlPointPosition(0, Vector2.Zero); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); assertControlPointPosition(1, new Vector2(0, 100)); @@ -288,7 +288,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(1, new Vector2(150, 50)); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -304,7 +304,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); assertControlPointPosition(4, new Vector2(150, 150)); - assertControlPointType(2, PathType.PERFECTCURVE); + assertControlPointType(2, PathType.PERFECT_CURVE); } [Test] @@ -312,12 +312,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { AddStep("change type to bezier", () => slider.Path.ControlPoints[2].Type = PathType.BEZIER); AddStep("add point", () => slider.Path.ControlPoints.Add(new PathControlPoint(new Vector2(500, 10)))); - AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PERFECTCURVE); + AddStep("change type to perfect", () => slider.Path.ControlPoints[3].Type = PathType.PERFECT_CURVE); moveMouseToControlPoint(4); AddStep("hold", () => InputManager.PressButton(MouseButton.Left)); - assertControlPointType(3, PathType.PERFECTCURVE); + assertControlPointType(3, PathType.PERFECT_CURVE); addMovementStep(new Vector2(350, 0.01f)); AddStep("release", () => InputManager.ReleaseButton(MouseButton.Left)); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs index 38ebeb7e8f..931c8c9e63 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderLengthValidity.cs @@ -126,7 +126,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor PathControlPoint[] points = { - new PathControlPoint(new Vector2(0), PathType.PERFECTCURVE), + new PathControlPoint(new Vector2(0), PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(100, 0)), new PathControlPoint(new Vector2(0, 10)) }; diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 4b120c1a3f..ecfc8105f1 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -90,7 +90,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); assertControlPointPosition(1, new Vector2(100, 0)); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -172,7 +172,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -241,7 +241,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointPosition(1, new Vector2(100, 0)); assertControlPointPosition(2, new Vector2(100)); assertControlPointType(0, PathType.LINEAR); - assertControlPointType(1, PathType.PERFECTCURVE); + assertControlPointType(1, PathType.PERFECT_CURVE); } [Test] @@ -269,8 +269,8 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointPosition(2, new Vector2(100)); assertControlPointPosition(3, new Vector2(200, 100)); assertControlPointPosition(4, new Vector2(200)); - assertControlPointType(0, PathType.PERFECTCURVE); - assertControlPointType(2, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); + assertControlPointType(2, PathType.PERFECT_CURVE); } [Test] @@ -326,7 +326,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -347,7 +347,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } [Test] @@ -385,7 +385,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertControlPointCount(3); - assertControlPointType(0, PathType.PERFECTCURVE); + assertControlPointType(0, PathType.PERFECT_CURVE); } private void addMovementStep(Vector2 position) => AddStep($"move mouse to {position}", () => InputManager.MoveMouseTo(InputManager.ToScreenSpace(position))); diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs index 0ddfc40946..a44c16a2e0 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderReversal.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private readonly PathControlPoint[][] paths = { createPathSegment( - PathType.PERFECTCURVE, + PathType.PERFECT_CURVE, new Vector2(200, -50), new Vector2(250, 0) ), diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs index c984d9168e..541fefb3a6 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSnapping.cs @@ -56,7 +56,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { ControlPoints = { - new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), + new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(136, 205)), new PathControlPoint(new Vector2(-4, 226)) } @@ -181,7 +181,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { OsuSelectionHandler selectionHandler; - AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); + AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE); AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); AddStep("rotate 90 degrees ccw", () => @@ -190,7 +190,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor selectionHandler.HandleRotation(-90); }); - AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); + AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE); } [Test] @@ -223,7 +223,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { OsuSelectionHandler selectionHandler; - AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); + AddAssert("first control point perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE); AddStep("select slider", () => EditorBeatmap.SelectedHitObjects.Add(slider)); AddStep("flip slider horizontally", () => @@ -232,7 +232,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor selectionHandler.OnPressed(new KeyBindingPressEvent(InputManager.CurrentState, GlobalAction.EditorFlipVertically)); }); - AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECTCURVE); + AddAssert("first control point still perfect", () => slider.Path.ControlPoints[0].Type == PathType.PERFECT_CURVE); } [Test] diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs index cded9165f4..6c7733e68a 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderSplitting.cs @@ -45,9 +45,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(0, 50), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), + new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE), + new PathControlPoint(new Vector2(300, 0), PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(400, 0)), new PathControlPoint(new Vector2(400, 150)) }) @@ -73,20 +73,20 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 2 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap, - (new Vector2(0, 50), PathType.PERFECTCURVE), + (new Vector2(0, 50), PathType.PERFECT_CURVE), (new Vector2(150, 200), null), (new Vector2(300, 50), null) ) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], slider.StartTime, endTime + split_gap, - (new Vector2(300, 50), PathType.PERFECTCURVE), + (new Vector2(300, 50), PathType.PERFECT_CURVE), (new Vector2(400, 50), null), (new Vector2(400, 200), null) )); AddStep("undo", () => Editor.Undo()); AddAssert("original slider restored", () => EditorBeatmap.HitObjects.Count == 1 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, endTime, - (new Vector2(0, 50), PathType.PERFECTCURVE), + (new Vector2(0, 50), PathType.PERFECT_CURVE), (new Vector2(150, 200), null), - (new Vector2(300, 50), PathType.PERFECTCURVE), + (new Vector2(300, 50), PathType.PERFECT_CURVE), (new Vector2(400, 50), null), (new Vector2(400, 200), null) )); @@ -104,7 +104,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(0, 50), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), + new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(150, 150)), new PathControlPoint(new Vector2(300, 0), PathType.BEZIER), new PathControlPoint(new Vector2(400, 0)), @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddAssert("slider split", () => slider is not null && EditorBeatmap.HitObjects.Count == 3 && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[0], 0, EditorBeatmap.HitObjects[1].StartTime - split_gap, - (new Vector2(0, 50), PathType.PERFECTCURVE), + (new Vector2(0, 50), PathType.PERFECT_CURVE), (new Vector2(150, 200), null), (new Vector2(300, 50), null) ) && sliderCreatedFor((Slider)EditorBeatmap.HitObjects[1], EditorBeatmap.HitObjects[0].GetEndTime() + split_gap, slider.StartTime - split_gap, @@ -165,9 +165,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor Position = new Vector2(0, 50), Path = new SliderPath(new[] { - new PathControlPoint(Vector2.Zero, PathType.PERFECTCURVE), + new PathControlPoint(Vector2.Zero, PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(150, 150)), - new PathControlPoint(new Vector2(300, 0), PathType.PERFECTCURVE), + new PathControlPoint(new Vector2(300, 0), PathType.PERFECT_CURVE), new PathControlPoint(new Vector2(400, 0)), new PathControlPoint(new Vector2(400, 150)) }) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs index 60003e7950..4600db8174 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSlider.cs @@ -219,7 +219,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(239, 176), - Path = new SliderPath(PathType.PERFECTCURVE, new[] + Path = new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(154, 28), @@ -255,7 +255,7 @@ namespace osu.Game.Rulesets.Osu.Tests SliderVelocityMultiplier = speedMultiplier, StartTime = Time.Current + time_offset, Position = new Vector2(0, -(distance / 2)), - Path = new SliderPath(PathType.PERFECTCURVE, new[] + Path = new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(0, distance), @@ -273,7 +273,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = Time.Current + time_offset, Position = new Vector2(-max_length / 2, 0), - Path = new SliderPath(PathType.PERFECTCURVE, new[] + Path = new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(max_length / 2, max_length / 2), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 08836ef819..0f7dd8b7bc 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -478,7 +478,7 @@ namespace osu.Game.Rulesets.Osu.Tests StartTime = time_slider_start, Position = new Vector2(0, 0), SliderVelocityMultiplier = 0.1f, - Path = new SliderPath(PathType.PERFECTCURVE, new[] + Path = new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(slider_path_length, 0), diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs index ebc5143aed..912b2b0626 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderSnaking.cs @@ -217,7 +217,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = 3000, Position = new Vector2(100, 100), - Path = new SliderPath(PathType.PERFECTCURVE, new[] + Path = new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(300, 200) @@ -227,7 +227,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = 13000, Position = new Vector2(100, 100), - Path = new SliderPath(PathType.PERFECTCURVE, new[] + Path = new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(300, 200) @@ -238,7 +238,7 @@ namespace osu.Game.Rulesets.Osu.Tests { StartTime = 23000, Position = new Vector2(100, 100), - Path = new SliderPath(PathType.PERFECTCURVE, new[] + Path = new SliderPath(PathType.PERFECT_CURVE, new[] { Vector2.Zero, new Vector2(300, 200) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 53228cff82..ac9048d5c7 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -221,7 +221,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// private void updatePathType() { - if (ControlPoint.Type != PathType.PERFECTCURVE) + if (ControlPoint.Type != PathType.PERFECT_CURVE) return; if (PointsInSegment.Count > 3) @@ -259,19 +259,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (ControlPoint.Type is not PathType pathType) return colours.Yellow; - switch (pathType) + switch (pathType.Type) { - case { Type: SplineType.Catmull }: + case SplineType.Catmull: return colours.SeaFoam; - case { Type: SplineType.BSpline, Degree: null }: - return colours.PinkLighter; + case SplineType.BSpline: + if (!pathType.Degree.HasValue) + return colours.PinkLighter; - case { Type: SplineType.BSpline, Degree: >= 1 }: int idx = Math.Clamp(pathType.Degree.Value, 0, 3); return new[] { colours.PinkDarker, colours.PinkDark, colours.Pink, colours.PinkLight }[idx]; - case { Type: SplineType.PerfectCurve }: + case SplineType.PerfectCurve: return colours.PurpleDark; default: diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 4e85835652..5ab050ed48 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -368,7 +368,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components // todo: hide/disable items which aren't valid for selected points curveTypeItems.Add(createMenuItemForPathType(PathType.LINEAR)); - curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECTCURVE)); + curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECT_CURVE)); curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER)); curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3))); curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 9ac28fe82a..c722f0fdbc 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -251,7 +251,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders break; case 3: - segmentStart.Type = PathType.PERFECTCURVE; + segmentStart.Type = PathType.PERFECT_CURVE; break; default: diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index ae772f53fc..643732d90a 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider + public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider, IToolboxAttachment { public BindableFloat Tolerance { get; } = new BindableFloat(0.1f) { @@ -27,12 +27,14 @@ namespace osu.Game.Rulesets.Osu.Edit private ExpandableSlider toleranceSlider = null!; - public OsuSliderDrawingSettingsProvider() + protected override void LoadComplete() { + base.LoadComplete(); + sliderTolerance.BindValueChanged(v => { float newValue = v.NewValue / 100f; - if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f)) + if (!Precision.AlmostEquals(newValue, Tolerance.Value)) Tolerance.Value = newValue; }); Tolerance.BindValueChanged(v => diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 18c21046fb..34b96bbd3f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -808,7 +808,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var first = ((IHasPath)decoded.HitObjects[0]).Path; Assert.That(first.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECTCURVE)); + Assert.That(first.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECT_CURVE)); Assert.That(first.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); Assert.That(first.ControlPoints[1].Type, Is.EqualTo(null)); @@ -827,7 +827,7 @@ namespace osu.Game.Tests.Beatmaps.Formats var second = ((IHasPath)decoded.HitObjects[1]).Path; Assert.That(second.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECTCURVE)); + Assert.That(second.ControlPoints[0].Type, Is.EqualTo(PathType.PERFECT_CURVE)); Assert.That(second.ControlPoints[1].Position, Is.EqualTo(new Vector2(161, -244))); Assert.That(second.ControlPoints[1].Type, Is.EqualTo(null)); Assert.That(second.ControlPoints[2].Position, Is.EqualTo(new Vector2(376, -3))); @@ -904,12 +904,12 @@ namespace osu.Game.Tests.Beatmaps.Formats var seventh = ((IHasPath)decoded.HitObjects[6]).Path; Assert.That(seventh.ControlPoints[0].Position, Is.EqualTo(Vector2.Zero)); - Assert.That(seventh.ControlPoints[0].Type == PathType.PERFECTCURVE); + Assert.That(seventh.ControlPoints[0].Type == PathType.PERFECT_CURVE); Assert.That(seventh.ControlPoints[1].Position, Is.EqualTo(new Vector2(75, 145))); Assert.That(seventh.ControlPoints[1].Type == null); Assert.That(seventh.ControlPoints[2].Position, Is.EqualTo(new Vector2(170, 75))); - Assert.That(seventh.ControlPoints[2].Type == PathType.PERFECTCURVE); + Assert.That(seventh.ControlPoints[2].Type == PathType.PERFECT_CURVE); Assert.That(seventh.ControlPoints[3].Position, Is.EqualTo(new Vector2(300, 145))); Assert.That(seventh.ControlPoints[3].Type == null); Assert.That(seventh.ControlPoints[4].Position, Is.EqualTo(new Vector2(410, 20))); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs index e2333011c7..27497f77be 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBezierConverter.cs @@ -143,7 +143,7 @@ namespace osu.Game.Tests.Visual.Gameplay { path.ControlPoints.AddRange(createSegment(PathType.LINEAR, Vector2.Zero, new Vector2(100, 0))); path.ControlPoints.AddRange(createSegment(PathType.BEZIER, new Vector2(100, 0), new Vector2(150, 30), new Vector2(100, 100))); - path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero)); + path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, new Vector2(100, 100), new Vector2(25, 50), Vector2.Zero)); }); } @@ -159,7 +159,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create path", () => { - path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(width / 2, height), new Vector2(width, 0))); }); } @@ -172,11 +172,11 @@ namespace osu.Game.Tests.Visual.Gameplay switch (points) { case 2: - path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(0, 100))); break; case 4: - path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); break; } }); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs index d44af45fe4..44a2e5fb9b 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSliderPath.cs @@ -149,11 +149,11 @@ namespace osu.Game.Tests.Visual.Gameplay switch (points) { case 2: - path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(0, 100))); break; case 4: - path.ControlPoints.AddRange(createSegment(PathType.PERFECTCURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); + path.ControlPoints.AddRange(createSegment(PathType.PERFECT_CURVE, Vector2.Zero, new Vector2(0, 100), new Vector2(100), new Vector2(100, 0))); break; } }); diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index ff446206ac..b375a6f7ff 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -437,7 +437,7 @@ namespace osu.Game.Beatmaps.Formats // Explicit segments have a new format in which the type is injected into the middle of the control point string. // To preserve compatibility with osu-stable as much as possible, explicit segments with the same type are converted to use implicit segments by duplicating the control point. // One exception are consecutive perfect curves, which aren't supported in osu!stable and can lead to decoding issues if encoded as implicit segments - bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PERFECTCURVE; + bool needsExplicitSegment = point.Type != lastType || point.Type == PathType.PERFECT_CURVE; // Another exception to this is when the last two control points of the last segment were duplicated. This is not a scenario supported by osu!stable. // Lazer does not add implicit segments for the last two control points of _any_ explicit segment, so an explicit segment is forced in order to maintain consistency with the decoder. @@ -453,25 +453,21 @@ namespace osu.Game.Beatmaps.Formats if (needsExplicitSegment) { - switch (point.Type) + switch (point.Type?.Type) { - case { Type: SplineType.BSpline, Degree: > 0 }: - writer.Write($"B{point.Type.Value.Degree}|"); + case SplineType.BSpline: + writer.Write(point.Type.Value.Degree > 0 ? $"B{point.Type.Value.Degree}|" : "B|"); break; - case { Type: SplineType.BSpline, Degree: <= 0 }: - writer.Write("B|"); - break; - - case { Type: SplineType.Catmull }: + case SplineType.Catmull: writer.Write("C|"); break; - case { Type: SplineType.PerfectCurve }: + case SplineType.PerfectCurve: writer.Write("P|"); break; - case { Type: SplineType.Linear }: + case SplineType.Linear: writer.Write("L|"); break; } diff --git a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs index ddf539771d..68411d2b01 100644 --- a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs @@ -29,7 +29,7 @@ using osu.Game.Screens.Edit.Components.TernaryButtons; namespace osu.Game.Rulesets.Edit { - public abstract partial class ComposerDistanceSnapProvider : Component, IDistanceSnapProvider, IScrollBindingHandler + public abstract partial class ComposerDistanceSnapProvider : Component, IDistanceSnapProvider, IScrollBindingHandler, IToolboxAttachment { private const float adjust_step = 0.1f; diff --git a/osu.Game/Rulesets/Edit/IToolboxAttachment.cs b/osu.Game/Rulesets/Edit/IToolboxAttachment.cs new file mode 100644 index 0000000000..7d7c5980b2 --- /dev/null +++ b/osu.Game/Rulesets/Edit/IToolboxAttachment.cs @@ -0,0 +1,10 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Rulesets.Edit +{ + public interface IToolboxAttachment + { + void AttachToToolbox(ExpandingToolboxContainer toolbox); + } +} diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index ed86fc10e0..5dc0839d37 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -126,9 +126,9 @@ namespace osu.Game.Rulesets.Objects var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); var segmentType = controlPoints[start].Type ?? PathType.LINEAR; - switch (segmentType) + switch (segmentType.Type) { - case { Type: SplineType.Catmull }: + case SplineType.Catmull: foreach (var segment in ConvertCatmullToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) @@ -139,7 +139,7 @@ namespace osu.Game.Rulesets.Objects break; - case { Type: SplineType.Linear }: + case SplineType.Linear: foreach (var segment in ConvertLinearToBezierAnchors(segmentVertices)) { for (int j = 0; j < segment.Length - 1; j++) @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Objects break; - case { Type: SplineType.PerfectCurve }: + case SplineType.PerfectCurve: var circleResult = ConvertCircleToBezierAnchors(segmentVertices); for (int j = 0; j < circleResult.Length - 1; j++) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 6a13a897c4..92a92dca8f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -224,19 +224,19 @@ namespace osu.Game.Rulesets.Objects.Legacy { default: case 'C': - return new PathType(SplineType.Catmull); + return PathType.CATMULL; case 'B': if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0) - return new PathType { Type = SplineType.BSpline, Degree = degree }; + return PathType.BSpline(degree); return new PathType(SplineType.BSpline); case 'L': - return new PathType(SplineType.Linear); + return PathType.LINEAR; case 'P': - return new PathType(SplineType.PerfectCurve); + return PathType.PERFECT_CURVE; } } @@ -323,7 +323,7 @@ namespace osu.Game.Rulesets.Objects.Legacy readPoint(endPoint, offset, out vertices[^1]); // Edge-case rules (to match stable). - if (type == PathType.PERFECTCURVE) + if (type == PathType.PERFECT_CURVE) { if (vertices.Length != 3) type = PathType.BEZIER; diff --git a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs index d7e5e4574d..29b34ae4f0 100644 --- a/osu.Game/Rulesets/Objects/SliderPathExtensions.cs +++ b/osu.Game/Rulesets/Objects/SliderPathExtensions.cs @@ -53,7 +53,7 @@ namespace osu.Game.Rulesets.Objects inheritedLinearPoints.ForEach(p => p.Type = null); // Recalculate middle perfect curve control points at the end of the slider path. - if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PERFECTCURVE && controlPoints[^2].Type is null && segmentEnds.Any()) + if (controlPoints.Count >= 3 && controlPoints[^3].Type == PathType.PERFECT_CURVE && controlPoints[^2].Type is null && segmentEnds.Any()) { double lastSegmentStart = segmentEnds.Length > 1 ? segmentEnds[^2] : 0; double lastSegmentEnd = segmentEnds[^1]; diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index a6e8e173d4..4fb48bb8b4 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; namespace osu.Game.Rulesets.Objects.Types { @@ -14,12 +13,12 @@ namespace osu.Game.Rulesets.Objects.Types PerfectCurve } - public readonly struct PathType + public readonly struct PathType : IEquatable { public static readonly PathType CATMULL = new PathType(SplineType.Catmull); public static readonly PathType BEZIER = new PathType(SplineType.BSpline); public static readonly PathType LINEAR = new PathType(SplineType.Linear); - public static readonly PathType PERFECTCURVE = new PathType(SplineType.PerfectCurve); + public static readonly PathType PERFECT_CURVE = new PathType(SplineType.PerfectCurve); /// /// The type of the spline that should be used to interpret the control points of the path. @@ -52,8 +51,13 @@ namespace osu.Game.Rulesets.Objects.Types public static PathType BSpline(int degree) { - Debug.Assert(degree > 0); + if (degree <= 0) + throw new ArgumentOutOfRangeException(nameof(degree), "The degree of a B-Spline path must be greater than zero."); + return new PathType { Type = SplineType.BSpline, Degree = degree }; } + + public bool Equals(PathType other) + => Type == other.Type && Degree == other.Degree; } } diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index fc10f90e8f..8d42cd22e3 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From e3137d575bbea46423c8124a9ade25fab18d03f2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 14 Nov 2023 01:11:17 +0900 Subject: [PATCH 0091/1118] Fix osu! and base HP processor break time implementation --- .../TestSceneOsuHealthProcessor.cs | 94 +++++++++++++++++++ .../Scoring/OsuHealthProcessor.cs | 25 ++--- .../TestSceneDrainingHealthProcessor.cs | 81 +++++++++++++++- .../Scoring/DrainingHealthProcessor.cs | 44 ++++----- 4 files changed, 203 insertions(+), 41 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs new file mode 100644 index 0000000000..f810bbf155 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs @@ -0,0 +1,94 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Scoring; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class TestSceneOsuHealthProcessor + { + [Test] + public void TestNoBreak() + { + OsuHealthProcessor hp = new OsuHealthProcessor(-1000); + hp.ApplyBeatmap(new Beatmap + { + HitObjects = + { + new HitCircle { StartTime = 0 }, + new HitCircle { StartTime = 2000 } + } + }); + + Assert.That(hp.DrainRate, Is.EqualTo(1.4E-5).Within(0.1E-5)); + } + + [Test] + public void TestSingleBreak() + { + OsuHealthProcessor hp = new OsuHealthProcessor(-1000); + hp.ApplyBeatmap(new Beatmap + { + HitObjects = + { + new HitCircle { StartTime = 0 }, + new HitCircle { StartTime = 2000 } + }, + Breaks = + { + new BreakPeriod(500, 1500) + } + }); + + Assert.That(hp.DrainRate, Is.EqualTo(4.3E-5).Within(0.1E-5)); + } + + [Test] + public void TestOverlappingBreak() + { + OsuHealthProcessor hp = new OsuHealthProcessor(-1000); + hp.ApplyBeatmap(new Beatmap + { + HitObjects = + { + new HitCircle { StartTime = 0 }, + new HitCircle { StartTime = 2000 } + }, + Breaks = + { + new BreakPeriod(500, 1400), + new BreakPeriod(750, 1500), + } + }); + + Assert.That(hp.DrainRate, Is.EqualTo(4.3E-5).Within(0.1E-5)); + } + + [Test] + public void TestSequentialBreak() + { + OsuHealthProcessor hp = new OsuHealthProcessor(-1000); + hp.ApplyBeatmap(new Beatmap + { + HitObjects = + { + new HitCircle { StartTime = 0 }, + new HitCircle { StartTime = 2000 } + }, + Breaks = + { + new BreakPeriod(500, 1000), + new BreakPeriod(1000, 1500), + } + }); + + Assert.That(hp.DrainRate, Is.EqualTo(4.3E-5).Within(0.1E-5)); + } + } +} diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 5802f8fc0d..3207c7fb51 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -3,9 +3,7 @@ using System; using System.Linq; -using osu.Framework.Logging; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; @@ -64,25 +62,16 @@ namespace osu.Game.Rulesets.Osu.Scoring { HitObject h = Beatmap.HitObjects[i]; - // Find active break (between current and lastTime) - double localLastTime = lastTime; - double breakTime = 0; - - // TODO: This doesn't handle overlapping/sequential breaks correctly (/b/614). - // Subtract any break time from the duration since the last object - if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) + while (currentBreak < Beatmap.Breaks.Count && Beatmap.Breaks[currentBreak].EndTime <= h.StartTime) { - BreakPeriod e = Beatmap.Breaks[currentBreak]; - - if (e.StartTime >= localLastTime && e.EndTime <= h.StartTime) - { - // consider break start equal to object end time for version 8+ since drain stops during this time - breakTime = (Beatmap.BeatmapInfo.BeatmapVersion < 8) ? (e.EndTime - e.StartTime) : e.EndTime - localLastTime; - currentBreak++; - } + // If two hitobjects are separated by a break period, there is no drain for the full duration between the hitobjects. + // This differs from legacy (version < 8) beatmaps which continue draining until the break section is entered, + // but this shouldn't have a noticeable impact in practice. + lastTime = h.StartTime; + currentBreak++; } - reduceHp(testDrop * (h.StartTime - lastTime - breakTime)); + reduceHp(testDrop * (h.StartTime - lastTime)); lastTime = h.GetEndTime(); diff --git a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs index fd0bff101f..584a9e09c0 100644 --- a/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneDrainingHealthProcessor.cs @@ -192,7 +192,8 @@ namespace osu.Game.Tests.Gameplay AddStep("apply perfect hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = HitResult.Perfect })); AddAssert("not failed", () => !processor.HasFailed); - AddStep($"apply {resultApplied.ToString().ToLowerInvariant()} hit result", () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = resultApplied })); + AddStep($"apply {resultApplied.ToString().ToLowerInvariant()} hit result", + () => processor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new Judgement()) { Type = resultApplied })); AddAssert("failed", () => processor.HasFailed); } @@ -232,6 +233,84 @@ namespace osu.Game.Tests.Gameplay assertHealthEqualTo(1); } + [Test] + public void TestNoBreakDrainRate() + { + DrainingHealthProcessor hp = new DrainingHealthProcessor(-1000); + hp.ApplyBeatmap(new Beatmap + { + HitObjects = + { + new JudgeableHitObject { StartTime = 0 }, + new JudgeableHitObject { StartTime = 2000 } + } + }); + + Assert.That(hp.DrainRate, Is.EqualTo(4.5E-5).Within(0.1E-5)); + } + + [Test] + public void TestSingleBreakDrainRate() + { + DrainingHealthProcessor hp = new DrainingHealthProcessor(-1000); + hp.ApplyBeatmap(new Beatmap + { + HitObjects = + { + new JudgeableHitObject { StartTime = 0 }, + new JudgeableHitObject { StartTime = 2000 } + }, + Breaks = + { + new BreakPeriod(500, 1500) + } + }); + + Assert.That(hp.DrainRate, Is.EqualTo(9.1E-5).Within(0.1E-5)); + } + + [Test] + public void TestOverlappingBreakDrainRate() + { + DrainingHealthProcessor hp = new DrainingHealthProcessor(-1000); + hp.ApplyBeatmap(new Beatmap + { + HitObjects = + { + new JudgeableHitObject { StartTime = 0 }, + new JudgeableHitObject { StartTime = 2000 } + }, + Breaks = + { + new BreakPeriod(500, 1400), + new BreakPeriod(750, 1500), + } + }); + + Assert.That(hp.DrainRate, Is.EqualTo(9.1E-5).Within(0.1E-5)); + } + + [Test] + public void TestSequentialBreakDrainRate() + { + DrainingHealthProcessor hp = new DrainingHealthProcessor(-1000); + hp.ApplyBeatmap(new Beatmap + { + HitObjects = + { + new JudgeableHitObject { StartTime = 0 }, + new JudgeableHitObject { StartTime = 2000 } + }, + Breaks = + { + new BreakPeriod(500, 1000), + new BreakPeriod(1000, 1500), + } + }); + + Assert.That(hp.DrainRate, Is.EqualTo(9.1E-5).Within(0.1E-5)); + } + private Beatmap createBeatmap(double startTime, double endTime, params BreakPeriod[] breaks) { var beatmap = new Beatmap diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index a8808d08e5..3d4fb862fb 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -103,18 +103,20 @@ namespace osu.Game.Rulesets.Scoring if (beatmap.HitObjects.Count > 0) gameplayEndTime = beatmap.HitObjects[^1].GetEndTime(); - noDrainPeriodTracker = new PeriodTracker(beatmap.Breaks.Select(breakPeriod => new Period( - beatmap.HitObjects - .Select(hitObject => hitObject.GetEndTime()) - .Where(endTime => endTime <= breakPeriod.StartTime) - .DefaultIfEmpty(double.MinValue) - .Last(), - beatmap.HitObjects - .Select(hitObject => hitObject.StartTime) - .Where(startTime => startTime >= breakPeriod.EndTime) - .DefaultIfEmpty(double.MaxValue) - .First() - ))); + noDrainPeriodTracker = new PeriodTracker( + beatmap.Breaks.Select(breakPeriod => + new Period( + beatmap.HitObjects + .Select(hitObject => hitObject.GetEndTime()) + .Where(endTime => endTime <= breakPeriod.StartTime) + .DefaultIfEmpty(double.MinValue) + .Last(), + beatmap.HitObjects + .Select(hitObject => hitObject.StartTime) + .Where(startTime => startTime >= breakPeriod.EndTime) + .DefaultIfEmpty(double.MaxValue) + .First() + ))); targetMinimumHealth = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, min_health_target, mid_health_target, max_health_target); @@ -161,26 +163,24 @@ namespace osu.Game.Rulesets.Scoring { double currentHealth = 1; double lowestHealth = 1; - int currentBreak = -1; + int currentBreak = 0; for (int i = 0; i < healthIncreases.Count; i++) { double currentTime = healthIncreases[i].time; double lastTime = i > 0 ? healthIncreases[i - 1].time : DrainStartTime; - // Subtract any break time from the duration since the last object - if (Beatmap.Breaks.Count > 0) + while (currentBreak < Beatmap.Breaks.Count && Beatmap.Breaks[currentBreak].EndTime <= currentTime) { - // Advance the last break occuring before the current time - while (currentBreak + 1 < Beatmap.Breaks.Count && Beatmap.Breaks[currentBreak + 1].EndTime < currentTime) - currentBreak++; - - if (currentBreak >= 0) - lastTime = Math.Max(lastTime, Beatmap.Breaks[currentBreak].EndTime); + // If two hitobjects are separated by a break period, there is no drain for the full duration between the hitobjects. + // This differs from legacy (version < 8) beatmaps which continue draining until the break section is entered, + // but this shouldn't have a noticeable impact in practice. + lastTime = currentTime; + currentBreak++; } // Apply health adjustments - currentHealth -= (healthIncreases[i].time - lastTime) * result; + currentHealth -= (currentTime - lastTime) * result; lowestHealth = Math.Min(lowestHealth, currentHealth); currentHealth = Math.Min(1, currentHealth + healthIncreases[i].health); From 90ec6895d100dea67e282c75a57779ffa87d0f5d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20Mu=CC=88ller-Ho=CC=88hne?= Date: Tue, 14 Nov 2023 16:52:45 +0900 Subject: [PATCH 0092/1118] Automatic red control point generation & corner threshold --- .../Sliders/SliderPlacementBlueprint.cs | 28 +++++++++-- .../Edit/ISliderDrawingSettingsProvider.cs | 1 + .../Edit/OsuSliderDrawingSettingsProvider.cs | 46 +++++++++++++++++-- 3 files changed, 65 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index c722f0fdbc..69e2a40689 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -86,6 +86,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); }, true); + + drawingSettingsProvider.CornerThreshold.BindValueChanged(e => + { + if (bSplineBuilder.CornerThreshold != e.NewValue) + bSplineBuilder.CornerThreshold = e.NewValue; + updateSliderPathFromBSplineBuilder(); + }, true); } [Resolved] @@ -100,8 +107,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders case SliderPlacementState.Initial: BeginPlacement(); - double? nearestSliderVelocity = (editorBeatmap.HitObjects - .LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier; + double? nearestSliderVelocity = (editorBeatmap + .HitObjects + .LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier; HitObject.SliderVelocityMultiplier = nearestSliderVelocity ?? 1; HitObject.Position = ToLocalSpace(result.ScreenSpacePosition); @@ -193,9 +201,19 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { Scheduler.AddOnce(static self => { - var cps = self.bSplineBuilder.GetControlPoints(); - self.HitObject.Path.ControlPoints.RemoveRange(1, self.HitObject.Path.ControlPoints.Count - 1); - self.HitObject.Path.ControlPoints.AddRange(cps.Skip(1).Select(v => new PathControlPoint(v))); + var cps = self.bSplineBuilder.ControlPoints; + var sliderCps = self.HitObject.Path.ControlPoints; + sliderCps.RemoveRange(1, sliderCps.Count - 1); + + // Add the control points from the BSpline builder while converting control points that repeat + // three or more times to a single PathControlPoint with linear type. + for (int i = 1; i < cps.Count; i++) + { + bool isSharp = i < cps.Count - 2 && cps[i] == cps[i + 1] && cps[i] == cps[i + 2]; + sliderCps.Add(new PathControlPoint(cps[i], isSharp ? PathType.BSpline(3) : null)); + if (isSharp) + i += 2; + } }, this); } diff --git a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs index 1138588259..31ed98e1dd 100644 --- a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs @@ -8,5 +8,6 @@ namespace osu.Game.Rulesets.Osu.Edit public interface ISliderDrawingSettingsProvider { BindableFloat Tolerance { get; } + BindableFloat CornerThreshold { get; } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index 643732d90a..1fe1326f38 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -12,20 +12,34 @@ namespace osu.Game.Rulesets.Osu.Edit { public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider, IToolboxAttachment { - public BindableFloat Tolerance { get; } = new BindableFloat(0.1f) + public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) + { + MinValue = 0.05f, + MaxValue = 3f, + Precision = 0.01f + }; + + private readonly BindableInt sliderTolerance = new BindableInt(50) + { + MinValue = 5, + MaxValue = 100 + }; + + public BindableFloat CornerThreshold { get; } = new BindableFloat(0.4f) { MinValue = 0.05f, MaxValue = 1f, Precision = 0.01f }; - private readonly BindableInt sliderTolerance = new BindableInt(10) + private readonly BindableInt sliderCornerThreshold = new BindableInt(40) { MinValue = 5, MaxValue = 100 }; private ExpandableSlider toleranceSlider = null!; + private ExpandableSlider cornerThresholdSlider = null!; protected override void LoadComplete() { @@ -33,16 +47,28 @@ namespace osu.Game.Rulesets.Osu.Edit sliderTolerance.BindValueChanged(v => { - float newValue = v.NewValue / 100f; - if (!Precision.AlmostEquals(newValue, Tolerance.Value)) + float newValue = v.NewValue / 33f; + if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f)) Tolerance.Value = newValue; }); Tolerance.BindValueChanged(v => { - int newValue = (int)Math.Round(v.NewValue * 100f); + int newValue = (int)Math.Round(v.NewValue * 33f); if (sliderTolerance.Value != newValue) sliderTolerance.Value = newValue; }); + sliderCornerThreshold.BindValueChanged(v => + { + float newValue = v.NewValue / 100f; + if (!Precision.AlmostEquals(newValue, CornerThreshold.Value, 1e-7f)) + CornerThreshold.Value = newValue; + }); + CornerThreshold.BindValueChanged(v => + { + int newValue = (int)Math.Round(v.NewValue * 100f); + if (sliderCornerThreshold.Value != newValue) + sliderCornerThreshold.Value = newValue; + }); } public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) @@ -54,6 +80,10 @@ namespace osu.Game.Rulesets.Osu.Edit toleranceSlider = new ExpandableSlider { Current = sliderTolerance + }, + cornerThresholdSlider = new ExpandableSlider + { + Current = sliderCornerThreshold } } }); @@ -63,6 +93,12 @@ namespace osu.Game.Rulesets.Osu.Edit toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; }, true); + + sliderCornerThreshold.BindValueChanged(e => + { + cornerThresholdSlider.ContractedLabelText = $"C. T.: {e.NewValue:N0}"; + cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {e.NewValue:N0}"; + }, true); } } } From f5e1734de9c37b1ea9fc8aac79bdf662e5503da0 Mon Sep 17 00:00:00 2001 From: Poyo Date: Tue, 14 Nov 2023 13:51:55 -0800 Subject: [PATCH 0093/1118] Use soft-cast to access IGameplayClock --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 7cfb6aedf0..5abca168ed 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -154,9 +154,6 @@ namespace osu.Game.Rulesets.Objects.Drawables [Resolved(CanBeNull = true)] private IPooledHitObjectProvider pooledObjectProvider { get; set; } - [Resolved(CanBeNull = true)] - private IGameplayClock gameplayClock { get; set; } - /// /// Whether the initialization logic in has applied. /// @@ -707,7 +704,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } Result.RawTime = Time.Current; - Result.GameplayRate = gameplayClock?.GetTrueGameplayRate() ?? 1.0; + Result.GameplayRate = (Clock as IGameplayClock)?.GetTrueGameplayRate() ?? 1.0; if (Result.HasResult) updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss); From ceeaf5b67c527c787b8d34eb3594207ed9ac14d7 Mon Sep 17 00:00:00 2001 From: cs Date: Wed, 15 Nov 2023 07:09:33 +0100 Subject: [PATCH 0094/1118] CI fixes and small tweaks --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 4 ++-- .../Edit/OsuSliderDrawingSettingsProvider.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 69e2a40689..df7d2c716b 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -108,8 +108,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders BeginPlacement(); double? nearestSliderVelocity = (editorBeatmap - .HitObjects - .LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier; + .HitObjects + .LastOrDefault(h => h is Slider && h.GetEndTime() < HitObject.StartTime) as Slider)?.SliderVelocityMultiplier; HitObject.SliderVelocityMultiplier = nearestSliderVelocity ?? 1; HitObject.Position = ToLocalSpace(result.ScreenSpacePosition); diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index 1fe1326f38..4326b2e943 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Edit sliderTolerance.BindValueChanged(v => { float newValue = v.NewValue / 33f; - if (!Precision.AlmostEquals(newValue, Tolerance.Value, 1e-7f)) + if (!Precision.AlmostEquals(newValue, Tolerance.Value)) Tolerance.Value = newValue; }); Tolerance.BindValueChanged(v => @@ -60,7 +60,7 @@ namespace osu.Game.Rulesets.Osu.Edit sliderCornerThreshold.BindValueChanged(v => { float newValue = v.NewValue / 100f; - if (!Precision.AlmostEquals(newValue, CornerThreshold.Value, 1e-7f)) + if (!Precision.AlmostEquals(newValue, CornerThreshold.Value)) CornerThreshold.Value = newValue; }); CornerThreshold.BindValueChanged(v => @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Edit public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) { - toolboxContainer.Add(new EditorToolboxGroup("drawing") + toolboxContainer.Add(new EditorToolboxGroup("slider") { Children = new Drawable[] { From 520642975bda8de23c51e2b350c637a93115b08c Mon Sep 17 00:00:00 2001 From: cs Date: Wed, 15 Nov 2023 07:45:09 +0100 Subject: [PATCH 0095/1118] Fix control point hover text and context menu --- .../Sliders/Components/PathControlPointPiece.cs | 2 +- .../Sliders/Components/PathControlPointVisualiser.cs | 2 +- osu.Game/Rulesets/Objects/Types/PathType.cs | 12 +++++++++++- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index ac9048d5c7..03792d8520 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -279,6 +279,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } } - public LocalisableString TooltipText => ControlPoint.Type.ToString() ?? string.Empty; + public LocalisableString TooltipText => ControlPoint.Type?.Description ?? string.Empty; } } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 5ab050ed48..faae966d02 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -403,7 +403,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components int totalCount = Pieces.Count(p => p.IsSelected.Value); int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type == type); - var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.ToString().Humanize(), MenuItemType.Standard, _ => + var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type!.Value.Description, MenuItemType.Standard, _ => { foreach (var p in Pieces.Where(p => p.IsSelected.Value)) updatePathType(p, type); diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 4fb48bb8b4..e4249154e5 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; namespace osu.Game.Rulesets.Objects.Types { @@ -13,7 +14,7 @@ namespace osu.Game.Rulesets.Objects.Types PerfectCurve } - public readonly struct PathType : IEquatable + public readonly struct PathType : IEquatable, IHasDescription { public static readonly PathType CATMULL = new PathType(SplineType.Catmull); public static readonly PathType BEZIER = new PathType(SplineType.BSpline); @@ -31,6 +32,15 @@ namespace osu.Game.Rulesets.Objects.Types /// public int? Degree { get; init; } + public string Description => Type switch + { + SplineType.Catmull => "Catmull", + SplineType.BSpline => Degree == null ? "Bezier" : "B-Spline", + SplineType.Linear => "Linear", + SplineType.PerfectCurve => "Perfect Curve", + _ => Type.ToString() + }; + public PathType(SplineType splineType) { Type = splineType; From 360864fd7b6dac30bda9a2cf4092a316b280427d Mon Sep 17 00:00:00 2001 From: cs Date: Wed, 15 Nov 2023 07:45:28 +0100 Subject: [PATCH 0096/1118] Hide catmull curve type when possible --- .../Sliders/Components/PathControlPointVisualiser.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index faae966d02..95c72a0a62 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -371,7 +371,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECT_CURVE)); curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER)); curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3))); - curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); + + var hoveredPiece = Pieces.FirstOrDefault(p => p.IsHovered); + + if (hoveredPiece?.ControlPoint.Type == PathType.CATMULL) + curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); var menuItems = new List { From a73c870712ff5e2bb6613db29dd8d45d71c1f516 Mon Sep 17 00:00:00 2001 From: Poyo Date: Wed, 15 Nov 2023 17:00:35 -0800 Subject: [PATCH 0097/1118] Allow GameplayRate to be nullable and assert before use --- osu.Game/Rulesets/Judgements/JudgementResult.cs | 2 +- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- osu.Game/Rulesets/Scoring/HitEvent.cs | 4 ++-- osu.Game/Rulesets/Scoring/HitEventExtensions.cs | 5 ++++- osu.Game/Rulesets/UI/Playfield.cs | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index 603d470954..db621b4851 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Judgements /// /// The gameplay rate at the time this occurred. /// - public double GameplayRate { get; internal set; } + public double? GameplayRate { get; internal set; } /// /// The combo prior to this occurring. diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 5abca168ed..baf13d8911 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -704,7 +704,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } Result.RawTime = Time.Current; - Result.GameplayRate = (Clock as IGameplayClock)?.GetTrueGameplayRate() ?? 1.0; + Result.GameplayRate = (Clock as IGameplayClock)?.GetTrueGameplayRate(); if (Result.HasResult) updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss); diff --git a/osu.Game/Rulesets/Scoring/HitEvent.cs b/osu.Game/Rulesets/Scoring/HitEvent.cs index afa654318b..1763190899 100644 --- a/osu.Game/Rulesets/Scoring/HitEvent.cs +++ b/osu.Game/Rulesets/Scoring/HitEvent.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Scoring /// /// The true gameplay rate at the time of the event. /// - public readonly double GameplayRate; + public readonly double? GameplayRate; /// /// The hit result. @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Scoring /// The that triggered the event. /// The previous . /// A position corresponding to the event. - public HitEvent(double timeOffset, double gameplayRate, HitResult result, HitObject hitObject, [CanBeNull] HitObject lastHitObject, [CanBeNull] Vector2? position) + public HitEvent(double timeOffset, double? gameplayRate, HitResult result, HitObject hitObject, [CanBeNull] HitObject lastHitObject, [CanBeNull] Vector2? position) { TimeOffset = timeOffset; GameplayRate = gameplayRate; diff --git a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs index a93385ef43..70a11ae760 100644 --- a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs +++ b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; namespace osu.Game.Rulesets.Scoring @@ -18,8 +19,10 @@ namespace osu.Game.Rulesets.Scoring /// public static double? CalculateUnstableRate(this IEnumerable hitEvents) { + Debug.Assert(!hitEvents.Any(ev => ev.GameplayRate == null)); + // Division by gameplay rate is to account for TimeOffset scaling with gameplay rate. - double[] timeOffsets = hitEvents.Where(affectsUnstableRate).Select(ev => ev.TimeOffset / ev.GameplayRate).ToArray(); + double[] timeOffsets = hitEvents.Where(affectsUnstableRate).Select(ev => ev.TimeOffset / ev.GameplayRate!.Value).ToArray(); return 10 * standardDeviation(timeOffsets); } diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index 17baf8838c..eb29d8f30a 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -473,7 +473,7 @@ namespace osu.Game.Rulesets.UI private void onNewResult(DrawableHitObject drawable, JudgementResult result) { - Debug.Assert(result != null && drawable.Entry?.Result == result && result.RawTime != null && result.GameplayRate != 0.0); + Debug.Assert(result != null && drawable.Entry?.Result == result && result.RawTime != null && result.GameplayRate != null); judgedEntries.Push(drawable.Entry.AsNonNull()); NewResult?.Invoke(drawable, result); From 265ae6fd30697495ab6a3896cd37eac883cfdcaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Nov 2023 15:14:32 +0900 Subject: [PATCH 0098/1118] Remove unused using refs --- osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 5802f8fc0d..9ed1820045 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -3,7 +3,6 @@ using System; using System.Linq; -using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Judgements; From 3c513d0b620232e4db4b1849dc869e855c9898b0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Nov 2023 15:29:32 +0900 Subject: [PATCH 0099/1118] Refactor fail reason output to not perform string interpolation unless hooked --- .../Scoring/OsuHealthProcessor.cs | 22 ++++++++----------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 9ed1820045..7d6a05026c 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -48,7 +48,7 @@ namespace osu.Game.Rulesets.Osu.Scoring double currentHp; double currentHpUncapped; - do + while (true) { currentHp = 1; currentHpUncapped = 1; @@ -57,7 +57,6 @@ namespace osu.Game.Rulesets.Osu.Scoring double lastTime = DrainStartTime; int currentBreak = 0; bool fail = false; - string failReason = string.Empty; for (int i = 0; i < Beatmap.HitObjects.Count; i++) { @@ -92,7 +91,7 @@ namespace osu.Game.Rulesets.Osu.Scoring { fail = true; testDrop *= 0.96; - failReason = $"hp too low ({currentHp} < {lowestHpEver})"; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: hp too low ({currentHp} < {lowestHpEver})"); break; } @@ -117,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.Scoring { fail = true; testDrop *= 0.96; - failReason = $"overkill ({currentHp} - {hpOverkill} <= {lowestHpEver})"; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: overkill ({currentHp} - {hpOverkill} <= {lowestHpEver})"); break; } @@ -129,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Scoring fail = true; testDrop *= 0.94; hpMultiplierNormal *= 1.01; - failReason = $"end hp too low ({currentHp} < {lowestHpEnd})"; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: end hp too low ({currentHp} < {lowestHpEnd})"); } double recovery = (currentHpUncapped - 1) / Beatmap.HitObjects.Count; @@ -139,18 +138,15 @@ namespace osu.Game.Rulesets.Osu.Scoring fail = true; testDrop *= 0.96; hpMultiplierNormal *= 1.01; - failReason = $"recovery too low ({recovery} < {hpRecoveryAvailable})"; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: recovery too low ({recovery} < {hpRecoveryAvailable})"); } - if (fail) + if (!fail) { - OnIterationFail?.Invoke($"FAILED drop {testDrop}: {failReason}"); - continue; + OnIterationSuccess?.Invoke($"PASSED drop {testDrop}"); + return testDrop; } - - OnIterationSuccess?.Invoke($"PASSED drop {testDrop}"); - return testDrop; - } while (true); + } void reduceHp(double amount) { From b88e3cd26f8eb3f7b90e8bc6a96097ae23d3773e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 16 Nov 2023 20:16:23 +0900 Subject: [PATCH 0100/1118] Change `ResourceStore` provided to `Skin` to be a fallback, not replacement --- .../CatchSkinColourDecodingTest.cs | 4 ++-- .../Formats/LegacyBeatmapEncoderTest.cs | 4 ++-- .../Skins/SkinDeserialisationTest.cs | 4 ++-- .../Skins/TestSceneSkinResources.cs | 4 ++-- osu.Game/Skinning/DefaultLegacySkin.cs | 3 +-- osu.Game/Skinning/LegacyBeatmapSkin.cs | 2 +- osu.Game/Skinning/LegacySkin.cs | 7 +++---- osu.Game/Skinning/Skin.cs | 21 +++++++++++-------- osu.Game/Tests/Visual/SkinnableTestScene.cs | 4 ++-- 9 files changed, 27 insertions(+), 26 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs index 72011042bc..74b02bab9b 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchSkinColourDecodingTest.cs @@ -28,9 +28,9 @@ namespace osu.Game.Rulesets.Catch.Tests private class TestLegacySkin : LegacySkin { - public TestLegacySkin(SkinInfo skin, IResourceStore storage) + public TestLegacySkin(SkinInfo skin, IResourceStore fallbackStore) // Bypass LegacySkinResourceStore to avoid returning null for retrieving files due to bad skin info (SkinInfo.Files = null). - : base(skin, null, storage) + : base(skin, null, fallbackStore) { } } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 5d9049ead7..9ff0fe874f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -174,8 +174,8 @@ namespace osu.Game.Tests.Beatmaps.Formats private class TestLegacySkin : LegacySkin { - public TestLegacySkin(IResourceStore storage, string fileName) - : base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, storage, fileName) + public TestLegacySkin(IResourceStore fallbackStore, string fileName) + : base(new SkinInfo { Name = "Test Skin", Creator = "Craftplacer" }, null, fallbackStore, fileName) { } } diff --git a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs index c45eadeff2..6423e061c5 100644 --- a/osu.Game.Tests/Skins/SkinDeserialisationTest.cs +++ b/osu.Game.Tests/Skins/SkinDeserialisationTest.cs @@ -149,8 +149,8 @@ namespace osu.Game.Tests.Skins private class TestSkin : Skin { - public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage = null, string configurationFilename = "skin.ini") - : base(skin, resources, storage, configurationFilename) + public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? fallbackStore = null, string configurationFilename = "skin.ini") + : base(skin, resources, fallbackStore, configurationFilename) { } diff --git a/osu.Game.Tests/Skins/TestSceneSkinResources.cs b/osu.Game.Tests/Skins/TestSceneSkinResources.cs index aaec319b57..e77affd817 100644 --- a/osu.Game.Tests/Skins/TestSceneSkinResources.cs +++ b/osu.Game.Tests/Skins/TestSceneSkinResources.cs @@ -95,8 +95,8 @@ namespace osu.Game.Tests.Skins { public const string SAMPLE_NAME = "test-sample"; - public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage = null, string configurationFilename = "skin.ini") - : base(skin, resources, storage, configurationFilename) + public TestSkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? fallbackStore = null, string configurationFilename = "skin.ini") + : base(skin, resources, fallbackStore, configurationFilename) { } diff --git a/osu.Game/Skinning/DefaultLegacySkin.cs b/osu.Game/Skinning/DefaultLegacySkin.cs index fd9653e3e5..34ea0af122 100644 --- a/osu.Game/Skinning/DefaultLegacySkin.cs +++ b/osu.Game/Skinning/DefaultLegacySkin.cs @@ -31,8 +31,7 @@ namespace osu.Game.Skinning : base( skin, resources, - // In the case of the actual default legacy skin (ie. the fallback one, which a user hasn't applied any modifications to) we want to use the game provided resources. - skin.Protected ? new NamespacedResourceStore(resources.Resources, "Skins/Legacy") : null + new NamespacedResourceStore(resources.Resources, "Skins/Legacy") ) { Configuration.CustomColours["SliderBall"] = new Color4(2, 170, 255, 255); diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index 90eb5fa013..d6ba6ea332 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -73,7 +73,7 @@ namespace osu.Game.Skinning // needs to be removed else it will cause incorrect skin behaviours. This is due to the config lookup having no context of which skin // it should be returning the version for. - Skin.LogLookupDebug(this, lookup, Skin.LookupDebugType.Miss); + LogLookupDebug(this, lookup, LookupDebugType.Miss); return null; } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index dc683f1dae..2e91770919 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -16,7 +16,6 @@ using osu.Framework.Graphics.Textures; using osu.Framework.IO.Stores; using osu.Game.Audio; using osu.Game.Beatmaps.Formats; -using osu.Game.Database; using osu.Game.Extensions; using osu.Game.IO; using osu.Game.Rulesets.Objects.Types; @@ -51,10 +50,10 @@ namespace osu.Game.Skinning /// /// The model for this skin. /// Access to raw game resources. - /// An optional store which will be used for looking up skin resources. If null, one will be created from realm pattern. + /// An optional fallback store which will be used for file lookups that are not serviced by realm user storage. /// The user-facing filename of the configuration file to be parsed. Can accept an .osu or skin.ini file. - protected LegacySkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage, string configurationFilename = @"skin.ini") - : base(skin, resources, storage, configurationFilename) + protected LegacySkin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? fallbackStore, string configurationFilename = @"skin.ini") + : base(skin, resources, fallbackStore, configurationFilename) { } diff --git a/osu.Game/Skinning/Skin.cs b/osu.Game/Skinning/Skin.cs index 1e312142d7..9ee69d033d 100644 --- a/osu.Game/Skinning/Skin.cs +++ b/osu.Game/Skinning/Skin.cs @@ -55,7 +55,7 @@ namespace osu.Game.Skinning where TLookup : notnull where TValue : notnull; - private readonly RealmBackedResourceStore? realmBackedStorage; + private readonly ResourceStore store = new ResourceStore(); public string Name { get; } @@ -64,9 +64,9 @@ namespace osu.Game.Skinning /// /// The skin's metadata. Usually a live realm object. /// Access to game-wide resources. - /// An optional store which will *replace* all file lookups that are usually sourced from . + /// An optional fallback store which will be used for file lookups that are not serviced by realm user storage. /// An optional filename to read the skin configuration from. If not provided, the configuration will be retrieved from the storage using "skin.ini". - protected Skin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? storage = null, string configurationFilename = @"skin.ini") + protected Skin(SkinInfo skin, IStorageResourceProvider? resources, IResourceStore? fallbackStore = null, string configurationFilename = @"skin.ini") { Name = skin.Name; @@ -74,9 +74,9 @@ namespace osu.Game.Skinning { SkinInfo = skin.ToLive(resources.RealmAccess); - storage ??= realmBackedStorage = new RealmBackedResourceStore(SkinInfo, resources.Files, resources.RealmAccess); + store.AddStore(new RealmBackedResourceStore(SkinInfo, resources.Files, resources.RealmAccess)); - var samples = resources.AudioManager?.GetSampleStore(storage); + var samples = resources.AudioManager?.GetSampleStore(store); if (samples != null) { @@ -88,7 +88,7 @@ namespace osu.Game.Skinning } Samples = samples; - Textures = new TextureStore(resources.Renderer, CreateTextureLoaderStore(resources, storage)); + Textures = new TextureStore(resources.Renderer, CreateTextureLoaderStore(resources, store)); } else { @@ -96,7 +96,10 @@ namespace osu.Game.Skinning SkinInfo = skin.ToLiveUnmanaged(); } - var configurationStream = storage?.GetStream(configurationFilename); + if (fallbackStore != null) + store.AddStore(fallbackStore); + + var configurationStream = store.GetStream(configurationFilename); if (configurationStream != null) { @@ -119,7 +122,7 @@ namespace osu.Game.Skinning { string filename = $"{skinnableTarget}.json"; - byte[]? bytes = storage?.Get(filename); + byte[]? bytes = store?.Get(filename); if (bytes == null) continue; @@ -252,7 +255,7 @@ namespace osu.Game.Skinning Textures?.Dispose(); Samples?.Dispose(); - realmBackedStorage?.Dispose(); + store.Dispose(); } #endregion diff --git a/osu.Game/Tests/Visual/SkinnableTestScene.cs b/osu.Game/Tests/Visual/SkinnableTestScene.cs index aab1b72990..f371cf721f 100644 --- a/osu.Game/Tests/Visual/SkinnableTestScene.cs +++ b/osu.Game/Tests/Visual/SkinnableTestScene.cs @@ -201,8 +201,8 @@ namespace osu.Game.Tests.Visual { private readonly bool extrapolateAnimations; - public TestLegacySkin(SkinInfo skin, IResourceStore storage, IStorageResourceProvider resources, bool extrapolateAnimations) - : base(skin, resources, storage) + public TestLegacySkin(SkinInfo skin, IResourceStore fallbackStore, IStorageResourceProvider resources, bool extrapolateAnimations) + : base(skin, resources, fallbackStore) { this.extrapolateAnimations = extrapolateAnimations; } From a1673160f12e08612aa42a9db89f8b9f2e6324f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 16:44:11 +0900 Subject: [PATCH 0101/1118] Refactor `OsuAutoGenerator` to allow custom SPM specifications --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 5a3d882ef0..acce8c03e8 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -339,6 +339,10 @@ namespace osu.Game.Rulesets.Osu.Replays AddFrameToReplay(startFrame); + // ~477 as per stable. + const float spin_rpm = 60000f / 20 * (180 / MathF.PI) / 360; + float radsPerMillisecond = MathUtils.DegreesToRadians(spin_rpm * 360) / 60000; + switch (h) { // We add intermediate frames for spinning / following a slider here. @@ -354,7 +358,7 @@ namespace osu.Game.Rulesets.Osu.Replays for (double nextFrame = h.StartTime + GetFrameDelay(h.StartTime); nextFrame < spinner.EndTime; nextFrame += GetFrameDelay(nextFrame)) { t = ApplyModsToTimeDelta(previousFrame, nextFrame) * spinnerDirection; - angle += (float)t / 20; + angle += (float)t * radsPerMillisecond; Vector2 pos = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); AddFrameToReplay(new OsuReplayFrame((int)nextFrame, new Vector2(pos.X, pos.Y), action)); @@ -363,7 +367,7 @@ namespace osu.Game.Rulesets.Osu.Replays } t = ApplyModsToTimeDelta(previousFrame, spinner.EndTime) * spinnerDirection; - angle += (float)t / 20; + angle += (float)t * radsPerMillisecond; Vector2 endPosition = SPINNER_CENTRE + CirclePosition(angle, SPIN_RADIUS); From d1cea10f2197b243aff39ba16ee86b89f325fe44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 16:52:40 +0900 Subject: [PATCH 0102/1118] Add note about localisation --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 25c18f7328..85eb81eb9e 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -210,7 +210,8 @@ namespace osu.Game.Screens.Select.Details { if (haveRateChangedValues) { - return $"One or more values are being adjusted by mods that change speed." + + // Rather than localising this, it should be displayed in a better way (a custom tooltip which isn't a single super-long line). + return "One or more values are being adjusted by mods that change speed." + $" (AR {originalDifficulty?.ApproachRate ?? 0}→{(adjustedDifficulty?.ApproachRate ?? 0):0.0#}, " + $"OD {originalDifficulty?.OverallDifficulty ?? 0}→{(adjustedDifficulty?.OverallDifficulty ?? 0):0.0#})"; } From 9172632b0b7177a643c2c1c26ab69533998298d8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 17:04:02 +0900 Subject: [PATCH 0103/1118] Rename method and adjust xmldoc to be very explicit about how wrong this is --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 ++-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++-- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 4 ++-- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 2 +- osu.Game/Rulesets/Ruleset.cs | 19 ++++++++++--------- .../Screens/Select/Details/AdvancedStats.cs | 2 +- 6 files changed, 18 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 7327fb4513..94c8c36c09 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -234,9 +234,9 @@ namespace osu.Game.Rulesets.Catch }; } - public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) + public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate) { - BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); double preempt = adjustedDifficulty.ApproachRate < 6 ? (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index f4cf9409e8..a99f0df066 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -330,9 +330,9 @@ namespace osu.Game.Rulesets.Osu public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection(); - public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) + public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate) { - BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); double preempt = adjustedDifficulty.ApproachRate < 5 ? (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); preempt /= rate; diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 88d0087622..6e5cdbf2d1 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -265,9 +265,9 @@ namespace osu.Game.Rulesets.Taiko }; } - public override BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) + public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate) { - BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(baseDifficulty); + BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); double hitwindow = 35.0 - 15.0 * (adjustedDifficulty.OverallDifficulty - 5) / 5; hitwindow /= rate; diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index bf39666f83..6270b2bb49 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -179,7 +179,7 @@ namespace osu.Game.Overlays.Mods mod.ApplyToDifficulty(originalDifficulty); Ruleset ruleset = gameRuleset.Value.CreateInstance(); - adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(originalDifficulty, rate); + adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); haveRateChangedValues = hasRateAdjustedProperties(originalDifficulty, adjustedDifficulty); diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index edfe691b86..c7c81ecb55 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -377,6 +377,16 @@ namespace osu.Game.Rulesets /// The display name. public virtual LocalisableString GetDisplayNameForHitResult(HitResult result) => result.GetLocalisableDescription(); + /// + /// Applies changes to difficulty attributes for presenting to a user a rough estimate of how rate adjust mods affect difficulty. + /// Importantly, this should NOT BE USED FOR ANY CALCULATIONS. + /// It is also not always correct, and arguably is never correct depending on your frame of mind. + /// + /// >The that will be adjusted. + /// The rate adjustment multiplier from mods. For example 1.5 for DT. + /// The adjusted difficulty attributes. + public virtual BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate) => new BeatmapDifficulty(difficulty); + /// /// Creates ruleset-specific beatmap filter criteria to be used on the song select screen. /// @@ -391,14 +401,5 @@ namespace osu.Game.Rulesets /// Can be overridden to alter the difficulty section to the editor beatmap setup screen. /// public virtual DifficultySection? CreateEditorDifficultySection() => null; - - /// - /// Changes after they're adjusted according to rate. - /// Doesn't change any attributes by default. - /// - /// >The that will be adjusted. - /// Rate of the gameplay. For example 1.5 for DT. - /// Copy of difficulty info with values changed according to rate and ruleset-specific behaviour. - public virtual BeatmapDifficulty GetRateAdjustedDifficulty(IBeatmapDifficultyInfo baseDifficulty, double rate) => new BeatmapDifficulty(baseDifficulty); } } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 85eb81eb9e..f617cb1d8e 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -137,7 +137,7 @@ namespace osu.Game.Screens.Select.Details foreach (var mod in mods.Value.OfType()) rate = mod.ApplyToRate(0, rate); - adjustedDifficulty = ruleset.GetRateAdjustedDifficulty(originalDifficulty, rate); + adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); haveRateChangedValues = hasRateAdjustedProperties(originalDifficulty, adjustedDifficulty); } } From bd932a5417dd7bc4fba8b237832d556c27ea0d9b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 17:07:21 +0900 Subject: [PATCH 0104/1118] Update localisation analyser Pulls in https://github.com/ppy/osu-localisation-analyser/pull/60. --- .config/dotnet-tools.json | 4 ++-- osu.Game/osu.Game.csproj | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.config/dotnet-tools.json b/.config/dotnet-tools.json index 3cecb0d07c..b8dc201559 100644 --- a/.config/dotnet-tools.json +++ b/.config/dotnet-tools.json @@ -21,10 +21,10 @@ ] }, "ppy.localisationanalyser.tools": { - "version": "2023.712.0", + "version": "2023.1117.0", "commands": [ "localisation" ] } } -} \ No newline at end of file +} diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 9985afbd8b..10ca49c768 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -31,7 +31,7 @@ - + all runtime; build; native; contentfiles; analyzers; buildtransitive From c9c8ed7c77332502de4f946affbd8dc107b99dba Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 18:41:09 +0900 Subject: [PATCH 0105/1118] Remove unused values --- osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 7d6a05026c..d1c9227c13 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -197,19 +197,10 @@ namespace osu.Game.Rulesets.Osu.Scoring increase = 0.011; break; - case HitResult.Good: - increase = 0.024; - break; - case HitResult.Great: increase = 0.03; break; - case HitResult.Perfect: - // 1.1 * Great. Unused. - increase = 0.033; - break; - case HitResult.SmallBonus: increase = 0.0085; break; From a556caae437b506b4d26258e0ab69008ccae2ec8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 18:41:54 +0900 Subject: [PATCH 0106/1118] Move default value out of switch statement --- osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index d1c9227c13..8265ca1c33 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -166,7 +166,7 @@ namespace osu.Game.Rulesets.Osu.Scoring private double healthIncreaseFor(HitObject hitObject, HitResult result) { - double increase; + double increase = 0; switch (result) { @@ -208,10 +208,6 @@ namespace osu.Game.Rulesets.Osu.Scoring case HitResult.LargeBonus: increase = 0.01; break; - - default: - increase = 0; - break; } return hpMultiplierNormal * increase; From 2ab84fdaa3f6a73715797d7c9d40d5835153eb68 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 18:45:16 +0900 Subject: [PATCH 0107/1118] Use switch statement for type matching --- .../Scoring/OsuHealthProcessor.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 8265ca1c33..0e0bc1916c 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -99,15 +99,21 @@ namespace osu.Game.Rulesets.Osu.Scoring double hpOverkill = Math.Max(0, hpReduction - currentHp); reduceHp(hpReduction); - if (h is Slider slider) + switch (h) { - foreach (var nested in slider.NestedHitObjects) - increaseHp(nested); - } - else if (h is Spinner spinner) - { - foreach (var nested in spinner.NestedHitObjects.Where(t => t is not SpinnerBonusTick)) - increaseHp(nested); + case Slider slider: + { + foreach (var nested in slider.NestedHitObjects) + increaseHp(nested); + break; + } + + case Spinner spinner: + { + foreach (var nested in spinner.NestedHitObjects.Where(t => t is not SpinnerBonusTick)) + increaseHp(nested); + break; + } } // Note: Because HP is capped during the above increases, long sliders (with many ticks) or spinners From fd3508254b835c2a9dcc07bdfe72c16cf8cec2f7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 18:49:19 +0900 Subject: [PATCH 0108/1118] Add note about break calculation method --- osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 0e0bc1916c..5a24f29330 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -68,6 +68,7 @@ namespace osu.Game.Rulesets.Osu.Scoring // TODO: This doesn't handle overlapping/sequential breaks correctly (/b/614). // Subtract any break time from the duration since the last object + // Note that this method is a bit convoluted, but matches stable code for compatibility. if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) { BreakPeriod e = Beatmap.Breaks[currentBreak]; From 66f7b9fae1e0aaf86609bdbf3315870198e0f8b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 19:09:24 +0900 Subject: [PATCH 0109/1118] Adjust slider follow circle animation to not abruptly scale on early ticks --- .../Skinning/Argon/ArgonFollowCircle.cs | 9 ++++++--- .../Skinning/Default/DefaultFollowCircle.cs | 9 ++++++--- .../Skinning/Legacy/LegacyFollowCircle.cs | 7 +++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonFollowCircle.cs index fca3e70236..ea21d71d5f 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonFollowCircle.cs @@ -88,9 +88,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon protected override void OnSliderTick() { - this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.08f, 40, Easing.OutQuint) - .Then() - .ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200f, Easing.OutQuint); + if (Scale.X >= DrawableSliderBall.FOLLOW_AREA * 0.98f) + { + this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.08f, 40, Easing.OutQuint) + .Then() + .ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200f, Easing.OutQuint); + } } protected override void OnSliderBreak() diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs index 3c41d473f4..4adbfc3928 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultFollowCircle.cs @@ -59,9 +59,12 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default protected override void OnSliderTick() { - this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.08f, 40, Easing.OutQuint) - .Then() - .ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200f, Easing.OutQuint); + if (Scale.X >= DrawableSliderBall.FOLLOW_AREA * 0.98f) + { + this.ScaleTo(DrawableSliderBall.FOLLOW_AREA * 1.08f, 40, Easing.OutQuint) + .Then() + .ScaleTo(DrawableSliderBall.FOLLOW_AREA, 200f, Easing.OutQuint); + } } protected override void OnSliderBreak() diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs index f8dcb9e8a2..fa2bb9b2ad 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyFollowCircle.cs @@ -44,8 +44,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override void OnSliderTick() { - this.ScaleTo(2.2f) - .ScaleTo(2f, 200); + if (Scale.X >= 2f) + { + this.ScaleTo(2.2f) + .ScaleTo(2f, 200); + } } protected override void OnSliderBreak() From 307ec172cbcc49f0edf26aed318d5390666faafa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 23:48:48 +0900 Subject: [PATCH 0110/1118] Use simplified formula --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index acce8c03e8..9d63949dcc 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -340,7 +340,7 @@ namespace osu.Game.Rulesets.Osu.Replays AddFrameToReplay(startFrame); // ~477 as per stable. - const float spin_rpm = 60000f / 20 * (180 / MathF.PI) / 360; + const float spin_rpm = 0.05f / (2 * MathF.PI) * 60000; float radsPerMillisecond = MathUtils.DegreesToRadians(spin_rpm * 360) / 60000; switch (h) From d9cd546377e7e7b3072fe905b966cebe7eeff746 Mon Sep 17 00:00:00 2001 From: Poyo Date: Sat, 18 Nov 2023 12:09:37 -0800 Subject: [PATCH 0111/1118] Use rate fallback in DrawableHitObject --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- osu.Game/Rulesets/UI/Playfield.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index baf13d8911..5abca168ed 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -704,7 +704,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } Result.RawTime = Time.Current; - Result.GameplayRate = (Clock as IGameplayClock)?.GetTrueGameplayRate(); + Result.GameplayRate = (Clock as IGameplayClock)?.GetTrueGameplayRate() ?? 1.0; if (Result.HasResult) updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss); diff --git a/osu.Game/Rulesets/UI/Playfield.cs b/osu.Game/Rulesets/UI/Playfield.cs index eb29d8f30a..e9c35555c8 100644 --- a/osu.Game/Rulesets/UI/Playfield.cs +++ b/osu.Game/Rulesets/UI/Playfield.cs @@ -473,7 +473,7 @@ namespace osu.Game.Rulesets.UI private void onNewResult(DrawableHitObject drawable, JudgementResult result) { - Debug.Assert(result != null && drawable.Entry?.Result == result && result.RawTime != null && result.GameplayRate != null); + Debug.Assert(result != null && drawable.Entry?.Result == result && result.RawTime != null); judgedEntries.Push(drawable.Entry.AsNonNull()); NewResult?.Invoke(drawable, result); From 2cf16e29acf5e5f397ea6ab98bbd8a8a6c5528f5 Mon Sep 17 00:00:00 2001 From: Zyf Date: Sun, 19 Nov 2023 21:02:56 +0100 Subject: [PATCH 0112/1118] Revert "Scoring : Adds fields to Catch/Mania/Taiko Simulators too" This reverts commit a97915180f37a87ac5716acdb0bfcafffe9e6452. --- .../Difficulty/CatchLegacyScoreSimulator.cs | 10 +++------- .../Difficulty/ManiaLegacyScoreSimulator.cs | 3 --- .../Difficulty/TaikoLegacyScoreSimulator.cs | 10 +++------- 3 files changed, 6 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs index 284fd23aea..c79fd36d96 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs @@ -20,12 +20,9 @@ namespace osu.Game.Rulesets.Catch.Difficulty public int ComboScore { get; private set; } - public int LegacyBonusScore { get; private set; } - - public int MaxCombo { get; private set; } - - public double BonusScoreRatio => LegacyBonusScore == 0 ? 0 : (double)modernBonusScore / LegacyBonusScore; + public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore; + private int legacyBonusScore; private int modernBonusScore; private int combo; @@ -77,7 +74,6 @@ namespace osu.Game.Rulesets.Catch.Difficulty foreach (var obj in playableBeatmap.HitObjects) simulateHit(obj); - MaxCombo = combo; } private void simulateHit(HitObject hitObject) @@ -133,7 +129,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (isBonus) { - LegacyBonusScore += scoreIncrease; + legacyBonusScore += scoreIncrease; modernBonusScore += Judgement.ToNumericResult(bonusResult); } else diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs index f09c911b98..e544428979 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs @@ -14,8 +14,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty { public int AccuracyScore => 0; public int ComboScore { get; private set; } - public int LegacyBonusScore => 0; - public int MaxCombo { get; private set; } public double BonusScoreRatio => 0; public void Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap, IReadOnlyList mods) @@ -25,7 +23,6 @@ namespace osu.Game.Rulesets.Mania.Difficulty .Aggregate(1.0, (c, n) => c * n); ComboScore = (int)(1000000 * multiplier); - MaxCombo = playableBeatmap.GetMaxCombo(); } } } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index 239ec5765c..e77327d622 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -20,12 +20,9 @@ namespace osu.Game.Rulesets.Taiko.Difficulty public int ComboScore { get; private set; } - public int LegacyBonusScore { get; private set; } - - public int MaxCombo { get; private set; } - - public double BonusScoreRatio => LegacyBonusScore == 0 ? 0 : (double)modernBonusScore / LegacyBonusScore; + public double BonusScoreRatio => legacyBonusScore == 0 ? 0 : (double)modernBonusScore / legacyBonusScore; + private int legacyBonusScore; private int modernBonusScore; private int combo; @@ -83,7 +80,6 @@ namespace osu.Game.Rulesets.Taiko.Difficulty foreach (var obj in playableBeatmap.HitObjects) simulateHit(obj); - MaxCombo = combo; } private void simulateHit(HitObject hitObject) @@ -193,7 +189,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (isBonus) { - LegacyBonusScore += scoreIncrease; + legacyBonusScore += scoreIncrease; modernBonusScore += Judgement.ToNumericResult(bonusResult); } else From 432b88674b3d2e0eff6bbf80b26016eea6da0b2d Mon Sep 17 00:00:00 2001 From: Zyf Date: Mon, 20 Nov 2023 00:02:58 +0100 Subject: [PATCH 0113/1118] Scoring : change formula parameters to match survey results --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 6 +++--- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index c38dd0e9cf..dfc2f8fb62 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -316,11 +316,11 @@ namespace osu.Game.Database 1.2 * (newLowerStrippedV3 + newUpperStrippedV3) / 2 ); - double newComboScoreProportion = (strippedV3 / maxStrippedV3) * score.Accuracy; + double newComboScoreProportion = (strippedV3 / maxStrippedV3); return (long)Math.Round(( - 700000 * newComboScoreProportion * score.Accuracy - + 300000 * Math.Pow(score.Accuracy, 8) + 500000 * newComboScoreProportion * score.Accuracy + + 500000 * Math.Pow(score.Accuracy, 5) + bonusProportion) * modMultiplier); case 1: diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 514630b351..adf8e318d3 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -301,7 +301,7 @@ namespace osu.Game.Rulesets.Scoring protected virtual double GetBonusScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type); - protected virtual double GetComboScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); + protected virtual double GetComboScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Judgement.MaxResult) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); protected virtual void ApplyScoreChange(JudgementResult result) { @@ -325,8 +325,8 @@ namespace osu.Game.Rulesets.Scoring protected virtual double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion) { - return 700000 * Accuracy.Value * comboProgress + - 300000 * Math.Pow(Accuracy.Value, 8) * accuracyProgress + + return 500000 * Accuracy.Value * comboProgress + + 500000 * Math.Pow(Accuracy.Value, 5) * accuracyProgress + bonusPortion; } From bfcca382007be5c91adfecfc08dc0a2fbad4cb36 Mon Sep 17 00:00:00 2001 From: Stedoss <29103029+Stedoss@users.noreply.github.com> Date: Mon, 20 Nov 2023 01:15:26 +0000 Subject: [PATCH 0114/1118] Handle login API state changes in `UserProfileOverlay` --- osu.Game/Overlays/UserProfileOverlay.cs | 46 ++++++++++++++++++++----- 1 file changed, 38 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 0ab842c907..d45a010e4a 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -42,6 +43,11 @@ namespace osu.Game.Overlays private ProfileSectionsContainer? sectionsContainer; private ProfileSectionTabControl? tabs; + private IUser? user; + private IRulesetInfo? ruleset; + + private IBindable apiUser = null!; + [Resolved] private RulesetStore rulesets { get; set; } = null!; @@ -58,17 +64,38 @@ namespace osu.Game.Overlays }); } + [BackgroundDependencyLoader] + private void load() + { + apiUser = API.LocalUser.GetBoundCopy(); + apiUser.BindValueChanged(_ => Schedule(() => + { + if (API.IsLoggedIn) + fetchAndSetContent(); + })); + } + protected override ProfileHeader CreateHeader() => new ProfileHeader(); protected override Color4 BackgroundColour => ColourProvider.Background5; - public void ShowUser(IUser user, IRulesetInfo? ruleset = null) + public void ShowUser(IUser userToShow, IRulesetInfo? userRuleset = null) { - if (user.OnlineID == APIUser.SYSTEM_USER_ID) + if (userToShow.OnlineID == APIUser.SYSTEM_USER_ID) return; + user = userToShow; + ruleset = userRuleset; + Show(); + fetchAndSetContent(); + } + + private void fetchAndSetContent() + { + Debug.Assert(user != null); + if (user.OnlineID == Header.User.Value?.User.Id && ruleset?.MatchesOnlineID(Header.User.Value?.Ruleset) == true) return; @@ -143,24 +170,27 @@ namespace osu.Game.Overlays sectionsContainer.ScrollToTop(); + if (!API.IsLoggedIn) + return; + userReq = user.OnlineID > 1 ? new GetUserRequest(user.OnlineID, ruleset) : new GetUserRequest(user.Username, ruleset); - userReq.Success += u => userLoadComplete(u, ruleset); + userReq.Success += userLoadComplete; API.Queue(userReq); loadingLayer.Show(); } - private void userLoadComplete(APIUser user, IRulesetInfo? ruleset) + private void userLoadComplete(APIUser loadedUser) { Debug.Assert(sections != null && sectionsContainer != null && tabs != null); - var actualRuleset = rulesets.GetRuleset(ruleset?.ShortName ?? user.PlayMode).AsNonNull(); + var actualRuleset = rulesets.GetRuleset(ruleset?.ShortName ?? loadedUser.PlayMode).AsNonNull(); - var userProfile = new UserProfileData(user, actualRuleset); + var userProfile = new UserProfileData(loadedUser, actualRuleset); Header.User.Value = userProfile; - if (user.ProfileOrder != null) + if (loadedUser.ProfileOrder != null) { - foreach (string id in user.ProfileOrder) + foreach (string id in loadedUser.ProfileOrder) { var sec = sections.FirstOrDefault(s => s.Identifier == id); From 1f88658bade025040f3c97a08020de9e21dba41e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Burgelin=20=28Zyfarok=29?= Date: Mon, 20 Nov 2023 03:05:46 +0100 Subject: [PATCH 0115/1118] Fix syntax --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 2 ++ osu.Game/Rulesets/Scoring/Legacy/LegacyScoreAttributes.cs | 1 - 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index dfc2f8fb62..685f852eb4 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -267,10 +267,12 @@ namespace osu.Game.Database { case 0: if (score.MaxCombo == 0 || score.Accuracy == 0) + { return (long)Math.Round(( 0 + 300000 * Math.Pow(score.Accuracy, 8) + bonusProportion) * modMultiplier); + } // Assumption : // - sliders and slider-ticks are uniformly spread arround the beatmap diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreAttributes.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreAttributes.cs index c2f87be194..6f6740c641 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreAttributes.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyScoreAttributes.cs @@ -29,6 +29,5 @@ namespace osu.Game.Rulesets.Scoring.Legacy /// The max combo of the legacy (ScoreV1) total score. /// public int MaxCombo; - } } From e182acf3e8c012fe36f8317d70ca7d6abe1803e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 11:50:28 +0900 Subject: [PATCH 0116/1118] Expand comment for clarification --- osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs index 9d63949dcc..1cf6bc91f0 100644 --- a/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs +++ b/osu.Game.Rulesets.Osu/Replays/OsuAutoGenerator.cs @@ -339,7 +339,8 @@ namespace osu.Game.Rulesets.Osu.Replays AddFrameToReplay(startFrame); - // ~477 as per stable. + // 0.05 rad/ms, or ~477 RPM, as per stable. + // the redundant conversion from RPM to rad/ms is here for ease of testing custom SPM specs. const float spin_rpm = 0.05f / (2 * MathF.PI) * 60000; float radsPerMillisecond = MathUtils.DegreesToRadians(spin_rpm * 360) / 60000; From 33b592f1c723fb4824f2084337de9dbb940d2b1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:04:30 +0900 Subject: [PATCH 0117/1118] Update framework (again) --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 1f6a65c450..dd5a8996fb 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 70525a5c59..9a0832b4e7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 577cb9994ce54202bf2c07548389f0e6b701fa8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:24:32 +0900 Subject: [PATCH 0118/1118] Move static instances / construction methods closer together --- osu.Game/Rulesets/Objects/Types/PathType.cs | 38 ++++++++++----------- 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index e4249154e5..0e3adb4473 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -16,11 +16,6 @@ namespace osu.Game.Rulesets.Objects.Types public readonly struct PathType : IEquatable, IHasDescription { - public static readonly PathType CATMULL = new PathType(SplineType.Catmull); - public static readonly PathType BEZIER = new PathType(SplineType.BSpline); - public static readonly PathType LINEAR = new PathType(SplineType.Linear); - public static readonly PathType PERFECT_CURVE = new PathType(SplineType.PerfectCurve); - /// /// The type of the spline that should be used to interpret the control points of the path. /// @@ -32,6 +27,25 @@ namespace osu.Game.Rulesets.Objects.Types /// public int? Degree { get; init; } + public PathType(SplineType splineType) + { + Type = splineType; + Degree = null; + } + + public static readonly PathType CATMULL = new PathType(SplineType.Catmull); + public static readonly PathType BEZIER = new PathType(SplineType.BSpline); + public static readonly PathType LINEAR = new PathType(SplineType.Linear); + public static readonly PathType PERFECT_CURVE = new PathType(SplineType.PerfectCurve); + + public static PathType BSpline(int degree) + { + if (degree <= 0) + throw new ArgumentOutOfRangeException(nameof(degree), "The degree of a B-Spline path must be greater than zero."); + + return new PathType { Type = SplineType.BSpline, Degree = degree }; + } + public string Description => Type switch { SplineType.Catmull => "Catmull", @@ -41,12 +55,6 @@ namespace osu.Game.Rulesets.Objects.Types _ => Type.ToString() }; - public PathType(SplineType splineType) - { - Type = splineType; - Degree = null; - } - public override int GetHashCode() => HashCode.Combine(Type, Degree); @@ -59,14 +67,6 @@ namespace osu.Game.Rulesets.Objects.Types public static bool operator !=(PathType a, PathType b) => a.Type != b.Type || a.Degree != b.Degree; - public static PathType BSpline(int degree) - { - if (degree <= 0) - throw new ArgumentOutOfRangeException(nameof(degree), "The degree of a B-Spline path must be greater than zero."); - - return new PathType { Type = SplineType.BSpline, Degree = degree }; - } - public bool Equals(PathType other) => Type == other.Type && Degree == other.Degree; } From 25c1a900473de216ba5c07b306b4e49316ec7828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:25:44 +0900 Subject: [PATCH 0119/1118] Change switchexpr to standard switch statement --- osu.Game/Rulesets/Objects/Types/PathType.cs | 29 ++++++++++++++++----- 1 file changed, 22 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 0e3adb4473..95ddcb8b05 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -46,14 +46,29 @@ namespace osu.Game.Rulesets.Objects.Types return new PathType { Type = SplineType.BSpline, Degree = degree }; } - public string Description => Type switch + public string Description { - SplineType.Catmull => "Catmull", - SplineType.BSpline => Degree == null ? "Bezier" : "B-Spline", - SplineType.Linear => "Linear", - SplineType.PerfectCurve => "Perfect Curve", - _ => Type.ToString() - }; + get + { + switch (Type) + { + case SplineType.Catmull: + return "Catmull"; + + case SplineType.BSpline: + return Degree == null ? "Bezier" : "B-Spline"; + + case SplineType.Linear: + return "Linear"; + + case SplineType.PerfectCurve: + return "Perfect Curve"; + + default: + return Type.ToString(); + } + } + } public override int GetHashCode() => HashCode.Combine(Type, Degree); From 7820c8ce4d558381f80444fcf3fb38035382a852 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:28:20 +0900 Subject: [PATCH 0120/1118] Decrease redundancy of equality implementations --- osu.Game/Rulesets/Objects/Types/PathType.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 95ddcb8b05..37c1d0ab50 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -74,15 +74,12 @@ namespace osu.Game.Rulesets.Objects.Types => HashCode.Combine(Type, Degree); public override bool Equals(object? obj) - => obj is PathType pathType && this == pathType; - - public static bool operator ==(PathType a, PathType b) - => a.Type == b.Type && a.Degree == b.Degree; - - public static bool operator !=(PathType a, PathType b) - => a.Type != b.Type || a.Degree != b.Degree; + => obj is PathType pathType && Equals(pathType); public bool Equals(PathType other) => Type == other.Type && Degree == other.Degree; + + public static bool operator ==(PathType a, PathType b) => a.Equals(b); + public static bool operator !=(PathType a, PathType b) => !a.Equals(b); } } From 518dcc567b14cdf8ade2d449b20cdcbcfcf45e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:41:22 +0900 Subject: [PATCH 0121/1118] Null-check `drawingSettingsProvider` As it's annotated as an optional dependency. --- .../Sliders/SliderPlacementBlueprint.cs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index df7d2c716b..9b24415604 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -80,19 +80,22 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.LoadComplete(); inputManager = GetContainingInputManager(); - drawingSettingsProvider.Tolerance.BindValueChanged(e => + if (drawingSettingsProvider != null) { - if (bSplineBuilder.Tolerance != e.NewValue) - bSplineBuilder.Tolerance = e.NewValue; - updateSliderPathFromBSplineBuilder(); - }, true); + drawingSettingsProvider.Tolerance.BindValueChanged(e => + { + if (bSplineBuilder.Tolerance != e.NewValue) + bSplineBuilder.Tolerance = e.NewValue; + updateSliderPathFromBSplineBuilder(); + }, true); - drawingSettingsProvider.CornerThreshold.BindValueChanged(e => - { - if (bSplineBuilder.CornerThreshold != e.NewValue) - bSplineBuilder.CornerThreshold = e.NewValue; - updateSliderPathFromBSplineBuilder(); - }, true); + drawingSettingsProvider.CornerThreshold.BindValueChanged(e => + { + if (bSplineBuilder.CornerThreshold != e.NewValue) + bSplineBuilder.CornerThreshold = e.NewValue; + updateSliderPathFromBSplineBuilder(); + }, true); + } } [Resolved] From f46945a29439716ef10e5e82277cf5e338044094 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 12:42:16 +0900 Subject: [PATCH 0122/1118] Avoid one unnecessary path update from B-spline builder --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 9b24415604..50b4377ccd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (bSplineBuilder.Tolerance != e.NewValue) bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); - }, true); + }); drawingSettingsProvider.CornerThreshold.BindValueChanged(e => { From 831884a64b74113f76bc59eed0466f232b71f7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:00:12 +0900 Subject: [PATCH 0123/1118] Remove unused enum member --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 50b4377ccd..50abcf1233 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -344,8 +344,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { Initial, ControlPoints, - Drawing, - DrawingFinalization + Drawing } } } From affef85f2521246412949531e291fdbed4cf1272 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:02:51 +0900 Subject: [PATCH 0124/1118] Remove `ISliderDrawingSettingsProvider` Seems like excessive abstraction. --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- .../Edit/ISliderDrawingSettingsProvider.cs | 13 ------------- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 2 +- .../Edit/OsuSliderDrawingSettingsProvider.cs | 2 +- 4 files changed, 3 insertions(+), 16 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 50abcf1233..fb2c1d5149 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private IDistanceSnapProvider distanceSnapProvider { get; set; } [Resolved(CanBeNull = true)] - private ISliderDrawingSettingsProvider drawingSettingsProvider { get; set; } + private OsuSliderDrawingSettingsProvider drawingSettingsProvider { get; set; } private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder(); diff --git a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs deleted file mode 100644 index 31ed98e1dd..0000000000 --- a/osu.Game.Rulesets.Osu/Edit/ISliderDrawingSettingsProvider.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Bindables; - -namespace osu.Game.Rulesets.Osu.Edit -{ - public interface ISliderDrawingSettingsProvider - { - BindableFloat Tolerance { get; } - BindableFloat CornerThreshold { get; } - } -} diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index d958b558cf..0dc0d6fd31 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Rulesets.Osu.Edit [Cached(typeof(IDistanceSnapProvider))] protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); - [Cached(typeof(ISliderDrawingSettingsProvider))] + [Cached] protected readonly OsuSliderDrawingSettingsProvider SliderDrawingSettingsProvider = new OsuSliderDrawingSettingsProvider(); [BackgroundDependencyLoader] diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index 4326b2e943..0126b366d5 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class OsuSliderDrawingSettingsProvider : Drawable, ISliderDrawingSettingsProvider, IToolboxAttachment + public partial class OsuSliderDrawingSettingsProvider : Drawable, IToolboxAttachment { public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) { From 5d1bac6d7a55a980e608fbf6bfa8744ceb30b18f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:17:43 +0900 Subject: [PATCH 0125/1118] Remove `IToolboxAttachment` for now The interface doesn't really do anything useful right now because it enforces a common contract, but all usages of the contract go through the concrete implementation, and it inflates the already-huge diff. --- .../Edit/OsuSliderDrawingSettingsProvider.cs | 2 +- osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs | 2 +- osu.Game/Rulesets/Edit/IToolboxAttachment.cs | 10 ---------- 3 files changed, 2 insertions(+), 12 deletions(-) delete mode 100644 osu.Game/Rulesets/Edit/IToolboxAttachment.cs diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index 0126b366d5..e44f0265c8 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class OsuSliderDrawingSettingsProvider : Drawable, IToolboxAttachment + public partial class OsuSliderDrawingSettingsProvider : Drawable { public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) { diff --git a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs index 68411d2b01..ddf539771d 100644 --- a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs @@ -29,7 +29,7 @@ using osu.Game.Screens.Edit.Components.TernaryButtons; namespace osu.Game.Rulesets.Edit { - public abstract partial class ComposerDistanceSnapProvider : Component, IDistanceSnapProvider, IScrollBindingHandler, IToolboxAttachment + public abstract partial class ComposerDistanceSnapProvider : Component, IDistanceSnapProvider, IScrollBindingHandler { private const float adjust_step = 0.1f; diff --git a/osu.Game/Rulesets/Edit/IToolboxAttachment.cs b/osu.Game/Rulesets/Edit/IToolboxAttachment.cs deleted file mode 100644 index 7d7c5980b2..0000000000 --- a/osu.Game/Rulesets/Edit/IToolboxAttachment.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Rulesets.Edit -{ - public interface IToolboxAttachment - { - void AttachToToolbox(ExpandingToolboxContainer toolbox); - } -} From 487326a4c786aa89b597ea191ec83c701386736d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:22:36 +0900 Subject: [PATCH 0126/1118] Remove pattern matching syntax usage in switch Also throw on unknown types. --- osu.Game/Rulesets/Objects/BezierConverter.cs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index 5dc0839d37..4a68161899 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -70,21 +70,21 @@ namespace osu.Game.Rulesets.Objects var segmentVertices = vertices.AsSpan().Slice(start, i - start + 1); var segmentType = controlPoints[start].Type ?? PathType.LINEAR; - switch (segmentType) + switch (segmentType.Type) { - case { Type: SplineType.Catmull }: + case SplineType.Catmull: result.AddRange(from segment in ConvertCatmullToBezierAnchors(segmentVertices) from v in segment select v + position); break; - case { Type: SplineType.Linear }: + case SplineType.Linear: result.AddRange(from segment in ConvertLinearToBezierAnchors(segmentVertices) from v in segment select v + position); break; - case { Type: SplineType.PerfectCurve }: + case SplineType.PerfectCurve: result.AddRange(ConvertCircleToBezierAnchors(segmentVertices).Select(v => v + position)); break; - default: + case SplineType.BSpline: if (segmentType.Degree != null) throw new NotImplementedException("BSpline conversion of arbitrary degree is not implemented."); @@ -94,6 +94,9 @@ namespace osu.Game.Rulesets.Objects } break; + + default: + throw new ArgumentOutOfRangeException(nameof(segmentType.Type), segmentType.Type, "Unsupported segment type found when converting to legacy Bezier"); } // Start the new segment at the current vertex @@ -160,13 +163,16 @@ namespace osu.Game.Rulesets.Objects break; - default: + case SplineType.BSpline: for (int j = 0; j < segmentVertices.Length - 1; j++) { result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? segmentType : null)); } break; + + default: + throw new ArgumentOutOfRangeException(nameof(segmentType.Type), segmentType.Type, "Unsupported segment type found when converting to legacy Bezier"); } // Start the new segment at the current vertex From 80a3225bb28f315aeceda4dc5e10c2222faa0e1f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 13:35:07 +0900 Subject: [PATCH 0127/1118] Use static `BEZIER` instead of allocating new every time --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 92a92dca8f..411a9b0d63 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -230,7 +230,7 @@ namespace osu.Game.Rulesets.Objects.Legacy if (input.Length > 1 && int.TryParse(input.Substring(1), out int degree) && degree > 0) return PathType.BSpline(degree); - return new PathType(SplineType.BSpline); + return PathType.BEZIER; case 'L': return PathType.LINEAR; From 6d7d826b8b76f775bf59d600d493912a92950540 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:08:58 +0900 Subject: [PATCH 0128/1118] Fix incorrect legacy conversion when B-splines are used --- osu.Game/Database/LegacyBeatmapExporter.cs | 3 ++- osu.Game/Rulesets/Objects/BezierConverter.cs | 8 ++++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/LegacyBeatmapExporter.cs b/osu.Game/Database/LegacyBeatmapExporter.cs index 9ca12a79dd..69120ea885 100644 --- a/osu.Game/Database/LegacyBeatmapExporter.cs +++ b/osu.Game/Database/LegacyBeatmapExporter.cs @@ -85,7 +85,8 @@ namespace osu.Game.Database if (hasPath.Path.ControlPoints.Count > 1) hasPath.Path.ControlPoints[^1].Type = null; - if (BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1) continue; + if (BezierConverter.CountSegments(hasPath.Path.ControlPoints) <= 1 + && hasPath.Path.ControlPoints[0].Type!.Value.Degree == null) continue; var newControlPoints = BezierConverter.ConvertToModernBezier(hasPath.Path.ControlPoints); diff --git a/osu.Game/Rulesets/Objects/BezierConverter.cs b/osu.Game/Rulesets/Objects/BezierConverter.cs index 4a68161899..638975630e 100644 --- a/osu.Game/Rulesets/Objects/BezierConverter.cs +++ b/osu.Game/Rulesets/Objects/BezierConverter.cs @@ -164,9 +164,13 @@ namespace osu.Game.Rulesets.Objects break; case SplineType.BSpline: - for (int j = 0; j < segmentVertices.Length - 1; j++) + var bSplineResult = segmentType.Degree == null + ? segmentVertices + : PathApproximator.BSplineToBezier(segmentVertices, segmentType.Degree.Value); + + for (int j = 0; j < bSplineResult.Length - 1; j++) { - result.Add(new PathControlPoint(segmentVertices[j], j == 0 ? segmentType : null)); + result.Add(new PathControlPoint(bSplineResult[j], j == 0 ? PathType.BEZIER : null)); } break; From 46d4587c97fc8a6d7b3a2ea0e98366fe149f2ed3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:34:01 +0900 Subject: [PATCH 0129/1118] Add test for slider drawing --- .../TestSceneSliderPlacementBlueprint.cs | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index ecfc8105f1..d1c94c9c9c 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -290,6 +290,27 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointType(0, PathType.LINEAR); } + [Test] + public void TestSliderDrawing() + { + addMovementStep(new Vector2(200)); + AddStep("press left button", () => InputManager.PressButton(MouseButton.Left)); + + addMovementStep(new Vector2(300, 200)); + addMovementStep(new Vector2(400, 200)); + addMovementStep(new Vector2(400, 300)); + addMovementStep(new Vector2(400)); + addMovementStep(new Vector2(300, 400)); + addMovementStep(new Vector2(200, 400)); + + AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left)); + + assertPlaced(true); + assertLength(600, tolerance: 10); + assertControlPointCount(4); + assertControlPointType(0, PathType.BSpline(3)); + } + [Test] public void TestPlacePerfectCurveSegmentAlmostLinearlyExterior() { @@ -397,7 +418,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private void assertPlaced(bool expected) => AddAssert($"slider {(expected ? "placed" : "not placed")}", () => (getSlider() != null) == expected); - private void assertLength(double expected) => AddAssert($"slider length is {expected}", () => Precision.AlmostEquals(expected, getSlider().Distance, 1)); + private void assertLength(double expected, double tolerance = 1) => AddAssert($"slider length is {expected}±{tolerance}", () => Precision.AlmostEquals(expected, getSlider().Distance, tolerance)); private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count == expected); From 5f302662be30eff9eb12318c40651f1f776fb09c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:34:23 +0900 Subject: [PATCH 0130/1118] Remove test terminally broken by introduction of slider drawing --- .../Editor/TestSceneSliderPlacementBlueprint.cs | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index d1c94c9c9c..c7a21ba689 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -273,23 +273,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointType(2, PathType.PERFECT_CURVE); } - [Test] - public void TestBeginPlacementWithoutReleasingMouse() - { - addMovementStep(new Vector2(200)); - AddStep("press left button", () => InputManager.PressButton(MouseButton.Left)); - - addMovementStep(new Vector2(400, 200)); - AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left)); - - addClickStep(MouseButton.Right); - - assertPlaced(true); - assertLength(200); - assertControlPointCount(2); - assertControlPointType(0, PathType.LINEAR); - } - [Test] public void TestSliderDrawing() { From 8e39dbbff1898830ab70c5b418ec31b661fede42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:41:26 +0900 Subject: [PATCH 0131/1118] Adjust casing of curve type menu items The "Perfect curve" one in particular... fixes test failures, as some tests were relying on this particular casing. But the new version feels more correct anyway, so it's whatever. --- osu.Game/Rulesets/Objects/Types/PathType.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index 37c1d0ab50..f84d43e3e7 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -56,13 +56,13 @@ namespace osu.Game.Rulesets.Objects.Types return "Catmull"; case SplineType.BSpline: - return Degree == null ? "Bezier" : "B-Spline"; + return Degree == null ? "Bezier" : "B-spline"; case SplineType.Linear: return "Linear"; case SplineType.PerfectCurve: - return "Perfect Curve"; + return "Perfect curve"; default: return Type.ToString(); From 43dbd65708de80e4e671e042e76496ffb7526d57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:53:25 +0900 Subject: [PATCH 0132/1118] Show Catmull as a control point type option if selection already contains it --- .../TestScenePathControlPointVisualiser.cs | 26 ++++++++++++++++++- .../Components/PathControlPointVisualiser.cs | 4 +-- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs index 811ecf53e9..8234381283 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestScenePathControlPointVisualiser.cs @@ -148,6 +148,30 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointPathType(3, null); } + [Test] + public void TestCatmullAvailableIffSelectionContainsCatmull() + { + createVisualiser(true); + + addControlPointStep(new Vector2(200), PathType.CATMULL); + addControlPointStep(new Vector2(300)); + addControlPointStep(new Vector2(500, 300)); + addControlPointStep(new Vector2(700, 200)); + addControlPointStep(new Vector2(500, 100)); + + moveMouseToControlPoint(2); + AddStep("select first and third control point", () => + { + visualiser.Pieces[0].IsSelected.Value = true; + visualiser.Pieces[2].IsSelected.Value = true; + }); + addContextMenuItemStep("Catmull"); + + assertControlPointPathType(0, PathType.CATMULL); + assertControlPointPathType(2, PathType.CATMULL); + assertControlPointPathType(4, null); + } + private void createVisualiser(bool allowSelection) => AddStep("create visualiser", () => Child = visualiser = new PathControlPointVisualiser(slider, allowSelection) { Anchor = Anchor.Centre, @@ -158,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private void addControlPointStep(Vector2 position, PathType? type) { - AddStep($"add {type} control point at {position}", () => + AddStep($"add {type?.Type} control point at {position}", () => { slider.Path.ControlPoints.Add(new PathControlPoint(position, type)); }); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 95c72a0a62..751ca7e753 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -372,9 +372,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER)); curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3))); - var hoveredPiece = Pieces.FirstOrDefault(p => p.IsHovered); - - if (hoveredPiece?.ControlPoint.Type == PathType.CATMULL) + if (selectedPieces.Any(piece => piece.ControlPoint.Type?.Type == SplineType.Catmull)) curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); var menuItems = new List From 4061417ac8cff9242cf5582fdb8b173f7095059e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 15:54:43 +0900 Subject: [PATCH 0133/1118] Decrease default value for slider tolerance Highly subjective change, but at 50 the drawing just did not feel responsive enough to input. --- osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs index e44f0265c8..7e72eee510 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; - private readonly BindableInt sliderTolerance = new BindableInt(50) + private readonly BindableInt sliderTolerance = new BindableInt(40) { MinValue = 5, MaxValue = 100 From ded9981d07b8bc54fd43a53a76f6335aab5d636f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Nov 2023 17:55:01 +0900 Subject: [PATCH 0134/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index dd5a8996fb..8175869405 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 9a0832b4e7..a5425ba4c7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 492fd06c624b2d8d8ff65464798bcde72c4fd51c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 20 Nov 2023 19:21:23 +0900 Subject: [PATCH 0135/1118] Remove unnecessary null override --- .../Blueprints/Sliders/Components/PathControlPointVisualiser.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 751ca7e753..3128f46357 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -405,7 +405,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components int totalCount = Pieces.Count(p => p.IsSelected.Value); int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type == type); - var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type!.Value.Description, MenuItemType.Standard, _ => + var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.Value.Description, MenuItemType.Standard, _ => { foreach (var p in Pieces.Where(p => p.IsSelected.Value)) updatePathType(p, type); From c9e8d66e1970fa8498761b4fb5fe868bf879d1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 21:02:12 +0900 Subject: [PATCH 0136/1118] Improve xmldoc --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 4e37481ba7..3fda18b7cb 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -555,9 +555,9 @@ namespace osu.Game public void ShowChangelogBuild(string updateStream, string version) => waitForReady(() => changelogOverlay, _ => changelogOverlay.ShowBuild(updateStream, version)); /// - /// Seek to a given timestamp in the Editor and select relevant HitObjects if needed + /// Seeks to the provided if the editor is currently open. + /// Can also select objects as indicated by the (depends on ruleset implementation). /// - /// The timestamp and the selected objects public void HandleTimestamp(string timestamp) { if (ScreenStack.CurrentScreen is not Editor editor) From 0e0ab66148ec99a0458b5102541e1a58206a2887 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 21:27:24 +0900 Subject: [PATCH 0137/1118] Simplify parsing code Less methods, less smeared around logic, saner data types. --- osu.Game/Localisation/EditorStrings.cs | 5 --- .../Rulesets/Edit/EditorTimestampParser.cs | 41 ++++++++++++------- osu.Game/Screens/Edit/Editor.cs | 29 +++---------- 3 files changed, 32 insertions(+), 43 deletions(-) diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index 227dbc5e0c..e762455e8b 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -129,11 +129,6 @@ namespace osu.Game.Localisation /// public static LocalisableString FailedToProcessTimestamp => new TranslatableString(getKey(@"failed_to_process_timestamp"), @"Failed to process timestamp"); - /// - /// "The timestamp was too long to process" - /// - public static LocalisableString TooLongTimestamp => new TranslatableString(getKey(@"too_long_timestamp"), @"The timestamp was too long to process"); - private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Rulesets/Edit/EditorTimestampParser.cs b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs index e36822cc63..bdfdce432e 100644 --- a/osu.Game/Rulesets/Edit/EditorTimestampParser.cs +++ b/osu.Game/Rulesets/Edit/EditorTimestampParser.cs @@ -2,8 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Diagnostics; -using System.Linq; +using System.Diagnostics.CodeAnalysis; using System.Text.RegularExpressions; namespace osu.Game.Rulesets.Edit @@ -12,26 +11,40 @@ namespace osu.Game.Rulesets.Edit { // 00:00:000 (...) - test // original osu-web regex: https://github.com/ppy/osu-web/blob/3b1698639244cfdaf0b41c68bfd651ea729ec2e3/resources/js/utils/beatmapset-discussion-helper.ts#L78 - public static readonly Regex TIME_REGEX = new Regex(@"\b(((\d{2,}):([0-5]\d)[:.](\d{3}))(\s\([^)]+\))?)"); + public static readonly Regex TIME_REGEX = new Regex(@"\b(((?\d{2,}):(?[0-5]\d)[:.](?\d{3}))(?\s\([^)]+\))?)", RegexOptions.Compiled); - public static string[] GetRegexGroups(string timestamp) + public static bool TryParse(string timestamp, [NotNullWhen(true)] out TimeSpan? parsedTime, out string? parsedSelection) { Match match = TIME_REGEX.Match(timestamp); - string[] result = match.Success - ? match.Groups.Values.Where(x => x is not Match && !x.Value.Contains(':')).Select(x => x.Value).ToArray() - : Array.Empty(); + if (!match.Success) + { + parsedTime = null; + parsedSelection = null; + return false; + } - return result; - } + bool result = true; - public static double GetTotalMilliseconds(params string[] timesGroup) - { - int[] times = timesGroup.Select(int.Parse).ToArray(); + result &= int.TryParse(match.Groups[@"minutes"].Value, out int timeMin); + result &= int.TryParse(match.Groups[@"seconds"].Value, out int timeSec); + result &= int.TryParse(match.Groups[@"milliseconds"].Value, out int timeMsec); - Debug.Assert(times.Length == 3); + // somewhat sane limit for timestamp duration (10 hours). + result &= timeMin < 600; - return (times[0] * 60 + times[1]) * 1000 + times[2]; + if (!result) + { + parsedTime = null; + parsedSelection = null; + return false; + } + + parsedTime = TimeSpan.FromMinutes(timeMin) + TimeSpan.FromSeconds(timeSec) + TimeSpan.FromMilliseconds(timeMsec); + parsedSelection = match.Groups[@"selection"].Value.Trim(); + if (!string.IsNullOrEmpty(parsedSelection)) + parsedSelection = parsedSelection[1..^1]; + return true; } } } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 03d3e3a1f8..c38e88cd02 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1141,9 +1141,7 @@ namespace osu.Game.Screens.Edit public void HandleTimestamp(string timestamp) { - string[] groups = EditorTimestampParser.GetRegexGroups(timestamp); - - if (groups.Length != 4 || string.IsNullOrEmpty(groups[0])) + if (!EditorTimestampParser.TryParse(timestamp, out var timeSpan, out string selection)) { Schedule(() => notifications?.Post(new SimpleNotification { @@ -1153,31 +1151,14 @@ namespace osu.Game.Screens.Edit return; } - string timeMin = groups[0]; - string timeSec = groups[1]; - string timeMss = groups[2]; - string objectsGroup = groups[3].Replace("(", "").Replace(")", "").Trim(); - - // Currently, lazer chat highlights infinite-long editor links like `10000000000:00:000 (1)` - // Limit timestamp link length at 30000 min (50 hr) to avoid parsing issues - if (string.IsNullOrEmpty(timeMin) || timeMin.Length > 5 || double.Parse(timeMin) > 30_000) - { - Schedule(() => notifications?.Post(new SimpleNotification - { - Icon = FontAwesome.Solid.ExclamationTriangle, - Text = EditorStrings.TooLongTimestamp - })); - return; - } - editorBeatmap.SelectedHitObjects.Clear(); if (clock.IsRunning) clock.Stop(); - double position = EditorTimestampParser.GetTotalMilliseconds(timeMin, timeSec, timeMss); + double position = timeSpan.Value.TotalMilliseconds; - if (string.IsNullOrEmpty(objectsGroup)) + if (string.IsNullOrEmpty(selection)) { clock.SeekSmoothlyTo(position); return; @@ -1194,8 +1175,8 @@ namespace osu.Game.Screens.Edit if (Mode.Value != EditorScreenMode.Compose) Mode.Value = EditorScreenMode.Compose; - // Let the Ruleset handle selection - currentScreen.Dependencies.Get().SelectHitObjects(position, objectsGroup); + // Delegate handling the selection to the ruleset. + currentScreen.Dependencies.Get().SelectHitObjects(position, selection); } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); From 246aacb216d692b7bc54ae7fccebfd5869561d82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 21:29:19 +0900 Subject: [PATCH 0138/1118] Remove unnecessary guard Setting a bindable's value to something if that value is already there is a no-op (doesn't trigger bindings / callbacks). --- osu.Game/Screens/Edit/Editor.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index c38e88cd02..888f535cc6 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1172,8 +1172,7 @@ namespace osu.Game.Screens.Edit clock.SeekSmoothlyTo(position); - if (Mode.Value != EditorScreenMode.Compose) - Mode.Value = EditorScreenMode.Compose; + Mode.Value = EditorScreenMode.Compose; // Delegate handling the selection to the ruleset. currentScreen.Dependencies.Get().SelectHitObjects(position, selection); From 234ef6f92379b3cbf0c9a07f2334f2a068f9fe95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 21:34:36 +0900 Subject: [PATCH 0139/1118] Rectify selection keep-alive logic --- .../Components/EditorBlueprintContainer.cs | 27 ++++++++++++------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs index a311054ffc..378d378be3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/EditorBlueprintContainer.cs @@ -51,10 +51,7 @@ namespace osu.Game.Screens.Edit.Compose.Components Beatmap.HitObjectAdded += AddBlueprintFor; Beatmap.HitObjectRemoved += RemoveBlueprintFor; - - // This makes sure HitObjects will have active Blueprints ready to display - // after clicking on an Editor Timestamp/Link - Beatmap.SelectedHitObjects.CollectionChanged += selectionChanged; + Beatmap.SelectedHitObjects.CollectionChanged += updateSelectionLifetime; if (Composer != null) { @@ -149,13 +146,23 @@ namespace osu.Game.Screens.Edit.Compose.Components SelectedItems.AddRange(Beatmap.HitObjects.Except(SelectedItems).ToArray()); } - private void selectionChanged(object sender, NotifyCollectionChangedEventArgs e) + /// + /// Ensures that newly-selected hitobjects are kept alive + /// and drops that keep-alive from newly-deselected objects. + /// + private void updateSelectionLifetime(object sender, NotifyCollectionChangedEventArgs e) { - if (e == null || e.Action != NotifyCollectionChangedAction.Add || e.NewItems == null) - return; + if (e.NewItems != null) + { + foreach (HitObject newSelection in e.NewItems) + Composer.Playfield.SetKeepAlive(newSelection, true); + } - foreach (HitObject item in e.NewItems) - Composer.Playfield.SetKeepAlive(item, true); + if (e.OldItems != null) + { + foreach (HitObject oldSelection in e.OldItems) + Composer.Playfield.SetKeepAlive(oldSelection, false); + } } protected override void OnBlueprintSelected(SelectionBlueprint blueprint) @@ -180,7 +187,7 @@ namespace osu.Game.Screens.Edit.Compose.Components { Beatmap.HitObjectAdded -= AddBlueprintFor; Beatmap.HitObjectRemoved -= RemoveBlueprintFor; - Beatmap.SelectedHitObjects.CollectionChanged -= selectionChanged; + Beatmap.SelectedHitObjects.CollectionChanged -= updateSelectionLifetime; } usageEventBuffer?.Dispose(); From b6215b2809aac10f2bbf82403b76c6ca37f8d285 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 21:37:29 +0900 Subject: [PATCH 0140/1118] Rename and document `SelectFromTimestamp` --- .../Edit/ManiaHitObjectComposer.cs | 2 +- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 2 +- osu.Game/Rulesets/Edit/HitObjectComposer.cs | 13 +++++++++---- osu.Game/Screens/Edit/Editor.cs | 2 +- 4 files changed, 12 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index 92ecea812c..df6c62da51 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -55,7 +55,7 @@ namespace osu.Game.Rulesets.Mania.Edit public override string ConvertSelectionToString() => string.Join(',', EditorBeatmap.SelectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => $"{h.StartTime}|{h.Column}")); - public override void SelectHitObjects(double timestamp, string objectDescription) + public override void SelectFromTimestamp(double timestamp, string objectDescription) { if (!selection_regex.IsMatch(objectDescription)) return; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 2afbd83ce5..585d692978 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -110,7 +110,7 @@ namespace osu.Game.Rulesets.Osu.Edit public override string ConvertSelectionToString() => string.Join(',', selectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString())); - public override void SelectHitObjects(double timestamp, string objectDescription) + public override void SelectFromTimestamp(double timestamp, string objectDescription) { if (!selection_regex.IsMatch(objectDescription)) return; diff --git a/osu.Game/Rulesets/Edit/HitObjectComposer.cs b/osu.Game/Rulesets/Edit/HitObjectComposer.cs index 52a525e84f..50e6393895 100644 --- a/osu.Game/Rulesets/Edit/HitObjectComposer.cs +++ b/osu.Game/Rulesets/Edit/HitObjectComposer.cs @@ -526,14 +526,19 @@ namespace osu.Game.Rulesets.Edit /// public abstract bool CursorInPlacementArea { get; } + /// + /// Returns a string representing the current selection. + /// The inverse method to . + /// public virtual string ConvertSelectionToString() => string.Empty; /// - /// Each ruleset has it's own selection method + /// Selects objects based on the supplied and . + /// The inverse method to . /// - /// The given timestamp - /// The selected object information between the brackets - public virtual void SelectHitObjects(double timestamp, string objectDescription) { } + /// The time instant to seek to, in milliseconds. + /// The ruleset-specific description of objects to select at the given timestamp. + public virtual void SelectFromTimestamp(double timestamp, string objectDescription) { } #region IPositionSnapProvider diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 888f535cc6..fb34767315 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1175,7 +1175,7 @@ namespace osu.Game.Screens.Edit Mode.Value = EditorScreenMode.Compose; // Delegate handling the selection to the ruleset. - currentScreen.Dependencies.Get().SelectHitObjects(position, selection); + currentScreen.Dependencies.Get().SelectFromTimestamp(position, selection); } public double SnapTime(double time, double? referenceTime) => editorBeatmap.SnapTime(time, referenceTime); From c16afeb34700b22a09932ad792ae801c0c5bffb9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 21:57:07 +0900 Subject: [PATCH 0141/1118] Fix tests --- .../Editing/TestSceneOpenEditorTimestamp.cs | 38 +++++++++---------- 1 file changed, 17 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index bc31924e2c..86ceb73f7a 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -83,46 +83,42 @@ namespace osu.Game.Tests.Visual.Editing RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; addStepClickLink("00:00:000", waitForSeek: false); - AddAssert("recieved 'must be in edit'", () => - Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 1 - ); + AddAssert("received 'must be in edit'", + () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit), + () => Is.EqualTo(1)); AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); - AddAssert("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); + AddUntilStep("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); addStepClickLink("00:00:000 (1)", waitForSeek: false); - AddAssert("recieved 'must be in edit'", () => - Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit) == 2 - ); + AddAssert("received 'must be in edit'", + () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit), + () => Is.EqualTo(2)); setUpEditor(rulesetInfo); - AddAssert("is editor Osu", () => editorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + AddAssert("ruleset is osu!", () => editorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); addStepClickLink("00:000", "invalid link", waitForSeek: false); - AddAssert("recieved 'failed to process'", () => - Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp) == 1 - ); + AddAssert("received 'failed to process'", + () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp), + () => Is.EqualTo(1)); addStepClickLink("50000:00:000", "too long link", waitForSeek: false); - AddAssert("recieved 'too long'", () => - Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.TooLongTimestamp) == 1 - ); + AddAssert("received 'failed to process'", + () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp), + () => Is.EqualTo(2)); } [Test] public void TestHandleCurrentScreenChanges() { - const long long_link_value = 1_000 * 60 * 1_000; RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; setUpEditor(rulesetInfo); - AddAssert("is editor Osu", () => editorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); + AddAssert("is osu! ruleset", () => editorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); - addStepClickLink("1000:00:000", "long link"); - AddAssert("moved to end of track", () => - editorClock.CurrentTime == long_link_value - || (editorClock.TrackLength < long_link_value && editorClock.CurrentTime == editorClock.TrackLength) - ); + addStepClickLink("100:00:000", "long link"); + AddUntilStep("moved to end of track", () => editorClock.CurrentTime, () => Is.EqualTo(editorClock.TrackLength)); addStepScreenModeTo(EditorScreenMode.SongSetup); addStepClickLink("00:00:000"); From 364a3f75e15001b785f185c41ba69e28edecf926 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 22:03:25 +0900 Subject: [PATCH 0142/1118] Compile regexes --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 6 +++--- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index df6c62da51..e876ff1c81 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -49,12 +49,12 @@ namespace osu.Game.Rulesets.Mania.Edit new HoldNoteCompositionTool() }; - // 123|0,456|1,789|2 ... - private static readonly Regex selection_regex = new Regex(@"^\d+\|\d+(,\d+\|\d+)*$"); - public override string ConvertSelectionToString() => string.Join(',', EditorBeatmap.SelectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => $"{h.StartTime}|{h.Column}")); + // 123|0,456|1,789|2 ... + private static readonly Regex selection_regex = new Regex(@"^\d+\|\d+(,\d+\|\d+)*$", RegexOptions.Compiled); + public override void SelectFromTimestamp(double timestamp, string objectDescription) { if (!selection_regex.IsMatch(objectDescription)) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 585d692978..c3fd36b933 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -104,12 +104,12 @@ namespace osu.Game.Rulesets.Osu.Edit protected override ComposeBlueprintContainer CreateBlueprintContainer() => new OsuBlueprintContainer(this); - // 1,2,3,4 ... - private static readonly Regex selection_regex = new Regex(@"^\d+(,\d+)*$"); - public override string ConvertSelectionToString() => string.Join(',', selectedHitObjects.Cast().OrderBy(h => h.StartTime).Select(h => (h.IndexInCurrentCombo + 1).ToString())); + // 1,2,3,4 ... + private static readonly Regex selection_regex = new Regex(@"^\d+(,\d+)*$", RegexOptions.Compiled); + public override void SelectFromTimestamp(double timestamp, string objectDescription) { if (!selection_regex.IsMatch(objectDescription)) From a8fc73695f30b3d22c260c8ea030f3034d1c21d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 22:04:08 +0900 Subject: [PATCH 0143/1118] Rename variable --- osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs index e876ff1c81..967cdb0e54 100644 --- a/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs +++ b/osu.Game.Rulesets.Mania/Edit/ManiaHitObjectComposer.cs @@ -61,11 +61,11 @@ namespace osu.Game.Rulesets.Mania.Edit return; List remainingHitObjects = EditorBeatmap.HitObjects.Cast().Where(h => h.StartTime >= timestamp).ToList(); - string[] splitDescription = objectDescription.Split(',').ToArray(); + string[] objectDescriptions = objectDescription.Split(',').ToArray(); - for (int i = 0; i < splitDescription.Length; i++) + for (int i = 0; i < objectDescriptions.Length; i++) { - string[] split = splitDescription[i].Split('|').ToArray(); + string[] split = objectDescriptions[i].Split('|').ToArray(); if (split.Length != 2) continue; @@ -79,7 +79,7 @@ namespace osu.Game.Rulesets.Mania.Edit EditorBeatmap.SelectedHitObjects.Add(current); - if (i < splitDescription.Length - 1) + if (i < objectDescriptions.Length - 1) remainingHitObjects = remainingHitObjects.Where(h => h != current && h.StartTime >= current.StartTime).ToList(); } } From 745a04a243b90a1b83da308ea3e8e6467c9faacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 20 Nov 2023 22:12:15 +0900 Subject: [PATCH 0144/1118] More test cleanup --- .../TestSceneOpenEditorTimestampInOsu.cs | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs index 93573b5ad8..753400e10e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; @@ -25,16 +26,17 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor addStepClickLink("00:00:000", "reset", false); } - private bool checkSnapAndSelectCombo(double startTime, params int[] comboNumbers) - { - bool checkCombos = comboNumbers.Any() - ? hasCombosInOrder(EditorBeatmap.SelectedHitObjects, comboNumbers) - : !EditorBeatmap.SelectedHitObjects.Any(); + private void checkSelection(Func startTime, params int[] comboNumbers) + => AddUntilStep($"seeked & selected {(comboNumbers.Any() ? string.Join(",", comboNumbers) : "nothing")}", () => + { + bool checkCombos = comboNumbers.Any() + ? hasCombosInOrder(EditorBeatmap.SelectedHitObjects, comboNumbers) + : !EditorBeatmap.SelectedHitObjects.Any(); - return EditorClock.CurrentTime == startTime - && EditorBeatmap.SelectedHitObjects.Count == comboNumbers.Length - && checkCombos; - } + return EditorClock.CurrentTime == startTime() + && EditorBeatmap.SelectedHitObjects.Count == comboNumbers.Length + && checkCombos; + }); private bool hasCombosInOrder(IEnumerable selected, params int[] comboNumbers) { @@ -51,19 +53,19 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor public void TestNormalSelection() { addStepClickLink("00:02:170 (1,2,3)"); - AddAssert("snap and select 1-2-3", () => checkSnapAndSelectCombo(2_170, 1, 2, 3)); + checkSelection(() => 2_170, 1, 2, 3); addReset(); addStepClickLink("00:04:748 (2,3,4,1,2)"); - AddAssert("snap and select 2-3-4-1-2", () => checkSnapAndSelectCombo(4_748, 2, 3, 4, 1, 2)); + checkSelection(() => 4_748, 2, 3, 4, 1, 2); addReset(); addStepClickLink("00:02:170 (1,1,1)"); - AddAssert("snap and select 1-1-1", () => checkSnapAndSelectCombo(2_170, 1, 1, 1)); + checkSelection(() => 2_170, 1, 1, 1); addReset(); addStepClickLink("00:02:873 (2,2,2,2)"); - AddAssert("snap and select 2-2-2-2", () => checkSnapAndSelectCombo(2_873, 2, 2, 2, 2)); + checkSelection(() => 2_873, 2, 2, 2, 2); } [Test] @@ -71,24 +73,22 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { HitObject firstObject = null!; + AddStep("retrieve first object", () => firstObject = EditorBeatmap.HitObjects.First()); + addStepClickLink("00:00:000 (0)", "invalid combo"); - AddAssert("snap to next, select none", () => - { - firstObject = EditorBeatmap.HitObjects.First(); - return checkSnapAndSelectCombo(firstObject.StartTime); - }); + checkSelection(() => firstObject.StartTime); addReset(); addStepClickLink("00:00:000 (1)", "wrong offset"); - AddAssert("snap and select 1", () => checkSnapAndSelectCombo(firstObject.StartTime, 1)); + checkSelection(() => firstObject.StartTime, 1); addReset(); addStepClickLink("00:00:956 (2,3,4)", "wrong offset"); - AddAssert("snap to next, select 2-3-4", () => checkSnapAndSelectCombo(firstObject.StartTime, 2, 3, 4)); + checkSelection(() => firstObject.StartTime, 2, 3, 4); addReset(); addStepClickLink("00:00:956 (956|1,956|2)", "mania link"); - AddAssert("snap to next, select none", () => checkSnapAndSelectCombo(firstObject.StartTime)); + checkSelection(() => firstObject.StartTime); } } } From 750bbc8a19cfdb0f1c3133e9bd86e02bacb51f0d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 00:17:25 +0900 Subject: [PATCH 0145/1118] Simplify null checks --- .../Sliders/Components/PathControlPointVisualiser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 3128f46357..ef8a033014 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -242,7 +242,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint); - if (type.HasValue && type.Value.Type == SplineType.PerfectCurve) + if (type?.Type == SplineType.PerfectCurve) { // Can't always create a circular arc out of 4 or more points, // so we split the segment into one 3-point circular arc segment @@ -405,7 +405,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components int totalCount = Pieces.Count(p => p.IsSelected.Value); int countOfState = Pieces.Where(p => p.IsSelected.Value).Count(p => p.ControlPoint.Type == type); - var item = new TernaryStateRadioMenuItem(type == null ? "Inherit" : type.Value.Description, MenuItemType.Standard, _ => + var item = new TernaryStateRadioMenuItem(type?.Description ?? "Inherit", MenuItemType.Standard, _ => { foreach (var p in Pieces.Where(p => p.IsSelected.Value)) updatePathType(p, type); From 6f5c468a83cd0341616d01d10ef5345568a7d943 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 00:21:44 +0900 Subject: [PATCH 0146/1118] Rename settings class --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 8 ++++---- ...ovider.cs => FreehandSliderSettingsProvider.cs} | 14 +++++++------- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 6 +++--- 3 files changed, 14 insertions(+), 14 deletions(-) rename osu.Game.Rulesets.Osu/Edit/{OsuSliderDrawingSettingsProvider.cs => FreehandSliderSettingsProvider.cs} (98%) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index fb2c1d5149..df8417d8ff 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private IDistanceSnapProvider distanceSnapProvider { get; set; } [Resolved(CanBeNull = true)] - private OsuSliderDrawingSettingsProvider drawingSettingsProvider { get; set; } + private FreehandSliderSettingsProvider freehandSettingsProvider { get; set; } private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder(); @@ -80,16 +80,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.LoadComplete(); inputManager = GetContainingInputManager(); - if (drawingSettingsProvider != null) + if (freehandSettingsProvider != null) { - drawingSettingsProvider.Tolerance.BindValueChanged(e => + freehandSettingsProvider.Tolerance.BindValueChanged(e => { if (bSplineBuilder.Tolerance != e.NewValue) bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); }); - drawingSettingsProvider.CornerThreshold.BindValueChanged(e => + freehandSettingsProvider.CornerThreshold.BindValueChanged(e => { if (bSplineBuilder.CornerThreshold != e.NewValue) bSplineBuilder.CornerThreshold = e.NewValue; diff --git a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs similarity index 98% rename from osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs rename to osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs index 7e72eee510..9ad2b5d0f5 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuSliderDrawingSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class OsuSliderDrawingSettingsProvider : Drawable + public partial class FreehandSliderSettingsProvider : Drawable { public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) { @@ -19,12 +19,6 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; - private readonly BindableInt sliderTolerance = new BindableInt(40) - { - MinValue = 5, - MaxValue = 100 - }; - public BindableFloat CornerThreshold { get; } = new BindableFloat(0.4f) { MinValue = 0.05f, @@ -32,6 +26,12 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; + private readonly BindableInt sliderTolerance = new BindableInt(40) + { + MinValue = 5, + MaxValue = 100 + }; + private readonly BindableInt sliderCornerThreshold = new BindableInt(40) { MinValue = 5, diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 0dc0d6fd31..7bf1a12149 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); [Cached] - protected readonly OsuSliderDrawingSettingsProvider SliderDrawingSettingsProvider = new OsuSliderDrawingSettingsProvider(); + protected readonly FreehandSliderSettingsProvider FreehandSliderSettingsProvider = new FreehandSliderSettingsProvider(); [BackgroundDependencyLoader] private void load() @@ -102,8 +102,8 @@ namespace osu.Game.Rulesets.Osu.Edit RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }); - AddInternal(SliderDrawingSettingsProvider); - SliderDrawingSettingsProvider.AttachToToolbox(RightToolbox); + AddInternal(FreehandSliderSettingsProvider); + FreehandSliderSettingsProvider.AttachToToolbox(RightToolbox); } protected override ComposeBlueprintContainer CreateBlueprintContainer() From 638c8f1adc2e135aff574e135c515adfbf384423 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 00:25:23 +0900 Subject: [PATCH 0147/1118] Get rid of weird cruft and non-standard flow --- .../Edit/FreehandSliderSettingsProvider.cs | 66 ++++++++++--------- .../Edit/OsuHitObjectComposer.cs | 5 +- 2 files changed, 37 insertions(+), 34 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs index 9ad2b5d0f5..f4ee130938 100644 --- a/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Utils; @@ -10,8 +11,13 @@ using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class FreehandSliderSettingsProvider : Drawable + public partial class FreehandSliderSettingsProvider : EditorToolboxGroup { + public FreehandSliderSettingsProvider() + : base("slider") + { + } + public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) { MinValue = 0.05f, @@ -41,6 +47,34 @@ namespace osu.Game.Rulesets.Osu.Edit private ExpandableSlider toleranceSlider = null!; private ExpandableSlider cornerThresholdSlider = null!; + [BackgroundDependencyLoader] + private void load() + { + Children = new Drawable[] + { + toleranceSlider = new ExpandableSlider + { + Current = sliderTolerance + }, + cornerThresholdSlider = new ExpandableSlider + { + Current = sliderCornerThreshold + } + }; + + sliderTolerance.BindValueChanged(e => + { + toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; + toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; + }, true); + + sliderCornerThreshold.BindValueChanged(e => + { + cornerThresholdSlider.ContractedLabelText = $"C. T.: {e.NewValue:N0}"; + cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {e.NewValue:N0}"; + }, true); + } + protected override void LoadComplete() { base.LoadComplete(); @@ -70,35 +104,5 @@ namespace osu.Game.Rulesets.Osu.Edit sliderCornerThreshold.Value = newValue; }); } - - public void AttachToToolbox(ExpandingToolboxContainer toolboxContainer) - { - toolboxContainer.Add(new EditorToolboxGroup("slider") - { - Children = new Drawable[] - { - toleranceSlider = new ExpandableSlider - { - Current = sliderTolerance - }, - cornerThresholdSlider = new ExpandableSlider - { - Current = sliderCornerThreshold - } - } - }); - - sliderTolerance.BindValueChanged(e => - { - toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; - toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; - }, true); - - sliderCornerThreshold.BindValueChanged(e => - { - cornerThresholdSlider.ContractedLabelText = $"C. T.: {e.NewValue:N0}"; - cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {e.NewValue:N0}"; - }, true); - } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 7bf1a12149..06584ef17a 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); [Cached] - protected readonly FreehandSliderSettingsProvider FreehandSliderSettingsProvider = new FreehandSliderSettingsProvider(); + protected readonly FreehandSliderSettingsProvider FreehandlSliderToolboxGroup = new FreehandSliderSettingsProvider(); [BackgroundDependencyLoader] private void load() @@ -102,8 +102,7 @@ namespace osu.Game.Rulesets.Osu.Edit RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }); - AddInternal(FreehandSliderSettingsProvider); - FreehandSliderSettingsProvider.AttachToToolbox(RightToolbox); + RightToolbox.Add(FreehandlSliderToolboxGroup); } protected override ComposeBlueprintContainer CreateBlueprintContainer() From e9f371a5811b72763a50fc1e5fffba5ef95e081f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 09:59:49 +0900 Subject: [PATCH 0148/1118] Refactor slider settings class --- .../Sliders/SliderPlacementBlueprint.cs | 8 +- ...vider.cs => FreehandSliderToolboxGroup.cs} | 74 +++++++++---------- .../Edit/OsuHitObjectComposer.cs | 2 +- 3 files changed, 38 insertions(+), 46 deletions(-) rename osu.Game.Rulesets.Osu/Edit/{FreehandSliderSettingsProvider.cs => FreehandSliderToolboxGroup.cs} (53%) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index df8417d8ff..f8b7642643 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -46,7 +46,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private IDistanceSnapProvider distanceSnapProvider { get; set; } [Resolved(CanBeNull = true)] - private FreehandSliderSettingsProvider freehandSettingsProvider { get; set; } + private FreehandSliderToolboxGroup freehandToolboxGroup { get; set; } private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder(); @@ -80,16 +80,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.LoadComplete(); inputManager = GetContainingInputManager(); - if (freehandSettingsProvider != null) + if (freehandToolboxGroup != null) { - freehandSettingsProvider.Tolerance.BindValueChanged(e => + freehandToolboxGroup.Tolerance.BindValueChanged(e => { if (bSplineBuilder.Tolerance != e.NewValue) bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); }); - freehandSettingsProvider.CornerThreshold.BindValueChanged(e => + freehandToolboxGroup.CornerThreshold.BindValueChanged(e => { if (bSplineBuilder.CornerThreshold != e.NewValue) bSplineBuilder.CornerThreshold = e.NewValue; diff --git a/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs similarity index 53% rename from osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs rename to osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs index f4ee130938..1974415d30 100644 --- a/osu.Game.Rulesets.Osu/Edit/FreehandSliderSettingsProvider.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs @@ -5,15 +5,14 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; namespace osu.Game.Rulesets.Osu.Edit { - public partial class FreehandSliderSettingsProvider : EditorToolboxGroup + public partial class FreehandSliderToolboxGroup : EditorToolboxGroup { - public FreehandSliderSettingsProvider() + public FreehandSliderToolboxGroup() : base("slider") { } @@ -32,13 +31,14 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; - private readonly BindableInt sliderTolerance = new BindableInt(40) + // We map internal ranges to a more standard range of values for display to the user. + private readonly BindableInt displayTolerance = new BindableInt(40) { MinValue = 5, MaxValue = 100 }; - private readonly BindableInt sliderCornerThreshold = new BindableInt(40) + private readonly BindableInt displayCornerThreshold = new BindableInt(40) { MinValue = 5, MaxValue = 100 @@ -54,55 +54,47 @@ namespace osu.Game.Rulesets.Osu.Edit { toleranceSlider = new ExpandableSlider { - Current = sliderTolerance + Current = displayTolerance }, cornerThresholdSlider = new ExpandableSlider { - Current = sliderCornerThreshold + Current = displayCornerThreshold } }; - - sliderTolerance.BindValueChanged(e => - { - toleranceSlider.ContractedLabelText = $"C. P. S.: {e.NewValue:N0}"; - toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {e.NewValue:N0}"; - }, true); - - sliderCornerThreshold.BindValueChanged(e => - { - cornerThresholdSlider.ContractedLabelText = $"C. T.: {e.NewValue:N0}"; - cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {e.NewValue:N0}"; - }, true); } protected override void LoadComplete() { base.LoadComplete(); - sliderTolerance.BindValueChanged(v => + displayTolerance.BindValueChanged(tolerance => { - float newValue = v.NewValue / 33f; - if (!Precision.AlmostEquals(newValue, Tolerance.Value)) - Tolerance.Value = newValue; - }); - Tolerance.BindValueChanged(v => + toleranceSlider.ContractedLabelText = $"C. P. S.: {tolerance.NewValue:N0}"; + toleranceSlider.ExpandedLabelText = $"Control Point Spacing: {tolerance.NewValue:N0}"; + + Tolerance.Value = displayToInternalTolerance(tolerance.NewValue); + }, true); + + displayCornerThreshold.BindValueChanged(threshold => { - int newValue = (int)Math.Round(v.NewValue * 33f); - if (sliderTolerance.Value != newValue) - sliderTolerance.Value = newValue; - }); - sliderCornerThreshold.BindValueChanged(v => - { - float newValue = v.NewValue / 100f; - if (!Precision.AlmostEquals(newValue, CornerThreshold.Value)) - CornerThreshold.Value = newValue; - }); - CornerThreshold.BindValueChanged(v => - { - int newValue = (int)Math.Round(v.NewValue * 100f); - if (sliderCornerThreshold.Value != newValue) - sliderCornerThreshold.Value = newValue; - }); + cornerThresholdSlider.ContractedLabelText = $"C. T.: {threshold.NewValue:N0}"; + cornerThresholdSlider.ExpandedLabelText = $"Corner Threshold: {threshold.NewValue:N0}"; + + CornerThreshold.Value = displayToInternalCornerThreshold(threshold.NewValue); + }, true); + + Tolerance.BindValueChanged(tolerance => + displayTolerance.Value = internalToDisplayTolerance(tolerance.NewValue) + ); + CornerThreshold.BindValueChanged(threshold => + displayCornerThreshold.Value = internalToDisplayCornerThreshold(threshold.NewValue) + ); + + float displayToInternalTolerance(float v) => v / 33f; + int internalToDisplayTolerance(float v) => (int)Math.Round(v * 33f); + + float displayToInternalCornerThreshold(float v) => v / 100f; + int internalToDisplayCornerThreshold(float v) => (int)Math.Round(v * 100f); } } } diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 06584ef17a..061f72d72f 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Osu.Edit protected readonly OsuDistanceSnapProvider DistanceSnapProvider = new OsuDistanceSnapProvider(); [Cached] - protected readonly FreehandSliderSettingsProvider FreehandlSliderToolboxGroup = new FreehandSliderSettingsProvider(); + protected readonly FreehandSliderToolboxGroup FreehandlSliderToolboxGroup = new FreehandSliderToolboxGroup(); [BackgroundDependencyLoader] private void load() From 5514a53df171d035d7d3afdee56b38903f29dc07 Mon Sep 17 00:00:00 2001 From: Stedoss <29103029+Stedoss@users.noreply.github.com> Date: Tue, 21 Nov 2023 01:04:46 +0000 Subject: [PATCH 0149/1118] Pass ruleset to callback to prevent ruleset desync --- osu.Game/Overlays/UserProfileOverlay.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index d45a010e4a..fd494f63e1 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -174,16 +174,16 @@ namespace osu.Game.Overlays return; userReq = user.OnlineID > 1 ? new GetUserRequest(user.OnlineID, ruleset) : new GetUserRequest(user.Username, ruleset); - userReq.Success += userLoadComplete; + userReq.Success += u => userLoadComplete(u, ruleset); API.Queue(userReq); loadingLayer.Show(); } - private void userLoadComplete(APIUser loadedUser) + private void userLoadComplete(APIUser loadedUser, IRulesetInfo? userRuleset) { Debug.Assert(sections != null && sectionsContainer != null && tabs != null); - var actualRuleset = rulesets.GetRuleset(ruleset?.ShortName ?? loadedUser.PlayMode).AsNonNull(); + var actualRuleset = rulesets.GetRuleset(userRuleset?.ShortName ?? loadedUser.PlayMode).AsNonNull(); var userProfile = new UserProfileData(loadedUser, actualRuleset); Header.User.Value = userProfile; From ec7b82f5e874e7728a10ba5fe5c5872218dac056 Mon Sep 17 00:00:00 2001 From: Stedoss <29103029+Stedoss@users.noreply.github.com> Date: Tue, 21 Nov 2023 01:55:08 +0000 Subject: [PATCH 0150/1118] Change early return to check for online `State` instead of `IsLoggedIn` --- osu.Game/Overlays/UserProfileOverlay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index fd494f63e1..193651fa72 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics.Cursor; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Online; +using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Profile; @@ -170,7 +171,7 @@ namespace osu.Game.Overlays sectionsContainer.ScrollToTop(); - if (!API.IsLoggedIn) + if (API.State.Value != APIState.Online) return; userReq = user.OnlineID > 1 ? new GetUserRequest(user.OnlineID, ruleset) : new GetUserRequest(user.Username, ruleset); From 826c82de474f1cf322500dcc10e2d2d38719e297 Mon Sep 17 00:00:00 2001 From: Stedoss <29103029+Stedoss@users.noreply.github.com> Date: Tue, 21 Nov 2023 01:56:37 +0000 Subject: [PATCH 0151/1118] Add unit test for handling login state in `UserProfileOverlay` --- .../Online/TestSceneUserProfileOverlay.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs index a321a194a9..1375689075 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserProfileOverlay.cs @@ -78,6 +78,31 @@ namespace osu.Game.Tests.Visual.Online AddStep("complete request", () => pendingRequest.TriggerSuccess(TEST_USER)); } + [Test] + public void TestLogin() + { + GetUserRequest pendingRequest = null!; + + AddStep("set up request handling", () => + { + dummyAPI.HandleRequest = req => + { + if (dummyAPI.State.Value == APIState.Online && req is GetUserRequest getUserRequest) + { + pendingRequest = getUserRequest; + return true; + } + + return false; + }; + }); + AddStep("logout", () => dummyAPI.Logout()); + AddStep("show user", () => profile.ShowUser(new APIUser { Id = 1 })); + AddStep("login", () => dummyAPI.Login("username", "password")); + AddWaitStep("wait some", 3); + AddStep("complete request", () => pendingRequest.TriggerSuccess(TEST_USER)); + } + public static readonly APIUser TEST_USER = new APIUser { Username = @"Somebody", From 3680024e3102d5dcca617371105d4c3bf3787945 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 11:15:00 +0900 Subject: [PATCH 0152/1118] Fix tolerance not being transferred to blueprint in all cases --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index f8b7642643..5f072eb171 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (bSplineBuilder.Tolerance != e.NewValue) bSplineBuilder.Tolerance = e.NewValue; updateSliderPathFromBSplineBuilder(); - }); + }, true); freehandToolboxGroup.CornerThreshold.BindValueChanged(e => { From 405ab499e9dfd7c75477513aa38b344ffb901781 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:24:10 +0900 Subject: [PATCH 0153/1118] Allow context menus to have visible spacers --- .../Visual/Editing/TestSceneEditorMenuBar.cs | 22 +++++++------- osu.Game/Graphics/UserInterface/OsuMenu.cs | 30 ++++++++++++++++++- .../UserInterface/OsuMenuItemSpacer.cs | 13 ++++++++ osu.Game/Overlays/SkinEditor/SkinEditor.cs | 10 +++---- .../SkinEditor/SkinSelectionHandler.cs | 7 ++--- .../Edit/Components/Menus/EditorMenuBar.cs | 27 +---------------- .../Components/Menus/EditorMenuItemSpacer.cs | 13 -------- osu.Game/Screens/Edit/Editor.cs | 10 +++---- 8 files changed, 67 insertions(+), 65 deletions(-) create mode 100644 osu.Game/Graphics/UserInterface/OsuMenuItemSpacer.cs delete mode 100644 osu.Game/Screens/Edit/Components/Menus/EditorMenuItemSpacer.cs diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs index dbcf66f005..fe47f5885d 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorMenuBar.cs @@ -34,51 +34,51 @@ namespace osu.Game.Tests.Visual.Editing { new MenuItem("File") { - Items = new[] + Items = new OsuMenuItem[] { new EditorMenuItem("Clear All Notes"), new EditorMenuItem("Open Difficulty..."), new EditorMenuItem("Save"), new EditorMenuItem("Create a new Difficulty..."), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem("Revert to Saved"), new EditorMenuItem("Revert to Saved (Full)"), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem("Test Beatmap"), new EditorMenuItem("Open AiMod"), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem("Upload Beatmap..."), new EditorMenuItem("Export Package"), new EditorMenuItem("Export Map Package"), new EditorMenuItem("Import from..."), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem("Open Song Folder"), new EditorMenuItem("Open .osu in Notepad"), new EditorMenuItem("Open .osb in Notepad"), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem("Exit"), } }, new MenuItem("Timing") { - Items = new[] + Items = new OsuMenuItem[] { new EditorMenuItem("Time Signature"), new EditorMenuItem("Metronome Clicks"), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem("Add Timing Section"), new EditorMenuItem("Add Inheriting Section"), new EditorMenuItem("Reset Current Section"), new EditorMenuItem("Delete Timing Section"), new EditorMenuItem("Resnap Current Section"), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem("Timing Setup"), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem("Resnap All Notes", MenuItemType.Destructive), new EditorMenuItem("Move all notes in time...", MenuItemType.Destructive), new EditorMenuItem("Recalculate Slider Lengths", MenuItemType.Destructive), new EditorMenuItem("Delete All Timing Sections", MenuItemType.Destructive), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem("Set Current Position as Preview Point"), } }, diff --git a/osu.Game/Graphics/UserInterface/OsuMenu.cs b/osu.Game/Graphics/UserInterface/OsuMenu.cs index 73d57af793..e2aac297e3 100644 --- a/osu.Game/Graphics/UserInterface/OsuMenu.cs +++ b/osu.Game/Graphics/UserInterface/OsuMenu.cs @@ -6,13 +6,15 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; -using osuTK.Graphics; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; using osu.Game.Graphics.Containers; using osuTK; +using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface { @@ -78,6 +80,9 @@ namespace osu.Game.Graphics.UserInterface { case StatefulMenuItem stateful: return new DrawableStatefulMenuItem(stateful); + + case OsuMenuItemSpacer spacer: + return new DrawableSpacer(spacer); } return new DrawableOsuMenuItem(item); @@ -89,5 +94,28 @@ namespace osu.Game.Graphics.UserInterface { Anchor = Direction == Direction.Horizontal ? Anchor.BottomLeft : Anchor.TopRight }; + + protected partial class DrawableSpacer : DrawableOsuMenuItem + { + public DrawableSpacer(MenuItem item) + : base(item) + { + Scale = new Vector2(1, 0.6f); + + AddInternal(new Box + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = BackgroundColourHover, + RelativeSizeAxes = Axes.X, + Height = 2f, + Width = 0.8f, + }); + } + + protected override bool OnHover(HoverEvent e) => true; + + protected override bool OnClick(ClickEvent e) => true; + } } } diff --git a/osu.Game/Graphics/UserInterface/OsuMenuItemSpacer.cs b/osu.Game/Graphics/UserInterface/OsuMenuItemSpacer.cs new file mode 100644 index 0000000000..8a3a928c60 --- /dev/null +++ b/osu.Game/Graphics/UserInterface/OsuMenuItemSpacer.cs @@ -0,0 +1,13 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Graphics.UserInterface +{ + public class OsuMenuItemSpacer : OsuMenuItem + { + public OsuMenuItemSpacer() + : base(" ") + { + } + } +} diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index 38eed55241..a816031668 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -151,23 +151,23 @@ namespace osu.Game.Overlays.SkinEditor { new MenuItem(CommonStrings.MenuBarFile) { - Items = new[] + Items = new OsuMenuItem[] { new EditorMenuItem(Web.CommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), new EditorMenuItem(CommonStrings.Export, MenuItemType.Standard, () => skins.ExportCurrentSkin()) { Action = { Disabled = !RuntimeInfo.IsDesktop } }, - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem(CommonStrings.RevertToDefault, MenuItemType.Destructive, () => dialogOverlay?.Push(new RevertConfirmDialog(revert))), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, () => skinEditorOverlay?.Hide()), }, }, new MenuItem(CommonStrings.MenuBarEdit) { - Items = new[] + Items = new OsuMenuItem[] { undoMenuItem = new EditorMenuItem(CommonStrings.Undo, MenuItemType.Standard, Undo), redoMenuItem = new EditorMenuItem(CommonStrings.Redo, MenuItemType.Standard, Redo), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), cutMenuItem = new EditorMenuItem(CommonStrings.Cut, MenuItemType.Standard, Cut), copyMenuItem = new EditorMenuItem(CommonStrings.Copy, MenuItemType.Standard, Copy), pasteMenuItem = new EditorMenuItem(CommonStrings.Paste, MenuItemType.Standard, Paste), diff --git a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs index 52c012a15a..cf6fb60636 100644 --- a/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs +++ b/osu.Game/Overlays/SkinEditor/SkinSelectionHandler.cs @@ -14,7 +14,6 @@ using osu.Framework.Utils; using osu.Game.Extensions; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; -using osu.Game.Screens.Edit.Components.Menus; using osu.Game.Screens.Edit.Compose.Components; using osu.Game.Skinning; using osu.Game.Utils; @@ -249,7 +248,7 @@ namespace osu.Game.Overlays.SkinEditor Items = createAnchorItems((d, o) => ((Drawable)d).Origin == o, applyOrigins).ToArray() }; - yield return new EditorMenuItemSpacer(); + yield return new OsuMenuItemSpacer(); yield return new OsuMenuItem("Reset position", MenuItemType.Standard, () => { @@ -277,13 +276,13 @@ namespace osu.Game.Overlays.SkinEditor } }); - yield return new EditorMenuItemSpacer(); + yield return new OsuMenuItemSpacer(); yield return new OsuMenuItem("Bring to front", MenuItemType.Standard, () => skinEditor.BringSelectionToFront()); yield return new OsuMenuItem("Send to back", MenuItemType.Standard, () => skinEditor.SendSelectionToBack()); - yield return new EditorMenuItemSpacer(); + yield return new OsuMenuItemSpacer(); foreach (var item in base.GetContextMenuItemsForSelection(selection)) yield return item; diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index fb0ae2df73..a67375b0a4 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs @@ -4,11 +4,9 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; @@ -151,7 +149,7 @@ namespace osu.Game.Screens.Edit.Components.Menus { switch (item) { - case EditorMenuItemSpacer spacer: + case OsuMenuItemSpacer spacer: return new DrawableSpacer(spacer); case StatefulMenuItem stateful: @@ -195,29 +193,6 @@ namespace osu.Game.Screens.Edit.Components.Menus Foreground.Padding = new MarginPadding { Vertical = 2 }; } } - - private partial class DrawableSpacer : DrawableOsuMenuItem - { - public DrawableSpacer(MenuItem item) - : base(item) - { - Scale = new Vector2(1, 0.6f); - - AddInternal(new Box - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Colour = BackgroundColourHover, - RelativeSizeAxes = Axes.X, - Height = 2f, - Width = 0.8f, - }); - } - - protected override bool OnHover(HoverEvent e) => true; - - protected override bool OnClick(ClickEvent e) => true; - } } } } diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuItemSpacer.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuItemSpacer.cs deleted file mode 100644 index 4e75a92e19..0000000000 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuItemSpacer.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Screens.Edit.Components.Menus -{ - public class EditorMenuItemSpacer : EditorMenuItem - { - public EditorMenuItemSpacer() - : base(" ") - { - } - } -} diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 3136faf855..5d0cc64cc3 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -321,7 +321,7 @@ namespace osu.Game.Screens.Edit { undoMenuItem = new EditorMenuItem(CommonStrings.Undo, MenuItemType.Standard, Undo), redoMenuItem = new EditorMenuItem(CommonStrings.Redo, MenuItemType.Standard, Redo), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), cutMenuItem = new EditorMenuItem(CommonStrings.Cut, MenuItemType.Standard, Cut), copyMenuItem = new EditorMenuItem(CommonStrings.Copy, MenuItemType.Standard, Copy), pasteMenuItem = new EditorMenuItem(CommonStrings.Paste, MenuItemType.Standard, Paste), @@ -1005,12 +1005,12 @@ namespace osu.Game.Screens.Edit { createDifficultyCreationMenu(), createDifficultySwitchMenu(), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem(EditorStrings.DeleteDifficulty, MenuItemType.Standard, deleteDifficulty) { Action = { Disabled = Beatmap.Value.BeatmapSetInfo.Beatmaps.Count < 2 } }, - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem(WebCommonStrings.ButtonsSave, MenuItemType.Standard, () => Save()), createExportMenu(), - new EditorMenuItemSpacer(), + new OsuMenuItemSpacer(), new EditorMenuItem(CommonStrings.Exit, MenuItemType.Standard, this.Exit) }; @@ -1130,7 +1130,7 @@ namespace osu.Game.Screens.Edit foreach (var rulesetBeatmaps in groupedOrderedBeatmaps) { if (difficultyItems.Count > 0) - difficultyItems.Add(new EditorMenuItemSpacer()); + difficultyItems.Add(new OsuMenuItemSpacer()); foreach (var beatmap in rulesetBeatmaps) { From 9718a802490519780c3d88f55a90037a20b26421 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:24:19 +0900 Subject: [PATCH 0154/1118] Add visible spacer between "inherit" and other curve types --- .../Blueprints/Sliders/Components/PathControlPointVisualiser.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index f891d23bbd..87e837cc71 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -369,6 +369,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (!selectedPieces.Contains(Pieces[0])) curveTypeItems.Add(createMenuItemForPathType(null)); + curveTypeItems.Add(new OsuMenuItemSpacer()); + // todo: hide/disable items which aren't valid for selected points curveTypeItems.Add(createMenuItemForPathType(PathType.Linear)); curveTypeItems.Add(createMenuItemForPathType(PathType.PerfectCurve)); From c9ee29028fbcf3056c82d5b5c182f862f68974fa Mon Sep 17 00:00:00 2001 From: Samuel Cattini-Schultz Date: Tue, 21 Nov 2023 16:54:20 +1100 Subject: [PATCH 0155/1118] Fix implicitly used method being named incorrectly --- osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs index 24d5635104..83538a2f42 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuDifficultyAttributes.cs @@ -95,7 +95,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty yield return (ATTRIB_ID_APPROACH_RATE, ApproachRate); yield return (ATTRIB_ID_DIFFICULTY, StarRating); - if (ShouldSerializeFlashlightRating()) + if (ShouldSerializeFlashlightDifficulty()) yield return (ATTRIB_ID_FLASHLIGHT, FlashlightDifficulty); yield return (ATTRIB_ID_SLIDER_FACTOR, SliderFactor); @@ -128,7 +128,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty // unless the fields are also renamed. [UsedImplicitly] - public bool ShouldSerializeFlashlightRating() => Mods.Any(m => m is ModFlashlight); + public bool ShouldSerializeFlashlightDifficulty() => Mods.Any(m => m is ModFlashlight); #endregion } From 3afaafb1d9a19692cbd6750eda24cbd7d920c7d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 15:05:51 +0900 Subject: [PATCH 0156/1118] Reorder and simplify private helper methods --- .../TestSceneOpenEditorTimestampInMania.cs | 53 +++++----- .../TestSceneOpenEditorTimestampInOsu.cs | 65 ++++++------ .../Editing/TestSceneOpenEditorTimestamp.cs | 100 +++++++++--------- 3 files changed, 103 insertions(+), 115 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs index 3c6a9f3b42..05c881d284 100644 --- a/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs +++ b/osu.Game.Rulesets.Mania.Tests/Editor/TestSceneOpenEditorTimestampInMania.cs @@ -14,35 +14,6 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor { protected override Ruleset CreateEditorRuleset() => new ManiaRuleset(); - private void addStepClickLink(string timestamp, string step = "", bool displayTimestamp = true) - { - AddStep(displayTimestamp ? $"{step} {timestamp}" : step, () => Editor.HandleTimestamp(timestamp)); - AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value); - } - - private void addReset() - { - addStepClickLink("00:00:000", "reset", false); - } - - private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(int, int)>? columnPairs = null) - { - bool checkColumns = columnPairs != null - ? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2))) - : !EditorBeatmap.SelectedHitObjects.Any(); - - return EditorClock.CurrentTime == startTime - && EditorBeatmap.SelectedHitObjects.Count == (columnPairs?.Count ?? 0) - && checkColumns; - } - - private bool isNoteAt(HitObject hitObject, double time, int column) - { - return hitObject is ManiaHitObject maniaHitObject - && maniaHitObject.StartTime == time - && maniaHitObject.Column == column; - } - [Test] public void TestNormalSelection() { @@ -95,5 +66,29 @@ namespace osu.Game.Rulesets.Mania.Tests.Editor addStepClickLink("00:00:000 (1,2)", "std link"); AddAssert("snap to 1, select none", () => checkSnapAndSelectColumn(2_170)); } + + private void addStepClickLink(string timestamp, string step = "", bool displayTimestamp = true) + { + AddStep(displayTimestamp ? $"{step} {timestamp}" : step, () => Editor.HandleTimestamp(timestamp)); + AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value); + } + + private void addReset() => addStepClickLink("00:00:000", "reset", false); + + private bool checkSnapAndSelectColumn(double startTime, IReadOnlyCollection<(int, int)>? columnPairs = null) + { + bool checkColumns = columnPairs != null + ? EditorBeatmap.SelectedHitObjects.All(x => columnPairs.Any(col => isNoteAt(x, col.Item1, col.Item2))) + : !EditorBeatmap.SelectedHitObjects.Any(); + + return EditorClock.CurrentTime == startTime + && EditorBeatmap.SelectedHitObjects.Count == (columnPairs?.Count ?? 0) + && checkColumns; + } + + private bool isNoteAt(HitObject hitObject, double time, int column) => + hitObject is ManiaHitObject maniaHitObject + && maniaHitObject.StartTime == time + && maniaHitObject.Column == column; } } diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs index 753400e10e..943858652c 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOpenEditorTimestampInOsu.cs @@ -15,40 +15,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor { protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); - private void addStepClickLink(string timestamp, string step = "", bool displayTimestamp = true) - { - AddStep(displayTimestamp ? $"{step} {timestamp}" : step, () => Editor.HandleTimestamp(timestamp)); - AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value); - } - - private void addReset() - { - addStepClickLink("00:00:000", "reset", false); - } - - private void checkSelection(Func startTime, params int[] comboNumbers) - => AddUntilStep($"seeked & selected {(comboNumbers.Any() ? string.Join(",", comboNumbers) : "nothing")}", () => - { - bool checkCombos = comboNumbers.Any() - ? hasCombosInOrder(EditorBeatmap.SelectedHitObjects, comboNumbers) - : !EditorBeatmap.SelectedHitObjects.Any(); - - return EditorClock.CurrentTime == startTime() - && EditorBeatmap.SelectedHitObjects.Count == comboNumbers.Length - && checkCombos; - }); - - private bool hasCombosInOrder(IEnumerable selected, params int[] comboNumbers) - { - List hitObjects = selected.ToList(); - if (hitObjects.Count != comboNumbers.Length) - return false; - - return !hitObjects.Select(x => (OsuHitObject)x) - .Where((x, i) => x.IndexInCurrentCombo + 1 != comboNumbers[i]) - .Any(); - } - [Test] public void TestNormalSelection() { @@ -90,5 +56,36 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor addStepClickLink("00:00:956 (956|1,956|2)", "mania link"); checkSelection(() => firstObject.StartTime); } + + private void addReset() => addStepClickLink("00:00:000", "reset", false); + + private void addStepClickLink(string timestamp, string step = "", bool displayTimestamp = true) + { + AddStep(displayTimestamp ? $"{step} {timestamp}" : step, () => Editor.HandleTimestamp(timestamp)); + AddUntilStep("wait for seek", () => EditorClock.SeekingOrStopped.Value); + } + + private void checkSelection(Func startTime, params int[] comboNumbers) + => AddUntilStep($"seeked & selected {(comboNumbers.Any() ? string.Join(",", comboNumbers) : "nothing")}", () => + { + bool checkCombos = comboNumbers.Any() + ? hasCombosInOrder(EditorBeatmap.SelectedHitObjects, comboNumbers) + : !EditorBeatmap.SelectedHitObjects.Any(); + + return EditorClock.CurrentTime == startTime() + && EditorBeatmap.SelectedHitObjects.Count == comboNumbers.Length + && checkCombos; + }); + + private bool hasCombosInOrder(IEnumerable selected, params int[] comboNumbers) + { + List hitObjects = selected.ToList(); + if (hitObjects.Count != comboNumbers.Length) + return false; + + return !hitObjects.Select(x => (OsuHitObject)x) + .Where((x, i) => x.IndexInCurrentCombo + 1 != comboNumbers[i]) + .Any(); + } } } diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index 86ceb73f7a..dacc5887d0 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -25,58 +25,6 @@ namespace osu.Game.Tests.Visual.Editing private EditorBeatmap editorBeatmap => editor.ChildrenOfType().Single(); private EditorClock editorClock => editor.ChildrenOfType().Single(); - private void addStepClickLink(string timestamp, string step = "", bool waitForSeek = true) - { - AddStep($"{step} {timestamp}", () => - Game.HandleLink(new LinkDetails(LinkAction.OpenEditorTimestamp, timestamp)) - ); - - if (waitForSeek) - AddUntilStep("wait for seek", () => editorClock.SeekingOrStopped.Value); - } - - private void addStepScreenModeTo(EditorScreenMode screenMode) - { - AddStep("change screen to " + screenMode, () => editor.Mode.Value = screenMode); - } - - private void assertOnScreenAt(EditorScreenMode screen, double time, string text = "stayed in") - { - AddAssert($"{text} {screen} at {time}", () => - editor.Mode.Value == screen - && editorClock.CurrentTime == time - ); - } - - private void assertMovedScreenTo(EditorScreenMode screen, string text = "moved to") - { - AddAssert($"{text} {screen}", () => editor.Mode.Value == screen); - } - - private void setUpEditor(RulesetInfo ruleset) - { - BeatmapSetInfo beatmapSet = null!; - - AddStep("Import test beatmap", () => - Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely() - ); - AddStep("Retrieve beatmap", () => - beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach() - ); - AddStep("Present beatmap", () => Game.PresentBeatmap(beatmapSet)); - AddUntilStep("Wait for song select", () => - Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) - && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect - && songSelect.IsLoaded - ); - AddStep("Switch ruleset", () => Game.Ruleset.Value = ruleset); - AddStep("Open editor for ruleset", () => - ((PlaySongSelect)Game.ScreenStack.CurrentScreen) - .Edit(beatmapSet.Beatmaps.Last(beatmap => beatmap.Ruleset.Name == ruleset.Name)) - ); - AddUntilStep("Wait for editor open", () => editor.ReadyForUse); - } - [Test] public void TestErrorNotifications() { @@ -151,5 +99,53 @@ namespace osu.Game.Tests.Visual.Editing addStepClickLink("00:00:000"); assertOnScreenAt(EditorScreenMode.Compose, 0); } + + private void addStepClickLink(string timestamp, string step = "", bool waitForSeek = true) + { + AddStep($"{step} {timestamp}", () => + Game.HandleLink(new LinkDetails(LinkAction.OpenEditorTimestamp, timestamp)) + ); + + if (waitForSeek) + AddUntilStep("wait for seek", () => editorClock.SeekingOrStopped.Value); + } + + private void addStepScreenModeTo(EditorScreenMode screenMode) => + AddStep("change screen to " + screenMode, () => editor.Mode.Value = screenMode); + + private void assertOnScreenAt(EditorScreenMode screen, double time) + { + AddAssert($"stayed on {screen} at {time}", () => + editor.Mode.Value == screen + && editorClock.CurrentTime == time + ); + } + + private void assertMovedScreenTo(EditorScreenMode screen, string text = "moved to") => + AddAssert($"{text} {screen}", () => editor.Mode.Value == screen); + + private void setUpEditor(RulesetInfo ruleset) + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("Import test beatmap", () => + Game.BeatmapManager.Import(TestResources.GetTestBeatmapForImport()).WaitSafely() + ); + AddStep("Retrieve beatmap", () => + beatmapSet = Game.BeatmapManager.QueryBeatmapSet(set => !set.Protected).AsNonNull().Value.Detach() + ); + AddStep("Present beatmap", () => Game.PresentBeatmap(beatmapSet)); + AddUntilStep("Wait for song select", () => + Game.Beatmap.Value.BeatmapSetInfo.Equals(beatmapSet) + && Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect + && songSelect.IsLoaded + ); + AddStep("Switch ruleset", () => Game.Ruleset.Value = ruleset); + AddStep("Open editor for ruleset", () => + ((PlaySongSelect)Game.ScreenStack.CurrentScreen) + .Edit(beatmapSet.Beatmaps.Last(beatmap => beatmap.Ruleset.Name == ruleset.Name)) + ); + AddUntilStep("Wait for editor open", () => editor.ReadyForUse); + } } } From 917a68eac3273f969a7b760cd7a258e475fcc23e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 15:08:15 +0900 Subject: [PATCH 0157/1118] Adjust localisablel strings and keys --- .../Visual/Editing/TestSceneOpenEditorTimestamp.cs | 8 ++++---- osu.Game/Localisation/EditorStrings.cs | 6 +++--- osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index dacc5887d0..b6f89ee4e7 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -32,7 +32,7 @@ namespace osu.Game.Tests.Visual.Editing addStepClickLink("00:00:000", waitForSeek: false); AddAssert("received 'must be in edit'", - () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit), + () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEditorToHandleLinks), () => Is.EqualTo(1)); AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Editing addStepClickLink("00:00:000 (1)", waitForSeek: false); AddAssert("received 'must be in edit'", - () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEdit), + () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEditorToHandleLinks), () => Is.EqualTo(2)); setUpEditor(rulesetInfo); @@ -48,12 +48,12 @@ namespace osu.Game.Tests.Visual.Editing addStepClickLink("00:000", "invalid link", waitForSeek: false); AddAssert("received 'failed to process'", - () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp), + () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToParseEditorLink), () => Is.EqualTo(1)); addStepClickLink("50000:00:000", "too long link", waitForSeek: false); AddAssert("received 'failed to process'", - () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToProcessTimestamp), + () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToParseEditorLink), () => Is.EqualTo(2)); } diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index e762455e8b..b20b5bc65a 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -122,12 +122,12 @@ namespace osu.Game.Localisation /// /// "Must be in edit mode to handle editor links" /// - public static LocalisableString MustBeInEdit => new TranslatableString(getKey(@"must_be_in_edit"), @"Must be in edit mode to handle editor links"); + public static LocalisableString MustBeInEditorToHandleLinks => new TranslatableString(getKey(@"must_be_in_editor_to_handle_links"), @"Must be in edit mode to handle editor links"); /// - /// "Failed to process timestamp" + /// "Failed to parse editor link" /// - public static LocalisableString FailedToProcessTimestamp => new TranslatableString(getKey(@"failed_to_process_timestamp"), @"Failed to process timestamp"); + public static LocalisableString FailedToParseEditorLink => new TranslatableString(getKey(@"failed_to_parse_edtior_link"), @"Failed to parse editor link"); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 3fda18b7cb..80a9853965 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -565,7 +565,7 @@ namespace osu.Game Schedule(() => Notifications.Post(new SimpleNotification { Icon = FontAwesome.Solid.ExclamationTriangle, - Text = EditorStrings.MustBeInEdit + Text = EditorStrings.MustBeInEditorToHandleLinks })); return; } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index fb34767315..7dd2666a7e 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1146,7 +1146,7 @@ namespace osu.Game.Screens.Edit Schedule(() => notifications?.Post(new SimpleNotification { Icon = FontAwesome.Solid.ExclamationTriangle, - Text = EditorStrings.FailedToProcessTimestamp + Text = EditorStrings.FailedToParseEditorLink })); return; } From 7c5345bf7e88ba364cd3606e424f4ae45b6760f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 15:09:33 +0900 Subject: [PATCH 0158/1118] Use `SimpleErrorNotification` for error display --- osu.Game/OsuGame.cs | 2 +- osu.Game/Screens/Edit/Editor.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 80a9853965..c4f93636e9 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -562,7 +562,7 @@ namespace osu.Game { if (ScreenStack.CurrentScreen is not Editor editor) { - Schedule(() => Notifications.Post(new SimpleNotification + Schedule(() => Notifications.Post(new SimpleErrorNotification { Icon = FontAwesome.Solid.ExclamationTriangle, Text = EditorStrings.MustBeInEditorToHandleLinks diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index 7dd2666a7e..b3cebb41de 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -1143,7 +1143,7 @@ namespace osu.Game.Screens.Edit { if (!EditorTimestampParser.TryParse(timestamp, out var timeSpan, out string selection)) { - Schedule(() => notifications?.Post(new SimpleNotification + Schedule(() => notifications?.Post(new SimpleErrorNotification { Icon = FontAwesome.Solid.ExclamationTriangle, Text = EditorStrings.FailedToParseEditorLink From 1c612e2e0c49e3d7ca01f562b091f791ea34f7cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 13 Nov 2023 14:35:07 +0900 Subject: [PATCH 0159/1118] Implement client-side disconnection flow --- osu.Game/Online/HubClientConnector.cs | 2 ++ osu.Game/Online/IHubClientConnector.cs | 5 +++++ osu.Game/Online/IStatefulUserHubClient.cs | 18 ++++++++++++++++++ .../Online/Multiplayer/IMultiplayerClient.cs | 2 +- .../Online/Multiplayer/MultiplayerClient.cs | 8 ++++++++ .../Multiplayer/OnlineMultiplayerClient.cs | 9 +++++++++ .../PersistentEndpointClientConnector.cs | 2 ++ osu.Game/Online/Spectator/ISpectatorClient.cs | 2 +- .../Online/Spectator/OnlineSpectatorClient.cs | 9 +++++++++ osu.Game/Online/Spectator/SpectatorClient.cs | 8 ++++++++ .../Multiplayer/TestMultiplayerClient.cs | 6 ++++++ .../Visual/Spectator/TestSpectatorClient.cs | 9 ++++++++- 12 files changed, 77 insertions(+), 3 deletions(-) create mode 100644 osu.Game/Online/IStatefulUserHubClient.cs diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index 8fd79bd703..f5a8678613 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -102,6 +102,8 @@ namespace osu.Game.Online return Task.FromResult((PersistentEndpointClient)new HubClient(newConnection)); } + Task IHubClientConnector.Disconnect() => base.Disconnect(); + protected override string ClientName { get; } } } diff --git a/osu.Game/Online/IHubClientConnector.cs b/osu.Game/Online/IHubClientConnector.cs index 53c4897e73..052972e6b4 100644 --- a/osu.Game/Online/IHubClientConnector.cs +++ b/osu.Game/Online/IHubClientConnector.cs @@ -30,6 +30,11 @@ namespace osu.Game.Online /// public Action? ConfigureConnection { get; set; } + /// + /// Forcefully disconnects the client from the server. + /// + Task Disconnect(); + /// /// Reconnect if already connected. /// diff --git a/osu.Game/Online/IStatefulUserHubClient.cs b/osu.Game/Online/IStatefulUserHubClient.cs new file mode 100644 index 0000000000..86105dd629 --- /dev/null +++ b/osu.Game/Online/IStatefulUserHubClient.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading.Tasks; + +namespace osu.Game.Online +{ + /// + /// Common interface for clients of "stateful user hubs", i.e. server-side hubs + /// that preserve user state. + /// In the case of such hubs, concurrency constraints are enforced (only one client + /// can be connected at a time). + /// + public interface IStatefulUserHubClient + { + Task DisconnectRequested(); + } +} diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index 327fb0d76a..a5fa49a94b 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -13,7 +13,7 @@ namespace osu.Game.Online.Multiplayer /// /// An interface defining a multiplayer client instance. /// - public interface IMultiplayerClient + public interface IMultiplayerClient : IStatefulUserHubClient { /// /// Signals that the room has changed state. diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 515a0dda08..4b351663d8 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -357,6 +357,8 @@ namespace osu.Game.Online.Multiplayer public abstract Task ChangeBeatmapAvailability(BeatmapAvailability newBeatmapAvailability); + public abstract Task DisconnectInternal(); + /// /// Change the local user's mods in the currently joined room. /// @@ -876,5 +878,11 @@ namespace osu.Game.Online.Multiplayer return tcs.Task; } + + Task IStatefulUserHubClient.DisconnectRequested() + { + Schedule(() => DisconnectInternal()); + return Task.CompletedTask; + } } } diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 20ec030eac..e400132693 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -68,6 +68,7 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.PlaylistItemAdded), ((IMultiplayerClient)this).PlaylistItemAdded); connection.On(nameof(IMultiplayerClient.PlaylistItemRemoved), ((IMultiplayerClient)this).PlaylistItemRemoved); connection.On(nameof(IMultiplayerClient.PlaylistItemChanged), ((IMultiplayerClient)this).PlaylistItemChanged); + connection.On(nameof(IStatefulUserHubClient.DisconnectRequested), ((IMultiplayerClient)this).DisconnectRequested); }; IsConnected.BindTo(connector.IsConnected); @@ -255,6 +256,14 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.RemovePlaylistItem), playlistItemId); } + public override Task DisconnectInternal() + { + if (connector == null) + return Task.CompletedTask; + + return connector.Disconnect(); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Online/PersistentEndpointClientConnector.cs b/osu.Game/Online/PersistentEndpointClientConnector.cs index e33924047d..8c1b58a750 100644 --- a/osu.Game/Online/PersistentEndpointClientConnector.cs +++ b/osu.Game/Online/PersistentEndpointClientConnector.cs @@ -159,6 +159,8 @@ namespace osu.Game.Online await Task.Run(connect, default).ConfigureAwait(false); } + protected Task Disconnect() => disconnect(true); + private async Task disconnect(bool takeLock) { cancelExistingConnect(); diff --git a/osu.Game/Online/Spectator/ISpectatorClient.cs b/osu.Game/Online/Spectator/ISpectatorClient.cs index 9605604966..2dc2283c23 100644 --- a/osu.Game/Online/Spectator/ISpectatorClient.cs +++ b/osu.Game/Online/Spectator/ISpectatorClient.cs @@ -8,7 +8,7 @@ namespace osu.Game.Online.Spectator /// /// An interface defining a spectator client instance. /// - public interface ISpectatorClient + public interface ISpectatorClient : IStatefulUserHubClient { /// /// Signals that a user has begun a new play session. diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs index 3118e05053..cd68abdea6 100644 --- a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs +++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs @@ -42,6 +42,7 @@ namespace osu.Game.Online.Spectator connection.On(nameof(ISpectatorClient.UserSentFrames), ((ISpectatorClient)this).UserSentFrames); connection.On(nameof(ISpectatorClient.UserFinishedPlaying), ((ISpectatorClient)this).UserFinishedPlaying); connection.On(nameof(ISpectatorClient.UserScoreProcessed), ((ISpectatorClient)this).UserScoreProcessed); + connection.On(nameof(IStatefulUserHubClient.DisconnectRequested), ((IStatefulUserHubClient)this).DisconnectRequested); }; IsConnected.BindTo(connector.IsConnected); @@ -113,5 +114,13 @@ namespace osu.Game.Online.Spectator return connection.InvokeAsync(nameof(ISpectatorServer.EndWatchingUser), userId); } + + protected override Task DisconnectInternal() + { + if (connector == null) + return Task.CompletedTask; + + return connector.Disconnect(); + } } } diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 14e137caf1..9c78f27e15 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -174,6 +174,12 @@ namespace osu.Game.Online.Spectator return Task.CompletedTask; } + Task IStatefulUserHubClient.DisconnectRequested() + { + Schedule(() => DisconnectInternal()); + return Task.CompletedTask; + } + public void BeginPlaying(long? scoreToken, GameplayState state, Score score) { // This schedule is only here to match the one below in `EndPlaying`. @@ -291,6 +297,8 @@ namespace osu.Game.Online.Spectator protected abstract Task StopWatchingUserInternal(int userId); + protected abstract Task DisconnectInternal(); + protected override void Update() { base.Update(); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 6007c7c076..577104db45 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -658,5 +658,11 @@ namespace osu.Game.Tests.Visual.Multiplayer PlayedAt = item.PlayedAt, StarRating = item.Beatmap.StarRating, }; + + public override Task DisconnectInternal() + { + isConnected.Value = false; + return Task.CompletedTask; + } } } diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs index 5db08810ca..ce2eee8aa4 100644 --- a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs +++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs @@ -33,7 +33,8 @@ namespace osu.Game.Tests.Visual.Spectator public int FrameSendAttempts { get; private set; } - public override IBindable IsConnected { get; } = new Bindable(true); + public override IBindable IsConnected => isConnected; + private readonly BindableBool isConnected = new BindableBool(true); public IReadOnlyDictionary LastReceivedUserFrames => lastReceivedUserFrames; @@ -179,5 +180,11 @@ namespace osu.Game.Tests.Visual.Spectator State = SpectatedUserState.Playing }); } + + protected override Task DisconnectInternal() + { + isConnected.Value = false; + return Task.CompletedTask; + } } } From 2391035e4969ac3d1a6a505cd5b73ef0e71fc7db Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 21 Nov 2023 14:33:18 +0900 Subject: [PATCH 0160/1118] Remove redundant `api` field from `HubClientConnector` --- osu.Game/Online/HubClientConnector.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index f5a8678613..e7494e50cc 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -27,7 +27,6 @@ namespace osu.Game.Online private readonly string endpoint; private readonly string versionHash; private readonly bool preferMessagePack; - private readonly IAPIProvider api; /// /// The current connection opened by this connector. @@ -47,7 +46,6 @@ namespace osu.Game.Online { ClientName = clientName; this.endpoint = endpoint; - this.api = api; this.versionHash = versionHash; this.preferMessagePack = preferMessagePack; @@ -70,7 +68,7 @@ namespace osu.Game.Online options.Proxy.Credentials = CredentialCache.DefaultCredentials; } - options.Headers.Add("Authorization", $"Bearer {api.AccessToken}"); + options.Headers.Add("Authorization", $"Bearer {API.AccessToken}"); options.Headers.Add("OsuVersionHash", versionHash); }); From 42fada578e35418b23d17d5607e54d9e79ca0b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 21 Nov 2023 14:39:33 +0900 Subject: [PATCH 0161/1118] Centralise and improve messaging around online state When the server requests a disconnect due to a user connecting via a second device, the client will now log the user out on the first device and show a notification informing them of the cause of disconnection. --- osu.Game/Online/HubClientConnector.cs | 6 +- .../Online/Multiplayer/MultiplayerClient.cs | 15 ++- osu.Game/Online/OnlineStatusNotifier.cs | 120 ++++++++++++++++++ .../Online/Spectator/OnlineSpectatorClient.cs | 10 +- osu.Game/Online/Spectator/SpectatorClient.cs | 11 +- osu.Game/OsuGame.cs | 1 + .../Screens/OnlinePlay/OnlinePlayScreen.cs | 5 +- .../Visual/Spectator/TestSpectatorClient.cs | 4 +- 8 files changed, 155 insertions(+), 17 deletions(-) create mode 100644 osu.Game/Online/OnlineStatusNotifier.cs diff --git a/osu.Game/Online/HubClientConnector.cs b/osu.Game/Online/HubClientConnector.cs index e7494e50cc..9d414deade 100644 --- a/osu.Game/Online/HubClientConnector.cs +++ b/osu.Game/Online/HubClientConnector.cs @@ -100,7 +100,11 @@ namespace osu.Game.Online return Task.FromResult((PersistentEndpointClient)new HubClient(newConnection)); } - Task IHubClientConnector.Disconnect() => base.Disconnect(); + async Task IHubClientConnector.Disconnect() + { + await Disconnect().ConfigureAwait(false); + API.Logout(); + } protected override string ClientName { get; } } diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 4b351663d8..79f46c2095 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -12,7 +12,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Development; using osu.Framework.Graphics; -using osu.Framework.Logging; using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; @@ -88,6 +87,11 @@ namespace osu.Game.Online.Multiplayer /// public event Action? ResultsReady; + /// + /// Invoked just prior to disconnection requested by the server via . + /// + public event Action? Disconnecting; + /// /// Whether the is currently connected. /// This is NOT thread safe and usage should be scheduled. @@ -155,10 +159,7 @@ namespace osu.Game.Online.Multiplayer { // clean up local room state on server disconnect. if (!connected.NewValue && Room != null) - { - Logger.Log("Clearing room due to multiplayer server connection loss.", LoggingTarget.Runtime, LogLevel.Important); LeaveRoom(); - } })); } @@ -881,7 +882,11 @@ namespace osu.Game.Online.Multiplayer Task IStatefulUserHubClient.DisconnectRequested() { - Schedule(() => DisconnectInternal()); + Schedule(() => + { + Disconnecting?.Invoke(); + DisconnectInternal(); + }); return Task.CompletedTask; } } diff --git a/osu.Game/Online/OnlineStatusNotifier.cs b/osu.Game/Online/OnlineStatusNotifier.cs new file mode 100644 index 0000000000..0cf672ac3c --- /dev/null +++ b/osu.Game/Online/OnlineStatusNotifier.cs @@ -0,0 +1,120 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Screens; +using osu.Game.Online.API; +using osu.Game.Online.Multiplayer; +using osu.Game.Online.Spectator; +using osu.Game.Overlays; +using osu.Game.Overlays.Notifications; +using osu.Game.Screens.OnlinePlay; + +namespace osu.Game.Online +{ + public partial class OnlineStatusNotifier : Component + { + private readonly Func getCurrentScreen; + + [Resolved] + private MultiplayerClient multiplayerClient { get; set; } = null!; + + [Resolved] + private SpectatorClient spectatorClient { get; set; } = null!; + + [Resolved] + private INotificationOverlay? notificationOverlay { get; set; } + + private IBindable apiState = null!; + private IBindable multiplayerState = null!; + private IBindable spectatorState = null!; + private bool forcedDisconnection; + + public OnlineStatusNotifier(Func getCurrentScreen) + { + this.getCurrentScreen = getCurrentScreen; + } + + [BackgroundDependencyLoader] + private void load(IAPIProvider api) + { + apiState = api.State.GetBoundCopy(); + multiplayerState = multiplayerClient.IsConnected.GetBoundCopy(); + spectatorState = spectatorClient.IsConnected.GetBoundCopy(); + + multiplayerClient.Disconnecting += notifyAboutForcedDisconnection; + spectatorClient.Disconnecting += notifyAboutForcedDisconnection; + } + + private void notifyAboutForcedDisconnection() + { + if (forcedDisconnection) + return; + + forcedDisconnection = true; + notificationOverlay?.Post(new SimpleErrorNotification + { + Icon = FontAwesome.Solid.ExclamationCircle, + Text = "You have been logged out on this device due to a login to your account on another device." + }); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + apiState.BindValueChanged(_ => + { + if (apiState.Value == APIState.Online) + forcedDisconnection = false; + + Scheduler.AddOnce(updateState); + }); + multiplayerState.BindValueChanged(_ => Scheduler.AddOnce(updateState)); + spectatorState.BindValueChanged(_ => Scheduler.AddOnce(updateState)); + } + + private void updateState() + { + if (forcedDisconnection) + return; + + if (apiState.Value == APIState.Offline && getCurrentScreen() is OnlinePlayScreen) + { + notificationOverlay?.Post(new SimpleErrorNotification + { + Icon = FontAwesome.Solid.ExclamationCircle, + Text = "API connection was lost. Can't continue with online play." + }); + return; + } + + if (!multiplayerClient.IsConnected.Value && multiplayerClient.Room != null) + { + notificationOverlay?.Post(new SimpleErrorNotification + { + Icon = FontAwesome.Solid.ExclamationCircle, + Text = "Connection to the multiplayer server was lost. Exiting multiplayer." + }); + } + + // TODO: handle spectator server failure somehow? + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (spectatorClient.IsNotNull()) + spectatorClient.Disconnecting -= notifyAboutForcedDisconnection; + + if (multiplayerClient.IsNotNull()) + multiplayerClient.Disconnecting -= notifyAboutForcedDisconnection; + } + } +} diff --git a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs index cd68abdea6..036cfa1d76 100644 --- a/osu.Game/Online/Spectator/OnlineSpectatorClient.cs +++ b/osu.Game/Online/Spectator/OnlineSpectatorClient.cs @@ -115,12 +115,14 @@ namespace osu.Game.Online.Spectator return connection.InvokeAsync(nameof(ISpectatorServer.EndWatchingUser), userId); } - protected override Task DisconnectInternal() + protected override async Task DisconnectInternal() { - if (connector == null) - return Task.CompletedTask; + await base.DisconnectInternal().ConfigureAwait(false); - return connector.Disconnect(); + if (connector == null) + return; + + await connector.Disconnect().ConfigureAwait(false); } } } diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 9c78f27e15..ca4ec52f4a 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -70,6 +70,11 @@ namespace osu.Game.Online.Spectator /// public virtual event Action? OnUserScoreProcessed; + /// + /// Invoked just prior to disconnection requested by the server via . + /// + public event Action? Disconnecting; + /// /// A dictionary containing all users currently being watched, with the number of watching components for each user. /// @@ -297,7 +302,11 @@ namespace osu.Game.Online.Spectator protected abstract Task StopWatchingUserInternal(int userId); - protected abstract Task DisconnectInternal(); + protected virtual Task DisconnectInternal() + { + Disconnecting?.Invoke(); + return Task.CompletedTask; + } protected override void Update() { diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 2f11964f6a..ed4bd21e93 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1054,6 +1054,7 @@ namespace osu.Game Add(difficultyRecommender); Add(externalLinkOpener = new ExternalLinkOpener()); Add(new MusicKeyBindingHandler()); + Add(new OnlineStatusNotifier(() => ScreenStack.CurrentScreen)); // side overlays which cancel each other. var singleDisplaySideOverlays = new OverlayContainer[] { Settings, Notifications, FirstRunOverlay }; diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs index f652e88f5a..9de458b5c6 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlayScreen.cs @@ -71,7 +71,7 @@ namespace osu.Game.Screens.OnlinePlay screenStack = new OnlinePlaySubScreenStack { RelativeSizeAxes = Axes.Both }, new Header(ScreenTitle, screenStack), RoomManager, - ongoingOperationTracker + ongoingOperationTracker, } }; } @@ -79,10 +79,7 @@ namespace osu.Game.Screens.OnlinePlay private void onlineStateChanged(ValueChangedEvent state) => Schedule(() => { if (state.NewValue != APIState.Online) - { - Logger.Log("API connection was lost, can't continue with online play", LoggingTarget.Network, LogLevel.Important); Schedule(forcefullyExit); - } }); protected override void LoadComplete() diff --git a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs index ce2eee8aa4..5aef85fa13 100644 --- a/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs +++ b/osu.Game/Tests/Visual/Spectator/TestSpectatorClient.cs @@ -181,10 +181,10 @@ namespace osu.Game.Tests.Visual.Spectator }); } - protected override Task DisconnectInternal() + protected override async Task DisconnectInternal() { + await base.DisconnectInternal().ConfigureAwait(false); isConnected.Value = false; - return Task.CompletedTask; } } } From aa3ff151c095ec76a15d4fc8d5b6d021d2ff52e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 21 Nov 2023 14:41:03 +0900 Subject: [PATCH 0162/1118] Fix `RoomManager` attempting to part room when not online --- osu.Game/Screens/OnlinePlay/Components/RoomManager.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs index 539d5b74b3..e892f9280f 100644 --- a/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs +++ b/osu.Game/Screens/OnlinePlay/Components/RoomManager.cs @@ -98,7 +98,9 @@ namespace osu.Game.Screens.OnlinePlay.Components if (JoinedRoom.Value == null) return; - api.Queue(new PartRoomRequest(joinedRoom.Value)); + if (api.State.Value == APIState.Online) + api.Queue(new PartRoomRequest(joinedRoom.Value)); + joinedRoom.Value = null; } From 314a7bf6f1058195ecf5995fc18a5e5bc1eb94fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 11:33:05 +0900 Subject: [PATCH 0163/1118] Simplify `AddOnce` call to avoid `self` argument --- .../Sliders/SliderPlacementBlueprint.cs | 41 +++++++++---------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 5f072eb171..32fb7ad351 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -3,10 +3,12 @@ #nullable disable +using System.Collections.Generic; using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; @@ -84,16 +86,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { freehandToolboxGroup.Tolerance.BindValueChanged(e => { - if (bSplineBuilder.Tolerance != e.NewValue) - bSplineBuilder.Tolerance = e.NewValue; - updateSliderPathFromBSplineBuilder(); + bSplineBuilder.Tolerance = e.NewValue; + Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); }, true); freehandToolboxGroup.CornerThreshold.BindValueChanged(e => { - if (bSplineBuilder.CornerThreshold != e.NewValue) - bSplineBuilder.CornerThreshold = e.NewValue; - updateSliderPathFromBSplineBuilder(); + bSplineBuilder.CornerThreshold = e.NewValue; + Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); }, true); } } @@ -197,27 +197,24 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.OnDrag(e); bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); - updateSliderPathFromBSplineBuilder(); + Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); } private void updateSliderPathFromBSplineBuilder() { - Scheduler.AddOnce(static self => - { - var cps = self.bSplineBuilder.ControlPoints; - var sliderCps = self.HitObject.Path.ControlPoints; - sliderCps.RemoveRange(1, sliderCps.Count - 1); + IReadOnlyList builderPoints = bSplineBuilder.ControlPoints; + BindableList sliderPoints = HitObject.Path.ControlPoints; - // Add the control points from the BSpline builder while converting control points that repeat - // three or more times to a single PathControlPoint with linear type. - for (int i = 1; i < cps.Count; i++) - { - bool isSharp = i < cps.Count - 2 && cps[i] == cps[i + 1] && cps[i] == cps[i + 2]; - sliderCps.Add(new PathControlPoint(cps[i], isSharp ? PathType.BSpline(3) : null)); - if (isSharp) - i += 2; - } - }, this); + sliderPoints.RemoveRange(1, sliderPoints.Count - 1); + + // Add the control points from the BSpline builder while converting control points that repeat + // three or more times to a single PathControlPoint with linear type. + for (int i = 1; i < builderPoints.Count; i++) + { + bool isSharp = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; + sliderPoints.Add(new PathControlPoint(builderPoints[i], isSharp ? PathType.BSpline(3) : null)); + if (isSharp) i += 2; + } } protected override void OnDragEnd(DragEndEvent e) From 0a5444d091de73c2cc9fe8360146248d1b06b39e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 13:05:31 +0900 Subject: [PATCH 0164/1118] Fix using the incorrect position for the first point --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 32fb7ad351..68f5b2d70f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -180,7 +180,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return false; bSplineBuilder.Clear(); - bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); + bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); setState(SliderPlacementState.Drawing); return true; } From e69e78ad4123a4d89915aa44286a8e637e49429d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 13:10:44 +0900 Subject: [PATCH 0165/1118] Refactor b-spline path conversion code to better handle linear segments --- .../Sliders/SliderPlacementBlueprint.cs | 44 +++++++++++++++---- 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 68f5b2d70f..6e9cc26af4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -8,7 +8,6 @@ using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; @@ -203,17 +202,44 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSliderPathFromBSplineBuilder() { IReadOnlyList builderPoints = bSplineBuilder.ControlPoints; - BindableList sliderPoints = HitObject.Path.ControlPoints; - sliderPoints.RemoveRange(1, sliderPoints.Count - 1); + if (builderPoints.Count == 0) + return; - // Add the control points from the BSpline builder while converting control points that repeat - // three or more times to a single PathControlPoint with linear type. - for (int i = 1; i < builderPoints.Count; i++) + int lastSegmentStart = 0; + PathType? lastPathType = null; + + HitObject.Path.ControlPoints.Clear(); + + // Iterate through generated points, finding each segment and adding non-inheriting path types where appropriate. + // Importantly, the B-Spline builder returns three Vector2s at the same location when a new segment is to be started. + for (int i = 0; i < builderPoints.Count; i++) { - bool isSharp = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; - sliderPoints.Add(new PathControlPoint(builderPoints[i], isSharp ? PathType.BSpline(3) : null)); - if (isSharp) i += 2; + bool isLastPoint = i == builderPoints.Count - 1; + bool isNewSegment = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; + + if (isNewSegment || isLastPoint) + { + int pointsInSegment = i - lastSegmentStart; + + // Where possible, we can use the simpler LINEAR path type. + PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3); + + // Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined. + if (lastPathType == pathType && lastPathType == PathType.LINEAR) + pathType = null; + + HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[lastSegmentStart], pathType)); + for (int j = lastSegmentStart + 1; j < i; j++) + HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[j])); + + if (isLastPoint) + HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[i])); + + // Skip the redundant duplicated points (see isNewSegment above) which have been coalesced into a path type. + lastSegmentStart = (i += 2); + if (pathType != null) lastPathType = pathType; + } } } From 5175464c18dafc3931b723ae06dbb4375c7c0a77 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 13:35:04 +0900 Subject: [PATCH 0166/1118] Update test coverage (and add test coverage of curve drawing) --- .../TestSceneSliderPlacementBlueprint.cs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index c7a21ba689..8498263138 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using NUnit.Framework; using osu.Framework.Utils; using osu.Game.Rulesets.Edit; @@ -274,7 +275,30 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor } [Test] - public void TestSliderDrawing() + public void TestSliderDrawingCurve() + { + Vector2 startPoint = new Vector2(200); + + addMovementStep(startPoint); + AddStep("press left button", () => InputManager.PressButton(MouseButton.Left)); + + for (int i = 0; i < 20; i++) + addMovementStep(startPoint + new Vector2(i * 40, MathF.Sin(i * MathF.PI / 5) * 50)); + + AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left)); + + assertPlaced(true); + assertLength(760, tolerance: 10); + assertControlPointCount(5); + assertControlPointType(0, PathType.BSpline(3)); + assertControlPointType(1, null); + assertControlPointType(2, null); + assertControlPointType(3, null); + assertControlPointType(4, null); + } + + [Test] + public void TestSliderDrawingLinear() { addMovementStep(new Vector2(200)); AddStep("press left button", () => InputManager.PressButton(MouseButton.Left)); @@ -291,7 +315,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertLength(600, tolerance: 10); assertControlPointCount(4); - assertControlPointType(0, PathType.BSpline(3)); + assertControlPointType(0, PathType.LINEAR); + assertControlPointType(1, null); + assertControlPointType(2, null); + assertControlPointType(3, null); } [Test] @@ -401,11 +428,11 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private void assertPlaced(bool expected) => AddAssert($"slider {(expected ? "placed" : "not placed")}", () => (getSlider() != null) == expected); - private void assertLength(double expected, double tolerance = 1) => AddAssert($"slider length is {expected}±{tolerance}", () => Precision.AlmostEquals(expected, getSlider().Distance, tolerance)); + private void assertLength(double expected, double tolerance = 1) => AddAssert($"slider length is {expected}±{tolerance}", () => getSlider().Distance, () => Is.EqualTo(expected).Within(tolerance)); - private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count == expected); + private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count, () => Is.EqualTo(expected)); - private void assertControlPointType(int index, PathType type) => AddAssert($"control point {index} is {type}", () => getSlider().Path.ControlPoints[index].Type == type); + private void assertControlPointType(int index, PathType? type) => AddAssert($"control point {index} is {type?.ToString() ?? "inherit"}", () => getSlider().Path.ControlPoints[index].Type, () => Is.EqualTo(type)); private void assertControlPointPosition(int index, Vector2 position) => AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position, 1)); From 7bedbe42642384a20b681220b507fcc737325c1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 13:36:06 +0900 Subject: [PATCH 0167/1118] Apply NRT to `SliderPlacementBlueprint` tests --- .../Editor/TestSceneSliderPlacementBlueprint.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 8498263138..75778b3a7e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using NUnit.Framework; using osu.Framework.Utils; @@ -428,16 +426,16 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor private void assertPlaced(bool expected) => AddAssert($"slider {(expected ? "placed" : "not placed")}", () => (getSlider() != null) == expected); - private void assertLength(double expected, double tolerance = 1) => AddAssert($"slider length is {expected}±{tolerance}", () => getSlider().Distance, () => Is.EqualTo(expected).Within(tolerance)); + private void assertLength(double expected, double tolerance = 1) => AddAssert($"slider length is {expected}±{tolerance}", () => getSlider()!.Distance, () => Is.EqualTo(expected).Within(tolerance)); - private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider().Path.ControlPoints.Count, () => Is.EqualTo(expected)); + private void assertControlPointCount(int expected) => AddAssert($"has {expected} control points", () => getSlider()!.Path.ControlPoints.Count, () => Is.EqualTo(expected)); - private void assertControlPointType(int index, PathType? type) => AddAssert($"control point {index} is {type?.ToString() ?? "inherit"}", () => getSlider().Path.ControlPoints[index].Type, () => Is.EqualTo(type)); + private void assertControlPointType(int index, PathType? type) => AddAssert($"control point {index} is {type?.ToString() ?? "inherit"}", () => getSlider()!.Path.ControlPoints[index].Type, () => Is.EqualTo(type)); private void assertControlPointPosition(int index, Vector2 position) => - AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider().Path.ControlPoints[index].Position, 1)); + AddAssert($"control point {index} at {position}", () => Precision.AlmostEquals(position, getSlider()!.Path.ControlPoints[index].Position, 1)); - private Slider getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null; + private Slider? getSlider() => HitObjectContainer.Count > 0 ? ((DrawableSlider)HitObjectContainer[0]).HitObject : null; protected override DrawableHitObject CreateHitObject(HitObject hitObject) => new DrawableSlider((Slider)hitObject); protected override PlacementBlueprint CreateBlueprint() => new SliderPlacementBlueprint(); From 9c3f9db31838295cccc0ea4f771933dbb4fa2dc7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:02:08 +0900 Subject: [PATCH 0168/1118] Add failing test coverage of BSpline encoding parse failure --- .../Formats/LegacyBeatmapEncoderTest.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs index 0dd277dc89..e847b61fbe 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapEncoderTest.cs @@ -113,6 +113,33 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.IsTrue(areComboColoursEqual(expected.skin.Configuration, actual.skin.Configuration)); } + [Test] + public void TestEncodeBSplineCurveType() + { + var beatmap = new Beatmap + { + HitObjects = + { + new Slider + { + Path = new SliderPath(new[] + { + new PathControlPoint(Vector2.Zero, PathType.BSpline(3)), + new PathControlPoint(new Vector2(50)), + new PathControlPoint(new Vector2(100), PathType.BSpline(3)), + new PathControlPoint(new Vector2(150)) + }) + }, + } + }; + + var decodedAfterEncode = decodeFromLegacy(encodeToLegacy((beatmap, new TestLegacySkin(beatmaps_resource_store, string.Empty))), string.Empty); + var decodedSlider = (Slider)decodedAfterEncode.beatmap.HitObjects[0]; + Assert.That(decodedSlider.Path.ControlPoints.Count, Is.EqualTo(4)); + Assert.That(decodedSlider.Path.ControlPoints[0].Type, Is.EqualTo(PathType.BSpline(3))); + Assert.That(decodedSlider.Path.ControlPoints[2].Type, Is.EqualTo(PathType.BSpline(3))); + } + [Test] public void TestEncodeMultiSegmentSliderWithFloatingPointError() { From 3d094f84add9f669b2bf0e1bd41bd83da4fd01fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:02:15 +0900 Subject: [PATCH 0169/1118] Fix incorrect parsing of BSpline curve types --- osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs index 411a9b0d63..f9e32fe26f 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObjectParser.cs @@ -273,8 +273,8 @@ namespace osu.Game.Rulesets.Objects.Legacy while (++endIndex < pointSplit.Length) { - // Keep incrementing endIndex while it's not the start of a new segment (indicated by having a type descriptor of length 1). - if (pointSplit[endIndex].Length > 1) + // Keep incrementing endIndex while it's not the start of a new segment (indicated by having an alpha character at position 0). + if (!char.IsLetter(pointSplit[endIndex][0])) continue; // Multi-segmented sliders DON'T contain the end point as part of the current segment as it's assumed to be the start of the next segment. From ba6fbbe43c122413c4b4b81eb9450e77f3361f8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:03:45 +0900 Subject: [PATCH 0170/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 8175869405..aa993485f3 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index a5425ba4c7..b43cb1b3f1 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 92728ea56460dfe0c2135d9165ee8d35e49fe8ad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:10:48 +0900 Subject: [PATCH 0171/1118] Simplify toolbox initialisation --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 061f72d72f..65acdb61ae 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -97,12 +97,12 @@ namespace osu.Game.Rulesets.Osu.Edit // we may be entering the screen with a selection already active updateDistanceSnapGrid(); - RightToolbox.Add(new TransformToolboxGroup - { - RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, - }); - - RightToolbox.Add(FreehandlSliderToolboxGroup); + RightToolbox.AddRange(new EditorToolboxGroup[] + { + new TransformToolboxGroup { RotationHandler = BlueprintContainer.SelectionHandler.RotationHandler, }, + FreehandlSliderToolboxGroup + } + ); } protected override ComposeBlueprintContainer CreateBlueprintContainer() From cf6f66b84f729a71e04a34b7d9acf9091dfbd7cb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:37:06 +0900 Subject: [PATCH 0172/1118] Remove redundant `Clear()` call --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 6e9cc26af4..32397330c6 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -178,7 +178,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (lastCp != cursor && HitObject.Path.ControlPoints.Count == 2) return false; - bSplineBuilder.Clear(); bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); setState(SliderPlacementState.Drawing); return true; From 016de7be6a9c0a5709e966b6f2bb45349d8f38e8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:51:09 +0900 Subject: [PATCH 0173/1118] Simplify drag handling code in `SliderPlacementBlueprint` --- .../Sliders/SliderPlacementBlueprint.cs | 47 +++++++------------ 1 file changed, 17 insertions(+), 30 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 32397330c6..bb4558171f 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders controlPointVisualiser = new PathControlPointVisualiser(HitObject, false) }; - setState(SliderPlacementState.Initial); + state = SliderPlacementState.Initial; } protected override void LoadComplete() @@ -164,38 +164,30 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders protected override bool OnDragStart(DragStartEvent e) { - if (e.Button == MouseButton.Left) - { - switch (state) - { - case SliderPlacementState.Initial: - return true; + if (e.Button != MouseButton.Left) + return base.OnDragStart(e); - case SliderPlacementState.ControlPoints: - if (HitObject.Path.ControlPoints.Count < 3) - { - var lastCp = HitObject.Path.ControlPoints.LastOrDefault(); - if (lastCp != cursor && HitObject.Path.ControlPoints.Count == 2) - return false; + if (state != SliderPlacementState.ControlPoints) + return base.OnDragStart(e); - bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); - setState(SliderPlacementState.Drawing); - return true; - } + // Only enter drawing mode if no additional control points have been placed. + if (HitObject.Path.ControlPoints.Count > 2) + return base.OnDragStart(e); - return false; - } - } - - return base.OnDragStart(e); + bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); + state = SliderPlacementState.Drawing; + return true; } protected override void OnDrag(DragEvent e) { base.OnDrag(e); - bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); - Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); + if (state == SliderPlacementState.Drawing) + { + bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMousePosition) - HitObject.Position); + Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); + } } private void updateSliderPathFromBSplineBuilder() @@ -260,7 +252,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void beginCurve() { BeginPlacement(commitStart: true); - setState(SliderPlacementState.ControlPoints); + state = SliderPlacementState.ControlPoints; } private void endCurve() @@ -357,11 +349,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders tailCirclePiece.UpdateFrom(HitObject.TailCircle); } - private void setState(SliderPlacementState newState) - { - state = newState; - } - private enum SliderPlacementState { Initial, From a210469956ca340bb302f76679c49142e2e0ae69 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 14:52:21 +0900 Subject: [PATCH 0174/1118] Reorder methods --- .../Sliders/SliderPlacementBlueprint.cs | 106 +++++++++--------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index bb4558171f..397d6ffb91 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -190,50 +190,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders } } - private void updateSliderPathFromBSplineBuilder() - { - IReadOnlyList builderPoints = bSplineBuilder.ControlPoints; - - if (builderPoints.Count == 0) - return; - - int lastSegmentStart = 0; - PathType? lastPathType = null; - - HitObject.Path.ControlPoints.Clear(); - - // Iterate through generated points, finding each segment and adding non-inheriting path types where appropriate. - // Importantly, the B-Spline builder returns three Vector2s at the same location when a new segment is to be started. - for (int i = 0; i < builderPoints.Count; i++) - { - bool isLastPoint = i == builderPoints.Count - 1; - bool isNewSegment = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; - - if (isNewSegment || isLastPoint) - { - int pointsInSegment = i - lastSegmentStart; - - // Where possible, we can use the simpler LINEAR path type. - PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3); - - // Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined. - if (lastPathType == pathType && lastPathType == PathType.LINEAR) - pathType = null; - - HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[lastSegmentStart], pathType)); - for (int j = lastSegmentStart + 1; j < i; j++) - HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[j])); - - if (isLastPoint) - HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[i])); - - // Skip the redundant duplicated points (see isNewSegment above) which have been coalesced into a path type. - lastSegmentStart = (i += 2); - if (pathType != null) lastPathType = pathType; - } - } - } - protected override void OnDragEnd(DragEndEvent e) { base.OnDragEnd(e); @@ -249,6 +205,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.OnMouseUp(e); } + protected override void Update() + { + base.Update(); + updateSlider(); + + // Maintain the path type in case it got defaulted to bezier at some point during the drag. + updatePathType(); + } + private void beginCurve() { BeginPlacement(commitStart: true); @@ -261,15 +226,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders EndPlacement(true); } - protected override void Update() - { - base.Update(); - updateSlider(); - - // Maintain the path type in case it got defaulted to bezier at some point during the drag. - updatePathType(); - } - private void updatePathType() { if (state == SliderPlacementState.Drawing) @@ -349,6 +305,50 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders tailCirclePiece.UpdateFrom(HitObject.TailCircle); } + private void updateSliderPathFromBSplineBuilder() + { + IReadOnlyList builderPoints = bSplineBuilder.ControlPoints; + + if (builderPoints.Count == 0) + return; + + int lastSegmentStart = 0; + PathType? lastPathType = null; + + HitObject.Path.ControlPoints.Clear(); + + // Iterate through generated points, finding each segment and adding non-inheriting path types where appropriate. + // Importantly, the B-Spline builder returns three Vector2s at the same location when a new segment is to be started. + for (int i = 0; i < builderPoints.Count; i++) + { + bool isLastPoint = i == builderPoints.Count - 1; + bool isNewSegment = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; + + if (isNewSegment || isLastPoint) + { + int pointsInSegment = i - lastSegmentStart; + + // Where possible, we can use the simpler LINEAR path type. + PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3); + + // Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined. + if (lastPathType == pathType && lastPathType == PathType.LINEAR) + pathType = null; + + HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[lastSegmentStart], pathType)); + for (int j = lastSegmentStart + 1; j < i; j++) + HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[j])); + + if (isLastPoint) + HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[i])); + + // Skip the redundant duplicated points (see isNewSegment above) which have been coalesced into a path type. + lastSegmentStart = (i += 2); + if (pathType != null) lastPathType = pathType; + } + } + } + private enum SliderPlacementState { Initial, From 1660eb3c15b1075e93a64a2938e8e86451f5df84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 15:31:24 +0900 Subject: [PATCH 0175/1118] Add failing test coverage of drag after point placement --- .../TestSceneSliderPlacementBlueprint.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 75778b3a7e..7ac34bc6c8 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -272,6 +272,30 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertControlPointType(2, PathType.PERFECT_CURVE); } + [Test] + public void TestSliderDrawingDoesntActivateAfterNormalPlacement() + { + Vector2 startPoint = new Vector2(200); + + addMovementStep(startPoint); + addClickStep(MouseButton.Left); + + for (int i = 0; i < 20; i++) + { + if (i == 5) + AddStep("press left button", () => InputManager.PressButton(MouseButton.Left)); + addMovementStep(startPoint + new Vector2(i * 40, MathF.Sin(i * MathF.PI / 5) * 50)); + } + + AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left)); + assertPlaced(false); + + addClickStep(MouseButton.Right); + assertPlaced(true); + + assertControlPointType(0, PathType.BEZIER); + } + [Test] public void TestSliderDrawingCurve() { From cc33e121258adb0d607fba5c6bf9897e1979eac5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 15:16:40 +0900 Subject: [PATCH 0176/1118] Fix dragging after one point already placed incorrectly entering drawing mode --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 397d6ffb91..63370350ed 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -171,7 +171,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders return base.OnDragStart(e); // Only enter drawing mode if no additional control points have been placed. - if (HitObject.Path.ControlPoints.Count > 2) + int controlPointCount = HitObject.Path.ControlPoints.Count; + if (controlPointCount > 2 || (controlPointCount == 2 && HitObject.Path.ControlPoints.Last() != cursor)) return base.OnDragStart(e); bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); From 4b2d8aa6a647b28c13f63dc3047cca04b8b8c398 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 15:31:39 +0900 Subject: [PATCH 0177/1118] Add `ToString` on `PathType` for better test output --- osu.Game/Rulesets/Objects/Types/PathType.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Types/PathType.cs b/osu.Game/Rulesets/Objects/Types/PathType.cs index f84d43e3e7..23f1ccf0bc 100644 --- a/osu.Game/Rulesets/Objects/Types/PathType.cs +++ b/osu.Game/Rulesets/Objects/Types/PathType.cs @@ -81,5 +81,7 @@ namespace osu.Game.Rulesets.Objects.Types public static bool operator ==(PathType a, PathType b) => a.Equals(b); public static bool operator !=(PathType a, PathType b) => !a.Equals(b); + + public override string ToString() => Description; } } From ed3874682365596f96fac6c8622821786b3645a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 21 Nov 2023 16:14:30 +0900 Subject: [PATCH 0178/1118] Fix spacer appearing on top of menu --- .../Sliders/Components/PathControlPointVisualiser.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 87e837cc71..3a80d04ab8 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -367,9 +367,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components List curveTypeItems = new List(); if (!selectedPieces.Contains(Pieces[0])) + { curveTypeItems.Add(createMenuItemForPathType(null)); - - curveTypeItems.Add(new OsuMenuItemSpacer()); + curveTypeItems.Add(new OsuMenuItemSpacer()); + } // todo: hide/disable items which aren't valid for selected points curveTypeItems.Add(createMenuItemForPathType(PathType.Linear)); From 85bddab52bb0cdc0419ebe3aea776bc4650d0c7a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 21 Nov 2023 16:52:52 +0900 Subject: [PATCH 0179/1118] Refactor `OnlineStatusNotifier` to be more local --- osu.Game/Online/OnlineStatusNotifier.cs | 108 ++++++++++++++---------- 1 file changed, 62 insertions(+), 46 deletions(-) diff --git a/osu.Game/Online/OnlineStatusNotifier.cs b/osu.Game/Online/OnlineStatusNotifier.cs index 0cf672ac3c..0d846f7d27 100644 --- a/osu.Game/Online/OnlineStatusNotifier.cs +++ b/osu.Game/Online/OnlineStatusNotifier.cs @@ -17,6 +17,9 @@ using osu.Game.Screens.OnlinePlay; namespace osu.Game.Online { + /// + /// Handles various scenarios where connection is lost and we need to let the user know what and why. + /// public partial class OnlineStatusNotifier : Component { private readonly Func getCurrentScreen; @@ -33,7 +36,11 @@ namespace osu.Game.Online private IBindable apiState = null!; private IBindable multiplayerState = null!; private IBindable spectatorState = null!; - private bool forcedDisconnection; + + /// + /// This flag will be set to true when the user has been notified so we don't show more than one notification. + /// + private bool userNotified; public OnlineStatusNotifier(Func getCurrentScreen) { @@ -51,12 +58,63 @@ namespace osu.Game.Online spectatorClient.Disconnecting += notifyAboutForcedDisconnection; } + protected override void LoadComplete() + { + base.LoadComplete(); + + apiState.BindValueChanged(state => + { + if (state.NewValue == APIState.Online) + { + userNotified = false; + return; + } + + if (userNotified) return; + + if (state.NewValue == APIState.Offline && getCurrentScreen() is OnlinePlayScreen) + { + userNotified = true; + notificationOverlay?.Post(new SimpleErrorNotification + { + Icon = FontAwesome.Solid.ExclamationCircle, + Text = "Connection to API was lost. Can't continue with online play." + }); + } + }); + + multiplayerState.BindValueChanged(connected => Schedule(() => + { + if (connected.NewValue) + { + userNotified = false; + return; + } + + if (userNotified) return; + + if (multiplayerClient.Room != null) + { + userNotified = true; + notificationOverlay?.Post(new SimpleErrorNotification + { + Icon = FontAwesome.Solid.ExclamationCircle, + Text = "Connection to the multiplayer server was lost. Exiting multiplayer." + }); + } + })); + + spectatorState.BindValueChanged(_ => + { + // TODO: handle spectator server failure somehow? + }); + } + private void notifyAboutForcedDisconnection() { - if (forcedDisconnection) - return; + if (userNotified) return; - forcedDisconnection = true; + userNotified = true; notificationOverlay?.Post(new SimpleErrorNotification { Icon = FontAwesome.Solid.ExclamationCircle, @@ -64,48 +122,6 @@ namespace osu.Game.Online }); } - protected override void LoadComplete() - { - base.LoadComplete(); - - apiState.BindValueChanged(_ => - { - if (apiState.Value == APIState.Online) - forcedDisconnection = false; - - Scheduler.AddOnce(updateState); - }); - multiplayerState.BindValueChanged(_ => Scheduler.AddOnce(updateState)); - spectatorState.BindValueChanged(_ => Scheduler.AddOnce(updateState)); - } - - private void updateState() - { - if (forcedDisconnection) - return; - - if (apiState.Value == APIState.Offline && getCurrentScreen() is OnlinePlayScreen) - { - notificationOverlay?.Post(new SimpleErrorNotification - { - Icon = FontAwesome.Solid.ExclamationCircle, - Text = "API connection was lost. Can't continue with online play." - }); - return; - } - - if (!multiplayerClient.IsConnected.Value && multiplayerClient.Room != null) - { - notificationOverlay?.Post(new SimpleErrorNotification - { - Icon = FontAwesome.Solid.ExclamationCircle, - Text = "Connection to the multiplayer server was lost. Exiting multiplayer." - }); - } - - // TODO: handle spectator server failure somehow? - } - protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From aa749aeb731dba4743338f88e541c326b8e5c12e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 21 Nov 2023 17:49:56 +0900 Subject: [PATCH 0180/1118] Save any unsaved changes in the skin editor when game changes screens Closes https://github.com/ppy/osu/issues/25494. --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index 68d6b7ced5..cbe122395c 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -188,7 +188,10 @@ namespace osu.Game.Overlays.SkinEditor } if (skinEditor.State.Value == Visibility.Visible) + { + skinEditor.Save(false); skinEditor.UpdateTargetScreen(target); + } else { skinEditor.Hide(); From 8302ebcf1ae54b2f0bc3ba7f8ba01b185ffeda39 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 21 Nov 2023 19:18:02 +0900 Subject: [PATCH 0181/1118] Remove default DrainRate value --- osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index a8808d08e5..4a651e2650 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Scoring /// /// The drain rate as a proportion of the total health drained per millisecond. /// - public double DrainRate { get; private set; } = 1; + public double DrainRate { get; private set; } /// /// The beatmap. @@ -139,8 +139,6 @@ namespace osu.Game.Rulesets.Scoring { base.Reset(storeResults); - DrainRate = 1; - if (storeResults) DrainRate = ComputeDrainRate(); From 16acec335f825eb93c791e64010f35e030f4ff69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Burgelin=20=28Zyf=29?= Date: Tue, 21 Nov 2023 11:27:03 +0100 Subject: [PATCH 0182/1118] Fix: update score migration of special case to match the new score MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 685f852eb4..a9569ac9ba 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -270,7 +270,7 @@ namespace osu.Game.Database { return (long)Math.Round(( 0 - + 300000 * Math.Pow(score.Accuracy, 8) + + 500000 * Math.Pow(score.Accuracy, 5) + bonusProportion) * modMultiplier); } From 901e45b6a4f134143d512c6cec378149c6bc4aea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment=20Burgelin=20=28Zyfarok=29?= Date: Tue, 21 Nov 2023 11:54:07 +0100 Subject: [PATCH 0183/1118] Scoring conversion: remove mention of v3 + improve comments --- .../StandardisedScoreMigrationTools.cs | 40 ++++++++++--------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index a9569ac9ba..fe51c6f130 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -275,50 +275,52 @@ namespace osu.Game.Database } // Assumption : - // - sliders and slider-ticks are uniformly spread arround the beatmap - // thus we can ignore them without losing much precision (consider a map of hit-circles only !) - // - the Ok/Meh hit results are uniformly spread in the score - // thus we can simplify and consider each hit result to be score.Accuracy without losing much precision - // What is strippedV1/strippedV3 : - // This is the ComboScore of v1/v3 were we remove all (map-)constant multipliers and accuracy multipliers (including hit results), - // based on the previous assumptions. For Scorev1, this is basically the sum of squared combos (because without sliders: object_count == combo). + // - sliders and slider-ticks are uniformly spread arround the beatmap, and thus can be ignored without losing much precision. + // We thus consider a map of hit-circles only, which gives objectCount == maximumCombo ! + // - the Ok/Meh hit results are uniformly spread in the score, and thus can be ignored without losing much precision. + // We simplify and consider each hit result to be equal to `300*score.Accuracy`, which allows us to isolate the accuracy multiplier. + // What is strippedV1/strippedNew : + // This is the ComboScore (of v1 / new score) were we remove all (map-)constant multipliers and accuracy multipliers (including hit results), + // based on the previous assumptions. + // We use integrals to approximate the sum of each object's combo contribution (thus the original combo exponent is increased by 1) + // For the maximum score, we thus integrate `f(combo) = 300*combo^exponent`, and ignoring the constant multipliers we get: double maxStrippedV1 = Math.Pow(maximumLegacyCombo, 2); - double maxStrippedV3 = Math.Pow(maximumLegacyCombo, 1 + ScoreProcessor.COMBO_EXPONENT); + double maxStrippedNew = Math.Pow(maximumLegacyCombo, 1 + ScoreProcessor.COMBO_EXPONENT); double strippedV1 = maxStrippedV1 * comboProportion / score.Accuracy; double strippedV1FromMaxCombo = Math.Pow(score.MaxCombo, 2); - double strippedV3FromMaxCombo = Math.Pow(score.MaxCombo, 1 + ScoreProcessor.COMBO_EXPONENT); + double strippedNewFromMaxCombo = Math.Pow(score.MaxCombo, 1 + ScoreProcessor.COMBO_EXPONENT); - // Compute approximate lower estimate scorev3 for that play + // Compute approximate lower estimate new score for that play // That is, a play were we made biggest amount of big combos (Repeat MaxCombo + 1 remaining big combo) // And didn't combo anything in the reminder of the map double possibleMaxComboRepeat = Math.Floor(strippedV1 / strippedV1FromMaxCombo); double strippedV1FromMaxComboRepeat = possibleMaxComboRepeat * strippedV1FromMaxCombo; double remainingStrippedV1 = strippedV1 - strippedV1FromMaxComboRepeat; double remainingCombo = Math.Sqrt(remainingStrippedV1); - double remainingStrippedV3 = Math.Pow(remainingCombo, 1 + ScoreProcessor.COMBO_EXPONENT); + double remainingStrippedNew = Math.Pow(remainingCombo, 1 + ScoreProcessor.COMBO_EXPONENT); - double newLowerStrippedV3 = (possibleMaxComboRepeat * strippedV3FromMaxCombo) + remainingStrippedV3; + double lowerStrippedNew = (possibleMaxComboRepeat * strippedNewFromMaxCombo) + remainingStrippedNew; - // Compute approximate upper estimate scorev3 for that play + // Compute approximate upper estimate new score for that play // That is, a play were all combos were equal (except MaxCombo) remainingStrippedV1 = strippedV1 - strippedV1FromMaxCombo; double remainingComboObjects = maximumLegacyCombo - score.MaxCombo - score.Statistics[HitResult.Miss]; double remainingAverageCombo = remainingComboObjects > 0 ? remainingStrippedV1 / remainingComboObjects : 0; - remainingStrippedV3 = remainingComboObjects * Math.Pow(remainingAverageCombo, ScoreProcessor.COMBO_EXPONENT); + remainingStrippedNew = remainingComboObjects * Math.Pow(remainingAverageCombo, ScoreProcessor.COMBO_EXPONENT); - double newUpperStrippedV3 = strippedV3FromMaxCombo + remainingStrippedV3; + double upperStrippedNew = strippedNewFromMaxCombo + remainingStrippedNew; // Approximate by combining lower and upper estimates // As the lower-estimate is very pessimistic, we use a 30/70 ratio // And cap it with 1.2 times the middle-point to avoid overstimates - double strippedV3 = Math.Min( - 0.3 * newLowerStrippedV3 + 0.7 * newUpperStrippedV3, - 1.2 * (newLowerStrippedV3 + newUpperStrippedV3) / 2 + double strippedNew = Math.Min( + 0.3 * lowerStrippedNew + 0.7 * upperStrippedNew, + 1.2 * (lowerStrippedNew + upperStrippedNew) / 2 ); - double newComboScoreProportion = (strippedV3 / maxStrippedV3); + double newComboScoreProportion = (strippedNew / maxStrippedNew); return (long)Math.Round(( 500000 * newComboScoreProportion * score.Accuracy From 671177e87120e4cacba35f824950d0847cbeacf6 Mon Sep 17 00:00:00 2001 From: yesseruser Date: Tue, 21 Nov 2023 19:02:23 +0100 Subject: [PATCH 0184/1118] Renamed UpdateableFlag to ClickableUpdateableFlag. --- osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs | 2 +- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 +- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 4 ++-- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Rankings/CountryPill.cs | 4 ++-- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 2 +- .../OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs | 2 +- osu.Game/Screens/Play/HUD/PlayerFlag.cs | 4 ++-- .../{UpdateableFlag.cs => ClickableUpdateableFlag.cs} | 4 ++-- osu.Game/Users/ExtendedUserPanel.cs | 2 +- 11 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game/Users/Drawables/{UpdateableFlag.cs => ClickableUpdateableFlag.cs} (91%) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs index 0bc71924ce..7a7679c376 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Menus AddStep("click on flag", () => { - InputManager.MoveMouseTo(loginOverlay.ChildrenOfType().First()); + InputManager.MoveMouseTo(loginOverlay.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); AddAssert("login overlay is hidden", () => loginOverlay.State.Value == Visibility.Hidden); diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 136c9cc8e7..114ef5db22 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -180,7 +180,7 @@ namespace osu.Game.Online.Leaderboards Masking = true, Children = new Drawable[] { - new UpdateableFlag(user.CountryCode) + new ClickableUpdateableFlag(user.CountryCode) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index 1fc997fdad..e144a55a96 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -157,7 +157,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Margin = new MarginPadding { Right = horizontal_inset }, Text = score.DisplayAccuracy, }, - new UpdateableFlag(score.User.CountryCode) + new ClickableUpdateableFlag(score.User.CountryCode) { Size = new Vector2(19, 14), ShowPlaceholderOnUnknown = false, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index 9dc2ce204f..e38e6efd06 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly UpdateableAvatar avatar; private readonly LinkFlowContainer usernameText; private readonly DrawableDate achievedOn; - private readonly UpdateableFlag flag; + private readonly ClickableUpdateableFlag flag; public TopScoreUserSection() { @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }, } }, - flag = new UpdateableFlag + flag = new ClickableUpdateableFlag { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 36bd8a5af5..15aaf333f4 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Profile.Header private OsuSpriteText usernameText = null!; private ExternalLinkButton openUserExternally = null!; private OsuSpriteText titleText = null!; - private UpdateableFlag userFlag = null!; + private ClickableUpdateableFlag userFlag = null!; private OsuHoverContainer userCountryContainer = null!; private OsuSpriteText userCountryText = null!; private GroupBadgeFlow groupBadgeFlow = null!; @@ -162,7 +162,7 @@ namespace osu.Game.Overlays.Profile.Header Direction = FillDirection.Horizontal, Children = new Drawable[] { - userFlag = new UpdateableFlag + userFlag = new ClickableUpdateableFlag { Size = new Vector2(28, 20), ShowPlaceholderOnUnknown = false, diff --git a/osu.Game/Overlays/Rankings/CountryPill.cs b/osu.Game/Overlays/Rankings/CountryPill.cs index 294b6df34d..bfa7363de8 100644 --- a/osu.Game/Overlays/Rankings/CountryPill.cs +++ b/osu.Game/Overlays/Rankings/CountryPill.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Rankings private readonly Container content; private readonly Box background; - private readonly UpdateableFlag flag; + private readonly ClickableUpdateableFlag flag; private readonly OsuSpriteText countryName; public CountryPill() @@ -74,7 +74,7 @@ namespace osu.Game.Overlays.Rankings Spacing = new Vector2(5, 0), Children = new Drawable[] { - flag = new UpdateableFlag + flag = new ClickableUpdateableFlag { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index 27d894cdc2..b68ecd709a 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Rankings.Tables Margin = new MarginPadding { Bottom = row_spacing }, Children = new[] { - new UpdateableFlag(GetCountryCode(item)) + new ClickableUpdateableFlag(GetCountryCode(item)) { Size = new Vector2(28, 20), ShowPlaceholderOnUnknown = false, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index c79c210e30..1f922073ec 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -123,7 +123,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants FillMode = FillMode.Fit, User = user }, - new UpdateableFlag + new ClickableUpdateableFlag { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Screens/Play/HUD/PlayerFlag.cs b/osu.Game/Screens/Play/HUD/PlayerFlag.cs index 85799c03d3..7234db71b5 100644 --- a/osu.Game/Screens/Play/HUD/PlayerFlag.cs +++ b/osu.Game/Screens/Play/HUD/PlayerFlag.cs @@ -12,14 +12,14 @@ namespace osu.Game.Screens.Play.HUD { public partial class PlayerFlag : CompositeDrawable, ISerialisableDrawable { - private readonly UpdateableFlag flag; + private readonly ClickableUpdateableFlag flag; private const float default_size = 40f; public PlayerFlag() { Size = new Vector2(default_size, default_size / 1.4f); - InternalChild = flag = new UpdateableFlag + InternalChild = flag = new ClickableUpdateableFlag { RelativeSizeAxes = Axes.Both, }; diff --git a/osu.Game/Users/Drawables/UpdateableFlag.cs b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs similarity index 91% rename from osu.Game/Users/Drawables/UpdateableFlag.cs rename to osu.Game/Users/Drawables/ClickableUpdateableFlag.cs index 8f8d7052e5..d19234fc17 100644 --- a/osu.Game/Users/Drawables/UpdateableFlag.cs +++ b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays; namespace osu.Game.Users.Drawables { - public partial class UpdateableFlag : ModelBackedDrawable + public partial class ClickableUpdateableFlag : ModelBackedDrawable { public CountryCode CountryCode { @@ -30,7 +30,7 @@ namespace osu.Game.Users.Drawables /// public Action? Action; - public UpdateableFlag(CountryCode countryCode = CountryCode.Unknown) + public ClickableUpdateableFlag(CountryCode countryCode = CountryCode.Unknown) { CountryCode = countryCode; } diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index 3c1b68f9ef..e798c8cc11 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -52,7 +52,7 @@ namespace osu.Game.Users protected UpdateableAvatar CreateAvatar() => new UpdateableAvatar(User, false); - protected UpdateableFlag CreateFlag() => new UpdateableFlag(User.CountryCode) + protected ClickableUpdateableFlag CreateFlag() => new ClickableUpdateableFlag(User.CountryCode) { Size = new Vector2(36, 26), Action = Action, From cd7e0bf620c0fd1617f1d99038dbbb85e52acd39 Mon Sep 17 00:00:00 2001 From: yesseruser Date: Tue, 21 Nov 2023 19:27:33 +0100 Subject: [PATCH 0185/1118] Created and implemened a BaseUpdateableFlag. The tooltip still shows. --- osu.Game/Screens/Play/HUD/PlayerFlag.cs | 4 +- .../Users/Drawables/BaseUpdateableFlag.cs | 45 +++++++++++++++++++ .../Drawables/ClickableUpdateableFlag.cs | 13 +----- 3 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 osu.Game/Users/Drawables/BaseUpdateableFlag.cs diff --git a/osu.Game/Screens/Play/HUD/PlayerFlag.cs b/osu.Game/Screens/Play/HUD/PlayerFlag.cs index 7234db71b5..f0fe8ad668 100644 --- a/osu.Game/Screens/Play/HUD/PlayerFlag.cs +++ b/osu.Game/Screens/Play/HUD/PlayerFlag.cs @@ -12,14 +12,14 @@ namespace osu.Game.Screens.Play.HUD { public partial class PlayerFlag : CompositeDrawable, ISerialisableDrawable { - private readonly ClickableUpdateableFlag flag; + private readonly BaseUpdateableFlag flag; private const float default_size = 40f; public PlayerFlag() { Size = new Vector2(default_size, default_size / 1.4f); - InternalChild = flag = new ClickableUpdateableFlag + InternalChild = flag = new BaseUpdateableFlag { RelativeSizeAxes = Axes.Both, }; diff --git a/osu.Game/Users/Drawables/BaseUpdateableFlag.cs b/osu.Game/Users/Drawables/BaseUpdateableFlag.cs new file mode 100644 index 0000000000..16a368c60c --- /dev/null +++ b/osu.Game/Users/Drawables/BaseUpdateableFlag.cs @@ -0,0 +1,45 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Users.Drawables +{ + public partial class BaseUpdateableFlag : ModelBackedDrawable + { + public CountryCode CountryCode + { + get => Model; + set => Model = value; + } + + /// + /// Whether to show a place holder on unknown country. + /// + public bool ShowPlaceholderOnUnknown = true; + + public BaseUpdateableFlag(CountryCode countryCode = CountryCode.Unknown) + { + CountryCode = countryCode; + } + + protected override Drawable? CreateDrawable(CountryCode countryCode) + { + if (countryCode == CountryCode.Unknown && !ShowPlaceholderOnUnknown) + return null; + + return new Container + { + RelativeSizeAxes = Axes.Both, + Children = new Drawable[] + { + new DrawableFlag(countryCode) + { + RelativeSizeAxes = Axes.Both + }, + } + }; + } + } +} diff --git a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs index d19234fc17..e00fe3c009 100644 --- a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs +++ b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs @@ -11,19 +11,8 @@ using osu.Game.Overlays; namespace osu.Game.Users.Drawables { - public partial class ClickableUpdateableFlag : ModelBackedDrawable + public partial class ClickableUpdateableFlag : BaseUpdateableFlag { - public CountryCode CountryCode - { - get => Model; - set => Model = value; - } - - /// - /// Whether to show a place holder on unknown country. - /// - public bool ShowPlaceholderOnUnknown = true; - /// /// Perform an action in addition to showing the country ranking. /// This should be used to perform auxiliary tasks and not as a primary action for clicking a flag (to maintain a consistent UX). From b2f74325efb4f477b42320cca8cbd380895b12da Mon Sep 17 00:00:00 2001 From: yesseruser Date: Tue, 21 Nov 2023 19:51:35 +0100 Subject: [PATCH 0186/1118] Added a BaseDrawableFlag without a tooltip, and used it. The BaseDrawableFlag is used in a BaseUpdateableFlag so the tooltip is not shown, the ClickableUpdateableFlag still shows the tooltip. --- osu.Game/Users/Drawables/BaseDrawableFlag.cs | 29 +++++++++++++++++++ .../Users/Drawables/BaseUpdateableFlag.cs | 3 +- .../Drawables/ClickableUpdateableFlag.cs | 1 + osu.Game/Users/Drawables/DrawableFlag.cs | 14 +++------ 4 files changed, 36 insertions(+), 11 deletions(-) create mode 100644 osu.Game/Users/Drawables/BaseDrawableFlag.cs diff --git a/osu.Game/Users/Drawables/BaseDrawableFlag.cs b/osu.Game/Users/Drawables/BaseDrawableFlag.cs new file mode 100644 index 0000000000..90f8cd7b70 --- /dev/null +++ b/osu.Game/Users/Drawables/BaseDrawableFlag.cs @@ -0,0 +1,29 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; + +namespace osu.Game.Users.Drawables +{ + public partial class BaseDrawableFlag : Sprite + { + protected readonly CountryCode CountryCode; + + public BaseDrawableFlag(CountryCode countryCode) + { + CountryCode = countryCode; + } + + [BackgroundDependencyLoader] + private void load(TextureStore ts) + { + ArgumentNullException.ThrowIfNull(ts); + + string textureName = CountryCode == CountryCode.Unknown ? "__" : CountryCode.ToString(); + Texture = ts.Get($@"Flags/{textureName}") ?? ts.Get(@"Flags/__"); + } + } +} diff --git a/osu.Game/Users/Drawables/BaseUpdateableFlag.cs b/osu.Game/Users/Drawables/BaseUpdateableFlag.cs index 16a368c60c..3a27238b19 100644 --- a/osu.Game/Users/Drawables/BaseUpdateableFlag.cs +++ b/osu.Game/Users/Drawables/BaseUpdateableFlag.cs @@ -34,7 +34,8 @@ namespace osu.Game.Users.Drawables RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - new DrawableFlag(countryCode) + // This is a BaseDrawableFlag which does not show a tooltip. + new BaseDrawableFlag(countryCode) { RelativeSizeAxes = Axes.Both }, diff --git a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs index e00fe3c009..2eaa3661d1 100644 --- a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs +++ b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs @@ -34,6 +34,7 @@ namespace osu.Game.Users.Drawables RelativeSizeAxes = Axes.Both, Children = new Drawable[] { + // This is a DrawableFlag which implements IHasTooltip, so a tooltip is shown. new DrawableFlag(countryCode) { RelativeSizeAxes = Axes.Both diff --git a/osu.Game/Users/Drawables/DrawableFlag.cs b/osu.Game/Users/Drawables/DrawableFlag.cs index 289f68ee7f..aef34c093f 100644 --- a/osu.Game/Users/Drawables/DrawableFlag.cs +++ b/osu.Game/Users/Drawables/DrawableFlag.cs @@ -5,29 +5,23 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; namespace osu.Game.Users.Drawables { - public partial class DrawableFlag : Sprite, IHasTooltip + public partial class DrawableFlag : BaseDrawableFlag, IHasTooltip { - private readonly CountryCode countryCode; + public LocalisableString TooltipText => CountryCode == CountryCode.Unknown ? string.Empty : CountryCode.GetDescription(); - public LocalisableString TooltipText => countryCode == CountryCode.Unknown ? string.Empty : countryCode.GetDescription(); - - public DrawableFlag(CountryCode countryCode) - { - this.countryCode = countryCode; - } + public DrawableFlag(CountryCode countryCode) : base(countryCode) { } [BackgroundDependencyLoader] private void load(TextureStore ts) { ArgumentNullException.ThrowIfNull(ts); - string textureName = countryCode == CountryCode.Unknown ? "__" : countryCode.ToString(); + string textureName = CountryCode == CountryCode.Unknown ? "__" : CountryCode.ToString(); Texture = ts.Get($@"Flags/{textureName}") ?? ts.Get(@"Flags/__"); } } From c98be5823db0818bf5eaf8bcab853fa8622d5257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 22 Nov 2023 07:52:28 +0900 Subject: [PATCH 0187/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index aa993485f3..ea08992710 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index b43cb1b3f1..53d5d6b010 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From aa724070653dc41df9e67c8476427752586b219f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 22 Nov 2023 07:55:39 +0900 Subject: [PATCH 0188/1118] Fix android compile failures due to invalid java version See https://github.com/ppy/osu-framework/pull/6057. --- .github/workflows/ci.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a8167ec4db..11c956bb71 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -108,6 +108,12 @@ jobs: - name: Checkout uses: actions/checkout@v3 + - name: Setup JDK 11 + uses: actions/setup-java@v3 + with: + distribution: microsoft + java-version: 11 + - name: Install .NET 6.0.x uses: actions/setup-dotnet@v3 with: From 0cf925dadf4e09fc6e8e8c14f2511f4ef8591a18 Mon Sep 17 00:00:00 2001 From: Poyo Date: Tue, 21 Nov 2023 15:18:04 -0800 Subject: [PATCH 0189/1118] Use better fallback Seems better to use the rate from a non-gameplay clock than to arbitrarily apply 1. --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 5abca168ed..1daaa24d57 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -704,7 +704,7 @@ namespace osu.Game.Rulesets.Objects.Drawables } Result.RawTime = Time.Current; - Result.GameplayRate = (Clock as IGameplayClock)?.GetTrueGameplayRate() ?? 1.0; + Result.GameplayRate = (Clock as IGameplayClock)?.GetTrueGameplayRate() ?? Clock.Rate; if (Result.HasResult) updateState(Result.IsHit ? ArmedState.Hit : ArmedState.Miss); From 3a0586a8f59ce286f986df00327cf7953fd62dd7 Mon Sep 17 00:00:00 2001 From: Poyo Date: Tue, 21 Nov 2023 15:19:04 -0800 Subject: [PATCH 0190/1118] Rewrite backwards assertion --- osu.Game/Rulesets/Scoring/HitEventExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs index 70a11ae760..9fb61c6cd9 100644 --- a/osu.Game/Rulesets/Scoring/HitEventExtensions.cs +++ b/osu.Game/Rulesets/Scoring/HitEventExtensions.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Scoring /// public static double? CalculateUnstableRate(this IEnumerable hitEvents) { - Debug.Assert(!hitEvents.Any(ev => ev.GameplayRate == null)); + Debug.Assert(hitEvents.All(ev => ev.GameplayRate != null)); // Division by gameplay rate is to account for TimeOffset scaling with gameplay rate. double[] timeOffsets = hitEvents.Where(affectsUnstableRate).Select(ev => ev.TimeOffset / ev.GameplayRate!.Value).ToArray(); From 04640b6fb0129aa880281d1883bf0b318a128fe0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 10:44:29 +0900 Subject: [PATCH 0191/1118] Improve commenting around `IHasCombo` interfaces Following discusion with smoogi IRL. --- osu.Game/Rulesets/Objects/Types/IHasCombo.cs | 6 ++++++ .../Rulesets/Objects/Types/IHasComboInformation.cs | 14 ++++++++++++++ 2 files changed, 20 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs index d1a4683a1d..5de5424bdc 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasCombo.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasCombo.cs @@ -16,6 +16,12 @@ namespace osu.Game.Rulesets.Objects.Types /// /// When starting a new combo, the offset of the new combo relative to the current one. /// + /// + /// This is generally a setting provided by a beatmap creator to choreograph interesting colour patterns + /// which can only be achieved by skipping combo colours with per-hitobject level. + /// + /// It is exposed via . + /// int ComboOffset { get; } } } diff --git a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs index d34e71021f..3aa68197ec 100644 --- a/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs +++ b/osu.Game/Rulesets/Objects/Types/IHasComboInformation.cs @@ -12,6 +12,9 @@ namespace osu.Game.Rulesets.Objects.Types /// public interface IHasComboInformation : IHasCombo { + /// + /// Bindable exposure of . + /// Bindable IndexInCurrentComboBindable { get; } /// @@ -19,13 +22,21 @@ namespace osu.Game.Rulesets.Objects.Types /// int IndexInCurrentCombo { get; set; } + /// + /// Bindable exposure of . + /// Bindable ComboIndexBindable { get; } /// /// The index of this combo in relation to the beatmap. + /// + /// In other words, this is incremented by 1 each time a is reached. /// int ComboIndex { get; set; } + /// + /// Bindable exposure of . + /// Bindable ComboIndexWithOffsetsBindable { get; } /// @@ -39,6 +50,9 @@ namespace osu.Game.Rulesets.Objects.Types /// new bool NewCombo { get; set; } + /// + /// Bindable exposure of . + /// Bindable LastInComboBindable { get; } /// From fe15b26bd2f780ba6e45e81e64944841ec23a54d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 12:02:37 +0900 Subject: [PATCH 0192/1118] Refactor to use API state instead of logged in user state --- osu.Game/Overlays/UserProfileOverlay.cs | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index 193651fa72..d978fe905f 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays private IUser? user; private IRulesetInfo? ruleset; - private IBindable apiUser = null!; + private readonly IBindable apiState = new Bindable(); [Resolved] private RulesetStore rulesets { get; set; } = null!; @@ -68,10 +68,10 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { - apiUser = API.LocalUser.GetBoundCopy(); - apiUser.BindValueChanged(_ => Schedule(() => + apiState.BindTo(API.State); + apiState.BindValueChanged(state => Schedule(() => { - if (API.IsLoggedIn) + if (state.NewValue == APIState.Online && user != null) fetchAndSetContent(); })); } @@ -89,7 +89,6 @@ namespace osu.Game.Overlays ruleset = userRuleset; Show(); - fetchAndSetContent(); } @@ -171,13 +170,14 @@ namespace osu.Game.Overlays sectionsContainer.ScrollToTop(); - if (API.State.Value != APIState.Online) - return; + if (API.State.Value != APIState.Offline) + { + userReq = user.OnlineID > 1 ? new GetUserRequest(user.OnlineID, ruleset) : new GetUserRequest(user.Username, ruleset); + userReq.Success += u => userLoadComplete(u, ruleset); - userReq = user.OnlineID > 1 ? new GetUserRequest(user.OnlineID, ruleset) : new GetUserRequest(user.Username, ruleset); - userReq.Success += u => userLoadComplete(u, ruleset); - API.Queue(userReq); - loadingLayer.Show(); + API.Queue(userReq); + loadingLayer.Show(); + } } private void userLoadComplete(APIUser loadedUser, IRulesetInfo? userRuleset) From ad6af1d9b74302b1d0396baba0fad68f31f88441 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 12:03:42 +0900 Subject: [PATCH 0193/1118] Ensure only run once --- osu.Game/Overlays/UserProfileOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/UserProfileOverlay.cs b/osu.Game/Overlays/UserProfileOverlay.cs index d978fe905f..9840551d9f 100644 --- a/osu.Game/Overlays/UserProfileOverlay.cs +++ b/osu.Game/Overlays/UserProfileOverlay.cs @@ -72,7 +72,7 @@ namespace osu.Game.Overlays apiState.BindValueChanged(state => Schedule(() => { if (state.NewValue == APIState.Online && user != null) - fetchAndSetContent(); + Scheduler.AddOnce(fetchAndSetContent); })); } @@ -89,7 +89,7 @@ namespace osu.Game.Overlays ruleset = userRuleset; Show(); - fetchAndSetContent(); + Scheduler.AddOnce(fetchAndSetContent); } private void fetchAndSetContent() From cb4568c4a1e7d12fea7dda4a643878e9c2bae675 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 16 Nov 2023 20:21:11 +0900 Subject: [PATCH 0194/1118] Fix first object after break not starting a new combo --- .../Formats/LegacyBeatmapDecoderTest.cs | 15 ++++++++++++ .../TestSceneHitObjectAccentColour.cs | 3 --- .../Resources/break-between-objects.osu | 15 ++++++++++++ .../Beatmaps/Formats/LegacyBeatmapDecoder.cs | 23 +++++++++++++++++++ .../Objects/Legacy/Catch/ConvertHit.cs | 6 +---- .../Objects/Legacy/Catch/ConvertSlider.cs | 6 +---- .../Objects/Legacy/Catch/ConvertSpinner.cs | 6 +---- .../Objects/Legacy/ConvertHitObject.cs | 7 +++++- .../Rulesets/Objects/Legacy/Osu/ConvertHit.cs | 6 +---- .../Objects/Legacy/Osu/ConvertSlider.cs | 6 +---- .../Objects/Legacy/Osu/ConvertSpinner.cs | 6 +---- 11 files changed, 65 insertions(+), 34 deletions(-) create mode 100644 osu.Game.Tests/Resources/break-between-objects.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index 66151a51e6..be1993957f 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -1093,5 +1093,20 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(hitObject.Samples.Select(s => s.Volume), Has.All.EqualTo(70)); } } + + [Test] + public void TestNewComboAfterBreak() + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("break-between-objects.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + var beatmap = decoder.Decode(stream); + Assert.That(((IHasCombo)beatmap.HitObjects[0]).NewCombo, Is.True); + Assert.That(((IHasCombo)beatmap.HitObjects[1]).NewCombo, Is.True); + Assert.That(((IHasCombo)beatmap.HitObjects[2]).NewCombo, Is.False); + } + } } } diff --git a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs index f38c2c9416..acb14f86fc 100644 --- a/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs +++ b/osu.Game.Tests/Gameplay/TestSceneHitObjectAccentColour.cs @@ -94,9 +94,6 @@ namespace osu.Game.Tests.Gameplay private class TestHitObjectWithCombo : ConvertHitObject, IHasComboInformation { - public bool NewCombo { get; set; } - public int ComboOffset => 0; - public Bindable IndexInCurrentComboBindable { get; } = new Bindable(); public int IndexInCurrentCombo diff --git a/osu.Game.Tests/Resources/break-between-objects.osu b/osu.Game.Tests/Resources/break-between-objects.osu new file mode 100644 index 0000000000..91821e2c58 --- /dev/null +++ b/osu.Game.Tests/Resources/break-between-objects.osu @@ -0,0 +1,15 @@ +osu file format v14 + +[General] +Mode: 0 + +[Events] +2,200,1200 + +[TimingPoints] +0,307.692307692308,4,2,1,60,1,0 + +[HitObjects] +142,99,0,1,0,0:0:0:0: +323,88,3000,1,0,0:0:0:0: +323,88,4000,1,0,0:0:0:0: diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs index 8c5e4971d5..1ee4670ae2 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapDecoder.cs @@ -93,6 +93,8 @@ namespace osu.Game.Beatmaps.Formats // The parsing order of hitobjects matters in mania difficulty calculation this.beatmap.HitObjects = this.beatmap.HitObjects.OrderBy(h => h.StartTime).ToList(); + postProcessBreaks(this.beatmap); + foreach (var hitObject in this.beatmap.HitObjects) { applyDefaults(hitObject); @@ -100,6 +102,27 @@ namespace osu.Game.Beatmaps.Formats } } + /// + /// Processes the beatmap such that a new combo is started the first hitobject following each break. + /// + private void postProcessBreaks(Beatmap beatmap) + { + int currentBreak = 0; + bool forceNewCombo = false; + + foreach (var h in beatmap.HitObjects.OfType()) + { + while (currentBreak < beatmap.Breaks.Count && beatmap.Breaks[currentBreak].EndTime < h.StartTime) + { + forceNewCombo = true; + currentBreak++; + } + + h.NewCombo |= forceNewCombo; + forceNewCombo = false; + } + } + private void applyDefaults(HitObject hitObject) { DifficultyControlPoint difficultyControlPoint = (beatmap.ControlPointInfo as LegacyControlPointInfo)?.DifficultyPointAt(hitObject.StartTime) ?? DifficultyControlPoint.DEFAULT; diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs index 12b4812824..96c779e79b 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHit.cs @@ -9,16 +9,12 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch /// /// Legacy osu!catch Hit-type, used for parsing Beatmaps. /// - internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo + internal sealed class ConvertHit : ConvertHitObject, IHasPosition { public float X => Position.X; public float Y => Position.Y; public Vector2 Position { get; set; } - - public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs index fb1afed3b4..bcf1c7fae2 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSlider.cs @@ -9,16 +9,12 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch /// /// Legacy osu!catch Slider-type, used for parsing Beatmaps. /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition { public float X => Position.X; public float Y => Position.Y; public Vector2 Position { get; set; } - - public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs index 014494ec54..5ef3d51cb3 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertSpinner.cs @@ -8,16 +8,12 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch /// /// Legacy osu!catch Spinner-type, used for parsing Beatmaps. /// - internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasXPosition, IHasCombo + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasXPosition { public double EndTime => StartTime + Duration; public double Duration { get; set; } public float X => 256; // Required for CatchBeatmapConverter - - public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObject.cs b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObject.cs index 54dbd28c76..bb36aab0b3 100644 --- a/osu.Game/Rulesets/Objects/Legacy/ConvertHitObject.cs +++ b/osu.Game/Rulesets/Objects/Legacy/ConvertHitObject.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Objects.Legacy @@ -9,8 +10,12 @@ namespace osu.Game.Rulesets.Objects.Legacy /// /// A hit object only used for conversion, not actual gameplay. /// - internal abstract class ConvertHitObject : HitObject + internal abstract class ConvertHitObject : HitObject, IHasCombo { + public bool NewCombo { get; set; } + + public int ComboOffset { get; set; } + public override Judgement CreateJudgement() => new IgnoreJudgement(); protected override HitWindows CreateHitWindows() => HitWindows.Empty; diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs index 069366bad3..b7cd4b0dcc 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHit.cs @@ -9,16 +9,12 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Hit-type, used for parsing Beatmaps. /// - internal sealed class ConvertHit : ConvertHitObject, IHasPosition, IHasCombo + internal sealed class ConvertHit : ConvertHitObject, IHasPosition { public Vector2 Position { get; set; } public float X => Position.X; public float Y => Position.Y; - - public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs index 790af6cfc1..8c37154f95 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSlider.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Slider-type, used for parsing Beatmaps. /// - internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasCombo, IHasGenerateTicks + internal sealed class ConvertSlider : Legacy.ConvertSlider, IHasPosition, IHasGenerateTicks { public Vector2 Position { get; set; } @@ -17,10 +17,6 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public float Y => Position.Y; - public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } - public bool GenerateTicks { get; set; } = true; } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs index e9e5ca8c94..d6e24b6bbf 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertSpinner.cs @@ -9,7 +9,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// /// Legacy osu! Spinner-type, used for parsing Beatmaps. /// - internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasPosition, IHasCombo + internal sealed class ConvertSpinner : ConvertHitObject, IHasDuration, IHasPosition { public double Duration { get; set; } @@ -20,9 +20,5 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu public float X => Position.X; public float Y => Position.Y; - - public bool NewCombo { get; set; } - - public int ComboOffset { get; set; } } } From f7fce1d714dc4d849aef066abe62d1feb4a3145f Mon Sep 17 00:00:00 2001 From: cs Date: Wed, 22 Nov 2023 09:55:32 +0100 Subject: [PATCH 0195/1118] Fix freehand-drawn sliders with distance snap --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 63370350ed..c35c6fdf95 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -175,6 +175,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (controlPointCount > 2 || (controlPointCount == 2 && HitObject.Path.ControlPoints.Last() != cursor)) return base.OnDragStart(e); + HitObject.Position = ToLocalSpace(e.ScreenSpaceMouseDownPosition); bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); state = SliderPlacementState.Drawing; return true; From 95b12082aed0bb40e998f8b0402be77cfb4639e1 Mon Sep 17 00:00:00 2001 From: cs Date: Wed, 22 Nov 2023 10:14:44 +0100 Subject: [PATCH 0196/1118] Update to respect distance snap This will cause freehand drawing to respect distance snap, instead changing the drawn path to start from the sliders start position and "snap" in a linear fashion to the cursor position from the position indicated by distance snap --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index c35c6fdf95..ac7b5e63e1 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (controlPointCount > 2 || (controlPointCount == 2 && HitObject.Path.ControlPoints.Last() != cursor)) return base.OnDragStart(e); - HitObject.Position = ToLocalSpace(e.ScreenSpaceMouseDownPosition); + bSplineBuilder.AddLinearPoint(Vector2.Zero); bSplineBuilder.AddLinearPoint(ToLocalSpace(e.ScreenSpaceMouseDownPosition) - HitObject.Position); state = SliderPlacementState.Drawing; return true; From aa8dc6bd8061a1eafa591b00fbefa706559e283a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 21:46:35 +0900 Subject: [PATCH 0197/1118] Attempt to fix intermittent failures on new tests See https://github.com/ppy/osu/pull/25418/checks?check_run_id=18886372597. --- .../Visual/Editing/TestSceneOpenEditorTimestamp.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index b6f89ee4e7..2c8655a5f5 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -31,7 +31,7 @@ namespace osu.Game.Tests.Visual.Editing RulesetInfo rulesetInfo = new OsuRuleset().RulesetInfo; addStepClickLink("00:00:000", waitForSeek: false); - AddAssert("received 'must be in edit'", + AddUntilStep("received 'must be in edit'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEditorToHandleLinks), () => Is.EqualTo(1)); @@ -39,7 +39,7 @@ namespace osu.Game.Tests.Visual.Editing AddUntilStep("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); addStepClickLink("00:00:000 (1)", waitForSeek: false); - AddAssert("received 'must be in edit'", + AddUntilStep("received 'must be in edit'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEditorToHandleLinks), () => Is.EqualTo(2)); @@ -47,12 +47,12 @@ namespace osu.Game.Tests.Visual.Editing AddAssert("ruleset is osu!", () => editorBeatmap.BeatmapInfo.Ruleset.Equals(rulesetInfo)); addStepClickLink("00:000", "invalid link", waitForSeek: false); - AddAssert("received 'failed to process'", + AddUntilStep("received 'failed to process'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToParseEditorLink), () => Is.EqualTo(1)); addStepClickLink("50000:00:000", "too long link", waitForSeek: false); - AddAssert("received 'failed to process'", + AddUntilStep("received 'failed to process'", () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.FailedToParseEditorLink), () => Is.EqualTo(2)); } From 7bc304f20ee27d7314fc06620cf1250acebba89f Mon Sep 17 00:00:00 2001 From: yesseruser Date: Wed, 22 Nov 2023 15:25:17 +0100 Subject: [PATCH 0198/1118] Revert "Added a BaseDrawableFlag without a tooltip, and used it." This reverts commit b2f74325efb4f477b42320cca8cbd380895b12da. --- osu.Game/Users/Drawables/BaseDrawableFlag.cs | 29 ------------------- .../Users/Drawables/BaseUpdateableFlag.cs | 3 +- .../Drawables/ClickableUpdateableFlag.cs | 1 - osu.Game/Users/Drawables/DrawableFlag.cs | 14 ++++++--- 4 files changed, 11 insertions(+), 36 deletions(-) delete mode 100644 osu.Game/Users/Drawables/BaseDrawableFlag.cs diff --git a/osu.Game/Users/Drawables/BaseDrawableFlag.cs b/osu.Game/Users/Drawables/BaseDrawableFlag.cs deleted file mode 100644 index 90f8cd7b70..0000000000 --- a/osu.Game/Users/Drawables/BaseDrawableFlag.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using osu.Framework.Allocation; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; - -namespace osu.Game.Users.Drawables -{ - public partial class BaseDrawableFlag : Sprite - { - protected readonly CountryCode CountryCode; - - public BaseDrawableFlag(CountryCode countryCode) - { - CountryCode = countryCode; - } - - [BackgroundDependencyLoader] - private void load(TextureStore ts) - { - ArgumentNullException.ThrowIfNull(ts); - - string textureName = CountryCode == CountryCode.Unknown ? "__" : CountryCode.ToString(); - Texture = ts.Get($@"Flags/{textureName}") ?? ts.Get(@"Flags/__"); - } - } -} diff --git a/osu.Game/Users/Drawables/BaseUpdateableFlag.cs b/osu.Game/Users/Drawables/BaseUpdateableFlag.cs index 3a27238b19..16a368c60c 100644 --- a/osu.Game/Users/Drawables/BaseUpdateableFlag.cs +++ b/osu.Game/Users/Drawables/BaseUpdateableFlag.cs @@ -34,8 +34,7 @@ namespace osu.Game.Users.Drawables RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - // This is a BaseDrawableFlag which does not show a tooltip. - new BaseDrawableFlag(countryCode) + new DrawableFlag(countryCode) { RelativeSizeAxes = Axes.Both }, diff --git a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs index 2eaa3661d1..e00fe3c009 100644 --- a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs +++ b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs @@ -34,7 +34,6 @@ namespace osu.Game.Users.Drawables RelativeSizeAxes = Axes.Both, Children = new Drawable[] { - // This is a DrawableFlag which implements IHasTooltip, so a tooltip is shown. new DrawableFlag(countryCode) { RelativeSizeAxes = Axes.Both diff --git a/osu.Game/Users/Drawables/DrawableFlag.cs b/osu.Game/Users/Drawables/DrawableFlag.cs index aef34c093f..289f68ee7f 100644 --- a/osu.Game/Users/Drawables/DrawableFlag.cs +++ b/osu.Game/Users/Drawables/DrawableFlag.cs @@ -5,23 +5,29 @@ using System; using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; namespace osu.Game.Users.Drawables { - public partial class DrawableFlag : BaseDrawableFlag, IHasTooltip + public partial class DrawableFlag : Sprite, IHasTooltip { - public LocalisableString TooltipText => CountryCode == CountryCode.Unknown ? string.Empty : CountryCode.GetDescription(); + private readonly CountryCode countryCode; - public DrawableFlag(CountryCode countryCode) : base(countryCode) { } + public LocalisableString TooltipText => countryCode == CountryCode.Unknown ? string.Empty : countryCode.GetDescription(); + + public DrawableFlag(CountryCode countryCode) + { + this.countryCode = countryCode; + } [BackgroundDependencyLoader] private void load(TextureStore ts) { ArgumentNullException.ThrowIfNull(ts); - string textureName = CountryCode == CountryCode.Unknown ? "__" : CountryCode.ToString(); + string textureName = countryCode == CountryCode.Unknown ? "__" : countryCode.ToString(); Texture = ts.Get($@"Flags/{textureName}") ?? ts.Get(@"Flags/__"); } } From be8b59e59d28ac99620e0252215ad7a4acf82604 Mon Sep 17 00:00:00 2001 From: yesseruser Date: Wed, 22 Nov 2023 15:25:35 +0100 Subject: [PATCH 0199/1118] Revert "Created and implemened a BaseUpdateableFlag." This reverts commit cd7e0bf620c0fd1617f1d99038dbbb85e52acd39. --- osu.Game/Screens/Play/HUD/PlayerFlag.cs | 4 +- .../Users/Drawables/BaseUpdateableFlag.cs | 45 ------------------- .../Drawables/ClickableUpdateableFlag.cs | 13 +++++- 3 files changed, 14 insertions(+), 48 deletions(-) delete mode 100644 osu.Game/Users/Drawables/BaseUpdateableFlag.cs diff --git a/osu.Game/Screens/Play/HUD/PlayerFlag.cs b/osu.Game/Screens/Play/HUD/PlayerFlag.cs index f0fe8ad668..7234db71b5 100644 --- a/osu.Game/Screens/Play/HUD/PlayerFlag.cs +++ b/osu.Game/Screens/Play/HUD/PlayerFlag.cs @@ -12,14 +12,14 @@ namespace osu.Game.Screens.Play.HUD { public partial class PlayerFlag : CompositeDrawable, ISerialisableDrawable { - private readonly BaseUpdateableFlag flag; + private readonly ClickableUpdateableFlag flag; private const float default_size = 40f; public PlayerFlag() { Size = new Vector2(default_size, default_size / 1.4f); - InternalChild = flag = new BaseUpdateableFlag + InternalChild = flag = new ClickableUpdateableFlag { RelativeSizeAxes = Axes.Both, }; diff --git a/osu.Game/Users/Drawables/BaseUpdateableFlag.cs b/osu.Game/Users/Drawables/BaseUpdateableFlag.cs deleted file mode 100644 index 16a368c60c..0000000000 --- a/osu.Game/Users/Drawables/BaseUpdateableFlag.cs +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Users.Drawables -{ - public partial class BaseUpdateableFlag : ModelBackedDrawable - { - public CountryCode CountryCode - { - get => Model; - set => Model = value; - } - - /// - /// Whether to show a place holder on unknown country. - /// - public bool ShowPlaceholderOnUnknown = true; - - public BaseUpdateableFlag(CountryCode countryCode = CountryCode.Unknown) - { - CountryCode = countryCode; - } - - protected override Drawable? CreateDrawable(CountryCode countryCode) - { - if (countryCode == CountryCode.Unknown && !ShowPlaceholderOnUnknown) - return null; - - return new Container - { - RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - new DrawableFlag(countryCode) - { - RelativeSizeAxes = Axes.Both - }, - } - }; - } - } -} diff --git a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs index e00fe3c009..d19234fc17 100644 --- a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs +++ b/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs @@ -11,8 +11,19 @@ using osu.Game.Overlays; namespace osu.Game.Users.Drawables { - public partial class ClickableUpdateableFlag : BaseUpdateableFlag + public partial class ClickableUpdateableFlag : ModelBackedDrawable { + public CountryCode CountryCode + { + get => Model; + set => Model = value; + } + + /// + /// Whether to show a place holder on unknown country. + /// + public bool ShowPlaceholderOnUnknown = true; + /// /// Perform an action in addition to showing the country ranking. /// This should be used to perform auxiliary tasks and not as a primary action for clicking a flag (to maintain a consistent UX). From 08e0279d72d1f76f099c3cda6f6dd0c086123501 Mon Sep 17 00:00:00 2001 From: yesseruser Date: Wed, 22 Nov 2023 15:25:43 +0100 Subject: [PATCH 0200/1118] Revert "Renamed UpdateableFlag to ClickableUpdateableFlag." This reverts commit 671177e87120e4cacba35f824950d0847cbeacf6. --- osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs | 2 +- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 +- osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs | 2 +- osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs | 4 ++-- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 4 ++-- osu.Game/Overlays/Rankings/CountryPill.cs | 4 ++-- osu.Game/Overlays/Rankings/Tables/RankingsTable.cs | 2 +- .../OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs | 2 +- osu.Game/Screens/Play/HUD/PlayerFlag.cs | 4 ++-- .../{ClickableUpdateableFlag.cs => UpdateableFlag.cs} | 4 ++-- osu.Game/Users/ExtendedUserPanel.cs | 2 +- 11 files changed, 16 insertions(+), 16 deletions(-) rename osu.Game/Users/Drawables/{ClickableUpdateableFlag.cs => UpdateableFlag.cs} (91%) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs b/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs index 7a7679c376..0bc71924ce 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneLoginOverlay.cs @@ -80,7 +80,7 @@ namespace osu.Game.Tests.Visual.Menus AddStep("click on flag", () => { - InputManager.MoveMouseTo(loginOverlay.ChildrenOfType().First()); + InputManager.MoveMouseTo(loginOverlay.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); AddAssert("login overlay is hidden", () => loginOverlay.State.Value == Visibility.Hidden); diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 114ef5db22..136c9cc8e7 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -180,7 +180,7 @@ namespace osu.Game.Online.Leaderboards Masking = true, Children = new Drawable[] { - new ClickableUpdateableFlag(user.CountryCode) + new UpdateableFlag(user.CountryCode) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs index e144a55a96..1fc997fdad 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/ScoreTable.cs @@ -157,7 +157,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores Margin = new MarginPadding { Right = horizontal_inset }, Text = score.DisplayAccuracy, }, - new ClickableUpdateableFlag(score.User.CountryCode) + new UpdateableFlag(score.User.CountryCode) { Size = new Vector2(19, 14), ShowPlaceholderOnUnknown = false, diff --git a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs index e38e6efd06..9dc2ce204f 100644 --- a/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs +++ b/osu.Game/Overlays/BeatmapSet/Scores/TopScoreUserSection.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores private readonly UpdateableAvatar avatar; private readonly LinkFlowContainer usernameText; private readonly DrawableDate achievedOn; - private readonly ClickableUpdateableFlag flag; + private readonly UpdateableFlag flag; public TopScoreUserSection() { @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.BeatmapSet.Scores }, } }, - flag = new ClickableUpdateableFlag + flag = new UpdateableFlag { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 15aaf333f4..36bd8a5af5 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays.Profile.Header private OsuSpriteText usernameText = null!; private ExternalLinkButton openUserExternally = null!; private OsuSpriteText titleText = null!; - private ClickableUpdateableFlag userFlag = null!; + private UpdateableFlag userFlag = null!; private OsuHoverContainer userCountryContainer = null!; private OsuSpriteText userCountryText = null!; private GroupBadgeFlow groupBadgeFlow = null!; @@ -162,7 +162,7 @@ namespace osu.Game.Overlays.Profile.Header Direction = FillDirection.Horizontal, Children = new Drawable[] { - userFlag = new ClickableUpdateableFlag + userFlag = new UpdateableFlag { Size = new Vector2(28, 20), ShowPlaceholderOnUnknown = false, diff --git a/osu.Game/Overlays/Rankings/CountryPill.cs b/osu.Game/Overlays/Rankings/CountryPill.cs index bfa7363de8..294b6df34d 100644 --- a/osu.Game/Overlays/Rankings/CountryPill.cs +++ b/osu.Game/Overlays/Rankings/CountryPill.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Rankings private readonly Container content; private readonly Box background; - private readonly ClickableUpdateableFlag flag; + private readonly UpdateableFlag flag; private readonly OsuSpriteText countryName; public CountryPill() @@ -74,7 +74,7 @@ namespace osu.Game.Overlays.Rankings Spacing = new Vector2(5, 0), Children = new Drawable[] { - flag = new ClickableUpdateableFlag + flag = new UpdateableFlag { Anchor = Anchor.Centre, Origin = Anchor.Centre, diff --git a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs index b68ecd709a..27d894cdc2 100644 --- a/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs +++ b/osu.Game/Overlays/Rankings/Tables/RankingsTable.cs @@ -96,7 +96,7 @@ namespace osu.Game.Overlays.Rankings.Tables Margin = new MarginPadding { Bottom = row_spacing }, Children = new[] { - new ClickableUpdateableFlag(GetCountryCode(item)) + new UpdateableFlag(GetCountryCode(item)) { Size = new Vector2(28, 20), ShowPlaceholderOnUnknown = false, diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs index 1f922073ec..c79c210e30 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Participants/ParticipantPanel.cs @@ -123,7 +123,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Participants FillMode = FillMode.Fit, User = user }, - new ClickableUpdateableFlag + new UpdateableFlag { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, diff --git a/osu.Game/Screens/Play/HUD/PlayerFlag.cs b/osu.Game/Screens/Play/HUD/PlayerFlag.cs index 7234db71b5..85799c03d3 100644 --- a/osu.Game/Screens/Play/HUD/PlayerFlag.cs +++ b/osu.Game/Screens/Play/HUD/PlayerFlag.cs @@ -12,14 +12,14 @@ namespace osu.Game.Screens.Play.HUD { public partial class PlayerFlag : CompositeDrawable, ISerialisableDrawable { - private readonly ClickableUpdateableFlag flag; + private readonly UpdateableFlag flag; private const float default_size = 40f; public PlayerFlag() { Size = new Vector2(default_size, default_size / 1.4f); - InternalChild = flag = new ClickableUpdateableFlag + InternalChild = flag = new UpdateableFlag { RelativeSizeAxes = Axes.Both, }; diff --git a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs b/osu.Game/Users/Drawables/UpdateableFlag.cs similarity index 91% rename from osu.Game/Users/Drawables/ClickableUpdateableFlag.cs rename to osu.Game/Users/Drawables/UpdateableFlag.cs index d19234fc17..8f8d7052e5 100644 --- a/osu.Game/Users/Drawables/ClickableUpdateableFlag.cs +++ b/osu.Game/Users/Drawables/UpdateableFlag.cs @@ -11,7 +11,7 @@ using osu.Game.Overlays; namespace osu.Game.Users.Drawables { - public partial class ClickableUpdateableFlag : ModelBackedDrawable + public partial class UpdateableFlag : ModelBackedDrawable { public CountryCode CountryCode { @@ -30,7 +30,7 @@ namespace osu.Game.Users.Drawables /// public Action? Action; - public ClickableUpdateableFlag(CountryCode countryCode = CountryCode.Unknown) + public UpdateableFlag(CountryCode countryCode = CountryCode.Unknown) { CountryCode = countryCode; } diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index e798c8cc11..3c1b68f9ef 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -52,7 +52,7 @@ namespace osu.Game.Users protected UpdateableAvatar CreateAvatar() => new UpdateableAvatar(User, false); - protected ClickableUpdateableFlag CreateFlag() => new ClickableUpdateableFlag(User.CountryCode) + protected UpdateableFlag CreateFlag() => new UpdateableFlag(User.CountryCode) { Size = new Vector2(36, 26), Action = Action, From 82fec4194d9cb676baa2ad8e35a863d21010394a Mon Sep 17 00:00:00 2001 From: yesseruser Date: Wed, 22 Nov 2023 15:45:32 +0100 Subject: [PATCH 0201/1118] Disabled RecievePositionalInputAtSubTree in PlayerFlag. --- osu.Game/Screens/Play/HUD/PlayerFlag.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/PlayerFlag.cs b/osu.Game/Screens/Play/HUD/PlayerFlag.cs index 85799c03d3..70ad078e34 100644 --- a/osu.Game/Screens/Play/HUD/PlayerFlag.cs +++ b/osu.Game/Screens/Play/HUD/PlayerFlag.cs @@ -12,6 +12,8 @@ namespace osu.Game.Screens.Play.HUD { public partial class PlayerFlag : CompositeDrawable, ISerialisableDrawable { + protected override bool ReceivePositionalInputAtSubTree(Vector2 screenSpacePos) => false; + private readonly UpdateableFlag flag; private const float default_size = 40f; From 9fd0641238fe8f21ade0528476245d2e23205f56 Mon Sep 17 00:00:00 2001 From: Rowe Wilson Frederisk Holme Date: Thu, 23 Nov 2023 01:18:55 +0800 Subject: [PATCH 0202/1118] Remove manual changes to Xcode versions in CI --- .github/workflows/ci.yml | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 11c956bb71..103e4dbc30 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -127,24 +127,14 @@ jobs: build-only-ios: name: Build only (iOS) - # `macos-13` is required, because Xcode 14.3 is required (see below). - # TODO: can be changed to `macos-latest` once `macos-13` becomes latest (currently in beta) + # `macos-13` is required, because the newest Microsoft.iOS.Sdk versions require Xcode 14.3. + # TODO: can be changed to `macos-latest` once `macos-13` becomes latest (currently in beta: https://github.com/actions/runner-images/tree/main#available-images) runs-on: macos-13 timeout-minutes: 60 steps: - name: Checkout uses: actions/checkout@v3 - # newest Microsoft.iOS.Sdk versions require Xcode 14.3. - # 14.3 is currently not the default Xcode version (https://github.com/actions/runner-images/blob/main/images/macos/macos-13-Readme.md#xcode), - # so set it manually. - # TODO: remove when 14.3 becomes the default Xcode version. - - name: Set Xcode version - shell: bash - run: | - sudo xcode-select -s "/Applications/Xcode_14.3.app" - echo "MD_APPLE_SDK_ROOT=/Applications/Xcode_14.3.app" >> $GITHUB_ENV - - name: Install .NET 6.0.x uses: actions/setup-dotnet@v3 with: From 3441a9a5b50bc5093dd06c232a96c4a4cbb5ccc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 Nov 2023 08:12:34 +0900 Subject: [PATCH 0203/1118] Add test coverage for classic scoring overflowing in osu! ruleset --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index cba90b2ebe..c957ddd7d3 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -10,14 +10,17 @@ using NUnit.Framework; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Difficulty; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.UI; using osu.Game.Scoring.Legacy; using osu.Game.Tests.Beatmaps; @@ -117,6 +120,35 @@ namespace osu.Game.Tests.Rulesets.Scoring Assert.That(scoreProcessor.GetDisplayScore(scoringMode), Is.EqualTo(expectedScore).Within(0.5d)); } + [TestCase(typeof(OsuRuleset))] + [TestCase(typeof(TaikoRuleset))] + [TestCase(typeof(CatchRuleset))] + [TestCase(typeof(ManiaRuleset))] + public void TestBeatmapWithALotOfObjectsDoesNotOverflowClassicScore(Type rulesetType) + { + const int object_count = 999999; + + var ruleset = (Ruleset)Activator.CreateInstance(rulesetType)!; + scoreProcessor = new ScoreProcessor(ruleset); + + var largeBeatmap = new TestBeatmap(ruleset.RulesetInfo) + { + HitObjects = new List(Enumerable.Repeat(new TestHitObject(HitResult.Great), object_count)) + }; + scoreProcessor.ApplyBeatmap(largeBeatmap); + + for (int i = 0; i < object_count; ++i) + { + var judgementResult = new JudgementResult(largeBeatmap.HitObjects[i], largeBeatmap.HitObjects[i].CreateJudgement()) + { + Type = HitResult.Great + }; + scoreProcessor.ApplyResult(judgementResult); + } + + Assert.That(scoreProcessor.GetDisplayScore(ScoringMode.Classic), Is.GreaterThan(0)); + } + [Test] public void TestEmptyBeatmap( [Values(ScoringMode.Standardised, ScoringMode.Classic)] From e28e0ef1cc5847ef4c596ea380c77270089bbcef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 Nov 2023 08:15:46 +0900 Subject: [PATCH 0204/1118] Fix classic scoring overflowing in osu! ruleset due to integer multiplication overflow Closes https://github.com/ppy/osu/issues/25545. --- osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs index f6ea5aa455..07c35a334f 100644 --- a/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs +++ b/osu.Game/Scoring/Legacy/ScoreInfoExtensions.cs @@ -50,7 +50,7 @@ namespace osu.Game.Scoring.Legacy switch (rulesetId) { case 0: - return (long)Math.Round((objectCount * objectCount * 32.57 + 100000) * standardisedTotalScore / ScoreProcessor.MAX_SCORE); + return (long)Math.Round((Math.Pow(objectCount, 2) * 32.57 + 100000) * standardisedTotalScore / ScoreProcessor.MAX_SCORE); case 1: return (long)Math.Round((objectCount * 1109 + 100000) * standardisedTotalScore / ScoreProcessor.MAX_SCORE); From 4edaaa30839a203eded9bfc16e1dee88b3fb6456 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 Nov 2023 09:45:38 +0900 Subject: [PATCH 0205/1118] Add test coverage of skin editor copy-paste --- .../Visual/Gameplay/TestSceneSkinEditor.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs index 9cd87932c8..3c97700fb0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinEditor.cs @@ -335,6 +335,40 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("value is less than default", () => hitErrorMeter.JudgementLineThickness.Value < hitErrorMeter.JudgementLineThickness.Default); } + [Test] + public void TestCopyPaste() + { + AddStep("paste", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.V); + InputManager.ReleaseKey(Key.LControl); + }); + // no assertions. just make sure nothing crashes. + + AddStep("select bar hit error blueprint", () => + { + var blueprint = skinEditor.ChildrenOfType().First(b => b.Item is BarHitErrorMeter); + skinEditor.SelectedComponents.Clear(); + skinEditor.SelectedComponents.Add(blueprint.Item); + }); + AddStep("copy", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.C); + InputManager.ReleaseKey(Key.LControl); + }); + AddStep("paste", () => + { + InputManager.PressKey(Key.LControl); + InputManager.Key(Key.V); + InputManager.ReleaseKey(Key.LControl); + }); + AddAssert("three hit error meters present", + () => skinEditor.ChildrenOfType().Count(b => b.Item is BarHitErrorMeter), + () => Is.EqualTo(3)); + } + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); private partial class TestSkinEditorChangeHandler : SkinEditorChangeHandler From abbcdaa7f7ad076053daa246668e5020fd8e3462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 Nov 2023 09:55:27 +0900 Subject: [PATCH 0206/1118] Fix skin editor crashing when pasting with nothing in clipboard --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index a816031668..f972186333 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -510,6 +510,9 @@ namespace osu.Game.Overlays.SkinEditor protected void Paste() { + if (!canPaste.Value) + return; + changeHandler?.BeginChange(); var drawableInfo = JsonConvert.DeserializeObject(clipboard.Content.Value); From e8d3d26d1607d381718ae0bf3650aa6ad6239c80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 Nov 2023 09:26:31 +0900 Subject: [PATCH 0207/1118] Fix slider length not updating when adding new anchor via ctrl-click --- .../Components/PathControlPointVisualiser.cs | 12 ++++----- .../Sliders/SliderSelectionBlueprint.cs | 27 +++++++++++++------ 2 files changed, 25 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 1a94d6253d..3add95b2b2 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -159,9 +159,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (allowSelection) d.RequestSelection = selectionRequested; - d.DragStarted = dragStarted; - d.DragInProgress = dragInProgress; - d.DragEnded = dragEnded; + d.DragStarted = DragStarted; + d.DragInProgress = DragInProgress; + d.DragEnded = DragEnded; })); Connections.Add(new PathControlPointConnectionPiece(hitObject, e.NewStartingIndex + i)); @@ -267,7 +267,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private int draggedControlPointIndex; private HashSet selectedControlPoints; - private void dragStarted(PathControlPoint controlPoint) + public void DragStarted(PathControlPoint controlPoint) { dragStartPositions = hitObject.Path.ControlPoints.Select(point => point.Position).ToArray(); dragPathTypes = hitObject.Path.ControlPoints.Select(point => point.Type).ToArray(); @@ -279,7 +279,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components changeHandler?.BeginChange(); } - private void dragInProgress(DragEvent e) + public void DragInProgress(DragEvent e) { Vector2[] oldControlPoints = hitObject.Path.ControlPoints.Select(cp => cp.Position).ToArray(); var oldPosition = hitObject.Position; @@ -341,7 +341,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components hitObject.Path.ControlPoints[i].Type = dragPathTypes[i]; } - private void dragEnded() => changeHandler?.EndChange(); + public void DragEnded() => changeHandler?.EndChange(); #endregion diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index 80c4cee7f2..a4b8064f05 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -39,9 +39,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders [CanBeNull] protected PathControlPointVisualiser ControlPointVisualiser { get; private set; } - [Resolved(CanBeNull = true)] - private IPositionSnapProvider positionSnapProvider { get; set; } - [Resolved(CanBeNull = true)] private IDistanceSnapProvider distanceSnapProvider { get; set; } @@ -191,15 +188,29 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders [CanBeNull] private PathControlPoint placementControlPoint; - protected override bool OnDragStart(DragStartEvent e) => placementControlPoint != null; + protected override bool OnDragStart(DragStartEvent e) + { + if (placementControlPoint == null) + return base.OnDragStart(e); + + ControlPointVisualiser?.DragStarted(placementControlPoint); + return true; + } protected override void OnDrag(DragEvent e) { + base.OnDrag(e); + if (placementControlPoint != null) - { - var result = positionSnapProvider?.FindSnappedPositionAndTime(ToScreenSpace(e.MousePosition)); - placementControlPoint.Position = ToLocalSpace(result?.ScreenSpacePosition ?? ToScreenSpace(e.MousePosition)) - HitObject.Position; - } + ControlPointVisualiser?.DragInProgress(e); + } + + protected override void OnDragEnd(DragEndEvent e) + { + base.OnDragEnd(e); + + if (placementControlPoint != null) + ControlPointVisualiser?.DragEnded(); } protected override void OnMouseUp(MouseUpEvent e) From 74966cae6ad9a98454731f19123a7044ff2d7e0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 Nov 2023 10:31:58 +0900 Subject: [PATCH 0208/1118] Remove unused using directive --- osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs index f810bbf155..16f28c0212 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using NUnit.Framework; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Timing; From 7998204cfe1e529bc684b54f64356882ec2c81a2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 23 Nov 2023 13:41:01 +0900 Subject: [PATCH 0209/1118] Fix combo/combo colouring issues around spinners --- .../Beatmaps/CatchBeatmapProcessor.cs | 16 ++++++ .../Objects/CatchHitObject.cs | 25 +++++++++ .../Beatmaps/OsuBeatmapProcessor.cs | 54 ++++++++++++------- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 25 +++++++++ .../Formats/LegacyBeatmapDecoderTest.cs | 48 +++++++++++++++++ .../Resources/spinner-between-objects.osu | 38 +++++++++++++ osu.Game/Beatmaps/BeatmapProcessor.cs | 6 --- .../Legacy/Catch/ConvertHitObjectParser.cs | 38 ++++--------- .../Legacy/Osu/ConvertHitObjectParser.cs | 38 ++++--------- 9 files changed, 210 insertions(+), 78 deletions(-) create mode 100644 osu.Game.Tests/Resources/spinner-between-objects.osu diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index ab61b14ac4..7c81ca03d1 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -23,6 +23,22 @@ namespace osu.Game.Rulesets.Catch.Beatmaps { } + public override void PreProcess() + { + IHasComboInformation? lastObj = null; + + // For sanity, ensures that both the first hitobject and the first hitobject after a banana shower start a new combo. + // This is normally enforced by the legacy decoder, but is not enforced by the editor. + foreach (var obj in Beatmap.HitObjects.OfType()) + { + if (obj is not BananaShower && (lastObj == null || lastObj is BananaShower)) + obj.NewCombo = true; + lastObj = obj; + } + + base.PreProcess(); + } + public override void PostProcess() { base.PostProcess(); diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index b9fef6bf8c..d122758a4e 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -155,6 +155,31 @@ namespace osu.Game.Rulesets.Catch.Objects Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize); } + public void UpdateComboInformation(IHasComboInformation? lastObj) + { + ComboIndex = lastObj?.ComboIndex ?? 0; + ComboIndexWithOffsets = lastObj?.ComboIndexWithOffsets ?? 0; + IndexInCurrentCombo = (lastObj?.IndexInCurrentCombo + 1) ?? 0; + + if (this is BananaShower) + { + // For the purpose of combo colours, spinners never start a new combo even if they are flagged as doing so. + return; + } + + // At decode time, the first hitobject in the beatmap and the first hitobject after a banana shower are both enforced to be a new combo, + // but this isn't directly enforced by the editor so the extra checks against the last hitobject are duplicated here. + if (NewCombo || lastObj == null || lastObj is BananaShower) + { + IndexInCurrentCombo = 0; + ComboIndex++; + ComboIndexWithOffsets += ComboOffset + 1; + + if (lastObj != null) + lastObj.LastInCombo = true; + } + } + protected override HitWindows CreateHitWindows() => HitWindows.Empty; #region Hit object conversion diff --git a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs index c081df3ac6..835c67ff19 100644 --- a/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapProcessor.cs @@ -2,9 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Graphics; using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osuTK; @@ -19,6 +21,22 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { } + public override void PreProcess() + { + IHasComboInformation? lastObj = null; + + // For sanity, ensures that both the first hitobject and the first hitobject after a spinner start a new combo. + // This is normally enforced by the legacy decoder, but is not enforced by the editor. + foreach (var obj in Beatmap.HitObjects.OfType()) + { + if (obj is not Spinner && (lastObj == null || lastObj is Spinner)) + obj.NewCombo = true; + lastObj = obj; + } + + base.PreProcess(); + } + public override void PostProcess() { base.PostProcess(); @@ -95,15 +113,15 @@ namespace osu.Game.Rulesets.Osu.Beatmaps { int n = i; /* We should check every note which has not yet got a stack. - * Consider the case we have two interwound stacks and this will make sense. - * - * o <-1 o <-2 - * o <-3 o <-4 - * - * We first process starting from 4 and handle 2, - * then we come backwards on the i loop iteration until we reach 3 and handle 1. - * 2 and 1 will be ignored in the i loop because they already have a stack value. - */ + * Consider the case we have two interwound stacks and this will make sense. + * + * o <-1 o <-2 + * o <-3 o <-4 + * + * We first process starting from 4 and handle 2, + * then we come backwards on the i loop iteration until we reach 3 and handle 1. + * 2 and 1 will be ignored in the i loop because they already have a stack value. + */ OsuHitObject objectI = beatmap.HitObjects[i]; if (objectI.StackHeight != 0 || objectI is Spinner) continue; @@ -111,9 +129,9 @@ namespace osu.Game.Rulesets.Osu.Beatmaps double stackThreshold = objectI.TimePreempt * beatmap.BeatmapInfo.StackLeniency; /* If this object is a hitcircle, then we enter this "special" case. - * It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider. - * Any other case is handled by the "is Slider" code below this. - */ + * It either ends with a stack of hitcircles only, or a stack of hitcircles that are underneath a slider. + * Any other case is handled by the "is Slider" code below this. + */ if (objectI is HitCircle) { while (--n >= 0) @@ -135,10 +153,10 @@ namespace osu.Game.Rulesets.Osu.Beatmaps } /* This is a special case where hticircles are moved DOWN and RIGHT (negative stacking) if they are under the *last* slider in a stacked pattern. - * o==o <- slider is at original location - * o <- hitCircle has stack of -1 - * o <- hitCircle has stack of -2 - */ + * o==o <- slider is at original location + * o <- hitCircle has stack of -1 + * o <- hitCircle has stack of -2 + */ if (objectN is Slider && Vector2Extensions.Distance(objectN.EndPosition, objectI.Position) < stack_distance) { int offset = objectI.StackHeight - objectN.StackHeight + 1; @@ -169,8 +187,8 @@ namespace osu.Game.Rulesets.Osu.Beatmaps else if (objectI is Slider) { /* We have hit the first slider in a possible stack. - * From this point on, we ALWAYS stack positive regardless. - */ + * From this point on, we ALWAYS stack positive regardless. + */ while (--n >= startIndex) { OsuHitObject objectN = beatmap.HitObjects[n]; diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index d74d28c748..716c34d024 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -159,6 +159,31 @@ namespace osu.Game.Rulesets.Osu.Objects Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize, true); } + public void UpdateComboInformation(IHasComboInformation? lastObj) + { + ComboIndex = lastObj?.ComboIndex ?? 0; + ComboIndexWithOffsets = lastObj?.ComboIndexWithOffsets ?? 0; + IndexInCurrentCombo = (lastObj?.IndexInCurrentCombo + 1) ?? 0; + + if (this is Spinner) + { + // For the purpose of combo colours, spinners never start a new combo even if they are flagged as doing so. + return; + } + + // At decode time, the first hitobject in the beatmap and the first hitobject after a spinner are both enforced to be a new combo, + // but this isn't directly enforced by the editor so the extra checks against the last hitobject are duplicated here. + if (NewCombo || lastObj == null || lastObj is Spinner) + { + IndexInCurrentCombo = 0; + ComboIndex++; + ComboIndexWithOffsets += ComboOffset + 1; + + if (lastObj != null) + lastObj.LastInCombo = true; + } + } + protected override HitWindows CreateHitWindows() => new OsuHitWindows(); } } diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index be1993957f..20f59384ab 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -12,6 +12,7 @@ using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Legacy; using osu.Game.Beatmaps.Timing; using osu.Game.IO; +using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Mods; @@ -1108,5 +1109,52 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(((IHasCombo)beatmap.HitObjects[2]).NewCombo, Is.False); } } + + /// + /// Test cases that involve a spinner between two hitobjects. + /// + [Test] + public void TestSpinnerNewComboBetweenObjects([Values("osu", "catch")] string rulesetName) + { + var decoder = new LegacyBeatmapDecoder { ApplyOffsets = false }; + + using (var resStream = TestResources.OpenResource("spinner-between-objects.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + Ruleset ruleset; + + switch (rulesetName) + { + case "osu": + ruleset = new OsuRuleset(); + break; + + case "catch": + ruleset = new CatchRuleset(); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(rulesetName), rulesetName, null); + } + + var working = new TestWorkingBeatmap(decoder.Decode(stream)); + var playable = working.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty()); + + // There's no good way to figure out these values other than to compare (in code) with osu!stable... + + Assert.That(((IHasComboInformation)playable.HitObjects[0]).ComboIndexWithOffsets, Is.EqualTo(1)); + Assert.That(((IHasComboInformation)playable.HitObjects[2]).ComboIndexWithOffsets, Is.EqualTo(2)); + Assert.That(((IHasComboInformation)playable.HitObjects[3]).ComboIndexWithOffsets, Is.EqualTo(2)); + Assert.That(((IHasComboInformation)playable.HitObjects[5]).ComboIndexWithOffsets, Is.EqualTo(3)); + Assert.That(((IHasComboInformation)playable.HitObjects[6]).ComboIndexWithOffsets, Is.EqualTo(3)); + Assert.That(((IHasComboInformation)playable.HitObjects[8]).ComboIndexWithOffsets, Is.EqualTo(4)); + Assert.That(((IHasComboInformation)playable.HitObjects[9]).ComboIndexWithOffsets, Is.EqualTo(4)); + Assert.That(((IHasComboInformation)playable.HitObjects[11]).ComboIndexWithOffsets, Is.EqualTo(5)); + Assert.That(((IHasComboInformation)playable.HitObjects[12]).ComboIndexWithOffsets, Is.EqualTo(6)); + Assert.That(((IHasComboInformation)playable.HitObjects[14]).ComboIndexWithOffsets, Is.EqualTo(7)); + Assert.That(((IHasComboInformation)playable.HitObjects[15]).ComboIndexWithOffsets, Is.EqualTo(8)); + Assert.That(((IHasComboInformation)playable.HitObjects[17]).ComboIndexWithOffsets, Is.EqualTo(9)); + } + } } } diff --git a/osu.Game.Tests/Resources/spinner-between-objects.osu b/osu.Game.Tests/Resources/spinner-between-objects.osu new file mode 100644 index 0000000000..03e61d965c --- /dev/null +++ b/osu.Game.Tests/Resources/spinner-between-objects.osu @@ -0,0 +1,38 @@ +osu file format v14 + +[General] +Mode: 0 + +[TimingPoints] +0,571.428571428571,4,2,1,5,1,0 + +[HitObjects] +// +C -> +C -> +C +104,95,0,5,0,0:0:0:0: +256,192,1000,12,0,2000,0:0:0:0: +178,171,3000,5,0,0:0:0:0: + +// -C -> +C -> +C +178,171,4000,1,0,0:0:0:0: +256,192,5000,12,0,6000,0:0:0:0: +178,171,7000,5,0,0:0:0:0: + +// -C -> -C -> +C +178,171,8000,1,0,0:0:0:0: +256,192,9000,8,0,10000,0:0:0:0: +178,171,11000,5,0,0:0:0:0: + +// -C -> -C -> -C +178,171,12000,1,0,0:0:0:0: +256,192,13000,8,0,14000,0:0:0:0: +178,171,15000,1,0,0:0:0:0: + +// +C -> -C -> -C +178,171,16000,5,0,0:0:0:0: +256,192,17000,8,0,18000,0:0:0:0: +178,171,19000,1,0,0:0:0:0: + +// +C -> +C -> -C +178,171,20000,5,0,0:0:0:0: +256,192,21000,12,0,22000,0:0:0:0: +178,171,23000,1,0,0:0:0:0: \ No newline at end of file diff --git a/osu.Game/Beatmaps/BeatmapProcessor.cs b/osu.Game/Beatmaps/BeatmapProcessor.cs index fb5313469f..89d6e9d3f8 100644 --- a/osu.Game/Beatmaps/BeatmapProcessor.cs +++ b/osu.Game/Beatmaps/BeatmapProcessor.cs @@ -24,12 +24,6 @@ namespace osu.Game.Beatmaps foreach (var obj in Beatmap.HitObjects.OfType()) { - if (lastObj == null) - { - // first hitobject should always be marked as a new combo for sanity. - obj.NewCombo = true; - } - obj.UpdateComboInformation(lastObj); lastObj = obj; } diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 4861e8b3f7..0ed5aef0cf 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -14,26 +14,19 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch /// public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser { + private ConvertHitObject lastObject; + public ConvertHitObjectParser(double offset, int formatVersion) : base(offset, formatVersion) { } - private bool forceNewCombo; - private int extraComboOffset; - protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset) { - newCombo |= forceNewCombo; - comboOffset += extraComboOffset; - - forceNewCombo = false; - extraComboOffset = 0; - - return new ConvertHit + return lastObject = new ConvertHit { Position = position, - NewCombo = newCombo, + NewCombo = FirstObject || lastObject is ConvertSpinner || newCombo, ComboOffset = comboOffset }; } @@ -41,16 +34,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, IList> nodeSamples) { - newCombo |= forceNewCombo; - comboOffset += extraComboOffset; - - forceNewCombo = false; - extraComboOffset = 0; - - return new ConvertSlider + return lastObject = new ConvertSlider { Position = position, - NewCombo = FirstObject || newCombo, + NewCombo = FirstObject || lastObject is ConvertSpinner || newCombo, ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, @@ -60,20 +47,17 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { - // Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo - // Their combo offset is still added to that next hitobject's combo index - forceNewCombo |= FormatVersion <= 8 || newCombo; - extraComboOffset += comboOffset; - - return new ConvertSpinner + return lastObject = new ConvertSpinner { - Duration = duration + Duration = duration, + NewCombo = newCombo + // Spinners cannot have combo offset. }; } protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { - return null; + return lastObject = null; } } } diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 7a88a31bd5..8bb9600a7d 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -14,26 +14,19 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu /// public class ConvertHitObjectParser : Legacy.ConvertHitObjectParser { + private ConvertHitObject lastObject; + public ConvertHitObjectParser(double offset, int formatVersion) : base(offset, formatVersion) { } - private bool forceNewCombo; - private int extraComboOffset; - protected override HitObject CreateHit(Vector2 position, bool newCombo, int comboOffset) { - newCombo |= forceNewCombo; - comboOffset += extraComboOffset; - - forceNewCombo = false; - extraComboOffset = 0; - - return new ConvertHit + return lastObject = new ConvertHit { Position = position, - NewCombo = FirstObject || newCombo, + NewCombo = FirstObject || lastObject is ConvertSpinner || newCombo, ComboOffset = comboOffset }; } @@ -41,16 +34,10 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu protected override HitObject CreateSlider(Vector2 position, bool newCombo, int comboOffset, PathControlPoint[] controlPoints, double? length, int repeatCount, IList> nodeSamples) { - newCombo |= forceNewCombo; - comboOffset += extraComboOffset; - - forceNewCombo = false; - extraComboOffset = 0; - - return new ConvertSlider + return lastObject = new ConvertSlider { Position = position, - NewCombo = FirstObject || newCombo, + NewCombo = FirstObject || lastObject is ConvertSpinner || newCombo, ComboOffset = comboOffset, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, @@ -60,21 +47,18 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu protected override HitObject CreateSpinner(Vector2 position, bool newCombo, int comboOffset, double duration) { - // Convert spinners don't create the new combo themselves, but force the next non-spinner hitobject to create a new combo - // Their combo offset is still added to that next hitobject's combo index - forceNewCombo |= FormatVersion <= 8 || newCombo; - extraComboOffset += comboOffset; - - return new ConvertSpinner + return lastObject = new ConvertSpinner { Position = position, - Duration = duration + Duration = duration, + NewCombo = newCombo + // Spinners cannot have combo offset. }; } protected override HitObject CreateHold(Vector2 position, bool newCombo, int comboOffset, double duration) { - return null; + return lastObject = null; } } } From 3da8a0cbed624319841b479e2cced91fa8608cd5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 23 Nov 2023 14:00:42 +0900 Subject: [PATCH 0210/1118] Fix undo being broken when ctrl-click and dragging new point --- .../Blueprints/Sliders/SliderSelectionBlueprint.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index a4b8064f05..b3efe1c495 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -205,18 +205,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders ControlPointVisualiser?.DragInProgress(e); } - protected override void OnDragEnd(DragEndEvent e) - { - base.OnDragEnd(e); - - if (placementControlPoint != null) - ControlPointVisualiser?.DragEnded(); - } - protected override void OnMouseUp(MouseUpEvent e) { if (placementControlPoint != null) { + if (IsDragged) + ControlPointVisualiser?.DragEnded(); + placementControlPoint = null; changeHandler?.EndChange(); } From 191e8c5487ccd4ea1b85aa7f47e6454f5a180c1d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Nov 2023 16:39:05 +0900 Subject: [PATCH 0211/1118] Add note about skin editor reload jank --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index cbe122395c..d1e7b97efc 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -210,6 +210,9 @@ namespace osu.Game.Overlays.SkinEditor // The skin editor doesn't work well if beatmap skins are being applied to the player screen. // To keep things simple, disable the setting game-wide while using the skin editor. + // + // This causes a full reload of the skin, which is pretty ugly. + // TODO: Investigate if we can avoid this when a beatmap skin is not being applied by the current beatmap. leasedBeatmapSkins = beatmapSkins.BeginLease(true); leasedBeatmapSkins.Value = false; } From a80a5be4ec01c179069b57ee384a7196670a0f85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Nov 2023 17:11:40 +0900 Subject: [PATCH 0212/1118] Fix a couple of new r# inspections --- osu.Game/Beatmaps/Beatmap.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 4f81b26c3e..0cafd4e08a 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -119,12 +119,11 @@ namespace osu.Game.Beatmaps IBeatmap IBeatmap.Clone() => Clone(); public Beatmap Clone() => (Beatmap)MemberwiseClone(); + + public override string ToString() => BeatmapInfo.ToString(); } public class Beatmap : Beatmap { - public new Beatmap Clone() => (Beatmap)base.Clone(); - - public override string ToString() => BeatmapInfo?.ToString() ?? base.ToString(); } } From 5239fee947435bf4fe99b43d28a7fe0118ca64b8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Nov 2023 17:15:03 +0900 Subject: [PATCH 0213/1118] Allow use of skin username/flag/avatar components outside of gameplay --- osu.Game/Screens/Play/HUD/PlayerAvatar.cs | 20 ++++++++++++++++++-- osu.Game/Screens/Play/HUD/PlayerFlag.cs | 22 ++++++++++++++++++++-- osu.Game/Skinning/Components/PlayerName.cs | 21 +++++++++++++++++++-- 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs index 1341a10d60..06d0f7bc9a 100644 --- a/osu.Game/Screens/Play/HUD/PlayerAvatar.cs +++ b/osu.Game/Screens/Play/HUD/PlayerAvatar.cs @@ -7,6 +7,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Configuration; using osu.Game.Localisation.SkinComponents; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Settings; using osu.Game.Skinning; using osu.Game.Users.Drawables; @@ -29,6 +31,14 @@ namespace osu.Game.Screens.Play.HUD private const float default_size = 80f; + [Resolved] + private GameplayState? gameplayState { get; set; } + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + private IBindable? apiUser; + public PlayerAvatar() { Size = new Vector2(default_size); @@ -41,9 +51,15 @@ namespace osu.Game.Screens.Play.HUD } [BackgroundDependencyLoader] - private void load(GameplayState gameplayState) + private void load() { - avatar.User = gameplayState.Score.ScoreInfo.User; + if (gameplayState != null) + avatar.User = gameplayState.Score.ScoreInfo.User; + else + { + apiUser = api.LocalUser.GetBoundCopy(); + apiUser.BindValueChanged(u => avatar.User = u.NewValue, true); + } } protected override void LoadComplete() diff --git a/osu.Game/Screens/Play/HUD/PlayerFlag.cs b/osu.Game/Screens/Play/HUD/PlayerFlag.cs index 70ad078e34..c7e247d26a 100644 --- a/osu.Game/Screens/Play/HUD/PlayerFlag.cs +++ b/osu.Game/Screens/Play/HUD/PlayerFlag.cs @@ -2,8 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Skinning; using osu.Game.Users.Drawables; using osuTK; @@ -18,9 +21,18 @@ namespace osu.Game.Screens.Play.HUD private const float default_size = 40f; + [Resolved] + private GameplayState? gameplayState { get; set; } + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + private IBindable? apiUser; + public PlayerFlag() { Size = new Vector2(default_size, default_size / 1.4f); + InternalChild = flag = new UpdateableFlag { RelativeSizeAxes = Axes.Both, @@ -28,9 +40,15 @@ namespace osu.Game.Screens.Play.HUD } [BackgroundDependencyLoader] - private void load(GameplayState gameplayState) + private void load() { - flag.CountryCode = gameplayState.Score.ScoreInfo.User.CountryCode; + if (gameplayState != null) + flag.CountryCode = gameplayState.Score.ScoreInfo.User.CountryCode; + else + { + apiUser = api.LocalUser.GetBoundCopy(); + apiUser.BindValueChanged(u => flag.CountryCode = u.NewValue.CountryCode, true); + } } public bool UsesFixedAnchor { get; set; } diff --git a/osu.Game/Skinning/Components/PlayerName.cs b/osu.Game/Skinning/Components/PlayerName.cs index 34ace53d47..21bf615bc6 100644 --- a/osu.Game/Skinning/Components/PlayerName.cs +++ b/osu.Game/Skinning/Components/PlayerName.cs @@ -3,9 +3,12 @@ using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Game.Graphics.Sprites; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens.Play; namespace osu.Game.Skinning.Components @@ -15,6 +18,14 @@ namespace osu.Game.Skinning.Components { private readonly OsuSpriteText text; + [Resolved] + private GameplayState? gameplayState { get; set; } + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + private IBindable? apiUser; + public PlayerName() { AutoSizeAxes = Axes.Both; @@ -30,9 +41,15 @@ namespace osu.Game.Skinning.Components } [BackgroundDependencyLoader] - private void load(GameplayState gameplayState) + private void load() { - text.Text = gameplayState.Score.ScoreInfo.User.Username; + if (gameplayState != null) + text.Text = gameplayState.Score.ScoreInfo.User.Username; + else + { + apiUser = api.LocalUser.GetBoundCopy(); + apiUser.BindValueChanged(u => text.Text = u.NewValue.Username, true); + } } protected override void SetFont(FontUsage font) => text.Font = font.With(size: 40); From 268b965ee890a3ea10ea152465eae6faede94e6c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Nov 2023 17:28:16 +0900 Subject: [PATCH 0214/1118] Enable NRT on `Beatmap` --- osu.Game/Beatmaps/Beatmap.cs | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/osu.Game/Beatmaps/Beatmap.cs b/osu.Game/Beatmaps/Beatmap.cs index 0cafd4e08a..6db9febf36 100644 --- a/osu.Game/Beatmaps/Beatmap.cs +++ b/osu.Game/Beatmaps/Beatmap.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Objects; @@ -26,8 +24,7 @@ namespace osu.Game.Beatmaps { difficulty = value; - if (beatmapInfo != null) - beatmapInfo.Difficulty = difficulty.Clone(); + beatmapInfo.Difficulty = difficulty.Clone(); } } @@ -40,8 +37,7 @@ namespace osu.Game.Beatmaps { beatmapInfo = value; - if (beatmapInfo?.Difficulty != null) - Difficulty = beatmapInfo.Difficulty.Clone(); + Difficulty = beatmapInfo.Difficulty.Clone(); } } From c2a44cf1185dd28897f5488829124d7533cfb337 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Thu, 23 Nov 2023 23:30:18 +0200 Subject: [PATCH 0215/1118] Made custom tooltip --- .../Mods/AdjustedAttributesTooltip.cs | 158 ++++++++++++++++++ .../Overlays/Mods/BeatmapAttributesDisplay.cs | 46 ++--- .../Screens/Select/Details/AdvancedStats.cs | 38 ++--- 3 files changed, 188 insertions(+), 54 deletions(-) create mode 100644 osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs new file mode 100644 index 0000000000..e6a554d1fb --- /dev/null +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -0,0 +1,158 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Framework.Utils; +using osuTK; + +namespace osu.Game.Overlays.Mods +{ + public partial class AdjustedAttributesTooltip : CompositeDrawable, ITooltip + { + private Dictionary> attributes = new Dictionary>(); + + private Container content; + + private FillFlowContainer attributesFillFlow; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + + public AdjustedAttributesTooltip() + { + // Need to be initialized in constructor to ensure accessability in AddAttribute function + InternalChild = content = new Container + { + AutoSizeAxes = Axes.Both + }; + attributesFillFlow = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both + }; + } + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + Masking = true; + CornerRadius = 15; + + content.AddRange(new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray1, + Alpha = 0.8f + }, + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding {Vertical = 10, Horizontal = 15}, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "One or more values are being adjusted by mods that change speed.", + }, + attributesFillFlow + } + } + }); + } + + private void checkAttributes() + { + foreach (var attribute in attributes) + { + if (!Precision.AlmostEquals(attribute.Value.Value.Old, attribute.Value.Value.New)) + { + content.Show(); + return; + } + } + content.Hide(); + } + + public void AddAttribute(string name) + { + Bindable newBindable = new Bindable(); + newBindable.BindValueChanged(_ => checkAttributes()); + attributes.Add(name, newBindable); + attributesFillFlow.Add(new AttributeDisplay(name, newBindable.GetBoundCopy())); + } + public void UpdateAttribute(string name, double oldValue, double newValue) + { + Bindable attribute = attributes[name]; + + OldNewPair attributeValue = attribute.Value; + attributeValue.Old = oldValue; + attributeValue.New = newValue; + + attribute.Value = attributeValue; + } + + protected override void Update() + { + } + public void SetContent(object content) + { + } + + public void Move(Vector2 pos) + { + Position = pos; + } + + private struct OldNewPair + { + public double Old, New; + } + + private partial class AttributeDisplay : CompositeDrawable + { + public Bindable AttributeValues = new Bindable(); + public string AttributeName; + + private OsuSpriteText text = new OsuSpriteText + { + Font = OsuFont.Default.With(weight: FontWeight.Bold) + }; + public AttributeDisplay(string name, Bindable boundCopy) + { + AutoSizeAxes = Axes.Both; + + AttributeName = name; + AttributeValues = boundCopy; + InternalChild = text; + AttributeValues.BindValueChanged(_ => update(), true); + } + + private void update() + { + if (Precision.AlmostEquals(AttributeValues.Value.Old, AttributeValues.Value.New)) + { + Hide(); + } + else + { + Show(); + text.Text = $"{AttributeName}: {(AttributeValues.Value.Old):0.0#} → {(AttributeValues.Value.New):0.0#}"; + } + } + } + } +} diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 6270b2bb49..fa8f9cb7a4 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Cursor; using osu.Framework.Input.Events; using osu.Framework.Localisation; -using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Drawables; using osu.Game.Configuration; @@ -28,7 +27,7 @@ namespace osu.Game.Overlays.Mods /// On the mod select overlay, this provides a local updating view of BPM, star rating and other /// difficulty attributes so the user can have a better insight into what mods are changing. /// - public partial class BeatmapAttributesDisplay : ModFooterInformationDisplay, IHasTooltip + public partial class BeatmapAttributesDisplay : ModFooterInformationDisplay, IHasCustomTooltip { private StarRatingDisplay starRatingDisplay = null!; private BPMDisplay bpmDisplay = null!; @@ -58,10 +57,11 @@ namespace osu.Game.Overlays.Mods private CancellationTokenSource? cancellationSource; private IBindable starDifficulty = null!; - private BeatmapDifficulty? originalDifficulty; - private BeatmapDifficulty? adjustedDifficulty; + private AdjustedAttributesTooltip rateAdjustTooltip = null!; + + public ITooltip GetCustomTooltip() => rateAdjustTooltip; + public object TooltipContent => this; - private bool haveRateChangedValues; private const float transition_duration = 250; @@ -70,6 +70,8 @@ namespace osu.Game.Overlays.Mods { const float shear = ShearedOverlayContainer.SHEAR; + rateAdjustTooltip = new AdjustedAttributesTooltip(); + LeftContent.AddRange(new Drawable[] { starRatingDisplay = new StarRatingDisplay(default, animated: true) @@ -105,7 +107,6 @@ namespace osu.Game.Overlays.Mods mods.BindValueChanged(_ => { modSettingChangeTracker?.Dispose(); - modSettingChangeTracker = new ModSettingChangeTracker(mods.Value); modSettingChangeTracker.SettingChanged += _ => updateValues(); updateValues(); @@ -125,6 +126,9 @@ namespace osu.Game.Overlays.Mods BeatmapInfo.BindValueChanged(_ => updateValues(), true); + rateAdjustTooltip.AddAttribute("AR"); + rateAdjustTooltip.AddAttribute("OD"); + updateCollapsedState(); } @@ -173,15 +177,16 @@ namespace osu.Game.Overlays.Mods bpmDisplay.Current.Value = BeatmapInfo.Value.BPM * rate; - originalDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); + BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(BeatmapInfo.Value.Difficulty); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(originalDifficulty); Ruleset ruleset = gameRuleset.Value.CreateInstance(); - adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); + BeatmapDifficulty adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); - haveRateChangedValues = hasRateAdjustedProperties(originalDifficulty, adjustedDifficulty); + rateAdjustTooltip.UpdateAttribute("AR", originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); + rateAdjustTooltip.UpdateAttribute("OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); @@ -197,29 +202,6 @@ namespace osu.Game.Overlays.Mods RightContent.FadeTo(Collapsed.Value && !IsHovered ? 0 : 1, transition_duration, Easing.OutQuint); } - public LocalisableString TooltipText - { - get - { - if (haveRateChangedValues) - { - return $"One or more values are being adjusted by mods that change speed." + - $" (AR {originalDifficulty?.ApproachRate ?? 0}→{(adjustedDifficulty?.ApproachRate ?? 0):0.0#}, " + - $"OD {originalDifficulty?.OverallDifficulty ?? 0}→{(adjustedDifficulty?.OverallDifficulty ?? 0):0.0#})"; - } - - return string.Empty; - } - } - - private static bool hasRateAdjustedProperties(BeatmapDifficulty a, BeatmapDifficulty b) - { - if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate)) return true; - if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty)) return true; - - return false; - } - private partial class BPMDisplay : RollingCounter { protected override double RollingDuration => 500; diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index f617cb1d8e..d3a6a88c68 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -26,10 +26,11 @@ using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; +using osu.Game.Overlays.Mods; namespace osu.Game.Screens.Select.Details { - public partial class AdvancedStats : Container, IHasTooltip + public partial class AdvancedStats : Container, IHasCustomTooltip { [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -45,9 +46,9 @@ namespace osu.Game.Screens.Select.Details protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; - private BeatmapDifficulty originalDifficulty; - private BeatmapDifficulty adjustedDifficulty; - private bool haveRateChangedValues; + private AdjustedAttributesTooltip rateAdjustTooltip; + public ITooltip GetCustomTooltip() => rateAdjustTooltip; + public object TooltipContent => this; private IBeatmapInfo beatmapInfo; @@ -85,6 +86,7 @@ namespace osu.Game.Screens.Select.Details private void load(OsuColour colours) { starDifficulty.AccentColour = colours.Yellow; + rateAdjustTooltip = new AdjustedAttributesTooltip(); } protected override void LoadComplete() @@ -99,6 +101,9 @@ namespace osu.Game.Screens.Select.Details gameRuleset.BindValueChanged(_ => updateStatistics()); mods.BindValueChanged(modsChanged, true); + + rateAdjustTooltip.AddAttribute("AR"); + rateAdjustTooltip.AddAttribute("OD"); } private ModSettingChangeTracker modSettingChangeTracker; @@ -121,14 +126,17 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; + BeatmapDifficulty adjustedDifficulty = null; if (baseDifficulty != null) { - originalDifficulty = new BeatmapDifficulty(baseDifficulty); + BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(originalDifficulty); + adjustedDifficulty = originalDifficulty; + if (gameRuleset != null) { Ruleset ruleset = gameRuleset.Value.CreateInstance(); @@ -138,7 +146,9 @@ namespace osu.Game.Screens.Select.Details rate = mod.ApplyToRate(0, rate); adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); - haveRateChangedValues = hasRateAdjustedProperties(originalDifficulty, adjustedDifficulty); + + rateAdjustTooltip.UpdateAttribute("AR", originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); + rateAdjustTooltip.UpdateAttribute("OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); } } @@ -204,22 +214,6 @@ namespace osu.Game.Screens.Select.Details starDifficultyCancellationSource?.Cancel(); } - public LocalisableString TooltipText - { - get - { - if (haveRateChangedValues) - { - // Rather than localising this, it should be displayed in a better way (a custom tooltip which isn't a single super-long line). - return "One or more values are being adjusted by mods that change speed." + - $" (AR {originalDifficulty?.ApproachRate ?? 0}→{(adjustedDifficulty?.ApproachRate ?? 0):0.0#}, " + - $"OD {originalDifficulty?.OverallDifficulty ?? 0}→{(adjustedDifficulty?.OverallDifficulty ?? 0):0.0#})"; - } - - return string.Empty; - } - } - private static bool hasRateAdjustedProperties(BeatmapDifficulty a, BeatmapDifficulty b) { if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate)) return true; From 93e3156868d674dd3f9a79729c5561859d96f063 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Fri, 24 Nov 2023 01:07:37 +0200 Subject: [PATCH 0216/1118] slight format changes --- osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs index e6a554d1fb..3e800bfaf1 100644 --- a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -26,7 +26,6 @@ namespace osu.Game.Overlays.Mods [Resolved] private OsuColour colours { get; set; } = null!; - public AdjustedAttributesTooltip() { // Need to be initialized in constructor to ensure accessability in AddAttribute function @@ -60,7 +59,7 @@ namespace osu.Game.Overlays.Mods new FillFlowContainer { AutoSizeAxes = Axes.Both, - Padding = new MarginPadding {Vertical = 10, Horizontal = 15}, + Padding = new MarginPadding { Vertical = 10, Horizontal = 15 }, Direction = FillDirection.Vertical, Children = new Drawable[] { @@ -85,6 +84,7 @@ namespace osu.Game.Overlays.Mods } } content.Hide(); + } public void AddAttribute(string name) @@ -94,6 +94,7 @@ namespace osu.Game.Overlays.Mods attributes.Add(name, newBindable); attributesFillFlow.Add(new AttributeDisplay(name, newBindable.GetBoundCopy())); } + public void UpdateAttribute(string name, double oldValue, double newValue) { Bindable attribute = attributes[name]; @@ -124,7 +125,7 @@ namespace osu.Game.Overlays.Mods private partial class AttributeDisplay : CompositeDrawable { - public Bindable AttributeValues = new Bindable(); + public readonly Bindable AttributeValues; public string AttributeName; private OsuSpriteText text = new OsuSpriteText From 10e16e4b04c58774573c1b8fff66ee46b717bcdc Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 24 Nov 2023 09:46:06 +0900 Subject: [PATCH 0217/1118] Fix handling of combo offset without new combo, and incorrect lazer tests --- .../Formats/LegacyBeatmapDecoderTest.cs | 24 +++++++++---------- .../Resources/hitobject-combo-offset.osu | 12 +++++----- .../Legacy/Catch/ConvertHitObjectParser.cs | 4 ++-- .../Legacy/Osu/ConvertHitObjectParser.cs | 4 ++-- 4 files changed, 22 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index cccceaf58e..dcfe8ecb41 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -434,12 +434,12 @@ namespace osu.Game.Tests.Beatmaps.Formats new OsuBeatmapProcessor(converted).PreProcess(); new OsuBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndexWithOffsets); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndexWithOffsets); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndexWithOffsets); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndexWithOffsets); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndexWithOffsets); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndexWithOffsets); + Assert.AreEqual(1, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndexWithOffsets); + Assert.AreEqual(2, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndexWithOffsets); + Assert.AreEqual(3, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndexWithOffsets); + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndexWithOffsets); + Assert.AreEqual(8, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndexWithOffsets); + Assert.AreEqual(9, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndexWithOffsets); } } @@ -457,12 +457,12 @@ namespace osu.Game.Tests.Beatmaps.Formats new CatchBeatmapProcessor(converted).PreProcess(); new CatchBeatmapProcessor(converted).PostProcess(); - Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndexWithOffsets); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndexWithOffsets); - Assert.AreEqual(5, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndexWithOffsets); - Assert.AreEqual(6, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndexWithOffsets); - Assert.AreEqual(11, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndexWithOffsets); - Assert.AreEqual(14, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndexWithOffsets); + Assert.AreEqual(1, ((IHasComboInformation)converted.HitObjects.ElementAt(0)).ComboIndexWithOffsets); + Assert.AreEqual(2, ((IHasComboInformation)converted.HitObjects.ElementAt(2)).ComboIndexWithOffsets); + Assert.AreEqual(3, ((IHasComboInformation)converted.HitObjects.ElementAt(4)).ComboIndexWithOffsets); + Assert.AreEqual(4, ((IHasComboInformation)converted.HitObjects.ElementAt(6)).ComboIndexWithOffsets); + Assert.AreEqual(8, ((IHasComboInformation)converted.HitObjects.ElementAt(8)).ComboIndexWithOffsets); + Assert.AreEqual(9, ((IHasComboInformation)converted.HitObjects.ElementAt(11)).ComboIndexWithOffsets); } } diff --git a/osu.Game.Tests/Resources/hitobject-combo-offset.osu b/osu.Game.Tests/Resources/hitobject-combo-offset.osu index d39a3e8548..9f39229d87 100644 --- a/osu.Game.Tests/Resources/hitobject-combo-offset.osu +++ b/osu.Game.Tests/Resources/hitobject-combo-offset.osu @@ -3,30 +3,30 @@ osu file format v14 [HitObjects] // Circle with combo offset (3) 255,193,1000,49,0,0:0:0:0: -// Combo index = 4 +// Combo index = 1 // Spinner with new combo followed by circle with no new combo 256,192,2000,12,0,2000,0:0:0:0: 255,193,3000,1,0,0:0:0:0: -// Combo index = 5 +// Combo index = 2 // Spinner without new combo followed by circle with no new combo 256,192,4000,8,0,5000,0:0:0:0: 255,193,6000,1,0,0:0:0:0: -// Combo index = 5 +// Combo index = 3 // Spinner without new combo followed by circle with new combo 256,192,7000,8,0,8000,0:0:0:0: 255,193,9000,5,0,0:0:0:0: -// Combo index = 6 +// Combo index = 4 // Spinner with new combo and offset (1) followed by circle with new combo and offset (3) 256,192,10000,28,0,11000,0:0:0:0: 255,193,12000,53,0,0:0:0:0: -// Combo index = 11 +// Combo index = 8 // Spinner with new combo and offset (2) followed by slider with no new combo followed by circle with no new combo 256,192,13000,44,0,14000,0:0:0:0: 256,192,15000,8,0,16000,0:0:0:0: 255,193,17000,1,0,0:0:0:0: -// Combo index = 14 \ No newline at end of file +// Combo index = 9 \ No newline at end of file diff --git a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs index 0ed5aef0cf..a5c1a73fa7 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Catch/ConvertHitObjectParser.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch { Position = position, NewCombo = FirstObject || lastObject is ConvertSpinner || newCombo, - ComboOffset = comboOffset + ComboOffset = newCombo ? comboOffset : 0 }; } @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Catch { Position = position, NewCombo = FirstObject || lastObject is ConvertSpinner || newCombo, - ComboOffset = comboOffset, + ComboOffset = newCombo ? comboOffset : 0, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount diff --git a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs index 8bb9600a7d..43c346b621 100644 --- a/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs +++ b/osu.Game/Rulesets/Objects/Legacy/Osu/ConvertHitObjectParser.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || lastObject is ConvertSpinner || newCombo, - ComboOffset = comboOffset + ComboOffset = newCombo ? comboOffset : 0 }; } @@ -38,7 +38,7 @@ namespace osu.Game.Rulesets.Objects.Legacy.Osu { Position = position, NewCombo = FirstObject || lastObject is ConvertSpinner || newCombo, - ComboOffset = comboOffset, + ComboOffset = newCombo ? comboOffset : 0, Path = new SliderPath(controlPoints, length), NodeSamples = nodeSamples, RepeatCount = repeatCount From 039f8e62421d13d23f532e146cadd97ec89e78d1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 10:25:23 +0900 Subject: [PATCH 0218/1118] Add note about shared code --- osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs | 2 ++ osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 2 ++ 2 files changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index d122758a4e..17ff8afb87 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -157,6 +157,8 @@ namespace osu.Game.Rulesets.Catch.Objects public void UpdateComboInformation(IHasComboInformation? lastObj) { + // Note that this implementation is shared with the osu! ruleset's implementation. + // If a change is made here, OsuHitObject.cs should also be updated. ComboIndex = lastObj?.ComboIndex ?? 0; ComboIndexWithOffsets = lastObj?.ComboIndexWithOffsets ?? 0; IndexInCurrentCombo = (lastObj?.IndexInCurrentCombo + 1) ?? 0; diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 716c34d024..21f7b4b22d 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -161,6 +161,8 @@ namespace osu.Game.Rulesets.Osu.Objects public void UpdateComboInformation(IHasComboInformation? lastObj) { + // Note that this implementation is shared with the osu!catch ruleset's implementation. + // If a change is made here, CatchHitObject.cs should also be updated. ComboIndex = lastObj?.ComboIndex ?? 0; ComboIndexWithOffsets = lastObj?.ComboIndexWithOffsets ?? 0; IndexInCurrentCombo = (lastObj?.IndexInCurrentCombo + 1) ?? 0; From 7590bae445b331aa9d5d2bb4c1dd3730a9cb97c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 22 Nov 2023 12:50:08 +0900 Subject: [PATCH 0219/1118] Rename and comment everything in score migration code Hopefully, _hopefully_, makes all this a little bit less inscrutable. --- .../StandardisedScoreMigrationTools.cs | 98 ++++++++++++------- 1 file changed, 62 insertions(+), 36 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index fe51c6f130..77421908de 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -274,53 +274,79 @@ namespace osu.Game.Database + bonusProportion) * modMultiplier); } - // Assumption : - // - sliders and slider-ticks are uniformly spread arround the beatmap, and thus can be ignored without losing much precision. - // We thus consider a map of hit-circles only, which gives objectCount == maximumCombo ! + // Assumptions: + // - sliders and slider ticks are uniformly distributed in the beatmap, and thus can be ignored without losing much precision. + // We thus consider a map of hit-circles only, which gives objectCount == maximumCombo. // - the Ok/Meh hit results are uniformly spread in the score, and thus can be ignored without losing much precision. - // We simplify and consider each hit result to be equal to `300*score.Accuracy`, which allows us to isolate the accuracy multiplier. - // What is strippedV1/strippedNew : - // This is the ComboScore (of v1 / new score) were we remove all (map-)constant multipliers and accuracy multipliers (including hit results), - // based on the previous assumptions. - // We use integrals to approximate the sum of each object's combo contribution (thus the original combo exponent is increased by 1) - // For the maximum score, we thus integrate `f(combo) = 300*combo^exponent`, and ignoring the constant multipliers we get: - double maxStrippedV1 = Math.Pow(maximumLegacyCombo, 2); - double maxStrippedNew = Math.Pow(maximumLegacyCombo, 1 + ScoreProcessor.COMBO_EXPONENT); + // We simplify and consider each hit result to have the same hit value of `300 * score.Accuracy` + // (which represents the average hit value over the entire play), + // which allows us to isolate the accuracy multiplier. - double strippedV1 = maxStrippedV1 * comboProportion / score.Accuracy; + // This is a very ballpark estimate of the maximum magnitude of the combo portion in score V1. + // It is derived by assuming a full combo play and summing up the contribution to combo portion from each individual object. + // Because each object's combo contribution is proportional to the current combo at the time of judgement, + // this can be roughly represented by summing / integrating f(combo) = combo. + // All mod- and beatmap-dependent multipliers and constants are not included here, + // as we will only be using the magnitude of this to compute ratios. + double maximumAchievableComboPortionInScoreV1 = Math.Pow(maximumLegacyCombo, 2); + // Similarly, estimate the maximum magnitude of the combo portion in standardised score. + // Roughly corresponds to integrating f(combo) = combo ^ COMBO_EXPONENT (omitting constants) + double maximumAchievableComboPortionInStandardisedScore = Math.Pow(maximumLegacyCombo, 1 + ScoreProcessor.COMBO_EXPONENT); - double strippedV1FromMaxCombo = Math.Pow(score.MaxCombo, 2); - double strippedNewFromMaxCombo = Math.Pow(score.MaxCombo, 1 + ScoreProcessor.COMBO_EXPONENT); + double comboPortionInScoreV1 = maximumAchievableComboPortionInScoreV1 * comboProportion / score.Accuracy; - // Compute approximate lower estimate new score for that play - // That is, a play were we made biggest amount of big combos (Repeat MaxCombo + 1 remaining big combo) - // And didn't combo anything in the reminder of the map - double possibleMaxComboRepeat = Math.Floor(strippedV1 / strippedV1FromMaxCombo); - double strippedV1FromMaxComboRepeat = possibleMaxComboRepeat * strippedV1FromMaxCombo; - double remainingStrippedV1 = strippedV1 - strippedV1FromMaxComboRepeat; - double remainingCombo = Math.Sqrt(remainingStrippedV1); - double remainingStrippedNew = Math.Pow(remainingCombo, 1 + ScoreProcessor.COMBO_EXPONENT); + // This is - roughly - how much score, in the combo portion, the longest combo on this particular play would gain in score V1. + double comboPortionFromLongestComboInScoreV1 = Math.Pow(score.MaxCombo, 2); + // Same for standardised score. + double comboPortionFromLongestComboInStandardisedScore = Math.Pow(score.MaxCombo, 1 + ScoreProcessor.COMBO_EXPONENT); - double lowerStrippedNew = (possibleMaxComboRepeat * strippedNewFromMaxCombo) + remainingStrippedNew; + // Calculate how many times the longest combo the user has achieved in the play can repeat + // without exceeding the combo portion in score V1 as achieved by the player. + // This is a pessimistic estimate; it intentionally does not operate on object count and uses only score instead. + double maximumOccurrencesOfLongestCombo = Math.Floor(comboPortionInScoreV1 / comboPortionFromLongestComboInScoreV1); + double comboPortionFromRepeatedLongestCombosInScoreV1 = maximumOccurrencesOfLongestCombo * comboPortionFromLongestComboInScoreV1; - // Compute approximate upper estimate new score for that play - // That is, a play were all combos were equal (except MaxCombo) - remainingStrippedV1 = strippedV1 - strippedV1FromMaxCombo; - double remainingComboObjects = maximumLegacyCombo - score.MaxCombo - score.Statistics[HitResult.Miss]; - double remainingAverageCombo = remainingComboObjects > 0 ? remainingStrippedV1 / remainingComboObjects : 0; - remainingStrippedNew = remainingComboObjects * Math.Pow(remainingAverageCombo, ScoreProcessor.COMBO_EXPONENT); + double remainingComboPortionInScoreV1 = comboPortionInScoreV1 - comboPortionFromRepeatedLongestCombosInScoreV1; + // `remainingComboPortionInScoreV1` is in the "score ballpark" realm, which means it's proportional to combo squared. + // To convert that back to a raw combo length, we need to take the square root... + double remainingCombo = Math.Sqrt(remainingComboPortionInScoreV1); + // ...and then based on that raw combo length, we calculate how much this last combo is worth in standardised score. + double remainingComboPortionInStandardisedScore = Math.Pow(remainingCombo, 1 + ScoreProcessor.COMBO_EXPONENT); - double upperStrippedNew = strippedNewFromMaxCombo + remainingStrippedNew; + double lowerEstimateOfComboPortionInStandardisedScore + = maximumOccurrencesOfLongestCombo * comboPortionFromLongestComboInStandardisedScore + + remainingComboPortionInStandardisedScore; - // Approximate by combining lower and upper estimates + // Compute approximate upper estimate new score for that play. + // This time, divide the remaining combo among remaining objects equally to achieve longest possible combo lengths. + // There is no rigorous proof that doing this will yield a correct upper bound, but it seems to work out in practice. + remainingComboPortionInScoreV1 = comboPortionInScoreV1 - comboPortionFromLongestComboInScoreV1; + double remainingCountOfObjectsGivingCombo = maximumLegacyCombo - score.MaxCombo - score.Statistics[HitResult.Miss]; + // Because we assumed all combos were equal, `remainingComboPortionInScoreV1` + // can be approximated by n * x^2, wherein n is the assumed number of equal combos, + // and x is the assumed length of every one of those combos. + // The remaining count of objects giving combo is, using those terms, equal to n * x. + // Therefore, dividing the two will result in x, i.e. the assumed length of the remaining combos. + double lengthOfRemainingCombos = remainingCountOfObjectsGivingCombo > 0 + ? remainingComboPortionInScoreV1 / remainingCountOfObjectsGivingCombo + : 0; + // In standardised scoring, each combo yields a score proportional to combo length to the power 1 + COMBO_EXPONENT. + // Using the symbols introduced above, that would be x ^ 1.5 per combo, n times (because there are n assumed equal-length combos). + // However, because `remainingCountOfObjectsGivingCombo` - using the symbols introduced above - is assumed to be equal to n * x, + // we can skip adding the 1 and just multiply by x ^ 0.5. + remainingComboPortionInStandardisedScore = remainingCountOfObjectsGivingCombo * Math.Pow(lengthOfRemainingCombos, ScoreProcessor.COMBO_EXPONENT); + + double upperEstimateOfComboPortionInStandardisedScore = comboPortionFromLongestComboInStandardisedScore + remainingComboPortionInStandardisedScore; + + // Approximate by combining lower and upper estimates. // As the lower-estimate is very pessimistic, we use a 30/70 ratio - // And cap it with 1.2 times the middle-point to avoid overstimates - double strippedNew = Math.Min( - 0.3 * lowerStrippedNew + 0.7 * upperStrippedNew, - 1.2 * (lowerStrippedNew + upperStrippedNew) / 2 + // and cap it with 1.2 times the middle-point to avoid overestimates. + double estimatedComboPortionInStandardisedScore = Math.Min( + 0.3 * lowerEstimateOfComboPortionInStandardisedScore + 0.7 * upperEstimateOfComboPortionInStandardisedScore, + 1.2 * (lowerEstimateOfComboPortionInStandardisedScore + upperEstimateOfComboPortionInStandardisedScore) / 2 ); - double newComboScoreProportion = (strippedNew / maxStrippedNew); + double newComboScoreProportion = estimatedComboPortionInStandardisedScore / maximumAchievableComboPortionInStandardisedScore; return (long)Math.Round(( 500000 * newComboScoreProportion * score.Accuracy From 2a6885164ced88450bd17bc24aea91d55a08343d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 Nov 2023 10:46:21 +0900 Subject: [PATCH 0220/1118] Update tests to match new scoring expectations --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 36 +++++++++---------- 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index c957ddd7d3..fd041d3dd0 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -45,11 +45,11 @@ namespace osu.Game.Tests.Rulesets.Scoring }; } - [TestCase(ScoringMode.Standardised, HitResult.Meh, 116_667)] - [TestCase(ScoringMode.Standardised, HitResult.Ok, 233_338)] + [TestCase(ScoringMode.Standardised, HitResult.Meh, 83_398)] + [TestCase(ScoringMode.Standardised, HitResult.Ok, 168_724)] [TestCase(ScoringMode.Standardised, HitResult.Great, 1_000_000)] - [TestCase(ScoringMode.Classic, HitResult.Meh, 11_670)] - [TestCase(ScoringMode.Classic, HitResult.Ok, 23_341)] + [TestCase(ScoringMode.Classic, HitResult.Meh, 8_343)] + [TestCase(ScoringMode.Classic, HitResult.Ok, 16_878)] [TestCase(ScoringMode.Classic, HitResult.Great, 100_033)] public void TestSingleOsuHit(ScoringMode scoringMode, HitResult hitResult, int expectedScore) { @@ -75,27 +75,27 @@ namespace osu.Game.Tests.Rulesets.Scoring /// This test intentionally misses the 3rd hitobject to achieve lower than 75% accuracy and 50% max combo. /// [TestCase(ScoringMode.Standardised, HitResult.Miss, HitResult.Great, 0)] - [TestCase(ScoringMode.Standardised, HitResult.Meh, HitResult.Great, 79_333)] - [TestCase(ScoringMode.Standardised, HitResult.Ok, HitResult.Great, 158_667)] - [TestCase(ScoringMode.Standardised, HitResult.Good, HitResult.Perfect, 317_626)] - [TestCase(ScoringMode.Standardised, HitResult.Great, HitResult.Great, 492_894)] - [TestCase(ScoringMode.Standardised, HitResult.Perfect, HitResult.Perfect, 492_894)] + [TestCase(ScoringMode.Standardised, HitResult.Meh, HitResult.Great, 34_734)] + [TestCase(ScoringMode.Standardised, HitResult.Ok, HitResult.Great, 69_925)] + [TestCase(ScoringMode.Standardised, HitResult.Good, HitResult.Perfect, 154_499)] + [TestCase(ScoringMode.Standardised, HitResult.Great, HitResult.Great, 326_963)] + [TestCase(ScoringMode.Standardised, HitResult.Perfect, HitResult.Perfect, 326_963)] [TestCase(ScoringMode.Standardised, HitResult.SmallTickMiss, HitResult.SmallTickHit, 0)] - [TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, HitResult.SmallTickHit, 541_894)] + [TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, HitResult.SmallTickHit, 493_652)] [TestCase(ScoringMode.Standardised, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] - [TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 492_894)] + [TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 326_963)] [TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] [TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] [TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)] - [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 7_975)] - [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 15_949)] - [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 31_928)] - [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 49_546)] - [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 49_546)] + [TestCase(ScoringMode.Classic, HitResult.Meh, HitResult.Great, 3_492)] + [TestCase(ScoringMode.Classic, HitResult.Ok, HitResult.Great, 7_029)] + [TestCase(ScoringMode.Classic, HitResult.Good, HitResult.Perfect, 15_530)] + [TestCase(ScoringMode.Classic, HitResult.Great, HitResult.Great, 32_867)] + [TestCase(ScoringMode.Classic, HitResult.Perfect, HitResult.Perfect, 32_867)] [TestCase(ScoringMode.Classic, HitResult.SmallTickMiss, HitResult.SmallTickHit, 0)] - [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 54_189)] + [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 49_365)] [TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] - [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 49_289)] + [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 32_696)] [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 100_003)] [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 100_015)] public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore) From 61d5a890f724a9bca05fd8e0842e47eeae7626a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 24 Nov 2023 12:37:02 +0900 Subject: [PATCH 0221/1118] Update links to figma library --- CONTRIBUTING.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 9f7d88f5c7..4106641adb 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -59,7 +59,7 @@ The [issue tracker](https://github.com/ppy/osu/issues) should provide plenty of In the case of simple issues, a direct PR is okay. However, if you decide to work on an existing issue which doesn't seem trivial, **please ask us first**. This way we can try to estimate if it is a good fit for you and provide the correct direction on how to address it. In addition, note that while we do not rule out external contributors from working on roadmapped issues, we will generally prefer to handle them ourselves unless they're not very time sensitive. -If you'd like to propose a subjective change to one of the visual aspects of the game, or there is a bigger task you'd like to work on, but there is no corresponding issue or discussion thread yet for it, **please open a discussion or issue first** to avoid wasted effort. This in particular applies if you want to work on [one of the available designs from the osu! public Figma library](https://www.figma.com/file/6m10GiGEncVFWmgOoSyakH/osu!-Figma-Library). +If you'd like to propose a subjective change to one of the visual aspects of the game, or there is a bigger task you'd like to work on, but there is no corresponding issue or discussion thread yet for it, **please open a discussion or issue first** to avoid wasted effort. This in particular applies if you want to work on [one of the available designs from the osu! Figma master library](https://www.figma.com/file/VIkXMYNPMtQem2RJg9k2iQ/Master-Library). Aside from the above, below is a brief checklist of things to watch out when you're preparing your code changes: @@ -85,4 +85,4 @@ If you're uncertain about some part of the codebase or some inner workings of th - [Development roadmap](https://github.com/orgs/ppy/projects/7/views/6): What the core team is currently working on - [`ppy/osu-framework` wiki](https://github.com/ppy/osu-framework/wiki): Contains introductory information about osu!framework, the bespoke 2D game framework we use for the game - [`ppy/osu` wiki](https://github.com/ppy/osu/wiki): Contains articles about various technical aspects of the game -- [Public Figma library](https://www.figma.com/file/6m10GiGEncVFWmgOoSyakH/osu!-Figma-Library): Contains finished and draft designs for osu! +- [Figma master library](https://www.figma.com/file/VIkXMYNPMtQem2RJg9k2iQ/Master-Library): Contains finished and draft designs for osu! From 537b0ae870d3fb4e1023c8418ce7badb71cb8cb2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 12:47:40 +0900 Subject: [PATCH 0222/1118] Add silly annotation for now (more new r# rules) --- osu.Game/Screens/Ranking/ScorePanel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Ranking/ScorePanel.cs b/osu.Game/Screens/Ranking/ScorePanel.cs index 1d332d6b27..1f7ba3692a 100644 --- a/osu.Game/Screens/Ranking/ScorePanel.cs +++ b/osu.Game/Screens/Ranking/ScorePanel.cs @@ -4,6 +4,7 @@ #nullable disable using System; +using JetBrains.Annotations; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -82,6 +83,7 @@ namespace osu.Game.Screens.Ranking private static readonly Color4 contracted_top_layer_colour = Color4Extensions.FromHex("#353535"); private static readonly Color4 contracted_middle_layer_colour = Color4Extensions.FromHex("#353535"); + [CanBeNull] public event Action StateChanged; /// From 95c00f966627c00648ed4c72848962676aad8eff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 12:45:55 +0900 Subject: [PATCH 0223/1118] Add `HexaconIcons` lookup to allow usage with `SpriteIcon` --- osu.Game/Graphics/HexaconsIcons.cs | 112 ++++++++++++++++++ osu.Game/OsuGameBase.cs | 2 + .../Play/HUD/ArgonCounterTextComponent.cs | 2 +- osu.Game/Skinning/LegacySpriteText.cs | 2 +- 4 files changed, 116 insertions(+), 2 deletions(-) create mode 100644 osu.Game/Graphics/HexaconsIcons.cs diff --git a/osu.Game/Graphics/HexaconsIcons.cs b/osu.Game/Graphics/HexaconsIcons.cs new file mode 100644 index 0000000000..9b0fb30963 --- /dev/null +++ b/osu.Game/Graphics/HexaconsIcons.cs @@ -0,0 +1,112 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Text; + +namespace osu.Game.Graphics +{ + public static class HexaconsIcons + { + public const string FONT_NAME = "Icons/Hexacons"; + + public static IconUsage Editor => get(HexaconsMapping.editor); + + private static IconUsage get(HexaconsMapping icon) + { + return new IconUsage((char)icon, FONT_NAME); + } + + // Basically just converting to something we can use in a `char` lookup for FontStore/GlyphStore compatibility. + // Names should match filenames in resources. + private enum HexaconsMapping + { + beatmap_packs, + beatmap, + calendar, + chart, + community, + contests, + devtools, + download, + editor, + featured_artist, + home, + messaging, + music, + news, + notification, + profile, + rankings, + search, + settings, + social, + store, + tournament, + wiki, + } + + public class HexaconsStore : ITextureStore, ITexturedGlyphLookupStore + { + private readonly TextureStore textures; + + public HexaconsStore(TextureStore textures) + { + this.textures = textures; + } + + public void Dispose() + { + textures.Dispose(); + } + + public ITexturedCharacterGlyph? Get(string? fontName, char character) + { + if (fontName == FONT_NAME) + return new Glyph(textures.Get($"{fontName}/{((HexaconsMapping)character).ToString().Replace("_", "-")}")); + + return null; + } + + public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); + + public Texture? Get(string name, WrapMode wrapModeS, WrapMode wrapModeT) => null; + + public Texture Get(string name) => throw new NotImplementedException(); + + public Task GetAsync(string name, CancellationToken cancellationToken = default) => throw new NotImplementedException(); + + public Stream GetStream(string name) => throw new NotImplementedException(); + + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); + + public Task GetAsync(string name, WrapMode wrapModeS, WrapMode wrapModeT, CancellationToken cancellationToken = default) => throw new NotImplementedException(); + + public class Glyph : ITexturedCharacterGlyph + { + public float XOffset => default; + public float YOffset => default; + public float XAdvance => default; + public float Baseline => default; + public char Character => default; + + public float GetKerning(T lastGlyph) where T : ICharacterGlyph => throw new NotImplementedException(); + + public Texture Texture { get; } + public float Width => Texture.Width; + public float Height => Texture.Height; + + public Glyph(Texture texture) + { + Texture = texture; + } + } + } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 228edc8952..2d8024a45a 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -477,6 +477,8 @@ namespace osu.Game AddFont(Resources, @"Fonts/Venera/Venera-Light"); AddFont(Resources, @"Fonts/Venera/Venera-Bold"); AddFont(Resources, @"Fonts/Venera/Venera-Black"); + + Fonts.AddStore(new HexaconsIcons.HexaconsStore(Textures)); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index d3fadb452b..2a3f4365cb 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -155,7 +155,7 @@ namespace osu.Game.Screens.Play.HUD this.getLookup = getLookup; } - public ITexturedCharacterGlyph? Get(string fontName, char character) + public ITexturedCharacterGlyph? Get(string? fontName, char character) { string lookup = getLookup(character); var texture = textures.Get($"Gameplay/Fonts/{fontName}-{lookup}"); diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs index 041a32e8de..8aefa50252 100644 --- a/osu.Game/Skinning/LegacySpriteText.cs +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -63,7 +63,7 @@ namespace osu.Game.Skinning this.maxSize = maxSize; } - public ITexturedCharacterGlyph? Get(string fontName, char character) + public ITexturedCharacterGlyph? Get(string? fontName, char character) { string lookup = getLookupName(character); From 340227a06d65c6b8bb1edf2e4834ab04d7f809ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 13:16:04 +0900 Subject: [PATCH 0224/1118] Replace all hexacon lookups with strongly typed properties --- .../UserInterface/TestSceneOverlayHeader.cs | 3 +- osu.Game/Graphics/HexaconsIcons.cs | 27 +++++++++-- .../BeatmapListing/BeatmapListingHeader.cs | 3 +- .../Overlays/BeatmapSet/BeatmapSetHeader.cs | 3 +- .../Overlays/Changelog/ChangelogHeader.cs | 3 +- osu.Game/Overlays/Chat/ChatOverlayTopBar.cs | 4 +- osu.Game/Overlays/ChatOverlay.cs | 4 +- .../Dashboard/DashboardOverlayHeader.cs | 3 +- osu.Game/Overlays/FullscreenOverlay.cs | 3 +- osu.Game/Overlays/INamedOverlayComponent.cs | 3 +- osu.Game/Overlays/News/NewsHeader.cs | 3 +- osu.Game/Overlays/NotificationOverlay.cs | 4 +- osu.Game/Overlays/NowPlayingOverlay.cs | 2 +- osu.Game/Overlays/OverlayTitle.cs | 45 ++++++------------- osu.Game/Overlays/Profile/ProfileHeader.cs | 3 +- .../Rankings/RankingsOverlayHeader.cs | 3 +- osu.Game/Overlays/SettingsOverlay.cs | 4 +- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 12 ++--- .../Overlays/Toolbar/ToolbarHomeButton.cs | 3 +- .../Toolbar/ToolbarOverlayToggleButton.cs | 2 +- osu.Game/Overlays/Wiki/WikiHeader.cs | 3 +- .../Edit/Components/Menus/EditorMenuBar.cs | 4 +- .../Screens/Edit/Setup/SetupScreenHeader.cs | 3 +- 23 files changed, 82 insertions(+), 65 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs index a927b0931b..55a04b129c 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Game.Graphics.Sprites; using osu.Framework.Allocation; using osu.Framework.Graphics.Shapes; +using osu.Game.Graphics; using osuTK.Graphics; namespace osu.Game.Tests.Visual.UserInterface @@ -153,7 +154,7 @@ namespace osu.Game.Tests.Visual.UserInterface public TestTitle() { Title = "title"; - IconTexture = "Icons/changelog"; + Icon = HexaconsIcons.Devtools; } } } diff --git a/osu.Game/Graphics/HexaconsIcons.cs b/osu.Game/Graphics/HexaconsIcons.cs index 9b0fb30963..3eee5d7197 100644 --- a/osu.Game/Graphics/HexaconsIcons.cs +++ b/osu.Game/Graphics/HexaconsIcons.cs @@ -16,12 +16,31 @@ namespace osu.Game.Graphics { public const string FONT_NAME = "Icons/Hexacons"; + public static IconUsage BeatmapPacks => get(HexaconsMapping.beatmap_packs); + public static IconUsage Beatmap => get(HexaconsMapping.beatmap); + public static IconUsage Calendar => get(HexaconsMapping.calendar); + public static IconUsage Chart => get(HexaconsMapping.chart); + public static IconUsage Community => get(HexaconsMapping.community); + public static IconUsage Contests => get(HexaconsMapping.contests); + public static IconUsage Devtools => get(HexaconsMapping.devtools); + public static IconUsage Download => get(HexaconsMapping.download); public static IconUsage Editor => get(HexaconsMapping.editor); + public static IconUsage FeaturedArtist => get(HexaconsMapping.featured_artist); + public static IconUsage Home => get(HexaconsMapping.home); + public static IconUsage Messaging => get(HexaconsMapping.messaging); + public static IconUsage Music => get(HexaconsMapping.music); + public static IconUsage News => get(HexaconsMapping.news); + public static IconUsage Notification => get(HexaconsMapping.notification); + public static IconUsage Profile => get(HexaconsMapping.profile); + public static IconUsage Rankings => get(HexaconsMapping.rankings); + public static IconUsage Search => get(HexaconsMapping.search); + public static IconUsage Settings => get(HexaconsMapping.settings); + public static IconUsage Social => get(HexaconsMapping.social); + public static IconUsage Store => get(HexaconsMapping.store); + public static IconUsage Tournament => get(HexaconsMapping.tournament); + public static IconUsage Wiki => get(HexaconsMapping.wiki); - private static IconUsage get(HexaconsMapping icon) - { - return new IconUsage((char)icon, FONT_NAME); - } + private static IconUsage get(HexaconsMapping icon) => new IconUsage((char)icon, FONT_NAME); // Basically just converting to something we can use in a `char` lookup for FontStore/GlyphStore compatibility. // Names should match filenames in resources. diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index 3336c383ff..27fab82bf3 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -4,6 +4,7 @@ #nullable disable using osu.Framework.Graphics; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; @@ -23,7 +24,7 @@ namespace osu.Game.Overlays.BeatmapListing { Title = PageTitleStrings.MainBeatmapsetsControllerIndex; Description = NamedOverlayComponentStrings.BeatmapListingDescription; - IconTexture = "Icons/Hexacons/beatmap"; + Icon = HexaconsIcons.Beatmap; } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index 858742648c..eced27f35e 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -9,6 +9,7 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; @@ -59,7 +60,7 @@ namespace osu.Game.Overlays.BeatmapSet public BeatmapHeaderTitle() { Title = PageTitleStrings.MainBeatmapsetsControllerShow; - IconTexture = "Icons/Hexacons/beatmap"; + Icon = HexaconsIcons.Beatmap; } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index e9be67e977..61ea9dc4db 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; @@ -123,7 +124,7 @@ namespace osu.Game.Overlays.Changelog { Title = PageTitleStrings.MainChangelogControllerDefault; Description = NamedOverlayComponentStrings.ChangelogDescription; - IconTexture = "Icons/Hexacons/devtools"; + Icon = HexaconsIcons.Devtools; } } } diff --git a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs index 0410174dc1..c6f63a72b9 100644 --- a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs +++ b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs @@ -45,11 +45,11 @@ namespace osu.Game.Overlays.Chat { new Drawable[] { - new Sprite + new SpriteIcon { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Texture = textures.Get("Icons/Hexacons/messaging"), + Icon = HexaconsIcons.Social, Size = new Vector2(18), }, // Placeholder text diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 724f77ad71..5cf2ac6c86 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -11,11 +11,13 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; @@ -29,7 +31,7 @@ namespace osu.Game.Overlays { public partial class ChatOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent, IKeyBindingHandler { - public string IconTexture => "Icons/Hexacons/messaging"; + public IconUsage Icon => HexaconsIcons.Messaging; public LocalisableString Title => ChatStrings.HeaderTitle; public LocalisableString Description => ChatStrings.HeaderDescription; diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 0f4697e33c..b9d869c2ec 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; @@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Dashboard { Title = PageTitleStrings.MainHomeControllerIndex; Description = NamedOverlayComponentStrings.DashboardDescription; - IconTexture = "Icons/Hexacons/social"; + Icon = HexaconsIcons.Social; } } } diff --git a/osu.Game/Overlays/FullscreenOverlay.cs b/osu.Game/Overlays/FullscreenOverlay.cs index 6ee045c492..6ddf1eecf0 100644 --- a/osu.Game/Overlays/FullscreenOverlay.cs +++ b/osu.Game/Overlays/FullscreenOverlay.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Graphics.Containers; using osu.Game.Online.API; @@ -17,7 +18,7 @@ namespace osu.Game.Overlays public abstract partial class FullscreenOverlay : WaveOverlayContainer, INamedOverlayComponent where T : OverlayHeader { - public virtual string IconTexture => Header.Title.IconTexture; + public virtual IconUsage Icon => Header.Title.Icon; public virtual LocalisableString Title => Header.Title.Title; public virtual LocalisableString Description => Header.Title.Description; diff --git a/osu.Game/Overlays/INamedOverlayComponent.cs b/osu.Game/Overlays/INamedOverlayComponent.cs index 65664b12e7..ef3c029aac 100644 --- a/osu.Game/Overlays/INamedOverlayComponent.cs +++ b/osu.Game/Overlays/INamedOverlayComponent.cs @@ -1,13 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; namespace osu.Game.Overlays { public interface INamedOverlayComponent { - string IconTexture { get; } + IconUsage Icon { get; } LocalisableString Title { get; } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index 44e2f6a8cb..f237ed66f2 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -7,6 +7,7 @@ using System; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; @@ -68,7 +69,7 @@ namespace osu.Game.Overlays.News { Title = PageTitleStrings.MainNewsControllerDefault; Description = NamedOverlayComponentStrings.NewsDescription; - IconTexture = "Icons/Hexacons/news"; + Icon = HexaconsIcons.News; } } } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index 81233b4343..c3ddb228ea 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -13,9 +13,11 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Framework.Threading; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays.Notifications; using osu.Game.Resources.Localisation.Web; @@ -27,7 +29,7 @@ namespace osu.Game.Overlays { public partial class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent, INotificationOverlay { - public string IconTexture => "Icons/Hexacons/notification"; + public IconUsage Icon => HexaconsIcons.Notification; public LocalisableString Title => NotificationsStrings.HeaderTitle; public LocalisableString Description => NotificationsStrings.HeaderDescription; diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 5bbf18a959..425ff0935d 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays { public partial class NowPlayingOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent { - public string IconTexture => "Icons/Hexacons/music"; + public IconUsage Icon => HexaconsIcons.Music; public LocalisableString Title => NowPlayingStrings.HeaderTitle; public LocalisableString Description => NowPlayingStrings.HeaderDescription; diff --git a/osu.Game/Overlays/OverlayTitle.cs b/osu.Game/Overlays/OverlayTitle.cs index 1d207e5f7d..a2ff7032b5 100644 --- a/osu.Game/Overlays/OverlayTitle.cs +++ b/osu.Game/Overlays/OverlayTitle.cs @@ -1,13 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - -using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Framework.Localisation; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; @@ -20,7 +16,7 @@ namespace osu.Game.Overlays public const float ICON_SIZE = 30; private readonly OsuSpriteText titleText; - private readonly Container icon; + private readonly Container iconContainer; private LocalisableString title; @@ -32,12 +28,20 @@ namespace osu.Game.Overlays public LocalisableString Description { get; protected set; } - private string iconTexture; + private IconUsage icon; - public string IconTexture + public IconUsage Icon { - get => iconTexture; - protected set => icon.Child = new OverlayTitleIcon(iconTexture = value); + get => icon; + protected set => iconContainer.Child = new SpriteIcon + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + FillMode = FillMode.Fit, + + Icon = icon = value, + }; } protected OverlayTitle() @@ -51,7 +55,7 @@ namespace osu.Game.Overlays Direction = FillDirection.Horizontal, Children = new Drawable[] { - icon = new Container + iconContainer = new Container { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -68,26 +72,5 @@ namespace osu.Game.Overlays } }; } - - private partial class OverlayTitleIcon : Sprite - { - private readonly string textureName; - - public OverlayTitleIcon(string textureName) - { - this.textureName = textureName; - - RelativeSizeAxes = Axes.Both; - Anchor = Anchor.Centre; - Origin = Anchor.Centre; - FillMode = FillMode.Fit; - } - - [BackgroundDependencyLoader] - private void load(TextureStore textures) - { - Texture = textures.Get(textureName); - } - } } } diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 80d48ae09e..78343d08f1 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Overlays.Profile.Header; using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Resources.Localisation.Web; @@ -86,7 +87,7 @@ namespace osu.Game.Overlays.Profile public ProfileHeaderTitle() { Title = PageTitleStrings.MainUsersControllerDefault; - IconTexture = "Icons/Hexacons/profile"; + Icon = HexaconsIcons.Profile; } } } diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 44f278a237..63128fb73d 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Game.Localisation; using osu.Game.Resources.Localisation.Web; using osu.Framework.Graphics; +using osu.Game.Graphics; using osu.Game.Rulesets; using osu.Game.Users; @@ -35,7 +36,7 @@ namespace osu.Game.Overlays.Rankings { Title = PageTitleStrings.MainRankingControllerDefault; Description = NamedOverlayComponentStrings.RankingsDescription; - IconTexture = "Icons/Hexacons/rankings"; + Icon = HexaconsIcons.Rankings; } } } diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 291281124c..746d451343 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -12,14 +12,16 @@ using osu.Game.Overlays.Settings.Sections.Input; using osuTK.Graphics; using System.Collections.Generic; using osu.Framework.Bindables; +using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; namespace osu.Game.Overlays { public partial class SettingsOverlay : SettingsPanel, INamedOverlayComponent { - public string IconTexture => "Icons/Hexacons/settings"; + public IconUsage Icon => HexaconsIcons.Settings; public LocalisableString Title => SettingsStrings.HeaderTitle; public LocalisableString Description => SettingsStrings.HeaderDescription; diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index e181322dda..08bcb6bd8a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -10,12 +10,11 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; -using osu.Game.Database; using osu.Framework.Localisation; +using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; @@ -36,16 +35,13 @@ namespace osu.Game.Overlays.Toolbar IconContainer.Show(); } - [Resolved] - private TextureStore textures { get; set; } = null!; - [Resolved] private ReadableKeyCombinationProvider keyCombinationProvider { get; set; } = null!; - public void SetIcon(string texture) => - SetIcon(new Sprite + public void SetIcon(IconUsage icon) => + SetIcon(new SpriteIcon { - Texture = textures.Get(texture), + Icon = icon, }); public LocalisableString Text diff --git a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs index ba2c8282c5..ded0229d67 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Game.Graphics; using osu.Game.Input.Bindings; using osu.Game.Localisation; @@ -20,7 +21,7 @@ namespace osu.Game.Overlays.Toolbar { TooltipMain = ToolbarStrings.HomeHeaderTitle; TooltipSub = ToolbarStrings.HomeHeaderDescription; - SetIcon("Icons/Hexacons/home"); + SetIcon(HexaconsIcons.Home); } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index 7bd48174db..78c976111b 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -39,7 +39,7 @@ namespace osu.Game.Overlays.Toolbar { TooltipMain = named.Title; TooltipSub = named.Description; - SetIcon(named.IconTexture); + SetIcon(named.Icon); } } } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 9317813fc4..9e9e565684 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -8,6 +8,7 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Online.API.Requests.Responses; using osu.Game.Resources.Localisation.Web; @@ -81,7 +82,7 @@ namespace osu.Game.Overlays.Wiki { Title = PageTitleStrings.MainWikiControllerDefault; Description = NamedOverlayComponentStrings.WikiDescription; - IconTexture = "Icons/Hexacons/wiki"; + Icon = HexaconsIcons.Wiki; } } } diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index a67375b0a4..5c77672d90 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs @@ -46,12 +46,12 @@ namespace osu.Game.Screens.Edit.Components.Menus Padding = new MarginPadding(8), Children = new Drawable[] { - new Sprite + new SpriteIcon { Size = new Vector2(26), Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Texture = textures.Get("Icons/Hexacons/editor"), + Icon = HexaconsIcons.Editor, }, text = new TextFlowContainer { diff --git a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs index 788beba9d9..93448c4394 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Overlays; using osuTK.Graphics; @@ -79,7 +80,7 @@ namespace osu.Game.Screens.Edit.Setup { Title = EditorSetupStrings.BeatmapSetup.ToLower(); Description = EditorSetupStrings.BeatmapSetupDescription; - IconTexture = "Icons/Hexacons/social"; + Icon = HexaconsIcons.Social; } } From 5905ca64925f4cf4b3ce7d07974e39a353df18e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 10:56:59 +0900 Subject: [PATCH 0225/1118] Add second level menu for skin editors --- .../TestSceneBeatmapEditorNavigation.cs | 2 +- .../UserInterface/TestSceneButtonSystem.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 19 +++++++++++++++++-- osu.Game/Screens/Menu/MainMenu.cs | 10 +++++++++- 4 files changed, 28 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index b79b61202b..ce266a2d77 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -239,7 +239,7 @@ namespace osu.Game.Tests.Visual.Navigation { AddUntilStep("wait for dialog overlay", () => Game.ChildrenOfType().SingleOrDefault() != null); - AddStep("open editor", () => Game.ChildrenOfType().Single().OnEdit.Invoke()); + AddStep("open editor", () => Game.ChildrenOfType().Single().OnEditBeatmap.Invoke()); AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.IsLoaded); AddStep("click on file", () => { diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index ac811aeb65..1de47aee69 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.UserInterface break; case Key.E: - buttons.OnEdit = action; + buttons.OnEditBeatmap = action; break; case Key.D: diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index a0cf9f5322..79739e4f0c 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -13,6 +13,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Sample; using osu.Framework.Bindables; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; @@ -40,7 +41,8 @@ namespace osu.Game.Screens.Menu private readonly IBindable isIdle = new BindableBool(); - public Action OnEdit; + public Action OnEditBeatmap; + public Action OnEditSkin; public Action OnExit; public Action OnBeatmapListing; public Action OnSolo; @@ -84,6 +86,7 @@ namespace osu.Game.Screens.Menu private readonly List buttonsTopLevel = new List(); private readonly List buttonsPlay = new List(); + private readonly List buttonsEdit = new List(); private Sample sampleBackToLogo; private Sample sampleLogoSwoosh; @@ -105,6 +108,11 @@ namespace osu.Game.Screens.Menu buttonArea.AddRange(new Drawable[] { new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), + backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, + -WEDGE_WIDTH) + { + VisibleState = ButtonSystemState.Edit, + }, backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH) { @@ -133,14 +141,19 @@ namespace osu.Game.Screens.Menu buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); + buttonsEdit.Add(new MainMenuButton(CommonStrings.Beatmaps.ToLower(), @"button-default-select", FontAwesome.Solid.User, new Color4(238, 170, 0, 255), () => OnEditBeatmap?.Invoke(), WEDGE_WIDTH, Key.B)); + buttonsEdit.Add(new MainMenuButton(CommonStrings.Skins.ToLower(), @"button-default-select", FontAwesome.Solid.Users, new Color4(220, 160, 0, 255), () => OnEditSkin?.Invoke(), 0, Key.S)); + buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-default-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => OnEdit?.Invoke(), 0, Key.E)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-default-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => State = ButtonSystemState.Edit, 0, Key.E)); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D)); if (host.CanExit) buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); buttonArea.AddRange(buttonsPlay); + buttonArea.AddRange(buttonsEdit); buttonArea.AddRange(buttonsTopLevel); buttonArea.ForEach(b => @@ -270,6 +283,7 @@ namespace osu.Game.Screens.Menu return true; + case ButtonSystemState.Edit: case ButtonSystemState.Play: StopSamplePlayback(); backButton.TriggerClick(); @@ -414,6 +428,7 @@ namespace osu.Game.Screens.Menu Initial, TopLevel, Play, + Edit, EnteringMode, } } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 0f73707544..c25e62d69e 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -25,6 +25,7 @@ using osu.Game.Input.Bindings; using osu.Game.IO; using osu.Game.Online.API; using osu.Game.Overlays; +using osu.Game.Overlays.SkinEditor; using osu.Game.Rulesets; using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; @@ -93,6 +94,9 @@ namespace osu.Game.Screens.Menu private Sample reappearSampleSwoosh; + [Resolved(canBeNull: true)] + private SkinEditorOverlay skinEditor { get; set; } + [BackgroundDependencyLoader(true)] private void load(BeatmapListingOverlay beatmapListing, SettingsOverlay settings, OsuConfigManager config, SessionStatics statics, AudioManager audio) { @@ -120,11 +124,15 @@ namespace osu.Game.Screens.Menu { Buttons = new ButtonSystem { - OnEdit = delegate + OnEditBeatmap = () => { Beatmap.SetDefault(); this.Push(new EditorLoader()); }, + OnEditSkin = () => + { + skinEditor?.Show(); + }, OnSolo = loadSoloSongSelect, OnMultiplayer = () => this.Push(new Multiplayer()), OnPlaylists = () => this.Push(new Playlists()), From 7e59a1d0be3eb3aae50f17e1a361b6a357286194 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 11:00:22 +0900 Subject: [PATCH 0226/1118] Apply NRT to `ButtonSystem` --- .../Editing/TestSceneOpenEditorTimestamp.cs | 2 +- .../TestSceneBeatmapEditorNavigation.cs | 2 +- osu.Game/Screens/Menu/ButtonSystem.cs | 52 +++++++++---------- 3 files changed, 26 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index 2c8655a5f5..1a754d5145 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -35,7 +35,7 @@ namespace osu.Game.Tests.Visual.Editing () => Game.Notifications.AllNotifications.Count(x => x.Text == EditorStrings.MustBeInEditorToHandleLinks), () => Is.EqualTo(1)); - AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo?.Invoke()); AddUntilStep("entered song select", () => Game.ScreenStack.CurrentScreen is PlaySongSelect); addStepClickLink("00:00:000 (1)", waitForSeek: false); diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs index ce266a2d77..9930349b1b 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneBeatmapEditorNavigation.cs @@ -239,7 +239,7 @@ namespace osu.Game.Tests.Visual.Navigation { AddUntilStep("wait for dialog overlay", () => Game.ChildrenOfType().SingleOrDefault() != null); - AddStep("open editor", () => Game.ChildrenOfType().Single().OnEditBeatmap.Invoke()); + AddStep("open editor", () => Game.ChildrenOfType().Single().OnEditBeatmap?.Invoke()); AddUntilStep("wait for editor", () => Game.ScreenStack.CurrentScreen is Editor editor && editor.IsLoaded); AddStep("click on file", () => { diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 79739e4f0c..c4c7599fd7 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -1,12 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; -using JetBrains.Annotations; using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -37,24 +34,23 @@ namespace osu.Game.Screens.Menu { public partial class ButtonSystem : Container, IStateful, IKeyBindingHandler { - public event Action StateChanged; - - private readonly IBindable isIdle = new BindableBool(); - - public Action OnEditBeatmap; - public Action OnEditSkin; - public Action OnExit; - public Action OnBeatmapListing; - public Action OnSolo; - public Action OnSettings; - public Action OnMultiplayer; - public Action OnPlaylists; - public const float BUTTON_WIDTH = 140f; public const float WEDGE_WIDTH = 20; - [CanBeNull] - private OsuLogo logo; + public event Action? StateChanged; + + public Action? OnEditBeatmap; + public Action? OnEditSkin; + public Action? OnExit; + public Action? OnBeatmapListing; + public Action? OnSolo; + public Action? OnSettings; + public Action? OnMultiplayer; + public Action? OnPlaylists; + + private readonly IBindable isIdle = new BindableBool(); + + private OsuLogo? logo; /// /// Assign the that this ButtonSystem should manage the position of. @@ -88,8 +84,8 @@ namespace osu.Game.Screens.Menu private readonly List buttonsPlay = new List(); private readonly List buttonsEdit = new List(); - private Sample sampleBackToLogo; - private Sample sampleLogoSwoosh; + private Sample? sampleBackToLogo; + private Sample? sampleLogoSwoosh; private readonly LogoTrackingContainer logoTrackingContainer; @@ -124,17 +120,17 @@ namespace osu.Game.Screens.Menu buttonArea.Flow.CentreTarget = logoTrackingContainer.LogoFacade; } - [Resolved(CanBeNull = true)] - private OsuGame game { get; set; } + [Resolved] + private IAPIProvider api { get; set; } = null!; [Resolved] - private IAPIProvider api { get; set; } + private OsuGame? game { get; set; } - [Resolved(CanBeNull = true)] - private LoginOverlay loginOverlay { get; set; } + [Resolved] + private LoginOverlay? loginOverlay { get; set; } - [BackgroundDependencyLoader(true)] - private void load(AudioManager audio, IdleTracker idleTracker, GameHost host) + [BackgroundDependencyLoader] + private void load(AudioManager audio, IdleTracker? idleTracker, GameHost host) { buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); @@ -354,7 +350,7 @@ namespace osu.Game.Screens.Menu } } - private ScheduledDelegate logoDelayedAction; + private ScheduledDelegate? logoDelayedAction; private void updateLogoState(ButtonSystemState lastState = ButtonSystemState.Initial) { From 8ad414488a630787453fe953bb1c70d327daa992 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 11:02:30 +0900 Subject: [PATCH 0227/1118] Play out previous transforms immediately to avoid flow issues with multiple sub menus --- osu.Game/Screens/Menu/ButtonSystem.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index c4c7599fd7..a954fb50b7 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -338,6 +338,8 @@ namespace osu.Game.Screens.Menu Logger.Log($"{nameof(ButtonSystem)}'s state changed from {lastState} to {state}"); + buttonArea.FinishTransforms(true); + using (buttonArea.BeginDelayedSequence(lastState == ButtonSystemState.Initial ? 150 : 0)) { buttonArea.ButtonSystemState = state; From 1d1b3ca9826127ad986bfd3afc35810622602406 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 11:05:57 +0900 Subject: [PATCH 0228/1118] Apply NRT to `MainMenuButton` --- osu.Game/Screens/Menu/MainMenuButton.cs | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index 63fc34b4fb..daf8451899 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Linq; using osu.Framework; @@ -33,7 +31,7 @@ namespace osu.Game.Screens.Menu /// public partial class MainMenuButton : BeatSyncedContainer, IStateful { - public event Action StateChanged; + public event Action? StateChanged; public readonly Key[] TriggerKeys; @@ -48,14 +46,14 @@ namespace osu.Game.Screens.Menu /// public ButtonSystemState VisibleState = ButtonSystemState.TopLevel; - private readonly Action clickAction; - private Sample sampleClick; - private Sample sampleHover; - private SampleChannel sampleChannel; + private readonly Action? clickAction; + private Sample? sampleClick; + private Sample? sampleHover; + private SampleChannel? sampleChannel; public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => box.ReceivePositionalInputAt(screenSpacePos); - public MainMenuButton(LocalisableString text, string sampleName, IconUsage symbol, Color4 colour, Action clickAction = null, float extraWidth = 0, params Key[] triggerKeys) + public MainMenuButton(LocalisableString text, string sampleName, IconUsage symbol, Color4 colour, Action? clickAction = null, float extraWidth = 0, params Key[] triggerKeys) { this.sampleName = sampleName; this.clickAction = clickAction; From a069a673fa1866e06451ba620daa2191df0e8403 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 11:15:18 +0900 Subject: [PATCH 0229/1118] Allow buttons to be displayed on more than one state (and share the back button) --- osu.Game/Screens/Menu/ButtonSystem.cs | 8 ++------ osu.Game/Screens/Menu/MainMenuButton.cs | 18 ++++++++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index a954fb50b7..995cdaf450 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -107,12 +107,8 @@ namespace osu.Game.Screens.Menu backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH) { - VisibleState = ButtonSystemState.Edit, - }, - backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, - -WEDGE_WIDTH) - { - VisibleState = ButtonSystemState.Play, + VisibleStateMin = ButtonSystemState.Play, + VisibleStateMax = ButtonSystemState.Edit, }, logoTrackingContainer.LogoFacade.With(d => d.Scale = new Vector2(0.74f)) }); diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index daf8451899..bc638b44ac 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -42,9 +42,19 @@ namespace osu.Game.Screens.Menu private readonly string sampleName; /// - /// The menu state for which we are visible for. + /// The menu state for which we are visible for (assuming only one). /// - public ButtonSystemState VisibleState = ButtonSystemState.TopLevel; + public ButtonSystemState VisibleState + { + set + { + VisibleStateMin = value; + VisibleStateMax = value; + } + } + + public ButtonSystemState VisibleStateMin = ButtonSystemState.TopLevel; + public ButtonSystemState VisibleStateMax = ButtonSystemState.TopLevel; private readonly Action? clickAction; private Sample? sampleClick; @@ -313,9 +323,9 @@ namespace osu.Game.Screens.Menu break; default: - if (value == VisibleState) + if (value <= VisibleStateMax && value >= VisibleStateMin) State = ButtonState.Expanded; - else if (value < VisibleState) + else if (value < VisibleStateMin) State = ButtonState.Contracted; else State = ButtonState.Exploded; From a02b6a5bcb23af26a6fee502a9423b517f75b112 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 11:28:06 +0900 Subject: [PATCH 0230/1118] Update menu key shortcut test coverage --- .../UserInterface/TestSceneButtonSystem.cs | 29 +++++++++++-------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs index 1de47aee69..8f72be37df 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneButtonSystem.cs @@ -67,14 +67,15 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("Enter mode", performEnterMode); } - [TestCase(Key.P, true)] - [TestCase(Key.M, true)] - [TestCase(Key.L, true)] - [TestCase(Key.E, false)] - [TestCase(Key.D, false)] - [TestCase(Key.Q, false)] - [TestCase(Key.O, false)] - public void TestShortcutKeys(Key key, bool entersPlay) + [TestCase(Key.P, Key.P)] + [TestCase(Key.M, Key.P)] + [TestCase(Key.L, Key.P)] + [TestCase(Key.B, Key.E)] + [TestCase(Key.S, Key.E)] + [TestCase(Key.D, null)] + [TestCase(Key.Q, null)] + [TestCase(Key.O, null)] + public void TestShortcutKeys(Key key, Key? subMenuEnterKey) { int activationCount = -1; AddStep("set up action", () => @@ -96,10 +97,14 @@ namespace osu.Game.Tests.Visual.UserInterface buttons.OnPlaylists = action; break; - case Key.E: + case Key.B: buttons.OnEditBeatmap = action; break; + case Key.S: + buttons.OnEditSkin = action; + break; + case Key.D: buttons.OnBeatmapListing = action; break; @@ -117,10 +122,10 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep($"press {key}", () => InputManager.Key(key)); AddAssert("state is top level", () => buttons.State == ButtonSystemState.TopLevel); - if (entersPlay) + if (subMenuEnterKey != null) { - AddStep("press P", () => InputManager.Key(Key.P)); - AddAssert("state is play", () => buttons.State == ButtonSystemState.Play); + AddStep($"press {subMenuEnterKey}", () => InputManager.Key(subMenuEnterKey.Value)); + AddAssert("state is not top menu", () => buttons.State != ButtonSystemState.TopLevel); } AddStep($"press {key}", () => InputManager.Key(key)); From c7f1fd23e76170ee9c52973022ff0d630b69917f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 24 Nov 2023 06:09:41 +0300 Subject: [PATCH 0231/1118] Implement spinner glow --- .../Skinning/Argon/ArgonSpinnerProgressArc.cs | 122 +++++++++++++++++- 1 file changed, 120 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs index 31cdc0dc0f..b719138d33 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs @@ -2,10 +2,17 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; +using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; +using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; +using osu.Framework.Graphics.Textures; using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Rulesets.Objects.Drawables; @@ -19,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon private const float arc_fill = 0.15f; private const float arc_radius = 0.12f; - private CircularProgress fill = null!; + private ProgressFill fill = null!; private DrawableSpinner spinner = null!; @@ -45,13 +52,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon InnerRadius = arc_radius, RoundedCaps = true, }, - fill = new CircularProgress + fill = new ProgressFill { Anchor = Anchor.Centre, Origin = Anchor.Centre, RelativeSizeAxes = Axes.Both, InnerRadius = arc_radius, RoundedCaps = true, + GlowColour = new Color4(171, 255, 255, 255) } }; } @@ -67,5 +75,115 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon fill.Rotation = (float)(90 - fill.Current.Value * 180); } + + private partial class ProgressFill : CircularProgress + { + private Color4 glowColour = Color4.White; + + public Color4 GlowColour + { + get => glowColour; + set + { + glowColour = value; + Invalidate(Invalidation.DrawNode); + } + } + + private Texture glowTexture = null!; + private IShader glowShader = null!; + private float glowSize; + + [BackgroundDependencyLoader] + private void load(TextureStore textures, ShaderManager shaders) + { + glowTexture = textures.Get("Gameplay/osu/spinner-glow"); + glowShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "SpinnerGlow"); + glowSize = Blur.KernelSize(50); // Half of the maximum blur sigma in the design (which is 100) + } + + protected override DrawNode CreateDrawNode() => new ProgressFillDrawNode(this); + + private class ProgressFillDrawNode : CircularProgressDrawNode + { + protected new ProgressFill Source => (ProgressFill)base.Source; + + public ProgressFillDrawNode(CircularProgress source) + : base(source) + { + } + + private Texture glowTexture = null!; + private IShader glowShader = null!; + private Quad glowQuad; + private float relativeGlowSize; + private Color4 glowColour; + + public override void ApplyState() + { + base.ApplyState(); + + glowTexture = Source.glowTexture; + glowShader = Source.glowShader; + glowColour = Source.glowColour; + + // Inflated draw quad by the size of the blur. + glowQuad = Source.ToScreenSpace(DrawRectangle.Inflate(Source.glowSize)); + relativeGlowSize = Source.glowSize / Source.DrawSize.X; + } + + public override void Draw(IRenderer renderer) + { + base.Draw(renderer); + drawGlow(renderer); + } + + private void drawGlow(IRenderer renderer) + { + renderer.SetBlend(BlendingParameters.Additive); + + glowShader.Bind(); + bindGlowUniformResources(glowShader, renderer); + + ColourInfo col = DrawColourInfo.Colour; + col.ApplyChild(glowColour); + + renderer.DrawQuad(glowTexture, glowQuad, col); + + glowShader.Unbind(); + } + + private IUniformBuffer? progressGlowParametersBuffer; + + private void bindGlowUniformResources(IShader shader, IRenderer renderer) + { + progressGlowParametersBuffer ??= renderer.CreateUniformBuffer(); + progressGlowParametersBuffer.Data = new ProgressGlowParameters + { + InnerRadius = InnerRadius, + Progress = Progress, + TexelSize = TexelSize, + GlowSize = relativeGlowSize + }; + + shader.BindUniformBlock("m_ProgressGlowParameters", progressGlowParametersBuffer); + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + progressGlowParametersBuffer?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct ProgressGlowParameters + { + public UniformFloat InnerRadius; + public UniformFloat Progress; + public UniformFloat TexelSize; + public UniformFloat GlowSize; + } + } + } } } From b8179aa875dcb27746bf1e9f236117c3f9bdb30c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 13:23:35 +0900 Subject: [PATCH 0232/1118] Use better(?) icons and full strings --- osu.Game/Localisation/EditorStrings.cs | 5 +++++ osu.Game/Screens/Menu/ButtonSystem.cs | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/EditorStrings.cs b/osu.Game/Localisation/EditorStrings.cs index b20b5bc65a..6ad12f54df 100644 --- a/osu.Game/Localisation/EditorStrings.cs +++ b/osu.Game/Localisation/EditorStrings.cs @@ -9,6 +9,11 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.Editor"; + /// + /// "Beatmap editor" + /// + public static LocalisableString BeatmapEditor => new TranslatableString(getKey(@"beatmap_editor"), @"Beatmap editor"); + /// /// "Waveform opacity" /// diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 995cdaf450..8173375c29 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -133,8 +133,8 @@ namespace osu.Game.Screens.Menu buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); - buttonsEdit.Add(new MainMenuButton(CommonStrings.Beatmaps.ToLower(), @"button-default-select", FontAwesome.Solid.User, new Color4(238, 170, 0, 255), () => OnEditBeatmap?.Invoke(), WEDGE_WIDTH, Key.B)); - buttonsEdit.Add(new MainMenuButton(CommonStrings.Skins.ToLower(), @"button-default-select", FontAwesome.Solid.Users, new Color4(220, 160, 0, 255), () => OnEditSkin?.Invoke(), 0, Key.S)); + buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", HexaconsIcons.Beatmap, new Color4(238, 170, 0, 255), () => OnEditBeatmap?.Invoke(), WEDGE_WIDTH, Key.B)); + buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", HexaconsIcons.Editor, new Color4(220, 160, 0, 255), () => OnEditSkin?.Invoke(), 0, Key.S)); buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); From 62a04a93c837db0aef81557585120877f513660c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 23 Nov 2023 16:22:34 +0900 Subject: [PATCH 0233/1118] Implement legacy catch health processor --- .../Scoring/CatchHealthProcessor.cs | 15 ++ .../Scoring/LegacyCatchHealthProcessor.cs | 237 ++++++++++++++++++ 2 files changed, 252 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs create mode 100644 osu.Game.Rulesets.Catch/Scoring/LegacyCatchHealthProcessor.cs diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs new file mode 100644 index 0000000000..7e8162bdfa --- /dev/null +++ b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Catch.Scoring +{ + public partial class CatchHealthProcessor : DrainingHealthProcessor + { + public CatchHealthProcessor(double drainStartTime) + : base(drainStartTime) + { + } + } +} diff --git a/osu.Game.Rulesets.Catch/Scoring/LegacyCatchHealthProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/LegacyCatchHealthProcessor.cs new file mode 100644 index 0000000000..ef13ba222c --- /dev/null +++ b/osu.Game.Rulesets.Catch/Scoring/LegacyCatchHealthProcessor.cs @@ -0,0 +1,237 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Catch.Scoring +{ + /// + /// Reference implementation for osu!stable's HP drain. + /// Cannot be used for gameplay. + /// + public partial class LegacyCatchHealthProcessor : DrainingHealthProcessor + { + private const double hp_bar_maximum = 200; + private const double hp_combo_geki = 14; + private const double hp_hit_300 = 6; + private const double hp_slider_tick = 3; + + public Action? OnIterationFail; + public Action? OnIterationSuccess; + public bool ApplyComboEndBonus { get; set; } = true; + + private double lowestHpEver; + private double lowestHpEnd; + private double lowestHpComboEnd; + private double hpRecoveryAvailable; + private double hpMultiplierNormal; + private double hpMultiplierComboEnd; + + public LegacyCatchHealthProcessor(double drainStartTime) + : base(drainStartTime) + { + } + + public override void ApplyBeatmap(IBeatmap beatmap) + { + lowestHpEver = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 195, 160, 60); + lowestHpComboEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 198, 170, 80); + lowestHpEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 198, 180, 80); + hpRecoveryAvailable = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 8, 4, 0); + + base.ApplyBeatmap(beatmap); + } + + protected override void ApplyResultInternal(JudgementResult result) + { + if (!IsSimulating) + throw new NotSupportedException("The legacy catch health processor is not supported for gameplay."); + } + + protected override void RevertResultInternal(JudgementResult result) + { + if (!IsSimulating) + throw new NotSupportedException("The legacy catch health processor is not supported for gameplay."); + } + + protected override void Reset(bool storeResults) + { + hpMultiplierNormal = 1; + hpMultiplierComboEnd = 1; + + base.Reset(storeResults); + } + + protected override double ComputeDrainRate() + { + double testDrop = 0.05; + double currentHp; + double currentHpUncapped; + + List<(HitObject hitObject, bool newCombo)> allObjects = enumerateHitObjects(Beatmap).Where(h => h.hitObject is Fruit || h.hitObject is Droplet || h.hitObject is Banana).ToList(); + + do + { + currentHp = hp_bar_maximum; + currentHpUncapped = hp_bar_maximum; + + double lowestHp = currentHp; + double lastTime = DrainStartTime; + int currentBreak = 0; + bool fail = false; + int comboTooLowCount = 0; + string failReason = string.Empty; + + for (int i = 0; i < allObjects.Count; i++) + { + HitObject h = allObjects[i].hitObject; + + // Find active break (between current and lastTime) + double localLastTime = lastTime; + double breakTime = 0; + + // Subtract any break time from the duration since the last object + if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) + { + BreakPeriod e = Beatmap.Breaks[currentBreak]; + + if (e.StartTime >= localLastTime && e.EndTime <= h.StartTime) + { + // consider break start equal to object end time for version 8+ since drain stops during this time + breakTime = (Beatmap.BeatmapInfo.BeatmapVersion < 8) ? (e.EndTime - e.StartTime) : e.EndTime - localLastTime; + currentBreak++; + } + } + + reduceHp(testDrop * (h.StartTime - lastTime - breakTime)); + + lastTime = h.GetEndTime(); + + if (currentHp < lowestHp) + lowestHp = currentHp; + + if (currentHp <= lowestHpEver) + { + fail = true; + testDrop *= 0.96; + failReason = $"hp too low ({currentHp / hp_bar_maximum} < {lowestHpEver / hp_bar_maximum})"; + break; + } + + switch (h) + { + case Fruit: + if (ApplyComboEndBonus && (i == allObjects.Count - 1 || allObjects[i + 1].newCombo)) + { + increaseHp(hpMultiplierComboEnd * hp_combo_geki + hpMultiplierNormal * hp_hit_300); + + if (currentHp < lowestHpComboEnd) + { + if (++comboTooLowCount > 2) + { + hpMultiplierComboEnd *= 1.07; + hpMultiplierNormal *= 1.03; + fail = true; + failReason = $"combo end hp too low ({currentHp / hp_bar_maximum} < {lowestHpComboEnd / hp_bar_maximum})"; + } + } + } + else + increaseHp(hpMultiplierNormal * hp_hit_300); + + break; + + case Banana: + increaseHp(hpMultiplierNormal / 2); + break; + + case TinyDroplet: + increaseHp(hpMultiplierNormal * hp_slider_tick * 0.1); + break; + + case Droplet: + increaseHp(hpMultiplierNormal * hp_slider_tick); + break; + } + + if (fail) + break; + } + + if (!fail && currentHp < lowestHpEnd) + { + fail = true; + testDrop *= 0.94; + hpMultiplierComboEnd *= 1.01; + hpMultiplierNormal *= 1.01; + failReason = $"end hp too low ({currentHp / hp_bar_maximum} < {lowestHpEnd / hp_bar_maximum})"; + } + + double recovery = (currentHpUncapped - hp_bar_maximum) / allObjects.Count; + + if (!fail && recovery < hpRecoveryAvailable) + { + fail = true; + testDrop *= 0.96; + hpMultiplierComboEnd *= 1.02; + hpMultiplierNormal *= 1.01; + failReason = $"recovery too low ({recovery / hp_bar_maximum} < {hpRecoveryAvailable / hp_bar_maximum})"; + } + + if (fail) + { + OnIterationFail?.Invoke($"FAILED drop {testDrop / hp_bar_maximum}: {failReason}"); + continue; + } + + OnIterationSuccess?.Invoke($"PASSED drop {testDrop / hp_bar_maximum}"); + return testDrop / hp_bar_maximum; + } while (true); + + void reduceHp(double amount) + { + currentHpUncapped = Math.Max(0, currentHpUncapped - amount); + currentHp = Math.Max(0, currentHp - amount); + } + + void increaseHp(double amount) + { + currentHpUncapped += amount; + currentHp = Math.Max(0, Math.Min(hp_bar_maximum, currentHp + amount)); + } + } + + private IEnumerable<(HitObject hitObject, bool newCombo)> enumerateHitObjects(IBeatmap beatmap) + { + return enumerateRecursively(beatmap.HitObjects); + + static IEnumerable<(HitObject hitObject, bool newCombo)> enumerateRecursively(IEnumerable hitObjects) + { + foreach (var hitObject in hitObjects) + { + // The combo end will either be attached to the hitobject itself if it has no children, or the very first child if it has children. + bool newCombo = (hitObject as IHasComboInformation)?.NewCombo ?? false; + + foreach ((HitObject nested, bool _) in enumerateRecursively(hitObject.NestedHitObjects)) + { + yield return (nested, newCombo); + + // Since the combo was attached to the first child, don't attach it to any other child or the parenting hitobject itself. + newCombo = false; + } + + yield return (hitObject, newCombo); + } + } + } + } +} From acf3de5e25f343f492bbf33135bb2160126a94e6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 24 Nov 2023 13:22:46 +0900 Subject: [PATCH 0234/1118] Add CatchHealthProcessor, following legacy calculations --- .../Scoring/CatchHealthProcessor.cs | 160 ++++++++++++++++++ 1 file changed, 160 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs index 7e8162bdfa..1f6a696f98 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs @@ -1,15 +1,175 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Timing; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Scoring { public partial class CatchHealthProcessor : DrainingHealthProcessor { + public Action? OnIterationFail; + public Action? OnIterationSuccess; + + private double lowestHpEver; + private double lowestHpEnd; + private double hpRecoveryAvailable; + private double hpMultiplierNormal; + public CatchHealthProcessor(double drainStartTime) : base(drainStartTime) { } + + public override void ApplyBeatmap(IBeatmap beatmap) + { + lowestHpEver = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.975, 0.8, 0.3); + lowestHpEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.99, 0.9, 0.4); + hpRecoveryAvailable = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.04, 0.02, 0); + + base.ApplyBeatmap(beatmap); + } + + protected override void Reset(bool storeResults) + { + hpMultiplierNormal = 1; + base.Reset(storeResults); + } + + protected override double ComputeDrainRate() + { + double testDrop = 0.00025; + double currentHp; + double currentHpUncapped; + + while (true) + { + currentHp = 1; + currentHpUncapped = 1; + + double lowestHp = currentHp; + double lastTime = DrainStartTime; + int currentBreak = 0; + bool fail = false; + + List allObjects = EnumerateHitObjects(Beatmap).Where(h => h is Fruit || h is Droplet || h is Banana).ToList(); + + for (int i = 0; i < allObjects.Count; i++) + { + HitObject h = allObjects[i]; + + double localLastTime = lastTime; + double breakTime = 0; + + if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) + { + BreakPeriod e = Beatmap.Breaks[currentBreak]; + + if (e.StartTime >= localLastTime && e.EndTime <= h.StartTime) + { + // consider break start equal to object end time for version 8+ since drain stops during this time + breakTime = (Beatmap.BeatmapInfo.BeatmapVersion < 8) ? (e.EndTime - e.StartTime) : e.EndTime - localLastTime; + currentBreak++; + } + } + + reduceHp(testDrop * (h.StartTime - lastTime - breakTime)); + + lastTime = h.GetEndTime(); + + if (currentHp < lowestHp) + lowestHp = currentHp; + + if (currentHp <= lowestHpEver) + { + fail = true; + testDrop *= 0.96; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: hp too low ({currentHp} < {lowestHpEver})"); + break; + } + + increaseHp(h); + } + + if (!fail && currentHp < lowestHpEnd) + { + fail = true; + testDrop *= 0.94; + hpMultiplierNormal *= 1.01; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: end hp too low ({currentHp} < {lowestHpEnd})"); + } + + double recovery = (currentHpUncapped - 1) / allObjects.Count; + + if (!fail && recovery < hpRecoveryAvailable) + { + fail = true; + testDrop *= 0.96; + hpMultiplierNormal *= 1.01; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: recovery too low ({recovery} < {hpRecoveryAvailable})"); + } + + if (!fail) + { + OnIterationSuccess?.Invoke($"PASSED drop {testDrop}"); + return testDrop; + } + } + + void reduceHp(double amount) + { + currentHpUncapped = Math.Max(0, currentHpUncapped - amount); + currentHp = Math.Max(0, currentHp - amount); + } + + void increaseHp(HitObject hitObject) + { + double amount = healthIncreaseFor(hitObject.CreateJudgement().MaxResult); + currentHpUncapped += amount; + currentHp = Math.Max(0, Math.Min(1, currentHp + amount)); + } + } + + protected override double GetHealthIncreaseFor(JudgementResult result) => healthIncreaseFor(result.Type); + + private double healthIncreaseFor(HitResult result) + { + double increase = 0; + + switch (result) + { + case HitResult.SmallTickMiss: + return 0; + + case HitResult.LargeTickMiss: + case HitResult.Miss: + return IBeatmapDifficultyInfo.DifficultyRange(Beatmap.Difficulty.DrainRate, -0.03, -0.125, -0.2); + + case HitResult.SmallTickHit: + increase = 0.0015; + break; + + case HitResult.LargeTickHit: + increase = 0.015; + break; + + case HitResult.Great: + increase = 0.03; + break; + + case HitResult.LargeBonus: + increase = 0.0025; + break; + } + + return hpMultiplierNormal * increase; + } } } From 4ba6450c7703bbd44e97a5b3180a5afc4a5115ab Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 24 Nov 2023 13:32:14 +0900 Subject: [PATCH 0235/1118] Use better break calculation --- .../Scoring/CatchHealthProcessor.cs | 21 +++++++------------ 1 file changed, 7 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs index 1f6a696f98..6d831ad223 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; @@ -65,22 +64,16 @@ namespace osu.Game.Rulesets.Catch.Scoring { HitObject h = allObjects[i]; - double localLastTime = lastTime; - double breakTime = 0; - - if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) + while (currentBreak < Beatmap.Breaks.Count && Beatmap.Breaks[currentBreak].EndTime <= h.StartTime) { - BreakPeriod e = Beatmap.Breaks[currentBreak]; - - if (e.StartTime >= localLastTime && e.EndTime <= h.StartTime) - { - // consider break start equal to object end time for version 8+ since drain stops during this time - breakTime = (Beatmap.BeatmapInfo.BeatmapVersion < 8) ? (e.EndTime - e.StartTime) : e.EndTime - localLastTime; - currentBreak++; - } + // If two hitobjects are separated by a break period, there is no drain for the full duration between the hitobjects. + // This differs from legacy (version < 8) beatmaps which continue draining until the break section is entered, + // but this shouldn't have a noticeable impact in practice. + lastTime = h.StartTime; + currentBreak++; } - reduceHp(testDrop * (h.StartTime - lastTime - breakTime)); + reduceHp(testDrop * (h.StartTime - lastTime)); lastTime = h.GetEndTime(); From bb662676347c79343da3ba480693ff5c272b6878 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 24 Nov 2023 13:46:41 +0900 Subject: [PATCH 0236/1118] Actually use CatchHealthProcessor for the ruleset --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 9ceb78893e..013a709663 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -39,6 +39,8 @@ namespace osu.Game.Rulesets.Catch public override ScoreProcessor CreateScoreProcessor() => new CatchScoreProcessor(); + public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new CatchHealthProcessor(drainStartTime); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new CatchBeatmapConverter(beatmap, this); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new CatchBeatmapProcessor(beatmap); From 4c7d2bb0fbae426e533572ad905531677f3f27fb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 15:14:25 +0900 Subject: [PATCH 0237/1118] Apply NRT to `SpectatorScreen` --- osu.Game/Screens/Spectate/SpectatorScreen.cs | 31 +++++++++----------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 48b5c210b8..f9d5355663 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -1,13 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -33,22 +30,27 @@ namespace osu.Game.Screens.Spectate private readonly List users = new List(); [Resolved] - private BeatmapManager beatmaps { get; set; } + private BeatmapManager beatmaps { get; set; } = null!; [Resolved] - private RulesetStore rulesets { get; set; } + private RulesetStore rulesets { get; set; } = null!; [Resolved] - private SpectatorClient spectatorClient { get; set; } + private SpectatorClient spectatorClient { get; set; } = null!; [Resolved] - private UserLookupCache userLookupCache { get; set; } + private UserLookupCache userLookupCache { get; set; } = null!; + + [Resolved] + private RealmAccess realm { get; set; } = null!; private readonly IBindableDictionary userStates = new BindableDictionary(); private readonly Dictionary userMap = new Dictionary(); private readonly Dictionary gameplayStates = new Dictionary(); + private IDisposable? realmSubscription; + /// /// Creates a new . /// @@ -58,11 +60,6 @@ namespace osu.Game.Screens.Spectate this.users.AddRange(users); } - [Resolved] - private RealmAccess realm { get; set; } - - private IDisposable realmSubscription; - protected override void LoadComplete() { base.LoadComplete(); @@ -90,7 +87,7 @@ namespace osu.Game.Screens.Spectate })); } - private void beatmapsChanged(IRealmCollection items, ChangeSet changes) + private void beatmapsChanged(IRealmCollection items, ChangeSet? changes) { if (changes?.InsertedIndices == null) return; @@ -109,7 +106,7 @@ namespace osu.Game.Screens.Spectate } } - private void onUserStatesChanged(object sender, NotifyDictionaryChangedEventArgs e) + private void onUserStatesChanged(object? sender, NotifyDictionaryChangedEventArgs e) { switch (e.Action) { @@ -207,14 +204,14 @@ namespace osu.Game.Screens.Spectate /// /// The user whose state has changed. /// The new state. - protected abstract void OnNewPlayingUserState(int userId, [NotNull] SpectatorState spectatorState); + protected abstract void OnNewPlayingUserState(int userId, SpectatorState spectatorState); /// /// Starts gameplay for a user. /// /// The user to start gameplay for. /// The gameplay state. - protected abstract void StartGameplay(int userId, [NotNull] SpectatorGameplayState spectatorGameplayState); + protected abstract void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState); /// /// Quits gameplay for a user. @@ -243,7 +240,7 @@ namespace osu.Game.Screens.Spectate { base.Dispose(isDisposing); - if (spectatorClient != null) + if (spectatorClient.IsNotNull()) { foreach ((int userId, var _) in userMap) spectatorClient.StopWatchingUser(userId); From dabbdf674bc7cab4bbb414ef451c2af158ff8be4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 15:19:04 +0900 Subject: [PATCH 0238/1118] Rename `SoloSpectator` to `SoloSpectatorScreen` --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 8 ++++---- osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs | 2 +- .../Play/{SoloSpectator.cs => SoloSpectatorScreen.cs} | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) rename osu.Game/Screens/Play/{SoloSpectator.cs => SoloSpectatorScreen.cs} (98%) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index ffd034e4d2..db74f7d673 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -43,7 +43,7 @@ namespace osu.Game.Tests.Visual.Gameplay private TestSpectatorClient spectatorClient => dependenciesScreen.SpectatorClient; private DependenciesScreen dependenciesScreen; - private SoloSpectator spectatorScreen; + private SoloSpectatorScreen spectatorScreen; private BeatmapSetInfo importedBeatmap; private int importedBeatmapId; @@ -127,7 +127,7 @@ namespace osu.Game.Tests.Visual.Gameplay { loadSpectatingScreen(); - AddAssert("screen hasn't changed", () => Stack.CurrentScreen is SoloSpectator); + AddAssert("screen hasn't changed", () => Stack.CurrentScreen is SoloSpectatorScreen); start(); waitForPlayer(); @@ -255,7 +255,7 @@ namespace osu.Game.Tests.Visual.Gameplay start(-1234); sendFrames(); - AddAssert("screen didn't change", () => Stack.CurrentScreen is SoloSpectator); + AddAssert("screen didn't change", () => Stack.CurrentScreen is SoloSpectatorScreen); } [Test] @@ -381,7 +381,7 @@ namespace osu.Game.Tests.Visual.Gameplay private void loadSpectatingScreen() { - AddStep("load spectator", () => LoadScreen(spectatorScreen = new SoloSpectator(streamingUser))); + AddStep("load spectator", () => LoadScreen(spectatorScreen = new SoloSpectatorScreen(streamingUser))); AddUntilStep("wait for screen load", () => spectatorScreen.LoadState == LoadState.Loaded); } diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs index 02f0a6e80d..6967a61204 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs @@ -212,7 +212,7 @@ namespace osu.Game.Overlays.Dashboard Text = "Spectate", Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, - Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectator(User))), + Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectatorScreen(User))), Enabled = { Value = User.Id != api.LocalUser.Value.Id } } } diff --git a/osu.Game/Screens/Play/SoloSpectator.cs b/osu.Game/Screens/Play/SoloSpectatorScreen.cs similarity index 98% rename from osu.Game/Screens/Play/SoloSpectator.cs rename to osu.Game/Screens/Play/SoloSpectatorScreen.cs index f5af2684d3..59b7d87129 100644 --- a/osu.Game/Screens/Play/SoloSpectator.cs +++ b/osu.Game/Screens/Play/SoloSpectatorScreen.cs @@ -33,7 +33,7 @@ using osuTK; namespace osu.Game.Screens.Play { [Cached(typeof(IPreviewTrackOwner))] - public partial class SoloSpectator : SpectatorScreen, IPreviewTrackOwner + public partial class SoloSpectatorScreen : SpectatorScreen, IPreviewTrackOwner { [NotNull] private readonly APIUser targetUser; @@ -67,7 +67,7 @@ namespace osu.Game.Screens.Play private APIBeatmapSet beatmapSet; - public SoloSpectator([NotNull] APIUser targetUser) + public SoloSpectatorScreen([NotNull] APIUser targetUser) : base(targetUser.Id) { this.targetUser = targetUser; From 335e8efff769c4ccd5cb4b9c66687a1cc16f941d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 15:56:49 +0900 Subject: [PATCH 0239/1118] Apply NRT to `SoloSpecatorScreen` --- osu.Game/Screens/Play/SoloSpectatorScreen.cs | 38 ++++++++++---------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/osu.Game/Screens/Play/SoloSpectatorScreen.cs b/osu.Game/Screens/Play/SoloSpectatorScreen.cs index 59b7d87129..7acdc51fb3 100644 --- a/osu.Game/Screens/Play/SoloSpectatorScreen.cs +++ b/osu.Game/Screens/Play/SoloSpectatorScreen.cs @@ -1,10 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Diagnostics; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -35,39 +32,38 @@ namespace osu.Game.Screens.Play [Cached(typeof(IPreviewTrackOwner))] public partial class SoloSpectatorScreen : SpectatorScreen, IPreviewTrackOwner { - [NotNull] - private readonly APIUser targetUser; + [Resolved] + private IAPIProvider api { get; set; } = null!; [Resolved] - private IAPIProvider api { get; set; } + private PreviewTrackManager previewTrackManager { get; set; } = null!; [Resolved] - private PreviewTrackManager previewTrackManager { get; set; } + private BeatmapManager beatmaps { get; set; } = null!; [Resolved] - private BeatmapManager beatmaps { get; set; } - - [Resolved] - private BeatmapModelDownloader beatmapDownloader { get; set; } + private BeatmapModelDownloader beatmapDownloader { get; set; } = null!; [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - private Container beatmapPanelContainer; - private RoundedButton watchButton; - private SettingsCheckbox automaticDownload; + private Container beatmapPanelContainer = null!; + private RoundedButton watchButton = null!; + private SettingsCheckbox automaticDownload = null!; + + private readonly APIUser targetUser; /// /// The player's immediate online gameplay state. /// This doesn't always reflect the gameplay state being watched. /// - private SpectatorGameplayState immediateSpectatorGameplayState; + private SpectatorGameplayState? immediateSpectatorGameplayState; - private GetBeatmapSetRequest onlineBeatmapRequest; + private GetBeatmapSetRequest? onlineBeatmapRequest; - private APIBeatmapSet beatmapSet; + private APIBeatmapSet? beatmapSet; - public SoloSpectatorScreen([NotNull] APIUser targetUser) + public SoloSpectatorScreen(APIUser targetUser) : base(targetUser.Id) { this.targetUser = targetUser; @@ -199,10 +195,12 @@ namespace osu.Game.Screens.Play previewTrackManager.StopAnyPlaying(this); } - private ScheduledDelegate scheduledStart; + private ScheduledDelegate? scheduledStart; - private void scheduleStart(SpectatorGameplayState spectatorGameplayState) + private void scheduleStart(SpectatorGameplayState? spectatorGameplayState) { + Debug.Assert(spectatorGameplayState != null); + // This function may be called multiple times in quick succession once the screen becomes current again. scheduledStart?.Cancel(); scheduledStart = Schedule(() => From e06d79281dc682fadf83cac72bd0bfc2561e3e65 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 16:40:22 +0900 Subject: [PATCH 0240/1118] Update tests to work with nested screen --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index db74f7d673..9a1fd660d5 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -100,19 +100,18 @@ namespace osu.Game.Tests.Visual.Gameplay start(); - AddUntilStep("wait for player loader", () => (Stack.CurrentScreen as PlayerLoader)?.IsLoaded == true); + AddUntilStep("wait for player loader", () => this.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); AddUntilStep("queue send frames on player load", () => { - var loadingPlayer = (Stack.CurrentScreen as PlayerLoader)?.CurrentPlayer; + var loadingPlayer = this.ChildrenOfType().SingleOrDefault()?.CurrentPlayer; if (loadingPlayer == null) return false; loadingPlayer.OnLoadComplete += _ => - { spectatorClient.SendFramesFromUser(streamingUser.Id, 10, gameplay_start); - }; + return true; }); @@ -360,12 +359,12 @@ namespace osu.Game.Tests.Visual.Gameplay private OsuFramedReplayInputHandler replayHandler => (OsuFramedReplayInputHandler)Stack.ChildrenOfType().First().ReplayInputHandler; - private Player player => Stack.CurrentScreen as Player; + private Player player => this.ChildrenOfType().Single(); private double currentFrameStableTime => player.ChildrenOfType().First().CurrentTime; - private void waitForPlayer() => AddUntilStep("wait for player", () => (Stack.CurrentScreen as Player)?.IsLoaded == true); + private void waitForPlayer() => AddUntilStep("wait for player", () => this.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); private void start(int? beatmapId = null) => AddStep("start play", () => spectatorClient.SendStartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); From ed5375536f1cf96446617b68251354a7f458c2c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 17:14:07 +0900 Subject: [PATCH 0241/1118] Reduce access to various fields and events in `SpectatorClient` Restore some `virtual` specs to appease Moq --- osu.Game/Online/Spectator/SpectatorClient.cs | 22 ++++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index 14e137caf1..e7435adf29 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -46,9 +46,9 @@ namespace osu.Game.Online.Spectator public IBindableList PlayingUsers => playingUsers; /// - /// Whether the local user is playing. + /// Whether the spectated user is playing. /// - protected internal bool IsPlaying { get; private set; } + private bool isPlaying { get; set; } /// /// Called whenever new frames arrive from the server. @@ -58,17 +58,17 @@ namespace osu.Game.Online.Spectator /// /// Called whenever a user starts a play session, or immediately if the user is being watched and currently in a play session. /// - public virtual event Action? OnUserBeganPlaying; + public event Action? OnUserBeganPlaying; /// /// Called whenever a user finishes a play session. /// - public virtual event Action? OnUserFinishedPlaying; + public event Action? OnUserFinishedPlaying; /// /// Called whenever a user-submitted score has been fully processed. /// - public virtual event Action? OnUserScoreProcessed; + public event Action? OnUserScoreProcessed; /// /// A dictionary containing all users currently being watched, with the number of watching components for each user. @@ -114,7 +114,7 @@ namespace osu.Game.Online.Spectator } // re-send state in case it wasn't received - if (IsPlaying) + if (isPlaying) // TODO: this is likely sent out of order after a reconnect scenario. needs further consideration. BeginPlayingInternal(currentScoreToken, currentState); } @@ -179,10 +179,10 @@ namespace osu.Game.Online.Spectator // This schedule is only here to match the one below in `EndPlaying`. Schedule(() => { - if (IsPlaying) + if (isPlaying) throw new InvalidOperationException($"Cannot invoke {nameof(BeginPlaying)} when already playing"); - IsPlaying = true; + isPlaying = true; // transfer state at point of beginning play currentState.BeatmapID = score.ScoreInfo.BeatmapInfo!.OnlineID; @@ -202,7 +202,7 @@ namespace osu.Game.Online.Spectator public void HandleFrame(ReplayFrame frame) => Schedule(() => { - if (!IsPlaying) + if (!isPlaying) { Logger.Log($"Frames arrived at {nameof(SpectatorClient)} outside of gameplay scope and will be ignored."); return; @@ -224,7 +224,7 @@ namespace osu.Game.Online.Spectator // We probably need to find a better way to handle this... Schedule(() => { - if (!IsPlaying) + if (!isPlaying) return; // Disposal can take some time, leading to EndPlaying potentially being called after a future play session. @@ -235,7 +235,7 @@ namespace osu.Game.Online.Spectator if (pendingFrames.Count > 0) purgePendingFrames(); - IsPlaying = false; + isPlaying = false; currentBeatmap = null; if (state.HasPassed) From 73ad92189b0b8026ec966a8051d72d04637f8ead Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Nov 2023 12:07:01 +0900 Subject: [PATCH 0242/1118] Add test coverage of remote player quitting immediately quitting locally --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index 9a1fd660d5..e3a10c655a 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -332,6 +332,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("send quit", () => spectatorClient.SendEndPlay(streamingUser.Id)); AddUntilStep("state is quit", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Quit); + AddAssert("wait for player exit", () => Stack.CurrentScreen is SoloSpectatorScreen); + start(); sendFrames(); waitForPlayer(); From b024065857bbfbf2953153d3ccc2ee90171f8e52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 14:21:52 +0900 Subject: [PATCH 0243/1118] Remove implicit schedule of `abstract` methods in `SpectatorScreen` This allows each implementation to have control over scheduling. Without this, the solo implementation would not be able to handle quit events while watching a player, as it would push a child (gameplay) screen to the stack where the `SpectatorScreen` would usually be. --- .../Spectate/MultiSpectatorScreen.cs | 8 +++---- osu.Game/Screens/Play/SoloSpectatorScreen.cs | 22 ++++++++++++------- osu.Game/Screens/Spectate/SpectatorScreen.cs | 9 +++++--- 3 files changed, 24 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index c1b1127542..eb2980fe1e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -228,7 +228,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate { } - protected override void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState) + protected override void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState) => Schedule(() => { var playerArea = instances.Single(i => i.UserId == userId); @@ -242,9 +242,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate return; playerArea.LoadScore(spectatorGameplayState.Score); - } + }); - protected override void QuitGameplay(int userId) + protected override void QuitGameplay(int userId) => Schedule(() => { RemoveUser(userId); @@ -252,7 +252,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate instance.FadeColour(colours.Gray4, 400, Easing.OutQuint); syncManager.RemoveManagedClock(instance.SpectatorPlayerClock); - } + }); public override bool OnBackButton() { diff --git a/osu.Game/Screens/Play/SoloSpectatorScreen.cs b/osu.Game/Screens/Play/SoloSpectatorScreen.cs index 7acdc51fb3..770018a0c8 100644 --- a/osu.Game/Screens/Play/SoloSpectatorScreen.cs +++ b/osu.Game/Screens/Play/SoloSpectatorScreen.cs @@ -164,27 +164,33 @@ namespace osu.Game.Screens.Play automaticDownload.Current.BindValueChanged(_ => checkForAutomaticDownload()); } - protected override void OnNewPlayingUserState(int userId, SpectatorState spectatorState) + protected override void OnNewPlayingUserState(int userId, SpectatorState spectatorState) => Schedule(() => { clearDisplay(); showBeatmapPanel(spectatorState); - } + }); - protected override void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState) + protected override void StartGameplay(int userId, SpectatorGameplayState spectatorGameplayState) => Schedule(() => { immediateSpectatorGameplayState = spectatorGameplayState; watchButton.Enabled.Value = true; scheduleStart(spectatorGameplayState); - } + }); protected override void QuitGameplay(int userId) { - scheduledStart?.Cancel(); - immediateSpectatorGameplayState = null; - watchButton.Enabled.Value = false; + // Importantly, don't schedule this call, as a child screen may be present (and will cause the schedule to not be run as expected). + this.MakeCurrent(); - clearDisplay(); + Schedule(() => + { + scheduledStart?.Cancel(); + immediateSpectatorGameplayState = null; + watchButton.Enabled.Value = false; + + clearDisplay(); + }); } private void clearDisplay() diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index f9d5355663..21f96c83f5 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -129,7 +129,7 @@ namespace osu.Game.Screens.Spectate switch (newState.State) { case SpectatedUserState.Playing: - Schedule(() => OnNewPlayingUserState(userId, newState)); + OnNewPlayingUserState(userId, newState); startGameplay(userId); break; @@ -173,7 +173,7 @@ namespace osu.Game.Screens.Spectate var gameplayState = new SpectatorGameplayState(score, resolvedRuleset, beatmaps.GetWorkingBeatmap(resolvedBeatmap)); gameplayStates[userId] = gameplayState; - Schedule(() => StartGameplay(userId, gameplayState)); + StartGameplay(userId, gameplayState); } /// @@ -196,11 +196,12 @@ namespace osu.Game.Screens.Spectate markReceivedAllFrames(userId); gameplayStates.Remove(userId); - Schedule(() => QuitGameplay(userId)); + QuitGameplay(userId); } /// /// Invoked when a spectated user's state has changed to a new state indicating the player is currently playing. + /// Thread safety is not guaranteed – should be scheduled as required. /// /// The user whose state has changed. /// The new state. @@ -208,6 +209,7 @@ namespace osu.Game.Screens.Spectate /// /// Starts gameplay for a user. + /// Thread safety is not guaranteed – should be scheduled as required. /// /// The user to start gameplay for. /// The gameplay state. @@ -215,6 +217,7 @@ namespace osu.Game.Screens.Spectate /// /// Quits gameplay for a user. + /// Thread safety is not guaranteed – should be scheduled as required. /// /// The user to quit gameplay for. protected abstract void QuitGameplay(int userId); From 51e2ce500d67c4f634d75af59b04ba69302ef44f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Nov 2023 12:45:03 +0900 Subject: [PATCH 0244/1118] Add test coverage of remote user failing immediately failing locally --- osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index e3a10c655a..f5e4c5da51 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -352,6 +352,8 @@ namespace osu.Game.Tests.Visual.Gameplay AddStep("send failed", () => spectatorClient.SendEndPlay(streamingUser.Id, SpectatedUserState.Failed)); AddUntilStep("state is failed", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Failed); + AddUntilStep("wait for player to fail", () => player.GameplayState.HasFailed); + start(); sendFrames(); waitForPlayer(); From ca93fdc94b07bd1bd539d72265ea65edfe8fa6a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 17:53:35 +0900 Subject: [PATCH 0245/1118] Add visualisation of when a spectated player fails Create a new stack each time for isolation and safety --- .../Spectate/MultiSpectatorScreen.cs | 5 ++++ osu.Game/Screens/Play/SoloSpectatorPlayer.cs | 4 +-- osu.Game/Screens/Play/SoloSpectatorScreen.cs | 14 ++++++++++- osu.Game/Screens/Play/SpectatorPlayer.cs | 12 ++++++++- osu.Game/Screens/Spectate/SpectatorScreen.cs | 25 +++++++++++++++++++ 5 files changed, 56 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs index eb2980fe1e..e2159f0e3b 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Spectate/MultiSpectatorScreen.cs @@ -244,6 +244,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Spectate playerArea.LoadScore(spectatorGameplayState.Score); }); + protected override void FailGameplay(int userId) + { + // We probably want to visualise this in the future. + } + protected override void QuitGameplay(int userId) => Schedule(() => { RemoveUser(userId); diff --git a/osu.Game/Screens/Play/SoloSpectatorPlayer.cs b/osu.Game/Screens/Play/SoloSpectatorPlayer.cs index c9d1f4acaa..8d25a0148d 100644 --- a/osu.Game/Screens/Play/SoloSpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SoloSpectatorPlayer.cs @@ -17,8 +17,8 @@ namespace osu.Game.Screens.Play protected override UserActivity InitialActivity => new UserActivity.SpectatingUser(Score.ScoreInfo); - public SoloSpectatorPlayer(Score score, PlayerConfiguration configuration = null) - : base(score, configuration) + public SoloSpectatorPlayer(Score score) + : base(score, new PlayerConfiguration { AllowUserInteraction = false }) { this.score = score; } diff --git a/osu.Game/Screens/Play/SoloSpectatorScreen.cs b/osu.Game/Screens/Play/SoloSpectatorScreen.cs index 770018a0c8..d4a9fc4b30 100644 --- a/osu.Game/Screens/Play/SoloSpectatorScreen.cs +++ b/osu.Game/Screens/Play/SoloSpectatorScreen.cs @@ -178,6 +178,19 @@ namespace osu.Game.Screens.Play scheduleStart(spectatorGameplayState); }); + protected override void FailGameplay(int userId) + { + if (this.GetChildScreen() is SpectatorPlayer player) + player.AllowFail(); + + Schedule(() => + { + scheduledStart?.Cancel(); + immediateSpectatorGameplayState = null; + clearDisplay(); + }); + } + protected override void QuitGameplay(int userId) { // Importantly, don't schedule this call, as a child screen may be present (and will cause the schedule to not be run as expected). @@ -187,7 +200,6 @@ namespace osu.Game.Screens.Play { scheduledStart?.Cancel(); immediateSpectatorGameplayState = null; - watchButton.Enabled.Value = false; clearDisplay(); }); diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index 30a5ac3741..bc95fa190c 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -25,7 +25,15 @@ namespace osu.Game.Screens.Play private readonly Score score; - protected override bool CheckModsAllowFailure() => false; // todo: better support starting mid-way through beatmap + protected override bool CheckModsAllowFailure() + { + if (!allowFail) + return false; + + return base.CheckModsAllowFailure(); + } + + private bool allowFail; protected SpectatorPlayer(Score score, PlayerConfiguration configuration = null) : base(configuration) @@ -123,5 +131,7 @@ namespace osu.Game.Screens.Play if (SpectatorClient != null) SpectatorClient.OnNewFrames -= userSentFrames; } + + public void AllowFail() => allowFail = true; } } diff --git a/osu.Game/Screens/Spectate/SpectatorScreen.cs b/osu.Game/Screens/Spectate/SpectatorScreen.cs index 21f96c83f5..c4aef3c878 100644 --- a/osu.Game/Screens/Spectate/SpectatorScreen.cs +++ b/osu.Game/Screens/Spectate/SpectatorScreen.cs @@ -137,6 +137,10 @@ namespace osu.Game.Screens.Spectate markReceivedAllFrames(userId); break; + case SpectatedUserState.Failed: + failGameplay(userId); + break; + case SpectatedUserState.Quit: quitGameplay(userId); break; @@ -185,6 +189,20 @@ namespace osu.Game.Screens.Spectate gameplayState.Score.Replay.HasReceivedAllFrames = true; } + private void failGameplay(int userId) + { + if (!userMap.ContainsKey(userId)) + return; + + if (!gameplayStates.ContainsKey(userId)) + return; + + markReceivedAllFrames(userId); + + gameplayStates.Remove(userId); + FailGameplay(userId); + } + private void quitGameplay(int userId) { if (!userMap.ContainsKey(userId)) @@ -222,6 +240,13 @@ namespace osu.Game.Screens.Spectate /// The user to quit gameplay for. protected abstract void QuitGameplay(int userId); + /// + /// Fails gameplay for a user. + /// Thread safety is not guaranteed – should be scheduled as required. + /// + /// The user to fail gameplay for. + protected abstract void FailGameplay(int userId); + /// /// Stops spectating a user. /// From 8375dd72d6e691ef393aecf1e2c3c418f46cd128 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Nov 2023 10:01:59 +0900 Subject: [PATCH 0246/1118] Add xmldoc to new `AllowFail` method --- osu.Game/Screens/Play/Player.cs | 7 +++++++ osu.Game/Screens/Play/SpectatorPlayer.cs | 8 ++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 8c7fc551ba..ad725e1a90 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -894,6 +894,13 @@ namespace osu.Game.Screens.Play #region Fail Logic + /// + /// Invoked when gameplay has permanently failed. + /// + protected virtual void OnFail() + { + } + protected FailOverlay FailOverlay { get; private set; } private FailAnimationContainer failAnimationContainer; diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index bc95fa190c..d1404ac184 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -68,6 +68,12 @@ namespace osu.Game.Screens.Play }, true); } + /// + /// Should be called when it is apparent that the player being spectated has failed. + /// This will subsequently stop blocking the fail screen from displaying (usually done out of safety). + /// + public void AllowFail() => allowFail = true; + protected override void StartGameplay() { base.StartGameplay(); @@ -131,7 +137,5 @@ namespace osu.Game.Screens.Play if (SpectatorClient != null) SpectatorClient.OnNewFrames -= userSentFrames; } - - public void AllowFail() => allowFail = true; } } From 4ad3cb3b4952aeb09847fe4e20fd581eaf8a52a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 18:20:22 +0900 Subject: [PATCH 0247/1118] Submit and send failed spectator state more aggressively --- osu.Game/Screens/Play/SubmittingPlayer.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 30fecbe149..14aaa8f638 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -54,6 +54,8 @@ namespace osu.Game.Screens.Play } AddInternal(new PlayerTouchInputDetector()); + + HealthProcessor.Failed += onFail; } protected override void LoadAsyncComplete() @@ -165,10 +167,21 @@ namespace osu.Game.Screens.Play spectatorClient.BeginPlaying(token, GameplayState, Score); } + private bool onFail() + { + submitFromFailOrQuit(); + return true; + } + public override bool OnExiting(ScreenExitEvent e) { bool exiting = base.OnExiting(e); + submitFromFailOrQuit(); + return exiting; + } + private void submitFromFailOrQuit() + { if (LoadedBeatmapSuccessfully) { Task.Run(async () => @@ -177,8 +190,6 @@ namespace osu.Game.Screens.Play spectatorClient.EndPlaying(GameplayState); }).FireAndForget(); } - - return exiting; } /// From ef5dd245894e3ea4b8658929054b92276b91c4e4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Nov 2023 10:28:15 +0900 Subject: [PATCH 0248/1118] Update failing test coverage and fix `onFail` being called too often --- .../Visual/Gameplay/TestScenePlayerScoreSubmission.cs | 1 - osu.Game/Screens/Play/Player.cs | 1 + osu.Game/Screens/Play/SubmittingPlayer.cs | 7 +++---- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index 1a7ea20cc0..f75a2656ef 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -179,7 +179,6 @@ namespace osu.Game.Tests.Visual.Gameplay addFakeHit(); AddUntilStep("wait for fail", () => Player.GameplayState.HasFailed); - AddStep("exit", () => Player.Exit()); AddUntilStep("wait for submission", () => Player.SubmittedScore != null); AddAssert("ensure failing submission", () => Player.SubmittedScore.ScoreInfo.Passed == false); diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ad725e1a90..b469ab443c 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -933,6 +933,7 @@ namespace osu.Game.Screens.Play if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail)) Restart(true); + OnFail(); return true; } diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 14aaa8f638..785164178a 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -54,8 +54,6 @@ namespace osu.Game.Screens.Play } AddInternal(new PlayerTouchInputDetector()); - - HealthProcessor.Failed += onFail; } protected override void LoadAsyncComplete() @@ -167,10 +165,11 @@ namespace osu.Game.Screens.Play spectatorClient.BeginPlaying(token, GameplayState, Score); } - private bool onFail() + protected override void OnFail() { + base.OnFail(); + submitFromFailOrQuit(); - return true; } public override bool OnExiting(ScreenExitEvent e) From d3a55d83c000dd1bf0174a9811585e214d605346 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 23 Nov 2023 11:05:52 +0900 Subject: [PATCH 0249/1118] Schedule `FailScore` inside `onFail` instead of `onFailComplete` --- osu.Game/Screens/Play/Player.cs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b469ab443c..ff00b52f71 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -930,10 +930,22 @@ namespace osu.Game.Screens.Play failAnimationContainer.Start(); - if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail)) - Restart(true); + // Failures can be triggered either by a judgement, or by a mod. + // + // For the case of a judgement, due to ordering considerations, ScoreProcessor will not have received + // the final judgement which triggered the failure yet (see DrawableRuleset.NewResult handling above). + // + // A schedule here ensures that any lingering judgements from the current frame are applied before we + // finalise the score as "failed". + Schedule(() => + { + ScoreProcessor.FailScore(Score.ScoreInfo); + OnFail(); + + if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail)) + Restart(true); + }); - OnFail(); return true; } @@ -942,11 +954,6 @@ namespace osu.Game.Screens.Play /// private void onFailComplete() { - // fail completion is a good point to mark a score as failed, - // since the last judgement that caused the fail only applies to score processor after onFail. - // todo: this should probably be handled better. - ScoreProcessor.FailScore(Score.ScoreInfo); - GameplayClockContainer.Stop(); FailOverlay.Retries = RestartCount; From 7ceb49fbc095ae9b8bd91a6bf4c6c2d8763186f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 14:58:57 +0900 Subject: [PATCH 0250/1118] Add extra test coverage and handle case where still at loading screen --- .../Visual/Gameplay/TestSceneSpectator.cs | 64 +++++++++++++------ osu.Game/Screens/Play/SoloSpectatorScreen.cs | 33 +++++----- 2 files changed, 61 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs index f5e4c5da51..1c7ede2b19 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSpectator.cs @@ -81,7 +81,7 @@ namespace osu.Game.Tests.Visual.Gameplay start(); - waitForPlayer(); + waitForPlayerCurrent(); sendFrames(startTime: gameplay_start); @@ -115,7 +115,7 @@ namespace osu.Game.Tests.Visual.Gameplay return true; }); - waitForPlayer(); + waitForPlayerCurrent(); AddUntilStep("state is playing", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Playing); AddAssert("time is greater than seek target", () => currentFrameStableTime, () => Is.GreaterThan(gameplay_start)); @@ -129,7 +129,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("screen hasn't changed", () => Stack.CurrentScreen is SoloSpectatorScreen); start(); - waitForPlayer(); + waitForPlayerCurrent(); sendFrames(); AddAssert("ensure frames arrived", () => replayHandler.HasFrames); @@ -155,7 +155,7 @@ namespace osu.Game.Tests.Visual.Gameplay loadSpectatingScreen(); start(); - waitForPlayer(); + waitForPlayerCurrent(); checkPaused(true); // send enough frames to ensure play won't be paused @@ -171,7 +171,7 @@ namespace osu.Game.Tests.Visual.Gameplay sendFrames(300); loadSpectatingScreen(); - waitForPlayer(); + waitForPlayerCurrent(); sendFrames(300); @@ -186,7 +186,7 @@ namespace osu.Game.Tests.Visual.Gameplay start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); Player lastPlayer = null; AddStep("store first player", () => lastPlayer = player); @@ -194,7 +194,7 @@ namespace osu.Game.Tests.Visual.Gameplay start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); AddAssert("player is different", () => lastPlayer != player); } @@ -205,7 +205,7 @@ namespace osu.Game.Tests.Visual.Gameplay start(); - waitForPlayer(); + waitForPlayerCurrent(); checkPaused(true); sendFrames(); @@ -223,7 +223,7 @@ namespace osu.Game.Tests.Visual.Gameplay start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); AddStep("stop spectating", () => (Stack.CurrentScreen as Player)?.Exit()); AddUntilStep("spectating stopped", () => spectatorScreen.GetChildScreen() == null); @@ -236,14 +236,14 @@ namespace osu.Game.Tests.Visual.Gameplay start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); AddStep("stop spectating", () => (Stack.CurrentScreen as Player)?.Exit()); AddUntilStep("spectating stopped", () => spectatorScreen.GetChildScreen() == null); // host starts playing a new session start(); - waitForPlayer(); + waitForPlayerCurrent(); } [Test] @@ -298,7 +298,7 @@ namespace osu.Game.Tests.Visual.Gameplay start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); AddUntilStep("state is playing", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Playing); } @@ -309,14 +309,14 @@ namespace osu.Game.Tests.Visual.Gameplay start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); AddStep("send passed", () => spectatorClient.SendEndPlay(streamingUser.Id, SpectatedUserState.Passed)); AddUntilStep("state is passed", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Passed); start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); AddUntilStep("state is playing", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Playing); } @@ -327,7 +327,7 @@ namespace osu.Game.Tests.Visual.Gameplay start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); AddStep("send quit", () => spectatorClient.SendEndPlay(streamingUser.Id)); AddUntilStep("state is quit", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Quit); @@ -336,18 +336,19 @@ namespace osu.Game.Tests.Visual.Gameplay start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); AddUntilStep("state is playing", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Playing); } [Test] - public void TestFailedState() + public void TestFailedStateDuringPlay() { loadSpectatingScreen(); start(); sendFrames(); - waitForPlayer(); + + waitForPlayerCurrent(); AddStep("send failed", () => spectatorClient.SendEndPlay(streamingUser.Id, SpectatedUserState.Failed)); AddUntilStep("state is failed", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Failed); @@ -356,7 +357,28 @@ namespace osu.Game.Tests.Visual.Gameplay start(); sendFrames(); - waitForPlayer(); + waitForPlayerCurrent(); + AddUntilStep("state is playing", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Playing); + } + + [Test] + public void TestFailedStateDuringLoading() + { + loadSpectatingScreen(); + + start(); + sendFrames(); + + waitForPlayerLoader(); + + AddStep("send failed", () => spectatorClient.SendEndPlay(streamingUser.Id, SpectatedUserState.Failed)); + AddUntilStep("state is failed", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Failed); + + AddAssert("wait for player exit", () => Stack.CurrentScreen is SoloSpectatorScreen); + + start(); + sendFrames(); + waitForPlayerCurrent(); AddUntilStep("state is playing", () => spectatorClient.WatchedUserStates[streamingUser.Id].State == SpectatedUserState.Playing); } @@ -368,7 +390,9 @@ namespace osu.Game.Tests.Visual.Gameplay private double currentFrameStableTime => player.ChildrenOfType().First().CurrentTime; - private void waitForPlayer() => AddUntilStep("wait for player", () => this.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + private void waitForPlayerLoader() => AddUntilStep("wait for loading", () => this.ChildrenOfType().SingleOrDefault()?.IsLoaded == true); + + private void waitForPlayerCurrent() => AddUntilStep("wait for player current", () => this.ChildrenOfType().SingleOrDefault()?.IsCurrentScreen() == true); private void start(int? beatmapId = null) => AddStep("start play", () => spectatorClient.SendStartPlay(streamingUser.Id, beatmapId ?? importedBeatmapId)); diff --git a/osu.Game/Screens/Play/SoloSpectatorScreen.cs b/osu.Game/Screens/Play/SoloSpectatorScreen.cs index d4a9fc4b30..2db751402c 100644 --- a/osu.Game/Screens/Play/SoloSpectatorScreen.cs +++ b/osu.Game/Screens/Play/SoloSpectatorScreen.cs @@ -180,31 +180,32 @@ namespace osu.Game.Screens.Play protected override void FailGameplay(int userId) { - if (this.GetChildScreen() is SpectatorPlayer player) - player.AllowFail(); - - Schedule(() => + if (this.GetChildScreen() is SpectatorPlayerLoader loader) { - scheduledStart?.Cancel(); - immediateSpectatorGameplayState = null; - clearDisplay(); - }); + if (loader.GetChildScreen() is SpectatorPlayer player) + { + player.AllowFail(); + resetStartState(); + } + else + QuitGameplay(userId); + } } protected override void QuitGameplay(int userId) { // Importantly, don't schedule this call, as a child screen may be present (and will cause the schedule to not be run as expected). this.MakeCurrent(); - - Schedule(() => - { - scheduledStart?.Cancel(); - immediateSpectatorGameplayState = null; - - clearDisplay(); - }); + resetStartState(); } + private void resetStartState() => Schedule(() => + { + scheduledStart?.Cancel(); + immediateSpectatorGameplayState = null; + clearDisplay(); + }); + private void clearDisplay() { watchButton.Enabled.Value = false; From 289cda71b236d98f622f5c8920ba91dd14bb40b8 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 24 Nov 2023 15:06:51 +0900 Subject: [PATCH 0251/1118] Fix inspections --- osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs | 8 ++++---- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs index 4bcd6b100a..459a8b0df5 100644 --- a/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs +++ b/osu.Game.Tests/Visual/Navigation/TestScenePresentScore.cs @@ -97,7 +97,7 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestFromSongSelectWithFilter([Values] ScorePresentType type) { - AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo?.Invoke()); AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded); AddStep("filter to nothing", () => ((PlaySongSelect)Game.ScreenStack.CurrentScreen).FilterControl.CurrentTextSearch.Value = "fdsajkl;fgewq"); @@ -110,7 +110,7 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestFromSongSelectWithConvertRulesetChange([Values] ScorePresentType type) { - AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo?.Invoke()); AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded); AddStep("set convert to false", () => Game.LocalConfig.SetValue(OsuSetting.ShowConvertedBeatmaps, false)); @@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestFromSongSelect([Values] ScorePresentType type) { - AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo?.Invoke()); AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded); var firstImport = importScore(1); @@ -135,7 +135,7 @@ namespace osu.Game.Tests.Visual.Navigation [Test] public void TestFromSongSelectDifferentRuleset([Values] ScorePresentType type) { - AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo.Invoke()); + AddStep("enter song select", () => Game.ChildrenOfType().Single().OnSolo?.Invoke()); AddUntilStep("song select is current", () => Game.ScreenStack.CurrentScreen is PlaySongSelect songSelect && songSelect.BeatmapSetsLoaded); var firstImport = importScore(1); diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 8173375c29..78eb410a48 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -56,7 +56,7 @@ namespace osu.Game.Screens.Menu /// Assign the that this ButtonSystem should manage the position of. /// /// The instance of the logo to be assigned. If null, we are suspending from the screen that uses this ButtonSystem. - public void SetOsuLogo(OsuLogo logo) + public void SetOsuLogo(OsuLogo? logo) { this.logo = logo; From c126c46e2d25bcf2b1853ef80b869a230a9bb8ff Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 24 Nov 2023 15:43:57 +0900 Subject: [PATCH 0252/1118] Remove legacy implementations (moved to osu-tools) --- .../Scoring/LegacyCatchHealthProcessor.cs | 237 ------------------ .../Scoring/LegacyOsuHealthProcessor.cs | 215 ---------------- 2 files changed, 452 deletions(-) delete mode 100644 osu.Game.Rulesets.Catch/Scoring/LegacyCatchHealthProcessor.cs delete mode 100644 osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs diff --git a/osu.Game.Rulesets.Catch/Scoring/LegacyCatchHealthProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/LegacyCatchHealthProcessor.cs deleted file mode 100644 index ef13ba222c..0000000000 --- a/osu.Game.Rulesets.Catch/Scoring/LegacyCatchHealthProcessor.cs +++ /dev/null @@ -1,237 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Catch.Scoring -{ - /// - /// Reference implementation for osu!stable's HP drain. - /// Cannot be used for gameplay. - /// - public partial class LegacyCatchHealthProcessor : DrainingHealthProcessor - { - private const double hp_bar_maximum = 200; - private const double hp_combo_geki = 14; - private const double hp_hit_300 = 6; - private const double hp_slider_tick = 3; - - public Action? OnIterationFail; - public Action? OnIterationSuccess; - public bool ApplyComboEndBonus { get; set; } = true; - - private double lowestHpEver; - private double lowestHpEnd; - private double lowestHpComboEnd; - private double hpRecoveryAvailable; - private double hpMultiplierNormal; - private double hpMultiplierComboEnd; - - public LegacyCatchHealthProcessor(double drainStartTime) - : base(drainStartTime) - { - } - - public override void ApplyBeatmap(IBeatmap beatmap) - { - lowestHpEver = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 195, 160, 60); - lowestHpComboEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 198, 170, 80); - lowestHpEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 198, 180, 80); - hpRecoveryAvailable = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 8, 4, 0); - - base.ApplyBeatmap(beatmap); - } - - protected override void ApplyResultInternal(JudgementResult result) - { - if (!IsSimulating) - throw new NotSupportedException("The legacy catch health processor is not supported for gameplay."); - } - - protected override void RevertResultInternal(JudgementResult result) - { - if (!IsSimulating) - throw new NotSupportedException("The legacy catch health processor is not supported for gameplay."); - } - - protected override void Reset(bool storeResults) - { - hpMultiplierNormal = 1; - hpMultiplierComboEnd = 1; - - base.Reset(storeResults); - } - - protected override double ComputeDrainRate() - { - double testDrop = 0.05; - double currentHp; - double currentHpUncapped; - - List<(HitObject hitObject, bool newCombo)> allObjects = enumerateHitObjects(Beatmap).Where(h => h.hitObject is Fruit || h.hitObject is Droplet || h.hitObject is Banana).ToList(); - - do - { - currentHp = hp_bar_maximum; - currentHpUncapped = hp_bar_maximum; - - double lowestHp = currentHp; - double lastTime = DrainStartTime; - int currentBreak = 0; - bool fail = false; - int comboTooLowCount = 0; - string failReason = string.Empty; - - for (int i = 0; i < allObjects.Count; i++) - { - HitObject h = allObjects[i].hitObject; - - // Find active break (between current and lastTime) - double localLastTime = lastTime; - double breakTime = 0; - - // Subtract any break time from the duration since the last object - if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) - { - BreakPeriod e = Beatmap.Breaks[currentBreak]; - - if (e.StartTime >= localLastTime && e.EndTime <= h.StartTime) - { - // consider break start equal to object end time for version 8+ since drain stops during this time - breakTime = (Beatmap.BeatmapInfo.BeatmapVersion < 8) ? (e.EndTime - e.StartTime) : e.EndTime - localLastTime; - currentBreak++; - } - } - - reduceHp(testDrop * (h.StartTime - lastTime - breakTime)); - - lastTime = h.GetEndTime(); - - if (currentHp < lowestHp) - lowestHp = currentHp; - - if (currentHp <= lowestHpEver) - { - fail = true; - testDrop *= 0.96; - failReason = $"hp too low ({currentHp / hp_bar_maximum} < {lowestHpEver / hp_bar_maximum})"; - break; - } - - switch (h) - { - case Fruit: - if (ApplyComboEndBonus && (i == allObjects.Count - 1 || allObjects[i + 1].newCombo)) - { - increaseHp(hpMultiplierComboEnd * hp_combo_geki + hpMultiplierNormal * hp_hit_300); - - if (currentHp < lowestHpComboEnd) - { - if (++comboTooLowCount > 2) - { - hpMultiplierComboEnd *= 1.07; - hpMultiplierNormal *= 1.03; - fail = true; - failReason = $"combo end hp too low ({currentHp / hp_bar_maximum} < {lowestHpComboEnd / hp_bar_maximum})"; - } - } - } - else - increaseHp(hpMultiplierNormal * hp_hit_300); - - break; - - case Banana: - increaseHp(hpMultiplierNormal / 2); - break; - - case TinyDroplet: - increaseHp(hpMultiplierNormal * hp_slider_tick * 0.1); - break; - - case Droplet: - increaseHp(hpMultiplierNormal * hp_slider_tick); - break; - } - - if (fail) - break; - } - - if (!fail && currentHp < lowestHpEnd) - { - fail = true; - testDrop *= 0.94; - hpMultiplierComboEnd *= 1.01; - hpMultiplierNormal *= 1.01; - failReason = $"end hp too low ({currentHp / hp_bar_maximum} < {lowestHpEnd / hp_bar_maximum})"; - } - - double recovery = (currentHpUncapped - hp_bar_maximum) / allObjects.Count; - - if (!fail && recovery < hpRecoveryAvailable) - { - fail = true; - testDrop *= 0.96; - hpMultiplierComboEnd *= 1.02; - hpMultiplierNormal *= 1.01; - failReason = $"recovery too low ({recovery / hp_bar_maximum} < {hpRecoveryAvailable / hp_bar_maximum})"; - } - - if (fail) - { - OnIterationFail?.Invoke($"FAILED drop {testDrop / hp_bar_maximum}: {failReason}"); - continue; - } - - OnIterationSuccess?.Invoke($"PASSED drop {testDrop / hp_bar_maximum}"); - return testDrop / hp_bar_maximum; - } while (true); - - void reduceHp(double amount) - { - currentHpUncapped = Math.Max(0, currentHpUncapped - amount); - currentHp = Math.Max(0, currentHp - amount); - } - - void increaseHp(double amount) - { - currentHpUncapped += amount; - currentHp = Math.Max(0, Math.Min(hp_bar_maximum, currentHp + amount)); - } - } - - private IEnumerable<(HitObject hitObject, bool newCombo)> enumerateHitObjects(IBeatmap beatmap) - { - return enumerateRecursively(beatmap.HitObjects); - - static IEnumerable<(HitObject hitObject, bool newCombo)> enumerateRecursively(IEnumerable hitObjects) - { - foreach (var hitObject in hitObjects) - { - // The combo end will either be attached to the hitobject itself if it has no children, or the very first child if it has children. - bool newCombo = (hitObject as IHasComboInformation)?.NewCombo ?? false; - - foreach ((HitObject nested, bool _) in enumerateRecursively(hitObject.NestedHitObjects)) - { - yield return (nested, newCombo); - - // Since the combo was attached to the first child, don't attach it to any other child or the parenting hitobject itself. - newCombo = false; - } - - yield return (hitObject, newCombo); - } - } - } - } -} diff --git a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs deleted file mode 100644 index e92c3c9b97..0000000000 --- a/osu.Game.Rulesets.Osu/Scoring/LegacyOsuHealthProcessor.cs +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Linq; -using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Timing; -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Osu.Scoring -{ - /// - /// Reference implementation for osu!stable's HP drain. - /// Cannot be used for gameplay. - /// - public partial class LegacyOsuHealthProcessor : DrainingHealthProcessor - { - private const double hp_bar_maximum = 200; - private const double hp_combo_geki = 14; - private const double hp_hit_300 = 6; - private const double hp_slider_repeat = 4; - private const double hp_slider_tick = 3; - - public Action? OnIterationFail; - public Action? OnIterationSuccess; - public bool ApplyComboEndBonus { get; set; } = true; - - private double lowestHpEver; - private double lowestHpEnd; - private double lowestHpComboEnd; - private double hpRecoveryAvailable; - private double hpMultiplierNormal; - private double hpMultiplierComboEnd; - - public LegacyOsuHealthProcessor(double drainStartTime) - : base(drainStartTime) - { - } - - public override void ApplyBeatmap(IBeatmap beatmap) - { - lowestHpEver = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 195, 160, 60); - lowestHpComboEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 198, 170, 80); - lowestHpEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 198, 180, 80); - hpRecoveryAvailable = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 8, 4, 0); - - base.ApplyBeatmap(beatmap); - } - - protected override void ApplyResultInternal(JudgementResult result) - { - if (!IsSimulating) - throw new NotSupportedException("The legacy osu! health processor is not supported for gameplay."); - } - - protected override void RevertResultInternal(JudgementResult result) - { - if (!IsSimulating) - throw new NotSupportedException("The legacy osu! health processor is not supported for gameplay."); - } - - protected override void Reset(bool storeResults) - { - hpMultiplierNormal = 1; - hpMultiplierComboEnd = 1; - - base.Reset(storeResults); - } - - protected override double ComputeDrainRate() - { - double testDrop = 0.05; - double currentHp; - double currentHpUncapped; - - do - { - currentHp = hp_bar_maximum; - currentHpUncapped = hp_bar_maximum; - - double lowestHp = currentHp; - double lastTime = DrainStartTime; - int currentBreak = 0; - bool fail = false; - int comboTooLowCount = 0; - string failReason = string.Empty; - - for (int i = 0; i < Beatmap.HitObjects.Count; i++) - { - HitObject h = Beatmap.HitObjects[i]; - - // Find active break (between current and lastTime) - double localLastTime = lastTime; - double breakTime = 0; - - // Subtract any break time from the duration since the last object - if (Beatmap.Breaks.Count > 0 && currentBreak < Beatmap.Breaks.Count) - { - BreakPeriod e = Beatmap.Breaks[currentBreak]; - - if (e.StartTime >= localLastTime && e.EndTime <= h.StartTime) - { - // consider break start equal to object end time for version 8+ since drain stops during this time - breakTime = (Beatmap.BeatmapInfo.BeatmapVersion < 8) ? (e.EndTime - e.StartTime) : e.EndTime - localLastTime; - currentBreak++; - } - } - - reduceHp(testDrop * (h.StartTime - lastTime - breakTime)); - - lastTime = h.GetEndTime(); - - if (currentHp < lowestHp) - lowestHp = currentHp; - - if (currentHp <= lowestHpEver) - { - fail = true; - testDrop *= 0.96; - failReason = $"hp too low ({currentHp / hp_bar_maximum} < {lowestHpEver / hp_bar_maximum})"; - break; - } - - double hpReduction = testDrop * (h.GetEndTime() - h.StartTime); - double hpOverkill = Math.Max(0, hpReduction - currentHp); - reduceHp(hpReduction); - - if (h is Slider slider) - { - for (int j = 0; j < slider.RepeatCount + 2; j++) - increaseHp(hpMultiplierNormal * hp_slider_repeat); - foreach (var _ in slider.NestedHitObjects.OfType()) - increaseHp(hpMultiplierNormal * hp_slider_tick); - } - else if (h is Spinner spinner) - { - foreach (var _ in spinner.NestedHitObjects.Where(t => t is not SpinnerBonusTick)) - increaseHp(hpMultiplierNormal * 1.7); - } - - if (hpOverkill > 0 && currentHp - hpOverkill <= lowestHpEver) - { - fail = true; - testDrop *= 0.96; - failReason = $"overkill ({currentHp / hp_bar_maximum} - {hpOverkill / hp_bar_maximum} <= {lowestHpEver / hp_bar_maximum})"; - break; - } - - if (ApplyComboEndBonus && (i == Beatmap.HitObjects.Count - 1 || ((OsuHitObject)Beatmap.HitObjects[i + 1]).NewCombo)) - { - increaseHp(hpMultiplierComboEnd * hp_combo_geki + hpMultiplierNormal * hp_hit_300); - - if (currentHp < lowestHpComboEnd) - { - if (++comboTooLowCount > 2) - { - hpMultiplierComboEnd *= 1.07; - hpMultiplierNormal *= 1.03; - fail = true; - failReason = $"combo end hp too low ({currentHp / hp_bar_maximum} < {lowestHpComboEnd / hp_bar_maximum})"; - break; - } - } - } - else - increaseHp(hpMultiplierNormal * hp_hit_300); - } - - if (!fail && currentHp < lowestHpEnd) - { - fail = true; - testDrop *= 0.94; - hpMultiplierComboEnd *= 1.01; - hpMultiplierNormal *= 1.01; - failReason = $"end hp too low ({currentHp / hp_bar_maximum} < {lowestHpEnd / hp_bar_maximum})"; - } - - double recovery = (currentHpUncapped - hp_bar_maximum) / Beatmap.HitObjects.Count; - - if (!fail && recovery < hpRecoveryAvailable) - { - fail = true; - testDrop *= 0.96; - hpMultiplierComboEnd *= 1.02; - hpMultiplierNormal *= 1.01; - failReason = $"recovery too low ({recovery / hp_bar_maximum} < {hpRecoveryAvailable / hp_bar_maximum})"; - } - - if (fail) - { - OnIterationFail?.Invoke($"FAILED drop {testDrop / hp_bar_maximum}: {failReason}"); - continue; - } - - OnIterationSuccess?.Invoke($"PASSED drop {testDrop / hp_bar_maximum}"); - return testDrop / hp_bar_maximum; - } while (true); - - void reduceHp(double amount) - { - currentHpUncapped = Math.Max(0, currentHpUncapped - amount); - currentHp = Math.Max(0, currentHp - amount); - } - - void increaseHp(double amount) - { - currentHpUncapped += amount; - currentHp = Math.Max(0, Math.Min(hp_bar_maximum, currentHp + amount)); - } - } - } -} From 36b45d34f7415327f80ed1a243a30af73390805f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 16:39:29 +0900 Subject: [PATCH 0253/1118] Check drag location on mouse down instead of drag start to avoid lenience issues --- osu.Game/Overlays/ChatOverlay.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 724f77ad71..54d5952bc3 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -251,10 +251,14 @@ namespace osu.Game.Overlays { } + protected override bool OnMouseDown(MouseDownEvent e) + { + isDraggingTopBar = topBar.DragBar.IsHovered; + return base.OnMouseDown(e); + } + protected override bool OnDragStart(DragStartEvent e) { - isDraggingTopBar = topBar.IsHovered; - if (!isDraggingTopBar) return base.OnDragStart(e); From 7600595e5dba09c0cd6b3a034be86b427dfc6a03 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 16:39:54 +0900 Subject: [PATCH 0254/1118] Add drag bar on chat overlay to better signal resizability --- osu.Game/Overlays/Chat/ChatOverlayTopBar.cs | 80 +++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs index 0410174dc1..44cb07ca91 100644 --- a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs +++ b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -23,6 +22,8 @@ namespace osu.Game.Overlays.Chat private Color4 backgroundColour; + public Drawable DragBar = null!; + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, TextureStore textures) { @@ -50,7 +51,7 @@ namespace osu.Game.Overlays.Chat Anchor = Anchor.Centre, Origin = Anchor.Centre, Texture = textures.Get("Icons/Hexacons/messaging"), - Size = new Vector2(18), + Size = new Vector2(24), }, // Placeholder text new OsuSpriteText @@ -64,19 +65,90 @@ namespace osu.Game.Overlays.Chat }, }, }, + DragBar = new DragArea + { + Alpha = 0, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Colour = colourProvider.Background4, + } }; } protected override bool OnHover(HoverEvent e) { - background.FadeColour(backgroundColour.Lighten(0.1f), 300, Easing.OutQuint); + DragBar.FadeIn(100); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - background.FadeColour(backgroundColour, 300, Easing.OutQuint); + DragBar.FadeOut(100); base.OnHoverLost(e); } + + private partial class DragArea : CompositeDrawable + { + private readonly Circle circle; + + public DragArea() + { + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + circle = new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(150, 7), + Margin = new MarginPadding(12), + } + }; + } + + protected override bool OnHover(HoverEvent e) + { + updateScale(); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateScale(); + base.OnHoverLost(e); + } + + private bool dragging; + + protected override bool OnMouseDown(MouseDownEvent e) + { + dragging = true; + updateScale(); + return base.OnMouseDown(e); + } + + protected override void OnMouseUp(MouseUpEvent e) + { + dragging = false; + updateScale(); + base.OnMouseUp(e); + } + + private void updateScale() + { + if (dragging || IsHovered) + circle.FadeIn(100); + else + circle.FadeTo(0.6f, 100); + + if (dragging) + circle.ScaleTo(1f, 400, Easing.OutQuint); + else if (IsHovered) + circle.ScaleTo(1.05f, 400, Easing.OutElasticHalf); + else + circle.ScaleTo(1f, 500, Easing.OutQuint); + } + } } } From 59800821da1afe62e520af591b172eb34c80bee2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 16:44:18 +0900 Subject: [PATCH 0255/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index ea08992710..ddaa371014 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 53d5d6b010..c11dfd06f3 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 3b41480beffb8eda3ea8428f1d1a02dc81cfd4c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 17:46:02 +0900 Subject: [PATCH 0256/1118] Always show drag bar on mobile --- osu.Game/Overlays/Chat/ChatOverlayTopBar.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs index 44cb07ca91..807bb26502 100644 --- a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs +++ b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -67,7 +68,7 @@ namespace osu.Game.Overlays.Chat }, DragBar = new DragArea { - Alpha = 0, + Alpha = RuntimeInfo.IsMobile ? 1 : 0, Anchor = Anchor.Centre, Origin = Anchor.Centre, Colour = colourProvider.Background4, @@ -77,13 +78,15 @@ namespace osu.Game.Overlays.Chat protected override bool OnHover(HoverEvent e) { - DragBar.FadeIn(100); + if (!RuntimeInfo.IsMobile) + DragBar.FadeIn(100); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - DragBar.FadeOut(100); + if (!RuntimeInfo.IsMobile) + DragBar.FadeOut(100); base.OnHoverLost(e); } From 290c3d63492315e700ff57d468585c0a8d3bc52a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 17:46:23 +0900 Subject: [PATCH 0257/1118] Clean up left-overs --- osu.Game/Overlays/Chat/ChatOverlayTopBar.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs index 807bb26502..330c991ce8 100644 --- a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs +++ b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs @@ -13,27 +13,22 @@ using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; using osuTK; -using osuTK.Graphics; namespace osu.Game.Overlays.Chat { public partial class ChatOverlayTopBar : Container { - private Box background = null!; - - private Color4 backgroundColour; - public Drawable DragBar = null!; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, TextureStore textures) { - Children = new Drawable[] + Children = new[] { - background = new Box + new Box { RelativeSizeAxes = Axes.Both, - Colour = backgroundColour = colourProvider.Background3, + Colour = colourProvider.Background3, }, new GridContainer { From 95229cb33607ba8ddc43af8da387830f36f63d48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 17:19:03 +0900 Subject: [PATCH 0258/1118] Show gameplay when loading the skin editor from the main menu --- .../Overlays/SkinEditor/SkinEditorOverlay.cs | 65 +++++++++++++++++++ .../SkinEditor/SkinEditorSceneLibrary.cs | 32 +-------- 2 files changed, 68 insertions(+), 29 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index d1e7b97efc..16fc6b6ec6 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -1,7 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; using System.Diagnostics; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -9,12 +12,21 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Primitives; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Screens; +using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Mods; +using osu.Game.Scoring; using osu.Game.Screens; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Components; +using osu.Game.Screens.Menu; +using osu.Game.Screens.Play; +using osu.Game.Screens.Select; +using osu.Game.Utils; using osuTK; namespace osu.Game.Overlays.SkinEditor @@ -31,12 +43,21 @@ namespace osu.Game.Overlays.SkinEditor private SkinEditor? skinEditor; + [Resolved] + private IPerformFromScreenRunner? performer { get; set; } + [Cached] public readonly EditorClipboard Clipboard = new EditorClipboard(); [Resolved] private OsuGame game { get; set; } = null!; + [Resolved] + private IBindable ruleset { get; set; } = null!; + + [Resolved] + private Bindable> mods { get; set; } = null!; + private OsuScreen? lastTargetScreen; private Vector2 lastDrawSize; @@ -72,6 +93,9 @@ namespace osu.Game.Overlays.SkinEditor { globallyDisableBeatmapSkinSetting(); + if (lastTargetScreen is MainMenu) + PresentGameplay(); + if (skinEditor != null) { skinEditor.Show(); @@ -105,6 +129,28 @@ namespace osu.Game.Overlays.SkinEditor globallyReenableBeatmapSkinSetting(); } + public void PresentGameplay() + { + performer?.PerformFromScreen(screen => + { + if (screen is Player) + return; + + var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod(); + + IReadOnlyList usableMods = mods.Value; + + if (replayGeneratingMod != null) + usableMods = usableMods.Append(replayGeneratingMod).ToArray(); + + if (!ModUtils.CheckCompatibleSet(usableMods, out var invalid)) + mods.Value = mods.Value.Except(invalid).ToArray(); + + if (replayGeneratingMod != null) + screen.Push(new EndlessPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods))); + }, new[] { typeof(Player), typeof(PlaySongSelect) }); + } + protected override void Update() { base.Update(); @@ -222,5 +268,24 @@ namespace osu.Game.Overlays.SkinEditor leasedBeatmapSkins?.Return(); leasedBeatmapSkins = null; } + + private partial class EndlessPlayer : ReplayPlayer + { + public EndlessPlayer(Func, Score> createScore) + : base(createScore, new PlayerConfiguration + { + ShowResults = false, + }) + { + } + + protected override void Update() + { + base.Update(); + + if (GameplayState.HasPassed) + GameplayClockContainer.Seek(0); + } + } } } diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs index 9b021632cf..7fa33ddcf5 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs @@ -1,10 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -14,12 +11,8 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; -using osu.Game.Rulesets; -using osu.Game.Rulesets.Mods; using osu.Game.Screens; -using osu.Game.Screens.Play; using osu.Game.Screens.Select; -using osu.Game.Utils; using osuTK; namespace osu.Game.Overlays.SkinEditor @@ -36,10 +29,7 @@ namespace osu.Game.Overlays.SkinEditor private IPerformFromScreenRunner? performer { get; set; } [Resolved] - private IBindable ruleset { get; set; } = null!; - - [Resolved] - private Bindable> mods { get; set; } = null!; + private SkinEditorOverlay skinEditorOverlay { get; set; } = null!; public SkinEditorSceneLibrary() { @@ -96,24 +86,7 @@ namespace osu.Game.Overlays.SkinEditor Text = SkinEditorStrings.Gameplay, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Action = () => performer?.PerformFromScreen(screen => - { - if (screen is Player) - return; - - var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod(); - - IReadOnlyList usableMods = mods.Value; - - if (replayGeneratingMod != null) - usableMods = usableMods.Append(replayGeneratingMod).ToArray(); - - if (!ModUtils.CheckCompatibleSet(usableMods, out var invalid)) - mods.Value = mods.Value.Except(invalid).ToArray(); - - if (replayGeneratingMod != null) - screen.Push(new PlayerLoader(() => new ReplayPlayer((beatmap, mods) => replayGeneratingMod.CreateScoreFromReplayData(beatmap, mods)))); - }, new[] { typeof(Player), typeof(PlaySongSelect) }) + Action = () => skinEditorOverlay.PresentGameplay(), }, } }, @@ -137,5 +110,6 @@ namespace osu.Game.Overlays.SkinEditor Content.CornerRadius = 5; } } + } } From 7153c823e8405713579f1a9d6c72f5ac5c2572b2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 17:34:08 +0900 Subject: [PATCH 0259/1118] Choose a better beatmap if the intro is still playing Also skip intro time. --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index 16fc6b6ec6..18d1c4c62b 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -52,12 +52,18 @@ namespace osu.Game.Overlays.SkinEditor [Resolved] private OsuGame game { get; set; } = null!; + [Resolved] + private MusicController music { get; set; } = null!; + [Resolved] private IBindable ruleset { get; set; } = null!; [Resolved] private Bindable> mods { get; set; } = null!; + [Resolved] + private IBindable beatmap { get; set; } = null!; + private OsuScreen? lastTargetScreen; private Vector2 lastDrawSize; @@ -133,6 +139,14 @@ namespace osu.Game.Overlays.SkinEditor { performer?.PerformFromScreen(screen => { + // If we're playing the intro, switch away to another beatmap. + if (beatmap.Value.BeatmapSetInfo.Protected) + { + music.NextTrack(); + Schedule(PresentGameplay); + return; + } + if (screen is Player) return; @@ -275,6 +289,7 @@ namespace osu.Game.Overlays.SkinEditor : base(createScore, new PlayerConfiguration { ShowResults = false, + AutomaticallySkipIntro = true, }) { } From 8314f656a3107b27fc82ddbad448de4c70bdf41d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 24 Nov 2023 17:32:18 +0900 Subject: [PATCH 0260/1118] Encapsulate common HP logic from osu and catch HP calculations --- .../Scoring/CatchHealthProcessor.cs | 121 +------------- .../Scoring/OsuHealthProcessor.cs | 155 ++--------------- .../Scoring/LegacyDrainingHealthProcessor.cs | 158 ++++++++++++++++++ 3 files changed, 179 insertions(+), 255 deletions(-) create mode 100644 osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs index 6d831ad223..c3cc488941 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchHealthProcessor.cs @@ -1,138 +1,27 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Catch.Scoring { - public partial class CatchHealthProcessor : DrainingHealthProcessor + public partial class CatchHealthProcessor : LegacyDrainingHealthProcessor { - public Action? OnIterationFail; - public Action? OnIterationSuccess; - - private double lowestHpEver; - private double lowestHpEnd; - private double hpRecoveryAvailable; - private double hpMultiplierNormal; - public CatchHealthProcessor(double drainStartTime) : base(drainStartTime) { } - public override void ApplyBeatmap(IBeatmap beatmap) - { - lowestHpEver = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.975, 0.8, 0.3); - lowestHpEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.99, 0.9, 0.4); - hpRecoveryAvailable = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.04, 0.02, 0); + protected override IEnumerable EnumerateTopLevelHitObjects() => EnumerateHitObjects(Beatmap).Where(h => h is Fruit || h is Droplet || h is Banana); - base.ApplyBeatmap(beatmap); - } + protected override IEnumerable EnumerateNestedHitObjects(HitObject hitObject) => Enumerable.Empty(); - protected override void Reset(bool storeResults) - { - hpMultiplierNormal = 1; - base.Reset(storeResults); - } - - protected override double ComputeDrainRate() - { - double testDrop = 0.00025; - double currentHp; - double currentHpUncapped; - - while (true) - { - currentHp = 1; - currentHpUncapped = 1; - - double lowestHp = currentHp; - double lastTime = DrainStartTime; - int currentBreak = 0; - bool fail = false; - - List allObjects = EnumerateHitObjects(Beatmap).Where(h => h is Fruit || h is Droplet || h is Banana).ToList(); - - for (int i = 0; i < allObjects.Count; i++) - { - HitObject h = allObjects[i]; - - while (currentBreak < Beatmap.Breaks.Count && Beatmap.Breaks[currentBreak].EndTime <= h.StartTime) - { - // If two hitobjects are separated by a break period, there is no drain for the full duration between the hitobjects. - // This differs from legacy (version < 8) beatmaps which continue draining until the break section is entered, - // but this shouldn't have a noticeable impact in practice. - lastTime = h.StartTime; - currentBreak++; - } - - reduceHp(testDrop * (h.StartTime - lastTime)); - - lastTime = h.GetEndTime(); - - if (currentHp < lowestHp) - lowestHp = currentHp; - - if (currentHp <= lowestHpEver) - { - fail = true; - testDrop *= 0.96; - OnIterationFail?.Invoke($"FAILED drop {testDrop}: hp too low ({currentHp} < {lowestHpEver})"); - break; - } - - increaseHp(h); - } - - if (!fail && currentHp < lowestHpEnd) - { - fail = true; - testDrop *= 0.94; - hpMultiplierNormal *= 1.01; - OnIterationFail?.Invoke($"FAILED drop {testDrop}: end hp too low ({currentHp} < {lowestHpEnd})"); - } - - double recovery = (currentHpUncapped - 1) / allObjects.Count; - - if (!fail && recovery < hpRecoveryAvailable) - { - fail = true; - testDrop *= 0.96; - hpMultiplierNormal *= 1.01; - OnIterationFail?.Invoke($"FAILED drop {testDrop}: recovery too low ({recovery} < {hpRecoveryAvailable})"); - } - - if (!fail) - { - OnIterationSuccess?.Invoke($"PASSED drop {testDrop}"); - return testDrop; - } - } - - void reduceHp(double amount) - { - currentHpUncapped = Math.Max(0, currentHpUncapped - amount); - currentHp = Math.Max(0, currentHp - amount); - } - - void increaseHp(HitObject hitObject) - { - double amount = healthIncreaseFor(hitObject.CreateJudgement().MaxResult); - currentHpUncapped += amount; - currentHp = Math.Max(0, Math.Min(1, currentHp + amount)); - } - } - - protected override double GetHealthIncreaseFor(JudgementResult result) => healthIncreaseFor(result.Type); - - private double healthIncreaseFor(HitResult result) + protected override double GetHealthIncreaseFor(HitObject hitObject, HitResult result) { double increase = 0; @@ -162,7 +51,7 @@ namespace osu.Game.Rulesets.Catch.Scoring break; } - return hpMultiplierNormal * increase; + return HpMultiplierNormal * increase; } } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 3c124b3162..7025a7be65 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -1,166 +1,43 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; +using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Scoring { - public partial class OsuHealthProcessor : DrainingHealthProcessor + public partial class OsuHealthProcessor : LegacyDrainingHealthProcessor { - public Action? OnIterationFail; - public Action? OnIterationSuccess; - - private double lowestHpEver; - private double lowestHpEnd; - private double hpRecoveryAvailable; - private double hpMultiplierNormal; - public OsuHealthProcessor(double drainStartTime) : base(drainStartTime) { } - public override void ApplyBeatmap(IBeatmap beatmap) + protected override IEnumerable EnumerateTopLevelHitObjects() => Beatmap.HitObjects; + + protected override IEnumerable EnumerateNestedHitObjects(HitObject hitObject) { - lowestHpEver = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.975, 0.8, 0.3); - lowestHpEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.99, 0.9, 0.4); - hpRecoveryAvailable = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.04, 0.02, 0); - - base.ApplyBeatmap(beatmap); - } - - protected override void Reset(bool storeResults) - { - hpMultiplierNormal = 1; - base.Reset(storeResults); - } - - protected override double ComputeDrainRate() - { - double testDrop = 0.00025; - double currentHp; - double currentHpUncapped; - - while (true) + switch (hitObject) { - currentHp = 1; - currentHpUncapped = 1; + case Slider slider: + foreach (var nested in slider.NestedHitObjects) + yield return nested; - double lowestHp = currentHp; - double lastTime = DrainStartTime; - int currentBreak = 0; - bool fail = false; + break; - for (int i = 0; i < Beatmap.HitObjects.Count; i++) - { - HitObject h = Beatmap.HitObjects[i]; + case Spinner spinner: + foreach (var nested in spinner.NestedHitObjects.Where(t => t is not SpinnerBonusTick)) + yield return nested; - while (currentBreak < Beatmap.Breaks.Count && Beatmap.Breaks[currentBreak].EndTime <= h.StartTime) - { - // If two hitobjects are separated by a break period, there is no drain for the full duration between the hitobjects. - // This differs from legacy (version < 8) beatmaps which continue draining until the break section is entered, - // but this shouldn't have a noticeable impact in practice. - lastTime = h.StartTime; - currentBreak++; - } - - reduceHp(testDrop * (h.StartTime - lastTime)); - - lastTime = h.GetEndTime(); - - if (currentHp < lowestHp) - lowestHp = currentHp; - - if (currentHp <= lowestHpEver) - { - fail = true; - testDrop *= 0.96; - OnIterationFail?.Invoke($"FAILED drop {testDrop}: hp too low ({currentHp} < {lowestHpEver})"); - break; - } - - double hpReduction = testDrop * (h.GetEndTime() - h.StartTime); - double hpOverkill = Math.Max(0, hpReduction - currentHp); - reduceHp(hpReduction); - - switch (h) - { - case Slider slider: - { - foreach (var nested in slider.NestedHitObjects) - increaseHp(nested); - break; - } - - case Spinner spinner: - { - foreach (var nested in spinner.NestedHitObjects.Where(t => t is not SpinnerBonusTick)) - increaseHp(nested); - break; - } - } - - // Note: Because HP is capped during the above increases, long sliders (with many ticks) or spinners - // will appear to overkill at lower drain levels than they should. However, it is also not correct to simply use the uncapped version. - if (hpOverkill > 0 && currentHp - hpOverkill <= lowestHpEver) - { - fail = true; - testDrop *= 0.96; - OnIterationFail?.Invoke($"FAILED drop {testDrop}: overkill ({currentHp} - {hpOverkill} <= {lowestHpEver})"); - break; - } - - increaseHp(h); - } - - if (!fail && currentHp < lowestHpEnd) - { - fail = true; - testDrop *= 0.94; - hpMultiplierNormal *= 1.01; - OnIterationFail?.Invoke($"FAILED drop {testDrop}: end hp too low ({currentHp} < {lowestHpEnd})"); - } - - double recovery = (currentHpUncapped - 1) / Beatmap.HitObjects.Count; - - if (!fail && recovery < hpRecoveryAvailable) - { - fail = true; - testDrop *= 0.96; - hpMultiplierNormal *= 1.01; - OnIterationFail?.Invoke($"FAILED drop {testDrop}: recovery too low ({recovery} < {hpRecoveryAvailable})"); - } - - if (!fail) - { - OnIterationSuccess?.Invoke($"PASSED drop {testDrop}"); - return testDrop; - } - } - - void reduceHp(double amount) - { - currentHpUncapped = Math.Max(0, currentHpUncapped - amount); - currentHp = Math.Max(0, currentHp - amount); - } - - void increaseHp(HitObject hitObject) - { - double amount = healthIncreaseFor(hitObject, hitObject.CreateJudgement().MaxResult); - currentHpUncapped += amount; - currentHp = Math.Max(0, Math.Min(1, currentHp + amount)); + break; } } - protected override double GetHealthIncreaseFor(JudgementResult result) => healthIncreaseFor(result.HitObject, result.Type); - - private double healthIncreaseFor(HitObject hitObject, HitResult result) + protected override double GetHealthIncreaseFor(HitObject hitObject, HitResult result) { double increase = 0; @@ -206,7 +83,7 @@ namespace osu.Game.Rulesets.Osu.Scoring break; } - return hpMultiplierNormal * increase; + return HpMultiplierNormal * increase; } } } diff --git a/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs new file mode 100644 index 0000000000..ce2f7d5624 --- /dev/null +++ b/osu.Game/Rulesets/Scoring/LegacyDrainingHealthProcessor.cs @@ -0,0 +1,158 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; + +namespace osu.Game.Rulesets.Scoring +{ + /// + /// A that matches legacy drain rate calculations as best as possible. + /// + public abstract partial class LegacyDrainingHealthProcessor : DrainingHealthProcessor + { + public Action? OnIterationFail; + public Action? OnIterationSuccess; + + protected double HpMultiplierNormal { get; private set; } + + private double lowestHpEver; + private double lowestHpEnd; + private double hpRecoveryAvailable; + + protected LegacyDrainingHealthProcessor(double drainStartTime) + : base(drainStartTime) + { + } + + public override void ApplyBeatmap(IBeatmap beatmap) + { + lowestHpEver = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.975, 0.8, 0.3); + lowestHpEnd = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.99, 0.9, 0.4); + hpRecoveryAvailable = IBeatmapDifficultyInfo.DifficultyRange(beatmap.Difficulty.DrainRate, 0.04, 0.02, 0); + + base.ApplyBeatmap(beatmap); + } + + protected override void Reset(bool storeResults) + { + HpMultiplierNormal = 1; + base.Reset(storeResults); + } + + protected override double ComputeDrainRate() + { + double testDrop = 0.00025; + double currentHp; + double currentHpUncapped; + + while (true) + { + currentHp = 1; + currentHpUncapped = 1; + + double lowestHp = currentHp; + double lastTime = DrainStartTime; + int currentBreak = 0; + bool fail = false; + int topLevelObjectCount = 0; + + foreach (var h in EnumerateTopLevelHitObjects()) + { + topLevelObjectCount++; + + while (currentBreak < Beatmap.Breaks.Count && Beatmap.Breaks[currentBreak].EndTime <= h.StartTime) + { + // If two hitobjects are separated by a break period, there is no drain for the full duration between the hitobjects. + // This differs from legacy (version < 8) beatmaps which continue draining until the break section is entered, + // but this shouldn't have a noticeable impact in practice. + lastTime = h.StartTime; + currentBreak++; + } + + reduceHp(testDrop * (h.StartTime - lastTime)); + + lastTime = h.GetEndTime(); + + if (currentHp < lowestHp) + lowestHp = currentHp; + + if (currentHp <= lowestHpEver) + { + fail = true; + testDrop *= 0.96; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: hp too low ({currentHp} < {lowestHpEver})"); + break; + } + + double hpReduction = testDrop * (h.GetEndTime() - h.StartTime); + double hpOverkill = Math.Max(0, hpReduction - currentHp); + reduceHp(hpReduction); + + foreach (var nested in EnumerateNestedHitObjects(h)) + increaseHp(nested); + + // Note: Because HP is capped during the above increases, long sliders (with many ticks) or spinners + // will appear to overkill at lower drain levels than they should. However, it is also not correct to simply use the uncapped version. + if (hpOverkill > 0 && currentHp - hpOverkill <= lowestHpEver) + { + fail = true; + testDrop *= 0.96; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: overkill ({currentHp} - {hpOverkill} <= {lowestHpEver})"); + break; + } + + increaseHp(h); + } + + if (!fail && currentHp < lowestHpEnd) + { + fail = true; + testDrop *= 0.94; + HpMultiplierNormal *= 1.01; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: end hp too low ({currentHp} < {lowestHpEnd})"); + } + + double recovery = (currentHpUncapped - 1) / Math.Max(1, topLevelObjectCount); + + if (!fail && recovery < hpRecoveryAvailable) + { + fail = true; + testDrop *= 0.96; + HpMultiplierNormal *= 1.01; + OnIterationFail?.Invoke($"FAILED drop {testDrop}: recovery too low ({recovery} < {hpRecoveryAvailable})"); + } + + if (!fail) + { + OnIterationSuccess?.Invoke($"PASSED drop {testDrop}"); + return testDrop; + } + } + + void reduceHp(double amount) + { + currentHpUncapped = Math.Max(0, currentHpUncapped - amount); + currentHp = Math.Max(0, currentHp - amount); + } + + void increaseHp(HitObject hitObject) + { + double amount = GetHealthIncreaseFor(hitObject, hitObject.CreateJudgement().MaxResult); + currentHpUncapped += amount; + currentHp = Math.Max(0, Math.Min(1, currentHp + amount)); + } + } + + protected sealed override double GetHealthIncreaseFor(JudgementResult result) => GetHealthIncreaseFor(result.HitObject, result.Type); + + protected abstract IEnumerable EnumerateTopLevelHitObjects(); + + protected abstract IEnumerable EnumerateNestedHitObjects(HitObject hitObject); + + protected abstract double GetHealthIncreaseFor(HitObject hitObject, HitResult result); + } +} From a44edfdeddadaac693c63312fb6d3cc14593ee0e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 19:37:57 +0900 Subject: [PATCH 0261/1118] Fix incorrect sample for top level edit button --- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 78eb410a48..259efad8b3 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -138,7 +138,7 @@ namespace osu.Game.Screens.Menu buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-default-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => State = ButtonSystemState.Edit, 0, Key.E)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-play-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => State = ButtonSystemState.Edit, 0, Key.E)); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D)); if (host.CanExit) From 901561533600690974d0c89756cc850dca5ec7d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 24 Nov 2023 19:42:30 +0900 Subject: [PATCH 0262/1118] Update test to work with new drag bar location --- osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 55e6b54af7..8a2a66f60f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -180,11 +180,8 @@ namespace osu.Game.Tests.Visual.Online }); AddStep("Show overlay", () => chatOverlay.Show()); AddAssert("Overlay uses config height", () => chatOverlay.Height == configChatHeight.Default); - AddStep("Click top bar", () => - { - InputManager.MoveMouseTo(chatOverlayTopBar); - InputManager.PressButton(MouseButton.Left); - }); + AddStep("Move mouse to drag bar", () => InputManager.MoveMouseTo(chatOverlayTopBar.DragBar)); + AddStep("Click drag bar", () => InputManager.PressButton(MouseButton.Left)); AddStep("Drag overlay to new height", () => InputManager.MoveMouseTo(chatOverlayTopBar, new Vector2(0, -300))); AddStep("Stop dragging", () => InputManager.ReleaseButton(MouseButton.Left)); AddStep("Store new height", () => newHeight = chatOverlay.Height); From a6cf1e5d2eabb3cd5f32da0b213e20c1ab601a91 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 25 Nov 2023 00:53:25 +0900 Subject: [PATCH 0263/1118] Make osu! logo do something when in edit submenu --- osu.Game/Screens/Menu/ButtonSystem.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index 259efad8b3..ca5bef985e 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -311,6 +311,10 @@ namespace osu.Game.Screens.Menu case ButtonSystemState.Play: buttonsPlay.First().TriggerClick(); return false; + + case ButtonSystemState.Edit: + buttonsEdit.First().TriggerClick(); + return false; } } From 27f9dfccc46429f2956d6ce4c3759620178fabf1 Mon Sep 17 00:00:00 2001 From: Zyf Date: Fri, 24 Nov 2023 22:05:24 +0100 Subject: [PATCH 0264/1118] Fix scoring-conversion when miss-count is 0 --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 77421908de..457262a1de 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -321,7 +321,7 @@ namespace osu.Game.Database // This time, divide the remaining combo among remaining objects equally to achieve longest possible combo lengths. // There is no rigorous proof that doing this will yield a correct upper bound, but it seems to work out in practice. remainingComboPortionInScoreV1 = comboPortionInScoreV1 - comboPortionFromLongestComboInScoreV1; - double remainingCountOfObjectsGivingCombo = maximumLegacyCombo - score.MaxCombo - score.Statistics[HitResult.Miss]; + double remainingCountOfObjectsGivingCombo = maximumLegacyCombo - score.MaxCombo - score.Statistics.GetValueOrDefault(HitResult.Miss); // Because we assumed all combos were equal, `remainingComboPortionInScoreV1` // can be approximated by n * x^2, wherein n is the assumed number of equal combos, // and x is the assumed length of every one of those combos. From 71e5654b645c07da60b7acbfbe60fe71021f9c45 Mon Sep 17 00:00:00 2001 From: Zyf Date: Fri, 24 Nov 2023 23:07:27 +0100 Subject: [PATCH 0265/1118] Account for legacyAccScore in score conversion --- .../Difficulty/CatchLegacyScoreSimulator.cs | 1 + .../Difficulty/TaikoLegacyScoreSimulator.cs | 1 + .../Database/StandardisedScoreMigrationTools.cs | 14 +++++++------- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs index 746f5713e4..ed27e11208 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs @@ -74,6 +74,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty simulateHit(obj, ref attributes); attributes.BonusScoreRatio = legacyBonusScore == 0 ? 0 : (double)standardisedBonusScore / legacyBonusScore; + attributes.BonusScore = legacyBonusScore; return attributes; } diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index 6a3eb68a22..1db44592b8 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -75,6 +75,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty simulateHit(obj, ref attributes); attributes.BonusScoreRatio = legacyBonusScore == 0 ? 0 : (double)standardisedBonusScore / legacyBonusScore; + attributes.BonusScore = legacyBonusScore; return attributes; } diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 457262a1de..6484500bcc 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -250,15 +250,14 @@ namespace osu.Game.Database long maximumLegacyComboScore = (long)Math.Round(attributes.ComboScore * legacyModMultiplier); double maximumLegacyBonusRatio = attributes.BonusScoreRatio; long maximumLegacyBonusScore = attributes.BonusScore; - int maximumLegacyCombo = attributes.MaxCombo; + double legacyAccScore = maximumLegacyAccuracyScore * score.Accuracy; + // We can not separate the ComboScore from the BonusScore, so we keep the bonus in the ratio. + double comboProportion = + ((double)score.LegacyTotalScore - legacyAccScore) / (maximumLegacyComboScore + maximumLegacyBonusScore); + + // We assume the bonus proportion only makes up the rest of the score that exceeds maximumLegacyBaseScore. long maximumLegacyBaseScore = maximumLegacyAccuracyScore + maximumLegacyComboScore; - long maximumLegacyTotalScore = maximumLegacyBaseScore + maximumLegacyBonusScore; - - // The combo proportion is calculated as a proportion of maximumLegacyTotalScore. - double comboProportion = (double)score.LegacyTotalScore / maximumLegacyTotalScore; - - // The bonus proportion makes up the rest of the score that exceeds maximumLegacyBaseScore. double bonusProportion = Math.Max(0, ((long)score.LegacyTotalScore - maximumLegacyBaseScore) * maximumLegacyBonusRatio); double modMultiplier = score.Mods.Select(m => m.ScoreMultiplier).Aggregate(1.0, (c, n) => c * n); @@ -288,6 +287,7 @@ namespace osu.Game.Database // this can be roughly represented by summing / integrating f(combo) = combo. // All mod- and beatmap-dependent multipliers and constants are not included here, // as we will only be using the magnitude of this to compute ratios. + int maximumLegacyCombo = attributes.MaxCombo; double maximumAchievableComboPortionInScoreV1 = Math.Pow(maximumLegacyCombo, 2); // Similarly, estimate the maximum magnitude of the combo portion in standardised score. // Roughly corresponds to integrating f(combo) = combo ^ COMBO_EXPONENT (omitting constants) From 68fca007577f29c9b57e7df2e33b6dd09ee6d7b2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 25 Nov 2023 02:40:30 +0300 Subject: [PATCH 0266/1118] Improve handling of beatmap collection changes in `CollectionDropdown` Co-authored-by: Dean Herbert --- osu.Game/Collections/CollectionDropdown.cs | 72 +++++++++++++--------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/osu.Game/Collections/CollectionDropdown.cs b/osu.Game/Collections/CollectionDropdown.cs index e435992381..299594b0a0 100644 --- a/osu.Game/Collections/CollectionDropdown.cs +++ b/osu.Game/Collections/CollectionDropdown.cs @@ -43,11 +43,13 @@ namespace osu.Game.Collections private IDisposable? realmSubscription; + private readonly CollectionFilterMenuItem allBeatmapsItem = new AllBeatmapsCollectionFilterMenuItem(); + public CollectionDropdown() { ItemSource = filters; - Current.Value = new AllBeatmapsCollectionFilterMenuItem(); + Current.Value = allBeatmapsItem; } protected override void LoadComplete() @@ -61,37 +63,51 @@ namespace osu.Game.Collections private void collectionsChanged(IRealmCollection collections, ChangeSet? changes) { - var selectedItem = SelectedItem?.Value?.Collection; - - var allBeatmaps = new AllBeatmapsCollectionFilterMenuItem(); - - filters.Clear(); - filters.Add(allBeatmaps); - filters.AddRange(collections.Select(c => new CollectionFilterMenuItem(c.ToLive(realm)))); - - if (ShowManageCollectionsItem) - filters.Add(new ManageCollectionsFilterMenuItem()); - - // This current update and schedule is required to work around dropdown headers not updating text even when the selected item - // changes. It's not great but honestly the whole dropdown menu structure isn't great. This needs to be fixed, but I'll issue - // a warning that it's going to be a frustrating journey. - Current.Value = allBeatmaps; - Schedule(() => + if (changes == null) { - // current may have changed before the scheduled call is run. - if (Current.Value != allBeatmaps) - return; - - Current.Value = filters.SingleOrDefault(f => f.Collection != null && f.Collection.ID == selectedItem?.ID) ?? filters[0]; - }); - - // Trigger a re-filter if the current item was in the change set. - if (selectedItem != null && changes != null) + filters.Add(allBeatmapsItem); + filters.AddRange(collections.Select(c => new CollectionFilterMenuItem(c.ToLive(realm)))); + if (ShowManageCollectionsItem) + filters.Add(new ManageCollectionsFilterMenuItem()); + } + else { - foreach (int index in changes.ModifiedIndices) + foreach (int i in changes.DeletedIndices) + filters.RemoveAt(i + 1); + + foreach (int i in changes.InsertedIndices) + filters.Insert(i + 1, new CollectionFilterMenuItem(collections[i].ToLive(realm))); + + var selectedItem = SelectedItem?.Value; + + foreach (int i in changes.NewModifiedIndices) { - if (collections[index].ID == selectedItem.ID) + var updatedItem = collections[i]; + + // This is responsible for updating the state of the +/- button and the collection's name. + // TODO: we can probably make the menu items update with changes to avoid this. + filters.RemoveAt(i + 1); + filters.Insert(i + 1, new CollectionFilterMenuItem(updatedItem.ToLive(realm))); + + if (updatedItem.ID == selectedItem?.Collection?.ID) + { + // This current update and schedule is required to work around dropdown headers not updating text even when the selected item + // changes. It's not great but honestly the whole dropdown menu structure isn't great. This needs to be fixed, but I'll issue + // a warning that it's going to be a frustrating journey. + Current.Value = allBeatmapsItem; + Schedule(() => + { + // current may have changed before the scheduled call is run. + if (Current.Value != allBeatmapsItem) + return; + + Current.Value = filters.SingleOrDefault(f => f.Collection?.ID == selectedItem.Collection?.ID) ?? filters[0]; + }); + + // Trigger an external re-filter if the current item was in the change set. RequestFilter?.Invoke(); + break; + } } } } From 6f66819e5152de82d807522089465b79a82b3850 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Nov 2023 10:44:50 +0900 Subject: [PATCH 0267/1118] Privatise setter --- osu.Game/Overlays/Chat/ChatOverlayTopBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs index 1cea198300..84fd342493 100644 --- a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs +++ b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs @@ -18,7 +18,7 @@ namespace osu.Game.Overlays.Chat { public partial class ChatOverlayTopBar : Container { - public Drawable DragBar = null!; + public Drawable DragBar { get; private set; } = null!; [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider, TextureStore textures) From 7f788058cdadf4f8196b4095afdf2c265b81edab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Nov 2023 11:35:10 +0900 Subject: [PATCH 0268/1118] Revert incorrect xmldoc change --- osu.Game/Online/Spectator/SpectatorClient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Spectator/SpectatorClient.cs b/osu.Game/Online/Spectator/SpectatorClient.cs index e7435adf29..47e2dd807a 100644 --- a/osu.Game/Online/Spectator/SpectatorClient.cs +++ b/osu.Game/Online/Spectator/SpectatorClient.cs @@ -46,7 +46,7 @@ namespace osu.Game.Online.Spectator public IBindableList PlayingUsers => playingUsers; /// - /// Whether the spectated user is playing. + /// Whether the local user is playing. /// private bool isPlaying { get; set; } From 3f48f4acdff18744317665661eb0119154eceff0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Nov 2023 12:06:08 +0900 Subject: [PATCH 0269/1118] Remove blank line --- osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs index 7fa33ddcf5..a682285549 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs @@ -110,6 +110,5 @@ namespace osu.Game.Overlays.SkinEditor Content.CornerRadius = 5; } } - } } From 7e3bb5f8dba1a3357a6a6729a557d1e0b13714c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Nov 2023 12:09:13 +0900 Subject: [PATCH 0270/1118] Make skin editor overlay dependency nullable to fix tests --- osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs index a682285549..5a283c0e8d 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorSceneLibrary.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.SkinEditor private IPerformFromScreenRunner? performer { get; set; } [Resolved] - private SkinEditorOverlay skinEditorOverlay { get; set; } = null!; + private SkinEditorOverlay? skinEditorOverlay { get; set; } public SkinEditorSceneLibrary() { @@ -86,7 +86,7 @@ namespace osu.Game.Overlays.SkinEditor Text = SkinEditorStrings.Gameplay, Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Action = () => skinEditorOverlay.PresentGameplay(), + Action = () => skinEditorOverlay?.PresentGameplay(), }, } }, From a4be28a2aebf4bf14c72a1aee35d60fad654c88e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 22 Nov 2023 17:53:06 +0900 Subject: [PATCH 0271/1118] Don't show buttons on fail overlay when player interaction is disabled --- osu.Game/Screens/Play/FailOverlay.cs | 15 +++++++++++++-- osu.Game/Screens/Play/Player.cs | 2 +- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/FailOverlay.cs b/osu.Game/Screens/Play/FailOverlay.cs index abfc401998..210ae5ceb6 100644 --- a/osu.Game/Screens/Play/FailOverlay.cs +++ b/osu.Game/Screens/Play/FailOverlay.cs @@ -26,11 +26,22 @@ namespace osu.Game.Screens.Play public override LocalisableString Header => GameplayMenuOverlayStrings.FailedHeader; + private readonly bool showButtons; + + public FailOverlay(bool showButtons = true) + { + this.showButtons = showButtons; + } + [BackgroundDependencyLoader] private void load(OsuColour colours) { - AddButton(GameplayMenuOverlayStrings.Retry, colours.YellowDark, () => OnRetry?.Invoke()); - AddButton(GameplayMenuOverlayStrings.Quit, new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); + if (showButtons) + { + AddButton(GameplayMenuOverlayStrings.Retry, colours.YellowDark, () => OnRetry?.Invoke()); + AddButton(GameplayMenuOverlayStrings.Quit, new Color4(170, 27, 39, 255), () => OnQuit?.Invoke()); + } + // from #10339 maybe this is a better visual effect Add(new Container { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ff00b52f71..1c97efcff7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -267,7 +267,7 @@ namespace osu.Game.Screens.Play createGameplayComponents(Beatmap.Value) } }, - FailOverlay = new FailOverlay + FailOverlay = new FailOverlay(Configuration.AllowUserInteraction) { SaveReplay = async () => await prepareAndImportScoreAsync(true).ConfigureAwait(false), OnRetry = () => Restart(), From d9242278105febc065dcc57b4bd318a89dcb190a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 24 Nov 2023 18:04:57 +0900 Subject: [PATCH 0272/1118] Add `ManiaHealthProcessor` that uses the legacy drain rate algorithm --- .../Scoring/ManiaHealthProcessor.cs | 52 ++++++++++++++++--- 1 file changed, 45 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs index e63a037ca9..183550eb7b 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs @@ -1,23 +1,61 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Rulesets.Judgements; +using System.Collections.Generic; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Scoring { - public partial class ManiaHealthProcessor : DrainingHealthProcessor + public partial class ManiaHealthProcessor : LegacyDrainingHealthProcessor { - /// public ManiaHealthProcessor(double drainStartTime) - : base(drainStartTime, 1.0) + : base(drainStartTime) { } - protected override HitResult GetSimulatedHitResult(Judgement judgement) + protected override IEnumerable EnumerateTopLevelHitObjects() => Beatmap.HitObjects; + + protected override IEnumerable EnumerateNestedHitObjects(HitObject hitObject) => hitObject.NestedHitObjects; + + protected override double GetHealthIncreaseFor(HitObject hitObject, HitResult result) { - // Users are not expected to attain perfect judgements for all notes due to the tighter hit window. - return judgement.MaxResult == HitResult.Perfect ? HitResult.Great : judgement.MaxResult; + double increase = 0; + + switch (result) + { + case HitResult.Miss: + switch (hitObject) + { + case HeadNote: + case TailNote: + return -(Beatmap.Difficulty.DrainRate + 1) * 0.00375; + + default: + return -(Beatmap.Difficulty.DrainRate + 1) * 0.0075; + } + + case HitResult.Meh: + return -(Beatmap.Difficulty.DrainRate + 1) * 0.0016; + + case HitResult.Ok: + return 0; + + case HitResult.Good: + increase = 0.004 - Beatmap.Difficulty.DrainRate * 0.0004; + break; + + case HitResult.Great: + increase = 0.005 - Beatmap.Difficulty.DrainRate * 0.0005; + break; + + case HitResult.Perfect: + increase = 0.0055 - Beatmap.Difficulty.DrainRate * 0.0005; + break; + } + + return HpMultiplierNormal * increase; } } } From 3f73610ee765b5543e851627efdd64ecba93eccf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 27 Nov 2023 15:06:11 +0900 Subject: [PATCH 0273/1118] Update framework ^& resources --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 4 ++-- osu.iOS.props | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index ea08992710..3b90b1675c 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 53d5d6b010..7e5c5be4ea 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From ff18f80559acc1a1d6fd6dd709d2cbb0ff98c710 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Nov 2023 16:56:11 +0900 Subject: [PATCH 0274/1118] Apply NRT to `UpdateSettings` --- .../Sections/General/UpdateSettings.cs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 2f68b3a82f..6dc3cc5f18 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Threading.Tasks; using osu.Framework; using osu.Framework.Allocation; @@ -21,17 +19,17 @@ namespace osu.Game.Overlays.Settings.Sections.General { public partial class UpdateSettings : SettingsSubsection { - [Resolved(CanBeNull = true)] - private UpdateManager updateManager { get; set; } - protected override LocalisableString Header => GeneralSettingsStrings.UpdateHeader; - private SettingsButton checkForUpdatesButton; + private SettingsButton checkForUpdatesButton = null!; - [Resolved(CanBeNull = true)] - private INotificationOverlay notifications { get; set; } + [Resolved] + private UpdateManager? updateManager { get; set; } - [BackgroundDependencyLoader(true)] + [Resolved] + private INotificationOverlay? notifications { get; set; } + + [BackgroundDependencyLoader] private void load(Storage storage, OsuConfigManager config, OsuGame game) { Add(new SettingsEnumDropdown @@ -77,7 +75,7 @@ namespace osu.Game.Overlays.Settings.Sections.General Add(new SettingsButton { Text = GeneralSettingsStrings.ChangeFolderLocation, - Action = () => game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) + Action = () => game.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) }); } } From 11f1f4423704a205fa231c55e1f52b9574ee57c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 27 Nov 2023 17:13:11 +0900 Subject: [PATCH 0275/1118] Add button to compress log files for bug submission --- .../Sections/General/UpdateSettings.cs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 6dc3cc5f18..5f74b5ecb9 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -14,6 +14,7 @@ using osu.Game.Localisation; using osu.Game.Overlays.Notifications; using osu.Game.Overlays.Settings.Sections.Maintenance; using osu.Game.Updater; +using SharpCompress.Archives.Zip; namespace osu.Game.Overlays.Settings.Sections.General { @@ -72,6 +73,29 @@ namespace osu.Game.Overlays.Settings.Sections.General Action = () => storage.PresentExternally(), }); + Add(new SettingsButton + { + Text = "Compress log files", + Keywords = new[] { @"bug", "report", "logs" }, + Action = () => + { + var logStorage = storage.GetStorageForDirectory(@"logs"); + + const string archive_filename = "exports/compressed-logs.zip"; + + using (var outStream = storage.CreateFileSafely(archive_filename)) + using (var zip = ZipArchive.Create()) + { + foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) + zip.AddEntry(f, logStorage.GetStream(f), true); + + zip.SaveTo(outStream); + } + + storage.PresentFileExternally(archive_filename); + }, + }); + Add(new SettingsButton { Text = GeneralSettingsStrings.ChangeFolderLocation, From 4c2819dbc2d5bd88af61a00ebe58d0ce6a1247ea Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2023 17:59:05 +0900 Subject: [PATCH 0276/1118] Update button text and make localisable --- osu.Game/Localisation/GeneralSettingsStrings.cs | 5 +++++ .../Overlays/Settings/Sections/General/UpdateSettings.cs | 4 ++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Localisation/GeneralSettingsStrings.cs b/osu.Game/Localisation/GeneralSettingsStrings.cs index ebf57d8109..42623f4632 100644 --- a/osu.Game/Localisation/GeneralSettingsStrings.cs +++ b/osu.Game/Localisation/GeneralSettingsStrings.cs @@ -49,6 +49,11 @@ namespace osu.Game.Localisation /// public static LocalisableString OpenOsuFolder => new TranslatableString(getKey(@"open_osu_folder"), @"Open osu! folder"); + /// + /// "Export logs" + /// + public static LocalisableString ExportLogs => new TranslatableString(getKey(@"export_logs"), @"Export logs"); + /// /// "Change folder location..." /// diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 5f74b5ecb9..e2c9de1807 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -75,8 +75,8 @@ namespace osu.Game.Overlays.Settings.Sections.General Add(new SettingsButton { - Text = "Compress log files", - Keywords = new[] { @"bug", "report", "logs" }, + Text = GeneralSettingsStrings.ExportLogs, + Keywords = new[] { @"bug", "report", "logs", "files" }, Action = () => { var logStorage = storage.GetStorageForDirectory(@"logs"); From 51de98f34186b799c0e51e6434a1783302df6f57 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 28 Nov 2023 17:59:21 +0900 Subject: [PATCH 0277/1118] Use `Logger.Storage` rather than locally querying --- osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index e2c9de1807..398e4b39af 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -7,6 +7,7 @@ using osu.Framework.Allocation; using osu.Framework.Extensions; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Framework.Screens; using osu.Game.Configuration; @@ -79,7 +80,7 @@ namespace osu.Game.Overlays.Settings.Sections.General Keywords = new[] { @"bug", "report", "logs", "files" }, Action = () => { - var logStorage = storage.GetStorageForDirectory(@"logs"); + var logStorage = Logger.Storage; const string archive_filename = "exports/compressed-logs.zip"; From 26855a2c04f623e87c1a357e3dab3cda241dd075 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 28 Nov 2023 21:14:34 +0900 Subject: [PATCH 0278/1118] Add failing test --- .../Formats/LegacyBeatmapDecoderTest.cs | 32 +++++++++++++++++++ .../Resources/custom-slider-length.osu | 19 +++++++++++ 2 files changed, 51 insertions(+) create mode 100644 osu.Game.Tests/Resources/custom-slider-length.osu diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs index dcfe8ecb41..02432a1935 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyBeatmapDecoderTest.cs @@ -15,12 +15,14 @@ using osu.Game.IO; using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Catch.Beatmaps; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Beatmaps; +using osu.Game.Rulesets.Taiko; using osu.Game.Skinning; using osu.Game.Tests.Resources; using osuTK; @@ -1156,5 +1158,35 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.That(((IHasComboInformation)playable.HitObjects[17]).ComboIndexWithOffsets, Is.EqualTo(9)); } } + + [Test] + public void TestSliderConversionWithCustomDistance([Values("taiko", "mania")] string rulesetName) + { + using (var resStream = TestResources.OpenResource("custom-slider-length.osu")) + using (var stream = new LineBufferedReader(resStream)) + { + Ruleset ruleset; + + switch (rulesetName) + { + case "taiko": + ruleset = new TaikoRuleset(); + break; + + case "mania": + ruleset = new ManiaRuleset(); + break; + + default: + throw new ArgumentOutOfRangeException(nameof(rulesetName), rulesetName, null); + } + + var decoder = Decoder.GetDecoder(stream); + var working = new TestWorkingBeatmap(decoder.Decode(stream)); + IBeatmap beatmap = working.GetPlayableBeatmap(ruleset.RulesetInfo, Array.Empty()); + + Assert.That(beatmap.HitObjects[0].GetEndTime(), Is.EqualTo(3153)); + } + } } } diff --git a/osu.Game.Tests/Resources/custom-slider-length.osu b/osu.Game.Tests/Resources/custom-slider-length.osu new file mode 100644 index 0000000000..f7529918a9 --- /dev/null +++ b/osu.Game.Tests/Resources/custom-slider-length.osu @@ -0,0 +1,19 @@ +osu file format v14 + +[General] +Mode: 0 + +[Difficulty] +HPDrainRate:6 +CircleSize:7 +OverallDifficulty:7 +ApproachRate:10 +SliderMultiplier:1.7 +SliderTickRate:1 + +[TimingPoints] +29,333.333333333333,4,1,0,100,1,0 +29,-10000,4,1,0,100,0,0 + +[HitObjects] +256,192,29,6,0,P|384:192|384:192,1,159.375 \ No newline at end of file From 16577829e27696d27f3ae99129a1c9f3a16639d3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 28 Nov 2023 21:14:56 +0900 Subject: [PATCH 0279/1118] Fix mania and taiko slider conversion distance --- .../Patterns/Legacy/DistanceObjectPatternGenerator.cs | 9 ++++++++- .../Beatmaps/TaikoBeatmapConverter.cs | 10 +++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs index cce0944564..8aa128be33 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs @@ -60,8 +60,15 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy SpanCount = repeatsData?.SpanCount() ?? 1; StartTime = (int)Math.Round(hitObject.StartTime); + double distance; + + if (hitObject is IHasPath pathData) + distance = pathData.Path.ExpectedDistance.Value ?? 0; + else + distance = distanceData.Distance; + // This matches stable's calculation. - EndTime = (int)Math.Floor(StartTime + distanceData.Distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.SliderMultiplier); + EndTime = (int)Math.Floor(StartTime + distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.SliderMultiplier); SegmentDuration = (EndTime - StartTime) / SpanCount; } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index e46e2ec09c..4827ec76aa 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -182,7 +182,15 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // The true distance, accounting for any repeats. This ends up being the drum roll distance later int spans = (obj as IHasRepeats)?.SpanCount() ?? 1; - double distance = distanceData.Distance * spans * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + + double distance; + + if (obj is IHasPath pathData) + distance = pathData.Path.ExpectedDistance.Value ?? 0; + else + distance = distanceData.Distance; + + distance *= spans * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); From 979bbf0d810fb080e883c04c51c53677dd9f01cd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 28 Nov 2023 22:12:23 +0900 Subject: [PATCH 0280/1118] Wrap echo in double quotes --- .github/workflows/diffcalc.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/diffcalc.yml b/.github/workflows/diffcalc.yml index d4150208d3..5f16e09040 100644 --- a/.github/workflows/diffcalc.yml +++ b/.github/workflows/diffcalc.yml @@ -189,8 +189,8 @@ jobs: COMMENT_BODY: ${{ github.event.comment.body }} run: | # Add comment environment - echo $COMMENT_BODY | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do - opt=$(echo ${line} | cut -d '=' -f1) + echo "$COMMENT_BODY" | sed -r 's/\r$//' | grep -E '^\w+=' | while read -r line; do + opt=$(echo "${line}" | cut -d '=' -f1) sed -i "s;^${opt}=.*$;${line};" "${{ needs.directory.outputs.GENERATOR_ENV }}" done From c3ddf773b75c2d2bd64bf918a4c275ea8ae971f5 Mon Sep 17 00:00:00 2001 From: Rodrigo Pina Date: Tue, 28 Nov 2023 14:56:07 +0000 Subject: [PATCH 0281/1118] # osu.Game.Tournament.Models + Add: New property BanCount in TournamentRound to save the number of bans # osu.Game.Tournament/Screens + Add: New slider setting in RoundEditorScreen to select the number of bans per round * Change: Modified setNextMode behavior to get the round ban count, and select bans accordingly --- osu.Game.Tournament/Models/TournamentRound.cs | 1 + .../Screens/Editors/RoundEditorScreen.cs | 6 ++++++ osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 11 +++++++++-- 3 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tournament/Models/TournamentRound.cs b/osu.Game.Tournament/Models/TournamentRound.cs index a92bab690e..7aa8bbb44f 100644 --- a/osu.Game.Tournament/Models/TournamentRound.cs +++ b/osu.Game.Tournament/Models/TournamentRound.cs @@ -18,6 +18,7 @@ namespace osu.Game.Tournament.Models public readonly Bindable Description = new Bindable(string.Empty); public readonly BindableInt BestOf = new BindableInt(9) { Default = 9, MinValue = 3, MaxValue = 23 }; + public readonly BindableInt BanCount = new BindableInt(1) { Default = 1, MinValue = 0, MaxValue = 5 }; [JsonProperty] public readonly BindableList Beatmaps = new BindableList(); diff --git a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs index f887c41749..253cca8c98 100644 --- a/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs +++ b/osu.Game.Tournament/Screens/Editors/RoundEditorScreen.cs @@ -82,6 +82,12 @@ namespace osu.Game.Tournament.Screens.Editors Current = Model.StartDate }, new SettingsSlider + { + LabelText = "# of Bans", + Width = 0.33f, + Current = Model.BanCount + }, + new SettingsSlider { LabelText = "Best of", Width = 0.33f, diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index f80f43bb77..5f5fb873f4 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -146,17 +146,24 @@ namespace osu.Game.Tournament.Screens.MapPool private void setNextMode() { + int banCount = 2; + if (CurrentMatch.Value == null) return; + if (CurrentMatch.Value.Round.Value != null) + { + banCount = CurrentMatch.Value.Round.Value.BanCount.Value * 2; + } + const TeamColour roll_winner = TeamColour.Red; //todo: draw from match var nextColour = (CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; - if (pickType == ChoiceType.Ban && CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2) + if (pickType == ChoiceType.Ban && CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= banCount) setMode(pickColour, ChoiceType.Pick); else - setMode(nextColour, CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= 2 ? ChoiceType.Pick : ChoiceType.Ban); + setMode(nextColour, CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= banCount ? ChoiceType.Pick : ChoiceType.Ban); } protected override bool OnMouseDown(MouseDownEvent e) From 2dd12a67257c8f201d307b240f5753e43fc75c86 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2023 15:49:28 +0900 Subject: [PATCH 0282/1118] Improve logic around map pool mode changes --- .../Screens/MapPool/MapPoolScreen.cs | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 5f5fb873f4..5148c0eaf4 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -136,34 +136,34 @@ namespace osu.Game.Tournament.Screens.MapPool pickColour = colour; pickType = choiceType; - static Color4 setColour(bool active) => active ? Color4.White : Color4.Gray; - buttonRedBan.Colour = setColour(pickColour == TeamColour.Red && pickType == ChoiceType.Ban); buttonBlueBan.Colour = setColour(pickColour == TeamColour.Blue && pickType == ChoiceType.Ban); buttonRedPick.Colour = setColour(pickColour == TeamColour.Red && pickType == ChoiceType.Pick); buttonBluePick.Colour = setColour(pickColour == TeamColour.Blue && pickType == ChoiceType.Pick); + + static Color4 setColour(bool active) => active ? Color4.White : Color4.Gray; } private void setNextMode() { - int banCount = 2; - - if (CurrentMatch.Value == null) + if (CurrentMatch.Value?.Round.Value == null) return; - if (CurrentMatch.Value.Round.Value != null) - { - banCount = CurrentMatch.Value.Round.Value.BanCount.Value * 2; - } + int totalBansRequired = CurrentMatch.Value.Round.Value.BanCount.Value * 2; const TeamColour roll_winner = TeamColour.Red; //todo: draw from match var nextColour = (CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; - if (pickType == ChoiceType.Ban && CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= banCount) - setMode(pickColour, ChoiceType.Pick); - else - setMode(nextColour, CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= banCount ? ChoiceType.Pick : ChoiceType.Ban); + bool hasAllBans = CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= totalBansRequired; + + if (hasAllBans && pickType == ChoiceType.Ban) + { + // When switching from bans to picks, we don't rotate the team colour. + nextColour = pickColour; + } + + setMode(nextColour, hasAllBans ? ChoiceType.Pick : ChoiceType.Ban); } protected override bool OnMouseDown(MouseDownEvent e) From 301d503b0b52b4ba7ba16be201efeff7a5040e64 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 29 Nov 2023 16:41:19 +0900 Subject: [PATCH 0283/1118] Add another source of FP inaccuracy to match osu!stable --- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 4827ec76aa..2393a248eb 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -190,7 +190,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps else distance = distanceData.Distance; - distance *= spans * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + // Do not combine the following two lines! + distance *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + distance *= spans; TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); From 1c3bcbd54812f30c1170f2f5a0ab150220505d67 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 29 Nov 2023 17:30:21 +0900 Subject: [PATCH 0284/1118] Use IHasPath instead of IHasDistance for mania/taiko --- .../Beatmaps/ManiaBeatmapConverter.cs | 4 ++-- ...rator.cs => PathObjectPatternGenerator.cs} | 20 +++++-------------- .../Beatmaps/TaikoBeatmapConverter.cs | 14 ++++--------- 3 files changed, 11 insertions(+), 27 deletions(-) rename osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/{DistanceObjectPatternGenerator.cs => PathObjectPatternGenerator.cs} (96%) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index aaef69f119..ccfe1501bd 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -174,9 +174,9 @@ namespace osu.Game.Rulesets.Mania.Beatmaps switch (original) { - case IHasDistance: + case IHasPath: { - var generator = new DistanceObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap); + var generator = new PathObjectPatternGenerator(Random, original, beatmap, lastPattern, originalBeatmap); conversion = generator; var positionData = original as IHasPosition; diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PathObjectPatternGenerator.cs similarity index 96% rename from osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs rename to osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PathObjectPatternGenerator.cs index 8aa128be33..4922915c7d 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/DistanceObjectPatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PathObjectPatternGenerator.cs @@ -22,13 +22,8 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// /// A pattern generator for IHasDistance hit objects. /// - internal class DistanceObjectPatternGenerator : PatternGenerator + internal class PathObjectPatternGenerator : PatternGenerator { - /// - /// Base osu! slider scoring distance. - /// - private const float osu_base_scoring_distance = 100; - public readonly int StartTime; public readonly int EndTime; public readonly int SegmentDuration; @@ -36,17 +31,17 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy private PatternType convertType; - public DistanceObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) + public PathObjectPatternGenerator(LegacyRandom random, HitObject hitObject, ManiaBeatmap beatmap, Pattern previousPattern, IBeatmap originalBeatmap) : base(random, hitObject, beatmap, previousPattern, originalBeatmap) { convertType = PatternType.None; if (!Beatmap.ControlPointInfo.EffectPointAt(hitObject.StartTime).KiaiMode) convertType = PatternType.LowProbability; - var distanceData = hitObject as IHasDistance; + var pathData = hitObject as IHasPath; var repeatsData = hitObject as IHasRepeats; - Debug.Assert(distanceData != null); + Debug.Assert(pathData != null); TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(hitObject.StartTime); @@ -60,12 +55,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy SpanCount = repeatsData?.SpanCount() ?? 1; StartTime = (int)Math.Round(hitObject.StartTime); - double distance; - - if (hitObject is IHasPath pathData) - distance = pathData.Path.ExpectedDistance.Value ?? 0; - else - distance = distanceData.Distance; + double distance = pathData.Path.ExpectedDistance.Value ?? 0; // This matches stable's calculation. EndTime = (int)Math.Floor(StartTime + distance * beatLength * SpanCount * 0.01 / beatmap.Difficulty.SliderMultiplier); diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 2393a248eb..2551321ff2 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -109,9 +109,9 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps switch (obj) { - case IHasDistance distanceData: + case IHasPath pathData: { - if (shouldConvertSliderToHits(obj, beatmap, distanceData, out int taikoDuration, out double tickSpacing)) + if (shouldConvertSliderToHits(obj, beatmap, pathData, out int taikoDuration, out double tickSpacing)) { IList> allSamples = obj is IHasPathWithRepeats curveData ? curveData.NodeSamples : new List>(new[] { samples }); @@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps } } - private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasDistance distanceData, out int taikoDuration, out double tickSpacing) + private bool shouldConvertSliderToHits(HitObject obj, IBeatmap beatmap, IHasPath pathData, out int taikoDuration, out double tickSpacing) { // DO NOT CHANGE OR REFACTOR ANYTHING IN HERE WITHOUT TESTING AGAINST _ALL_ BEATMAPS. // Some of these calculations look redundant, but they are not - extremely small floating point errors are introduced to maintain 1:1 compatibility with stable. @@ -182,13 +182,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps // The true distance, accounting for any repeats. This ends up being the drum roll distance later int spans = (obj as IHasRepeats)?.SpanCount() ?? 1; - - double distance; - - if (obj is IHasPath pathData) - distance = pathData.Path.ExpectedDistance.Value ?? 0; - else - distance = distanceData.Distance; + double distance = pathData.Path.ExpectedDistance.Value ?? 0; // Do not combine the following two lines! distance *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; From 295a1b01d6257980a688551e7aaa0fdd99b49257 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 29 Nov 2023 19:05:24 +0900 Subject: [PATCH 0285/1118] Adjust catch score grade cutoffs --- .../Scoring/CatchScoreProcessor.cs | 53 +++++++++ .../Visual/Ranking/TestSceneAccuracyCircle.cs | 110 +++++++++++------- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 +- .../Expanded/Accuracy/AccuracyCircle.cs | 69 ++++++----- 4 files changed, 160 insertions(+), 76 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 9323296b7f..66c76f9b17 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -4,11 +4,19 @@ using System; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; namespace osu.Game.Rulesets.Catch.Scoring { public partial class CatchScoreProcessor : ScoreProcessor { + private const double accuracy_cutoff_x = 1; + private const double accuracy_cutoff_s = 0.98; + private const double accuracy_cutoff_a = 0.94; + private const double accuracy_cutoff_b = 0.9; + private const double accuracy_cutoff_c = 0.85; + private const double accuracy_cutoff_d = 0; + private const int combo_cap = 200; private const double combo_base = 4; @@ -26,5 +34,50 @@ namespace osu.Game.Rulesets.Catch.Scoring protected override double GetComboScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); + + public override ScoreRank RankFromAccuracy(double accuracy) + { + if (accuracy == accuracy_cutoff_x) + return ScoreRank.X; + if (accuracy >= accuracy_cutoff_s) + return ScoreRank.S; + if (accuracy >= accuracy_cutoff_a) + return ScoreRank.A; + if (accuracy >= accuracy_cutoff_b) + return ScoreRank.B; + if (accuracy >= accuracy_cutoff_c) + return ScoreRank.C; + + return ScoreRank.D; + } + + public override double AccuracyCutoffFromRank(ScoreRank rank) + { + switch (rank) + { + case ScoreRank.X: + case ScoreRank.XH: + return accuracy_cutoff_x; + + case ScoreRank.S: + case ScoreRank.SH: + return accuracy_cutoff_s; + + case ScoreRank.A: + return accuracy_cutoff_a; + + case ScoreRank.B: + return accuracy_cutoff_b; + + case ScoreRank.C: + return accuracy_cutoff_c; + + case ScoreRank.D: + return accuracy_cutoff_d; + + default: + throw new ArgumentOutOfRangeException(nameof(rank), rank, null); + } + } } } diff --git a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs index 03b168c72c..435dd77120 100644 --- a/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs +++ b/osu.Game.Tests/Visual/Ranking/TestSceneAccuracyCircle.cs @@ -9,6 +9,8 @@ using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets; +using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; @@ -22,31 +24,48 @@ namespace osu.Game.Tests.Visual.Ranking { public partial class TestSceneAccuracyCircle : OsuTestScene { - [TestCase(0)] - [TestCase(0.2)] - [TestCase(0.5)] - [TestCase(0.6999)] - [TestCase(0.7)] - [TestCase(0.75)] - [TestCase(0.7999)] - [TestCase(0.8)] - [TestCase(0.85)] - [TestCase(0.8999)] - [TestCase(0.9)] - [TestCase(0.925)] - [TestCase(0.9499)] - [TestCase(0.95)] - [TestCase(0.975)] - [TestCase(0.9999)] - [TestCase(1)] - public void TestRank(double accuracy) + [Test] + public void TestOsuRank() { - var score = createScore(accuracy, ScoreProcessor.RankFromAccuracy(accuracy)); - - addCircleStep(score); + addCircleStep(createScore(0, new OsuRuleset())); + addCircleStep(createScore(0.5, new OsuRuleset())); + addCircleStep(createScore(0.699, new OsuRuleset())); + addCircleStep(createScore(0.7, new OsuRuleset())); + addCircleStep(createScore(0.75, new OsuRuleset())); + addCircleStep(createScore(0.799, new OsuRuleset())); + addCircleStep(createScore(0.8, new OsuRuleset())); + addCircleStep(createScore(0.85, new OsuRuleset())); + addCircleStep(createScore(0.899, new OsuRuleset())); + addCircleStep(createScore(0.9, new OsuRuleset())); + addCircleStep(createScore(0.925, new OsuRuleset())); + addCircleStep(createScore(0.9499, new OsuRuleset())); + addCircleStep(createScore(0.95, new OsuRuleset())); + addCircleStep(createScore(0.975, new OsuRuleset())); + addCircleStep(createScore(0.99, new OsuRuleset())); + addCircleStep(createScore(1, new OsuRuleset())); } - private void addCircleStep(ScoreInfo score) => AddStep("add panel", () => + [Test] + public void TestCatchRank() + { + addCircleStep(createScore(0, new CatchRuleset())); + addCircleStep(createScore(0.5, new CatchRuleset())); + addCircleStep(createScore(0.8499, new CatchRuleset())); + addCircleStep(createScore(0.85, new CatchRuleset())); + addCircleStep(createScore(0.875, new CatchRuleset())); + addCircleStep(createScore(0.899, new CatchRuleset())); + addCircleStep(createScore(0.9, new CatchRuleset())); + addCircleStep(createScore(0.925, new CatchRuleset())); + addCircleStep(createScore(0.9399, new CatchRuleset())); + addCircleStep(createScore(0.94, new CatchRuleset())); + addCircleStep(createScore(0.9675, new CatchRuleset())); + addCircleStep(createScore(0.9799, new CatchRuleset())); + addCircleStep(createScore(0.98, new CatchRuleset())); + addCircleStep(createScore(0.99, new CatchRuleset())); + addCircleStep(createScore(1, new CatchRuleset())); + } + + private void addCircleStep(ScoreInfo score) => AddStep($"add panel ({score.DisplayAccuracy})", () => { Children = new Drawable[] { @@ -73,28 +92,33 @@ namespace osu.Game.Tests.Visual.Ranking }; }); - private ScoreInfo createScore(double accuracy, ScoreRank rank) => new ScoreInfo + private ScoreInfo createScore(double accuracy, Ruleset ruleset) { - User = new APIUser + var scoreProcessor = ruleset.CreateScoreProcessor(); + + return new ScoreInfo { - Id = 2, - Username = "peppy", - }, - BeatmapInfo = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, - Ruleset = new OsuRuleset().RulesetInfo, - Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, - TotalScore = 2845370, - Accuracy = accuracy, - MaxCombo = 999, - Rank = rank, - Date = DateTimeOffset.Now, - Statistics = - { - { HitResult.Miss, 1 }, - { HitResult.Meh, 50 }, - { HitResult.Good, 100 }, - { HitResult.Great, 300 }, - } - }; + User = new APIUser + { + Id = 2, + Username = "peppy", + }, + BeatmapInfo = new TestBeatmap(new OsuRuleset().RulesetInfo).BeatmapInfo, + Ruleset = ruleset.RulesetInfo, + Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, + TotalScore = 2845370, + Accuracy = accuracy, + MaxCombo = 999, + Rank = scoreProcessor.RankFromAccuracy(accuracy), + Date = DateTimeOffset.Now, + Statistics = + { + { HitResult.Miss, 1 }, + { HitResult.Meh, 50 }, + { HitResult.Good, 100 }, + { HitResult.Great, 300 }, + } + }; + } } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 4e899479bd..92336b2c21 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -446,7 +446,7 @@ namespace osu.Game.Rulesets.Scoring /// /// Given an accuracy (0..1), return the correct . /// - public static ScoreRank RankFromAccuracy(double accuracy) + public virtual ScoreRank RankFromAccuracy(double accuracy) { if (accuracy == accuracy_cutoff_x) return ScoreRank.X; @@ -466,7 +466,7 @@ namespace osu.Game.Rulesets.Scoring /// Given a , return the cutoff accuracy (0..1). /// Accuracy must be greater than or equal to the cutoff to qualify for the provided rank. /// - public static double AccuracyCutoffFromRank(ScoreRank rank) + public virtual double AccuracyCutoffFromRank(ScoreRank rank) { switch (rank) { diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 2ec4270c3c..80ff872312 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -29,13 +29,6 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy /// public partial class AccuracyCircle : CompositeDrawable { - private static readonly double accuracy_x = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.X); - private static readonly double accuracy_s = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.S); - private static readonly double accuracy_a = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.A); - private static readonly double accuracy_b = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.B); - private static readonly double accuracy_c = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.C); - private static readonly double accuracy_d = ScoreProcessor.AccuracyCutoffFromRank(ScoreRank.D); - /// /// Duration for the transforms causing this component to appear. /// @@ -110,12 +103,26 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy private double lastTickPlaybackTime; private bool isTicking; + private readonly double accuracyX; + private readonly double accuracyS; + private readonly double accuracyA; + private readonly double accuracyB; + private readonly double accuracyC; + private readonly double accuracyD; private readonly bool withFlair; public AccuracyCircle(ScoreInfo score, bool withFlair = false) { this.score = score; this.withFlair = withFlair; + + ScoreProcessor scoreProcessor = score.Ruleset.CreateInstance().CreateScoreProcessor(); + accuracyX = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.X); + accuracyS = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.S); + accuracyA = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.A); + accuracyB = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.B); + accuracyC = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.C); + accuracyD = scoreProcessor.AccuracyCutoffFromRank(ScoreRank.D); } [BackgroundDependencyLoader] @@ -158,49 +165,49 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.X), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = accuracy_x } + Current = { Value = accuracyX } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.S), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = accuracy_x - virtual_ss_percentage } + Current = { Value = accuracyX - virtual_ss_percentage } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.A), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = accuracy_s } + Current = { Value = accuracyS } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.B), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = accuracy_a } + Current = { Value = accuracyA } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.C), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = accuracy_b } + Current = { Value = accuracyB } }, new CircularProgress { RelativeSizeAxes = Axes.Both, Colour = OsuColour.ForRank(ScoreRank.D), InnerRadius = RANK_CIRCLE_RADIUS, - Current = { Value = accuracy_c } + Current = { Value = accuracyC } }, - new RankNotch((float)accuracy_x), - new RankNotch((float)(accuracy_x - virtual_ss_percentage)), - new RankNotch((float)accuracy_s), - new RankNotch((float)accuracy_a), - new RankNotch((float)accuracy_b), - new RankNotch((float)accuracy_c), + new RankNotch((float)accuracyX), + new RankNotch((float)(accuracyX - virtual_ss_percentage)), + new RankNotch((float)accuracyS), + new RankNotch((float)accuracyA), + new RankNotch((float)accuracyB), + new RankNotch((float)accuracyC), new BufferedContainer { Name = "Graded circle mask", @@ -229,12 +236,12 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy Children = new[] { // The S and A badges are moved down slightly to prevent collision with the SS badge. - new RankBadge(accuracy_x, accuracy_x, getRank(ScoreRank.X)), - new RankBadge(accuracy_s, Interpolation.Lerp(accuracy_s, (accuracy_x - virtual_ss_percentage), 0.25), getRank(ScoreRank.S)), - new RankBadge(accuracy_a, Interpolation.Lerp(accuracy_a, accuracy_s, 0.25), getRank(ScoreRank.A)), - new RankBadge(accuracy_b, Interpolation.Lerp(accuracy_b, accuracy_a, 0.5), getRank(ScoreRank.B)), - new RankBadge(accuracy_c, Interpolation.Lerp(accuracy_c, accuracy_b, 0.5), getRank(ScoreRank.C)), - new RankBadge(accuracy_d, Interpolation.Lerp(accuracy_d, accuracy_c, 0.5), getRank(ScoreRank.D)), + new RankBadge(accuracyX, accuracyX, getRank(ScoreRank.X)), + new RankBadge(accuracyS, Interpolation.Lerp(accuracyS, (accuracyX - virtual_ss_percentage), 0.25), getRank(ScoreRank.S)), + new RankBadge(accuracyA, Interpolation.Lerp(accuracyA, accuracyS, 0.25), getRank(ScoreRank.A)), + new RankBadge(accuracyB, Interpolation.Lerp(accuracyB, accuracyA, 0.5), getRank(ScoreRank.B)), + new RankBadge(accuracyC, Interpolation.Lerp(accuracyC, accuracyB, 0.5), getRank(ScoreRank.C)), + new RankBadge(accuracyD, Interpolation.Lerp(accuracyD, accuracyC, 0.5), getRank(ScoreRank.D)), } }, rankText = new RankText(score.Rank) @@ -280,10 +287,10 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy double targetAccuracy = score.Accuracy; double[] notchPercentages = { - accuracy_s, - accuracy_a, - accuracy_b, - accuracy_c, + accuracyS, + accuracyA, + accuracyB, + accuracyC, }; // Ensure the gauge overshoots or undershoots a bit so it doesn't land in the gaps of the inner graded circle (caused by `RankNotch`es), @@ -302,7 +309,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy if (score.Rank == ScoreRank.X || score.Rank == ScoreRank.XH) targetAccuracy = 1; else - targetAccuracy = Math.Min(accuracy_x - virtual_ss_percentage - NOTCH_WIDTH_PERCENTAGE / 2, targetAccuracy); + targetAccuracy = Math.Min(accuracyX - virtual_ss_percentage - NOTCH_WIDTH_PERCENTAGE / 2, targetAccuracy); // The accuracy circle gauge visually fills up a bit too much. // This wouldn't normally matter but we want it to align properly with the inner graded circle in the above cases. @@ -339,7 +346,7 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy if (badge.Accuracy > score.Accuracy) continue; - using (BeginDelayedSequence(inverseEasing(ACCURACY_TRANSFORM_EASING, Math.Min(accuracy_x - virtual_ss_percentage, badge.Accuracy) / targetAccuracy) * ACCURACY_TRANSFORM_DURATION)) + using (BeginDelayedSequence(inverseEasing(ACCURACY_TRANSFORM_EASING, Math.Min(accuracyX - virtual_ss_percentage, badge.Accuracy) / targetAccuracy) * ACCURACY_TRANSFORM_DURATION)) { badge.Appear(); From 60d6c0fe53ebd1b5d9761cc1151f3a44473948e4 Mon Sep 17 00:00:00 2001 From: Rodrigo Pina Date: Wed, 29 Nov 2023 11:22:07 +0000 Subject: [PATCH 0286/1118] Changed ban order to match typical tournament ban structure --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 5148c0eaf4..60abddfe67 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -153,7 +153,9 @@ namespace osu.Game.Tournament.Screens.MapPool const TeamColour roll_winner = TeamColour.Red; //todo: draw from match - var nextColour = (CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; + var previousBan = CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner; + + var nextColour = (CurrentMatch.Value.PicksBans.Count() >= 2 ? CurrentMatch.Value.PicksBans[^2]?.Team : previousBan) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; bool hasAllBans = CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= totalBansRequired; From 1cfcaee121c7f5b62610a751d1251b752c7dc794 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2023 20:29:50 +0900 Subject: [PATCH 0287/1118] Reorder badges so that SS shows above others This isn't perfect and probably needs much more consideration, but let's at least give the "better" ranks more visibility by bringing them to the front. Of note, this is only important due to the changes to osu!catch accuracy-grade cutoffs, which brings things closer in proximity than ever before. --- .../Ranking/Expanded/Accuracy/AccuracyCircle.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs index 80ff872312..8cbca74466 100644 --- a/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs +++ b/osu.Game/Screens/Ranking/Expanded/Accuracy/AccuracyCircle.cs @@ -235,13 +235,13 @@ namespace osu.Game.Screens.Ranking.Expanded.Accuracy Padding = new MarginPadding { Vertical = -15, Horizontal = -20 }, Children = new[] { - // The S and A badges are moved down slightly to prevent collision with the SS badge. - new RankBadge(accuracyX, accuracyX, getRank(ScoreRank.X)), - new RankBadge(accuracyS, Interpolation.Lerp(accuracyS, (accuracyX - virtual_ss_percentage), 0.25), getRank(ScoreRank.S)), - new RankBadge(accuracyA, Interpolation.Lerp(accuracyA, accuracyS, 0.25), getRank(ScoreRank.A)), - new RankBadge(accuracyB, Interpolation.Lerp(accuracyB, accuracyA, 0.5), getRank(ScoreRank.B)), - new RankBadge(accuracyC, Interpolation.Lerp(accuracyC, accuracyB, 0.5), getRank(ScoreRank.C)), new RankBadge(accuracyD, Interpolation.Lerp(accuracyD, accuracyC, 0.5), getRank(ScoreRank.D)), + new RankBadge(accuracyC, Interpolation.Lerp(accuracyC, accuracyB, 0.5), getRank(ScoreRank.C)), + new RankBadge(accuracyB, Interpolation.Lerp(accuracyB, accuracyA, 0.5), getRank(ScoreRank.B)), + // The S and A badges are moved down slightly to prevent collision with the SS badge. + new RankBadge(accuracyA, Interpolation.Lerp(accuracyA, accuracyS, 0.25), getRank(ScoreRank.A)), + new RankBadge(accuracyS, Interpolation.Lerp(accuracyS, (accuracyX - virtual_ss_percentage), 0.25), getRank(ScoreRank.S)), + new RankBadge(accuracyX, accuracyX, getRank(ScoreRank.X)), } }, rankText = new RankText(score.Rank) From a33a4c4d1d7094c6fe2b200ab14f8fd8a4439413 Mon Sep 17 00:00:00 2001 From: Rodrigo Pina Date: Wed, 29 Nov 2023 11:31:15 +0000 Subject: [PATCH 0288/1118] Fixed issue where pick order was following ban order structure --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 60abddfe67..9da55cc607 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -155,10 +155,14 @@ namespace osu.Game.Tournament.Screens.MapPool var previousBan = CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner; - var nextColour = (CurrentMatch.Value.PicksBans.Count() >= 2 ? CurrentMatch.Value.PicksBans[^2]?.Team : previousBan) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; + var nextColour = previousBan == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; bool hasAllBans = CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= totalBansRequired; + if (!hasAllBans) + // If it's the third ban or later, we need to check if it's the team's first or second ban in a row + nextColour = (CurrentMatch.Value.PicksBans.Count() >= 2 ? CurrentMatch.Value.PicksBans[^2]?.Team : previousBan) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; + if (hasAllBans && pickType == ChoiceType.Ban) { // When switching from bans to picks, we don't rotate the team colour. From 3553717cc6238251cd7a0dd95d4749584c23504c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 29 Nov 2023 21:28:25 +0900 Subject: [PATCH 0289/1118] Fix results screen not including slider end misses in tick count --- osu.Game/Scoring/ScoreInfo.cs | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index d712702331..5545ba552e 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -342,23 +342,7 @@ namespace osu.Game.Scoring switch (r.result) { case HitResult.SmallTickHit: - { - int total = value + Statistics.GetValueOrDefault(HitResult.SmallTickMiss); - if (total > 0) - yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); - - break; - } - case HitResult.LargeTickHit: - { - int total = value + Statistics.GetValueOrDefault(HitResult.LargeTickMiss); - if (total > 0) - yield return new HitResultDisplayStatistic(r.result, value, total, r.displayName); - - break; - } - case HitResult.LargeBonus: case HitResult.SmallBonus: if (MaximumStatistics.TryGetValue(r.result, out int count) && count > 0) From ecbf07c52acbc247a7825c102b8a10cb88ea333e Mon Sep 17 00:00:00 2001 From: Rodrigo Pina Date: Thu, 30 Nov 2023 02:56:23 +0000 Subject: [PATCH 0290/1118] Replace Count() from CurrentMatch.Value.PicksBans with property alternative --- osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 9da55cc607..1223fd8464 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -161,7 +161,7 @@ namespace osu.Game.Tournament.Screens.MapPool if (!hasAllBans) // If it's the third ban or later, we need to check if it's the team's first or second ban in a row - nextColour = (CurrentMatch.Value.PicksBans.Count() >= 2 ? CurrentMatch.Value.PicksBans[^2]?.Team : previousBan) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; + nextColour = (CurrentMatch.Value.PicksBans.Count >= 2 ? CurrentMatch.Value.PicksBans[^2]?.Team : previousBan) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; if (hasAllBans && pickType == ChoiceType.Ban) { From 831fe5b9f2a833dc8d1dbd4c4340376c10c854cc Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 30 Nov 2023 14:36:23 +0100 Subject: [PATCH 0291/1118] Use collection assert to ease debugging --- .../Visual/Online/TestSceneChatLink.cs | 20 ++----------------- 1 file changed, 2 insertions(+), 18 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 7616b9b83c..70eca64084 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -107,27 +107,11 @@ namespace osu.Game.Tests.Visual.Online textContainer.Add(newLine); }); - AddAssert($"msg #{index} has {linkAmount} link(s)", () => newLine.Message.Links.Count == linkAmount); - AddAssert($"msg #{index} has the right action", hasExpectedActions); + AddAssert($"msg #{index} has {linkAmount} link(s)", () => newLine.Message.Links, () => Has.Count.EqualTo(linkAmount)); + AddAssert($"msg #{index} has the right action", () => newLine.Message.Links.Select(l => l.Action), () => Is.EqualTo(expectedActions)); //AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); AddAssert($"msg #{index} shows {linkAmount} link(s)", isShowingLinks); - bool hasExpectedActions() - { - var expectedActionsList = expectedActions.ToList(); - - if (expectedActionsList.Count != newLine.Message.Links.Count) - return false; - - for (int i = 0; i < newLine.Message.Links.Count; i++) - { - var action = newLine.Message.Links[i].Action; - if (action != expectedActions[i]) return false; - } - - return true; - } - //bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); bool isShowingLinks() From 9a32c0368e6d03153ac21070cf5a979e200df674 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 30 Nov 2023 14:38:58 +0100 Subject: [PATCH 0292/1118] Remove redundant `linkAmount` parameter --- .../Visual/Online/TestSceneChatLink.cs | 59 +++++++++---------- 1 file changed, 29 insertions(+), 30 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 70eca64084..1ab7e3257f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -63,40 +63,40 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("test!"); addMessageWithChecks("dev.ppy.sh!"); - addMessageWithChecks("https://dev.ppy.sh!", 1, expectedActions: LinkAction.External); - addMessageWithChecks("http://dev.ppy.sh!", 1, expectedActions: LinkAction.External); - addMessageWithChecks("forgothttps://dev.ppy.sh!", 1, expectedActions: LinkAction.External); - addMessageWithChecks("forgothttp://dev.ppy.sh!", 1, expectedActions: LinkAction.External); - addMessageWithChecks("00:12:345 (1,2) - Test?", 1, expectedActions: LinkAction.OpenEditorTimestamp); - addMessageWithChecks("Wiki link for tasty [[Performance Points]]", 1, expectedActions: LinkAction.OpenWiki); - addMessageWithChecks("(osu forums)[https://dev.ppy.sh/forum] (old link format)", 1, expectedActions: LinkAction.External); - addMessageWithChecks("[https://dev.ppy.sh/home New site] (new link format)", 1, expectedActions: LinkAction.External); - addMessageWithChecks("[osu forums](https://dev.ppy.sh/forum) (new link format 2)", 1, expectedActions: LinkAction.External); - addMessageWithChecks("[https://dev.ppy.sh/home This is only a link to the new osu webpage but this is supposed to test word wrap.]", 1, expectedActions: LinkAction.External); - addMessageWithChecks("is now listening to [https://dev.ppy.sh/s/93523 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmapSet); - addMessageWithChecks("is now playing [https://dev.ppy.sh/b/252238 IMAGE -MATERIAL- ]", 1, true, expectedActions: LinkAction.OpenBeatmap); - addMessageWithChecks("Let's (try)[https://dev.ppy.sh/home] [https://dev.ppy.sh/b/252238 multiple links] https://dev.ppy.sh/home", 3, + addMessageWithChecks("https://dev.ppy.sh!", expectedActions: LinkAction.External); + addMessageWithChecks("http://dev.ppy.sh!", expectedActions: LinkAction.External); + addMessageWithChecks("forgothttps://dev.ppy.sh!", expectedActions: LinkAction.External); + addMessageWithChecks("forgothttp://dev.ppy.sh!", expectedActions: LinkAction.External); + addMessageWithChecks("00:12:345 (1,2) - Test?", expectedActions: LinkAction.OpenEditorTimestamp); + addMessageWithChecks("Wiki link for tasty [[Performance Points]]", expectedActions: LinkAction.OpenWiki); + addMessageWithChecks("(osu forums)[https://dev.ppy.sh/forum] (old link format)", expectedActions: LinkAction.External); + addMessageWithChecks("[https://dev.ppy.sh/home New site] (new link format)", expectedActions: LinkAction.External); + addMessageWithChecks("[osu forums](https://dev.ppy.sh/forum) (new link format 2)", expectedActions: LinkAction.External); + addMessageWithChecks("[https://dev.ppy.sh/home This is only a link to the new osu webpage but this is supposed to test word wrap.]", expectedActions: LinkAction.External); + addMessageWithChecks("is now listening to [https://dev.ppy.sh/s/93523 IMAGE -MATERIAL- ]", isAction: true, expectedActions: LinkAction.OpenBeatmapSet); + addMessageWithChecks("is now playing [https://dev.ppy.sh/b/252238 IMAGE -MATERIAL- ]", isAction: true, expectedActions: LinkAction.OpenBeatmap); + addMessageWithChecks("Let's (try)[https://dev.ppy.sh/home] [https://dev.ppy.sh/b/252238 multiple links] https://dev.ppy.sh/home", expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); - addMessageWithChecks("[https://dev.ppy.sh/home New link format with escaped [and \\[ paired] braces]", 1, expectedActions: LinkAction.External); - addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://dev.ppy.sh/home)", 1, expectedActions: LinkAction.External); - addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://dev.ppy.sh/home] and [[also a rogue wiki link]]", 2, + addMessageWithChecks("[https://dev.ppy.sh/home New link format with escaped [and \\[ paired] braces]", expectedActions: LinkAction.External); + addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://dev.ppy.sh/home)", expectedActions: LinkAction.External); + addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://dev.ppy.sh/home] and [[also a rogue wiki link]]", expectedActions: new[] { LinkAction.External, LinkAction.OpenWiki }); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); - addMessageWithChecks("I am important!", 0, false, true); - addMessageWithChecks("feels important", 0, true, true); - addMessageWithChecks("likes to post this [https://dev.ppy.sh/home link].", 1, true, true, expectedActions: LinkAction.External); - addMessageWithChecks("Join my multiplayer game osump://12346.", 1, expectedActions: LinkAction.JoinMultiplayerMatch); - addMessageWithChecks("Join my multiplayer gameosump://12346.", 1, expectedActions: LinkAction.JoinMultiplayerMatch); - addMessageWithChecks("Join my [multiplayer game](osump://12346).", 1, expectedActions: LinkAction.JoinMultiplayerMatch); - addMessageWithChecks($"Join my [#english]({OsuGameBase.OSU_PROTOCOL}chan/#english).", 1, expectedActions: LinkAction.OpenChannel); - addMessageWithChecks($"Join my {OsuGameBase.OSU_PROTOCOL}chan/#english.", 1, expectedActions: LinkAction.OpenChannel); - addMessageWithChecks($"Join my{OsuGameBase.OSU_PROTOCOL}chan/#english.", 1, expectedActions: LinkAction.OpenChannel); - addMessageWithChecks("Join my #english or #japanese channels.", 2, expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel }); - addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", 1, expectedActions: LinkAction.OpenChannel); + addMessageWithChecks("I am important!", isAction: false, isImportant: true); + addMessageWithChecks("feels important", isAction: true, isImportant: true); + addMessageWithChecks("likes to post this [https://dev.ppy.sh/home link].", isAction: true, isImportant: true, expectedActions: LinkAction.External); + addMessageWithChecks("Join my multiplayer game osump://12346.", expectedActions: LinkAction.JoinMultiplayerMatch); + addMessageWithChecks("Join my multiplayer gameosump://12346.", expectedActions: LinkAction.JoinMultiplayerMatch); + addMessageWithChecks("Join my [multiplayer game](osump://12346).", expectedActions: LinkAction.JoinMultiplayerMatch); + addMessageWithChecks($"Join my [#english]({OsuGameBase.OSU_PROTOCOL}chan/#english).", expectedActions: LinkAction.OpenChannel); + addMessageWithChecks($"Join my {OsuGameBase.OSU_PROTOCOL}chan/#english.", expectedActions: LinkAction.OpenChannel); + addMessageWithChecks($"Join my{OsuGameBase.OSU_PROTOCOL}chan/#english.", expectedActions: LinkAction.OpenChannel); + addMessageWithChecks("Join my #english or #japanese channels.", expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel }); + addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", expectedActions: LinkAction.OpenChannel); addMessageWithChecks("Hello world\uD83D\uDE12(<--This is an emoji). There are more:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20"); - void addMessageWithChecks(string text, int linkAmount = 0, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) + void addMessageWithChecks(string text, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) { ChatLine newLine = null; int index = messageIndex++; @@ -107,10 +107,9 @@ namespace osu.Game.Tests.Visual.Online textContainer.Add(newLine); }); - AddAssert($"msg #{index} has {linkAmount} link(s)", () => newLine.Message.Links, () => Has.Count.EqualTo(linkAmount)); AddAssert($"msg #{index} has the right action", () => newLine.Message.Links.Select(l => l.Action), () => Is.EqualTo(expectedActions)); //AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); - AddAssert($"msg #{index} shows {linkAmount} link(s)", isShowingLinks); + AddAssert($"msg #{index} shows {expectedActions.Length} link(s)", isShowingLinks); //bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); From d2324cd8f9ca27da010d26d542ccd80d7a3a29d8 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 30 Nov 2023 10:20:01 -0800 Subject: [PATCH 0293/1118] Fix chat overlay top bar icon being incorrect --- osu.Game/Overlays/Chat/ChatOverlayTopBar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs index 84fd342493..4fc9fbb6d5 100644 --- a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs +++ b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Chat { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Icon = HexaconsIcons.Social, + Icon = HexaconsIcons.Messaging, Size = new Vector2(24), }, // Placeholder text From 3ca3f254925603f953163ad9f7d694b18865cd13 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 30 Nov 2023 23:25:28 +0100 Subject: [PATCH 0294/1118] Extract local method --- .../Visual/Online/TestSceneChatLink.cs | 45 +++++++++---------- 1 file changed, 21 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 1ab7e3257f..a0fcdf4337 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -59,8 +59,6 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestLinksGeneral() { - int messageIndex = 0; - addMessageWithChecks("test!"); addMessageWithChecks("dev.ppy.sh!"); addMessageWithChecks("https://dev.ppy.sh!", expectedActions: LinkAction.External); @@ -95,36 +93,35 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("Join my #english or #japanese channels.", expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel }); addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", expectedActions: LinkAction.OpenChannel); addMessageWithChecks("Hello world\uD83D\uDE12(<--This is an emoji). There are more:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20"); + } - void addMessageWithChecks(string text, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) + private void addMessageWithChecks(string text, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) + { + ChatLine newLine = null; + + AddStep("add message", () => { - ChatLine newLine = null; - int index = messageIndex++; + newLine = new ChatLine(new DummyMessage(text, isAction, isImportant)); + textContainer.Add(newLine); + }); - AddStep("add message", () => - { - newLine = new ChatLine(new DummyMessage(text, isAction, isImportant, index)); - textContainer.Add(newLine); - }); + AddAssert($"msg has the right action", () => newLine.Message.Links.Select(l => l.Action), () => Is.EqualTo(expectedActions)); + //AddAssert($"msg is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); + AddAssert($"msg shows {expectedActions.Length} link(s)", isShowingLinks); - AddAssert($"msg #{index} has the right action", () => newLine.Message.Links.Select(l => l.Action), () => Is.EqualTo(expectedActions)); - //AddAssert($"msg #{index} is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); - AddAssert($"msg #{index} shows {expectedActions.Length} link(s)", isShowingLinks); + //bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); - //bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); + bool isShowingLinks() + { + bool hasBackground = !string.IsNullOrEmpty(newLine.Message.Sender.Colour); - bool isShowingLinks() - { - bool hasBackground = !string.IsNullOrEmpty(newLine.Message.Sender.Colour); + Color4 textColour = isAction && hasBackground ? Color4Extensions.FromHex(newLine.Message.Sender.Colour) : Color4.White; - Color4 textColour = isAction && hasBackground ? Color4Extensions.FromHex(newLine.Message.Sender.Colour) : Color4.White; + var linkCompilers = newLine.DrawableContentFlow.Where(d => d is DrawableLinkCompiler).ToList(); + var linkSprites = linkCompilers.SelectMany(comp => ((DrawableLinkCompiler)comp).Parts); - var linkCompilers = newLine.DrawableContentFlow.Where(d => d is DrawableLinkCompiler).ToList(); - var linkSprites = linkCompilers.SelectMany(comp => ((DrawableLinkCompiler)comp).Parts); - - return linkSprites.All(d => d.Colour == linkColour) - && newLine.DrawableContentFlow.Except(linkSprites.Concat(linkCompilers)).All(d => d.Colour == textColour); - } + return linkSprites.All(d => d.Colour == linkColour) + && newLine.DrawableContentFlow.Except(linkSprites.Concat(linkCompilers)).All(d => d.Colour == textColour); } } From 52ffbcc6db7e21eca7f49ac6d124b8b01c12ed4d Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 30 Nov 2023 23:29:22 +0100 Subject: [PATCH 0295/1118] Move special cases to `TestCase`'d test --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index a0fcdf4337..52eebeb9ce 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -71,8 +71,6 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("[https://dev.ppy.sh/home New site] (new link format)", expectedActions: LinkAction.External); addMessageWithChecks("[osu forums](https://dev.ppy.sh/forum) (new link format 2)", expectedActions: LinkAction.External); addMessageWithChecks("[https://dev.ppy.sh/home This is only a link to the new osu webpage but this is supposed to test word wrap.]", expectedActions: LinkAction.External); - addMessageWithChecks("is now listening to [https://dev.ppy.sh/s/93523 IMAGE -MATERIAL- ]", isAction: true, expectedActions: LinkAction.OpenBeatmapSet); - addMessageWithChecks("is now playing [https://dev.ppy.sh/b/252238 IMAGE -MATERIAL- ]", isAction: true, expectedActions: LinkAction.OpenBeatmap); addMessageWithChecks("Let's (try)[https://dev.ppy.sh/home] [https://dev.ppy.sh/b/252238 multiple links] https://dev.ppy.sh/home", expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); addMessageWithChecks("[https://dev.ppy.sh/home New link format with escaped [and \\[ paired] braces]", expectedActions: LinkAction.External); @@ -81,9 +79,6 @@ namespace osu.Game.Tests.Visual.Online expectedActions: new[] { LinkAction.External, LinkAction.OpenWiki }); // note that there's 0 links here (they get removed if a channel is not found) addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); - addMessageWithChecks("I am important!", isAction: false, isImportant: true); - addMessageWithChecks("feels important", isAction: true, isImportant: true); - addMessageWithChecks("likes to post this [https://dev.ppy.sh/home link].", isAction: true, isImportant: true, expectedActions: LinkAction.External); addMessageWithChecks("Join my multiplayer game osump://12346.", expectedActions: LinkAction.JoinMultiplayerMatch); addMessageWithChecks("Join my multiplayer gameosump://12346.", expectedActions: LinkAction.JoinMultiplayerMatch); addMessageWithChecks("Join my [multiplayer game](osump://12346).", expectedActions: LinkAction.JoinMultiplayerMatch); @@ -95,6 +90,16 @@ namespace osu.Game.Tests.Visual.Online addMessageWithChecks("Hello world\uD83D\uDE12(<--This is an emoji). There are more:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20"); } + [TestCase("is now listening to [https://dev.ppy.sh/s/93523 IMAGE -MATERIAL- ]", true, false, LinkAction.OpenBeatmapSet)] + [TestCase("is now playing [https://dev.ppy.sh/b/252238 IMAGE -MATERIAL- ]", true, false, LinkAction.OpenBeatmap)] + [TestCase("I am important!", false, true)] + [TestCase("feels important", true, true)] + [TestCase("likes to post this [https://dev.ppy.sh/home link].", true, true, LinkAction.External)] + public void TestActionAndImportantLinks(string text, bool isAction, bool isImportant, params LinkAction[] expectedActions) + { + addMessageWithChecks(text, isAction, isImportant, expectedActions); + } + private void addMessageWithChecks(string text, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) { ChatLine newLine = null; From 0520ac265fa57bbbe7f07dd9e63a966f332414d8 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 30 Nov 2023 23:34:43 +0100 Subject: [PATCH 0296/1118] Move to `TestCase`-based test --- .../Visual/Online/TestSceneChatLink.cs | 59 +++++++++---------- 1 file changed, 28 insertions(+), 31 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 52eebeb9ce..ae94d1887e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -56,38 +56,35 @@ namespace osu.Game.Tests.Visual.Online textContainer.Clear(); }); - [Test] - public void TestLinksGeneral() + [TestCase("test!")] + [TestCase("dev.ppy.sh!")] + [TestCase("https://dev.ppy.sh!", LinkAction.External)] + [TestCase("http://dev.ppy.sh!", LinkAction.External)] + [TestCase("forgothttps://dev.ppy.sh!", LinkAction.External)] + [TestCase("forgothttp://dev.ppy.sh!", LinkAction.External)] + [TestCase("00:12:345 (1,2) - Test?", LinkAction.OpenEditorTimestamp)] + [TestCase("Wiki link for tasty [[Performance Points]]", LinkAction.OpenWiki)] + [TestCase("(osu forums)[https://dev.ppy.sh/forum] (old link format)", LinkAction.External)] + [TestCase("[https://dev.ppy.sh/home New site] (new link format)", LinkAction.External)] + [TestCase("[osu forums](https://dev.ppy.sh/forum) (new link format 2)", LinkAction.External)] + [TestCase("[https://dev.ppy.sh/home This is only a link to the new osu webpage but this is supposed to test word wrap.]", LinkAction.External)] + [TestCase("Let's (try)[https://dev.ppy.sh/home] [https://dev.ppy.sh/b/252238 multiple links] https://dev.ppy.sh/home", LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External)] + [TestCase("[https://dev.ppy.sh/home New link format with escaped [and \\[ paired] braces]", LinkAction.External)] + [TestCase("[Markdown link format with escaped [and \\[ paired] braces](https://dev.ppy.sh/home)", LinkAction.External)] + [TestCase("(Old link format with escaped (and \\( paired) parentheses)[https://dev.ppy.sh/home] and [[also a rogue wiki link]]", LinkAction.External, LinkAction.OpenWiki)] + [TestCase("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present).")] // note that there's 0 links here (they get removed if a channel is not found) + [TestCase("Join my multiplayer game osump://12346.", LinkAction.JoinMultiplayerMatch)] + [TestCase("Join my multiplayer gameosump://12346.", LinkAction.JoinMultiplayerMatch)] + [TestCase("Join my [multiplayer game](osump://12346).", LinkAction.JoinMultiplayerMatch)] + [TestCase($"Join my [#english]({OsuGameBase.OSU_PROTOCOL}chan/#english).", LinkAction.OpenChannel)] + [TestCase($"Join my {OsuGameBase.OSU_PROTOCOL}chan/#english.", LinkAction.OpenChannel)] + [TestCase($"Join my{OsuGameBase.OSU_PROTOCOL}chan/#english.", LinkAction.OpenChannel)] + [TestCase("Join my #english or #japanese channels.", LinkAction.OpenChannel, LinkAction.OpenChannel)] + [TestCase("Join my #english or #nonexistent #hashtag channels.", LinkAction.OpenChannel)] + [TestCase("Hello world\uD83D\uDE12(<--This is an emoji). There are more:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20")] + public void TestLinksGeneral(string text, params LinkAction[] actions) { - addMessageWithChecks("test!"); - addMessageWithChecks("dev.ppy.sh!"); - addMessageWithChecks("https://dev.ppy.sh!", expectedActions: LinkAction.External); - addMessageWithChecks("http://dev.ppy.sh!", expectedActions: LinkAction.External); - addMessageWithChecks("forgothttps://dev.ppy.sh!", expectedActions: LinkAction.External); - addMessageWithChecks("forgothttp://dev.ppy.sh!", expectedActions: LinkAction.External); - addMessageWithChecks("00:12:345 (1,2) - Test?", expectedActions: LinkAction.OpenEditorTimestamp); - addMessageWithChecks("Wiki link for tasty [[Performance Points]]", expectedActions: LinkAction.OpenWiki); - addMessageWithChecks("(osu forums)[https://dev.ppy.sh/forum] (old link format)", expectedActions: LinkAction.External); - addMessageWithChecks("[https://dev.ppy.sh/home New site] (new link format)", expectedActions: LinkAction.External); - addMessageWithChecks("[osu forums](https://dev.ppy.sh/forum) (new link format 2)", expectedActions: LinkAction.External); - addMessageWithChecks("[https://dev.ppy.sh/home This is only a link to the new osu webpage but this is supposed to test word wrap.]", expectedActions: LinkAction.External); - addMessageWithChecks("Let's (try)[https://dev.ppy.sh/home] [https://dev.ppy.sh/b/252238 multiple links] https://dev.ppy.sh/home", - expectedActions: new[] { LinkAction.External, LinkAction.OpenBeatmap, LinkAction.External }); - addMessageWithChecks("[https://dev.ppy.sh/home New link format with escaped [and \\[ paired] braces]", expectedActions: LinkAction.External); - addMessageWithChecks("[Markdown link format with escaped [and \\[ paired] braces](https://dev.ppy.sh/home)", expectedActions: LinkAction.External); - addMessageWithChecks("(Old link format with escaped (and \\( paired) parentheses)[https://dev.ppy.sh/home] and [[also a rogue wiki link]]", - expectedActions: new[] { LinkAction.External, LinkAction.OpenWiki }); - // note that there's 0 links here (they get removed if a channel is not found) - addMessageWithChecks("#lobby or #osu would be blue (and work) in the ChatDisplay test (when a proper ChatOverlay is present)."); - addMessageWithChecks("Join my multiplayer game osump://12346.", expectedActions: LinkAction.JoinMultiplayerMatch); - addMessageWithChecks("Join my multiplayer gameosump://12346.", expectedActions: LinkAction.JoinMultiplayerMatch); - addMessageWithChecks("Join my [multiplayer game](osump://12346).", expectedActions: LinkAction.JoinMultiplayerMatch); - addMessageWithChecks($"Join my [#english]({OsuGameBase.OSU_PROTOCOL}chan/#english).", expectedActions: LinkAction.OpenChannel); - addMessageWithChecks($"Join my {OsuGameBase.OSU_PROTOCOL}chan/#english.", expectedActions: LinkAction.OpenChannel); - addMessageWithChecks($"Join my{OsuGameBase.OSU_PROTOCOL}chan/#english.", expectedActions: LinkAction.OpenChannel); - addMessageWithChecks("Join my #english or #japanese channels.", expectedActions: new[] { LinkAction.OpenChannel, LinkAction.OpenChannel }); - addMessageWithChecks("Join my #english or #nonexistent #hashtag channels.", expectedActions: LinkAction.OpenChannel); - addMessageWithChecks("Hello world\uD83D\uDE12(<--This is an emoji). There are more:\uD83D\uDE10\uD83D\uDE00,\uD83D\uDE20"); + addMessageWithChecks(text, expectedActions: actions); } [TestCase("is now listening to [https://dev.ppy.sh/s/93523 IMAGE -MATERIAL- ]", true, false, LinkAction.OpenBeatmapSet)] From 7b1ccb38cb8070cdcf576b4e14c9437589f74867 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 30 Nov 2023 23:35:10 +0100 Subject: [PATCH 0297/1118] Remove redundant string interpolation --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index ae94d1887e..f4dd4289c3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -107,8 +107,8 @@ namespace osu.Game.Tests.Visual.Online textContainer.Add(newLine); }); - AddAssert($"msg has the right action", () => newLine.Message.Links.Select(l => l.Action), () => Is.EqualTo(expectedActions)); - //AddAssert($"msg is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); + AddAssert("msg has the right action", () => newLine.Message.Links.Select(l => l.Action), () => Is.EqualTo(expectedActions)); + //AddAssert("msg is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); AddAssert($"msg shows {expectedActions.Length} link(s)", isShowingLinks); //bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); From 21fedff54fbc675947022b61138ca49b90e49f21 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Fri, 1 Dec 2023 00:29:37 +0100 Subject: [PATCH 0298/1118] Check number of links shown --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index f4dd4289c3..5947c01b96 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -123,7 +123,8 @@ namespace osu.Game.Tests.Visual.Online var linkSprites = linkCompilers.SelectMany(comp => ((DrawableLinkCompiler)comp).Parts); return linkSprites.All(d => d.Colour == linkColour) - && newLine.DrawableContentFlow.Except(linkSprites.Concat(linkCompilers)).All(d => d.Colour == textColour); + && newLine.DrawableContentFlow.Except(linkSprites.Concat(linkCompilers)).All(d => d.Colour == textColour) + && linkCompilers.Count == expectedActions.Length; } } From fba6349c6528793328d3125222b0328f253764fa Mon Sep 17 00:00:00 2001 From: Susko3 Date: Fri, 1 Dec 2023 00:36:40 +0100 Subject: [PATCH 0299/1118] Remove unused properties --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 5947c01b96..b9a65f734f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -167,21 +167,12 @@ namespace osu.Game.Tests.Visual.Online { private static long messageCounter; - internal static readonly APIUser TEST_SENDER_BACKGROUND = new APIUser - { - Username = @"i-am-important", - Id = 42, - Colour = "#250cc9", - }; - internal static readonly APIUser TEST_SENDER = new APIUser { Username = @"Somebody", Id = 1, }; - public new DateTimeOffset Timestamp = DateTimeOffset.Now; - public DummyMessage(string text, bool isAction = false, bool isImportant = false, int number = 0) : base(messageCounter++) { From 1a33bfbb3a8d0b298651844c4b9ea4bbff141629 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Fri, 1 Dec 2023 00:37:16 +0100 Subject: [PATCH 0300/1118] Enable NRT --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index b9a65f734f..8d4ff82303 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Linq; using NUnit.Framework; @@ -99,7 +97,7 @@ namespace osu.Game.Tests.Visual.Online private void addMessageWithChecks(string text, bool isAction = false, bool isImportant = false, params LinkAction[] expectedActions) { - ChatLine newLine = null; + ChatLine newLine = null!; AddStep("add message", () => { @@ -138,7 +136,7 @@ namespace osu.Game.Tests.Visual.Online addEchoWithWait("[https://dev.ppy.sh/forum let's try multiple words too!]"); addEchoWithWait("(long loading times! clickable while loading?)[https://dev.ppy.sh/home]", null, 5000); - void addEchoWithWait(string text, string completeText = null, double delay = 250) + void addEchoWithWait(string text, string? completeText = null, double delay = 250) { int index = messageIndex++; From 7f9ae55f5eab245e2051d0a85744031289424ad4 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Fri, 1 Dec 2023 00:45:35 +0100 Subject: [PATCH 0301/1118] Add passing tests --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 8d4ff82303..2dde1447e4 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -60,7 +60,9 @@ namespace osu.Game.Tests.Visual.Online [TestCase("http://dev.ppy.sh!", LinkAction.External)] [TestCase("forgothttps://dev.ppy.sh!", LinkAction.External)] [TestCase("forgothttp://dev.ppy.sh!", LinkAction.External)] + [TestCase("00:12:345 - Test?", LinkAction.OpenEditorTimestamp)] [TestCase("00:12:345 (1,2) - Test?", LinkAction.OpenEditorTimestamp)] + [TestCase($"{OsuGameBase.OSU_PROTOCOL}edit/00:12:345 - Test?", LinkAction.OpenEditorTimestamp)] [TestCase("Wiki link for tasty [[Performance Points]]", LinkAction.OpenWiki)] [TestCase("(osu forums)[https://dev.ppy.sh/forum] (old link format)", LinkAction.External)] [TestCase("[https://dev.ppy.sh/home New site] (new link format)", LinkAction.External)] From c395ae2460fbbc7e8329060732fa132f1df7ca01 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Fri, 1 Dec 2023 00:49:21 +0100 Subject: [PATCH 0302/1118] Add failing tests They throws `ArgumentOutOfRangeException` on the first drawable Update() --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 2dde1447e4..e77ff5c1cd 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -63,6 +63,8 @@ namespace osu.Game.Tests.Visual.Online [TestCase("00:12:345 - Test?", LinkAction.OpenEditorTimestamp)] [TestCase("00:12:345 (1,2) - Test?", LinkAction.OpenEditorTimestamp)] [TestCase($"{OsuGameBase.OSU_PROTOCOL}edit/00:12:345 - Test?", LinkAction.OpenEditorTimestamp)] + [TestCase($"{OsuGameBase.OSU_PROTOCOL}edit/00:12:345 (1,2) - Test?", LinkAction.OpenEditorTimestamp)] + [TestCase($"{OsuGameBase.OSU_PROTOCOL}00:12:345 - not an editor timestamp", LinkAction.External)] [TestCase("Wiki link for tasty [[Performance Points]]", LinkAction.OpenWiki)] [TestCase("(osu forums)[https://dev.ppy.sh/forum] (old link format)", LinkAction.External)] [TestCase("[https://dev.ppy.sh/home New site] (new link format)", LinkAction.External)] From 152c7e513ea98e0e25f3e3461c81307b6bd61376 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Thu, 30 Nov 2023 22:59:02 +0100 Subject: [PATCH 0303/1118] Ignore overlapping links instead of crashing --- osu.Game/Graphics/Containers/LinkFlowContainer.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Graphics/Containers/LinkFlowContainer.cs b/osu.Game/Graphics/Containers/LinkFlowContainer.cs index 40e883f8ac..aa72996fff 100644 --- a/osu.Game/Graphics/Containers/LinkFlowContainer.cs +++ b/osu.Game/Graphics/Containers/LinkFlowContainer.cs @@ -12,6 +12,7 @@ using System.Collections.Generic; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; +using osu.Framework.Logging; using osu.Framework.Platform; using osu.Game.Online; using osu.Game.Users; @@ -47,9 +48,16 @@ namespace osu.Game.Graphics.Containers foreach (var link in links) { + string displayText = text.Substring(link.Index, link.Length); + + if (previousLinkEnd > link.Index) + { + Logger.Log($@"Link ""{link.Url}"" with text ""{displayText}"" overlaps previous link, ignoring."); + continue; + } + AddText(text[previousLinkEnd..link.Index]); - string displayText = text.Substring(link.Index, link.Length); object linkArgument = link.Argument; string tooltip = displayText == link.Url ? null : link.Url; From 30bdd2d4c0eaa6922ced5f44c589df93d50c9518 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Fri, 1 Dec 2023 01:07:23 +0100 Subject: [PATCH 0304/1118] Extract `Overlaps()` logic to accept generic index and length --- osu.Game/Online/Chat/MessageFormatter.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 9a194dba47..078af667d1 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -364,7 +364,9 @@ namespace osu.Game.Online.Chat Argument = argument; } - public bool Overlaps(Link otherLink) => Index < otherLink.Index + otherLink.Length && otherLink.Index < Index + Length; + public bool Overlaps(Link otherLink) => Overlaps(otherLink.Index, otherLink.Length); + + public bool Overlaps(int index, int length) => Index < index + length && index < Index + Length; public int CompareTo(Link? otherLink) => Index > otherLink?.Index ? 1 : -1; } From d3517998cff60db3d80d38a9fc71460ce1707258 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Fri, 1 Dec 2023 01:08:22 +0100 Subject: [PATCH 0305/1118] Use common `Overlaps()` logic This actually fixes the problem and makes the tests pass --- osu.Game/Online/Chat/MessageFormatter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index 078af667d1..c5256c3c74 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -85,8 +85,8 @@ namespace osu.Game.Online.Chat if (escapeChars != null) displayText = escapeChars.Aggregate(displayText, (current, c) => current.Replace($"\\{c}", c.ToString())); - // Check for encapsulated links - if (result.Links.Find(l => (l.Index <= index && l.Index + l.Length >= index + m.Length) || (index <= l.Index && index + m.Length >= l.Index + l.Length)) == null) + // Check for overlapping links + if (!result.Links.Exists(l => l.Overlaps(index, m.Length))) { result.Text = result.Text.Remove(index, m.Length).Insert(index, displayText); From 894c31753b1c1737ef2dd7ecdf7440d9ef739cee Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Dec 2023 15:31:06 +0900 Subject: [PATCH 0306/1118] Add initial support for aborting multiplayer games --- .../Multiplayer/IMultiplayerRoomServer.cs | 5 +++ .../Online/Multiplayer/MultiplayerClient.cs | 2 ++ .../Multiplayer/OnlineMultiplayerClient.cs | 10 ++++++ .../Multiplayer/Match/ConfirmAbortDialog.cs | 33 +++++++++++++++++++ .../Multiplayer/Match/MatchStartControl.cs | 27 +++++++++++++-- .../Match/MultiplayerReadyButton.cs | 31 ++++++++++++----- .../Multiplayer/TestMultiplayerClient.cs | 6 ++++ 7 files changed, 104 insertions(+), 10 deletions(-) create mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index b7a608581c..15a8b42457 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -82,6 +82,11 @@ namespace osu.Game.Online.Multiplayer /// Task AbortGameplay(); + /// + /// Real. + /// + Task AbortGameplayReal(); + /// /// Adds an item to the playlist. /// diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 79f46c2095..140380d679 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -374,6 +374,8 @@ namespace osu.Game.Online.Multiplayer public abstract Task AbortGameplay(); + public abstract Task AbortGameplayReal(); + public abstract Task AddPlaylistItem(MultiplayerPlaylistItem item); public abstract Task EditPlaylistItem(MultiplayerPlaylistItem item); diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index e400132693..47f4205dfd 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -226,6 +226,16 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.AbortGameplay)); } + public override Task AbortGameplayReal() + { + if (!IsConnected.Value) + return Task.CompletedTask; + + Debug.Assert(connection != null); + + return connection.InvokeAsync(nameof(IMultiplayerServer.AbortGameplayReal)); + } + public override Task AddPlaylistItem(MultiplayerPlaylistItem item) { if (!IsConnected.Value) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs new file mode 100644 index 0000000000..8aca96a918 --- /dev/null +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using osu.Framework.Graphics.Sprites; +using osu.Game.Overlays.Dialog; + +namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match +{ + public partial class ConfirmAbortDialog : PopupDialog + { + public ConfirmAbortDialog(Action onConfirm, Action onCancel) + { + HeaderText = "Are you sure you want to go abort the match?"; + + Icon = FontAwesome.Solid.ExclamationTriangle; + + Buttons = new PopupDialogButton[] + { + new PopupDialogDangerousButton + { + Text = @"Yes", + Action = onConfirm + }, + new PopupDialogCancelButton + { + Text = @"No I didn't mean to", + Action = onCancel + }, + }; + } + } +} diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index 44e18dd2bb..d44878f7c3 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -16,6 +16,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Threading; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.Countdown; +using osu.Game.Overlays; using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match @@ -28,6 +29,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match [CanBeNull] private IDisposable clickOperation; + [Resolved(canBeNull: true)] + private IDialogOverlay dialogOverlay { get; set; } + private Sample sampleReady; private Sample sampleReadyAll; private Sample sampleUnready; @@ -109,8 +113,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Debug.Assert(clickOperation == null); clickOperation = ongoingOperationTracker.BeginOperation(); - if (isReady() && Client.IsHost && !Room.ActiveCountdowns.Any(c => c is MatchStartCountdown)) - startMatch(); + if (Client.IsHost) + { + if (Room.State == MultiplayerRoomState.Open) + { + if (isReady() && !Room.ActiveCountdowns.Any(c => c is MatchStartCountdown)) + startMatch(); + else + toggleReady(); + } + else + { + if (dialogOverlay == null) + abortMatch(); + else + dialogOverlay.Push(new ConfirmAbortDialog(abortMatch, endOperation)); + } + } else toggleReady(); @@ -128,6 +147,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match // gameplay was not started due to an exception; unblock button. endOperation(); }); + + void abortMatch() => Client.AbortGameplayReal().FireAndForget(endOperation, _ => endOperation()); } private void startCountdown(TimeSpan duration) @@ -198,6 +219,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match if (localUser?.State == MultiplayerUserState.Spectating) readyButton.Enabled.Value &= Client.IsHost && newCountReady > 0 && !Room.ActiveCountdowns.Any(c => c is MatchStartCountdown); + readyButton.Enabled.Value = true; + if (newCountReady == countReady) return; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 1be573bdb8..8a9d027469 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -158,7 +158,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match Text = room.Host?.Equals(localUser) == true ? $"Start match {countText}" : $"Waiting for host... {countText}"; + break; + case MultiplayerUserState.Idle: + if (room.State == MultiplayerRoomState.Open || room.Host?.Equals(localUser) != true) + { + Text = "Ready"; + break; + } + + Text = "Abort!"; break; } } @@ -204,17 +213,23 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match setYellow(); break; + + case MultiplayerUserState.Idle: + if (room.State == MultiplayerRoomState.Open || room.Host?.Equals(localUser) != true) + { + setGreen(); + break; + } + + setRed(); + break; } - void setYellow() - { - BackgroundColour = colours.YellowDark; - } + void setYellow() => BackgroundColour = colours.YellowDark; - void setGreen() - { - BackgroundColour = colours.Green; - } + void setGreen() => BackgroundColour = colours.Green; + + void setRed() => BackgroundColour = colours.Red; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 577104db45..a73c3a72a2 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -396,6 +396,12 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } + public override Task AbortGameplayReal() + { + // Todo: + return Task.CompletedTask; + } + public async Task AddUserPlaylistItem(int userId, MultiplayerPlaylistItem item) { Debug.Assert(ServerRoom != null); From a94180c8c6cde22814da67de2ff0e78be9285609 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Dec 2023 18:26:59 +0900 Subject: [PATCH 0307/1118] Rename LoadAborted -> GameplayAborted, AbortGameplayReal -> AbortMatch --- .../Online/Multiplayer/GameplayAbortReason.cs | 11 +++++++++++ .../Online/Multiplayer/IMultiplayerClient.cs | 11 ++++++----- .../Online/Multiplayer/IMultiplayerRoomServer.cs | 10 +++++----- osu.Game/Online/Multiplayer/MultiplayerClient.cs | 10 +++++----- .../Multiplayer/OnlineMultiplayerClient.cs | 6 +++--- .../Multiplayer/Match/MatchStartControl.cs | 2 +- .../OnlinePlay/Multiplayer/Multiplayer.cs | 16 +++++++++++++--- .../Visual/Multiplayer/TestMultiplayerClient.cs | 2 +- 8 files changed, 45 insertions(+), 23 deletions(-) create mode 100644 osu.Game/Online/Multiplayer/GameplayAbortReason.cs diff --git a/osu.Game/Online/Multiplayer/GameplayAbortReason.cs b/osu.Game/Online/Multiplayer/GameplayAbortReason.cs new file mode 100644 index 0000000000..15151ea68b --- /dev/null +++ b/osu.Game/Online/Multiplayer/GameplayAbortReason.cs @@ -0,0 +1,11 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +namespace osu.Game.Online.Multiplayer +{ + public enum GameplayAbortReason + { + LoadTookTooLong, + HostAbortedTheMatch + } +} diff --git a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs index a5fa49a94b..0452d8b79c 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerClient.cs @@ -107,17 +107,18 @@ namespace osu.Game.Online.Multiplayer /// Task LoadRequested(); - /// - /// Signals that loading of gameplay is to be aborted. - /// - Task LoadAborted(); - /// /// Signals that gameplay has started. /// All users in the or states should begin gameplay as soon as possible. /// Task GameplayStarted(); + /// + /// Signals that gameplay has been aborted. + /// + /// The reason why gameplay was aborted. + Task GameplayAborted(GameplayAbortReason reason); + /// /// Signals that the match has ended, all players have finished and results are ready to be displayed. /// diff --git a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs index 15a8b42457..55f00b447f 100644 --- a/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs +++ b/osu.Game/Online/Multiplayer/IMultiplayerRoomServer.cs @@ -77,16 +77,16 @@ namespace osu.Game.Online.Multiplayer /// If an attempt to start the game occurs when the game's (or users') state disallows it. Task StartMatch(); + /// + /// As the host of a room, aborts an on-going match. + /// + Task AbortMatch(); + /// /// Aborts an ongoing gameplay load. /// Task AbortGameplay(); - /// - /// Real. - /// - Task AbortGameplayReal(); - /// /// Adds an item to the playlist. /// diff --git a/osu.Game/Online/Multiplayer/MultiplayerClient.cs b/osu.Game/Online/Multiplayer/MultiplayerClient.cs index 140380d679..bbf0e3697a 100644 --- a/osu.Game/Online/Multiplayer/MultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/MultiplayerClient.cs @@ -73,9 +73,9 @@ namespace osu.Game.Online.Multiplayer public virtual event Action? LoadRequested; /// - /// Invoked when the multiplayer server requests loading of play to be aborted. + /// Invoked when the multiplayer server requests gameplay to be aborted. /// - public event Action? LoadAborted; + public event Action? GameplayAborted; /// /// Invoked when the multiplayer server requests gameplay to be started. @@ -374,7 +374,7 @@ namespace osu.Game.Online.Multiplayer public abstract Task AbortGameplay(); - public abstract Task AbortGameplayReal(); + public abstract Task AbortMatch(); public abstract Task AddPlaylistItem(MultiplayerPlaylistItem item); @@ -684,14 +684,14 @@ namespace osu.Game.Online.Multiplayer return Task.CompletedTask; } - Task IMultiplayerClient.LoadAborted() + Task IMultiplayerClient.GameplayAborted(GameplayAbortReason reason) { Scheduler.Add(() => { if (Room == null) return; - LoadAborted?.Invoke(); + GameplayAborted?.Invoke(reason); }, false); return Task.CompletedTask; diff --git a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs index 47f4205dfd..40436d730e 100644 --- a/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs +++ b/osu.Game/Online/Multiplayer/OnlineMultiplayerClient.cs @@ -58,7 +58,7 @@ namespace osu.Game.Online.Multiplayer connection.On(nameof(IMultiplayerClient.UserStateChanged), ((IMultiplayerClient)this).UserStateChanged); connection.On(nameof(IMultiplayerClient.LoadRequested), ((IMultiplayerClient)this).LoadRequested); connection.On(nameof(IMultiplayerClient.GameplayStarted), ((IMultiplayerClient)this).GameplayStarted); - connection.On(nameof(IMultiplayerClient.LoadAborted), ((IMultiplayerClient)this).LoadAborted); + connection.On(nameof(IMultiplayerClient.GameplayAborted), ((IMultiplayerClient)this).GameplayAborted); connection.On(nameof(IMultiplayerClient.ResultsReady), ((IMultiplayerClient)this).ResultsReady); connection.On>(nameof(IMultiplayerClient.UserModsChanged), ((IMultiplayerClient)this).UserModsChanged); connection.On(nameof(IMultiplayerClient.UserBeatmapAvailabilityChanged), ((IMultiplayerClient)this).UserBeatmapAvailabilityChanged); @@ -226,14 +226,14 @@ namespace osu.Game.Online.Multiplayer return connection.InvokeAsync(nameof(IMultiplayerServer.AbortGameplay)); } - public override Task AbortGameplayReal() + public override Task AbortMatch() { if (!IsConnected.Value) return Task.CompletedTask; Debug.Assert(connection != null); - return connection.InvokeAsync(nameof(IMultiplayerServer.AbortGameplayReal)); + return connection.InvokeAsync(nameof(IMultiplayerServer.AbortMatch)); } public override Task AddPlaylistItem(MultiplayerPlaylistItem item) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index d44878f7c3..8ca5d61ab4 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -148,7 +148,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match endOperation(); }); - void abortMatch() => Client.AbortGameplayReal().FireAndForget(endOperation, _ => endOperation()); + void abortMatch() => Client.AbortMatch().FireAndForget(endOperation, _ => endOperation()); } private void startCountdown(TimeSpan duration) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs index edf5ce276a..7d27725775 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Multiplayer.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer base.LoadComplete(); client.RoomUpdated += onRoomUpdated; - client.LoadAborted += onLoadAborted; + client.GameplayAborted += onGameplayAborted; onRoomUpdated(); } @@ -39,12 +39,22 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer transitionFromResults(); } - private void onLoadAborted() + private void onGameplayAborted(GameplayAbortReason reason) { // If the server aborts gameplay for this user (due to loading too slow), exit gameplay screens. if (!this.IsCurrentScreen()) { - Logger.Log("Gameplay aborted because loading the beatmap took too long.", LoggingTarget.Runtime, LogLevel.Important); + switch (reason) + { + case GameplayAbortReason.LoadTookTooLong: + Logger.Log("Gameplay aborted because loading the beatmap took too long.", LoggingTarget.Runtime, LogLevel.Important); + break; + + case GameplayAbortReason.HostAbortedTheMatch: + Logger.Log("The host aborted the match.", LoggingTarget.Runtime, LogLevel.Important); + break; + } + this.MakeCurrent(); } } diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index a73c3a72a2..3af8f9c5db 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -396,7 +396,7 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } - public override Task AbortGameplayReal() + public override Task AbortMatch() { // Todo: return Task.CompletedTask; From 15c9416244a47e9d5fb713a99ddd9ce77b5403bd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Dec 2023 18:47:40 +0900 Subject: [PATCH 0308/1118] Rename method --- .../Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index 8ca5d61ab4..a21c85e316 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -60,7 +60,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { RelativeSizeAxes = Axes.Both, Size = Vector2.One, - Action = onReadyClick, + Action = onReadyButtonClick, }, countdownButton = new MultiplayerCountdownButton { @@ -105,7 +105,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match endOperation(); } - private void onReadyClick() + private void onReadyButtonClick() { if (Room == null) return; From 1b0fc8ca9d8740d9c61aa20dcbace2f72357cd96 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Dec 2023 19:02:27 +0900 Subject: [PATCH 0309/1118] Refactor --- .../Multiplayer/Match/ConfirmAbortDialog.cs | 2 +- .../Match/MultiplayerReadyButton.cs | 24 ++++++------------- 2 files changed, 8 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs index 8aca96a918..06f2b2c8f6 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs @@ -11,7 +11,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { public ConfirmAbortDialog(Action onConfirm, Action onCancel) { - HeaderText = "Are you sure you want to go abort the match?"; + HeaderText = "Are you sure you want to abort the match?"; Icon = FontAwesome.Solid.ExclamationTriangle; diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 8a9d027469..368e5210de 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -155,19 +155,14 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match case MultiplayerUserState.Spectating: case MultiplayerUserState.Ready: - Text = room.Host?.Equals(localUser) == true + Text = multiplayerClient.IsHost ? $"Start match {countText}" : $"Waiting for host... {countText}"; break; - case MultiplayerUserState.Idle: - if (room.State == MultiplayerRoomState.Open || room.Host?.Equals(localUser) != true) - { - Text = "Ready"; - break; - } - - Text = "Abort!"; + // Show the abort button for the host as long as gameplay is in progress. + case MultiplayerUserState when multiplayerClient.IsHost && room.State != MultiplayerRoomState.Open: + Text = "Abort the match"; break; } } @@ -207,20 +202,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match case MultiplayerUserState.Spectating: case MultiplayerUserState.Ready: - if (room?.Host?.Equals(localUser) == true && !room.ActiveCountdowns.Any(c => c is MatchStartCountdown)) + if (multiplayerClient.IsHost && !room.ActiveCountdowns.Any(c => c is MatchStartCountdown)) setGreen(); else setYellow(); break; - case MultiplayerUserState.Idle: - if (room.State == MultiplayerRoomState.Open || room.Host?.Equals(localUser) != true) - { - setGreen(); - break; - } - + // Show the abort button for the host as long as gameplay is in progress. + case MultiplayerUserState when multiplayerClient.IsHost && room.State != MultiplayerRoomState.Open: setRed(); break; } From e0eea07a3fa201cb227856213f7831524d635cef Mon Sep 17 00:00:00 2001 From: Susko3 Date: Fri, 1 Dec 2023 13:00:34 +0100 Subject: [PATCH 0310/1118] Request `READ_EXTERNAL_STORAGE` on older android versions --- osu.Android/AndroidManifest.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Android/AndroidManifest.xml b/osu.Android/AndroidManifest.xml index af102a1e4e..2fb4d1f850 100644 --- a/osu.Android/AndroidManifest.xml +++ b/osu.Android/AndroidManifest.xml @@ -5,4 +5,5 @@ + \ No newline at end of file From cdaff30aa6e69582d18d7034728c2c615cbb44fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 1 Dec 2023 13:24:51 +0100 Subject: [PATCH 0311/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 3b90b1675c..6609db3027 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 7e5c5be4ea..a6b3527466 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From d97ae8df6a916c75c1fca2c9ab80919039c9b3d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 1 Dec 2023 13:31:09 +0100 Subject: [PATCH 0312/1118] Remove commented out italic code It's a holdover from the Exo days (anyone remember those?) and hasn't been relevant for years, so why keep it. --- osu.Game.Tests/Visual/Online/TestSceneChatLink.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs index 8d4ff82303..af7cc003a5 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatLink.cs @@ -106,11 +106,8 @@ namespace osu.Game.Tests.Visual.Online }); AddAssert("msg has the right action", () => newLine.Message.Links.Select(l => l.Action), () => Is.EqualTo(expectedActions)); - //AddAssert("msg is " + (isAction ? "italic" : "not italic"), () => newLine.ContentFlow.Any() && isAction == isItalic()); AddAssert($"msg shows {expectedActions.Length} link(s)", isShowingLinks); - //bool isItalic() => newLine.ContentFlow.Where(d => d is OsuSpriteText).Cast().All(sprite => sprite.Font.Italics); - bool isShowingLinks() { bool hasBackground = !string.IsNullOrEmpty(newLine.Message.Sender.Colour); From f3530a79b18817e3ca1fd005933add63ac75f82e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 1 Dec 2023 21:34:20 +0900 Subject: [PATCH 0313/1118] Add test --- .../Multiplayer/TestSceneMatchStartControl.cs | 25 +++++++++++++++++++ .../Multiplayer/TestMultiplayerClient.cs | 6 ++--- 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 6d309078e6..f8719ba80c 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -378,6 +378,31 @@ namespace osu.Game.Tests.Visual.Multiplayer }, users); } + [Test] + public void TestAbortMatch() + { + multiplayerClient.Setup(m => m.StartMatch()) + .Callback(() => + { + multiplayerClient.Raise(m => m.LoadRequested -= null); + multiplayerClient.Object.Room!.State = MultiplayerRoomState.WaitingForLoad; + + // The local user state doesn't really matter, so let's do the same as the base implementation for these tests. + changeUserState(localUser.UserID, MultiplayerUserState.Idle); + }); + + multiplayerClient.Setup(m => m.AbortMatch()) + .Callback(() => + { + multiplayerClient.Object.Room!.State = MultiplayerRoomState.Open; + raiseRoomUpdated(); + }); + + ClickButtonWhenEnabled(); + ClickButtonWhenEnabled(); + ClickButtonWhenEnabled(); + } + private void verifyGameplayStartFlow() { checkLocalUserState(MultiplayerUserState.Ready); diff --git a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs index 3af8f9c5db..4c3deac1d7 100644 --- a/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs +++ b/osu.Game/Tests/Visual/Multiplayer/TestMultiplayerClient.cs @@ -396,10 +396,10 @@ namespace osu.Game.Tests.Visual.Multiplayer return Task.CompletedTask; } - public override Task AbortMatch() + public override async Task AbortMatch() { - // Todo: - return Task.CompletedTask; + ChangeUserState(api.LocalUser.Value.Id, MultiplayerUserState.Idle); + await ((IMultiplayerClient)this).GameplayAborted(GameplayAbortReason.HostAbortedTheMatch).ConfigureAwait(false); } public async Task AddUserPlaylistItem(int userId, MultiplayerPlaylistItem item) From abb4c943a7f36a5e8fb4ada240cfb66ed433a9e0 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Fri, 1 Dec 2023 18:35:57 +0100 Subject: [PATCH 0314/1118] Rename to more readable names --- osu.Game/Online/Chat/MessageFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Chat/MessageFormatter.cs b/osu.Game/Online/Chat/MessageFormatter.cs index c5256c3c74..f055633d64 100644 --- a/osu.Game/Online/Chat/MessageFormatter.cs +++ b/osu.Game/Online/Chat/MessageFormatter.cs @@ -366,7 +366,7 @@ namespace osu.Game.Online.Chat public bool Overlaps(Link otherLink) => Overlaps(otherLink.Index, otherLink.Length); - public bool Overlaps(int index, int length) => Index < index + length && index < Index + Length; + public bool Overlaps(int otherIndex, int otherLength) => Index < otherIndex + otherLength && otherIndex < Index + Length; public int CompareTo(Link? otherLink) => Index > otherLink?.Index ? 1 : -1; } From 230278f2c94230803f11310b592dcbc15043274c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 3 Dec 2023 01:45:15 +0900 Subject: [PATCH 0315/1118] Once again remove Mania passive HP drain --- .../ManiaHealthProcessorTest.cs | 31 +++++++++++++++++++ .../Scoring/ManiaHealthProcessor.cs | 8 +++++ 2 files changed, 39 insertions(+) create mode 100644 osu.Game.Rulesets.Mania.Tests/ManiaHealthProcessorTest.cs diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaHealthProcessorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaHealthProcessorTest.cs new file mode 100644 index 0000000000..315849f7de --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/ManiaHealthProcessorTest.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Scoring; + +namespace osu.Game.Rulesets.Mania.Tests +{ + [TestFixture] + public class ManiaHealthProcessorTest + { + [Test] + public void TestNoDrain() + { + var processor = new ManiaHealthProcessor(0); + processor.ApplyBeatmap(new ManiaBeatmap(new StageDefinition(4)) + { + HitObjects = + { + new Note { StartTime = 0 }, + new Note { StartTime = 1000 }, + } + }); + + // No matter what, mania doesn't have passive HP drain. + Assert.That(processor.DrainRate, Is.Zero); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs index 183550eb7b..34b1787a71 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs @@ -15,6 +15,14 @@ namespace osu.Game.Rulesets.Mania.Scoring { } + protected override double ComputeDrainRate() + { + // Base call is run only to compute HP recovery. + base.ComputeDrainRate(); + + return 0; + } + protected override IEnumerable EnumerateTopLevelHitObjects() => Beatmap.HitObjects; protected override IEnumerable EnumerateNestedHitObjects(HitObject hitObject) => hitObject.NestedHitObjects; From dc588e6d56a8e88eb408d75f7b7d2cb1780e5ef9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Dec 2023 04:58:17 +0300 Subject: [PATCH 0316/1118] Implement OsuModDepth --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 163 ++++++++++++++++++++++ osu.Game.Rulesets.Osu/OsuRuleset.cs | 3 +- 2 files changed, 165 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs new file mode 100644 index 0000000000..9c0e34d0c8 --- /dev/null +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -0,0 +1,163 @@ +using System; +using System.Linq; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Rulesets.Osu.UI; +using osu.Game.Rulesets.UI; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Mods +{ + public class OsuModDepth : ModWithVisibilityAdjustment, IUpdatableByPlayfield, IApplicableToDrawableRuleset + { + public override string Name => "Depth"; + public override string Acronym => "DH"; + public override IconUsage? Icon => FontAwesome.Solid.Cube; + public override ModType Type => ModType.Fun; + public override LocalisableString Description => "3D. Almost."; + public override double ScoreMultiplier => 1; + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModRepel), typeof(OsuModFreezeFrame), typeof(ModWithVisibilityAdjustment) }).ToArray(); + + private static readonly Vector3 camera_position = new Vector3(OsuPlayfield.BASE_SIZE.X * 0.5f, OsuPlayfield.BASE_SIZE.Y * 0.5f, -100); + + [SettingSource("Maximum Depth", "How far away object appear.", 0)] + public BindableFloat MaxDepth { get; } = new BindableFloat(100) + { + Precision = 10, + MinValue = 50, + MaxValue = 200 + }; + + [SettingSource("Show Approach Circles", "Whether approach circles should be visible.", 1)] + public BindableBool ShowApproachCircles { get; } = new BindableBool(true); + + protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyTransform(hitObject, state); + + protected override void ApplyNormalVisibilityState(DrawableHitObject hitObject, ArmedState state) => applyTransform(hitObject, state); + + public void ApplyToDrawableRuleset(DrawableRuleset drawableRuleset) + { + // Hide judgment displays and follow points as they won't make any sense. + // Judgements can potentially be turned on in a future where they display at a position relative to their drawable counterpart. + drawableRuleset.Playfield.DisplayJudgements.Value = false; + (drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide(); + } + + public override void ApplyToDrawableHitObject(DrawableHitObject dho) + { + base.ApplyToDrawableHitObject(dho); + + switch (dho) + { + case DrawableSliderHead: + case DrawableSliderTail: + case DrawableSliderTick: + case DrawableSliderRepeat: + return; + + case DrawableHitCircle: + case DrawableSlider: + dho.Anchor = Anchor.Centre; + break; + } + } + + private void applyTransform(DrawableHitObject drawable, ArmedState state) + { + switch (drawable) + { + case DrawableSliderHead head: + if (!ShowApproachCircles.Value) + { + var hitObject = (OsuHitObject)drawable.HitObject; + double appearTime = hitObject.StartTime - hitObject.TimePreempt; + + using (head.BeginAbsoluteSequence(appearTime)) + head.ApproachCircle.Hide(); + } + + break; + + case DrawableSliderTail: + case DrawableSliderTick: + case DrawableSliderRepeat: + return; + + case DrawableHitCircle circle: + + if (!ShowApproachCircles.Value) + { + var hitObject = (OsuHitObject)drawable.HitObject; + double appearTime = hitObject.StartTime - hitObject.TimePreempt; + + using (circle.BeginAbsoluteSequence(appearTime)) + circle.ApproachCircle.Hide(); + } + + setStartPosition(drawable); + break; + + case DrawableSlider: + setStartPosition(drawable); + break; + } + } + + private void setStartPosition(DrawableHitObject drawable) + { + var hitObject = (OsuHitObject)drawable.HitObject; + + float d = mappedDepth(MaxDepth.Value); + + using (drawable.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) + { + drawable.MoveTo(positionAtDepth(d, hitObject.Position)); + drawable.ScaleTo(new Vector2(d)); + } + } + + public void Update(Playfield playfield) + { + double time = playfield.Time.Current; + + foreach (var drawable in playfield.HitObjectContainer.AliveObjects) + { + switch (drawable) + { + case DrawableSliderHead: + case DrawableSliderTail: + case DrawableSliderTick: + case DrawableSliderRepeat: + continue; + + case DrawableHitCircle: + case DrawableSlider: + var hitObject = (OsuHitObject)drawable.HitObject; + + double appearTime = hitObject.StartTime - hitObject.TimePreempt; + double moveDuration = hitObject.TimePreempt; + float z = time > appearTime + moveDuration ? 0 : (MaxDepth.Value - (float)((time - appearTime) / moveDuration * MaxDepth.Value)); + + float d = mappedDepth(z); + drawable.Position = positionAtDepth(d, hitObject.Position); + drawable.Scale = new Vector2(d); + break; + } + } + } + + private static float mappedDepth(float depth) => 100 / (depth - camera_position.Z); + + private static Vector2 positionAtDepth(float mappedDepth, Vector2 positionAtZeroDepth) + { + return (positionAtZeroDepth - camera_position.Xy) * mappedDepth; + } + } +} diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index aaf0ab41a0..50e6a5b572 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -211,7 +211,8 @@ namespace osu.Game.Rulesets.Osu new ModAdaptiveSpeed(), new OsuModFreezeFrame(), new OsuModBubbles(), - new OsuModSynesthesia() + new OsuModSynesthesia(), + new OsuModDepth() }; case ModType.System: From cf6e50f73c5f0fabd35141602187981c86842a44 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Dec 2023 05:07:40 +0300 Subject: [PATCH 0317/1118] Add header and fix typo --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs index 9c0e34d0c8..b2d52aef42 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -1,3 +1,6 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + using System; using System.Linq; using osu.Framework.Bindables; @@ -27,7 +30,7 @@ namespace osu.Game.Rulesets.Osu.Mods private static readonly Vector3 camera_position = new Vector3(OsuPlayfield.BASE_SIZE.X * 0.5f, OsuPlayfield.BASE_SIZE.Y * 0.5f, -100); - [SettingSource("Maximum Depth", "How far away object appear.", 0)] + [SettingSource("Maximum depth", "How far away objects appear.", 0)] public BindableFloat MaxDepth { get; } = new BindableFloat(100) { Precision = 10, From 937689ee6bb4eeee18b007ea153b36e5c8e6a961 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Dec 2023 05:39:44 +0300 Subject: [PATCH 0318/1118] Add OsuModDepth as incompatable to other mods --- osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTargetPractice.cs | 3 ++- osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs | 2 +- osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs | 2 +- 10 files changed, 11 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs index f1197ce0cd..06cb9c3419 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFreezeFrame.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => "Burn the notes into your memory."; //Alters the transforms of the approach circles, breaking the effects of these mods. - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModApproachDifferent), typeof(OsuModTransform) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModApproachDifferent), typeof(OsuModTransform), typeof(OsuModDepth) }).ToArray(); public override ModType Type => ModType.Fun; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs index dd2befef4e..6dc0d5d522 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModHidden.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => @"Play with no approach circles and fading circles/sliders."; public override double ScoreMultiplier => UsesDefaultConfiguration ? 1.06 : 1; - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn), typeof(OsuModDepth) }; public const double FADE_IN_DURATION_MULTIPLIER = 0.4; public const double FADE_OUT_DURATION_MULTIPLIER = 0.3; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs index c8c4cd6a14..befee4af5a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModMagnetised.cs @@ -27,7 +27,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override LocalisableString Description => "No need to chase the circles – your cursor is a magnet!"; public override double ScoreMultiplier => 0.5; - public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel), typeof(OsuModBubbles) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModRelax), typeof(OsuModRepel), typeof(OsuModBubbles), typeof(OsuModDepth) }; [SettingSource("Attraction strength", "How strong the pull is.", 0)] public BindableFloat AttractionStrength { get; } = new BindableFloat(0.5f) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs index 6f1206382a..1df344648a 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModObjectScaleTween.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods protected virtual float EndScale => 1; - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModSpinIn), typeof(OsuModObjectScaleTween), typeof(OsuModDepth) }; protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs index 28d459cedb..91feb33931 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRepel.cs @@ -26,7 +26,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override LocalisableString Description => "Hit objects run away!"; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModBubbles) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModAutopilot), typeof(OsuModWiggle), typeof(OsuModTransform), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModBubbles), typeof(OsuModDepth) }; [SettingSource("Repulsion strength", "How strong the repulsion is.", 0)] public BindableFloat RepulsionStrength { get; } = new BindableFloat(0.5f) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs index b0533d0cfa..59a1342480 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSpinIn.cs @@ -24,7 +24,7 @@ namespace osu.Game.Rulesets.Osu.Mods // todo: this mod needs to be incompatible with "hidden" due to forcing the circle to remain opaque, // further implementation will be required for supporting that. - public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModObjectScaleTween), typeof(OsuModHidden) }; + public override Type[] IncompatibleMods => new[] { typeof(IRequiresApproachCircles), typeof(OsuModObjectScaleTween), typeof(OsuModHidden), typeof(OsuModDepth) }; private const int rotate_offset = 360; private const float rotate_starting_width = 2; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTargetPractice.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTargetPractice.cs index 77cf340b95..a5846efdfe 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTargetPractice.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTargetPractice.cs @@ -47,7 +47,8 @@ namespace osu.Game.Rulesets.Osu.Mods typeof(OsuModRandom), typeof(OsuModSpunOut), typeof(OsuModStrictTracking), - typeof(OsuModSuddenDeath) + typeof(OsuModSuddenDeath), + typeof(OsuModDepth) }).ToArray(); [SettingSource("Seed", "Use a custom seed instead of a random one", SettingControlType = typeof(SettingsNumberBox))] diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs index 25d05a88a8..9671f53bea 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTraceable.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override LocalisableString Description => "Put your faith in the approach circles..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles) }; + public override Type[] IncompatibleMods => new[] { typeof(IHidesApproachCircles), typeof(OsuModDepth) }; protected override void ApplyIncreasedVisibilityState(DrawableHitObject hitObject, ArmedState state) { diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs index 92a499e735..b6907af119 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModTransform.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override LocalisableString Description => "Everything rotates. EVERYTHING."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModWiggle), typeof(OsuModMagnetised), typeof(OsuModRepel), typeof(OsuModFreezeFrame) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModWiggle), typeof(OsuModMagnetised), typeof(OsuModRepel), typeof(OsuModFreezeFrame), typeof(OsuModDepth) }).ToArray(); private float theta; diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs index a45338d91f..d14a821541 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModWiggle.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override ModType Type => ModType.Fun; public override LocalisableString Description => "They just won't stay still..."; public override double ScoreMultiplier => 1; - public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModMagnetised), typeof(OsuModRepel) }; + public override Type[] IncompatibleMods => new[] { typeof(OsuModTransform), typeof(OsuModMagnetised), typeof(OsuModRepel), typeof(OsuModDepth) }; private const int wiggle_duration = 100; // (ms) Higher = fewer wiggles From ebcde63caa4aef0696c977c04bbd5d3e2f2ed5da Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Dec 2023 17:13:47 +0300 Subject: [PATCH 0319/1118] Don't override hitobjects anchor --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 21 +-------------------- 1 file changed, 1 insertion(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs index b2d52aef42..5ae9fc4fac 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -53,25 +53,6 @@ namespace osu.Game.Rulesets.Osu.Mods (drawableRuleset.Playfield as OsuPlayfield)?.FollowPoints.Hide(); } - public override void ApplyToDrawableHitObject(DrawableHitObject dho) - { - base.ApplyToDrawableHitObject(dho); - - switch (dho) - { - case DrawableSliderHead: - case DrawableSliderTail: - case DrawableSliderTick: - case DrawableSliderRepeat: - return; - - case DrawableHitCircle: - case DrawableSlider: - dho.Anchor = Anchor.Centre; - break; - } - } - private void applyTransform(DrawableHitObject drawable, ArmedState state) { switch (drawable) @@ -160,7 +141,7 @@ namespace osu.Game.Rulesets.Osu.Mods private static Vector2 positionAtDepth(float mappedDepth, Vector2 positionAtZeroDepth) { - return (positionAtZeroDepth - camera_position.Xy) * mappedDepth; + return (positionAtZeroDepth - camera_position.Xy) * mappedDepth + camera_position.Xy; } } } From b90000f7b70f2840d6ef683aacb6043f78add0e3 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 3 Dec 2023 17:15:52 +0300 Subject: [PATCH 0320/1118] Simplify objects depth calculation --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs index 5ae9fc4fac..f2ea8b9698 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -7,6 +7,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; @@ -126,8 +127,7 @@ namespace osu.Game.Rulesets.Osu.Mods var hitObject = (OsuHitObject)drawable.HitObject; double appearTime = hitObject.StartTime - hitObject.TimePreempt; - double moveDuration = hitObject.TimePreempt; - float z = time > appearTime + moveDuration ? 0 : (MaxDepth.Value - (float)((time - appearTime) / moveDuration * MaxDepth.Value)); + float z = Interpolation.ValueAt(Math.Clamp(time, appearTime, hitObject.StartTime), MaxDepth.Value, 0f, appearTime, hitObject.StartTime); float d = mappedDepth(z); drawable.Position = positionAtDepth(d, hitObject.Position); From 595bc9398a37596ad98760f0e32448b33741b9bd Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 20:14:00 +0100 Subject: [PATCH 0321/1118] update to new builder control point signature --- .../Sliders/SliderPlacementBlueprint.cs | 37 ++++++++----------- 1 file changed, 15 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index ac7b5e63e1..584c8fabbf 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -309,12 +309,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSliderPathFromBSplineBuilder() { - IReadOnlyList builderPoints = bSplineBuilder.ControlPoints; + IReadOnlyList> builderPoints = bSplineBuilder.ControlPoints; if (builderPoints.Count == 0) return; - int lastSegmentStart = 0; PathType? lastPathType = null; HitObject.Path.ControlPoints.Clear(); @@ -323,31 +322,25 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // Importantly, the B-Spline builder returns three Vector2s at the same location when a new segment is to be started. for (int i = 0; i < builderPoints.Count; i++) { - bool isLastPoint = i == builderPoints.Count - 1; - bool isNewSegment = i < builderPoints.Count - 2 && builderPoints[i] == builderPoints[i + 1] && builderPoints[i] == builderPoints[i + 2]; + bool isLastSegment = i == builderPoints.Count - 1; + var segment = builderPoints[i]; + int pointsInSegment = segment.Count; - if (isNewSegment || isLastPoint) - { - int pointsInSegment = i - lastSegmentStart; + // Where possible, we can use the simpler LINEAR path type. + PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3); - // Where possible, we can use the simpler LINEAR path type. - PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3); + // Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined. + if (lastPathType == pathType && lastPathType == PathType.LINEAR) + pathType = null; - // Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined. - if (lastPathType == pathType && lastPathType == PathType.LINEAR) - pathType = null; + HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[0], pathType)); + for (int j = 1; j < pointsInSegment - 1; j++) + HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[j])); - HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[lastSegmentStart], pathType)); - for (int j = lastSegmentStart + 1; j < i; j++) - HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[j])); + if (isLastSegment) + HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[pointsInSegment - 1])); - if (isLastPoint) - HitObject.Path.ControlPoints.Add(new PathControlPoint(builderPoints[i])); - - // Skip the redundant duplicated points (see isNewSegment above) which have been coalesced into a path type. - lastSegmentStart = (i += 2); - if (pathType != null) lastPathType = pathType; - } + if (pathType != null) lastPathType = pathType; } } From 4cd6efc8f750e8dddc65f23f22e94d1be38fa3bc Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 20:53:05 +0100 Subject: [PATCH 0322/1118] update default parameters --- osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs index 1974415d30..87dd8636c9 100644 --- a/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Edit { } - public BindableFloat Tolerance { get; } = new BindableFloat(1.5f) + public BindableFloat Tolerance { get; } = new BindableFloat(2f) { MinValue = 0.05f, MaxValue = 3f, @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit }; // We map internal ranges to a more standard range of values for display to the user. - private readonly BindableInt displayTolerance = new BindableInt(40) + private readonly BindableInt displayTolerance = new BindableInt(66) { MinValue = 5, MaxValue = 100 From 8873824107a2e29eb914536aec11addddb421857 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 20:53:25 +0100 Subject: [PATCH 0323/1118] fix control points being cleared --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 584c8fabbf..bc32acda96 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -311,7 +311,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { IReadOnlyList> builderPoints = bSplineBuilder.ControlPoints; - if (builderPoints.Count == 0) + if (builderPoints.Count == 0 || builderPoints[0].Count == 0) return; PathType? lastPathType = null; @@ -326,6 +326,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders var segment = builderPoints[i]; int pointsInSegment = segment.Count; + if (segment.Count == 0) + continue; + // Where possible, we can use the simpler LINEAR path type. PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3); From ba2cc0243c03e014bb360aca82ad61f17df828f6 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 21:10:01 +0100 Subject: [PATCH 0324/1118] update comment --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index bc32acda96..14de35965e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -318,8 +318,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders HitObject.Path.ControlPoints.Clear(); - // Iterate through generated points, finding each segment and adding non-inheriting path types where appropriate. - // Importantly, the B-Spline builder returns three Vector2s at the same location when a new segment is to be started. + // Iterate through generated segments and adding non-inheriting path types where appropriate. for (int i = 0; i < builderPoints.Count; i++) { bool isLastSegment = i == builderPoints.Count - 1; From 34b5264616aa784439153110eb2b93d542c1968f Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 21:13:27 +0100 Subject: [PATCH 0325/1118] fix the linear segment --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 14de35965e..f98eadb817 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -329,7 +329,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders continue; // Where possible, we can use the simpler LINEAR path type. - PathType? pathType = pointsInSegment == 1 ? PathType.LINEAR : PathType.BSpline(3); + PathType? pathType = pointsInSegment == 2 ? PathType.LINEAR : PathType.BSpline(3); // Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined. if (lastPathType == pathType && lastPathType == PathType.LINEAR) From bcf2effae9b9f4ee80e652940834533d96d0c044 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 21:22:04 +0100 Subject: [PATCH 0326/1118] Remove the Linear segment simplification because it just makes things harder to edit afterwards if it made some segment linear type when you actually intended there to be some curve --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index f98eadb817..5f1066d92d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -314,8 +314,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (builderPoints.Count == 0 || builderPoints[0].Count == 0) return; - PathType? lastPathType = null; - HitObject.Path.ControlPoints.Clear(); // Iterate through generated segments and adding non-inheriting path types where appropriate. @@ -328,21 +326,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (segment.Count == 0) continue; - // Where possible, we can use the simpler LINEAR path type. - PathType? pathType = pointsInSegment == 2 ? PathType.LINEAR : PathType.BSpline(3); - - // Linear segments can be combined, as two adjacent linear sections are computationally the same as one with the points combined. - if (lastPathType == pathType && lastPathType == PathType.LINEAR) - pathType = null; - - HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[0], pathType)); + HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[0], PathType.BSpline(3))); for (int j = 1; j < pointsInSegment - 1; j++) HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[j])); if (isLastSegment) HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[pointsInSegment - 1])); - - if (pathType != null) lastPathType = pathType; } } From ca55a7b2bfb913f0de9d733a4213650484ad3296 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 21:43:37 +0100 Subject: [PATCH 0327/1118] call builder finish before ending curve --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 5f1066d92d..4413fc4bf4 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -197,7 +197,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders base.OnDragEnd(e); if (state == SliderPlacementState.Drawing) + { + bSplineBuilder.Finish(); + updateSliderPathFromBSplineBuilder(); endCurve(); + } } protected override void OnMouseUp(MouseUpEvent e) From b3d1a9ee2e0665e4a9adc5088db33823615e9f23 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 22:03:51 +0100 Subject: [PATCH 0328/1118] Dont snap expected distance while drawing This makes it 10 billion times smoother to draw, very nice --- .../Blueprints/Sliders/SliderPlacementBlueprint.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 4413fc4bf4..c40f90bfa3 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -200,6 +200,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { bSplineBuilder.Finish(); updateSliderPathFromBSplineBuilder(); + + // Change the state so it will snap the expected distance in endCurve. + state = SliderPlacementState.Finishing; endCurve(); } } @@ -304,7 +307,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSlider() { - HitObject.Path.ExpectedDistance.Value = distanceSnapProvider?.FindSnappedDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; + if (state == SliderPlacementState.Drawing) + HitObject.Path.ExpectedDistance.Value = (float)HitObject.Path.CalculatedDistance; + else + HitObject.Path.ExpectedDistance.Value = distanceSnapProvider?.FindSnappedDistance(HitObject, (float)HitObject.Path.CalculatedDistance) ?? (float)HitObject.Path.CalculatedDistance; bodyPiece.UpdateFrom(HitObject); headCirclePiece.UpdateFrom(HitObject.HeadCircle); @@ -343,7 +349,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { Initial, ControlPoints, - Drawing + Drawing, + Finishing } } } From 060141866c4d9e60f8a0d1172318d89fdb821206 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 22:06:07 +0100 Subject: [PATCH 0329/1118] Update SliderPlacementBlueprint.cs --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index c40f90bfa3..e13a46d0cd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -331,17 +331,16 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { bool isLastSegment = i == builderPoints.Count - 1; var segment = builderPoints[i]; - int pointsInSegment = segment.Count; if (segment.Count == 0) continue; HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[0], PathType.BSpline(3))); - for (int j = 1; j < pointsInSegment - 1; j++) + for (int j = 1; j < segment.Count - 1; j++) HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[j])); if (isLastSegment) - HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[pointsInSegment - 1])); + HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[^1])); } } From d0acb7f4f9e601f10b7650a217329cfb0b041158 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 4 Dec 2023 06:08:31 +0900 Subject: [PATCH 0330/1118] Improve commenting MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs index 34b1787a71..a33eac83c2 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaHealthProcessor.cs @@ -17,7 +17,8 @@ namespace osu.Game.Rulesets.Mania.Scoring protected override double ComputeDrainRate() { - // Base call is run only to compute HP recovery. + // Base call is run only to compute HP recovery (namely, `HpMultiplierNormal`). + // This closely mirrors (broken) behaviour of stable and as such is preserved unchanged. base.ComputeDrainRate(); return 0; From 13b7f2fa42e68fe72ec0835260b6b84411d3dbbc Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sun, 3 Dec 2023 22:42:48 +0100 Subject: [PATCH 0331/1118] Fix test cases --- .../Editor/TestSceneSliderPlacementBlueprint.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index 7ac34bc6c8..f889999fe3 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -310,7 +310,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor AddStep("release left button", () => InputManager.ReleaseButton(MouseButton.Left)); assertPlaced(true); - assertLength(760, tolerance: 10); + assertLength(808, tolerance: 10); assertControlPointCount(5); assertControlPointType(0, PathType.BSpline(3)); assertControlPointType(1, null); @@ -337,9 +337,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertLength(600, tolerance: 10); assertControlPointCount(4); - assertControlPointType(0, PathType.LINEAR); - assertControlPointType(1, null); - assertControlPointType(2, null); + assertControlPointType(0, PathType.BSpline(3)); + assertControlPointType(1, PathType.BSpline(3)); + assertControlPointType(2, PathType.BSpline(3)); assertControlPointType(3, null); } From b56a78c6ecaa9f47e0a8de9b8c4d567975b3c500 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 4 Dec 2023 08:51:21 +0900 Subject: [PATCH 0332/1118] Adjust with framework changes --- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs | 2 +- osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs | 2 +- osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs | 2 +- osu.Game/Graphics/Backgrounds/Triangles.cs | 2 +- osu.Game/Graphics/Backgrounds/TrianglesV2.cs | 2 +- osu.Game/Graphics/UserInterface/BarGraph.cs | 2 +- osu.Game/Graphics/UserInterface/SegmentedGraph.cs | 2 +- osu.Game/Rulesets/Mods/ModFlashlight.cs | 2 +- osu.Game/Screens/Menu/LogoVisualisation.cs | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs index b719138d33..88769442a1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs @@ -132,7 +132,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon relativeGlowSize = Source.glowSize / Source.DrawSize.X; } - public override void Draw(IRenderer renderer) + protected override void Draw(IRenderer renderer) { base.Draw(renderer); drawGlow(renderer); diff --git a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs index d818c8baee..9838cb2c37 100644 --- a/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs +++ b/osu.Game.Rulesets.Osu/Skinning/SmokeSegment.cs @@ -231,7 +231,7 @@ namespace osu.Game.Rulesets.Osu.Skinning points.AddRange(Source.SmokePoints.Skip(firstVisiblePointIndex).Take(futurePointIndex - firstVisiblePointIndex)); } - public sealed override void Draw(IRenderer renderer) + protected sealed override void Draw(IRenderer renderer) { base.Draw(renderer); diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs index a29faac5a0..95a052dadb 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorTrail.cs @@ -258,7 +258,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private IUniformBuffer cursorTrailParameters; - public override void Draw(IRenderer renderer) + protected override void Draw(IRenderer renderer) { base.Draw(renderer); diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 0ee42c69d5..1a78c1ec5e 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -286,7 +286,7 @@ namespace osu.Game.Graphics.Backgrounds private IUniformBuffer borderDataBuffer; - public override void Draw(IRenderer renderer) + protected override void Draw(IRenderer renderer) { base.Draw(renderer); diff --git a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs index 750e96440d..a20fd78569 100644 --- a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs +++ b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs @@ -228,7 +228,7 @@ namespace osu.Game.Graphics.Backgrounds private IUniformBuffer? borderDataBuffer; - public override void Draw(IRenderer renderer) + protected override void Draw(IRenderer renderer) { base.Draw(renderer); diff --git a/osu.Game/Graphics/UserInterface/BarGraph.cs b/osu.Game/Graphics/UserInterface/BarGraph.cs index 0ac987e85b..e7592128b0 100644 --- a/osu.Game/Graphics/UserInterface/BarGraph.cs +++ b/osu.Game/Graphics/UserInterface/BarGraph.cs @@ -134,7 +134,7 @@ namespace osu.Game.Graphics.UserInterface lengths.AddRange(Source.bars.InstantaneousLengths); } - public override void Draw(IRenderer renderer) + protected override void Draw(IRenderer renderer) { base.Draw(renderer); diff --git a/osu.Game/Graphics/UserInterface/SegmentedGraph.cs b/osu.Game/Graphics/UserInterface/SegmentedGraph.cs index 91971e5af9..9f467687a4 100644 --- a/osu.Game/Graphics/UserInterface/SegmentedGraph.cs +++ b/osu.Game/Graphics/UserInterface/SegmentedGraph.cs @@ -221,7 +221,7 @@ namespace osu.Game.Graphics.UserInterface tierColours.AddRange(Source.tierColours); } - public override void Draw(IRenderer renderer) + protected override void Draw(IRenderer renderer) { base.Draw(renderer); diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 215fc877dc..95406cc9e6 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -252,7 +252,7 @@ namespace osu.Game.Rulesets.Mods private IUniformBuffer? flashlightParametersBuffer; - public override void Draw(IRenderer renderer) + protected override void Draw(IRenderer renderer) { base.Draw(renderer); diff --git a/osu.Game/Screens/Menu/LogoVisualisation.cs b/osu.Game/Screens/Menu/LogoVisualisation.cs index fa26cfab46..b722b83280 100644 --- a/osu.Game/Screens/Menu/LogoVisualisation.cs +++ b/osu.Game/Screens/Menu/LogoVisualisation.cs @@ -189,7 +189,7 @@ namespace osu.Game.Screens.Menu Source.frequencyAmplitudes.AsSpan().CopyTo(audioData); } - public override void Draw(IRenderer renderer) + protected override void Draw(IRenderer renderer) { base.Draw(renderer); From c2644a5d5e208214f383714b606c5e442bc04a28 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 4 Dec 2023 10:18:37 +0900 Subject: [PATCH 0333/1118] Correctly implement button enabled state --- .../Visual/Multiplayer/TestSceneMatchStartControl.cs | 1 + .../OnlinePlay/Multiplayer/Match/MatchStartControl.cs | 8 +++++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index f8719ba80c..c64dea3f59 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -401,6 +401,7 @@ namespace osu.Game.Tests.Visual.Multiplayer ClickButtonWhenEnabled(); ClickButtonWhenEnabled(); ClickButtonWhenEnabled(); + AddStep("check abort request received", () => multiplayerClient.Verify(m => m.AbortMatch(), Times.Once)); } private void verifyGameplayStartFlow() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index a21c85e316..e61735fe61 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -130,7 +130,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match dialogOverlay.Push(new ConfirmAbortDialog(abortMatch, endOperation)); } } - else + else if (Room.State != MultiplayerRoomState.Closed) toggleReady(); bool isReady() => Client.LocalUser?.State == MultiplayerUserState.Ready || Client.LocalUser?.State == MultiplayerUserState.Spectating; @@ -210,7 +210,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match } readyButton.Enabled.Value = countdownButton.Enabled.Value = - Room.State == MultiplayerRoomState.Open + Room.State != MultiplayerRoomState.Closed && CurrentPlaylistItem.Value?.ID == Room.Settings.PlaylistItemId && !Room.Playlist.Single(i => i.ID == Room.Settings.PlaylistItemId).Expired && !operationInProgress.Value; @@ -219,7 +219,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match if (localUser?.State == MultiplayerUserState.Spectating) readyButton.Enabled.Value &= Client.IsHost && newCountReady > 0 && !Room.ActiveCountdowns.Any(c => c is MatchStartCountdown); - readyButton.Enabled.Value = true; + // When the local user is not the host, the button should only be enabled when no match is in progress. + if (!Client.IsHost) + readyButton.Enabled.Value &= Room.State == MultiplayerRoomState.Open; if (newCountReady == countReady) return; From 9ccd33a1ecc61e684ca92b30781fb55b669a9016 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 4 Dec 2023 10:20:53 +0900 Subject: [PATCH 0334/1118] Add comments to test --- .../Visual/Multiplayer/TestSceneMatchStartControl.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index c64dea3f59..750968fc75 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -398,8 +398,13 @@ namespace osu.Game.Tests.Visual.Multiplayer raiseRoomUpdated(); }); + // Ready ClickButtonWhenEnabled(); + + // Start match ClickButtonWhenEnabled(); + + // Abort ClickButtonWhenEnabled(); AddStep("check abort request received", () => multiplayerClient.Verify(m => m.AbortMatch(), Times.Once)); } From 8587652869ea92665de36af0f1bc4a9079ab3c8a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 4 Dec 2023 11:00:11 +0900 Subject: [PATCH 0335/1118] Fix countdown button being enabled --- .../Multiplayer/TestSceneMatchStartControl.cs | 32 +++++++++++-------- .../Multiplayer/Match/MatchStartControl.cs | 3 ++ 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs index 750968fc75..2d61c26a6b 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMatchStartControl.cs @@ -381,28 +381,32 @@ namespace osu.Game.Tests.Visual.Multiplayer [Test] public void TestAbortMatch() { - multiplayerClient.Setup(m => m.StartMatch()) - .Callback(() => - { - multiplayerClient.Raise(m => m.LoadRequested -= null); - multiplayerClient.Object.Room!.State = MultiplayerRoomState.WaitingForLoad; + AddStep("setup client", () => + { + multiplayerClient.Setup(m => m.StartMatch()) + .Callback(() => + { + multiplayerClient.Raise(m => m.LoadRequested -= null); + multiplayerClient.Object.Room!.State = MultiplayerRoomState.WaitingForLoad; - // The local user state doesn't really matter, so let's do the same as the base implementation for these tests. - changeUserState(localUser.UserID, MultiplayerUserState.Idle); - }); + // The local user state doesn't really matter, so let's do the same as the base implementation for these tests. + changeUserState(localUser.UserID, MultiplayerUserState.Idle); + }); - multiplayerClient.Setup(m => m.AbortMatch()) - .Callback(() => - { - multiplayerClient.Object.Room!.State = MultiplayerRoomState.Open; - raiseRoomUpdated(); - }); + multiplayerClient.Setup(m => m.AbortMatch()) + .Callback(() => + { + multiplayerClient.Object.Room!.State = MultiplayerRoomState.Open; + raiseRoomUpdated(); + }); + }); // Ready ClickButtonWhenEnabled(); // Start match ClickButtonWhenEnabled(); + AddUntilStep("countdown button disabled", () => !this.ChildrenOfType().Single().Enabled.Value); // Abort ClickButtonWhenEnabled(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index e61735fe61..99934acaae 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -223,6 +223,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match if (!Client.IsHost) readyButton.Enabled.Value &= Room.State == MultiplayerRoomState.Open; + // At all times, the countdown button should only be enabled when no match is in progress. + countdownButton.Enabled.Value &= Room.State == MultiplayerRoomState.Open; + if (newCountReady == countReady) return; From c2a4a6d8cb0cbc8a00b98489b6496f1e843c9afc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Dec 2023 12:10:31 +0900 Subject: [PATCH 0336/1118] Add inline comment matching framework --- osu.Android/AndroidManifest.xml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Android/AndroidManifest.xml b/osu.Android/AndroidManifest.xml index 2fb4d1f850..492d48542e 100644 --- a/osu.Android/AndroidManifest.xml +++ b/osu.Android/AndroidManifest.xml @@ -5,5 +5,12 @@ + - \ No newline at end of file + From c755bcbec4167c9924d6d6ee5c980c491cc4f2dd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 4 Dec 2023 14:30:08 +0900 Subject: [PATCH 0337/1118] Add failing test --- .../CatchBeatmapConversionTest.cs | 1 + .../pixel-jump-expected-conversion.json | 34 +++++++++++++++++++ .../Resources/Testing/Beatmaps/pixel-jump.osu | 23 +++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump.osu diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index baca8166d1..a11aaa5e29 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -27,6 +27,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("hardrock-spinner", new[] { typeof(CatchModHardRock) })] [TestCase("right-bound-hr-offset", new[] { typeof(CatchModHardRock) })] [TestCase("basic-hyperdash")] + [TestCase("pixel-jump")] public new void Test(string name, params Type[] mods) => base.Test(name, mods); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump-expected-conversion.json new file mode 100644 index 0000000000..c9fbaf92a3 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump-expected-conversion.json @@ -0,0 +1,34 @@ +{ + "Mappings": [ + { + "StartTime": 23143.0, + "Objects": [ + { + "StartTime": 23143.0, + "Position": 307.0, + "HyperDash": false + }, + { + "StartTime": 23226.0, + "Position": 354.644958, + "HyperDash": false + } + ] + }, + { + "StartTime": 23310.0, + "Objects": [ + { + "StartTime": 23310.0, + "Position": 214.0, + "HyperDash": false + }, + { + "StartTime": 23393.0, + "Position": 154.841156, + "HyperDash": false + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump.osu new file mode 100644 index 0000000000..6f470e77e5 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump.osu @@ -0,0 +1,23 @@ +osu file format v14 + +[General] +StackLeniency: 0.2 +Mode: 0 + +[Difficulty] +HPDrainRate:5.5 +CircleSize:4 +OverallDifficulty:8.6 +ApproachRate:9.4 +SliderMultiplier:2 +SliderTickRate:1 + +[TimingPoints] +310,333.333333333333,4,2,1,45,1,0 +23142,-83.3333333333333,4,2,1,70,0,0 +23225,-83.3333333333333,4,2,1,5,0,0 +23309,-83.3333333333333,4,2,1,75,0,0 + +[HitObjects] +307,184,23143,2,0,P|330:160|366:150,1,59.9999981689454,2|0,0:1|0:0,0:0:0:0: +214,335,23310,2,0,L|149:324,1,59.9999981689454,10|0,0:0|0:0,0:0:0:0: \ No newline at end of file From 6f73d78bc90a2ec78f1eb3137363db40ece6af2a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 4 Dec 2023 14:32:14 +0900 Subject: [PATCH 0338/1118] Replicate integer calculations for catch hyperdash generation --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 7c81ca03d1..50e6fd9673 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -247,7 +247,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps currentObject.DistanceToHyperDash = 0; int thisDirection = nextObject.EffectiveX > currentObject.EffectiveX ? 1 : -1; - double timeToNext = nextObject.StartTime - currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable + double timeToNext = (int)nextObject.StartTime - (int)currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable double distanceToNext = Math.Abs(nextObject.EffectiveX - currentObject.EffectiveX) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); float distanceToHyper = (float)(timeToNext * Catcher.BASE_DASH_SPEED - distanceToNext); From ec5c7d7830dfac2f37a8e3d6cbc14967c8473f21 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 4 Dec 2023 09:43:09 +0300 Subject: [PATCH 0339/1118] Add deceleration and rework depth handling --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 105 ++++++++++------------ 1 file changed, 49 insertions(+), 56 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs index f2ea8b9698..5054c75e97 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -4,7 +4,6 @@ using System; using System.Linq; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Utils; @@ -30,6 +29,7 @@ namespace osu.Game.Rulesets.Osu.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModRepel), typeof(OsuModFreezeFrame), typeof(ModWithVisibilityAdjustment) }).ToArray(); private static readonly Vector3 camera_position = new Vector3(OsuPlayfield.BASE_SIZE.X * 0.5f, OsuPlayfield.BASE_SIZE.Y * 0.5f, -100); + private readonly float minDepth = depthForScale(1.5f); [SettingSource("Maximum depth", "How far away objects appear.", 0)] public BindableFloat MaxDepth { get; } = new BindableFloat(100) @@ -58,25 +58,7 @@ namespace osu.Game.Rulesets.Osu.Mods { switch (drawable) { - case DrawableSliderHead head: - if (!ShowApproachCircles.Value) - { - var hitObject = (OsuHitObject)drawable.HitObject; - double appearTime = hitObject.StartTime - hitObject.TimePreempt; - - using (head.BeginAbsoluteSequence(appearTime)) - head.ApproachCircle.Hide(); - } - - break; - - case DrawableSliderTail: - case DrawableSliderTick: - case DrawableSliderRepeat: - return; - case DrawableHitCircle circle: - if (!ShowApproachCircles.Value) { var hitObject = (OsuHitObject)drawable.HitObject; @@ -86,25 +68,7 @@ namespace osu.Game.Rulesets.Osu.Mods circle.ApproachCircle.Hide(); } - setStartPosition(drawable); break; - - case DrawableSlider: - setStartPosition(drawable); - break; - } - } - - private void setStartPosition(DrawableHitObject drawable) - { - var hitObject = (OsuHitObject)drawable.HitObject; - - float d = mappedDepth(MaxDepth.Value); - - using (drawable.BeginAbsoluteSequence(hitObject.StartTime - hitObject.TimePreempt)) - { - drawable.MoveTo(positionAtDepth(d, hitObject.Position)); - drawable.ScaleTo(new Vector2(d)); } } @@ -114,34 +78,63 @@ namespace osu.Game.Rulesets.Osu.Mods foreach (var drawable in playfield.HitObjectContainer.AliveObjects) { - switch (drawable) - { - case DrawableSliderHead: - case DrawableSliderTail: - case DrawableSliderTick: - case DrawableSliderRepeat: - continue; + if (drawable is not DrawableOsuHitObject d) + continue; + switch (d) + { case DrawableHitCircle: case DrawableSlider: - var hitObject = (OsuHitObject)drawable.HitObject; - - double appearTime = hitObject.StartTime - hitObject.TimePreempt; - float z = Interpolation.ValueAt(Math.Clamp(time, appearTime, hitObject.StartTime), MaxDepth.Value, 0f, appearTime, hitObject.StartTime); - - float d = mappedDepth(z); - drawable.Position = positionAtDepth(d, hitObject.Position); - drawable.Scale = new Vector2(d); + processObject(time, d); break; } } } - private static float mappedDepth(float depth) => 100 / (depth - camera_position.Z); - - private static Vector2 positionAtDepth(float mappedDepth, Vector2 positionAtZeroDepth) + private void processObject(double time, DrawableOsuHitObject drawable) { - return (positionAtZeroDepth - camera_position.Xy) * mappedDepth + camera_position.Xy; + var hitObject = drawable.HitObject; + + double baseSpeed = MaxDepth.Value / hitObject.TimePreempt; + double hitObjectDuration = hitObject is Slider s ? s.Duration : 0.0; + double offsetAfterStartTime = hitObjectDuration + hitObject.MaximumJudgementOffset + 500; + double slowSpeed = -minDepth / offsetAfterStartTime; + + float decelerationDistance = MaxDepth.Value * 0.2f; + double decelerationTime = (slowSpeed - baseSpeed) * 2 * decelerationDistance / (slowSpeed * slowSpeed - baseSpeed * baseSpeed); + + float z; + + if (time < hitObject.StartTime - decelerationTime) + { + double appearTime = hitObject.StartTime - hitObject.TimePreempt; + float fullDistance = decelerationDistance + (float)(baseSpeed * (hitObject.TimePreempt - decelerationTime)); + z = Interpolation.ValueAt(Math.Max(time, appearTime), fullDistance, decelerationDistance, appearTime, hitObject.StartTime - decelerationTime); + } + else if (time < hitObject.StartTime) + { + double timeOffset = time - (hitObject.StartTime - decelerationTime); + double deceleration = (slowSpeed - baseSpeed) / decelerationTime; + z = decelerationDistance - (float)(baseSpeed * timeOffset + deceleration * timeOffset * timeOffset * 0.5); + } + else + { + double endTime = hitObject.StartTime + offsetAfterStartTime; + z = Interpolation.ValueAt(Math.Min(time, endTime), 0f, minDepth, hitObject.StartTime, endTime); + } + + float scale = scaleForDepth(z); + drawable.Position = positionAtDepth(scale, hitObject.Position); + drawable.Scale = new Vector2(scale); + } + + private static float scaleForDepth(float depth) => 100 / (depth - camera_position.Z); + + private static float depthForScale(float scale) => 100 / scale + camera_position.Z; + + private static Vector2 positionAtDepth(float scale, Vector2 positionAtZeroDepth) + { + return (positionAtZeroDepth - camera_position.Xy) * scale + camera_position.Xy; } } } From bb198e0c5a96120ac3ac3139879ccc47265eac72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 09:26:23 +0100 Subject: [PATCH 0340/1118] Add test coverage for missing hyperdashes on simultaneous notes --- .../TestSceneHyperDash.cs | 20 ++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index 3c222662f5..445fa42b36 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Catch.Tests AddAssert("First note is hyperdash", () => Beatmap.Value.Beatmap.HitObjects[0] is Fruit f && f.HyperDash); - for (int i = 0; i < 9; i++) + for (int i = 0; i < 11; i++) { int count = i + 1; AddUntilStep($"wait for hyperdash #{count}", () => hyperDashCount >= count); @@ -100,12 +100,22 @@ namespace osu.Game.Rulesets.Catch.Tests }) }, 1); + createObjects(() => new Fruit { X = right_x }, count: 2, spacing: 0, spacingAfterGroup: 400); + createObjects(() => new TestJuiceStream(left_x) + { + Path = new SliderPath(new[] + { + new PathControlPoint(Vector2.Zero), + new PathControlPoint(new Vector2(0, 300)) + }) + }, count: 1, spacingAfterGroup: 150); + createObjects(() => new Fruit { X = left_x }, count: 1, spacing: 0, spacingAfterGroup: 400); + createObjects(() => new Fruit { X = right_x }, count: 2, spacing: 0); + return beatmap; - void createObjects(Func createObject, int count = 3) + void createObjects(Func createObject, int count = 3, float spacing = 140, float spacingAfterGroup = 700) { - const float spacing = 140; - for (int i = 0; i < count; i++) { var hitObject = createObject(); @@ -113,7 +123,7 @@ namespace osu.Game.Rulesets.Catch.Tests beatmap.HitObjects.Add(hitObject); } - startTime += 700; + startTime += spacingAfterGroup; } } From fcb6f40666bc351c8bd8122314978ce33ac2993e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 09:30:18 +0100 Subject: [PATCH 0341/1118] Prioritise hyperfruit over non-hyperfruit if simultaneous In case of simultaneous hyperfruit and non-hyperfruit - which happen to occur on some aspire maps - the desired behaviour is to hyperdash. This did not previously occur, due to annoying details in how `HitObjectContainer` is structured. `HitObjectContainer`'s drawable comparer determines the order of updating the objects. One could say that forcing the hyperfruit to be updated last, after normal fruit, could help; unfortunately this is complicated by the existence of juice streams and the fact that while a juice stream can be terminated by a normal fruit that is coincidental with a hyperfruit, the two are not comparable directly using the comparer in any feasible way. Therefore, apply a `Catcher`-level workaround that intends to handle this locally; in short, if a hyperdash was toggled in a given frame, it cannot be toggled off again in the same frame. This yields the desired behaviour. --- osu.Game.Rulesets.Catch/UI/Catcher.cs | 26 +++++++++++++++++++------- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/UI/Catcher.cs b/osu.Game.Rulesets.Catch/UI/Catcher.cs index 0c2c157d10..147850a9b7 100644 --- a/osu.Game.Rulesets.Catch/UI/Catcher.cs +++ b/osu.Game.Rulesets.Catch/UI/Catcher.cs @@ -126,6 +126,7 @@ namespace osu.Game.Rulesets.Catch.UI private Color4 hyperDashColour = DEFAULT_HYPER_DASH_COLOUR; + private double? lastHyperDashStartTime; private double hyperDashModifier = 1; private int hyperDashDirection; private float hyperDashTargetPosition; @@ -233,16 +234,23 @@ namespace osu.Game.Rulesets.Catch.UI // droplet doesn't affect the catcher state if (hitObject is TinyDroplet) return; - if (result.IsHit && hitObject.HyperDashTarget is CatchHitObject target) + // if a hyper fruit was already handled this frame, just go where it says to go. + // this special-cases some aspire maps that have doubled-up objects (one hyper, one not) at the same time instant. + // handling this "properly" elsewhere is impossible as there is no feasible way to ensure + // that the hyperfruit gets judged second (especially if it coincides with a last fruit in a juice stream). + if (lastHyperDashStartTime != Time.Current) { - double timeDifference = target.StartTime - hitObject.StartTime; - double positionDifference = target.EffectiveX - X; - double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0); + if (result.IsHit && hitObject.HyperDashTarget is CatchHitObject target) + { + double timeDifference = target.StartTime - hitObject.StartTime; + double positionDifference = target.EffectiveX - X; + double velocity = positionDifference / Math.Max(1.0, timeDifference - 1000.0 / 60.0); - SetHyperDashState(Math.Abs(velocity) / BASE_DASH_SPEED, target.EffectiveX); + SetHyperDashState(Math.Abs(velocity) / BASE_DASH_SPEED, target.EffectiveX); + } + else + SetHyperDashState(); } - else - SetHyperDashState(); if (result.IsHit) CurrentState = hitObject.Kiai ? CatcherAnimationState.Kiai : CatcherAnimationState.Idle; @@ -292,6 +300,8 @@ namespace osu.Game.Rulesets.Catch.UI if (wasHyperDashing) runHyperDashStateTransition(false); + + lastHyperDashStartTime = null; } else { @@ -301,6 +311,8 @@ namespace osu.Game.Rulesets.Catch.UI if (!wasHyperDashing) runHyperDashStateTransition(true); + + lastHyperDashStartTime = Time.Current; } } From 7deff70b4ae1710c0d66495d52aafbb6a8b3cd37 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 13:34:36 +0100 Subject: [PATCH 0342/1118] Extract beatmap import steps from gameplay scene switch helper --- .../TestSceneSkinEditorNavigation.cs | 22 ++++++++++++++++--- 1 file changed, 19 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index c17a9ddf5f..28855830e1 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -38,6 +38,9 @@ namespace osu.Game.Tests.Visual.Navigation advanceToSongSelect(); openSkinEditor(); + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + switchToGameplayScene(); BarHitErrorMeter hitErrorMeter = null; @@ -98,6 +101,10 @@ namespace osu.Game.Tests.Visual.Navigation { advanceToSongSelect(); openSkinEditor(); + + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + switchToGameplayScene(); AddUntilStep("wait for components", () => skinEditor.ChildrenOfType().Any()); @@ -162,6 +169,9 @@ namespace osu.Game.Tests.Visual.Navigation openSkinEditor(); AddStep("select DT", () => Game.SelectedMods.Value = new Mod[] { new OsuModDoubleTime() }); + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + switchToGameplayScene(); AddAssert("DT still selected", () => ((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Single() is OsuModDoubleTime); @@ -174,6 +184,9 @@ namespace osu.Game.Tests.Visual.Navigation openSkinEditor(); AddStep("select relax and spun out", () => Game.SelectedMods.Value = new Mod[] { new OsuModRelax(), new OsuModSpunOut() }); + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + switchToGameplayScene(); AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); @@ -186,6 +199,9 @@ namespace osu.Game.Tests.Visual.Navigation openSkinEditor(); AddStep("select autoplay", () => Game.SelectedMods.Value = new Mod[] { new OsuModAutoplay() }); + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + switchToGameplayScene(); AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); @@ -198,6 +214,9 @@ namespace osu.Game.Tests.Visual.Navigation openSkinEditor(); AddStep("select cinema", () => Game.SelectedMods.Value = new Mod[] { new OsuModCinema() }); + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + switchToGameplayScene(); AddAssert("no mod selected", () => !((Player)Game.ScreenStack.CurrentScreen).Mods.Value.Any()); @@ -266,9 +285,6 @@ namespace osu.Game.Tests.Visual.Navigation private void switchToGameplayScene() { - AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); - AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); - AddStep("Click gameplay scene button", () => { InputManager.MoveMouseTo(skinEditor.ChildrenOfType().First(b => b.Text.ToString() == "Gameplay")); From d3e94cd5bfaa328032c12c44cb32d08b9b8f8728 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 13:50:52 +0100 Subject: [PATCH 0343/1118] Add test coverage for crashing on empty beatmap --- .../Navigation/TestSceneSkinEditorNavigation.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index 28855830e1..62b53f9bac 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -12,9 +12,11 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Framework.Threading; +using osu.Game.Online.API; using osu.Game.Overlays.Settings; using osu.Game.Overlays.SkinEditor; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Play; @@ -259,6 +261,19 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("editor sidebars not empty", () => skinEditor.ChildrenOfType().SelectMany(sidebar => sidebar.Children).Count(), () => Is.GreaterThan(0)); } + [Test] + public void TestOpenSkinEditorGameplaySceneOnBeatmapWithNoObjects() + { + AddStep("set dummy beatmap", () => Game.Beatmap.SetDefault()); + advanceToSongSelect(); + + AddStep("create empty beatmap", () => Game.BeatmapManager.CreateNew(new OsuRuleset().RulesetInfo, new GuestUser())); + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + + openSkinEditor(); + switchToGameplayScene(); + } + private void advanceToSongSelect() { PushAndConfirm(() => songSelect = new TestPlaySongSelect()); From 7c041df0f1875a8274597b626cb465f05143304a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 13:52:41 +0100 Subject: [PATCH 0344/1118] Add test coverage for crashing on dummy beatmap --- .../Visual/Navigation/TestSceneSkinEditorNavigation.cs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index 62b53f9bac..4424b8cef6 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -274,6 +274,15 @@ namespace osu.Game.Tests.Visual.Navigation switchToGameplayScene(); } + [Test] + public void TestOpenSkinEditorGameplaySceneWhenDummyBeatmapActive() + { + AddStep("set dummy beatmap", () => Game.Beatmap.SetDefault()); + + openSkinEditor(); + switchToGameplayScene(); + } + private void advanceToSongSelect() { PushAndConfirm(() => songSelect = new TestPlaySongSelect()); From 43312619e36c6d57a0c904adfd7a4d12890cf867 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 13:58:07 +0100 Subject: [PATCH 0345/1118] Add test coverage for crashing on wrong ruleset --- .../Navigation/TestSceneSkinEditorNavigation.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index 4424b8cef6..74e6ba1566 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -13,6 +13,7 @@ using osu.Framework.Screens; using osu.Framework.Testing; using osu.Framework.Threading; using osu.Game.Online.API; +using osu.Game.Beatmaps; using osu.Game.Overlays.Settings; using osu.Game.Overlays.SkinEditor; using osu.Game.Rulesets.Mods; @@ -283,6 +284,22 @@ namespace osu.Game.Tests.Visual.Navigation switchToGameplayScene(); } + [Test] + public void TestOpenSkinEditorGameplaySceneWhenDifferentRulesetActive() + { + BeatmapSetInfo beatmapSet = null!; + + AddStep("import beatmap", () => beatmapSet = BeatmapImportHelper.LoadQuickOszIntoOsu(Game).GetResultSafely()); + AddStep("select mania difficulty", () => + { + var beatmap = beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == 3); + Game.Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(beatmap); + }); + + openSkinEditor(); + switchToGameplayScene(); + } + private void advanceToSongSelect() { PushAndConfirm(() => songSelect = new TestPlaySongSelect()); From 055fb5bd8f482d4fc23658f259a9f9108cf4bc29 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 14:14:07 +0100 Subject: [PATCH 0346/1118] Do not set initial activity in skin editor endless player Seems like an overreach to say that the user is "watching a replay" there. --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index 18d1c4c62b..6e64f5b786 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -26,6 +26,7 @@ using osu.Game.Screens.Edit.Components; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; using osu.Game.Screens.Select; +using osu.Game.Users; using osu.Game.Utils; using osuTK; @@ -285,6 +286,8 @@ namespace osu.Game.Overlays.SkinEditor private partial class EndlessPlayer : ReplayPlayer { + protected override UserActivity? InitialActivity => null; + public EndlessPlayer(Func, Score> createScore) : base(createScore, new PlayerConfiguration { From 9d39b70e38d6beef46e0ad3a7fad03acf0673dc5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 14:14:26 +0100 Subject: [PATCH 0347/1118] Fix endless player not handling load failure --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index 6e64f5b786..46658bb993 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -297,10 +297,21 @@ namespace osu.Game.Overlays.SkinEditor { } + protected override void LoadComplete() + { + base.LoadComplete(); + + if (!LoadedBeatmapSuccessfully) + Scheduler.AddDelayed(this.Exit, 3000); + } + protected override void Update() { base.Update(); + if (!LoadedBeatmapSuccessfully) + return; + if (GameplayState.HasPassed) GameplayClockContainer.Seek(0); } From 063694f5444a3d2b22409e45c121a67470800446 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 14:20:03 +0100 Subject: [PATCH 0348/1118] Do not attempt to load gameplay scene if current beatmap is dummy --- .../Visual/Navigation/TestSceneSkinEditorNavigation.cs | 1 - osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 6 ++++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index 74e6ba1566..fa85c8c9f8 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -281,7 +281,6 @@ namespace osu.Game.Tests.Visual.Navigation AddStep("set dummy beatmap", () => Game.Beatmap.SetDefault()); openSkinEditor(); - switchToGameplayScene(); } [Test] diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index 46658bb993..ae118836c8 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -140,6 +140,12 @@ namespace osu.Game.Overlays.SkinEditor { performer?.PerformFromScreen(screen => { + if (beatmap.Value is DummyWorkingBeatmap) + { + // presume we don't have anything good to play and just bail. + return; + } + // If we're playing the intro, switch away to another beatmap. if (beatmap.Value.BeatmapSetInfo.Protected) { From 8754fa40f4251bd5c94752eb3fadccaf2e95a490 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 14:23:38 +0100 Subject: [PATCH 0349/1118] Source autoplay mod from beatmap about to be presented rather than ambient global --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index ae118836c8..d5c0cd89cf 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -157,7 +157,7 @@ namespace osu.Game.Overlays.SkinEditor if (screen is Player) return; - var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod(); + var replayGeneratingMod = beatmap.Value.BeatmapInfo.Ruleset.CreateInstance().GetAutoplayMod(); IReadOnlyList usableMods = mods.Value; From 5512298d60e84950f15ab9d3cc2453e2a0a7a74d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 4 Dec 2023 15:01:23 +0100 Subject: [PATCH 0350/1118] Trim unused resolved bindable --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index d5c0cd89cf..0821e603d1 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -56,9 +56,6 @@ namespace osu.Game.Overlays.SkinEditor [Resolved] private MusicController music { get; set; } = null!; - [Resolved] - private IBindable ruleset { get; set; } = null!; - [Resolved] private Bindable> mods { get; set; } = null!; From c5a08a07118204227dc26a4c1177908ab77f531f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 4 Dec 2023 23:06:08 +0900 Subject: [PATCH 0351/1118] Remove unused using statement --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index 0821e603d1..be7ddd115b 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -17,7 +17,6 @@ using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; -using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens; From 0e474928580f369645381dae6a9a1388a3cd0391 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 4 Dec 2023 20:17:22 +0100 Subject: [PATCH 0352/1118] Uncomment net6.0 code and remove old code --- osu.Game/Overlays/BeatmapListingOverlay.cs | 20 +------------------- 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListingOverlay.cs b/osu.Game/Overlays/BeatmapListingOverlay.cs index f8784504b8..a645683c5f 100644 --- a/osu.Game/Overlays/BeatmapListingOverlay.cs +++ b/osu.Game/Overlays/BeatmapListingOverlay.cs @@ -183,9 +183,7 @@ namespace osu.Game.Overlays // new results may contain beatmaps from a previous page, // this is dodgy but matches web behaviour for now. // see: https://github.com/ppy/osu-web/issues/9270 - // todo: replace custom equality compraer with ExceptBy in net6.0 - // newCards = newCards.ExceptBy(foundContent.Select(c => c.BeatmapSet.OnlineID), c => c.BeatmapSet.OnlineID); - newCards = newCards.Except(foundContent, BeatmapCardEqualityComparer.Default); + newCards = newCards.ExceptBy(foundContent.Select(c => c.BeatmapSet.OnlineID), c => c.BeatmapSet.OnlineID); panelLoadTask = LoadComponentsAsync(newCards, loaded => { @@ -378,21 +376,5 @@ namespace osu.Game.Overlays if (shouldShowMore) filterControl.FetchNextPage(); } - - private class BeatmapCardEqualityComparer : IEqualityComparer - { - public static BeatmapCardEqualityComparer Default { get; } = new BeatmapCardEqualityComparer(); - - public bool Equals(BeatmapCard x, BeatmapCard y) - { - if (ReferenceEquals(x, y)) return true; - if (ReferenceEquals(x, null)) return false; - if (ReferenceEquals(y, null)) return false; - - return x.BeatmapSet.Equals(y.BeatmapSet); - } - - public int GetHashCode(BeatmapCard obj) => obj.BeatmapSet.GetHashCode(); - } } } From 17577a660654cd2e7b5f8a61405064ebed5aacc4 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 28 Nov 2023 04:40:28 +0300 Subject: [PATCH 0353/1118] Add visual test case for late miss in argon health display --- .../Gameplay/TestSceneArgonHealthDisplay.cs | 73 +++++++++++++++---- 1 file changed, 57 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs index 6c364e41c7..3197de42d0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs @@ -24,6 +24,23 @@ namespace osu.Game.Tests.Visual.Gameplay private ArgonHealthDisplay healthDisplay = null!; + protected override void LoadComplete() + { + base.LoadComplete(); + + AddSliderStep("Height", 0, 64, 0, val => + { + if (healthDisplay.IsNotNull()) + healthDisplay.BarHeight.Value = val; + }); + + AddSliderStep("Width", 0, 1f, 0.98f, val => + { + if (healthDisplay.IsNotNull()) + healthDisplay.Width = val; + }); + } + [SetUpSteps] public void SetUpSteps() { @@ -46,27 +63,12 @@ namespace osu.Game.Tests.Visual.Gameplay }, }; }); - - AddSliderStep("Height", 0, 64, 0, val => - { - if (healthDisplay.IsNotNull()) - healthDisplay.BarHeight.Value = val; - }); - - AddSliderStep("Width", 0, 1f, 0.98f, val => - { - if (healthDisplay.IsNotNull()) - healthDisplay.Width = val; - }); } [Test] public void TestHealthDisplayIncrementing() { - AddRepeatStep("apply miss judgement", delegate - { - healthProcessor.ApplyResult(new JudgementResult(new HitObject(), new Judgement()) { Type = HitResult.Miss }); - }, 5); + AddRepeatStep("apply miss judgement", applyMiss, 5); AddRepeatStep(@"decrease hp slightly", delegate { @@ -87,5 +89,44 @@ namespace osu.Game.Tests.Visual.Gameplay }); }, 3); } + + [Test] + public void TestLateMissAfterConsequentMisses() + { + AddUntilStep("wait for health", () => healthDisplay.Current.Value == 1); + AddStep("apply sequence", () => + { + for (int i = 0; i < 10; i++) + applyMiss(); + + Scheduler.AddDelayed(applyMiss, 500 + 30); + }); + } + + [Test] + public void TestMissAlmostExactlyAfterLastMissAnimation() + { + AddUntilStep("wait for health", () => healthDisplay.Current.Value == 1); + AddStep("apply sequence", () => + { + const double interval = 500 + 15; + + for (int i = 0; i < 5; i++) + { + if (i % 2 == 0) + Scheduler.AddDelayed(applyMiss, i * interval); + else + { + Scheduler.AddDelayed(applyMiss, i * interval); + Scheduler.AddDelayed(applyMiss, i * interval); + } + } + }); + } + + private void applyMiss() + { + healthProcessor.ApplyResult(new JudgementResult(new HitObject(), new Judgement()) { Type = HitResult.Miss }); + } } } From 5723715ea01b8790699ff2562303caa0d7e6b6ee Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Dec 2023 23:23:48 +0300 Subject: [PATCH 0354/1118] Fix `ArgonHealthDisplay` sometimes behaving weirdly on miss judgements --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 39 +++++++++++-------- 1 file changed, 22 insertions(+), 17 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 3f6791d226..2bef6c312f 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -15,6 +15,7 @@ using osu.Framework.Layout; using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Configuration; +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; @@ -54,6 +55,8 @@ namespace osu.Game.Screens.Play.HUD private ScheduledDelegate? resetMissBarDelegate; + private bool displayingMiss => resetMissBarDelegate != null; + private readonly List missBarVertices = new List(); private readonly List healthBarVertices = new List(); @@ -147,10 +150,13 @@ namespace osu.Game.Screens.Play.HUD }; } + private JudgementResult? pendingJudgementResult; + protected override void LoadComplete() { base.LoadComplete(); + HealthProcessor.NewJudgement += result => pendingJudgementResult = result; Current.BindValueChanged(_ => Scheduler.AddOnce(updateCurrent), true); // we're about to set `RelativeSizeAxes` depending on the value of `UseRelativeSize`. @@ -166,13 +172,22 @@ namespace osu.Game.Screens.Play.HUD private void updateCurrent() { - if (Current.Value >= GlowBarValue) finishMissDisplay(); + var result = pendingJudgementResult; + + if (Current.Value >= GlowBarValue) + finishMissDisplay(); double time = Current.Value > GlowBarValue ? 500 : 250; // TODO: this should probably use interpolation in update. this.TransformTo(nameof(HealthBarValue), Current.Value, time, Easing.OutQuint); - if (resetMissBarDelegate == null) this.TransformTo(nameof(GlowBarValue), Current.Value, time, Easing.OutQuint); + + if (result != null && !result.IsHit) + triggerMissDisplay(); + else if (!displayingMiss) + this.TransformTo(nameof(GlowBarValue), Current.Value, time, Easing.OutQuint); + + pendingJudgementResult = null; } protected override void Update() @@ -196,7 +211,7 @@ namespace osu.Game.Screens.Play.HUD mainBar.TransformTo(nameof(BarPath.GlowColour), main_bar_glow_colour.Opacity(0.8f)) .TransformTo(nameof(BarPath.GlowColour), main_bar_glow_colour, 300, Easing.OutQuint); - if (resetMissBarDelegate == null) + if (!displayingMiss) { glowBar.TransformTo(nameof(BarPath.BarColour), Colour4.White, 30, Easing.OutQuint) .Then() @@ -208,20 +223,10 @@ namespace osu.Game.Screens.Play.HUD } } - protected override void Miss() + private void triggerMissDisplay() { - base.Miss(); - - if (resetMissBarDelegate != null) - { - resetMissBarDelegate.Cancel(); - resetMissBarDelegate = null; - } - else - { - // Reset any ongoing animation immediately, else things get weird. - this.TransformTo(nameof(GlowBarValue), HealthBarValue); - } + resetMissBarDelegate?.Cancel(); + resetMissBarDelegate = null; this.Delay(500).Schedule(() => { @@ -238,7 +243,7 @@ namespace osu.Game.Screens.Play.HUD private void finishMissDisplay() { - if (resetMissBarDelegate == null) + if (!displayingMiss) return; if (Current.Value > 0) From 4d82a555942d1b6e2e30a0c219a160feec598f81 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Dec 2023 23:23:58 +0300 Subject: [PATCH 0355/1118] Remove method for being unused --- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index fdbce15b40..13dc05229e 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -45,14 +45,6 @@ namespace osu.Game.Screens.Play.HUD { } - /// - /// Triggered when a resulted in the player losing health. - /// Calls to this method are debounced. - /// - protected virtual void Miss() - { - } - [Resolved] private HUDOverlay? hudOverlay { get; set; } @@ -122,8 +114,6 @@ namespace osu.Game.Screens.Play.HUD { if (judgement.IsHit && judgement.Type != HitResult.IgnoreHit) Scheduler.AddOnce(Flash); - else if (judgement.Judgement.HealthIncreaseFor(judgement) < 0) - Scheduler.AddOnce(Miss); } protected override void Dispose(bool isDisposing) From 629e64d50aa712d226480fe6c91c3ad933bb6a04 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 4 Dec 2023 23:55:31 +0300 Subject: [PATCH 0356/1118] Fix `ArgonHealthDisplay` not displaying miss correctly during initial transition --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 7 +++++++ osu.Game/Screens/Play/HUD/HealthDisplay.cs | 10 ++++++---- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 2bef6c312f..de591cdb31 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -204,6 +204,13 @@ namespace osu.Game.Screens.Play.HUD glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed); } + protected override void FinishInitialAnimation(double value) + { + base.FinishInitialAnimation(value); + this.TransformTo(nameof(HealthBarValue), value, 500, Easing.OutQuint); + this.TransformTo(nameof(GlowBarValue), value, 250, Easing.OutQuint); + } + protected override void Flash() { base.Flash(); diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 13dc05229e..7747036826 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -58,7 +58,9 @@ namespace osu.Game.Screens.Play.HUD health = HealthProcessor.Health.GetBoundCopy(); health.BindValueChanged(h => { - finishInitialAnimation(); + if (initialIncrease != null) + FinishInitialAnimation(h.OldValue); + Current.Value = h.NewValue; }); @@ -90,16 +92,16 @@ namespace osu.Game.Screens.Play.HUD Scheduler.AddOnce(Flash); if (newValue >= health.Value) - finishInitialAnimation(); + FinishInitialAnimation(health.Value); }, increase_delay, true); } - private void finishInitialAnimation() + protected virtual void FinishInitialAnimation(double value) { if (initialIncrease == null) return; - initialIncrease?.Cancel(); + initialIncrease.Cancel(); initialIncrease = null; // aside from the repeating `initialIncrease` scheduled task, From 8756dd25c6d7cff903b0ec9e2fce1a2af48e197c Mon Sep 17 00:00:00 2001 From: Guido <75315940+vegguid@users.noreply.github.com> Date: Mon, 4 Dec 2023 22:51:56 +0100 Subject: [PATCH 0357/1118] Changed file chooser in resource selection to show file name when file is selected --- osu.Game/Localisation/EditorSetupStrings.cs | 10 ---------- osu.Game/Screens/Edit/Setup/ResourcesSection.cs | 9 ++------- 2 files changed, 2 insertions(+), 17 deletions(-) diff --git a/osu.Game/Localisation/EditorSetupStrings.cs b/osu.Game/Localisation/EditorSetupStrings.cs index 401411365b..eff6f9e6b8 100644 --- a/osu.Game/Localisation/EditorSetupStrings.cs +++ b/osu.Game/Localisation/EditorSetupStrings.cs @@ -179,21 +179,11 @@ namespace osu.Game.Localisation /// public static LocalisableString ClickToSelectTrack => new TranslatableString(getKey(@"click_to_select_track"), @"Click to select a track"); - /// - /// "Click to replace the track" - /// - public static LocalisableString ClickToReplaceTrack => new TranslatableString(getKey(@"click_to_replace_track"), @"Click to replace the track"); - /// /// "Click to select a background image" /// public static LocalisableString ClickToSelectBackground => new TranslatableString(getKey(@"click_to_select_background"), @"Click to select a background image"); - /// - /// "Click to replace the background image" - /// - public static LocalisableString ClickToReplaceBackground => new TranslatableString(getKey(@"click_to_replace_background"), @"Click to replace the background image"); - /// /// "Ruleset ({0})" /// diff --git a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs index 8c84ad90ba..f6d20319cb 100644 --- a/osu.Game/Screens/Edit/Setup/ResourcesSection.cs +++ b/osu.Game/Screens/Edit/Setup/ResourcesSection.cs @@ -146,13 +146,8 @@ namespace osu.Game.Screens.Edit.Setup private void updatePlaceholderText() { - audioTrackChooser.Text = audioTrackChooser.Current.Value == null - ? EditorSetupStrings.ClickToSelectTrack - : EditorSetupStrings.ClickToReplaceTrack; - - backgroundChooser.Text = backgroundChooser.Current.Value == null - ? EditorSetupStrings.ClickToSelectBackground - : EditorSetupStrings.ClickToReplaceBackground; + audioTrackChooser.Text = audioTrackChooser.Current.Value?.Name ?? EditorSetupStrings.ClickToSelectTrack; + backgroundChooser.Text = backgroundChooser.Current.Value?.Name ?? EditorSetupStrings.ClickToSelectBackground; } } } From 68907fe1ba93fec9b513c1c6e6072567e7913bb5 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 5 Dec 2023 02:48:11 +0300 Subject: [PATCH 0358/1118] Cleanup pass --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 34 +++++++++++------------ 1 file changed, 16 insertions(+), 18 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs index 5054c75e97..18d4fef5e8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -6,7 +6,6 @@ using System.Linq; using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; -using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects.Drawables; @@ -78,30 +77,29 @@ namespace osu.Game.Rulesets.Osu.Mods foreach (var drawable in playfield.HitObjectContainer.AliveObjects) { - if (drawable is not DrawableOsuHitObject d) - continue; - - switch (d) + switch (drawable) { - case DrawableHitCircle: - case DrawableSlider: - processObject(time, d); + case DrawableHitCircle circle: + processObject(time, circle, 0); + break; + + case DrawableSlider slider: + processObject(time, slider, slider.HitObject.Duration); break; } } } - private void processObject(double time, DrawableOsuHitObject drawable) + private void processObject(double time, DrawableOsuHitObject drawable, double duration) { var hitObject = drawable.HitObject; double baseSpeed = MaxDepth.Value / hitObject.TimePreempt; - double hitObjectDuration = hitObject is Slider s ? s.Duration : 0.0; - double offsetAfterStartTime = hitObjectDuration + hitObject.MaximumJudgementOffset + 500; - double slowSpeed = -minDepth / offsetAfterStartTime; + double offsetAfterStartTime = duration + hitObject.MaximumJudgementOffset + 500; + double slowSpeed = Math.Min(-minDepth / offsetAfterStartTime, baseSpeed); - float decelerationDistance = MaxDepth.Value * 0.2f; - double decelerationTime = (slowSpeed - baseSpeed) * 2 * decelerationDistance / (slowSpeed * slowSpeed - baseSpeed * baseSpeed); + double decelerationTime = hitObject.TimePreempt * 0.2; + float decelerationDistance = (float)(decelerationTime * (baseSpeed + slowSpeed) * 0.5); float z; @@ -109,7 +107,7 @@ namespace osu.Game.Rulesets.Osu.Mods { double appearTime = hitObject.StartTime - hitObject.TimePreempt; float fullDistance = decelerationDistance + (float)(baseSpeed * (hitObject.TimePreempt - decelerationTime)); - z = Interpolation.ValueAt(Math.Max(time, appearTime), fullDistance, decelerationDistance, appearTime, hitObject.StartTime - decelerationTime); + z = fullDistance - (float)((Math.Max(time, appearTime) - appearTime) * baseSpeed); } else if (time < hitObject.StartTime) { @@ -120,11 +118,11 @@ namespace osu.Game.Rulesets.Osu.Mods else { double endTime = hitObject.StartTime + offsetAfterStartTime; - z = Interpolation.ValueAt(Math.Min(time, endTime), 0f, minDepth, hitObject.StartTime, endTime); + z = -(float)((Math.Min(time, endTime) - hitObject.StartTime) * slowSpeed); } float scale = scaleForDepth(z); - drawable.Position = positionAtDepth(scale, hitObject.Position); + drawable.Position = toPlayfieldPosition(scale, hitObject.Position); drawable.Scale = new Vector2(scale); } @@ -132,7 +130,7 @@ namespace osu.Game.Rulesets.Osu.Mods private static float depthForScale(float scale) => 100 / scale + camera_position.Z; - private static Vector2 positionAtDepth(float scale, Vector2 positionAtZeroDepth) + private static Vector2 toPlayfieldPosition(float scale, Vector2 positionAtZeroDepth) { return (positionAtZeroDepth - camera_position.Xy) * scale + camera_position.Xy; } From b90de83f33d330baaf37d71759ac081dd37c04ea Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 5 Dec 2023 11:57:51 +0900 Subject: [PATCH 0359/1118] Replicate integer calculations for tiny tick conversion --- .../CatchBeatmapConversionTest.cs | 1 + .../Objects/JuiceStream.cs | 2 +- .../tiny-ticks-expected-conversion.json | 44 +++++++++++++++++++ .../Resources/Testing/Beatmaps/tiny-ticks.osu | 21 +++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks.osu diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index a11aaa5e29..d70b171ff2 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -28,6 +28,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("right-bound-hr-offset", new[] { typeof(CatchModHardRock) })] [TestCase("basic-hyperdash")] [TestCase("pixel-jump")] + [TestCase("tiny-ticks")] public new void Test(string name, params Type[] mods) => base.Test(name, mods); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index fb1a86d8c0..019a67a70a 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -82,7 +82,7 @@ namespace osu.Game.Rulesets.Catch.Objects // generate tiny droplets since the last point if (lastEvent != null) { - double sinceLastTick = e.Time - lastEvent.Value.Time; + double sinceLastTick = (int)e.Time - (int)lastEvent.Value.Time; if (sinceLastTick > 80) { diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks-expected-conversion.json new file mode 100644 index 0000000000..7a9e848a7b --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks-expected-conversion.json @@ -0,0 +1,44 @@ +{ + "Mappings": [ + { + "StartTime": 27002.0, + "Objects": [ + { + "StartTime": 27002.0, + "Position": 326.0, + "HyperDash": false + }, + { + "StartTime": 27102.0, + "Position": 267.416656, + "HyperDash": false + }, + { + "StartTime": 27238.0, + "Position": 217.484329, + "HyperDash": false + } + ] + }, + { + "StartTime": 27318.0, + "Objects": [ + { + "StartTime": 27318.0, + "Position": 215.0, + "HyperDash": false + }, + { + "StartTime": 27418.0, + "Position": 251.682343, + "HyperDash": false + }, + { + "StartTime": 27554.0, + "Position": 323.347046, + "HyperDash": false + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks.osu new file mode 100644 index 0000000000..7808bd0764 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks.osu @@ -0,0 +1,21 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:4.7 +CircleSize:3.7 +OverallDifficulty:8.4 +ApproachRate:9 +SliderMultiplier:1.57 +SliderTickRate:1 + +[TimingPoints] +476,315.789473684211,4,2,1,50,1,0 +18160,-103.092783505155,4,2,1,70,0,0 + +[HitObjects] +326,119,27002,6,0,P|266:96|196:111,1,114.217502701372,14|0,0:2|0:0,0:0:0:0: +215,85,27318,2,0,P|271:80|323:102,1,114.217502701372,8|0,0:2|0:0,0:0:0:0: \ No newline at end of file From 7fda38d0b02496f4e255e496a73def950deca6a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2023 14:12:25 +0900 Subject: [PATCH 0360/1118] Use ordinal comparison when searching at song select Bypasses various overheads. In theory should be fine? (until it's not on some language) --- osu.Game/Screens/Select/FilterCriteria.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 812a16c484..811f623ee5 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -176,13 +176,15 @@ namespace osu.Game.Screens.Select { default: case MatchMode.Substring: - return value.Contains(SearchTerm, StringComparison.InvariantCultureIgnoreCase); + // Note that we are using ordinal here to avoid performance issues caused by globalisation concerns. + // See https://github.com/ppy/osu/issues/11571 / https://github.com/dotnet/docs/issues/18423. + return value.Contains(SearchTerm, StringComparison.OrdinalIgnoreCase); case MatchMode.IsolatedPhrase: return Regex.IsMatch(value, $@"(^|\s){Regex.Escape(searchTerm)}($|\s)", RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); case MatchMode.FullPhrase: - return CultureInfo.InvariantCulture.CompareInfo.Compare(value, searchTerm, CompareOptions.IgnoreCase) == 0; + return CultureInfo.InvariantCulture.CompareInfo.Compare(value, searchTerm, CompareOptions.OrdinalIgnoreCase) == 0; } } From 27e778ae09364981ce5e2768110efde51b4a3f7b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2023 14:15:05 +0900 Subject: [PATCH 0361/1118] Avoid sorting items when already in the correct sort order --- .../Screens/Select/Carousel/CarouselGroup.cs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index 9302578038..c353ee98ae 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -86,16 +86,20 @@ namespace osu.Game.Screens.Select.Carousel items.ForEach(c => c.Filter(criteria)); - criteriaComparer = Comparer.Create((x, y) => + // Sorting is expensive, so only perform if it's actually changed. + if (lastCriteria?.Sort != criteria.Sort) { - int comparison = x.CompareTo(criteria, y); - if (comparison != 0) - return comparison; + criteriaComparer = Comparer.Create((x, y) => + { + int comparison = x.CompareTo(criteria, y); + if (comparison != 0) + return comparison; - return x.ItemID.CompareTo(y.ItemID); - }); + return x.ItemID.CompareTo(y.ItemID); + }); - items.Sort(criteriaComparer); + items.Sort(criteriaComparer); + } lastCriteria = criteria; } From 7d602c792da2446375e73d1ed211f3d7f62f432f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 5 Dec 2023 15:09:55 +0900 Subject: [PATCH 0362/1118] Fix legacy tick distance in JuiceStream generation --- .../CatchBeatmapConversionTest.cs | 1 + .../Beatmaps/CatchBeatmapConverter.cs | 4 ++ .../Objects/JuiceStream.cs | 8 ++- .../v8-tick-distance-expected-conversion.json | 54 +++++++++++++++++++ .../Testing/Beatmaps/v8-tick-distance.osu | 19 +++++++ 5 files changed, 85 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance.osu diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index d70b171ff2..d1fe213a32 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -29,6 +29,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("basic-hyperdash")] [TestCase("pixel-jump")] [TestCase("tiny-ticks")] + [TestCase("v8-tick-distance")] public new void Test(string name, params Type[] mods) => base.Test(name, mods); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs index 6a24c26844..8c460586b0 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapConverter.cs @@ -9,6 +9,7 @@ using System.Threading; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Objects; using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Game.Beatmaps.Legacy; namespace osu.Game.Rulesets.Catch.Beatmaps { @@ -42,6 +43,9 @@ namespace osu.Game.Rulesets.Catch.Beatmaps NewCombo = comboData?.NewCombo ?? false, ComboOffset = comboData?.ComboOffset ?? 0, LegacyConvertedY = yPositionData?.Y ?? CatchHitObject.DEFAULT_LEGACY_CONVERT_Y, + // prior to v8, speed multipliers don't adjust for how many ticks are generated over the same distance. + // this results in more (or less) ticks being generated in SliderVelocityMultiplierBindable.Value = value; } + /// + /// An extra multiplier that affects the number of s generated by this . + /// An increase in this value increases , which reduces the number of ticks generated. + /// + public double TickDistanceMultiplier = 1; + [JsonIgnore] private double velocityFactor; @@ -51,7 +57,7 @@ namespace osu.Game.Rulesets.Catch.Objects public double Velocity => velocityFactor * SliderVelocityMultiplier; [JsonIgnore] - public double TickDistance => tickDistanceFactor * SliderVelocityMultiplier; + public double TickDistance => tickDistanceFactor * TickDistanceMultiplier; /// /// The length of one span of this . diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance-expected-conversion.json new file mode 100644 index 0000000000..82167f37dd --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance-expected-conversion.json @@ -0,0 +1,54 @@ +{ + "Mappings": [ + { + "StartTime": 81593.0, + "Objects": [ + { + "StartTime": 81593.0, + "Position": 384.0, + "HyperDash": false + }, + { + "StartTime": 81652.0, + "Position": 377.608948, + "HyperDash": false + }, + { + "StartTime": 81712.0, + "Position": 390.3638, + "HyperDash": false + }, + { + "StartTime": 81772.0, + "Position": 407.118683, + "HyperDash": false + }, + { + "StartTime": 81832.0, + "Position": 433.873535, + "HyperDash": false + }, + { + "StartTime": 81891.0, + "Position": 444.482483, + "HyperDash": false + }, + { + "StartTime": 81951.0, + "Position": 437.237366, + "HyperDash": false + }, + { + "StartTime": 82011.0, + "Position": 443.992218, + "HyperDash": false + }, + { + "StartTime": 82107.0, + "Position": 459.0, + "HyperDash": false + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance.osu new file mode 100644 index 0000000000..9fdba9dc0b --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance.osu @@ -0,0 +1,19 @@ +osu file format v7 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:5 +CircleSize:4 +OverallDifficulty:8 +SliderMultiplier:1 +SliderTickRate:1 + +[TimingPoints] +336,342.857142857143,4,1,0,100,1,0 +81588,-200,4,2,0,100,0,0 + +[HitObjects] +384,72,81593,2,12,B|464:72,1,75,4|4 \ No newline at end of file From 42010574b5d27d8ca6925a8b21ba7a61e4391926 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2023 14:53:47 +0900 Subject: [PATCH 0363/1118] Avoid list construction when doing filtering --- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 22 +++++++++---------- .../Beatmaps/BeatmapMetadataInfoExtensions.cs | 19 +++++++++++++++- .../Select/Carousel/CarouselBeatmap.cs | 21 +----------------- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index 3aab9a24e1..a3bc03acc8 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -1,8 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; using osu.Framework.Localisation; +using osu.Game.Screens.Select; namespace osu.Game.Beatmaps { @@ -29,20 +29,18 @@ namespace osu.Game.Beatmaps return new RomanisableString($"{metadata.GetPreferred(true)}".Trim(), $"{metadata.GetPreferred(false)}".Trim()); } - public static List GetSearchableTerms(this IBeatmapInfo beatmapInfo) + public static bool Match(this IBeatmapInfo beatmapInfo, params FilterCriteria.OptionalTextFilter[] filters) { - var termsList = new List(BeatmapMetadataInfoExtensions.MAX_SEARCHABLE_TERM_COUNT + 1); - - addIfNotNull(beatmapInfo.DifficultyName); - - BeatmapMetadataInfoExtensions.CollectSearchableTerms(beatmapInfo.Metadata, termsList); - return termsList; - - void addIfNotNull(string? s) + foreach (var filter in filters) { - if (!string.IsNullOrEmpty(s)) - termsList.Add(s); + if (filter.Matches(beatmapInfo.DifficultyName)) + return true; + + if (BeatmapMetadataInfoExtensions.Match(beatmapInfo.Metadata, filters)) + return true; } + + return false; } private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; diff --git a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs index be96a66614..ee3afdaef5 100644 --- a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs @@ -3,11 +3,14 @@ using System.Collections.Generic; using osu.Framework.Localisation; +using osu.Game.Screens.Select; namespace osu.Game.Beatmaps { public static class BeatmapMetadataInfoExtensions { + internal const int MAX_SEARCHABLE_TERM_COUNT = 7; + /// /// An array of all searchable terms provided in contained metadata. /// @@ -18,7 +21,21 @@ namespace osu.Game.Beatmaps return termsList.ToArray(); } - internal const int MAX_SEARCHABLE_TERM_COUNT = 7; + public static bool Match(IBeatmapMetadataInfo metadataInfo, FilterCriteria.OptionalTextFilter[] filters) + { + foreach (var filter in filters) + { + if (filter.Matches(metadataInfo.Author.Username)) return true; + if (filter.Matches(metadataInfo.Artist)) return true; + if (filter.Matches(metadataInfo.ArtistUnicode)) return true; + if (filter.Matches(metadataInfo.Title)) return true; + if (filter.Matches(metadataInfo.TitleUnicode)) return true; + if (filter.Matches(metadataInfo.Source)) return true; + if (filter.Matches(metadataInfo.Tags)) return true; + } + + return false; + } internal static void CollectSearchableTerms(IBeatmapMetadataInfo metadataInfo, IList termsList) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 1d40862df7..8b891a035c 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -66,26 +66,7 @@ namespace osu.Game.Screens.Select.Carousel if (criteria.SearchTerms.Length > 0) { - var searchableTerms = BeatmapInfo.GetSearchableTerms(); - - foreach (FilterCriteria.OptionalTextFilter criteriaTerm in criteria.SearchTerms) - { - bool any = false; - - // ReSharper disable once ForeachCanBeConvertedToQueryUsingAnotherGetEnumerator - foreach (string searchTerm in searchableTerms) - { - if (!criteriaTerm.Matches(searchTerm)) continue; - - any = true; - break; - } - - if (any) continue; - - match = false; - break; - } + match = BeatmapInfo.Match(criteria.SearchTerms); // if a match wasn't found via text matching of terms, do a second catch-all check matching against online IDs. // this should be done after text matching so we can prioritise matching numbers in metadata. From 45e499778f8696087defd6c116970f918428539f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2023 15:28:56 +0900 Subject: [PATCH 0364/1118] Search terms before performing other criteria checks Very minor, but putting the more common case towards the start of the method allows early return. --- .../Select/Carousel/CarouselBeatmap.cs | 30 +++++++++---------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 8b891a035c..45594bd0e8 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -41,6 +41,21 @@ namespace osu.Game.Screens.Select.Carousel return match; } + if (criteria.SearchTerms.Length > 0) + { + match = BeatmapInfo.Match(criteria.SearchTerms); + + // if a match wasn't found via text matching of terms, do a second catch-all check matching against online IDs. + // this should be done after text matching so we can prioritise matching numbers in metadata. + if (!match && criteria.SearchNumber.HasValue) + { + match = (BeatmapInfo.OnlineID == criteria.SearchNumber.Value) || + (BeatmapInfo.BeatmapSet?.OnlineID == criteria.SearchNumber.Value); + } + } + + if (!match) return false; + match &= !criteria.StarDifficulty.HasFilter || criteria.StarDifficulty.IsInRange(BeatmapInfo.StarRating); match &= !criteria.ApproachRate.HasFilter || criteria.ApproachRate.IsInRange(BeatmapInfo.Difficulty.ApproachRate); match &= !criteria.DrainRate.HasFilter || criteria.DrainRate.IsInRange(BeatmapInfo.Difficulty.DrainRate); @@ -64,21 +79,6 @@ namespace osu.Game.Screens.Select.Carousel if (!match) return false; - if (criteria.SearchTerms.Length > 0) - { - match = BeatmapInfo.Match(criteria.SearchTerms); - - // if a match wasn't found via text matching of terms, do a second catch-all check matching against online IDs. - // this should be done after text matching so we can prioritise matching numbers in metadata. - if (!match && criteria.SearchNumber.HasValue) - { - match = (BeatmapInfo.OnlineID == criteria.SearchNumber.Value) || - (BeatmapInfo.BeatmapSet?.OnlineID == criteria.SearchNumber.Value); - } - } - - if (!match) return false; - match &= criteria.CollectionBeatmapMD5Hashes?.Contains(BeatmapInfo.MD5Hash) ?? true; if (match && criteria.RulesetCriteria != null) match &= criteria.RulesetCriteria.Matches(BeatmapInfo); From 3aaba3183b91d7a2608002929ab236604c99b2bc Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 5 Dec 2023 15:39:23 +0900 Subject: [PATCH 0365/1118] Match stable precision when generating catch bananas --- .../CatchBeatmapConversionTest.cs | 1 + .../Objects/BananaShower.cs | 14 +- ...spinner-precision-expected-conversion.json | 649 ++++++++++++++++++ .../Testing/Beatmaps/spinner-precision.osu | 20 + 4 files changed, 677 insertions(+), 7 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision.osu diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index d1fe213a32..5528ce0bfa 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -30,6 +30,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("pixel-jump")] [TestCase("tiny-ticks")] [TestCase("v8-tick-distance")] + [TestCase("spinner-precision")] public new void Test(string name, params Type[] mods) => base.Test(name, mods); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index b05c8e5f77..abeb7fe61d 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -23,29 +23,29 @@ namespace osu.Game.Rulesets.Catch.Objects private void createBananas(CancellationToken cancellationToken) { - double spacing = Duration; + int startTime = (int)StartTime; + int endTime = (int)EndTime; + float spacing = (float)(EndTime - StartTime); while (spacing > 100) spacing /= 2; if (spacing <= 0) return; - double time = StartTime; - int i = 0; + int count = 0; - while (time <= EndTime) + for (float time = startTime; time <= endTime; time += spacing) { cancellationToken.ThrowIfCancellationRequested(); AddNested(new Banana { StartTime = time, - BananaIndex = i, + BananaIndex = count, Samples = new List { new Banana.BananaHitSampleInfo(CreateHitSampleInfo().Volume) } }); - time += spacing; - i++; + count++; } } diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision-expected-conversion.json b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision-expected-conversion.json new file mode 100644 index 0000000000..95a0c8b34e --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision-expected-conversion.json @@ -0,0 +1,649 @@ +{ + "Mappings": [ + { + "StartTime": 276419.0, + "Objects": [ + { + "StartTime": 276419.0, + "Position": 65.0, + "HyperDash": false + }, + { + "StartTime": 276494.0, + "Position": 482.0, + "HyperDash": false + }, + { + "StartTime": 276569.0, + "Position": 164.0, + "HyperDash": false + }, + { + "StartTime": 276645.0, + "Position": 315.0, + "HyperDash": false + }, + { + "StartTime": 276720.0, + "Position": 145.0, + "HyperDash": false + }, + { + "StartTime": 276795.0, + "Position": 159.0, + "HyperDash": false + }, + { + "StartTime": 276871.0, + "Position": 310.0, + "HyperDash": false + }, + { + "StartTime": 276946.0, + "Position": 441.0, + "HyperDash": false + }, + { + "StartTime": 277021.0, + "Position": 428.0, + "HyperDash": false + }, + { + "StartTime": 277097.0, + "Position": 243.0, + "HyperDash": false + }, + { + "StartTime": 277172.0, + "Position": 422.0, + "HyperDash": false + }, + { + "StartTime": 277247.0, + "Position": 481.0, + "HyperDash": false + }, + { + "StartTime": 277323.0, + "Position": 104.0, + "HyperDash": false + }, + { + "StartTime": 277398.0, + "Position": 473.0, + "HyperDash": false + }, + { + "StartTime": 277473.0, + "Position": 135.0, + "HyperDash": false + }, + { + "StartTime": 277549.0, + "Position": 360.0, + "HyperDash": false + }, + { + "StartTime": 277624.0, + "Position": 123.0, + "HyperDash": false + }, + { + "StartTime": 277699.0, + "Position": 42.0, + "HyperDash": false + }, + { + "StartTime": 277775.0, + "Position": 393.0, + "HyperDash": false + }, + { + "StartTime": 277850.0, + "Position": 75.0, + "HyperDash": false + }, + { + "StartTime": 277925.0, + "Position": 377.0, + "HyperDash": false + }, + { + "StartTime": 278001.0, + "Position": 354.0, + "HyperDash": false + }, + { + "StartTime": 278076.0, + "Position": 287.0, + "HyperDash": false + }, + { + "StartTime": 278151.0, + "Position": 361.0, + "HyperDash": false + }, + { + "StartTime": 278227.0, + "Position": 479.0, + "HyperDash": false + }, + { + "StartTime": 278302.0, + "Position": 346.0, + "HyperDash": false + }, + { + "StartTime": 278377.0, + "Position": 266.0, + "HyperDash": false + }, + { + "StartTime": 278453.0, + "Position": 400.0, + "HyperDash": false + }, + { + "StartTime": 278528.0, + "Position": 202.0, + "HyperDash": false + }, + { + "StartTime": 278603.0, + "Position": 500.0, + "HyperDash": false + }, + { + "StartTime": 278679.0, + "Position": 80.0, + "HyperDash": false + }, + { + "StartTime": 278754.0, + "Position": 399.0, + "HyperDash": false + }, + { + "StartTime": 278830.0, + "Position": 455.0, + "HyperDash": false + }, + { + "StartTime": 278905.0, + "Position": 105.0, + "HyperDash": false + }, + { + "StartTime": 278980.0, + "Position": 100.0, + "HyperDash": false + }, + { + "StartTime": 279056.0, + "Position": 195.0, + "HyperDash": false + }, + { + "StartTime": 279131.0, + "Position": 106.0, + "HyperDash": false + }, + { + "StartTime": 279206.0, + "Position": 305.0, + "HyperDash": false + }, + { + "StartTime": 279282.0, + "Position": 225.0, + "HyperDash": false + }, + { + "StartTime": 279357.0, + "Position": 79.0, + "HyperDash": false + }, + { + "StartTime": 279432.0, + "Position": 38.0, + "HyperDash": false + }, + { + "StartTime": 279508.0, + "Position": 99.0, + "HyperDash": false + }, + { + "StartTime": 279583.0, + "Position": 79.0, + "HyperDash": false + }, + { + "StartTime": 279658.0, + "Position": 169.0, + "HyperDash": false + }, + { + "StartTime": 279734.0, + "Position": 238.0, + "HyperDash": false + }, + { + "StartTime": 279809.0, + "Position": 511.0, + "HyperDash": false + }, + { + "StartTime": 279884.0, + "Position": 58.0, + "HyperDash": false + }, + { + "StartTime": 279960.0, + "Position": 368.0, + "HyperDash": false + }, + { + "StartTime": 280035.0, + "Position": 52.0, + "HyperDash": false + }, + { + "StartTime": 280110.0, + "Position": 327.0, + "HyperDash": false + }, + { + "StartTime": 280186.0, + "Position": 226.0, + "HyperDash": false + }, + { + "StartTime": 280261.0, + "Position": 110.0, + "HyperDash": false + }, + { + "StartTime": 280336.0, + "Position": 3.0, + "HyperDash": false + }, + { + "StartTime": 280412.0, + "Position": 26.0, + "HyperDash": false + }, + { + "StartTime": 280487.0, + "Position": 173.0, + "HyperDash": false + }, + { + "StartTime": 280562.0, + "Position": 18.0, + "HyperDash": false + }, + { + "StartTime": 280638.0, + "Position": 310.0, + "HyperDash": false + }, + { + "StartTime": 280713.0, + "Position": 394.0, + "HyperDash": false + }, + { + "StartTime": 280788.0, + "Position": 406.0, + "HyperDash": false + }, + { + "StartTime": 280864.0, + "Position": 262.0, + "HyperDash": false + }, + { + "StartTime": 280939.0, + "Position": 278.0, + "HyperDash": false + }, + { + "StartTime": 281014.0, + "Position": 171.0, + "HyperDash": false + }, + { + "StartTime": 281090.0, + "Position": 22.0, + "HyperDash": false + }, + { + "StartTime": 281165.0, + "Position": 187.0, + "HyperDash": false + }, + { + "StartTime": 281241.0, + "Position": 124.0, + "HyperDash": false + }, + { + "StartTime": 281316.0, + "Position": 454.0, + "HyperDash": false + }, + { + "StartTime": 281391.0, + "Position": 16.0, + "HyperDash": false + }, + { + "StartTime": 281467.0, + "Position": 61.0, + "HyperDash": false + }, + { + "StartTime": 281542.0, + "Position": 161.0, + "HyperDash": false + }, + { + "StartTime": 281617.0, + "Position": 243.0, + "HyperDash": false + }, + { + "StartTime": 281693.0, + "Position": 375.0, + "HyperDash": false + }, + { + "StartTime": 281768.0, + "Position": 247.0, + "HyperDash": false + }, + { + "StartTime": 281843.0, + "Position": 162.0, + "HyperDash": false + }, + { + "StartTime": 281919.0, + "Position": 383.0, + "HyperDash": false + }, + { + "StartTime": 281994.0, + "Position": 127.0, + "HyperDash": false + }, + { + "StartTime": 282069.0, + "Position": 161.0, + "HyperDash": false + }, + { + "StartTime": 282145.0, + "Position": 332.0, + "HyperDash": false + }, + { + "StartTime": 282220.0, + "Position": 356.0, + "HyperDash": false + }, + { + "StartTime": 282295.0, + "Position": 362.0, + "HyperDash": false + }, + { + "StartTime": 282371.0, + "Position": 347.0, + "HyperDash": false + }, + { + "StartTime": 282446.0, + "Position": 252.0, + "HyperDash": false + }, + { + "StartTime": 282521.0, + "Position": 477.0, + "HyperDash": false + }, + { + "StartTime": 282597.0, + "Position": 358.0, + "HyperDash": false + }, + { + "StartTime": 282672.0, + "Position": 17.0, + "HyperDash": false + }, + { + "StartTime": 282747.0, + "Position": 399.0, + "HyperDash": false + }, + { + "StartTime": 282823.0, + "Position": 280.0, + "HyperDash": false + }, + { + "StartTime": 282898.0, + "Position": 304.0, + "HyperDash": false + }, + { + "StartTime": 282973.0, + "Position": 221.0, + "HyperDash": false + }, + { + "StartTime": 283049.0, + "Position": 407.0, + "HyperDash": false + }, + { + "StartTime": 283124.0, + "Position": 287.0, + "HyperDash": false + }, + { + "StartTime": 283199.0, + "Position": 135.0, + "HyperDash": false + }, + { + "StartTime": 283275.0, + "Position": 437.0, + "HyperDash": false + }, + { + "StartTime": 283350.0, + "Position": 289.0, + "HyperDash": false + }, + { + "StartTime": 283425.0, + "Position": 464.0, + "HyperDash": false + }, + { + "StartTime": 283501.0, + "Position": 36.0, + "HyperDash": false + }, + { + "StartTime": 283576.0, + "Position": 378.0, + "HyperDash": false + }, + { + "StartTime": 283652.0, + "Position": 297.0, + "HyperDash": false + }, + { + "StartTime": 283727.0, + "Position": 418.0, + "HyperDash": false + }, + { + "StartTime": 283802.0, + "Position": 329.0, + "HyperDash": false + }, + { + "StartTime": 283878.0, + "Position": 338.0, + "HyperDash": false + }, + { + "StartTime": 283953.0, + "Position": 394.0, + "HyperDash": false + }, + { + "StartTime": 284028.0, + "Position": 40.0, + "HyperDash": false + }, + { + "StartTime": 284104.0, + "Position": 13.0, + "HyperDash": false + }, + { + "StartTime": 284179.0, + "Position": 80.0, + "HyperDash": false + }, + { + "StartTime": 284254.0, + "Position": 138.0, + "HyperDash": false + }, + { + "StartTime": 284330.0, + "Position": 311.0, + "HyperDash": false + }, + { + "StartTime": 284405.0, + "Position": 216.0, + "HyperDash": false + }, + { + "StartTime": 284480.0, + "Position": 310.0, + "HyperDash": false + }, + { + "StartTime": 284556.0, + "Position": 397.0, + "HyperDash": false + }, + { + "StartTime": 284631.0, + "Position": 214.0, + "HyperDash": false + }, + { + "StartTime": 284706.0, + "Position": 505.0, + "HyperDash": false + }, + { + "StartTime": 284782.0, + "Position": 173.0, + "HyperDash": false + }, + { + "StartTime": 284857.0, + "Position": 295.0, + "HyperDash": false + }, + { + "StartTime": 284932.0, + "Position": 199.0, + "HyperDash": false + }, + { + "StartTime": 285008.0, + "Position": 494.0, + "HyperDash": false + }, + { + "StartTime": 285083.0, + "Position": 293.0, + "HyperDash": false + }, + { + "StartTime": 285158.0, + "Position": 115.0, + "HyperDash": false + }, + { + "StartTime": 285234.0, + "Position": 412.0, + "HyperDash": false + }, + { + "StartTime": 285309.0, + "Position": 506.0, + "HyperDash": false + }, + { + "StartTime": 285384.0, + "Position": 293.0, + "HyperDash": false + }, + { + "StartTime": 285460.0, + "Position": 346.0, + "HyperDash": false + }, + { + "StartTime": 285535.0, + "Position": 117.0, + "HyperDash": false + }, + { + "StartTime": 285610.0, + "Position": 285.0, + "HyperDash": false + }, + { + "StartTime": 285686.0, + "Position": 17.0, + "HyperDash": false + }, + { + "StartTime": 285761.0, + "Position": 238.0, + "HyperDash": false + }, + { + "StartTime": 285836.0, + "Position": 222.0, + "HyperDash": false + }, + { + "StartTime": 285912.0, + "Position": 450.0, + "HyperDash": false + }, + { + "StartTime": 285987.0, + "Position": 67.0, + "HyperDash": false + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision.osu b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision.osu new file mode 100644 index 0000000000..2ba1fea357 --- /dev/null +++ b/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision.osu @@ -0,0 +1,20 @@ +osu file format v14 + +[General] +StackLeniency: 0.8 +Mode: 0 + +[Difficulty] +HPDrainRate:5 +CircleSize:3 +OverallDifficulty:8 +ApproachRate:9.2 +SliderMultiplier:1.7 +SliderTickRate:1 + +[TimingPoints] +276254,995.850622406639,4,2,1,70,1,0 +276254,-100,4,2,1,70,0,0 + +[HitObjects] +256,192,276419,12,4,286062,2:3:0:0: From f317e06da14040d2fb5fbbc7375bbf12c729c1e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2023 16:54:44 +0900 Subject: [PATCH 0366/1118] Use `DangerousActionDialog` --- .../Overlays/Dialog/DangerousActionDialog.cs | 8 ++++++- .../Multiplayer/Match/ConfirmAbortDialog.cs | 22 ++----------------- .../Multiplayer/Match/MatchStartControl.cs | 12 ++++++++++ 3 files changed, 21 insertions(+), 21 deletions(-) diff --git a/osu.Game/Overlays/Dialog/DangerousActionDialog.cs b/osu.Game/Overlays/Dialog/DangerousActionDialog.cs index c86570386f..42a3ff827c 100644 --- a/osu.Game/Overlays/Dialog/DangerousActionDialog.cs +++ b/osu.Game/Overlays/Dialog/DangerousActionDialog.cs @@ -23,6 +23,11 @@ namespace osu.Game.Overlays.Dialog /// protected Action? DangerousAction { get; set; } + /// + /// The action to perform if cancelled. + /// + protected Action? CancelAction { get; set; } + protected DangerousActionDialog() { HeaderText = DeleteConfirmationDialogStrings.HeaderText; @@ -38,7 +43,8 @@ namespace osu.Game.Overlays.Dialog }, new PopupDialogCancelButton { - Text = DeleteConfirmationDialogStrings.Cancel + Text = DeleteConfirmationDialogStrings.Cancel, + Action = () => CancelAction?.Invoke() } }; } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs index 06f2b2c8f6..0793981f41 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs @@ -1,33 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using osu.Framework.Graphics.Sprites; using osu.Game.Overlays.Dialog; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { - public partial class ConfirmAbortDialog : PopupDialog + public partial class ConfirmAbortDialog : DangerousActionDialog { - public ConfirmAbortDialog(Action onConfirm, Action onCancel) + public ConfirmAbortDialog() { HeaderText = "Are you sure you want to abort the match?"; - - Icon = FontAwesome.Solid.ExclamationTriangle; - - Buttons = new PopupDialogButton[] - { - new PopupDialogDangerousButton - { - Text = @"Yes", - Action = onConfirm - }, - new PopupDialogCancelButton - { - Text = @"No I didn't mean to", - Action = onCancel - }, - }; } } } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs index 99934acaae..ba3508b24f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MatchStartControl.cs @@ -17,6 +17,7 @@ using osu.Framework.Threading; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.Countdown; using osu.Game.Overlays; +using osu.Game.Overlays.Dialog; using osuTK; namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match @@ -247,5 +248,16 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match countReady = newCountReady; }); } + + public partial class ConfirmAbortDialog : DangerousActionDialog + { + public ConfirmAbortDialog(Action abortMatch, Action cancel) + { + HeaderText = "Are you sure you want to abort the match?"; + + DangerousAction = abortMatch; + CancelAction = cancel; + } + } } } From 02178d8e611dfc3c8a8335f41c68d6fab5dfbe0c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2023 16:58:16 +0900 Subject: [PATCH 0367/1118] Remove usage of `case-when` --- .../Match/MultiplayerReadyButton.cs | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs index 368e5210de..7ce3dde7c2 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/MultiplayerReadyButton.cs @@ -149,10 +149,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match { switch (localUser?.State) { - default: - Text = "Ready"; - break; - case MultiplayerUserState.Spectating: case MultiplayerUserState.Ready: Text = multiplayerClient.IsHost @@ -160,9 +156,12 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match : $"Waiting for host... {countText}"; break; - // Show the abort button for the host as long as gameplay is in progress. - case MultiplayerUserState when multiplayerClient.IsHost && room.State != MultiplayerRoomState.Open: - Text = "Abort the match"; + default: + // Show the abort button for the host as long as gameplay is in progress. + if (multiplayerClient.IsHost && room.State != MultiplayerRoomState.Open) + Text = "Abort the match"; + else + Text = "Ready"; break; } } @@ -197,7 +196,11 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match switch (localUser?.State) { default: - setGreen(); + // Show the abort button for the host as long as gameplay is in progress. + if (multiplayerClient.IsHost && room.State != MultiplayerRoomState.Open) + setRed(); + else + setGreen(); break; case MultiplayerUserState.Spectating: @@ -208,11 +211,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match setYellow(); break; - - // Show the abort button for the host as long as gameplay is in progress. - case MultiplayerUserState when multiplayerClient.IsHost && room.State != MultiplayerRoomState.Open: - setRed(); - break; } void setYellow() => BackgroundColour = colours.YellowDark; From 8704dc3505a934f42f2259ec734bb072aa940282 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 5 Dec 2023 18:20:27 +0900 Subject: [PATCH 0368/1118] Fix change in filter behaviour --- osu.Game/Beatmaps/BeatmapInfoExtensions.cs | 12 ++++++++---- .../Beatmaps/BeatmapMetadataInfoExtensions.cs | 19 ++++++++----------- 2 files changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs index a3bc03acc8..b00d0ba316 100644 --- a/osu.Game/Beatmaps/BeatmapInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapInfoExtensions.cs @@ -34,13 +34,17 @@ namespace osu.Game.Beatmaps foreach (var filter in filters) { if (filter.Matches(beatmapInfo.DifficultyName)) - return true; + continue; - if (BeatmapMetadataInfoExtensions.Match(beatmapInfo.Metadata, filters)) - return true; + if (BeatmapMetadataInfoExtensions.Match(beatmapInfo.Metadata, filter)) + continue; + + // failed to match a single filter at all - fail the whole match. + return false; } - return false; + // got through all filters without failing any - pass the whole match. + return true; } private static string getVersionString(IBeatmapInfo beatmapInfo) => string.IsNullOrEmpty(beatmapInfo.DifficultyName) ? string.Empty : $"[{beatmapInfo.DifficultyName}]"; diff --git a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs index ee3afdaef5..198469dba6 100644 --- a/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs +++ b/osu.Game/Beatmaps/BeatmapMetadataInfoExtensions.cs @@ -21,18 +21,15 @@ namespace osu.Game.Beatmaps return termsList.ToArray(); } - public static bool Match(IBeatmapMetadataInfo metadataInfo, FilterCriteria.OptionalTextFilter[] filters) + public static bool Match(IBeatmapMetadataInfo metadataInfo, FilterCriteria.OptionalTextFilter filter) { - foreach (var filter in filters) - { - if (filter.Matches(metadataInfo.Author.Username)) return true; - if (filter.Matches(metadataInfo.Artist)) return true; - if (filter.Matches(metadataInfo.ArtistUnicode)) return true; - if (filter.Matches(metadataInfo.Title)) return true; - if (filter.Matches(metadataInfo.TitleUnicode)) return true; - if (filter.Matches(metadataInfo.Source)) return true; - if (filter.Matches(metadataInfo.Tags)) return true; - } + if (filter.Matches(metadataInfo.Author.Username)) return true; + if (filter.Matches(metadataInfo.Artist)) return true; + if (filter.Matches(metadataInfo.ArtistUnicode)) return true; + if (filter.Matches(metadataInfo.Title)) return true; + if (filter.Matches(metadataInfo.TitleUnicode)) return true; + if (filter.Matches(metadataInfo.Source)) return true; + if (filter.Matches(metadataInfo.Tags)) return true; return false; } From 4644c4e7a2c8676df418def1b6630e63253ebd81 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Dec 2023 12:43:32 +0100 Subject: [PATCH 0369/1118] Remove unused class --- .../Multiplayer/Match/ConfirmAbortDialog.cs | 15 --------------- 1 file changed, 15 deletions(-) delete mode 100644 osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs deleted file mode 100644 index 0793981f41..0000000000 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/Match/ConfirmAbortDialog.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Overlays.Dialog; - -namespace osu.Game.Screens.OnlinePlay.Multiplayer.Match -{ - public partial class ConfirmAbortDialog : DangerousActionDialog - { - public ConfirmAbortDialog() - { - HeaderText = "Are you sure you want to abort the match?"; - } - } -} From cda55065e7f1e574bca8f9e8e21687ef2f4d686e Mon Sep 17 00:00:00 2001 From: Rodrigo Pina Date: Tue, 5 Dec 2023 12:56:24 +0000 Subject: [PATCH 0370/1118] Simplified ban order logic Implemented tests to make sure logic works as intended --- .../Screens/TestSceneMapPoolScreen.cs | 107 ++++++++++++++++++ .../Screens/MapPool/MapPoolScreen.cs | 26 +++-- 2 files changed, 126 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 7b2c1ba336..24dfb95317 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -5,6 +5,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; using osu.Framework.Testing; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; @@ -151,6 +152,105 @@ namespace osu.Game.Tournament.Tests.Screens }); } + [Test] + public void TestSingleTeamBan() + { + AddStep("set ban count", () => Ladder.CurrentMatch.Value!.Round.Value!.BanCount.Value = 1); + + AddStep("load some maps", () => + { + Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear(); + + for (int i = 0; i < 4; i++) + addBeatmap(); + }); + + AddStep("update displayed maps", () => Ladder.SplitMapPoolByMods.Value = false); + + AddStep("perform bans", () => + { + var tournamentMaps = screen.ChildrenOfType().ToList(); + + screen.ChildrenOfType().Where(btn => btn.Text == "Red Ban").First().TriggerClick(); + + PerformMapAction(tournamentMaps[0]); + PerformMapAction(tournamentMaps[1]); + }); + + AddAssert("ensure 1 ban per team", () => Ladder.CurrentMatch.Value!.PicksBans.Count() == 2 && Ladder.CurrentMatch.Value!.PicksBans.Last().Team == TeamColour.Blue); + + AddStep("reset match", () => + { + InputManager.UseParentInput = true; + Ladder.CurrentMatch.Value = new TournamentMatch(); + Ladder.CurrentMatch.Value = Ladder.Matches.First(); + Ladder.CurrentMatch.Value.PicksBans.Clear(); + }); + } + + [Test] + public void TestMultipleTeamBans() + { + AddStep("set ban count", () => Ladder.CurrentMatch.Value!.Round.Value!.BanCount.Value = 3); + + AddStep("load some maps", () => + { + Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear(); + + for (int i = 0; i < 12; i++) + addBeatmap(); + }); + + AddStep("update displayed maps", () => Ladder.SplitMapPoolByMods.Value = false); + + AddStep("red team ban", () => + { + var tournamentMaps = screen.ChildrenOfType().ToList(); + + screen.ChildrenOfType().Where(btn => btn.Text == "Red Ban").First().TriggerClick(); + + PerformMapAction(tournamentMaps[0]); + }); + + AddAssert("ensure red team ban", () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team == TeamColour.Red); + + AddStep("blue team bans", () => + { + var tournamentMaps = screen.ChildrenOfType().ToList(); + + PerformMapAction(tournamentMaps[1]); + PerformMapAction(tournamentMaps[2]); + }); + + AddAssert("ensure blue team double ban", () => Ladder.CurrentMatch.Value!.PicksBans.Count(ban => ban.Team == TeamColour.Blue) == 2); + + AddStep("red team bans", () => + { + var tournamentMaps = screen.ChildrenOfType().ToList(); + + PerformMapAction(tournamentMaps[3]); + PerformMapAction(tournamentMaps[4]); + }); + + AddAssert("ensure red team double ban", () => Ladder.CurrentMatch.Value!.PicksBans.Count(ban => ban.Team == TeamColour.Red) == 3); + + AddStep("blue team bans", () => + { + var tournamentMaps = screen.ChildrenOfType().ToList(); + + PerformMapAction(tournamentMaps[5]); + }); + + AddAssert("ensure blue team ban", () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team == TeamColour.Blue); + + AddStep("reset match", () => + { + InputManager.UseParentInput = true; + Ladder.CurrentMatch.Value = new TournamentMatch(); + Ladder.CurrentMatch.Value = Ladder.Matches.First(); + Ladder.CurrentMatch.Value.PicksBans.Clear(); + }); + } private void addBeatmap(string mods = "NM") { Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Add(new RoundBeatmap @@ -159,5 +259,12 @@ namespace osu.Game.Tournament.Tests.Screens Mods = mods }); } + + private void PerformMapAction(TournamentBeatmapPanel map) + { + InputManager.MoveMouseTo(map); + + InputManager.Click(osuTK.Input.MouseButton.Left); + } } } diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 1223fd8464..72134ccb51 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -153,23 +153,35 @@ namespace osu.Game.Tournament.Screens.MapPool const TeamColour roll_winner = TeamColour.Red; //todo: draw from match - var previousBan = CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner; + var previousColour = CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner; - var nextColour = previousBan == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; + TeamColour nextColour; bool hasAllBans = CurrentMatch.Value.PicksBans.Count(p => p.Type == ChoiceType.Ban) >= totalBansRequired; if (!hasAllBans) - // If it's the third ban or later, we need to check if it's the team's first or second ban in a row - nextColour = (CurrentMatch.Value.PicksBans.Count >= 2 ? CurrentMatch.Value.PicksBans[^2]?.Team : previousBan) == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; - - if (hasAllBans && pickType == ChoiceType.Ban) { - // When switching from bans to picks, we don't rotate the team colour. + // Ban phase. + // Switch teams every second ban. + nextColour = CurrentMatch.Value.PicksBans.Count % 2 == 1 + ? getOppositeTeamColour(previousColour) + : previousColour; + } + else if (pickType == ChoiceType.Ban) + { + // Switching from bans to picks - stay with the last team that was banning. nextColour = pickColour; } + else + { + // Pick phase. + // Switch teams every pick. + nextColour = getOppositeTeamColour(previousColour); + } setMode(nextColour, hasAllBans ? ChoiceType.Pick : ChoiceType.Ban); + + TeamColour getOppositeTeamColour(TeamColour colour) => colour == TeamColour.Red ? TeamColour.Blue : TeamColour.Red; } protected override bool OnMouseDown(MouseDownEvent e) From 594ea4da5f5324bb4cce86b071acff13a9a1cb1d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 5 Dec 2023 16:00:20 +0300 Subject: [PATCH 0371/1118] Apply suggested behaviour --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs index 18d4fef5e8..16517a2f36 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -103,13 +103,7 @@ namespace osu.Game.Rulesets.Osu.Mods float z; - if (time < hitObject.StartTime - decelerationTime) - { - double appearTime = hitObject.StartTime - hitObject.TimePreempt; - float fullDistance = decelerationDistance + (float)(baseSpeed * (hitObject.TimePreempt - decelerationTime)); - z = fullDistance - (float)((Math.Max(time, appearTime) - appearTime) * baseSpeed); - } - else if (time < hitObject.StartTime) + if (time < hitObject.StartTime) { double timeOffset = time - (hitObject.StartTime - decelerationTime); double deceleration = (slowSpeed - baseSpeed) / decelerationTime; From 927cfe42570db2edb8e51cbcfd0a729e6b1cb6e5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 19:44:01 +0300 Subject: [PATCH 0372/1118] Fix health processor event leaks --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 20 ++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 2bef6c312f..e044db7bb2 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; @@ -156,8 +157,8 @@ namespace osu.Game.Screens.Play.HUD { base.LoadComplete(); - HealthProcessor.NewJudgement += result => pendingJudgementResult = result; - Current.BindValueChanged(_ => Scheduler.AddOnce(updateCurrent), true); + HealthProcessor.NewJudgement += onNewJudgement; + Current.BindValueChanged(onCurrentChanged, true); // we're about to set `RelativeSizeAxes` depending on the value of `UseRelativeSize`. // setting `RelativeSizeAxes` internally transforms absolute sizing to relative and back to keep the size the same, @@ -170,7 +171,12 @@ namespace osu.Game.Screens.Play.HUD BarHeight.BindValueChanged(_ => updatePath(), true); } - private void updateCurrent() + private void onNewJudgement(JudgementResult result) => pendingJudgementResult = result; + + private void onCurrentChanged(ValueChangedEvent valueChangedEvent) + => Scheduler.AddOnce(updateDisplay); + + private void updateDisplay() { var result = pendingJudgementResult; @@ -333,6 +339,14 @@ namespace osu.Game.Screens.Play.HUD mainBar.Position = healthBarVertices[0]; } + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + if (HealthProcessor.IsNotNull()) + HealthProcessor.NewJudgement -= onNewJudgement; + } + private partial class BackgroundPath : SmoothPath { protected override Color4 ColourAt(float position) From 9496cdf42bf9d024ca40a0622677e60a233693ed Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 19:44:14 +0300 Subject: [PATCH 0373/1118] Add explanatory note for scheduling --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index e044db7bb2..9993ca1ef6 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -174,6 +174,7 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult result) => pendingJudgementResult = result; private void onCurrentChanged(ValueChangedEvent valueChangedEvent) + // schedule display updates one frame later to ensure we know the judgement result causing this change (if there is one). => Scheduler.AddOnce(updateDisplay); private void updateDisplay() From 986f4fa407caf9e5906444b2fa4ec0cca5a4b68b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 21:55:25 +0300 Subject: [PATCH 0374/1118] Add test scenarios for same-frame judgements --- .../Gameplay/TestSceneArgonHealthDisplay.cs | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs index 3197de42d0..d863755a85 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs @@ -83,13 +83,18 @@ namespace osu.Game.Tests.Visual.Gameplay AddRepeatStep(@"increase hp with flash", delegate { healthProcessor.Health.Value += 0.1f; - healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) - { - Type = HitResult.Perfect - }); + applyPerfectHit(); }, 3); } + private void applyPerfectHit() + { + healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) + { + Type = HitResult.Perfect + }); + } + [Test] public void TestLateMissAfterConsequentMisses() { @@ -124,6 +129,29 @@ namespace osu.Game.Tests.Visual.Gameplay }); } + [Test] + public void TestMissThenHitAtSameUpdateFrame() + { + AddUntilStep("wait for health", () => healthDisplay.Current.Value == 1); + AddStep("set half health", () => healthProcessor.Health.Value = 0.5f); + AddStep("apply miss and hit", () => + { + applyMiss(); + applyMiss(); + applyPerfectHit(); + applyPerfectHit(); + }); + AddWaitStep("wait", 3); + AddStep("apply miss and cancel with hit", () => + { + applyMiss(); + applyPerfectHit(); + applyPerfectHit(); + applyPerfectHit(); + applyPerfectHit(); + }); + } + private void applyMiss() { healthProcessor.ApplyResult(new JudgementResult(new HitObject(), new Judgement()) { Type = HitResult.Miss }); From 20fd458fac9f85140328d22948fb33a53d2c2c78 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 21:57:53 +0300 Subject: [PATCH 0375/1118] Perserve miss animation when followed by a hit at same frame --- .../Gameplay/TestSceneArgonHealthDisplay.cs | 3 +++ .../Screens/Play/HUD/ArgonHealthDisplay.cs | 21 ++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs index d863755a85..59819d781f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs @@ -134,6 +134,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddUntilStep("wait for health", () => healthDisplay.Current.Value == 1); AddStep("set half health", () => healthProcessor.Health.Value = 0.5f); + AddStep("apply miss and hit", () => { applyMiss(); @@ -141,7 +142,9 @@ namespace osu.Game.Tests.Visual.Gameplay applyPerfectHit(); applyPerfectHit(); }); + AddWaitStep("wait", 3); + AddStep("apply miss and cancel with hit", () => { applyMiss(); diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 9993ca1ef6..eaaf1c3c14 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -151,7 +151,7 @@ namespace osu.Game.Screens.Play.HUD }; } - private JudgementResult? pendingJudgementResult; + private bool pendingMissAnimation; protected override void LoadComplete() { @@ -171,7 +171,7 @@ namespace osu.Game.Screens.Play.HUD BarHeight.BindValueChanged(_ => updatePath(), true); } - private void onNewJudgement(JudgementResult result) => pendingJudgementResult = result; + private void onNewJudgement(JudgementResult result) => pendingMissAnimation |= !result.IsHit; private void onCurrentChanged(ValueChangedEvent valueChangedEvent) // schedule display updates one frame later to ensure we know the judgement result causing this change (if there is one). @@ -179,22 +179,23 @@ namespace osu.Game.Screens.Play.HUD private void updateDisplay() { - var result = pendingJudgementResult; + double newHealth = Current.Value; - if (Current.Value >= GlowBarValue) + if (newHealth >= GlowBarValue) finishMissDisplay(); - double time = Current.Value > GlowBarValue ? 500 : 250; + double time = newHealth > GlowBarValue ? 500 : 250; // TODO: this should probably use interpolation in update. - this.TransformTo(nameof(HealthBarValue), Current.Value, time, Easing.OutQuint); + this.TransformTo(nameof(HealthBarValue), newHealth, time, Easing.OutQuint); - if (result != null && !result.IsHit) + if (pendingMissAnimation && newHealth < GlowBarValue) triggerMissDisplay(); - else if (!displayingMiss) - this.TransformTo(nameof(GlowBarValue), Current.Value, time, Easing.OutQuint); - pendingJudgementResult = null; + pendingMissAnimation = false; + + if (!displayingMiss) + this.TransformTo(nameof(GlowBarValue), newHealth, time, Easing.OutQuint); } protected override void Update() From c55bfc03ee6f02f8234a80b8b0bf11022e763c34 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 22:06:53 +0300 Subject: [PATCH 0376/1118] Move private method --- .../Gameplay/TestSceneArgonHealthDisplay.cs | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs index 59819d781f..30fb4412f4 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs @@ -87,14 +87,6 @@ namespace osu.Game.Tests.Visual.Gameplay }, 3); } - private void applyPerfectHit() - { - healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) - { - Type = HitResult.Perfect - }); - } - [Test] public void TestLateMissAfterConsequentMisses() { @@ -159,5 +151,13 @@ namespace osu.Game.Tests.Visual.Gameplay { healthProcessor.ApplyResult(new JudgementResult(new HitObject(), new Judgement()) { Type = HitResult.Miss }); } + + private void applyPerfectHit() + { + healthProcessor.ApplyResult(new JudgementResult(new HitCircle(), new OsuJudgement()) + { + Type = HitResult.Perfect + }); + } } } From a0813d18cab2cb5485d5a0f792819d8018ef1189 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 22:47:08 +0300 Subject: [PATCH 0377/1118] `CalculatedTextSize` -> `FontSize` --- osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs | 2 +- osu.Game/Graphics/UserInterface/OsuTextBox.cs | 2 +- osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs index 123854a2dd..0be7b4dc48 100644 --- a/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuPasswordTextBox.cs @@ -23,7 +23,7 @@ namespace osu.Game.Graphics.UserInterface protected override Drawable GetDrawableCharacter(char c) => new FallingDownContainer { AutoSizeAxes = Axes.Both, - Child = new PasswordMaskChar(CalculatedTextSize), + Child = new PasswordMaskChar(FontSize), }; protected override bool AllowUniqueCharacterSamples => false; diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index 04ecfa7e9a..4742da6d0b 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -268,7 +268,7 @@ namespace osu.Game.Graphics.UserInterface protected override Drawable GetDrawableCharacter(char c) => new FallingDownContainer { AutoSizeAxes = Axes.Both, - Child = new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: CalculatedTextSize) }, + Child = new OsuSpriteText { Text = c.ToString(), Font = OsuFont.GetFont(size: FontSize) }, }; protected override Caret CreateCaret() => caret = new OsuCaret diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index 3940bf8bca..131041e706 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -110,7 +110,7 @@ namespace osu.Game.Graphics.UserInterface BackgroundFocused = colourProvider.Background4; BackgroundUnfocused = colourProvider.Background4; - Placeholder.Font = OsuFont.GetFont(size: CalculatedTextSize, weight: FontWeight.SemiBold); + Placeholder.Font = OsuFont.GetFont(size: FontSize, weight: FontWeight.SemiBold); PlaceholderText = CommonStrings.InputSearch; CornerRadius = corner_radius; From b8b82f890172cd2a6dd70e57ad6ecf591dca073c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 22:45:46 +0300 Subject: [PATCH 0378/1118] Handle back action in `OsuDropdown` rather than menu --- .../Graphics/UserInterface/OsuDropdown.cs | 35 +++++++++---------- 1 file changed, 16 insertions(+), 19 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index b530172f3e..7a052c2298 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -22,7 +22,7 @@ using osuTK.Graphics; namespace osu.Game.Graphics.UserInterface { - public partial class OsuDropdown : Dropdown + public partial class OsuDropdown : Dropdown, IKeyBindingHandler { private const float corner_radius = 5; @@ -30,9 +30,23 @@ namespace osu.Game.Graphics.UserInterface protected override DropdownMenu CreateMenu() => new OsuDropdownMenu(); + public bool OnPressed(KeyBindingPressEvent e) + { + if (e.Repeat) return false; + + if (e.Action == GlobalAction.Back) + return Back(); + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + #region OsuDropdownMenu - protected partial class OsuDropdownMenu : DropdownMenu, IKeyBindingHandler + protected partial class OsuDropdownMenu : DropdownMenu { public override bool HandleNonPositionalInput => State == MenuState.Open; @@ -276,23 +290,6 @@ namespace osu.Game.Graphics.UserInterface } #endregion - - public bool OnPressed(KeyBindingPressEvent e) - { - if (e.Repeat) return false; - - if (e.Action == GlobalAction.Back) - { - State = MenuState.Closed; - return true; - } - - return false; - } - - public void OnReleased(KeyBindingReleaseEvent e) - { - } } #endregion From a67df766543cb2e967695675434885edd0431d7c Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 22:50:05 +0300 Subject: [PATCH 0379/1118] Add test coverage --- .../UserInterface/TestSceneOsuDropdown.cs | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuDropdown.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuDropdown.cs index b0548d7e9f..1678890b73 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOsuDropdown.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOsuDropdown.cs @@ -1,9 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; +using NUnit.Framework; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Framework.Input.States; +using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Graphics.UserInterface; +using osu.Game.Input.Bindings; namespace osu.Game.Tests.Visual.UserInterface { @@ -13,8 +21,29 @@ namespace osu.Game.Tests.Visual.UserInterface new OsuEnumDropdown { Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Origin = Anchor.TopCentre, Width = 150 }; + + [Test] + // todo: this can be written much better if ThemeComparisonTestScene has a manual input manager + public void TestBackAction() + { + AddStep("open", () => dropdown().ChildrenOfType().Single().Open()); + AddStep("press back", () => dropdown().OnPressed(new KeyBindingPressEvent(new InputState(), GlobalAction.Back))); + AddAssert("closed", () => dropdown().ChildrenOfType().Single().State == MenuState.Closed); + + AddStep("open", () => dropdown().ChildrenOfType().Single().Open()); + AddStep("type something", () => dropdown().ChildrenOfType().Single().SearchTerm.Value = "something"); + AddAssert("search bar visible", () => dropdown().ChildrenOfType().Single().State.Value == Visibility.Visible); + AddStep("press back", () => dropdown().OnPressed(new KeyBindingPressEvent(new InputState(), GlobalAction.Back))); + AddAssert("text clear", () => dropdown().ChildrenOfType().Single().SearchTerm.Value == string.Empty); + AddAssert("search bar hidden", () => dropdown().ChildrenOfType().Single().State.Value == Visibility.Hidden); + AddAssert("still open", () => dropdown().ChildrenOfType().Single().State == MenuState.Open); + AddStep("press back", () => dropdown().OnPressed(new KeyBindingPressEvent(new InputState(), GlobalAction.Back))); + AddAssert("closed", () => dropdown().ChildrenOfType().Single().State == MenuState.Closed); + + OsuEnumDropdown dropdown() => this.ChildrenOfType>().First(); + } } } From d92db8059e1b8804af4a88d9450cd837a0e97d6d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 22:49:43 +0300 Subject: [PATCH 0380/1118] Add new dropdown properties to `SettingsDropdown` --- osu.Game/Overlays/Settings/SettingsDropdown.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game/Overlays/Settings/SettingsDropdown.cs b/osu.Game/Overlays/Settings/SettingsDropdown.cs index 5798d02e03..ec69224bb6 100644 --- a/osu.Game/Overlays/Settings/SettingsDropdown.cs +++ b/osu.Game/Overlays/Settings/SettingsDropdown.cs @@ -16,6 +16,18 @@ namespace osu.Game.Overlays.Settings { protected new OsuDropdown Control => (OsuDropdown)base.Control; + public bool AlwaysShowSearchBar + { + get => Control.AlwaysShowSearchBar; + set => Control.AlwaysShowSearchBar = value; + } + + public bool AllowNonContiguousMatching + { + get => Control.AllowNonContiguousMatching; + set => Control.AllowNonContiguousMatching = value; + } + public IEnumerable Items { get => Control.Items; From ee2e176082834d34350d2c9183d825b5468d7333 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 22:52:48 +0300 Subject: [PATCH 0381/1118] Add osu! dropdown search bar implementation --- .../Graphics/UserInterface/OsuDropdown.cs | 79 ++++++++++++++++++- 1 file changed, 75 insertions(+), 4 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 7a052c2298..96604275ea 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -352,11 +352,82 @@ namespace osu.Game.Graphics.UserInterface AddInternal(new HoverClickSounds()); } - [BackgroundDependencyLoader(true)] - private void load(OverlayColourProvider? colourProvider, OsuColour colours) + [Resolved] + private OverlayColourProvider? colourProvider { get; set; } + + [Resolved] + private OsuColour colours { get; set; } = null!; + + protected override void LoadComplete() { - BackgroundColour = colourProvider?.Background5 ?? Color4.Black.Opacity(0.5f); - BackgroundColourHover = colourProvider?.Light4 ?? colours.PinkDarker; + base.LoadComplete(); + + SearchBar.State.ValueChanged += _ => updateColour(); + updateColour(); + } + + protected override bool OnHover(HoverEvent e) + { + updateColour(); + return false; + } + + protected override void OnHoverLost(HoverLostEvent e) + { + updateColour(); + } + + private void updateColour() + { + bool hovered = Enabled.Value && IsHovered; + var hoveredColour = colourProvider?.Light4 ?? colours.PinkDarker; + var unhoveredColour = colourProvider?.Background5 ?? Color4.Black.Opacity(0.5f); + + if (SearchBar.State.Value == Visibility.Visible) + { + Icon.Colour = hovered ? hoveredColour.Lighten(0.5f) : Colour4.White; + Background.Colour = unhoveredColour; + } + else + { + Icon.Colour = Color4.White; + Background.Colour = hovered ? hoveredColour : unhoveredColour; + } + } + + protected override DropdownSearchBar CreateSearchBar() => new OsuDropdownSearchBar + { + Padding = new MarginPadding { Right = 36 }, + }; + + private partial class OsuDropdownSearchBar : DropdownSearchBar + { + protected override void PopIn() => this.FadeIn(); + + protected override void PopOut() => this.FadeOut(); + + protected override TextBox CreateTextBox() => new DropdownSearchTextBox + { + FontSize = OsuFont.Default.Size, + }; + + private partial class DropdownSearchTextBox : SearchTextBox + { + public DropdownSearchTextBox() + { + TextContainer.Margin = new MarginPadding { Top = 4f }; + } + + public override bool OnPressed(KeyBindingPressEvent e) + { + if (e.Action == GlobalAction.Back) + // this method is blocking Dropdown from receiving the back action, despite this text box residing in a separate input manager. + // to fix this properly, a local global action container needs to be added as well, but for simplicity, just don't handle the back action here. + return false; + + return base.OnPressed(e); + } + } } } } From d4aedaf22d730cf03eaf199c06df66939b86f9a8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 22:53:24 +0300 Subject: [PATCH 0382/1118] Always show search bar in skin dropdown for visibility --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index e997e70157..40c54b26a0 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -57,9 +57,10 @@ namespace osu.Game.Overlays.Settings.Sections { skinDropdown = new SkinSettingsDropdown { + AlwaysShowSearchBar = true, LabelText = SkinSettingsStrings.CurrentSkin, Current = skins.CurrentSkinInfo, - Keywords = new[] { @"skins" } + Keywords = new[] { @"skins" }, }, new SettingsButton { From f45336a4f65126fbf258df1627074a089ce51852 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 5 Dec 2023 22:53:35 +0300 Subject: [PATCH 0383/1118] Make skin dropdown searching non-contiguous --- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 40c54b26a0..1d057f42c0 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -58,6 +58,7 @@ namespace osu.Game.Overlays.Settings.Sections skinDropdown = new SkinSettingsDropdown { AlwaysShowSearchBar = true, + AllowNonContiguousMatching = true, LabelText = SkinSettingsStrings.CurrentSkin, Current = skins.CurrentSkinInfo, Keywords = new[] { @"skins" }, From 2c7db61a5c0263ce58e8cd5134acfd2df727e9ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Dec 2023 21:19:35 +0100 Subject: [PATCH 0384/1118] Improve test --- .../Screens/TestSceneMapPoolScreen.cs | 137 ++++++++++++------ 1 file changed, 96 insertions(+), 41 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 24dfb95317..b99735bda4 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -5,7 +5,6 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; -using osu.Framework.Input.Events; using osu.Framework.Testing; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; @@ -161,23 +160,51 @@ namespace osu.Game.Tournament.Tests.Screens { Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear(); - for (int i = 0; i < 4; i++) + for (int i = 0; i < 5; i++) addBeatmap(); }); AddStep("update displayed maps", () => Ladder.SplitMapPoolByMods.Value = false); - AddStep("perform bans", () => - { - var tournamentMaps = screen.ChildrenOfType().ToList(); + AddStep("start bans from blue team", () => screen.ChildrenOfType().First(btn => btn.Text == "Blue Ban").TriggerClick()); + AddStep("ban map", () => clickBeatmapPanel(0)); + AddAssert("one ban registered", () => Ladder.CurrentMatch.Value!.PicksBans, () => Has.Count.EqualTo(1)); + AddAssert("ban was blue's", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Blue), + () => Is.EqualTo(1)); - screen.ChildrenOfType().Where(btn => btn.Text == "Red Ban").First().TriggerClick(); + AddStep("ban map", () => clickBeatmapPanel(1)); + AddAssert("two bans registered", () => Ladder.CurrentMatch.Value!.PicksBans, () => Has.Count.EqualTo(2)); + AddAssert("one ban for red team", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Red), + () => Is.EqualTo(1)); + AddAssert("one ban for blue team", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Blue), + () => Is.EqualTo(1)); - PerformMapAction(tournamentMaps[0]); - PerformMapAction(tournamentMaps[1]); - }); + AddStep("pick map", () => clickBeatmapPanel(2)); + AddAssert("one pick registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Pick), + () => Is.EqualTo(1)); + AddAssert("pick was red's", + () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team, + () => Is.EqualTo(TeamColour.Red)); - AddAssert("ensure 1 ban per team", () => Ladder.CurrentMatch.Value!.PicksBans.Count() == 2 && Ladder.CurrentMatch.Value!.PicksBans.Last().Team == TeamColour.Blue); + AddStep("pick map", () => clickBeatmapPanel(3)); + AddAssert("two picks registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Pick), + () => Is.EqualTo(2)); + AddAssert("pick was blue's", + () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team, + () => Is.EqualTo(TeamColour.Blue)); + + AddStep("pick map", () => clickBeatmapPanel(4)); + AddAssert("three picks registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Pick), + () => Is.EqualTo(3)); + AddAssert("pick was red's", + () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team, + () => Is.EqualTo(TeamColour.Red)); AddStep("reset match", () => { @@ -203,45 +230,73 @@ namespace osu.Game.Tournament.Tests.Screens AddStep("update displayed maps", () => Ladder.SplitMapPoolByMods.Value = false); - AddStep("red team ban", () => + AddStep("start bans with red team", () => screen.ChildrenOfType().First(btn => btn.Text == "Red Ban").TriggerClick()); + + AddStep("first ban", () => clickBeatmapPanel(0)); + AddAssert("red ban registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Red), + () => Is.EqualTo(1)); + + AddStep("ban two more maps", () => { - var tournamentMaps = screen.ChildrenOfType().ToList(); - - screen.ChildrenOfType().Where(btn => btn.Text == "Red Ban").First().TriggerClick(); - - PerformMapAction(tournamentMaps[0]); + clickBeatmapPanel(1); + clickBeatmapPanel(2); }); - AddAssert("ensure red team ban", () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team == TeamColour.Red); + AddAssert("three bans registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban), + () => Is.EqualTo(3)); + AddAssert("both new bans for blue team", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Blue), + () => Is.EqualTo(2)); - AddStep("blue team bans", () => + AddStep("ban two more maps", () => { - var tournamentMaps = screen.ChildrenOfType().ToList(); - - PerformMapAction(tournamentMaps[1]); - PerformMapAction(tournamentMaps[2]); + clickBeatmapPanel(3); + clickBeatmapPanel(4); }); - AddAssert("ensure blue team double ban", () => Ladder.CurrentMatch.Value!.PicksBans.Count(ban => ban.Team == TeamColour.Blue) == 2); + AddAssert("five bans registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban), + () => Is.EqualTo(5)); + AddAssert("both new bans for red team", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Red), + () => Is.EqualTo(3)); - AddStep("red team bans", () => - { - var tournamentMaps = screen.ChildrenOfType().ToList(); + AddStep("ban last map", () => clickBeatmapPanel(5)); + AddAssert("six bans registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban), + () => Is.EqualTo(6)); + AddAssert("red banned three", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Red), + () => Is.EqualTo(3)); + AddAssert("blue banned three", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Blue), + () => Is.EqualTo(3)); - PerformMapAction(tournamentMaps[3]); - PerformMapAction(tournamentMaps[4]); - }); + AddStep("pick map", () => clickBeatmapPanel(6)); + AddAssert("one pick registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Pick), + () => Is.EqualTo(1)); + AddAssert("pick was blue's", + () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team, + () => Is.EqualTo(TeamColour.Blue)); - AddAssert("ensure red team double ban", () => Ladder.CurrentMatch.Value!.PicksBans.Count(ban => ban.Team == TeamColour.Red) == 3); + AddStep("pick map", () => clickBeatmapPanel(7)); + AddAssert("two picks registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Pick), + () => Is.EqualTo(2)); + AddAssert("pick was red's", + () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team, + () => Is.EqualTo(TeamColour.Red)); - AddStep("blue team bans", () => - { - var tournamentMaps = screen.ChildrenOfType().ToList(); - - PerformMapAction(tournamentMaps[5]); - }); - - AddAssert("ensure blue team ban", () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team == TeamColour.Blue); + AddStep("pick map", () => clickBeatmapPanel(8)); + AddAssert("three picks registered", + () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Pick), + () => Is.EqualTo(3)); + AddAssert("pick was blue's", + () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team, + () => Is.EqualTo(TeamColour.Blue)); AddStep("reset match", () => { @@ -251,6 +306,7 @@ namespace osu.Game.Tournament.Tests.Screens Ladder.CurrentMatch.Value.PicksBans.Clear(); }); } + private void addBeatmap(string mods = "NM") { Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Add(new RoundBeatmap @@ -260,10 +316,9 @@ namespace osu.Game.Tournament.Tests.Screens }); } - private void PerformMapAction(TournamentBeatmapPanel map) + private void clickBeatmapPanel(int index) { - InputManager.MoveMouseTo(map); - + InputManager.MoveMouseTo(screen.ChildrenOfType().ElementAt(index)); InputManager.Click(osuTK.Input.MouseButton.Left); } } From 7392cc2fda72dd22d1daa767dab4d507c0073688 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Dec 2023 21:49:04 +0100 Subject: [PATCH 0385/1118] Fix headless test failures due to input handling idiosyncrasies --- .../Screens/TestSceneMapPoolScreen.cs | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index b99735bda4..c459de1e43 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -9,6 +9,7 @@ using osu.Framework.Testing; using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.MapPool; +using osuTK; namespace osu.Game.Tournament.Tests.Screens { @@ -19,7 +20,7 @@ namespace osu.Game.Tournament.Tests.Screens [BackgroundDependencyLoader] private void load() { - Add(screen = new MapPoolScreen { Width = 0.7f }); + Add(screen = new TestMapPoolScreen { Width = 0.7f }); } [SetUp] @@ -208,7 +209,6 @@ namespace osu.Game.Tournament.Tests.Screens AddStep("reset match", () => { - InputManager.UseParentInput = true; Ladder.CurrentMatch.Value = new TournamentMatch(); Ladder.CurrentMatch.Value = Ladder.Matches.First(); Ladder.CurrentMatch.Value.PicksBans.Clear(); @@ -300,7 +300,6 @@ namespace osu.Game.Tournament.Tests.Screens AddStep("reset match", () => { - InputManager.UseParentInput = true; Ladder.CurrentMatch.Value = new TournamentMatch(); Ladder.CurrentMatch.Value = Ladder.Matches.First(); Ladder.CurrentMatch.Value.PicksBans.Clear(); @@ -321,5 +320,16 @@ namespace osu.Game.Tournament.Tests.Screens InputManager.MoveMouseTo(screen.ChildrenOfType().ElementAt(index)); InputManager.Click(osuTK.Input.MouseButton.Left); } + + private partial class TestMapPoolScreen : MapPoolScreen + { + // this is a bit of a test-specific workaround. + // the way pick/ban is implemented is a bit funky; the screen itself is what handles the mouse there, + // rather than the beatmap panels themselves. + // in some extreme situations headless it may turn out that the panels overflow the screen, + // and as such picking stops working anymore outside of the bounds of the screen drawable. + // this override makes it so the screen sees all of the input at all times, making that impossible to happen. + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + } } } From 43701c5d47154e73012f06c26b08938c9517d1a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 5 Dec 2023 21:49:32 +0100 Subject: [PATCH 0386/1118] Prefer using statement to fully qualified name --- osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index c459de1e43..2ef290ff49 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -10,6 +10,7 @@ using osu.Game.Tournament.Components; using osu.Game.Tournament.Models; using osu.Game.Tournament.Screens.MapPool; using osuTK; +using osuTK.Input; namespace osu.Game.Tournament.Tests.Screens { @@ -318,7 +319,7 @@ namespace osu.Game.Tournament.Tests.Screens private void clickBeatmapPanel(int index) { InputManager.MoveMouseTo(screen.ChildrenOfType().ElementAt(index)); - InputManager.Click(osuTK.Input.MouseButton.Left); + InputManager.Click(MouseButton.Left); } private partial class TestMapPoolScreen : MapPoolScreen From 07f9f5c6d842d3c7c564f96576682b6fb54c50b4 Mon Sep 17 00:00:00 2001 From: POeticPotatoes Date: Wed, 6 Dec 2023 06:33:25 +0800 Subject: [PATCH 0387/1118] Remove hover checks for mod-copying menu item --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 136c9cc8e7..a76f4ae955 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -421,7 +421,7 @@ namespace osu.Game.Online.Leaderboards { List items = new List(); - if (Score.Mods.Length > 0 && modsContainer.Any(s => s.IsHovered) && songSelect != null) + if (Score.Mods.Length > 0 && songSelect != null) items.Add(new OsuMenuItem("Use these mods", MenuItemType.Highlighted, () => songSelect.Mods.Value = Score.Mods)); if (Score.Files.Count > 0) From 8286d3896f4240ad920f94bf7e5735230d90d588 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 11:17:32 +0900 Subject: [PATCH 0388/1118] Fix searching at song select matching incorrect ruleset Regressed with https://github.com/ppy/osu/pull/25679. --- .../SongSelect/TestScenePlaySongSelect.cs | 18 ++++++++++++++++++ .../Screens/Select/Carousel/CarouselBeatmap.cs | 2 ++ 2 files changed, 20 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 7313bde8fe..84750d4c16 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -580,6 +580,24 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("start not requested", () => !startRequested); } + [Test] + public void TestSearchTextWithRulesetCriteria() + { + createSongSelect(); + + addRulesetImportStep(0); + + AddStep("disallow convert display", () => config.SetValue(OsuSetting.ShowConvertedBeatmaps, false)); + + AddUntilStep("has selection", () => songSelect!.Carousel.SelectedBeatmapInfo != null); + + AddStep("set filter to match all", () => songSelect!.FilterControl.CurrentTextSearch.Value = "Some"); + + changeRuleset(1); + + AddUntilStep("has no selection", () => songSelect!.Carousel.SelectedBeatmapInfo == null); + } + [TestCase(false)] [TestCase(true)] public void TestExternalBeatmapChangeWhileFiltered(bool differentRuleset) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs index 45594bd0e8..1ca4b371c3 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmap.cs @@ -41,6 +41,8 @@ namespace osu.Game.Screens.Select.Carousel return match; } + if (!match) return false; + if (criteria.SearchTerms.Length > 0) { match = BeatmapInfo.Match(criteria.SearchTerms); From ac67320b61d1459ab367e46c9100ab33379203a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 11:50:43 +0900 Subject: [PATCH 0389/1118] Refactor for readability --- .../Screens/TestSceneMapPoolScreen.cs | 2 +- .../Screens/MapPool/MapPoolScreen.cs | 23 +++++++------------ 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 2ef290ff49..2911ba6acb 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -154,7 +154,7 @@ namespace osu.Game.Tournament.Tests.Screens } [Test] - public void TestSingleTeamBan() + public void TestPickBanOrder() { AddStep("set ban count", () => Ladder.CurrentMatch.Value!.Round.Value!.BanCount.Value = 1); diff --git a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs index 72134ccb51..665d3c131a 100644 --- a/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs +++ b/osu.Game.Tournament/Screens/MapPool/MapPoolScreen.cs @@ -151,9 +151,7 @@ namespace osu.Game.Tournament.Screens.MapPool int totalBansRequired = CurrentMatch.Value.Round.Value.BanCount.Value * 2; - const TeamColour roll_winner = TeamColour.Red; //todo: draw from match - - var previousColour = CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? roll_winner; + TeamColour lastPickColour = CurrentMatch.Value.PicksBans.LastOrDefault()?.Team ?? TeamColour.Red; TeamColour nextColour; @@ -161,22 +159,17 @@ namespace osu.Game.Tournament.Screens.MapPool if (!hasAllBans) { - // Ban phase. - // Switch teams every second ban. + // Ban phase: switch teams every second ban. nextColour = CurrentMatch.Value.PicksBans.Count % 2 == 1 - ? getOppositeTeamColour(previousColour) - : previousColour; - } - else if (pickType == ChoiceType.Ban) - { - // Switching from bans to picks - stay with the last team that was banning. - nextColour = pickColour; + ? getOppositeTeamColour(lastPickColour) + : lastPickColour; } else { - // Pick phase. - // Switch teams every pick. - nextColour = getOppositeTeamColour(previousColour); + // Pick phase : switch teams every pick, except for the first pick which generally goes to the team that placed the last ban. + nextColour = pickType == ChoiceType.Pick + ? getOppositeTeamColour(lastPickColour) + : lastPickColour; } setMode(nextColour, hasAllBans ? ChoiceType.Pick : ChoiceType.Ban); From 1d1b85551000586cda3aef40ea26d447242bd754 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 11:57:04 +0900 Subject: [PATCH 0390/1118] Refactor test for readability --- .../Screens/TestSceneMapPoolScreen.cs | 47 +++++++------------ 1 file changed, 18 insertions(+), 29 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 2911ba6acb..dcf9dc47b9 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -169,44 +169,26 @@ namespace osu.Game.Tournament.Tests.Screens AddStep("update displayed maps", () => Ladder.SplitMapPoolByMods.Value = false); AddStep("start bans from blue team", () => screen.ChildrenOfType().First(btn => btn.Text == "Blue Ban").TriggerClick()); + AddStep("ban map", () => clickBeatmapPanel(0)); - AddAssert("one ban registered", () => Ladder.CurrentMatch.Value!.PicksBans, () => Has.Count.EqualTo(1)); - AddAssert("ban was blue's", - () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Blue), - () => Is.EqualTo(1)); + checkTotalPickBans(1); + checkLastPick(ChoiceType.Ban, TeamColour.Blue); AddStep("ban map", () => clickBeatmapPanel(1)); - AddAssert("two bans registered", () => Ladder.CurrentMatch.Value!.PicksBans, () => Has.Count.EqualTo(2)); - AddAssert("one ban for red team", - () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Red), - () => Is.EqualTo(1)); - AddAssert("one ban for blue team", - () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Ban && pb.Team == TeamColour.Blue), - () => Is.EqualTo(1)); + checkTotalPickBans(2); + checkLastPick(ChoiceType.Ban, TeamColour.Red); AddStep("pick map", () => clickBeatmapPanel(2)); - AddAssert("one pick registered", - () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Pick), - () => Is.EqualTo(1)); - AddAssert("pick was red's", - () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team, - () => Is.EqualTo(TeamColour.Red)); + checkTotalPickBans(3); + checkLastPick(ChoiceType.Pick, TeamColour.Red); AddStep("pick map", () => clickBeatmapPanel(3)); - AddAssert("two picks registered", - () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Pick), - () => Is.EqualTo(2)); - AddAssert("pick was blue's", - () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team, - () => Is.EqualTo(TeamColour.Blue)); + checkTotalPickBans(4); + checkLastPick(ChoiceType.Pick, TeamColour.Blue); AddStep("pick map", () => clickBeatmapPanel(4)); - AddAssert("three picks registered", - () => Ladder.CurrentMatch.Value!.PicksBans.Count(pb => pb.Type == ChoiceType.Pick), - () => Is.EqualTo(3)); - AddAssert("pick was red's", - () => Ladder.CurrentMatch.Value!.PicksBans.Last().Team, - () => Is.EqualTo(TeamColour.Red)); + checkTotalPickBans(5); + checkLastPick(ChoiceType.Pick, TeamColour.Red); AddStep("reset match", () => { @@ -214,6 +196,13 @@ namespace osu.Game.Tournament.Tests.Screens Ladder.CurrentMatch.Value = Ladder.Matches.First(); Ladder.CurrentMatch.Value.PicksBans.Clear(); }); + + void checkTotalPickBans(int expected) => AddAssert($"total pickbans is {expected}", () => Ladder.CurrentMatch.Value!.PicksBans, () => Has.Count.EqualTo(expected)); + + void checkLastPick(ChoiceType expectedChoice, TeamColour expectedColour) => + AddAssert($"last choice was {expectedChoice} by {expectedColour}", + () => Ladder.CurrentMatch.Value!.PicksBans.Select(pb => (pb.Type, pb.Team)).Last(), + () => Is.EqualTo((expectedChoice, expectedColour))); } [Test] From 73aaa0406a35786e07ac8c11b07e018522303d85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 12:00:32 +0900 Subject: [PATCH 0391/1118] Add test coverage of multiple bans order --- .../Screens/TestSceneMapPoolScreen.cs | 61 ++++++++++++++++--- 1 file changed, 53 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index dcf9dc47b9..5e535e2749 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -25,7 +25,14 @@ namespace osu.Game.Tournament.Tests.Screens } [SetUp] - public void SetUp() => Schedule(() => Ladder.SplitMapPoolByMods.Value = true); + public void SetUp() => Schedule(() => + { + Ladder.SplitMapPoolByMods.Value = true; + + Ladder.CurrentMatch.Value = new TournamentMatch(); + Ladder.CurrentMatch.Value = Ladder.Matches.First(); + Ladder.CurrentMatch.Value.PicksBans.Clear(); + }); [Test] public void TestFewMaps() @@ -153,6 +160,44 @@ namespace osu.Game.Tournament.Tests.Screens }); } + [Test] + public void TestBanOrderMultipleBans() + { + AddStep("set ban count", () => Ladder.CurrentMatch.Value!.Round.Value!.BanCount.Value = 2); + + AddStep("load some maps", () => + { + Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Clear(); + + for (int i = 0; i < 5; i++) + addBeatmap(); + }); + + AddStep("update displayed maps", () => Ladder.SplitMapPoolByMods.Value = false); + + AddStep("start bans from blue team", () => screen.ChildrenOfType().First(btn => btn.Text == "Blue Ban").TriggerClick()); + + AddStep("ban map", () => clickBeatmapPanel(0)); + checkTotalPickBans(1); + checkLastPick(ChoiceType.Ban, TeamColour.Blue); + + AddStep("ban map", () => clickBeatmapPanel(1)); + checkTotalPickBans(2); + checkLastPick(ChoiceType.Ban, TeamColour.Red); + + AddStep("ban map", () => clickBeatmapPanel(2)); + checkTotalPickBans(3); + checkLastPick(ChoiceType.Ban, TeamColour.Red); + + AddStep("pick map", () => clickBeatmapPanel(3)); + checkTotalPickBans(4); + checkLastPick(ChoiceType.Ban, TeamColour.Blue); + + AddStep("pick map", () => clickBeatmapPanel(4)); + checkTotalPickBans(5); + checkLastPick(ChoiceType.Pick, TeamColour.Blue); + } + [Test] public void TestPickBanOrder() { @@ -196,13 +241,6 @@ namespace osu.Game.Tournament.Tests.Screens Ladder.CurrentMatch.Value = Ladder.Matches.First(); Ladder.CurrentMatch.Value.PicksBans.Clear(); }); - - void checkTotalPickBans(int expected) => AddAssert($"total pickbans is {expected}", () => Ladder.CurrentMatch.Value!.PicksBans, () => Has.Count.EqualTo(expected)); - - void checkLastPick(ChoiceType expectedChoice, TeamColour expectedColour) => - AddAssert($"last choice was {expectedChoice} by {expectedColour}", - () => Ladder.CurrentMatch.Value!.PicksBans.Select(pb => (pb.Type, pb.Team)).Last(), - () => Is.EqualTo((expectedChoice, expectedColour))); } [Test] @@ -296,6 +334,13 @@ namespace osu.Game.Tournament.Tests.Screens }); } + private void checkTotalPickBans(int expected) => AddAssert($"total pickbans is {expected}", () => Ladder.CurrentMatch.Value!.PicksBans, () => Has.Count.EqualTo(expected)); + + private void checkLastPick(ChoiceType expectedChoice, TeamColour expectedColour) => + AddAssert($"last choice was {expectedChoice} by {expectedColour}", + () => Ladder.CurrentMatch.Value!.PicksBans.Select(pb => (pb.Type, pb.Team)).Last(), + () => Is.EqualTo((expectedChoice, expectedColour))); + private void addBeatmap(string mods = "NM") { Ladder.CurrentMatch.Value!.Round.Value!.Beatmaps.Add(new RoundBeatmap From 49ca1ccb22792b973f49d409eec464de1ab75d74 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 12:03:54 +0900 Subject: [PATCH 0392/1118] Simplify state reset in test scene --- .../Screens/TestSceneMapPoolScreen.cs | 45 +++++++------------ 1 file changed, 17 insertions(+), 28 deletions(-) diff --git a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs index 5e535e2749..7e008a6897 100644 --- a/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs +++ b/osu.Game.Tournament.Tests/Screens/TestSceneMapPoolScreen.cs @@ -24,14 +24,24 @@ namespace osu.Game.Tournament.Tests.Screens Add(screen = new TestMapPoolScreen { Width = 0.7f }); } - [SetUp] - public void SetUp() => Schedule(() => + [SetUpSteps] + public override void SetUpSteps() + { + AddStep("reset state", resetState); + } + + private void resetState() { Ladder.SplitMapPoolByMods.Value = true; Ladder.CurrentMatch.Value = new TournamentMatch(); Ladder.CurrentMatch.Value = Ladder.Matches.First(); Ladder.CurrentMatch.Value.PicksBans.Clear(); + } + + [SetUp] + public void SetUp() => Schedule(() => + { }); [Test] @@ -48,7 +58,6 @@ namespace osu.Game.Tournament.Tests.Screens AddStep("reset match", () => { Ladder.CurrentMatch.Value = new TournamentMatch(); - Ladder.CurrentMatch.Value = Ladder.Matches.First(); }); assertTwoWide(); @@ -65,11 +74,7 @@ namespace osu.Game.Tournament.Tests.Screens addBeatmap(); }); - AddStep("reset match", () => - { - Ladder.CurrentMatch.Value = new TournamentMatch(); - Ladder.CurrentMatch.Value = Ladder.Matches.First(); - }); + AddStep("reset state", resetState); assertTwoWide(); } @@ -85,11 +90,7 @@ namespace osu.Game.Tournament.Tests.Screens addBeatmap(); }); - AddStep("reset match", () => - { - Ladder.CurrentMatch.Value = new TournamentMatch(); - Ladder.CurrentMatch.Value = Ladder.Matches.First(); - }); + AddStep("reset state", resetState); assertThreeWide(); } @@ -105,11 +106,7 @@ namespace osu.Game.Tournament.Tests.Screens addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM"); }); - AddStep("reset match", () => - { - Ladder.CurrentMatch.Value = new TournamentMatch(); - Ladder.CurrentMatch.Value = Ladder.Matches.First(); - }); + AddStep("reset state", resetState); assertTwoWide(); } @@ -131,11 +128,7 @@ namespace osu.Game.Tournament.Tests.Screens addBeatmap(i > 4 ? Ruleset.Value.CreateInstance().AllMods.ElementAt(i).Acronym : "NM"); }); - AddStep("reset match", () => - { - Ladder.CurrentMatch.Value = new TournamentMatch(); - Ladder.CurrentMatch.Value = Ladder.Matches.First(); - }); + AddStep("reset state", resetState); assertThreeWide(); } @@ -153,11 +146,7 @@ namespace osu.Game.Tournament.Tests.Screens AddStep("disable splitting map pool by mods", () => Ladder.SplitMapPoolByMods.Value = false); - AddStep("reset match", () => - { - Ladder.CurrentMatch.Value = new TournamentMatch(); - Ladder.CurrentMatch.Value = Ladder.Matches.First(); - }); + AddStep("reset state", resetState); } [Test] From 639fac2d49e8d34292bd1487224b31edbaa1fee9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 12:09:33 +0900 Subject: [PATCH 0393/1118] Fix being able to change ruleset / beatmap when opening skin editor from main menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit No tests because it would be silly to test this – it's already a well established behaviour and was just initialised incorrectly. --- osu.Game/Screens/Play/Player.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1c97efcff7..48411e9c87 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -59,6 +59,10 @@ namespace osu.Game.Screens.Play protected override bool PlayExitSound => !isRestarting; + public override bool DisallowExternalBeatmapRulesetChanges => true; + + public override bool? AllowGlobalTrackControl => false; + protected override UserActivity InitialActivity => new UserActivity.InSoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); public override float BackgroundParallaxAmount => 0.1f; From 2c44ca191592286b60f311fb10ab6d14fdfec4ee Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 5 Dec 2023 15:51:59 +0900 Subject: [PATCH 0394/1118] Add more test beatmaps Move test files to Catch.Tests project --- .../CatchBeatmapConversionTest.cs | 25 +- .../Beatmaps/103019-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/103019.osu | 329 ++++ .../Beatmaps/104973-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/104973.osu | 491 ++++++ .../Beatmaps/1284935-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/1284935.osu | 210 +++ .../Beatmaps/1431386-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/1431386.osu | 560 +++++++ .../Beatmaps/1597806-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/1597806.osu | 152 ++ .../Beatmaps/2190499-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/2190499.osu | 977 +++++++++++ .../Beatmaps/2571731-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/2571731.osu | 277 ++++ .../Beatmaps/2768615-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/2768615.osu | 200 +++ .../Beatmaps/2781126-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/2781126.osu | 908 +++++++++++ .../Beatmaps/3152510-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/3152510.osu | 468 ++++++ .../Beatmaps/3227428-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/3227428.osu | 142 ++ .../Beatmaps/3524302-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/3524302.osu | 889 ++++++++++ .../Beatmaps/3644427-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/3644427.osu | 1450 +++++++++++++++++ .../Beatmaps/3689906-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/3689906.osu | 942 +++++++++++ .../Beatmaps/37902-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/37902.osu | 230 +++ .../Beatmaps/39206-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/39206.osu | 524 ++++++ .../Beatmaps/3949367-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/3949367.osu | 832 ++++++++++ .../Beatmaps/42587-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/42587.osu | 528 ++++++ .../Beatmaps/50859-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/50859.osu | 290 ++++ .../Beatmaps/75858-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/75858.osu | 417 +++++ .../Beatmaps/871815-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/871815.osu | 165 ++ .../Beatmaps/basic-expected-conversion.json | 0 .../basic-hyperdash-expected-conversion.json | 0 .../Testing/Beatmaps/basic-hyperdash.osu | 0 .../Resources/Testing/Beatmaps/basic.osu | 54 +- .../Testing/Beatmaps/diffcalc-test.osu | 0 ...ock-repeat-slider-expected-conversion.json | 0 .../Beatmaps/hardrock-repeat-slider.osu | 0 .../hardrock-spinner-expected-conversion.json | 0 .../Testing/Beatmaps/hardrock-spinner.osu | 0 .../hardrock-stream-expected-conversion.json | 0 .../Testing/Beatmaps/hardrock-stream.osu | 0 .../pixel-jump-expected-conversion.json | 0 .../Resources/Testing/Beatmaps/pixel-jump.osu | 0 ...t-bound-hr-offset-expected-conversion.json | 0 .../Beatmaps/right-bound-hr-offset.osu | 0 .../Beatmaps/slider-expected-conversion.json | 0 .../Resources/Testing/Beatmaps/slider.osu | 0 ...inner-and-circles-expected-conversion.json | 0 .../Testing/Beatmaps/spinner-and-circles.osu | 0 .../Beatmaps/spinner-expected-conversion.json | 0 ...spinner-precision-expected-conversion.json | 0 .../Testing/Beatmaps/spinner-precision.osu | 0 .../Resources/Testing/Beatmaps/spinner.osu | 0 .../tiny-ticks-expected-conversion.json | 0 .../Resources/Testing/Beatmaps/tiny-ticks.osu | 0 .../v8-tick-distance-expected-conversion.json | 0 .../Testing/Beatmaps/v8-tick-distance.osu | 0 70 files changed, 11052 insertions(+), 29 deletions(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/103019-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/103019.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/104973-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/104973.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1284935-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1284935.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1431386-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1431386.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1597806-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1597806.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2190499-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2190499.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2571731-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2571731.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2768615-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2768615.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2781126-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2781126.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3152510-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3152510.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3227428-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3227428.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3524302-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3524302.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3644427-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3644427.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3689906-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3689906.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/37902-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/37902.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/39206-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/39206.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3949367-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3949367.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/42587-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/42587.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/50859-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/50859.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/75858-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/75858.osu create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/871815-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/871815.osu rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/basic-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/basic-hyperdash-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/basic-hyperdash.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/basic.osu (96%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/diffcalc-test.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/hardrock-spinner.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/hardrock-stream.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/pixel-jump-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/pixel-jump.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/right-bound-hr-offset-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/right-bound-hr-offset.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/slider-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/slider.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/spinner-and-circles-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/spinner-and-circles.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/spinner-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/spinner-precision-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/spinner-precision.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/spinner.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/tiny-ticks-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/tiny-ticks.osu (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/v8-tick-distance-expected-conversion.json (100%) rename {osu.Game.Rulesets.Catch => osu.Game.Rulesets.Catch.Tests}/Resources/Testing/Beatmaps/v8-tick-distance.osu (100%) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index 5528ce0bfa..7572c6670f 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -16,7 +16,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestFixture] public class CatchBeatmapConversionTest : BeatmapConversionTest { - protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; + protected override string ResourceAssembly => "osu.Game.Rulesets.Catch.Tests"; [TestCase("basic")] [TestCase("spinner")] @@ -31,6 +31,27 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("tiny-ticks")] [TestCase("v8-tick-distance")] [TestCase("spinner-precision")] + [TestCase("37902", new[] { typeof(CatchModDoubleTime), typeof(CatchModHardRock), typeof(CatchModHidden) })] + [TestCase("39206", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })] + [TestCase("42587")] + [TestCase("50859", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })] + [TestCase("75858", new[] { typeof(CatchModHardRock), typeof(CatchModHidden) })] + [TestCase("103019", new[] { typeof(CatchModHidden) })] + [TestCase("104973", new[] { typeof(CatchModHardRock), typeof(CatchModHidden) })] + [TestCase("871815", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })] + [TestCase("1284935", new[] { typeof(CatchModDoubleTime), typeof(CatchModHardRock) })] + [TestCase("1431386", new[] { typeof(CatchModDoubleTime), typeof(CatchModHardRock), typeof(CatchModHidden) })] + [TestCase("1597806", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })] + [TestCase("2190499", new[] { typeof(CatchModDoubleTime), typeof(CatchModHidden) })] + [TestCase("2571731", new[] { typeof(CatchModHardRock), typeof(CatchModHidden) })] + [TestCase("2768615", new[] { typeof(CatchModDoubleTime), typeof(CatchModHardRock) })] + [TestCase("2781126", new[] { typeof(CatchModHidden) })] + [TestCase("3152510", new[] { typeof(CatchModDoubleTime) })] + [TestCase("3227428", new[] { typeof(CatchModHardRock), typeof(CatchModHidden) })] + [TestCase("3524302", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })] + [TestCase("3644427", new[] { typeof(CatchModEasy), typeof(CatchModFlashlight) })] + [TestCase("3689906", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })] + [TestCase("3949367", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })] public new void Test(string name, params Type[] mods) => base.Test(name, mods); protected override IEnumerable CreateConvertValue(HitObject hitObject) @@ -64,7 +85,7 @@ namespace osu.Game.Rulesets.Catch.Tests /// /// A sane value to account for osu!stable using ints everwhere. /// - private const float conversion_lenience = 2; + private const float conversion_lenience = 3; [JsonIgnore] public readonly CatchHitObject HitObject; diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/103019-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/103019-expected-conversion.json new file mode 100644 index 0000000000..f518db17a0 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/103019-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":571.0,"Objects":[{"StartTime":571.0,"Position":184.0,"HyperDash":false},{"StartTime":656.0,"Position":168.664017,"HyperDash":false},{"StartTime":742.0,"Position":196.577621,"HyperDash":false},{"StartTime":827.0,"Position":218.922379,"HyperDash":false},{"StartTime":913.0,"Position":255.565826,"HyperDash":false},{"StartTime":999.0,"Position":306.3156,"HyperDash":false},{"StartTime":1085.0,"Position":315.164,"HyperDash":false},{"StartTime":1152.0,"Position":325.552582,"HyperDash":false},{"StartTime":1256.0,"Position":328.091736,"HyperDash":false}]},{"StartTime":1599.0,"Objects":[{"StartTime":1599.0,"Position":256.0,"HyperDash":false},{"StartTime":1684.0,"Position":241.0,"HyperDash":false},{"StartTime":1770.0,"Position":256.0,"HyperDash":false},{"StartTime":1855.0,"Position":244.0,"HyperDash":false},{"StartTime":1941.0,"Position":256.0,"HyperDash":false},{"StartTime":2027.0,"Position":252.0,"HyperDash":false},{"StartTime":2113.0,"Position":256.0,"HyperDash":false},{"StartTime":2198.0,"Position":260.0,"HyperDash":false},{"StartTime":2284.0,"Position":256.0,"HyperDash":false},{"StartTime":2370.0,"Position":247.0,"HyperDash":false},{"StartTime":2456.0,"Position":256.0,"HyperDash":false},{"StartTime":2523.0,"Position":237.0,"HyperDash":false},{"StartTime":2627.0,"Position":256.0,"HyperDash":false}]},{"StartTime":2971.0,"Objects":[{"StartTime":2971.0,"Position":256.0,"HyperDash":false}]},{"StartTime":3313.0,"Objects":[{"StartTime":3313.0,"Position":128.0,"HyperDash":false}]},{"StartTime":3656.0,"Objects":[{"StartTime":3656.0,"Position":128.0,"HyperDash":false},{"StartTime":3741.0,"Position":119.0,"HyperDash":false},{"StartTime":3827.0,"Position":128.0,"HyperDash":false},{"StartTime":3894.0,"Position":146.0,"HyperDash":false},{"StartTime":3998.0,"Position":128.0,"HyperDash":false}]},{"StartTime":4342.0,"Objects":[{"StartTime":4342.0,"Position":384.0,"HyperDash":false},{"StartTime":4427.0,"Position":401.0,"HyperDash":false},{"StartTime":4513.0,"Position":384.0,"HyperDash":false},{"StartTime":4580.0,"Position":397.0,"HyperDash":false},{"StartTime":4684.0,"Position":384.0,"HyperDash":false}]},{"StartTime":4856.0,"Objects":[{"StartTime":4856.0,"Position":384.0,"HyperDash":false}]},{"StartTime":5028.0,"Objects":[{"StartTime":5028.0,"Position":384.0,"HyperDash":false}]},{"StartTime":5371.0,"Objects":[{"StartTime":5371.0,"Position":256.0,"HyperDash":false}]},{"StartTime":5713.0,"Objects":[{"StartTime":5713.0,"Position":256.0,"HyperDash":false}]},{"StartTime":6056.0,"Objects":[{"StartTime":6056.0,"Position":128.0,"HyperDash":false},{"StartTime":6141.0,"Position":88.01805,"HyperDash":false},{"StartTime":6227.0,"Position":72.0,"HyperDash":false},{"StartTime":6294.0,"Position":85.0,"HyperDash":false},{"StartTime":6398.0,"Position":72.0,"HyperDash":false}]},{"StartTime":6742.0,"Objects":[{"StartTime":6742.0,"Position":384.0,"HyperDash":false},{"StartTime":6827.0,"Position":410.981934,"HyperDash":false},{"StartTime":6913.0,"Position":440.0,"HyperDash":false},{"StartTime":6980.0,"Position":425.0,"HyperDash":false},{"StartTime":7084.0,"Position":440.0,"HyperDash":false}]},{"StartTime":7428.0,"Objects":[{"StartTime":7428.0,"Position":256.0,"HyperDash":false},{"StartTime":7513.0,"Position":243.6103,"HyperDash":false},{"StartTime":7599.0,"Position":259.546265,"HyperDash":false},{"StartTime":7684.0,"Position":282.3688,"HyperDash":false},{"StartTime":7770.0,"Position":257.824768,"HyperDash":false},{"StartTime":7856.0,"Position":253.344818,"HyperDash":false},{"StartTime":7942.0,"Position":259.546265,"HyperDash":false},{"StartTime":8009.0,"Position":232.678436,"HyperDash":false},{"StartTime":8113.0,"Position":256.0,"HyperDash":false}]},{"StartTime":8456.0,"Objects":[{"StartTime":8456.0,"Position":256.0,"HyperDash":false}]},{"StartTime":8799.0,"Objects":[{"StartTime":8799.0,"Position":428.0,"HyperDash":false},{"StartTime":8874.0,"Position":243.0,"HyperDash":false},{"StartTime":8949.0,"Position":422.0,"HyperDash":false},{"StartTime":9024.0,"Position":481.0,"HyperDash":false},{"StartTime":9099.0,"Position":104.0,"HyperDash":false},{"StartTime":9174.0,"Position":473.0,"HyperDash":false},{"StartTime":9249.0,"Position":135.0,"HyperDash":false},{"StartTime":9324.0,"Position":360.0,"HyperDash":false},{"StartTime":9399.0,"Position":123.0,"HyperDash":false},{"StartTime":9474.0,"Position":42.0,"HyperDash":false},{"StartTime":9549.0,"Position":393.0,"HyperDash":false},{"StartTime":9624.0,"Position":75.0,"HyperDash":false},{"StartTime":9699.0,"Position":377.0,"HyperDash":false},{"StartTime":9774.0,"Position":354.0,"HyperDash":false},{"StartTime":9849.0,"Position":287.0,"HyperDash":false},{"StartTime":9924.0,"Position":361.0,"HyperDash":false},{"StartTime":9999.0,"Position":479.0,"HyperDash":false},{"StartTime":10074.0,"Position":346.0,"HyperDash":false},{"StartTime":10149.0,"Position":266.0,"HyperDash":false},{"StartTime":10224.0,"Position":400.0,"HyperDash":false},{"StartTime":10299.0,"Position":202.0,"HyperDash":false},{"StartTime":10374.0,"Position":500.0,"HyperDash":false},{"StartTime":10449.0,"Position":80.0,"HyperDash":false},{"StartTime":10524.0,"Position":399.0,"HyperDash":false},{"StartTime":10599.0,"Position":455.0,"HyperDash":false},{"StartTime":10674.0,"Position":105.0,"HyperDash":false},{"StartTime":10749.0,"Position":100.0,"HyperDash":false},{"StartTime":10824.0,"Position":195.0,"HyperDash":false},{"StartTime":10899.0,"Position":106.0,"HyperDash":false},{"StartTime":10974.0,"Position":305.0,"HyperDash":false},{"StartTime":11049.0,"Position":225.0,"HyperDash":false},{"StartTime":11124.0,"Position":79.0,"HyperDash":false},{"StartTime":11199.0,"Position":38.0,"HyperDash":false}]},{"StartTime":11542.0,"Objects":[{"StartTime":11542.0,"Position":256.0,"HyperDash":false}]},{"StartTime":11885.0,"Objects":[{"StartTime":11885.0,"Position":60.0,"HyperDash":false},{"StartTime":11970.0,"Position":34.9856834,"HyperDash":false},{"StartTime":12056.0,"Position":54.15636,"HyperDash":false},{"StartTime":12141.0,"Position":60.52591,"HyperDash":false},{"StartTime":12227.0,"Position":114.312965,"HyperDash":false},{"StartTime":12313.0,"Position":82.90555,"HyperDash":false},{"StartTime":12399.0,"Position":54.15636,"HyperDash":false},{"StartTime":12466.0,"Position":53.6008873,"HyperDash":false},{"StartTime":12570.0,"Position":60.0,"HyperDash":false}]},{"StartTime":12913.0,"Objects":[{"StartTime":12913.0,"Position":256.0,"HyperDash":false}]},{"StartTime":13256.0,"Objects":[{"StartTime":13256.0,"Position":452.0,"HyperDash":false},{"StartTime":13341.0,"Position":477.0143,"HyperDash":false},{"StartTime":13427.0,"Position":457.843628,"HyperDash":false},{"StartTime":13512.0,"Position":425.4741,"HyperDash":false},{"StartTime":13598.0,"Position":397.687042,"HyperDash":false},{"StartTime":13684.0,"Position":442.094452,"HyperDash":false},{"StartTime":13770.0,"Position":457.843628,"HyperDash":false},{"StartTime":13837.0,"Position":471.3991,"HyperDash":false},{"StartTime":13941.0,"Position":452.0,"HyperDash":false}]},{"StartTime":14285.0,"Objects":[{"StartTime":14285.0,"Position":256.0,"HyperDash":false}]},{"StartTime":14799.0,"Objects":[{"StartTime":14799.0,"Position":88.0,"HyperDash":false},{"StartTime":14884.0,"Position":60.0,"HyperDash":false},{"StartTime":14970.0,"Position":88.0,"HyperDash":false},{"StartTime":15056.0,"Position":60.0,"HyperDash":false},{"StartTime":15141.0,"Position":88.0,"HyperDash":false},{"StartTime":15227.0,"Position":60.0,"HyperDash":false},{"StartTime":15313.0,"Position":88.0,"HyperDash":false},{"StartTime":15399.0,"Position":60.0,"HyperDash":false},{"StartTime":15484.0,"Position":88.0,"HyperDash":false},{"StartTime":15570.0,"Position":60.0,"HyperDash":false},{"StartTime":15656.0,"Position":88.0,"HyperDash":false}]},{"StartTime":15999.0,"Objects":[{"StartTime":15999.0,"Position":32.0,"HyperDash":false}]},{"StartTime":16171.0,"Objects":[{"StartTime":16171.0,"Position":96.0,"HyperDash":false}]},{"StartTime":16342.0,"Objects":[{"StartTime":16342.0,"Position":160.0,"HyperDash":false}]},{"StartTime":16685.0,"Objects":[{"StartTime":16685.0,"Position":224.0,"HyperDash":false}]},{"StartTime":17028.0,"Objects":[{"StartTime":17028.0,"Position":328.0,"HyperDash":false},{"StartTime":17095.0,"Position":334.2591,"HyperDash":false},{"StartTime":17199.0,"Position":349.0792,"HyperDash":false}]},{"StartTime":17371.0,"Objects":[{"StartTime":17371.0,"Position":412.0,"HyperDash":false},{"StartTime":17438.0,"Position":425.881073,"HyperDash":false},{"StartTime":17542.0,"Position":432.114349,"HyperDash":false}]},{"StartTime":17713.0,"Objects":[{"StartTime":17713.0,"Position":448.0,"HyperDash":false},{"StartTime":17780.0,"Position":467.063019,"HyperDash":false},{"StartTime":17884.0,"Position":511.9668,"HyperDash":false}]},{"StartTime":18056.0,"Objects":[{"StartTime":18056.0,"Position":472.0,"HyperDash":false},{"StartTime":18123.0,"Position":439.87265,"HyperDash":false},{"StartTime":18227.0,"Position":407.869,"HyperDash":false}]},{"StartTime":18399.0,"Objects":[{"StartTime":18399.0,"Position":388.0,"HyperDash":false},{"StartTime":18466.0,"Position":396.55722,"HyperDash":false},{"StartTime":18570.0,"Position":361.3475,"HyperDash":false}]},{"StartTime":18742.0,"Objects":[{"StartTime":18742.0,"Position":300.0,"HyperDash":false},{"StartTime":18809.0,"Position":305.44278,"HyperDash":false},{"StartTime":18913.0,"Position":326.6525,"HyperDash":false}]},{"StartTime":19085.0,"Objects":[{"StartTime":19085.0,"Position":344.0,"HyperDash":false}]},{"StartTime":19428.0,"Objects":[{"StartTime":19428.0,"Position":156.0,"HyperDash":false}]},{"StartTime":19771.0,"Objects":[{"StartTime":19771.0,"Position":256.0,"HyperDash":false}]},{"StartTime":20456.0,"Objects":[{"StartTime":20456.0,"Position":256.0,"HyperDash":false}]},{"StartTime":21142.0,"Objects":[{"StartTime":21142.0,"Position":124.0,"HyperDash":false}]},{"StartTime":21485.0,"Objects":[{"StartTime":21485.0,"Position":256.0,"HyperDash":false}]},{"StartTime":21828.0,"Objects":[{"StartTime":21828.0,"Position":388.0,"HyperDash":false}]},{"StartTime":22513.0,"Objects":[{"StartTime":22513.0,"Position":504.0,"HyperDash":false},{"StartTime":22580.0,"Position":476.5731,"HyperDash":false},{"StartTime":22684.0,"Position":434.0,"HyperDash":false}]},{"StartTime":22856.0,"Objects":[{"StartTime":22856.0,"Position":448.0,"HyperDash":false}]},{"StartTime":23028.0,"Objects":[{"StartTime":23028.0,"Position":376.0,"HyperDash":false}]},{"StartTime":23199.0,"Objects":[{"StartTime":23199.0,"Position":360.0,"HyperDash":false},{"StartTime":23266.0,"Position":347.5731,"HyperDash":false},{"StartTime":23370.0,"Position":290.0,"HyperDash":false}]},{"StartTime":23542.0,"Objects":[{"StartTime":23542.0,"Position":304.0,"HyperDash":false}]},{"StartTime":23713.0,"Objects":[{"StartTime":23713.0,"Position":232.0,"HyperDash":false}]},{"StartTime":23885.0,"Objects":[{"StartTime":23885.0,"Position":216.0,"HyperDash":false},{"StartTime":23952.0,"Position":172.5731,"HyperDash":false},{"StartTime":24056.0,"Position":146.0,"HyperDash":false}]},{"StartTime":24228.0,"Objects":[{"StartTime":24228.0,"Position":160.0,"HyperDash":false}]},{"StartTime":24399.0,"Objects":[{"StartTime":24399.0,"Position":88.0,"HyperDash":false}]},{"StartTime":24571.0,"Objects":[{"StartTime":24571.0,"Position":72.0,"HyperDash":false},{"StartTime":24656.0,"Position":54.2046776,"HyperDash":false},{"StartTime":24742.0,"Position":2.0,"HyperDash":false},{"StartTime":24809.0,"Position":32.4269028,"HyperDash":false},{"StartTime":24913.0,"Position":72.0,"HyperDash":false}]},{"StartTime":25256.0,"Objects":[{"StartTime":25256.0,"Position":8.0,"HyperDash":false},{"StartTime":25323.0,"Position":31.4269028,"HyperDash":false},{"StartTime":25427.0,"Position":78.0,"HyperDash":false}]},{"StartTime":25599.0,"Objects":[{"StartTime":25599.0,"Position":64.0,"HyperDash":false}]},{"StartTime":25771.0,"Objects":[{"StartTime":25771.0,"Position":136.0,"HyperDash":false}]},{"StartTime":25942.0,"Objects":[{"StartTime":25942.0,"Position":152.0,"HyperDash":false},{"StartTime":26009.0,"Position":187.4269,"HyperDash":false},{"StartTime":26113.0,"Position":222.0,"HyperDash":false}]},{"StartTime":26285.0,"Objects":[{"StartTime":26285.0,"Position":208.0,"HyperDash":false}]},{"StartTime":26456.0,"Objects":[{"StartTime":26456.0,"Position":280.0,"HyperDash":false}]},{"StartTime":26628.0,"Objects":[{"StartTime":26628.0,"Position":296.0,"HyperDash":false},{"StartTime":26695.0,"Position":322.4269,"HyperDash":false},{"StartTime":26799.0,"Position":366.0,"HyperDash":false}]},{"StartTime":26971.0,"Objects":[{"StartTime":26971.0,"Position":352.0,"HyperDash":false}]},{"StartTime":27142.0,"Objects":[{"StartTime":27142.0,"Position":424.0,"HyperDash":false}]},{"StartTime":27313.0,"Objects":[{"StartTime":27313.0,"Position":440.0,"HyperDash":false},{"StartTime":27398.0,"Position":489.795319,"HyperDash":false},{"StartTime":27484.0,"Position":510.0,"HyperDash":false},{"StartTime":27551.0,"Position":470.5731,"HyperDash":false},{"StartTime":27655.0,"Position":440.0,"HyperDash":false}]},{"StartTime":27999.0,"Objects":[{"StartTime":27999.0,"Position":40.0,"HyperDash":false},{"StartTime":28066.0,"Position":24.0,"HyperDash":false},{"StartTime":28170.0,"Position":40.0,"HyperDash":false}]},{"StartTime":28342.0,"Objects":[{"StartTime":28342.0,"Position":112.0,"HyperDash":false},{"StartTime":28427.0,"Position":112.0,"HyperDash":false},{"StartTime":28513.0,"Position":112.0,"HyperDash":false}]},{"StartTime":28685.0,"Objects":[{"StartTime":28685.0,"Position":184.0,"HyperDash":false},{"StartTime":28752.0,"Position":177.0,"HyperDash":false},{"StartTime":28856.0,"Position":184.0,"HyperDash":false}]},{"StartTime":29028.0,"Objects":[{"StartTime":29028.0,"Position":260.0,"HyperDash":false},{"StartTime":29113.0,"Position":260.0,"HyperDash":false},{"StartTime":29199.0,"Position":260.0,"HyperDash":false}]},{"StartTime":29371.0,"Objects":[{"StartTime":29371.0,"Position":336.0,"HyperDash":false},{"StartTime":29438.0,"Position":333.2137,"HyperDash":false},{"StartTime":29542.0,"Position":374.829,"HyperDash":false}]},{"StartTime":29713.0,"Objects":[{"StartTime":29713.0,"Position":440.0,"HyperDash":false},{"StartTime":29780.0,"Position":420.18338,"HyperDash":false},{"StartTime":29884.0,"Position":399.632172,"HyperDash":false}]},{"StartTime":30056.0,"Objects":[{"StartTime":30056.0,"Position":460.0,"HyperDash":false},{"StartTime":30141.0,"Position":479.41452,"HyperDash":false},{"StartTime":30227.0,"Position":460.0,"HyperDash":false},{"StartTime":30313.0,"Position":479.41452,"HyperDash":false},{"StartTime":30398.0,"Position":460.0,"HyperDash":false}]},{"StartTime":30742.0,"Objects":[{"StartTime":30742.0,"Position":328.0,"HyperDash":false},{"StartTime":30827.0,"Position":293.0,"HyperDash":false},{"StartTime":30913.0,"Position":328.0,"HyperDash":false},{"StartTime":30999.0,"Position":293.0,"HyperDash":false}]},{"StartTime":31085.0,"Objects":[{"StartTime":31085.0,"Position":256.0,"HyperDash":false},{"StartTime":31170.0,"Position":221.0,"HyperDash":false},{"StartTime":31256.0,"Position":256.0,"HyperDash":false},{"StartTime":31342.0,"Position":221.0,"HyperDash":false}]},{"StartTime":31428.0,"Objects":[{"StartTime":31428.0,"Position":184.0,"HyperDash":false},{"StartTime":31513.0,"Position":149.0,"HyperDash":false},{"StartTime":31599.0,"Position":184.0,"HyperDash":false},{"StartTime":31685.0,"Position":149.0,"HyperDash":false}]},{"StartTime":31771.0,"Objects":[{"StartTime":31771.0,"Position":112.0,"HyperDash":false},{"StartTime":31856.0,"Position":77.0,"HyperDash":false},{"StartTime":31942.0,"Position":112.0,"HyperDash":false},{"StartTime":32028.0,"Position":77.0,"HyperDash":false}]},{"StartTime":32113.0,"Objects":[{"StartTime":32113.0,"Position":40.0,"HyperDash":false}]},{"StartTime":32456.0,"Objects":[{"StartTime":32456.0,"Position":40.0,"HyperDash":false}]},{"StartTime":32799.0,"Objects":[{"StartTime":32799.0,"Position":184.0,"HyperDash":false}]},{"StartTime":33142.0,"Objects":[{"StartTime":33142.0,"Position":184.0,"HyperDash":false}]},{"StartTime":33485.0,"Objects":[{"StartTime":33485.0,"Position":304.0,"HyperDash":false},{"StartTime":33570.0,"Position":332.600983,"HyperDash":false},{"StartTime":33656.0,"Position":351.4796,"HyperDash":false},{"StartTime":33723.0,"Position":368.082733,"HyperDash":false},{"StartTime":33827.0,"Position":398.9592,"HyperDash":false}]},{"StartTime":34342.0,"Objects":[{"StartTime":34342.0,"Position":256.0,"HyperDash":false}]},{"StartTime":34513.0,"Objects":[{"StartTime":34513.0,"Position":256.0,"HyperDash":false}]},{"StartTime":34856.0,"Objects":[{"StartTime":34856.0,"Position":136.0,"HyperDash":false},{"StartTime":34941.0,"Position":152.0,"HyperDash":false},{"StartTime":35027.0,"Position":136.0,"HyperDash":false},{"StartTime":35094.0,"Position":150.0,"HyperDash":false},{"StartTime":35198.0,"Position":136.0,"HyperDash":false}]},{"StartTime":35371.0,"Objects":[{"StartTime":35371.0,"Position":104.0,"HyperDash":false},{"StartTime":35456.0,"Position":124.558014,"HyperDash":false},{"StartTime":35542.0,"Position":170.988922,"HyperDash":false},{"StartTime":35609.0,"Position":180.576416,"HyperDash":false},{"StartTime":35713.0,"Position":209.857956,"HyperDash":false}]},{"StartTime":35885.0,"Objects":[{"StartTime":35885.0,"Position":212.0,"HyperDash":false}]},{"StartTime":36228.0,"Objects":[{"StartTime":36228.0,"Position":408.0,"HyperDash":false},{"StartTime":36313.0,"Position":441.692383,"HyperDash":false},{"StartTime":36399.0,"Position":463.7653,"HyperDash":false},{"StartTime":36466.0,"Position":471.929932,"HyperDash":false},{"StartTime":36570.0,"Position":480.400452,"HyperDash":false}]},{"StartTime":37085.0,"Objects":[{"StartTime":37085.0,"Position":360.0,"HyperDash":false}]},{"StartTime":37256.0,"Objects":[{"StartTime":37256.0,"Position":360.0,"HyperDash":false}]},{"StartTime":37599.0,"Objects":[{"StartTime":37599.0,"Position":232.0,"HyperDash":false},{"StartTime":37684.0,"Position":186.367691,"HyperDash":false},{"StartTime":37770.0,"Position":175.2116,"HyperDash":false},{"StartTime":37837.0,"Position":153.710571,"HyperDash":false},{"StartTime":37941.0,"Position":106.279663,"HyperDash":false}]},{"StartTime":38113.0,"Objects":[{"StartTime":38113.0,"Position":56.0,"HyperDash":false},{"StartTime":38198.0,"Position":39.6659164,"HyperDash":false},{"StartTime":38284.0,"Position":38.9134,"HyperDash":false},{"StartTime":38351.0,"Position":31.39479,"HyperDash":false},{"StartTime":38455.0,"Position":85.0976944,"HyperDash":false}]},{"StartTime":38628.0,"Objects":[{"StartTime":38628.0,"Position":156.0,"HyperDash":false}]},{"StartTime":38971.0,"Objects":[{"StartTime":38971.0,"Position":256.0,"HyperDash":false},{"StartTime":39056.0,"Position":221.399033,"HyperDash":false},{"StartTime":39142.0,"Position":208.5204,"HyperDash":false},{"StartTime":39209.0,"Position":182.917267,"HyperDash":false},{"StartTime":39313.0,"Position":161.0408,"HyperDash":false}]},{"StartTime":39828.0,"Objects":[{"StartTime":39828.0,"Position":256.0,"HyperDash":false}]},{"StartTime":39999.0,"Objects":[{"StartTime":39999.0,"Position":256.0,"HyperDash":false}]},{"StartTime":40342.0,"Objects":[{"StartTime":40342.0,"Position":376.0,"HyperDash":false},{"StartTime":40427.0,"Position":392.0,"HyperDash":false},{"StartTime":40513.0,"Position":376.0,"HyperDash":false},{"StartTime":40580.0,"Position":369.0,"HyperDash":false},{"StartTime":40684.0,"Position":376.0,"HyperDash":false}]},{"StartTime":40856.0,"Objects":[{"StartTime":40856.0,"Position":408.0,"HyperDash":false},{"StartTime":40941.0,"Position":355.442,"HyperDash":false},{"StartTime":41027.0,"Position":341.011078,"HyperDash":false},{"StartTime":41094.0,"Position":333.423584,"HyperDash":false},{"StartTime":41198.0,"Position":302.142059,"HyperDash":false}]},{"StartTime":41371.0,"Objects":[{"StartTime":41371.0,"Position":300.0,"HyperDash":false}]},{"StartTime":41713.0,"Objects":[{"StartTime":41713.0,"Position":104.0,"HyperDash":false},{"StartTime":41798.0,"Position":74.30763,"HyperDash":false},{"StartTime":41884.0,"Position":48.23472,"HyperDash":false},{"StartTime":41951.0,"Position":33.07008,"HyperDash":false},{"StartTime":42055.0,"Position":31.59955,"HyperDash":false}]},{"StartTime":42571.0,"Objects":[{"StartTime":42571.0,"Position":152.0,"HyperDash":false}]},{"StartTime":42742.0,"Objects":[{"StartTime":42742.0,"Position":152.0,"HyperDash":false}]},{"StartTime":43085.0,"Objects":[{"StartTime":43085.0,"Position":256.0,"HyperDash":false},{"StartTime":43170.0,"Position":256.0,"HyperDash":false},{"StartTime":43256.0,"Position":256.0,"HyperDash":false},{"StartTime":43342.0,"Position":256.0,"HyperDash":false},{"StartTime":43427.0,"Position":256.0,"HyperDash":false},{"StartTime":43513.0,"Position":256.0,"HyperDash":false},{"StartTime":43599.0,"Position":256.0,"HyperDash":false},{"StartTime":43685.0,"Position":256.0,"HyperDash":false},{"StartTime":43770.0,"Position":256.0,"HyperDash":false}]},{"StartTime":44113.0,"Objects":[{"StartTime":44113.0,"Position":256.0,"HyperDash":false}]},{"StartTime":44456.0,"Objects":[{"StartTime":44456.0,"Position":124.0,"HyperDash":false}]},{"StartTime":44628.0,"Objects":[{"StartTime":44628.0,"Position":72.0,"HyperDash":false},{"StartTime":44713.0,"Position":40.92307,"HyperDash":false},{"StartTime":44799.0,"Position":52.98573,"HyperDash":false},{"StartTime":44884.0,"Position":77.93154,"HyperDash":false},{"StartTime":44970.0,"Position":95.82509,"HyperDash":false},{"StartTime":45038.0,"Position":118.951027,"HyperDash":false},{"StartTime":45142.0,"Position":163.988525,"HyperDash":false}]},{"StartTime":45485.0,"Objects":[{"StartTime":45485.0,"Position":256.0,"HyperDash":false}]},{"StartTime":45828.0,"Objects":[{"StartTime":45828.0,"Position":388.0,"HyperDash":false}]},{"StartTime":45999.0,"Objects":[{"StartTime":45999.0,"Position":440.0,"HyperDash":false},{"StartTime":46084.0,"Position":441.0769,"HyperDash":false},{"StartTime":46170.0,"Position":459.014282,"HyperDash":false},{"StartTime":46255.0,"Position":425.068451,"HyperDash":false},{"StartTime":46341.0,"Position":416.174927,"HyperDash":false},{"StartTime":46409.0,"Position":398.048981,"HyperDash":false},{"StartTime":46513.0,"Position":348.011475,"HyperDash":false}]},{"StartTime":46856.0,"Objects":[{"StartTime":46856.0,"Position":256.0,"HyperDash":false}]},{"StartTime":47199.0,"Objects":[{"StartTime":47199.0,"Position":256.0,"HyperDash":false},{"StartTime":47284.0,"Position":244.431641,"HyperDash":false},{"StartTime":47370.0,"Position":255.566513,"HyperDash":false},{"StartTime":47455.0,"Position":277.621033,"HyperDash":false},{"StartTime":47541.0,"Position":254.8021,"HyperDash":false},{"StartTime":47627.0,"Position":267.5996,"HyperDash":false},{"StartTime":47713.0,"Position":255.632889,"HyperDash":false},{"StartTime":47798.0,"Position":231.420425,"HyperDash":false},{"StartTime":47884.0,"Position":256.0,"HyperDash":false},{"StartTime":47970.0,"Position":247.424866,"HyperDash":false},{"StartTime":48056.0,"Position":255.699265,"HyperDash":false},{"StartTime":48123.0,"Position":258.327057,"HyperDash":false},{"StartTime":48227.0,"Position":254.8021,"HyperDash":false}]},{"StartTime":48571.0,"Objects":[{"StartTime":48571.0,"Position":392.0,"HyperDash":false},{"StartTime":48656.0,"Position":373.0,"HyperDash":false},{"StartTime":48742.0,"Position":392.0,"HyperDash":false},{"StartTime":48809.0,"Position":387.0,"HyperDash":false},{"StartTime":48913.0,"Position":392.0,"HyperDash":false}]},{"StartTime":49085.0,"Objects":[{"StartTime":49085.0,"Position":464.0,"HyperDash":false},{"StartTime":49170.0,"Position":434.350128,"HyperDash":false},{"StartTime":49256.0,"Position":431.4105,"HyperDash":false},{"StartTime":49341.0,"Position":405.503876,"HyperDash":false},{"StartTime":49427.0,"Position":365.203827,"HyperDash":false},{"StartTime":49495.0,"Position":336.536133,"HyperDash":false},{"StartTime":49599.0,"Position":324.364319,"HyperDash":false}]},{"StartTime":49942.0,"Objects":[{"StartTime":49942.0,"Position":187.0,"HyperDash":false},{"StartTime":50027.0,"Position":163.228943,"HyperDash":false},{"StartTime":50113.0,"Position":148.783264,"HyperDash":false},{"StartTime":50198.0,"Position":108.904266,"HyperDash":false},{"StartTime":50284.0,"Position":81.87666,"HyperDash":false},{"StartTime":50352.0,"Position":62.3181648,"HyperDash":false},{"StartTime":50456.0,"Position":47.9551849,"HyperDash":false}]},{"StartTime":50628.0,"Objects":[{"StartTime":50628.0,"Position":120.0,"HyperDash":false},{"StartTime":50713.0,"Position":106.0,"HyperDash":false},{"StartTime":50799.0,"Position":120.0,"HyperDash":false},{"StartTime":50866.0,"Position":135.0,"HyperDash":false},{"StartTime":50970.0,"Position":120.0,"HyperDash":false}]},{"StartTime":51313.0,"Objects":[{"StartTime":51313.0,"Position":257.0,"HyperDash":false},{"StartTime":51398.0,"Position":234.050232,"HyperDash":false},{"StartTime":51484.0,"Position":255.277374,"HyperDash":false},{"StartTime":51569.0,"Position":284.5524,"HyperDash":false},{"StartTime":51655.0,"Position":256.423248,"HyperDash":false},{"StartTime":51741.0,"Position":248.555389,"HyperDash":false},{"StartTime":51827.0,"Position":255.347473,"HyperDash":false},{"StartTime":51912.0,"Position":263.0151,"HyperDash":false},{"StartTime":51998.0,"Position":257.0,"HyperDash":false},{"StartTime":52084.0,"Position":228.030624,"HyperDash":false},{"StartTime":52170.0,"Position":255.417587,"HyperDash":false},{"StartTime":52237.0,"Position":278.820038,"HyperDash":false},{"StartTime":52341.0,"Position":256.423248,"HyperDash":false}]},{"StartTime":52685.0,"Objects":[{"StartTime":52685.0,"Position":256.0,"HyperDash":false}]},{"StartTime":53028.0,"Objects":[{"StartTime":53028.0,"Position":169.0,"HyperDash":false},{"StartTime":53113.0,"Position":148.767334,"HyperDash":false},{"StartTime":53199.0,"Position":102.039978,"HyperDash":false},{"StartTime":53284.0,"Position":65.15436,"HyperDash":false},{"StartTime":53370.0,"Position":56.49534,"HyperDash":false},{"StartTime":53438.0,"Position":50.6727638,"HyperDash":false},{"StartTime":53542.0,"Position":72.11841,"HyperDash":false}]},{"StartTime":53713.0,"Objects":[{"StartTime":53713.0,"Position":124.0,"HyperDash":false}]},{"StartTime":54056.0,"Objects":[{"StartTime":54056.0,"Position":68.0,"HyperDash":false},{"StartTime":54141.0,"Position":56.93203,"HyperDash":false},{"StartTime":54227.0,"Position":68.0,"HyperDash":false}]},{"StartTime":54399.0,"Objects":[{"StartTime":54399.0,"Position":156.0,"HyperDash":false}]},{"StartTime":54742.0,"Objects":[{"StartTime":54742.0,"Position":444.0,"HyperDash":false},{"StartTime":54827.0,"Position":455.067963,"HyperDash":false},{"StartTime":54913.0,"Position":444.0,"HyperDash":false}]},{"StartTime":55085.0,"Objects":[{"StartTime":55085.0,"Position":356.0,"HyperDash":false}]},{"StartTime":55428.0,"Objects":[{"StartTime":55428.0,"Position":356.0,"HyperDash":false},{"StartTime":55513.0,"Position":335.3816,"HyperDash":false},{"StartTime":55599.0,"Position":294.1601,"HyperDash":false},{"StartTime":55684.0,"Position":272.865723,"HyperDash":false},{"StartTime":55770.0,"Position":255.69072,"HyperDash":false},{"StartTime":55856.0,"Position":254.907425,"HyperDash":false},{"StartTime":55942.0,"Position":216.981689,"HyperDash":false},{"StartTime":56009.0,"Position":188.30954,"HyperDash":false},{"StartTime":56113.0,"Position":154.812271,"HyperDash":false}]},{"StartTime":56285.0,"Objects":[{"StartTime":56285.0,"Position":160.0,"HyperDash":false}]},{"StartTime":56456.0,"Objects":[{"StartTime":56456.0,"Position":92.0,"HyperDash":false}]},{"StartTime":56628.0,"Objects":[{"StartTime":56628.0,"Position":84.0,"HyperDash":false}]},{"StartTime":56799.0,"Objects":[{"StartTime":56799.0,"Position":156.0,"HyperDash":false},{"StartTime":56884.0,"Position":179.6867,"HyperDash":false},{"StartTime":56970.0,"Position":184.530014,"HyperDash":false},{"StartTime":57055.0,"Position":210.992,"HyperDash":false},{"StartTime":57141.0,"Position":239.917923,"HyperDash":false},{"StartTime":57227.0,"Position":197.399063,"HyperDash":false},{"StartTime":57313.0,"Position":182.462265,"HyperDash":false},{"StartTime":57380.0,"Position":158.21933,"HyperDash":false},{"StartTime":57484.0,"Position":155.038208,"HyperDash":false}]},{"StartTime":57656.0,"Objects":[{"StartTime":57656.0,"Position":92.0,"HyperDash":false}]},{"StartTime":57828.0,"Objects":[{"StartTime":57828.0,"Position":88.0,"HyperDash":false}]},{"StartTime":57999.0,"Objects":[{"StartTime":57999.0,"Position":148.0,"HyperDash":false}]},{"StartTime":58171.0,"Objects":[{"StartTime":58171.0,"Position":155.0,"HyperDash":false},{"StartTime":58256.0,"Position":190.6184,"HyperDash":false},{"StartTime":58342.0,"Position":216.83992,"HyperDash":false},{"StartTime":58427.0,"Position":255.134277,"HyperDash":false},{"StartTime":58513.0,"Position":255.3093,"HyperDash":false},{"StartTime":58599.0,"Position":262.09256,"HyperDash":false},{"StartTime":58685.0,"Position":294.0183,"HyperDash":false},{"StartTime":58752.0,"Position":306.69046,"HyperDash":false},{"StartTime":58856.0,"Position":356.187744,"HyperDash":false}]},{"StartTime":59028.0,"Objects":[{"StartTime":59028.0,"Position":356.0,"HyperDash":false}]},{"StartTime":59199.0,"Objects":[{"StartTime":59199.0,"Position":424.0,"HyperDash":false}]},{"StartTime":59371.0,"Objects":[{"StartTime":59371.0,"Position":428.0,"HyperDash":false}]},{"StartTime":59542.0,"Objects":[{"StartTime":59542.0,"Position":356.0,"HyperDash":false},{"StartTime":59627.0,"Position":337.313324,"HyperDash":false},{"StartTime":59713.0,"Position":327.469971,"HyperDash":false},{"StartTime":59798.0,"Position":290.008,"HyperDash":false},{"StartTime":59884.0,"Position":272.0821,"HyperDash":false},{"StartTime":59970.0,"Position":294.600952,"HyperDash":false},{"StartTime":60056.0,"Position":329.53775,"HyperDash":false},{"StartTime":60123.0,"Position":351.78067,"HyperDash":false},{"StartTime":60227.0,"Position":356.9618,"HyperDash":false}]},{"StartTime":60399.0,"Objects":[{"StartTime":60399.0,"Position":424.0,"HyperDash":false}]},{"StartTime":60571.0,"Objects":[{"StartTime":60571.0,"Position":428.0,"HyperDash":false}]},{"StartTime":60742.0,"Objects":[{"StartTime":60742.0,"Position":360.0,"HyperDash":false}]},{"StartTime":60913.0,"Objects":[{"StartTime":60913.0,"Position":284.0,"HyperDash":false},{"StartTime":60980.0,"Position":271.5731,"HyperDash":false},{"StartTime":61084.0,"Position":214.0,"HyperDash":false}]},{"StartTime":61256.0,"Objects":[{"StartTime":61256.0,"Position":136.0,"HyperDash":false}]},{"StartTime":61428.0,"Objects":[{"StartTime":61428.0,"Position":60.0,"HyperDash":false}]},{"StartTime":61513.0,"Objects":[{"StartTime":61513.0,"Position":60.0,"HyperDash":false}]},{"StartTime":61599.0,"Objects":[{"StartTime":61599.0,"Position":60.0,"HyperDash":false},{"StartTime":61666.0,"Position":65.0,"HyperDash":false},{"StartTime":61770.0,"Position":60.0,"HyperDash":false}]},{"StartTime":61942.0,"Objects":[{"StartTime":61942.0,"Position":60.0,"HyperDash":false}]},{"StartTime":62113.0,"Objects":[{"StartTime":62113.0,"Position":136.0,"HyperDash":false}]},{"StartTime":62199.0,"Objects":[{"StartTime":62199.0,"Position":136.0,"HyperDash":false}]},{"StartTime":62285.0,"Objects":[{"StartTime":62285.0,"Position":136.0,"HyperDash":false},{"StartTime":62352.0,"Position":120.0,"HyperDash":false},{"StartTime":62456.0,"Position":136.0,"HyperDash":false}]},{"StartTime":62628.0,"Objects":[{"StartTime":62628.0,"Position":212.0,"HyperDash":false}]},{"StartTime":62799.0,"Objects":[{"StartTime":62799.0,"Position":212.0,"HyperDash":false}]},{"StartTime":62885.0,"Objects":[{"StartTime":62885.0,"Position":212.0,"HyperDash":false}]},{"StartTime":62971.0,"Objects":[{"StartTime":62971.0,"Position":212.0,"HyperDash":false},{"StartTime":63038.0,"Position":195.0,"HyperDash":false},{"StartTime":63142.0,"Position":212.0,"HyperDash":false}]},{"StartTime":63313.0,"Objects":[{"StartTime":63313.0,"Position":136.0,"HyperDash":false},{"StartTime":63380.0,"Position":120.5731,"HyperDash":false},{"StartTime":63484.0,"Position":66.0,"HyperDash":false}]},{"StartTime":63656.0,"Objects":[{"StartTime":63656.0,"Position":356.0,"HyperDash":false},{"StartTime":63741.0,"Position":362.0,"HyperDash":false},{"StartTime":63827.0,"Position":347.0,"HyperDash":false},{"StartTime":63913.0,"Position":252.0,"HyperDash":false},{"StartTime":63999.0,"Position":477.0,"HyperDash":false},{"StartTime":64084.0,"Position":358.0,"HyperDash":false},{"StartTime":64170.0,"Position":17.0,"HyperDash":false},{"StartTime":64256.0,"Position":399.0,"HyperDash":false},{"StartTime":64342.0,"Position":280.0,"HyperDash":false},{"StartTime":64427.0,"Position":304.0,"HyperDash":false},{"StartTime":64513.0,"Position":221.0,"HyperDash":false},{"StartTime":64599.0,"Position":407.0,"HyperDash":false},{"StartTime":64685.0,"Position":287.0,"HyperDash":false},{"StartTime":64770.0,"Position":135.0,"HyperDash":false},{"StartTime":64856.0,"Position":437.0,"HyperDash":false},{"StartTime":64942.0,"Position":289.0,"HyperDash":false},{"StartTime":65028.0,"Position":464.0,"HyperDash":false}]},{"StartTime":65713.0,"Objects":[{"StartTime":65713.0,"Position":256.0,"HyperDash":false},{"StartTime":65798.0,"Position":256.0,"HyperDash":false},{"StartTime":65884.0,"Position":256.0,"HyperDash":false}]},{"StartTime":66056.0,"Objects":[{"StartTime":66056.0,"Position":288.0,"HyperDash":false}]},{"StartTime":66228.0,"Objects":[{"StartTime":66228.0,"Position":328.0,"HyperDash":false}]},{"StartTime":66399.0,"Objects":[{"StartTime":66399.0,"Position":400.0,"HyperDash":false},{"StartTime":66466.0,"Position":404.432526,"HyperDash":false},{"StartTime":66570.0,"Position":443.844757,"HyperDash":false}]},{"StartTime":66742.0,"Objects":[{"StartTime":66742.0,"Position":380.0,"HyperDash":false}]},{"StartTime":66913.0,"Objects":[{"StartTime":66913.0,"Position":444.0,"HyperDash":false},{"StartTime":66980.0,"Position":415.4034,"HyperDash":false},{"StartTime":67084.0,"Position":392.189362,"HyperDash":false}]},{"StartTime":67256.0,"Objects":[{"StartTime":67256.0,"Position":316.0,"HyperDash":false},{"StartTime":67323.0,"Position":306.150818,"HyperDash":false},{"StartTime":67427.0,"Position":300.033234,"HyperDash":false}]},{"StartTime":67599.0,"Objects":[{"StartTime":67599.0,"Position":224.0,"HyperDash":false},{"StartTime":67666.0,"Position":211.175949,"HyperDash":false},{"StartTime":67770.0,"Position":163.867111,"HyperDash":false}]},{"StartTime":67942.0,"Objects":[{"StartTime":67942.0,"Position":104.0,"HyperDash":false},{"StartTime":68009.0,"Position":130.849182,"HyperDash":false},{"StartTime":68113.0,"Position":119.966782,"HyperDash":false}]},{"StartTime":68285.0,"Objects":[{"StartTime":68285.0,"Position":80.0,"HyperDash":false},{"StartTime":68352.0,"Position":100.824059,"HyperDash":false},{"StartTime":68456.0,"Position":140.132889,"HyperDash":false}]},{"StartTime":68628.0,"Objects":[{"StartTime":68628.0,"Position":200.0,"HyperDash":false},{"StartTime":68713.0,"Position":188.823929,"HyperDash":false},{"StartTime":68799.0,"Position":213.728134,"HyperDash":false},{"StartTime":68866.0,"Position":223.349274,"HyperDash":false},{"StartTime":68970.0,"Position":200.0,"HyperDash":false}]},{"StartTime":69142.0,"Objects":[{"StartTime":69142.0,"Position":212.0,"HyperDash":false}]},{"StartTime":69313.0,"Objects":[{"StartTime":69313.0,"Position":256.0,"HyperDash":false}]},{"StartTime":69485.0,"Objects":[{"StartTime":69485.0,"Position":256.0,"HyperDash":false}]},{"StartTime":69656.0,"Objects":[{"StartTime":69656.0,"Position":292.0,"HyperDash":false}]},{"StartTime":69828.0,"Objects":[{"StartTime":69828.0,"Position":292.0,"HyperDash":false}]},{"StartTime":69999.0,"Objects":[{"StartTime":69999.0,"Position":368.0,"HyperDash":false}]},{"StartTime":70085.0,"Objects":[{"StartTime":70085.0,"Position":376.0,"HyperDash":false}]},{"StartTime":70171.0,"Objects":[{"StartTime":70171.0,"Position":384.0,"HyperDash":false}]},{"StartTime":70256.0,"Objects":[{"StartTime":70256.0,"Position":392.0,"HyperDash":false}]},{"StartTime":70342.0,"Objects":[{"StartTime":70342.0,"Position":400.0,"HyperDash":false}]},{"StartTime":70428.0,"Objects":[{"StartTime":70428.0,"Position":408.0,"HyperDash":false}]},{"StartTime":70513.0,"Objects":[{"StartTime":70513.0,"Position":416.0,"HyperDash":false},{"StartTime":70598.0,"Position":442.363953,"HyperDash":false},{"StartTime":70684.0,"Position":451.799652,"HyperDash":false},{"StartTime":70769.0,"Position":450.290955,"HyperDash":false},{"StartTime":70855.0,"Position":444.293518,"HyperDash":false},{"StartTime":70941.0,"Position":469.222717,"HyperDash":false},{"StartTime":71027.0,"Position":451.823273,"HyperDash":false},{"StartTime":71112.0,"Position":447.6526,"HyperDash":false},{"StartTime":71198.0,"Position":416.0,"HyperDash":false},{"StartTime":71284.0,"Position":452.508881,"HyperDash":false},{"StartTime":71370.0,"Position":451.846527,"HyperDash":false},{"StartTime":71437.0,"Position":457.989929,"HyperDash":false},{"StartTime":71541.0,"Position":444.293518,"HyperDash":false}]},{"StartTime":71885.0,"Objects":[{"StartTime":71885.0,"Position":312.0,"HyperDash":false}]},{"StartTime":72056.0,"Objects":[{"StartTime":72056.0,"Position":312.0,"HyperDash":false}]},{"StartTime":72228.0,"Objects":[{"StartTime":72228.0,"Position":224.0,"HyperDash":false}]},{"StartTime":72313.0,"Objects":[{"StartTime":72313.0,"Position":216.0,"HyperDash":false}]},{"StartTime":72399.0,"Objects":[{"StartTime":72399.0,"Position":208.0,"HyperDash":false}]},{"StartTime":72485.0,"Objects":[{"StartTime":72485.0,"Position":200.0,"HyperDash":false}]},{"StartTime":72571.0,"Objects":[{"StartTime":72571.0,"Position":192.0,"HyperDash":false}]},{"StartTime":72742.0,"Objects":[{"StartTime":72742.0,"Position":124.0,"HyperDash":false}]},{"StartTime":72913.0,"Objects":[{"StartTime":72913.0,"Position":48.0,"HyperDash":false},{"StartTime":72980.0,"Position":68.42183,"HyperDash":false},{"StartTime":73084.0,"Position":84.285,"HyperDash":false}]},{"StartTime":73256.0,"Objects":[{"StartTime":73256.0,"Position":44.0,"HyperDash":false},{"StartTime":73323.0,"Position":27.0,"HyperDash":false},{"StartTime":73427.0,"Position":44.0,"HyperDash":false}]},{"StartTime":73599.0,"Objects":[{"StartTime":73599.0,"Position":116.0,"HyperDash":false},{"StartTime":73666.0,"Position":134.0,"HyperDash":false},{"StartTime":73770.0,"Position":116.0,"HyperDash":false}]},{"StartTime":73942.0,"Objects":[{"StartTime":73942.0,"Position":188.0,"HyperDash":false},{"StartTime":74027.0,"Position":194.0,"HyperDash":false},{"StartTime":74113.0,"Position":188.0,"HyperDash":false},{"StartTime":74180.0,"Position":177.0,"HyperDash":false},{"StartTime":74284.0,"Position":188.0,"HyperDash":false}]},{"StartTime":74456.0,"Objects":[{"StartTime":74456.0,"Position":188.0,"HyperDash":false}]},{"StartTime":74628.0,"Objects":[{"StartTime":74628.0,"Position":260.0,"HyperDash":false},{"StartTime":74695.0,"Position":292.008942,"HyperDash":false},{"StartTime":74799.0,"Position":311.0676,"HyperDash":false}]},{"StartTime":74971.0,"Objects":[{"StartTime":74971.0,"Position":361.0,"HyperDash":false},{"StartTime":75038.0,"Position":333.214569,"HyperDash":false},{"StartTime":75142.0,"Position":310.502869,"HyperDash":false}]},{"StartTime":75313.0,"Objects":[{"StartTime":75313.0,"Position":260.0,"HyperDash":false},{"StartTime":75380.0,"Position":290.008942,"HyperDash":false},{"StartTime":75484.0,"Position":311.0676,"HyperDash":false}]},{"StartTime":75656.0,"Objects":[{"StartTime":75656.0,"Position":360.0,"HyperDash":false},{"StartTime":75723.0,"Position":337.803131,"HyperDash":false},{"StartTime":75827.0,"Position":311.005,"HyperDash":false}]},{"StartTime":75999.0,"Objects":[{"StartTime":75999.0,"Position":49.0,"HyperDash":false},{"StartTime":76063.0,"Position":21.0,"HyperDash":false},{"StartTime":76127.0,"Position":193.0,"HyperDash":false},{"StartTime":76191.0,"Position":52.0,"HyperDash":false},{"StartTime":76256.0,"Position":466.0,"HyperDash":false},{"StartTime":76320.0,"Position":135.0,"HyperDash":false},{"StartTime":76384.0,"Position":121.0,"HyperDash":false},{"StartTime":76449.0,"Position":427.0,"HyperDash":false},{"StartTime":76513.0,"Position":176.0,"HyperDash":false},{"StartTime":76577.0,"Position":96.0,"HyperDash":false},{"StartTime":76642.0,"Position":345.0,"HyperDash":false},{"StartTime":76706.0,"Position":11.0,"HyperDash":false},{"StartTime":76770.0,"Position":393.0,"HyperDash":false},{"StartTime":76835.0,"Position":440.0,"HyperDash":false},{"StartTime":76899.0,"Position":179.0,"HyperDash":false},{"StartTime":76963.0,"Position":470.0,"HyperDash":false},{"StartTime":77028.0,"Position":89.0,"HyperDash":false}]},{"StartTime":77371.0,"Objects":[{"StartTime":77371.0,"Position":48.0,"HyperDash":false},{"StartTime":77456.0,"Position":59.0,"HyperDash":false},{"StartTime":77542.0,"Position":48.0,"HyperDash":false},{"StartTime":77609.0,"Position":67.0,"HyperDash":false},{"StartTime":77713.0,"Position":48.0,"HyperDash":false}]},{"StartTime":78056.0,"Objects":[{"StartTime":78056.0,"Position":152.0,"HyperDash":false},{"StartTime":78141.0,"Position":162.0,"HyperDash":false},{"StartTime":78227.0,"Position":152.0,"HyperDash":false},{"StartTime":78294.0,"Position":135.0,"HyperDash":false},{"StartTime":78398.0,"Position":152.0,"HyperDash":false}]},{"StartTime":78742.0,"Objects":[{"StartTime":78742.0,"Position":152.0,"HyperDash":false},{"StartTime":78827.0,"Position":154.0,"HyperDash":false},{"StartTime":78913.0,"Position":152.0,"HyperDash":false},{"StartTime":78980.0,"Position":138.0,"HyperDash":false},{"StartTime":79084.0,"Position":152.0,"HyperDash":false}]},{"StartTime":79427.0,"Objects":[{"StartTime":79427.0,"Position":256.0,"HyperDash":false},{"StartTime":79512.0,"Position":248.0,"HyperDash":false},{"StartTime":79598.0,"Position":256.0,"HyperDash":false},{"StartTime":79665.0,"Position":270.0,"HyperDash":false},{"StartTime":79769.0,"Position":256.0,"HyperDash":false}]},{"StartTime":80113.0,"Objects":[{"StartTime":80113.0,"Position":256.0,"HyperDash":false},{"StartTime":80198.0,"Position":249.0,"HyperDash":false},{"StartTime":80284.0,"Position":256.0,"HyperDash":false},{"StartTime":80369.0,"Position":245.0,"HyperDash":false},{"StartTime":80455.0,"Position":256.0,"HyperDash":false},{"StartTime":80541.0,"Position":244.0,"HyperDash":false},{"StartTime":80627.0,"Position":256.0,"HyperDash":false},{"StartTime":80694.0,"Position":265.0,"HyperDash":false},{"StartTime":80798.0,"Position":256.0,"HyperDash":false}]},{"StartTime":81142.0,"Objects":[{"StartTime":81142.0,"Position":256.0,"HyperDash":false},{"StartTime":81227.0,"Position":292.744537,"HyperDash":false},{"StartTime":81313.0,"Position":325.897827,"HyperDash":false},{"StartTime":81398.0,"Position":358.642334,"HyperDash":false},{"StartTime":81484.0,"Position":396.0,"HyperDash":false},{"StartTime":81570.0,"Position":346.0511,"HyperDash":false},{"StartTime":81656.0,"Position":325.897827,"HyperDash":false},{"StartTime":81723.0,"Position":285.510956,"HyperDash":false},{"StartTime":81827.0,"Position":256.0,"HyperDash":false}]},{"StartTime":82171.0,"Objects":[{"StartTime":82171.0,"Position":468.0,"HyperDash":false}]},{"StartTime":82513.0,"Objects":[{"StartTime":82513.0,"Position":468.0,"HyperDash":false}]},{"StartTime":82856.0,"Objects":[{"StartTime":82856.0,"Position":352.0,"HyperDash":false},{"StartTime":82941.0,"Position":368.54422,"HyperDash":false},{"StartTime":83027.0,"Position":407.5205,"HyperDash":false},{"StartTime":83112.0,"Position":374.08432,"HyperDash":false},{"StartTime":83198.0,"Position":352.0,"HyperDash":false},{"StartTime":83266.0,"Position":371.819336,"HyperDash":false},{"StartTime":83370.0,"Position":407.5205,"HyperDash":false}]},{"StartTime":83542.0,"Objects":[{"StartTime":83542.0,"Position":448.0,"HyperDash":false}]},{"StartTime":83885.0,"Objects":[{"StartTime":83885.0,"Position":324.0,"HyperDash":false}]},{"StartTime":84228.0,"Objects":[{"StartTime":84228.0,"Position":160.0,"HyperDash":false},{"StartTime":84313.0,"Position":124.276367,"HyperDash":false},{"StartTime":84399.0,"Position":104.117874,"HyperDash":false},{"StartTime":84484.0,"Position":150.732773,"HyperDash":false},{"StartTime":84570.0,"Position":160.0,"HyperDash":false},{"StartTime":84638.0,"Position":132.038544,"HyperDash":false},{"StartTime":84742.0,"Position":104.117874,"HyperDash":false}]},{"StartTime":84913.0,"Objects":[{"StartTime":84913.0,"Position":64.0,"HyperDash":false}]},{"StartTime":85256.0,"Objects":[{"StartTime":85256.0,"Position":188.0,"HyperDash":false}]},{"StartTime":85599.0,"Objects":[{"StartTime":85599.0,"Position":352.0,"HyperDash":false},{"StartTime":85684.0,"Position":376.7821,"HyperDash":false},{"StartTime":85770.0,"Position":408.0,"HyperDash":false},{"StartTime":85855.0,"Position":395.326843,"HyperDash":false},{"StartTime":85941.0,"Position":352.0,"HyperDash":false},{"StartTime":86009.0,"Position":380.007782,"HyperDash":false},{"StartTime":86113.0,"Position":408.0,"HyperDash":false}]},{"StartTime":86285.0,"Objects":[{"StartTime":86285.0,"Position":356.0,"HyperDash":false}]},{"StartTime":86456.0,"Objects":[{"StartTime":86456.0,"Position":356.0,"HyperDash":false}]},{"StartTime":86628.0,"Objects":[{"StartTime":86628.0,"Position":356.0,"HyperDash":false}]},{"StartTime":86971.0,"Objects":[{"StartTime":86971.0,"Position":160.0,"HyperDash":false},{"StartTime":87056.0,"Position":162.926041,"HyperDash":false},{"StartTime":87142.0,"Position":133.659821,"HyperDash":false},{"StartTime":87227.0,"Position":161.695328,"HyperDash":false},{"StartTime":87313.0,"Position":160.0,"HyperDash":false},{"StartTime":87399.0,"Position":140.849136,"HyperDash":false},{"StartTime":87485.0,"Position":133.659821,"HyperDash":false},{"StartTime":87552.0,"Position":142.003632,"HyperDash":false},{"StartTime":87656.0,"Position":160.0,"HyperDash":false}]},{"StartTime":87999.0,"Objects":[{"StartTime":87999.0,"Position":256.0,"HyperDash":false}]},{"StartTime":88342.0,"Objects":[{"StartTime":88342.0,"Position":104.0,"HyperDash":false},{"StartTime":88427.0,"Position":100.104553,"HyperDash":false},{"StartTime":88513.0,"Position":76.88288,"HyperDash":false},{"StartTime":88598.0,"Position":61.4504166,"HyperDash":false},{"StartTime":88684.0,"Position":80.21796,"HyperDash":false},{"StartTime":88770.0,"Position":95.16115,"HyperDash":false},{"StartTime":88856.0,"Position":115.62986,"HyperDash":false},{"StartTime":88941.0,"Position":138.0912,"HyperDash":false},{"StartTime":89027.0,"Position":175.517044,"HyperDash":false},{"StartTime":89113.0,"Position":220.3342,"HyperDash":false},{"StartTime":89199.0,"Position":244.674866,"HyperDash":false},{"StartTime":89284.0,"Position":264.2282,"HyperDash":false},{"StartTime":89370.0,"Position":300.583649,"HyperDash":false},{"StartTime":89456.0,"Position":336.589539,"HyperDash":false},{"StartTime":89542.0,"Position":332.588257,"HyperDash":false},{"StartTime":89609.0,"Position":357.060455,"HyperDash":false},{"StartTime":89713.0,"Position":343.816345,"HyperDash":false}]},{"StartTime":89885.0,"Objects":[{"StartTime":89885.0,"Position":408.0,"HyperDash":false}]},{"StartTime":90056.0,"Objects":[{"StartTime":90056.0,"Position":416.0,"HyperDash":false}]},{"StartTime":90228.0,"Objects":[{"StartTime":90228.0,"Position":400.0,"HyperDash":false}]},{"StartTime":90399.0,"Objects":[{"StartTime":90399.0,"Position":360.0,"HyperDash":false},{"StartTime":90484.0,"Position":325.326477,"HyperDash":false},{"StartTime":90570.0,"Position":314.08017,"HyperDash":false},{"StartTime":90655.0,"Position":295.765259,"HyperDash":false},{"StartTime":90741.0,"Position":250.349167,"HyperDash":false},{"StartTime":90827.0,"Position":234.540588,"HyperDash":false},{"StartTime":90913.0,"Position":180.487732,"HyperDash":false},{"StartTime":90998.0,"Position":158.6242,"HyperDash":false},{"StartTime":91084.0,"Position":114.161362,"HyperDash":false},{"StartTime":91170.0,"Position":64.53248,"HyperDash":false},{"StartTime":91256.0,"Position":58.7642,"HyperDash":false},{"StartTime":91323.0,"Position":33.0224953,"HyperDash":false},{"StartTime":91427.0,"Position":23.1158314,"HyperDash":false}]},{"StartTime":91599.0,"Objects":[{"StartTime":91599.0,"Position":60.0,"HyperDash":false}]},{"StartTime":91771.0,"Objects":[{"StartTime":91771.0,"Position":24.0,"HyperDash":false},{"StartTime":91856.0,"Position":42.1049347,"HyperDash":false},{"StartTime":91942.0,"Position":82.55228,"HyperDash":false},{"StartTime":92009.0,"Position":124.493813,"HyperDash":false},{"StartTime":92113.0,"Position":141.104553,"HyperDash":false}]},{"StartTime":92285.0,"Objects":[{"StartTime":92285.0,"Position":339.0,"HyperDash":false},{"StartTime":92381.0,"Position":342.0,"HyperDash":false},{"StartTime":92477.0,"Position":249.0,"HyperDash":false},{"StartTime":92574.0,"Position":235.0,"HyperDash":false},{"StartTime":92670.0,"Position":323.0,"HyperDash":false},{"StartTime":92767.0,"Position":365.0,"HyperDash":false},{"StartTime":92863.0,"Position":74.0,"HyperDash":false},{"StartTime":92960.0,"Position":281.0,"HyperDash":false},{"StartTime":93056.0,"Position":398.0,"HyperDash":false},{"StartTime":93152.0,"Position":335.0,"HyperDash":false},{"StartTime":93249.0,"Position":388.0,"HyperDash":false},{"StartTime":93345.0,"Position":228.0,"HyperDash":false},{"StartTime":93442.0,"Position":323.0,"HyperDash":false},{"StartTime":93538.0,"Position":441.0,"HyperDash":false},{"StartTime":93635.0,"Position":442.0,"HyperDash":false},{"StartTime":93731.0,"Position":278.0,"HyperDash":false},{"StartTime":93828.0,"Position":90.0,"HyperDash":false}]},{"StartTime":94513.0,"Objects":[{"StartTime":94513.0,"Position":64.0,"HyperDash":false},{"StartTime":94598.0,"Position":68.14916,"HyperDash":false},{"StartTime":94684.0,"Position":62.2626343,"HyperDash":false},{"StartTime":94769.0,"Position":86.91272,"HyperDash":false},{"StartTime":94855.0,"Position":102.010681,"HyperDash":false},{"StartTime":94941.0,"Position":141.25354,"HyperDash":false},{"StartTime":95027.0,"Position":166.435471,"HyperDash":false},{"StartTime":95094.0,"Position":206.542572,"HyperDash":false},{"StartTime":95198.0,"Position":230.41568,"HyperDash":false}]},{"StartTime":95371.0,"Objects":[{"StartTime":95371.0,"Position":300.0,"HyperDash":false}]},{"StartTime":95542.0,"Objects":[{"StartTime":95542.0,"Position":340.0,"HyperDash":false}]},{"StartTime":95713.0,"Objects":[{"StartTime":95713.0,"Position":404.0,"HyperDash":false}]},{"StartTime":95885.0,"Objects":[{"StartTime":95885.0,"Position":448.0,"HyperDash":false},{"StartTime":95970.0,"Position":440.850861,"HyperDash":false},{"StartTime":96056.0,"Position":449.737366,"HyperDash":false},{"StartTime":96141.0,"Position":429.08728,"HyperDash":false},{"StartTime":96227.0,"Position":409.989319,"HyperDash":false},{"StartTime":96313.0,"Position":361.74646,"HyperDash":false},{"StartTime":96399.0,"Position":345.564545,"HyperDash":false},{"StartTime":96466.0,"Position":303.457428,"HyperDash":false},{"StartTime":96570.0,"Position":281.58432,"HyperDash":false}]},{"StartTime":96913.0,"Objects":[{"StartTime":96913.0,"Position":256.0,"HyperDash":false}]},{"StartTime":97256.0,"Objects":[{"StartTime":97256.0,"Position":464.0,"HyperDash":false},{"StartTime":97341.0,"Position":440.493042,"HyperDash":false},{"StartTime":97427.0,"Position":396.852631,"HyperDash":false},{"StartTime":97494.0,"Position":353.930542,"HyperDash":false},{"StartTime":97598.0,"Position":329.726563,"HyperDash":false}]},{"StartTime":97771.0,"Objects":[{"StartTime":97771.0,"Position":252.0,"HyperDash":false}]},{"StartTime":97942.0,"Objects":[{"StartTime":97942.0,"Position":176.0,"HyperDash":false},{"StartTime":98027.0,"Position":129.262726,"HyperDash":false},{"StartTime":98113.0,"Position":106.0,"HyperDash":false},{"StartTime":98198.0,"Position":154.620514,"HyperDash":false},{"StartTime":98284.0,"Position":176.0,"HyperDash":false},{"StartTime":98370.0,"Position":142.08757,"HyperDash":false},{"StartTime":98456.0,"Position":106.0,"HyperDash":false},{"StartTime":98541.0,"Position":132.795654,"HyperDash":false},{"StartTime":98627.0,"Position":176.0,"HyperDash":false},{"StartTime":98713.0,"Position":145.912415,"HyperDash":false},{"StartTime":98799.0,"Position":106.0,"HyperDash":false},{"StartTime":98884.0,"Position":134.9708,"HyperDash":false},{"StartTime":98970.0,"Position":176.0,"HyperDash":false},{"StartTime":99038.0,"Position":137.093414,"HyperDash":false},{"StartTime":99141.0,"Position":106.0,"HyperDash":false}]},{"StartTime":99313.0,"Objects":[{"StartTime":99313.0,"Position":28.0,"HyperDash":false},{"StartTime":99398.0,"Position":26.2349854,"HyperDash":false},{"StartTime":99484.0,"Position":17.7651138,"HyperDash":false},{"StartTime":99569.0,"Position":33.3757133,"HyperDash":false},{"StartTime":99655.0,"Position":31.6727753,"HyperDash":false},{"StartTime":99741.0,"Position":33.7641869,"HyperDash":false},{"StartTime":99827.0,"Position":72.2299042,"HyperDash":false},{"StartTime":99912.0,"Position":92.74443,"HyperDash":false},{"StartTime":99998.0,"Position":133.558716,"HyperDash":false},{"StartTime":100084.0,"Position":158.430649,"HyperDash":false},{"StartTime":100170.0,"Position":202.717,"HyperDash":false},{"StartTime":100237.0,"Position":217.776627,"HyperDash":false},{"StartTime":100341.0,"Position":255.047836,"HyperDash":false}]},{"StartTime":100685.0,"Objects":[{"StartTime":100685.0,"Position":484.0,"HyperDash":false},{"StartTime":100770.0,"Position":489.764954,"HyperDash":false},{"StartTime":100856.0,"Position":494.2329,"HyperDash":false},{"StartTime":100941.0,"Position":504.6108,"HyperDash":false},{"StartTime":101027.0,"Position":480.2734,"HyperDash":false},{"StartTime":101113.0,"Position":448.084351,"HyperDash":false},{"StartTime":101199.0,"Position":439.444244,"HyperDash":false},{"StartTime":101284.0,"Position":426.701172,"HyperDash":false},{"StartTime":101370.0,"Position":377.68396,"HyperDash":false},{"StartTime":101456.0,"Position":326.754578,"HyperDash":false},{"StartTime":101542.0,"Position":308.535339,"HyperDash":false},{"StartTime":101609.0,"Position":299.317078,"HyperDash":false},{"StartTime":101713.0,"Position":254.267319,"HyperDash":false}]},{"StartTime":102056.0,"Objects":[{"StartTime":102056.0,"Position":160.0,"HyperDash":false},{"StartTime":102141.0,"Position":171.288788,"HyperDash":false},{"StartTime":102227.0,"Position":190.8506,"HyperDash":false},{"StartTime":102312.0,"Position":206.4524,"HyperDash":false},{"StartTime":102398.0,"Position":256.016479,"HyperDash":false},{"StartTime":102484.0,"Position":285.578949,"HyperDash":false},{"StartTime":102570.0,"Position":321.497925,"HyperDash":false},{"StartTime":102655.0,"Position":361.7266,"HyperDash":false},{"StartTime":102741.0,"Position":352.033936,"HyperDash":false},{"StartTime":102827.0,"Position":339.916229,"HyperDash":false},{"StartTime":102913.0,"Position":321.497925,"HyperDash":false},{"StartTime":102998.0,"Position":307.967651,"HyperDash":false},{"StartTime":103084.0,"Position":256.424927,"HyperDash":false},{"StartTime":103170.0,"Position":225.84108,"HyperDash":false},{"StartTime":103256.0,"Position":190.8506,"HyperDash":false},{"StartTime":103323.0,"Position":189.241409,"HyperDash":false},{"StartTime":103427.0,"Position":160.0,"HyperDash":false}]},{"StartTime":103771.0,"Objects":[{"StartTime":103771.0,"Position":48.0,"HyperDash":false}]},{"StartTime":104113.0,"Objects":[{"StartTime":104113.0,"Position":256.0,"HyperDash":false}]},{"StartTime":104456.0,"Objects":[{"StartTime":104456.0,"Position":464.0,"HyperDash":false}]},{"StartTime":104799.0,"Objects":[{"StartTime":104799.0,"Position":352.0,"HyperDash":false}]},{"StartTime":104971.0,"Objects":[{"StartTime":104971.0,"Position":272.0,"HyperDash":false},{"StartTime":105056.0,"Position":237.0,"HyperDash":false},{"StartTime":105142.0,"Position":272.0,"HyperDash":false}]},{"StartTime":105313.0,"Objects":[{"StartTime":105313.0,"Position":240.0,"HyperDash":false},{"StartTime":105398.0,"Position":275.0,"HyperDash":false},{"StartTime":105484.0,"Position":240.0,"HyperDash":false}]},{"StartTime":105656.0,"Objects":[{"StartTime":105656.0,"Position":272.0,"HyperDash":false},{"StartTime":105741.0,"Position":237.0,"HyperDash":false},{"StartTime":105827.0,"Position":272.0,"HyperDash":false}]},{"StartTime":105999.0,"Objects":[{"StartTime":105999.0,"Position":240.0,"HyperDash":false},{"StartTime":106084.0,"Position":275.0,"HyperDash":false},{"StartTime":106170.0,"Position":240.0,"HyperDash":false}]},{"StartTime":106342.0,"Objects":[{"StartTime":106342.0,"Position":168.0,"HyperDash":false},{"StartTime":106409.0,"Position":144.031464,"HyperDash":false},{"StartTime":106513.0,"Position":104.274345,"HyperDash":false}]},{"StartTime":106685.0,"Objects":[{"StartTime":106685.0,"Position":56.0,"HyperDash":false},{"StartTime":106752.0,"Position":96.4269,"HyperDash":false},{"StartTime":106856.0,"Position":126.0,"HyperDash":false}]},{"StartTime":107028.0,"Objects":[{"StartTime":107028.0,"Position":104.0,"HyperDash":false},{"StartTime":107095.0,"Position":137.981888,"HyperDash":false},{"StartTime":107199.0,"Position":167.759735,"HyperDash":false}]},{"StartTime":107371.0,"Objects":[{"StartTime":107371.0,"Position":256.0,"HyperDash":false}]},{"StartTime":107542.0,"Objects":[{"StartTime":107542.0,"Position":344.0,"HyperDash":false},{"StartTime":107609.0,"Position":367.968536,"HyperDash":false},{"StartTime":107713.0,"Position":407.725647,"HyperDash":false}]},{"StartTime":107885.0,"Objects":[{"StartTime":107885.0,"Position":456.0,"HyperDash":false},{"StartTime":107952.0,"Position":441.5731,"HyperDash":false},{"StartTime":108056.0,"Position":386.0,"HyperDash":false}]},{"StartTime":108228.0,"Objects":[{"StartTime":108228.0,"Position":408.0,"HyperDash":false},{"StartTime":108295.0,"Position":368.018127,"HyperDash":false},{"StartTime":108399.0,"Position":344.240265,"HyperDash":false}]},{"StartTime":108571.0,"Objects":[{"StartTime":108571.0,"Position":256.0,"HyperDash":false},{"StartTime":108628.0,"Position":256.0,"HyperDash":false},{"StartTime":108685.0,"Position":256.0,"HyperDash":false},{"StartTime":108742.0,"Position":256.0,"HyperDash":false},{"StartTime":108799.0,"Position":256.0,"HyperDash":false},{"StartTime":108856.0,"Position":256.0,"HyperDash":false},{"StartTime":108913.0,"Position":256.0,"HyperDash":false}]},{"StartTime":108999.0,"Objects":[{"StartTime":108999.0,"Position":152.0,"HyperDash":false},{"StartTime":109057.0,"Position":321.0,"HyperDash":false},{"StartTime":109116.0,"Position":303.0,"HyperDash":false},{"StartTime":109175.0,"Position":259.0,"HyperDash":false},{"StartTime":109234.0,"Position":186.0,"HyperDash":false},{"StartTime":109293.0,"Position":140.0,"HyperDash":false},{"StartTime":109352.0,"Position":207.0,"HyperDash":false},{"StartTime":109411.0,"Position":278.0,"HyperDash":false},{"StartTime":109470.0,"Position":223.0,"HyperDash":false},{"StartTime":109529.0,"Position":389.0,"HyperDash":false},{"StartTime":109588.0,"Position":245.0,"HyperDash":false},{"StartTime":109647.0,"Position":400.0,"HyperDash":false},{"StartTime":109706.0,"Position":445.0,"HyperDash":false},{"StartTime":109765.0,"Position":443.0,"HyperDash":false},{"StartTime":109824.0,"Position":245.0,"HyperDash":false},{"StartTime":109883.0,"Position":259.0,"HyperDash":false},{"StartTime":109942.0,"Position":422.0,"HyperDash":false}]},{"StartTime":110285.0,"Objects":[{"StartTime":110285.0,"Position":168.0,"HyperDash":false},{"StartTime":110352.0,"Position":172.393753,"HyperDash":false},{"StartTime":110456.0,"Position":217.497467,"HyperDash":false}]},{"StartTime":110628.0,"Objects":[{"StartTime":110628.0,"Position":344.0,"HyperDash":false},{"StartTime":110695.0,"Position":329.606262,"HyperDash":false},{"StartTime":110799.0,"Position":294.502533,"HyperDash":false}]},{"StartTime":110971.0,"Objects":[{"StartTime":110971.0,"Position":207.0,"HyperDash":false},{"StartTime":111038.0,"Position":237.393753,"HyperDash":false},{"StartTime":111142.0,"Position":256.497467,"HyperDash":false}]},{"StartTime":111313.0,"Objects":[{"StartTime":111313.0,"Position":305.0,"HyperDash":false},{"StartTime":111380.0,"Position":285.606262,"HyperDash":false},{"StartTime":111484.0,"Position":255.502533,"HyperDash":false}]},{"StartTime":111656.0,"Objects":[{"StartTime":111656.0,"Position":216.0,"HyperDash":false},{"StartTime":111741.0,"Position":222.948441,"HyperDash":false},{"StartTime":111827.0,"Position":256.131561,"HyperDash":false},{"StartTime":111912.0,"Position":267.080017,"HyperDash":false},{"StartTime":111998.0,"Position":296.419617,"HyperDash":false},{"StartTime":112084.0,"Position":260.392944,"HyperDash":false},{"StartTime":112170.0,"Position":256.2098,"HyperDash":false},{"StartTime":112255.0,"Position":223.261353,"HyperDash":false},{"StartTime":112341.0,"Position":216.0,"HyperDash":false},{"StartTime":112427.0,"Position":243.1049,"HyperDash":false},{"StartTime":112513.0,"Position":256.288025,"HyperDash":false},{"StartTime":112580.0,"Position":290.012115,"HyperDash":false},{"StartTime":112684.0,"Position":296.419617,"HyperDash":false}]},{"StartTime":113028.0,"Objects":[{"StartTime":113028.0,"Position":352.0,"HyperDash":false}]},{"StartTime":113199.0,"Objects":[{"StartTime":113199.0,"Position":360.0,"HyperDash":false},{"StartTime":113284.0,"Position":364.341217,"HyperDash":false},{"StartTime":113370.0,"Position":360.0,"HyperDash":false}]},{"StartTime":113542.0,"Objects":[{"StartTime":113542.0,"Position":424.0,"HyperDash":false}]},{"StartTime":113713.0,"Objects":[{"StartTime":113713.0,"Position":352.0,"HyperDash":false}]},{"StartTime":113885.0,"Objects":[{"StartTime":113885.0,"Position":408.0,"HyperDash":false},{"StartTime":113970.0,"Position":441.203918,"HyperDash":false},{"StartTime":114056.0,"Position":408.0,"HyperDash":false}]},{"StartTime":114228.0,"Objects":[{"StartTime":114228.0,"Position":336.0,"HyperDash":false}]},{"StartTime":114399.0,"Objects":[{"StartTime":114399.0,"Position":352.0,"HyperDash":false}]},{"StartTime":114571.0,"Objects":[{"StartTime":114571.0,"Position":280.0,"HyperDash":false},{"StartTime":114656.0,"Position":248.695053,"HyperDash":false},{"StartTime":114742.0,"Position":280.0,"HyperDash":false}]},{"StartTime":114913.0,"Objects":[{"StartTime":114913.0,"Position":352.0,"HyperDash":false}]},{"StartTime":115085.0,"Objects":[{"StartTime":115085.0,"Position":296.0,"HyperDash":false}]},{"StartTime":115256.0,"Objects":[{"StartTime":115256.0,"Position":368.0,"HyperDash":false}]},{"StartTime":115428.0,"Objects":[{"StartTime":115428.0,"Position":424.0,"HyperDash":false}]},{"StartTime":115771.0,"Objects":[{"StartTime":115771.0,"Position":128.0,"HyperDash":false},{"StartTime":115838.0,"Position":111.239639,"HyperDash":false},{"StartTime":115942.0,"Position":60.5060654,"HyperDash":false}]},{"StartTime":116113.0,"Objects":[{"StartTime":116113.0,"Position":64.0,"HyperDash":false},{"StartTime":116180.0,"Position":98.76035,"HyperDash":false},{"StartTime":116284.0,"Position":131.493927,"HyperDash":false}]},{"StartTime":116456.0,"Objects":[{"StartTime":116456.0,"Position":136.0,"HyperDash":false},{"StartTime":116523.0,"Position":92.23965,"HyperDash":false},{"StartTime":116627.0,"Position":68.5060654,"HyperDash":false}]},{"StartTime":116798.0,"Objects":[{"StartTime":116798.0,"Position":72.0,"HyperDash":false},{"StartTime":116865.0,"Position":112.760361,"HyperDash":false},{"StartTime":116969.0,"Position":139.493927,"HyperDash":false}]},{"StartTime":117142.0,"Objects":[{"StartTime":117142.0,"Position":216.0,"HyperDash":false}]},{"StartTime":117313.0,"Objects":[{"StartTime":117313.0,"Position":216.0,"HyperDash":false}]},{"StartTime":117485.0,"Objects":[{"StartTime":117485.0,"Position":216.0,"HyperDash":false}]},{"StartTime":117828.0,"Objects":[{"StartTime":117828.0,"Position":296.0,"HyperDash":false}]},{"StartTime":117999.0,"Objects":[{"StartTime":117999.0,"Position":296.0,"HyperDash":false}]},{"StartTime":118171.0,"Objects":[{"StartTime":118171.0,"Position":296.0,"HyperDash":false}]},{"StartTime":118513.0,"Objects":[{"StartTime":118513.0,"Position":448.0,"HyperDash":false},{"StartTime":118580.0,"Position":418.681824,"HyperDash":false},{"StartTime":118684.0,"Position":391.038666,"HyperDash":false}]},{"StartTime":118856.0,"Objects":[{"StartTime":118856.0,"Position":392.0,"HyperDash":false}]},{"StartTime":118942.0,"Objects":[{"StartTime":118942.0,"Position":392.0,"HyperDash":false}]},{"StartTime":119028.0,"Objects":[{"StartTime":119028.0,"Position":392.0,"HyperDash":false}]},{"StartTime":119199.0,"Objects":[{"StartTime":119199.0,"Position":392.0,"HyperDash":false},{"StartTime":119284.0,"Position":392.0,"HyperDash":false},{"StartTime":119370.0,"Position":392.0,"HyperDash":false}]},{"StartTime":119542.0,"Objects":[{"StartTime":119542.0,"Position":464.0,"HyperDash":false}]},{"StartTime":119628.0,"Objects":[{"StartTime":119628.0,"Position":464.0,"HyperDash":false}]},{"StartTime":119713.0,"Objects":[{"StartTime":119713.0,"Position":464.0,"HyperDash":false}]},{"StartTime":119885.0,"Objects":[{"StartTime":119885.0,"Position":464.0,"HyperDash":false},{"StartTime":119970.0,"Position":443.501282,"HyperDash":false},{"StartTime":120056.0,"Position":394.59668,"HyperDash":false},{"StartTime":120141.0,"Position":351.097931,"HyperDash":false},{"StartTime":120227.0,"Position":325.193329,"HyperDash":false},{"StartTime":120313.0,"Position":287.288727,"HyperDash":false},{"StartTime":120399.0,"Position":255.384125,"HyperDash":false},{"StartTime":120484.0,"Position":213.8854,"HyperDash":false},{"StartTime":120570.0,"Position":185.980774,"HyperDash":false},{"StartTime":120656.0,"Position":160.0762,"HyperDash":false},{"StartTime":120742.0,"Position":116.1716,"HyperDash":false},{"StartTime":120809.0,"Position":79.9784546,"HyperDash":false},{"StartTime":120913.0,"Position":46.7682343,"HyperDash":false}]},{"StartTime":121256.0,"Objects":[{"StartTime":121256.0,"Position":441.0,"HyperDash":false},{"StartTime":121341.0,"Position":45.0,"HyperDash":false},{"StartTime":121427.0,"Position":92.0,"HyperDash":false},{"StartTime":121513.0,"Position":399.0,"HyperDash":false},{"StartTime":121598.0,"Position":494.0,"HyperDash":false},{"StartTime":121684.0,"Position":324.0,"HyperDash":false},{"StartTime":121770.0,"Position":31.0,"HyperDash":false},{"StartTime":121856.0,"Position":79.0,"HyperDash":false},{"StartTime":121941.0,"Position":163.0,"HyperDash":false},{"StartTime":122027.0,"Position":303.0,"HyperDash":false},{"StartTime":122113.0,"Position":462.0,"HyperDash":false},{"StartTime":122198.0,"Position":74.0,"HyperDash":false},{"StartTime":122284.0,"Position":4.0,"HyperDash":false},{"StartTime":122370.0,"Position":253.0,"HyperDash":false},{"StartTime":122456.0,"Position":159.0,"HyperDash":false},{"StartTime":122541.0,"Position":74.0,"HyperDash":false},{"StartTime":122627.0,"Position":242.0,"HyperDash":false},{"StartTime":122713.0,"Position":251.0,"HyperDash":false},{"StartTime":122798.0,"Position":146.0,"HyperDash":false},{"StartTime":122884.0,"Position":487.0,"HyperDash":false},{"StartTime":122970.0,"Position":294.0,"HyperDash":false},{"StartTime":123056.0,"Position":322.0,"HyperDash":false},{"StartTime":123141.0,"Position":208.0,"HyperDash":false},{"StartTime":123227.0,"Position":154.0,"HyperDash":false},{"StartTime":123313.0,"Position":476.0,"HyperDash":false},{"StartTime":123398.0,"Position":27.0,"HyperDash":false},{"StartTime":123484.0,"Position":377.0,"HyperDash":false},{"StartTime":123570.0,"Position":234.0,"HyperDash":false},{"StartTime":123656.0,"Position":459.0,"HyperDash":false},{"StartTime":123741.0,"Position":106.0,"HyperDash":false},{"StartTime":123827.0,"Position":321.0,"HyperDash":false},{"StartTime":123913.0,"Position":368.0,"HyperDash":false},{"StartTime":123999.0,"Position":488.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/103019.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/103019.osu new file mode 100644 index 0000000000..2f3814c57b --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/103019.osu @@ -0,0 +1,329 @@ +osu file format v9 + +[General] +StackLeniency: 0.3 +Mode: 0 + +[Difficulty] +HPDrainRate:5 +CircleSize:4 +OverallDifficulty:5 +ApproachRate:7 +SliderMultiplier:1.4 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +571.114342485549,342.857142857143,4,1,0,60,1,0 +11542,-100,4,1,0,90,0,0 +59542,-100,4,2,0,90,0,0 +60913,-100,4,1,0,90,0,0 +65028,-100,4,1,0,90,0,0 +66399,-100,4,1,0,90,0,1 +77371,-100,4,1,0,90,0,0 +86971,-200,4,1,0,90,0,0 +87999,-50,4,1,0,90,0,0 +88342,-100,4,1,0,90,0,0 +110285,-100,4,1,0,90,0,1 +119885,-100,4,1,0,100,0,0 +121574,-50,4,2,0,90,0,0 +121917,-50,4,2,0,80,0,0 +122260,-50,4,2,0,70,0,0 +122603,-50,4,2,0,60,0,0 +122946,-50,4,2,0,50,0,0 +123288,-50,4,2,0,40,0,0 +123631,-50,4,2,0,30,0,0 +123974,-50,4,2,0,5,0,0 + +[HitObjects] +184,192,571,6,0,B|168:104|256:45|344:104|328:192,1,280 +256,312,1599,2,0,B|256:160,3,140 +256,32,2971,5,0 +128,88,3313,1,0 +128,232,3656,2,0,B|128:328,2,70 +384,232,4342,2,0,B|384:328,2,70 +384,160,4856,1,0 +384,88,5028,1,0 +256,32,5371,37,2 +256,352,5713,1,2 +128,296,6056,22,0,B|72:272|72:272|72:192,1,140 +384,296,6742,2,0,B|440:272|440:272|440:192,1,140 +256,72,7428,2,0,B|224:136|296:136|256:208,2,140 +256,296,8456,1,2 +256,192,8799,12,0,11199 +256,192,11542,5,4 +60,192,11885,2,0,B|28:240|60:304|132:280,2,140,8|0|8 +256,192,12913,5,0 +452,192,13256,2,0,B|484:144|452:80|380:104,2,140,8|0|8 +256,192,14285,1,0 +88,64,14799,6,0,B|56:40,10,35 +32,192,15999,1,8 +96,220,16171,1,0 +160,248,16342,1,0 +224,120,16685,1,8 +328,24,17028,6,0,B|352:100,1,70 +412,56,17371,2,0,B|436:136,1,70,8|0 +448,192,17713,2,0,B|520:224,1,70 +472,280,18056,2,0,B|408:252,1,70,8|0 +388,320,18399,2,0,B|360:388,1,70 +300,348,18742,2,0,B|328:280,1,70,8|0 +344,216,19085,1,0 +156,192,19428,1,0 +256,76,19771,5,4 +256,76,20456,1,4 +124,324,21142,1,2 +256,372,21485,1,2 +388,324,21828,1,2 +504,32,22513,6,0,B|424:32,1,70,4|0 +448,104,22856,1,8 +376,104,23028,1,0 +360,32,23199,2,0,B|280:32,1,70 +304,104,23542,1,8 +232,104,23713,1,0 +216,32,23885,2,0,B|144:32,1,70 +160,104,24228,1,8 +88,104,24399,1,0 +72,32,24571,2,8,B|0:32,2,70,2|8|10 +8,352,25256,6,0,B|88:352,1,70 +64,280,25599,1,8 +136,280,25771,1,0 +152,352,25942,2,0,B|232:352,1,70 +208,280,26285,1,8 +280,280,26456,1,0 +296,352,26628,2,0,B|368:352,1,70 +352,280,26971,1,8 +424,280,27142,1,0 +440,352,27313,2,0,B|512:352,2,70,2|0|10 +40,228,27999,6,0,B|40:148,1,70,6|0 +112,196,28342,2,0,B|112:232,2,35,8|0|0 +184,156,28685,2,0,B|184:236,1,70,2|0 +260,188,29028,2,0,B|260:152,2,35,8|0|0 +336,188,29371,2,0,B|376:248,1,70,2|0 +440,216,29713,2,0,B|392:148,1,70,8|0 +460,124,30056,2,0,B|484:160,4,35,10|0|8|0|10 +328,72,30742,6,0,B|288:72,3,35 +256,72,31085,2,0,B|208:72,3,35,8|0|0|0 +184,72,31428,2,0,B|128:72,3,35,0|0|0|0 +112,72,31771,2,0,B|72:72,3,35,8|0|0|0 +40,72,32113,5,8 +40,216,32456,1,0 +184,216,32799,1,8 +184,360,33142,1,0 +304,288,33485,6,0,B|400:184,1,140,0|8 +256,32,34342,1,0 +256,32,34513,1,8 +136,112,34856,2,0,B|136:256,1,140,0|10 +104,320,35371,2,0,B|144:344|224:304|208:240,1,140,0|2 +212,192,35885,1,8 +408,336,36228,6,0,B|488:304|480:224,1,140,0|8 +360,56,37085,1,0 +360,56,37256,1,8 +232,120,37599,2,0,B|184:64|96:72,1,140,0|10 +56,120,38113,2,0,B|16:72|40:16|80:0|104:8,1,140,0|2 +156,4,38628,1,8 +256,100,38971,6,0,B|160:204,1,140,0|8 +256,352,39828,1,0 +256,352,39999,1,8 +376,272,40342,2,0,B|376:128,1,140,0|10 +408,64,40856,2,0,B|368:40|288:80|304:144,1,140,0|2 +300,192,41371,1,8 +104,48,41713,6,0,B|24:80|32:160,1,140,0|8 +152,328,42571,1,0 +152,328,42742,1,8 +256,232,43085,6,0,B|256:184,8,35,0|0|0|0|8|0|0|0|8 +256,92,44113,1,2 +124,140,44456,5,4 +72,92,44628,2,0,B|16:144|80:260|184:192,1,210 +256,92,45485,1,8 +388,140,45828,5,0 +440,92,45999,2,0,B|496:144|432:260|328:192,1,210 +256,92,46856,1,8 +256,232,47199,2,0,B|216:296|296:296|252:368,3,140,0|8|0|8 +392,320,48571,6,0,B|392:176,1,140,0|8 +464,184,49085,2,0,B|448:96|376:88|320:128|324:184,1,210,0|8 +187,170,49942,6,0,B|188:128|140:88|60:96|48:180,1,210,4|8 +120,180,50628,2,0,B|120:320,1,140,0|8 +257,363,51313,2,0,B|216:296|296:296|256:232,3,140,0|8|0|8 +256,92,52685,5,0 +169,196,53028,2,0,B|80:248|16:140|80:84,1,210,8|0 +124,140,53713,1,8 +68,268,54056,6,0,B|56:304,2,35 +156,296,54399,1,8 +444,268,54742,6,0,B|456:304,2,35 +356,296,55085,1,8 +356,96,55428,6,0,B|296:96|256:9|256:9|216:96|152:96,1,280 +160,20,56285,1,0 +92,56,56456,1,8 +84,132,56628,1,0 +156,96,56799,6,0,B|156:155|242:195|242:195|156:236|155:300,1,280 +92,252,57656,1,0 +88,328,57828,1,8 +148,376,57999,1,0 +155,299,58171,6,0,B|215:299|255:386|255:386|295:299|359:299,1,280 +356,376,59028,1,0 +424,336,59199,1,8 +428,260,59371,1,0 +356,298,59542,6,0,B|356:239|270:199|270:199|356:158|357:94,1,280 +424,140,60399,1,0 +428,64,60571,1,8 +360,24,60742,1,0 +284,24,60913,6,0,B|212:24,1,70 +136,24,61256,1,8 +60,24,61428,1,0 +60,36,61513,1,0 +60,48,61599,2,0,B|60:124,1,70 +60,196,61942,1,8 +136,196,62113,1,0 +136,184,62199,1,0 +136,172,62285,6,0,B|136:96,1,70 +212,104,62628,1,8 +212,180,62799,1,0 +212,192,62885,1,0 +212,204,62971,2,0,B|212:292,1,70,8|0 +136,272,63313,2,0,B|60:272,1,70,8|0 +256,192,63656,12,0,65028 +256,324,65713,6,0,B|256:360,2,35,0|0|0 +288,256,66056,1,0 +328,316,66228,1,8 +400,288,66399,6,0,B|448:264|448:204,1,70,6|0 +380,192,66742,1,8 +444,148,66913,2,0,B|420:100|360:100,1,70,2|0 +316,124,67256,2,0,B|292:96|292:96|300:64,1,70,0|10 +224,48,67599,2,0,B|196:72|196:72|164:64,1,70,0|2 +104,112,67942,2,0,B|128:140|128:140|120:172,1,70,0|10 +80,240,68285,2,0,B|108:264|108:264|140:256,1,70,0|2 +200,208,68628,2,0,B|216:128,2,70,0|10|0 +212,284,69142,5,2 +256,356,69313,1,0 +256,356,69485,1,0 +292,280,69656,1,2 +292,280,69828,1,0 +368,308,69999,1,0 +376,304,70085,1,0 +384,300,70171,1,10 +392,296,70256,1,0 +400,292,70342,1,0 +408,288,70428,1,0 +416,284,70513,2,8,B|472:240|444:156,3,140,10|10|10|10 +312,44,71885,5,2 +312,44,72056,1,0 +224,32,72228,1,8 +216,40,72313,1,0 +208,48,72399,1,2 +200,56,72485,1,0 +192,64,72571,1,0 +124,28,72742,1,0 +48,36,72913,2,0,B|60:84|100:104,1,70,10|0 +44,160,73256,6,0,B|44:228,1,70,2|0 +116,200,73599,2,0,B|116:272,1,70,10|0 +188,240,73942,2,0,B|188:312,2,70,2|0|10 +188,164,74456,1,0 +260,196,74628,6,0,B|324:256,1,70,2|0 +361,195,74971,2,0,B|311:243,1,70,8|2 +260,292,75313,2,0,B|324:232,1,70 +360,294,75656,2,0,B|311:244,1,70,10|0 +256,192,75999,12,0,77028 +48,192,77371,6,0,B|48:48,1,140 +152,192,78056,2,0,B|152:336,1,140 +152,192,78742,2,0,B|152:48,1,140 +256,192,79427,2,0,B|256:336,1,140 +256,192,80113,6,0,B|256:32,2,140 +256,304,81142,2,0,B|416:304,2,140 +468,304,82171,1,0 +468,304,82513,1,0 +352,112,82856,6,0,B|408:69,3,70 +448,128,83542,1,0 +324,192,83885,1,0 +160,112,84228,6,0,B|103:69,3,70 +64,128,84913,1,0 +188,192,85256,1,0 +352,272,85599,6,0,B|408:314,3,70 +356,364,86285,1,0 +356,364,86456,1,0 +356,364,86628,1,0 +160,272,86971,6,0,B|128:300,4,35 +256,64,87999,1,12 +104,80,88342,6,0,B|20:200|96:376|336:380|344:128,1,560,4|0 +408,100,89885,1,0 +416,168,90056,1,0 +400,236,90228,1,0 +360,296,90399,6,0,B|300:412|104:424|24:300|16:224,1,420 +60,196,91599,1,0 +24,136,91771,2,0,B|140:60,1,140 +256,192,92285,12,0,93828 +64,168,94513,6,0,B|24:272|168:376|244:280,1,280 +300,300,95371,1,0 +340,244,95542,1,0 +404,272,95713,1,0 +448,216,95885,6,0,B|488:112|344:8|268:104,1,280 +256,228,96913,1,0 +464,336,97256,6,0,B|388:296|388:364|320:324,1,140 +252,328,97771,1,0 +176,328,97942,2,0,B|104:328,7,70,0|0|0|0|0|0|8|0 +28,328,99313,6,0,B|-16:184|72:68|216:64|260:160,1,420,4|8 +484,328,100685,2,0,B|528:184|440:68|296:64|244:168,1,420,0|8 +160,264,102056,6,0,B|160:336|256:385|352:336|352:264,2,280,0|8|0 +48,152,103771,1,8 +256,72,104113,1,0 +464,152,104456,1,8 +352,264,104799,5,0 +272,264,104971,2,0,B|208:264,2,35,0|0|8 +240,184,105313,2,0,B|304:184,2,35 +272,104,105656,2,0,B|208:104,2,35,0|0|8 +240,28,105999,2,0,B|304:28,2,35 +168,64,106342,6,0,B|80:104,1,70,0|8 +56,192,106685,2,0,B|128:192,1,70 +104,291,107028,2,0,B|168:320,1,70,0|8 +256,192,107371,1,0 +344,64,107542,6,0,B|432:104,1,70,8|0 +456,192,107885,2,0,B|384:192,1,70,8|0 +408,291,108228,2,0,B|344:320,1,70,8|0 +256,192,108571,2,0,B|256:160,6,23.3333333333333,0|0|0|0|0|0|4 +256,192,108999,12,8,109942 +168,120,110285,6,0,B|232:56,1,70 +344,120,110628,2,0,B|280:56,1,70,8|0 +207,192,110971,2,0,B|271:128,1,70 +305,192,111313,2,0,B|241:128,1,70,8|0 +216,304,111656,2,0,B|256:247|256:247|296:304,3,140,0|8|0|8 +352,112,113028,5,0 +360,192,113199,2,0,B|368:256,2,35,0|0|8 +424,144,113542,1,0 +352,112,113713,1,0 +408,64,113885,2,0,B|456:48,2,35,0|0|8 +336,40,114228,1,0 +352,112,114399,1,0 +280,88,114571,2,0,B|248:72,2,35,0|0|8 +352,112,114913,1,0 +296,160,115085,1,8 +368,184,115256,1,8 +424,136,115428,1,8 +128,72,115771,6,0,B|88:56|88:88|56:72,1,70 +64,152,116113,2,0,B|104:168|104:136|136:152,1,70,8|0 +136,232,116456,2,0,B|96:216|96:248|64:232,1,70 +72,312,116798,2,0,B|112:328|112:296|144:312,1,70,8|0 +216,312,117142,5,0 +216,192,117313,1,0 +216,72,117485,1,8 +296,296,117828,5,0 +296,176,117999,1,0 +296,56,118171,1,8 +448,64,118513,6,0,B|392:104,1,70 +392,184,118856,1,8 +392,192,118942,1,0 +392,200,119028,1,0 +392,288,119199,2,0,B|392:328,2,35 +464,240,119542,1,8 +464,248,119628,1,0 +464,256,119713,1,0 +464,336,119885,6,2,B|256:360|256:360|48:336,1,420 +256,192,121256,12,0,123999 diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/104973-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/104973-expected-conversion.json new file mode 100644 index 0000000000..8a5fa1ab79 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/104973-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":11980.0,"Objects":[{"StartTime":11980.0,"Position":152.0,"HyperDash":false}]},{"StartTime":12313.0,"Objects":[{"StartTime":12313.0,"Position":344.0,"HyperDash":false}]},{"StartTime":12647.0,"Objects":[{"StartTime":12647.0,"Position":132.0,"HyperDash":false},{"StartTime":12730.0,"Position":96.8423157,"HyperDash":false},{"StartTime":12813.0,"Position":80.68463,"HyperDash":false},{"StartTime":12896.0,"Position":68.52695,"HyperDash":false},{"StartTime":12980.0,"Position":51.1263962,"HyperDash":false},{"StartTime":13054.0,"Position":84.0983,"HyperDash":false},{"StartTime":13128.0,"Position":104.070213,"HyperDash":false},{"StartTime":13202.0,"Position":106.04213,"HyperDash":false},{"StartTime":13313.0,"Position":132.0,"HyperDash":false}]},{"StartTime":13646.0,"Objects":[{"StartTime":13646.0,"Position":220.0,"HyperDash":false}]},{"StartTime":13980.0,"Objects":[{"StartTime":13980.0,"Position":240.0,"HyperDash":false},{"StartTime":14063.0,"Position":219.934647,"HyperDash":false},{"StartTime":14146.0,"Position":186.8693,"HyperDash":false},{"StartTime":14229.0,"Position":174.80394,"HyperDash":false},{"StartTime":14313.0,"Position":163.508881,"HyperDash":false},{"StartTime":14387.0,"Position":168.5069,"HyperDash":false},{"StartTime":14461.0,"Position":193.504929,"HyperDash":false},{"StartTime":14535.0,"Position":228.50296,"HyperDash":false},{"StartTime":14646.0,"Position":240.0,"HyperDash":false}]},{"StartTime":14980.0,"Objects":[{"StartTime":14980.0,"Position":316.0,"HyperDash":false}]},{"StartTime":15313.0,"Objects":[{"StartTime":15313.0,"Position":304.0,"HyperDash":false},{"StartTime":15387.0,"Position":327.87616,"HyperDash":false},{"StartTime":15461.0,"Position":334.752319,"HyperDash":false},{"StartTime":15535.0,"Position":368.628479,"HyperDash":false},{"StartTime":15646.0,"Position":393.442719,"HyperDash":false}]},{"StartTime":15980.0,"Objects":[{"StartTime":15980.0,"Position":496.0,"HyperDash":false},{"StartTime":16054.0,"Position":463.669525,"HyperDash":false},{"StartTime":16128.0,"Position":457.4449,"HyperDash":false},{"StartTime":16202.0,"Position":466.1673,"HyperDash":false},{"StartTime":16313.0,"Position":418.33728,"HyperDash":false}]},{"StartTime":16647.0,"Objects":[{"StartTime":16647.0,"Position":296.0,"HyperDash":false},{"StartTime":16730.0,"Position":288.3361,"HyperDash":false},{"StartTime":16813.0,"Position":260.278259,"HyperDash":false},{"StartTime":16896.0,"Position":216.255356,"HyperDash":false},{"StartTime":16980.0,"Position":202.409332,"HyperDash":false},{"StartTime":17063.0,"Position":195.537857,"HyperDash":false},{"StartTime":17146.0,"Position":159.494614,"HyperDash":false},{"StartTime":17229.0,"Position":152.264984,"HyperDash":false},{"StartTime":17313.0,"Position":133.499115,"HyperDash":false},{"StartTime":17396.0,"Position":132.895508,"HyperDash":false},{"StartTime":17480.0,"Position":160.2883,"HyperDash":false},{"StartTime":17563.0,"Position":181.309479,"HyperDash":false},{"StartTime":17647.0,"Position":202.409348,"HyperDash":false},{"StartTime":17721.0,"Position":206.570633,"HyperDash":false},{"StartTime":17795.0,"Position":222.901245,"HyperDash":false},{"StartTime":17869.0,"Position":247.13147,"HyperDash":false},{"StartTime":17980.0,"Position":296.0,"HyperDash":false}]},{"StartTime":18312.0,"Objects":[{"StartTime":18312.0,"Position":296.0,"HyperDash":false}]},{"StartTime":18646.0,"Objects":[{"StartTime":18646.0,"Position":276.0,"HyperDash":false}]},{"StartTime":18980.0,"Objects":[{"StartTime":18980.0,"Position":416.0,"HyperDash":false},{"StartTime":19054.0,"Position":407.972717,"HyperDash":false},{"StartTime":19128.0,"Position":394.945435,"HyperDash":false},{"StartTime":19202.0,"Position":393.918152,"HyperDash":false},{"StartTime":19313.0,"Position":384.377228,"HyperDash":false}]},{"StartTime":19646.0,"Objects":[{"StartTime":19646.0,"Position":160.0,"HyperDash":false}]},{"StartTime":19980.0,"Objects":[{"StartTime":19980.0,"Position":376.0,"HyperDash":false}]},{"StartTime":20313.0,"Objects":[{"StartTime":20313.0,"Position":168.0,"HyperDash":false},{"StartTime":20396.0,"Position":166.842316,"HyperDash":false},{"StartTime":20479.0,"Position":121.684631,"HyperDash":false},{"StartTime":20562.0,"Position":112.526947,"HyperDash":false},{"StartTime":20646.0,"Position":87.1263962,"HyperDash":false},{"StartTime":20720.0,"Position":118.0983,"HyperDash":false},{"StartTime":20794.0,"Position":140.070221,"HyperDash":false},{"StartTime":20868.0,"Position":158.04213,"HyperDash":false},{"StartTime":20979.0,"Position":168.0,"HyperDash":false}]},{"StartTime":21313.0,"Objects":[{"StartTime":21313.0,"Position":232.0,"HyperDash":false},{"StartTime":21396.0,"Position":222.713379,"HyperDash":false},{"StartTime":21479.0,"Position":200.426743,"HyperDash":false},{"StartTime":21562.0,"Position":168.140121,"HyperDash":false},{"StartTime":21646.0,"Position":134.560883,"HyperDash":false},{"StartTime":21720.0,"Position":139.21402,"HyperDash":false},{"StartTime":21794.0,"Position":182.867157,"HyperDash":false},{"StartTime":21868.0,"Position":187.5203,"HyperDash":false},{"StartTime":21979.0,"Position":232.0,"HyperDash":false}]},{"StartTime":22647.0,"Objects":[{"StartTime":22647.0,"Position":453.0,"HyperDash":false}]},{"StartTime":22980.0,"Objects":[{"StartTime":22980.0,"Position":363.0,"HyperDash":false}]},{"StartTime":23313.0,"Objects":[{"StartTime":23313.0,"Position":309.0,"HyperDash":false}]},{"StartTime":23647.0,"Objects":[{"StartTime":23647.0,"Position":448.0,"HyperDash":false}]},{"StartTime":23980.0,"Objects":[{"StartTime":23980.0,"Position":336.0,"HyperDash":false},{"StartTime":24063.0,"Position":321.0,"HyperDash":false},{"StartTime":24146.0,"Position":354.0,"HyperDash":false},{"StartTime":24229.0,"Position":328.0,"HyperDash":false},{"StartTime":24313.0,"Position":336.0,"HyperDash":false},{"StartTime":24387.0,"Position":352.0,"HyperDash":false},{"StartTime":24461.0,"Position":339.0,"HyperDash":false},{"StartTime":24535.0,"Position":326.0,"HyperDash":false},{"StartTime":24646.0,"Position":336.0,"HyperDash":false}]},{"StartTime":24980.0,"Objects":[{"StartTime":24980.0,"Position":176.0,"HyperDash":false}]},{"StartTime":25313.0,"Objects":[{"StartTime":25313.0,"Position":48.0,"HyperDash":false}]},{"StartTime":25647.0,"Objects":[{"StartTime":25647.0,"Position":228.0,"HyperDash":false}]},{"StartTime":25979.0,"Objects":[{"StartTime":25979.0,"Position":36.0,"HyperDash":false}]},{"StartTime":26313.0,"Objects":[{"StartTime":26313.0,"Position":176.0,"HyperDash":false}]},{"StartTime":26646.0,"Objects":[{"StartTime":26646.0,"Position":132.0,"HyperDash":false},{"StartTime":26729.0,"Position":157.86972,"HyperDash":false},{"StartTime":26812.0,"Position":171.739441,"HyperDash":false},{"StartTime":26895.0,"Position":206.609161,"HyperDash":false},{"StartTime":26979.0,"Position":231.7785,"HyperDash":false},{"StartTime":27053.0,"Position":191.605515,"HyperDash":false},{"StartTime":27127.0,"Position":171.43251,"HyperDash":false},{"StartTime":27201.0,"Position":173.2595,"HyperDash":false},{"StartTime":27312.0,"Position":132.0,"HyperDash":false}]},{"StartTime":27647.0,"Objects":[{"StartTime":27647.0,"Position":256.0,"HyperDash":false}]},{"StartTime":27980.0,"Objects":[{"StartTime":27980.0,"Position":404.0,"HyperDash":false},{"StartTime":28054.0,"Position":410.368652,"HyperDash":false},{"StartTime":28128.0,"Position":420.008545,"HyperDash":false},{"StartTime":28202.0,"Position":448.395325,"HyperDash":false},{"StartTime":28313.0,"Position":467.645935,"HyperDash":false}]},{"StartTime":28646.0,"Objects":[{"StartTime":28646.0,"Position":220.0,"HyperDash":false}]},{"StartTime":28980.0,"Objects":[{"StartTime":28980.0,"Position":348.0,"HyperDash":false}]},{"StartTime":29313.0,"Objects":[{"StartTime":29313.0,"Position":336.0,"HyperDash":false},{"StartTime":29387.0,"Position":303.6809,"HyperDash":false},{"StartTime":29461.0,"Position":303.67157,"HyperDash":false},{"StartTime":29535.0,"Position":277.6959,"HyperDash":false},{"StartTime":29646.0,"Position":260.962,"HyperDash":false}]},{"StartTime":29979.0,"Objects":[{"StartTime":29979.0,"Position":360.0,"HyperDash":false}]},{"StartTime":30313.0,"Objects":[{"StartTime":30313.0,"Position":248.0,"HyperDash":false}]},{"StartTime":30646.0,"Objects":[{"StartTime":30646.0,"Position":360.0,"HyperDash":false}]},{"StartTime":31313.0,"Objects":[{"StartTime":31313.0,"Position":24.0,"HyperDash":false}]},{"StartTime":31646.0,"Objects":[{"StartTime":31646.0,"Position":96.0,"HyperDash":false}]},{"StartTime":31979.0,"Objects":[{"StartTime":31979.0,"Position":116.0,"HyperDash":false}]},{"StartTime":32313.0,"Objects":[{"StartTime":32313.0,"Position":168.0,"HyperDash":false}]},{"StartTime":32647.0,"Objects":[{"StartTime":32647.0,"Position":360.0,"HyperDash":false}]},{"StartTime":33313.0,"Objects":[{"StartTime":33313.0,"Position":488.0,"HyperDash":false}]},{"StartTime":33647.0,"Objects":[{"StartTime":33647.0,"Position":488.0,"HyperDash":false},{"StartTime":33721.0,"Position":462.092926,"HyperDash":false},{"StartTime":33795.0,"Position":475.185822,"HyperDash":false},{"StartTime":33869.0,"Position":480.278748,"HyperDash":false},{"StartTime":33980.0,"Position":447.918121,"HyperDash":false}]},{"StartTime":34313.0,"Objects":[{"StartTime":34313.0,"Position":380.0,"HyperDash":false},{"StartTime":34396.0,"Position":378.853241,"HyperDash":false},{"StartTime":34479.0,"Position":357.6393,"HyperDash":false},{"StartTime":34544.0,"Position":376.301575,"HyperDash":false},{"StartTime":34646.0,"Position":380.0,"HyperDash":false}]},{"StartTime":34980.0,"Objects":[{"StartTime":34980.0,"Position":312.0,"HyperDash":false},{"StartTime":35054.0,"Position":293.8341,"HyperDash":false},{"StartTime":35128.0,"Position":274.729065,"HyperDash":false},{"StartTime":35202.0,"Position":262.3615,"HyperDash":false},{"StartTime":35313.0,"Position":217.647247,"HyperDash":false}]},{"StartTime":35646.0,"Objects":[{"StartTime":35646.0,"Position":116.0,"HyperDash":false},{"StartTime":35729.0,"Position":76.07507,"HyperDash":false},{"StartTime":35812.0,"Position":66.0,"HyperDash":false},{"StartTime":35877.0,"Position":82.36937,"HyperDash":false},{"StartTime":35979.0,"Position":116.0,"HyperDash":false}]},{"StartTime":36313.0,"Objects":[{"StartTime":36313.0,"Position":232.0,"HyperDash":false},{"StartTime":36387.0,"Position":244.2069,"HyperDash":false},{"StartTime":36461.0,"Position":281.2214,"HyperDash":false},{"StartTime":36535.0,"Position":306.592651,"HyperDash":false},{"StartTime":36646.0,"Position":327.491272,"HyperDash":false}]},{"StartTime":36813.0,"Objects":[{"StartTime":36813.0,"Position":356.0,"HyperDash":false},{"StartTime":36896.0,"Position":384.924927,"HyperDash":false},{"StartTime":36979.0,"Position":406.0,"HyperDash":false},{"StartTime":37044.0,"Position":376.6306,"HyperDash":false},{"StartTime":37146.0,"Position":356.0,"HyperDash":false}]},{"StartTime":37313.0,"Objects":[{"StartTime":37313.0,"Position":172.0,"HyperDash":false}]},{"StartTime":37646.0,"Objects":[{"StartTime":37646.0,"Position":176.0,"HyperDash":false},{"StartTime":37729.0,"Position":141.075073,"HyperDash":false},{"StartTime":37812.0,"Position":126.0,"HyperDash":false},{"StartTime":37877.0,"Position":152.36937,"HyperDash":false},{"StartTime":37979.0,"Position":176.0,"HyperDash":false}]},{"StartTime":38313.0,"Objects":[{"StartTime":38313.0,"Position":232.0,"HyperDash":false}]},{"StartTime":38647.0,"Objects":[{"StartTime":38647.0,"Position":60.0,"HyperDash":false}]},{"StartTime":38980.0,"Objects":[{"StartTime":38980.0,"Position":276.0,"HyperDash":false}]},{"StartTime":39313.0,"Objects":[{"StartTime":39313.0,"Position":60.0,"HyperDash":false},{"StartTime":39396.0,"Position":79.53542,"HyperDash":false},{"StartTime":39479.0,"Position":80.3983,"HyperDash":false},{"StartTime":39562.0,"Position":95.7953949,"HyperDash":false},{"StartTime":39646.0,"Position":96.9988861,"HyperDash":false},{"StartTime":39711.0,"Position":90.0326843,"HyperDash":false},{"StartTime":39813.0,"Position":130.8265,"HyperDash":false}]},{"StartTime":39980.0,"Objects":[{"StartTime":39980.0,"Position":148.0,"HyperDash":false},{"StartTime":40063.0,"Position":155.921555,"HyperDash":false},{"StartTime":40146.0,"Position":200.495987,"HyperDash":false},{"StartTime":40229.0,"Position":229.243881,"HyperDash":false},{"StartTime":40313.0,"Position":244.105148,"HyperDash":false},{"StartTime":40378.0,"Position":263.884155,"HyperDash":false},{"StartTime":40479.0,"Position":285.356873,"HyperDash":false}]},{"StartTime":40647.0,"Objects":[{"StartTime":40647.0,"Position":274.0,"HyperDash":false}]},{"StartTime":40980.0,"Objects":[{"StartTime":40980.0,"Position":392.0,"HyperDash":false},{"StartTime":41063.0,"Position":414.371643,"HyperDash":false},{"StartTime":41146.0,"Position":440.8901,"HyperDash":false},{"StartTime":41211.0,"Position":438.9507,"HyperDash":false},{"StartTime":41313.0,"Position":392.0,"HyperDash":false}]},{"StartTime":41647.0,"Objects":[{"StartTime":41647.0,"Position":292.0,"HyperDash":false},{"StartTime":41721.0,"Position":255.813812,"HyperDash":false},{"StartTime":41795.0,"Position":263.524658,"HyperDash":false},{"StartTime":41869.0,"Position":212.4873,"HyperDash":false},{"StartTime":41980.0,"Position":199.067825,"HyperDash":false}]},{"StartTime":42147.0,"Objects":[{"StartTime":42147.0,"Position":176.0,"HyperDash":false},{"StartTime":42212.0,"Position":183.0,"HyperDash":false},{"StartTime":42313.0,"Position":176.0,"HyperDash":false}]},{"StartTime":42480.0,"Objects":[{"StartTime":42480.0,"Position":140.0,"HyperDash":false},{"StartTime":42545.0,"Position":131.421692,"HyperDash":false},{"StartTime":42646.0,"Position":90.0,"HyperDash":false}]},{"StartTime":42980.0,"Objects":[{"StartTime":42980.0,"Position":210.0,"HyperDash":false},{"StartTime":43063.0,"Position":225.924927,"HyperDash":false},{"StartTime":43146.0,"Position":260.0,"HyperDash":false},{"StartTime":43211.0,"Position":233.63063,"HyperDash":false},{"StartTime":43313.0,"Position":210.0,"HyperDash":false}]},{"StartTime":43647.0,"Objects":[{"StartTime":43647.0,"Position":248.0,"HyperDash":false}]},{"StartTime":43980.0,"Objects":[{"StartTime":43980.0,"Position":264.0,"HyperDash":false}]},{"StartTime":44313.0,"Objects":[{"StartTime":44313.0,"Position":248.0,"HyperDash":false}]},{"StartTime":44647.0,"Objects":[{"StartTime":44647.0,"Position":344.0,"HyperDash":false},{"StartTime":44721.0,"Position":364.6328,"HyperDash":false},{"StartTime":44795.0,"Position":391.265625,"HyperDash":false},{"StartTime":44869.0,"Position":390.898438,"HyperDash":false},{"StartTime":44980.0,"Position":436.847656,"HyperDash":false}]},{"StartTime":45313.0,"Objects":[{"StartTime":45313.0,"Position":340.0,"HyperDash":false},{"StartTime":45387.0,"Position":332.927124,"HyperDash":false},{"StartTime":45461.0,"Position":320.854218,"HyperDash":false},{"StartTime":45535.0,"Position":286.7813,"HyperDash":false},{"StartTime":45646.0,"Position":272.172,"HyperDash":false}]},{"StartTime":45980.0,"Objects":[{"StartTime":45980.0,"Position":236.0,"HyperDash":false},{"StartTime":46054.0,"Position":231.452988,"HyperDash":false},{"StartTime":46128.0,"Position":230.905975,"HyperDash":false},{"StartTime":46202.0,"Position":205.358963,"HyperDash":false},{"StartTime":46313.0,"Position":197.538452,"HyperDash":false}]},{"StartTime":46647.0,"Objects":[{"StartTime":46647.0,"Position":92.0,"HyperDash":false},{"StartTime":46721.0,"Position":83.9194641,"HyperDash":false},{"StartTime":46795.0,"Position":66.01362,"HyperDash":false},{"StartTime":46869.0,"Position":83.9567261,"HyperDash":false},{"StartTime":46980.0,"Position":93.07765,"HyperDash":false}]},{"StartTime":47313.0,"Objects":[{"StartTime":47313.0,"Position":312.0,"HyperDash":false}]},{"StartTime":47647.0,"Objects":[{"StartTime":47647.0,"Position":324.0,"HyperDash":false},{"StartTime":47730.0,"Position":367.924927,"HyperDash":false},{"StartTime":47813.0,"Position":374.0,"HyperDash":false},{"StartTime":47878.0,"Position":351.6306,"HyperDash":false},{"StartTime":47980.0,"Position":324.0,"HyperDash":false}]},{"StartTime":48313.0,"Objects":[{"StartTime":48313.0,"Position":212.0,"HyperDash":false},{"StartTime":48387.0,"Position":201.990753,"HyperDash":false},{"StartTime":48461.0,"Position":179.8291,"HyperDash":false},{"StartTime":48535.0,"Position":186.227524,"HyperDash":false},{"StartTime":48646.0,"Position":213.404251,"HyperDash":false}]},{"StartTime":48980.0,"Objects":[{"StartTime":48980.0,"Position":428.0,"HyperDash":false}]},{"StartTime":49313.0,"Objects":[{"StartTime":49313.0,"Position":220.0,"HyperDash":false}]},{"StartTime":49647.0,"Objects":[{"StartTime":49647.0,"Position":256.0,"HyperDash":false},{"StartTime":49730.0,"Position":255.0,"HyperDash":false},{"StartTime":49813.0,"Position":256.0,"HyperDash":false},{"StartTime":49878.0,"Position":273.0,"HyperDash":false},{"StartTime":49980.0,"Position":256.0,"HyperDash":false}]},{"StartTime":50313.0,"Objects":[{"StartTime":50313.0,"Position":392.0,"HyperDash":false}]},{"StartTime":50647.0,"Objects":[{"StartTime":50647.0,"Position":256.0,"HyperDash":false}]},{"StartTime":50980.0,"Objects":[{"StartTime":50980.0,"Position":256.0,"HyperDash":false},{"StartTime":51063.0,"Position":268.0,"HyperDash":false},{"StartTime":51146.0,"Position":256.0,"HyperDash":false},{"StartTime":51211.0,"Position":267.0,"HyperDash":false},{"StartTime":51313.0,"Position":256.0,"HyperDash":false}]},{"StartTime":51647.0,"Objects":[{"StartTime":51647.0,"Position":200.0,"HyperDash":false},{"StartTime":51721.0,"Position":220.222229,"HyperDash":false},{"StartTime":51795.0,"Position":261.444458,"HyperDash":false},{"StartTime":51869.0,"Position":257.6667,"HyperDash":false},{"StartTime":51980.0,"Position":300.0,"HyperDash":false}]},{"StartTime":52647.0,"Objects":[{"StartTime":52647.0,"Position":136.0,"HyperDash":false}]},{"StartTime":52980.0,"Objects":[{"StartTime":52980.0,"Position":256.0,"HyperDash":false},{"StartTime":53063.0,"Position":291.9663,"HyperDash":false},{"StartTime":53146.0,"Position":284.932617,"HyperDash":false},{"StartTime":53229.0,"Position":307.898926,"HyperDash":false},{"StartTime":53313.0,"Position":340.117859,"HyperDash":false},{"StartTime":53387.0,"Position":325.425,"HyperDash":false},{"StartTime":53461.0,"Position":290.732147,"HyperDash":false},{"StartTime":53535.0,"Position":300.039276,"HyperDash":false},{"StartTime":53646.0,"Position":256.0,"HyperDash":false}]},{"StartTime":53980.0,"Objects":[{"StartTime":53980.0,"Position":384.0,"HyperDash":false}]},{"StartTime":54313.0,"Objects":[{"StartTime":54313.0,"Position":256.0,"HyperDash":false},{"StartTime":54396.0,"Position":223.033691,"HyperDash":false},{"StartTime":54479.0,"Position":213.067383,"HyperDash":false},{"StartTime":54562.0,"Position":180.101074,"HyperDash":false},{"StartTime":54646.0,"Position":171.882141,"HyperDash":false},{"StartTime":54720.0,"Position":184.575012,"HyperDash":false},{"StartTime":54794.0,"Position":201.267853,"HyperDash":false},{"StartTime":54868.0,"Position":218.960709,"HyperDash":false},{"StartTime":54979.0,"Position":256.0,"HyperDash":false}]},{"StartTime":55313.0,"Objects":[{"StartTime":55313.0,"Position":368.0,"HyperDash":false}]},{"StartTime":55647.0,"Objects":[{"StartTime":55647.0,"Position":256.0,"HyperDash":false},{"StartTime":55730.0,"Position":251.0,"HyperDash":false},{"StartTime":55813.0,"Position":251.0,"HyperDash":false},{"StartTime":55896.0,"Position":270.0,"HyperDash":false},{"StartTime":55980.0,"Position":256.0,"HyperDash":false},{"StartTime":56054.0,"Position":259.0,"HyperDash":false},{"StartTime":56128.0,"Position":244.0,"HyperDash":false},{"StartTime":56202.0,"Position":244.0,"HyperDash":false},{"StartTime":56313.0,"Position":256.0,"HyperDash":false}]},{"StartTime":56647.0,"Objects":[{"StartTime":56647.0,"Position":276.0,"HyperDash":false}]},{"StartTime":57313.0,"Objects":[{"StartTime":57313.0,"Position":488.0,"HyperDash":false}]},{"StartTime":57647.0,"Objects":[{"StartTime":57647.0,"Position":488.0,"HyperDash":false},{"StartTime":57721.0,"Position":481.4433,"HyperDash":false},{"StartTime":57795.0,"Position":483.349152,"HyperDash":false},{"StartTime":57869.0,"Position":454.0119,"HyperDash":false},{"StartTime":57980.0,"Position":458.509216,"HyperDash":false}]},{"StartTime":58313.0,"Objects":[{"StartTime":58313.0,"Position":360.0,"HyperDash":false},{"StartTime":58387.0,"Position":344.2625,"HyperDash":false},{"StartTime":58461.0,"Position":344.958252,"HyperDash":false},{"StartTime":58535.0,"Position":319.941345,"HyperDash":false},{"StartTime":58646.0,"Position":314.506317,"HyperDash":false}]},{"StartTime":58980.0,"Objects":[{"StartTime":58980.0,"Position":428.0,"HyperDash":false}]},{"StartTime":59313.0,"Objects":[{"StartTime":59313.0,"Position":260.0,"HyperDash":false}]},{"StartTime":59647.0,"Objects":[{"StartTime":59647.0,"Position":224.0,"HyperDash":false},{"StartTime":59730.0,"Position":233.954819,"HyperDash":false},{"StartTime":59813.0,"Position":211.873215,"HyperDash":false},{"StartTime":59878.0,"Position":207.570984,"HyperDash":false},{"StartTime":59980.0,"Position":224.0,"HyperDash":false}]},{"StartTime":60313.0,"Objects":[{"StartTime":60313.0,"Position":304.0,"HyperDash":false}]},{"StartTime":60647.0,"Objects":[{"StartTime":60647.0,"Position":208.0,"HyperDash":false}]},{"StartTime":60980.0,"Objects":[{"StartTime":60980.0,"Position":136.0,"HyperDash":false},{"StartTime":61063.0,"Position":100.414207,"HyperDash":false},{"StartTime":61146.0,"Position":86.6803,"HyperDash":false},{"StartTime":61211.0,"Position":91.78613,"HyperDash":false},{"StartTime":61313.0,"Position":136.0,"HyperDash":false}]},{"StartTime":61647.0,"Objects":[{"StartTime":61647.0,"Position":448.0,"HyperDash":false}]},{"StartTime":61980.0,"Objects":[{"StartTime":61980.0,"Position":256.0,"HyperDash":false}]},{"StartTime":62313.0,"Objects":[{"StartTime":62313.0,"Position":420.0,"HyperDash":false}]},{"StartTime":62647.0,"Objects":[{"StartTime":62647.0,"Position":228.0,"HyperDash":false}]},{"StartTime":62980.0,"Objects":[{"StartTime":62980.0,"Position":204.0,"HyperDash":false},{"StartTime":63063.0,"Position":197.227524,"HyperDash":false},{"StartTime":63146.0,"Position":154.305817,"HyperDash":false},{"StartTime":63211.0,"Position":183.556717,"HyperDash":false},{"StartTime":63313.0,"Position":204.0,"HyperDash":false}]},{"StartTime":63647.0,"Objects":[{"StartTime":63647.0,"Position":324.0,"HyperDash":false},{"StartTime":63721.0,"Position":356.66507,"HyperDash":false},{"StartTime":63795.0,"Position":328.949554,"HyperDash":false},{"StartTime":63869.0,"Position":341.904877,"HyperDash":false},{"StartTime":63980.0,"Position":341.121216,"HyperDash":false}]},{"StartTime":64313.0,"Objects":[{"StartTime":64313.0,"Position":180.0,"HyperDash":false}]},{"StartTime":64647.0,"Objects":[{"StartTime":64647.0,"Position":116.0,"HyperDash":false}]},{"StartTime":64980.0,"Objects":[{"StartTime":64980.0,"Position":36.0,"HyperDash":false},{"StartTime":65063.0,"Position":48.422226,"HyperDash":false},{"StartTime":65146.0,"Position":60.8444481,"HyperDash":false},{"StartTime":65229.0,"Position":42.26667,"HyperDash":false},{"StartTime":65313.0,"Position":61.7662659,"HyperDash":false},{"StartTime":65387.0,"Position":42.0404358,"HyperDash":false},{"StartTime":65461.0,"Position":31.3145981,"HyperDash":false},{"StartTime":65535.0,"Position":47.5887566,"HyperDash":false},{"StartTime":65646.0,"Position":36.0,"HyperDash":false}]},{"StartTime":65980.0,"Objects":[{"StartTime":65980.0,"Position":24.0,"HyperDash":false},{"StartTime":66063.0,"Position":33.5504036,"HyperDash":false},{"StartTime":66146.0,"Position":78.42056,"HyperDash":false},{"StartTime":66229.0,"Position":110.084938,"HyperDash":false},{"StartTime":66313.0,"Position":121.840134,"HyperDash":false},{"StartTime":66387.0,"Position":136.73616,"HyperDash":false},{"StartTime":66461.0,"Position":175.859619,"HyperDash":false},{"StartTime":66535.0,"Position":197.00679,"HyperDash":false},{"StartTime":66646.0,"Position":219.1586,"HyperDash":false}]},{"StartTime":66980.0,"Objects":[{"StartTime":66980.0,"Position":340.0,"HyperDash":false},{"StartTime":67054.0,"Position":368.672729,"HyperDash":false},{"StartTime":67128.0,"Position":380.049255,"HyperDash":false},{"StartTime":67202.0,"Position":406.45874,"HyperDash":false},{"StartTime":67313.0,"Position":423.819183,"HyperDash":false}]},{"StartTime":67647.0,"Objects":[{"StartTime":67647.0,"Position":436.0,"HyperDash":false},{"StartTime":67730.0,"Position":414.429535,"HyperDash":false},{"StartTime":67813.0,"Position":404.765259,"HyperDash":false},{"StartTime":67878.0,"Position":409.865173,"HyperDash":false},{"StartTime":67980.0,"Position":436.0,"HyperDash":false}]},{"StartTime":68313.0,"Objects":[{"StartTime":68313.0,"Position":468.0,"HyperDash":false}]},{"StartTime":68646.0,"Objects":[{"StartTime":68646.0,"Position":332.0,"HyperDash":false},{"StartTime":68720.0,"Position":334.127625,"HyperDash":false},{"StartTime":68794.0,"Position":293.255249,"HyperDash":false},{"StartTime":68868.0,"Position":281.382874,"HyperDash":false},{"StartTime":68979.0,"Position":256.074341,"HyperDash":false}]},{"StartTime":69313.0,"Objects":[{"StartTime":69313.0,"Position":272.0,"HyperDash":false},{"StartTime":69387.0,"Position":268.51,"HyperDash":false},{"StartTime":69461.0,"Position":219.019989,"HyperDash":false},{"StartTime":69535.0,"Position":233.529968,"HyperDash":false},{"StartTime":69646.0,"Position":188.794968,"HyperDash":false}]},{"StartTime":69980.0,"Objects":[{"StartTime":69980.0,"Position":208.0,"HyperDash":false},{"StartTime":70054.0,"Position":193.222229,"HyperDash":false},{"StartTime":70128.0,"Position":168.444443,"HyperDash":false},{"StartTime":70202.0,"Position":162.666656,"HyperDash":false},{"StartTime":70313.0,"Position":127.999992,"HyperDash":false}]},{"StartTime":70647.0,"Objects":[{"StartTime":70647.0,"Position":128.0,"HyperDash":false},{"StartTime":70721.0,"Position":108.251968,"HyperDash":false},{"StartTime":70795.0,"Position":105.503937,"HyperDash":false},{"StartTime":70869.0,"Position":59.7558975,"HyperDash":false},{"StartTime":70980.0,"Position":43.63385,"HyperDash":false}]},{"StartTime":71313.0,"Objects":[{"StartTime":71313.0,"Position":20.0,"HyperDash":false}]},{"StartTime":71647.0,"Objects":[{"StartTime":71647.0,"Position":72.0,"HyperDash":false},{"StartTime":71730.0,"Position":42.17414,"HyperDash":false},{"StartTime":71813.0,"Position":44.26499,"HyperDash":false},{"StartTime":71878.0,"Position":48.0091858,"HyperDash":false},{"StartTime":71980.0,"Position":72.0,"HyperDash":false}]},{"StartTime":72313.0,"Objects":[{"StartTime":72313.0,"Position":344.0,"HyperDash":false}]},{"StartTime":72647.0,"Objects":[{"StartTime":72647.0,"Position":256.0,"HyperDash":false}]},{"StartTime":72980.0,"Objects":[{"StartTime":72980.0,"Position":344.0,"HyperDash":false}]},{"StartTime":73313.0,"Objects":[{"StartTime":73313.0,"Position":192.0,"HyperDash":false}]},{"StartTime":73647.0,"Objects":[{"StartTime":73647.0,"Position":72.0,"HyperDash":false},{"StartTime":73730.0,"Position":35.075592,"HyperDash":false},{"StartTime":73813.0,"Position":34.03717,"HyperDash":false},{"StartTime":73878.0,"Position":44.7434921,"HyperDash":false},{"StartTime":73980.0,"Position":72.0,"HyperDash":false}]},{"StartTime":74313.0,"Objects":[{"StartTime":74313.0,"Position":208.0,"HyperDash":false}]},{"StartTime":74647.0,"Objects":[{"StartTime":74647.0,"Position":112.0,"HyperDash":false},{"StartTime":74730.0,"Position":135.84227,"HyperDash":false},{"StartTime":74813.0,"Position":152.1162,"HyperDash":false},{"StartTime":74896.0,"Position":171.061676,"HyperDash":false},{"StartTime":74980.0,"Position":196.921387,"HyperDash":false},{"StartTime":75063.0,"Position":218.520676,"HyperDash":false},{"StartTime":75146.0,"Position":260.403442,"HyperDash":false},{"StartTime":75229.0,"Position":258.21,"HyperDash":false},{"StartTime":75313.0,"Position":295.594574,"HyperDash":false},{"StartTime":75387.0,"Position":303.6625,"HyperDash":false},{"StartTime":75462.0,"Position":337.3289,"HyperDash":false},{"StartTime":75536.0,"Position":361.249237,"HyperDash":false},{"StartTime":75646.0,"Position":374.243744,"HyperDash":false}]},{"StartTime":75980.0,"Objects":[{"StartTime":75980.0,"Position":492.0,"HyperDash":false},{"StartTime":76063.0,"Position":469.9186,"HyperDash":false},{"StartTime":76146.0,"Position":442.890717,"HyperDash":false},{"StartTime":76229.0,"Position":461.403656,"HyperDash":false},{"StartTime":76313.0,"Position":454.9664,"HyperDash":false},{"StartTime":76387.0,"Position":453.878967,"HyperDash":false},{"StartTime":76461.0,"Position":434.64566,"HyperDash":false},{"StartTime":76535.0,"Position":431.048553,"HyperDash":false},{"StartTime":76646.0,"Position":439.4531,"HyperDash":false}]},{"StartTime":76980.0,"Objects":[{"StartTime":76980.0,"Position":320.0,"HyperDash":false},{"StartTime":77054.0,"Position":316.474152,"HyperDash":false},{"StartTime":77128.0,"Position":343.948273,"HyperDash":false},{"StartTime":77202.0,"Position":335.422424,"HyperDash":false},{"StartTime":77313.0,"Position":353.633636,"HyperDash":false}]},{"StartTime":77646.0,"Objects":[{"StartTime":77646.0,"Position":256.0,"HyperDash":false},{"StartTime":77720.0,"Position":272.0,"HyperDash":false},{"StartTime":77794.0,"Position":270.0,"HyperDash":false},{"StartTime":77868.0,"Position":249.0,"HyperDash":false},{"StartTime":77979.0,"Position":256.0,"HyperDash":false}]},{"StartTime":78313.0,"Objects":[{"StartTime":78313.0,"Position":192.0,"HyperDash":false},{"StartTime":78387.0,"Position":165.525864,"HyperDash":false},{"StartTime":78461.0,"Position":159.051712,"HyperDash":false},{"StartTime":78535.0,"Position":183.577576,"HyperDash":false},{"StartTime":78646.0,"Position":158.366364,"HyperDash":false}]},{"StartTime":78980.0,"Objects":[{"StartTime":78980.0,"Position":280.0,"HyperDash":false}]},{"StartTime":79313.0,"Objects":[{"StartTime":79313.0,"Position":320.0,"HyperDash":false},{"StartTime":79396.0,"Position":342.939819,"HyperDash":false},{"StartTime":79479.0,"Position":365.9111,"HyperDash":false},{"StartTime":79562.0,"Position":376.53537,"HyperDash":false},{"StartTime":79646.0,"Position":394.836121,"HyperDash":false},{"StartTime":79711.0,"Position":403.9664,"HyperDash":false},{"StartTime":79813.0,"Position":418.107727,"HyperDash":false}]},{"StartTime":79980.0,"Objects":[{"StartTime":79980.0,"Position":408.0,"HyperDash":false},{"StartTime":80063.0,"Position":393.190674,"HyperDash":false},{"StartTime":80146.0,"Position":340.936066,"HyperDash":false},{"StartTime":80229.0,"Position":331.749939,"HyperDash":false},{"StartTime":80313.0,"Position":313.736053,"HyperDash":false},{"StartTime":80378.0,"Position":308.810822,"HyperDash":false},{"StartTime":80480.0,"Position":274.773529,"HyperDash":false}]},{"StartTime":80647.0,"Objects":[{"StartTime":80647.0,"Position":236.0,"HyperDash":false},{"StartTime":80730.0,"Position":199.526276,"HyperDash":false},{"StartTime":80813.0,"Position":215.925659,"HyperDash":false},{"StartTime":80896.0,"Position":186.386475,"HyperDash":false},{"StartTime":80980.0,"Position":154.006546,"HyperDash":false},{"StartTime":81045.0,"Position":134.148682,"HyperDash":false},{"StartTime":81147.0,"Position":104.824638,"HyperDash":false}]},{"StartTime":81313.0,"Objects":[{"StartTime":81313.0,"Position":88.0,"HyperDash":false},{"StartTime":81396.0,"Position":110.135536,"HyperDash":false},{"StartTime":81479.0,"Position":112.874176,"HyperDash":false},{"StartTime":81562.0,"Position":127.188362,"HyperDash":false},{"StartTime":81646.0,"Position":144.1023,"HyperDash":false},{"StartTime":81711.0,"Position":162.4082,"HyperDash":false},{"StartTime":81813.0,"Position":185.452866,"HyperDash":false}]},{"StartTime":81980.0,"Objects":[{"StartTime":81980.0,"Position":240.0,"HyperDash":false}]},{"StartTime":82313.0,"Objects":[{"StartTime":82313.0,"Position":344.0,"HyperDash":false},{"StartTime":82396.0,"Position":356.924927,"HyperDash":false},{"StartTime":82479.0,"Position":394.0,"HyperDash":false},{"StartTime":82544.0,"Position":367.6306,"HyperDash":false},{"StartTime":82646.0,"Position":344.0,"HyperDash":false}]},{"StartTime":82980.0,"Objects":[{"StartTime":82980.0,"Position":96.0,"HyperDash":false}]},{"StartTime":83313.0,"Objects":[{"StartTime":83313.0,"Position":344.0,"HyperDash":false}]},{"StartTime":83647.0,"Objects":[{"StartTime":83647.0,"Position":436.0,"HyperDash":false}]},{"StartTime":83980.0,"Objects":[{"StartTime":83980.0,"Position":252.0,"HyperDash":false}]},{"StartTime":84313.0,"Objects":[{"StartTime":84313.0,"Position":228.0,"HyperDash":false},{"StartTime":84396.0,"Position":209.0,"HyperDash":false},{"StartTime":84479.0,"Position":228.0,"HyperDash":false},{"StartTime":84544.0,"Position":230.0,"HyperDash":false},{"StartTime":84646.0,"Position":228.0,"HyperDash":false}]},{"StartTime":84980.0,"Objects":[{"StartTime":84980.0,"Position":12.0,"HyperDash":false}]},{"StartTime":85313.0,"Objects":[{"StartTime":85313.0,"Position":228.0,"HyperDash":false}]},{"StartTime":85647.0,"Objects":[{"StartTime":85647.0,"Position":12.0,"HyperDash":false}]},{"StartTime":85980.0,"Objects":[{"StartTime":85980.0,"Position":228.0,"HyperDash":false}]},{"StartTime":86313.0,"Objects":[{"StartTime":86313.0,"Position":220.0,"HyperDash":false}]},{"StartTime":86647.0,"Objects":[{"StartTime":86647.0,"Position":104.0,"HyperDash":false}]},{"StartTime":86980.0,"Objects":[{"StartTime":86980.0,"Position":124.0,"HyperDash":false}]},{"StartTime":87313.0,"Objects":[{"StartTime":87313.0,"Position":104.0,"HyperDash":false},{"StartTime":87396.0,"Position":109.906219,"HyperDash":false},{"StartTime":87479.0,"Position":138.812454,"HyperDash":false},{"StartTime":87562.0,"Position":184.718689,"HyperDash":false},{"StartTime":87646.0,"Position":203.924988,"HyperDash":false},{"StartTime":87729.0,"Position":222.8312,"HyperDash":false},{"StartTime":87812.0,"Position":240.737427,"HyperDash":false},{"StartTime":87895.0,"Position":269.643677,"HyperDash":false},{"StartTime":87979.0,"Position":304.0,"HyperDash":false},{"StartTime":88062.0,"Position":273.2438,"HyperDash":false},{"StartTime":88146.0,"Position":243.0375,"HyperDash":false},{"StartTime":88229.0,"Position":229.131271,"HyperDash":false},{"StartTime":88313.0,"Position":203.924973,"HyperDash":false},{"StartTime":88387.0,"Position":182.719421,"HyperDash":false},{"StartTime":88461.0,"Position":174.513885,"HyperDash":false},{"StartTime":88535.0,"Position":126.308319,"HyperDash":false},{"StartTime":88646.0,"Position":104.0,"HyperDash":false}]},{"StartTime":88980.0,"Objects":[{"StartTime":88980.0,"Position":12.0,"HyperDash":false}]},{"StartTime":89313.0,"Objects":[{"StartTime":89313.0,"Position":196.0,"HyperDash":false}]},{"StartTime":89647.0,"Objects":[{"StartTime":89647.0,"Position":52.0,"HyperDash":false}]},{"StartTime":89980.0,"Objects":[{"StartTime":89980.0,"Position":244.0,"HyperDash":false},{"StartTime":90063.0,"Position":262.898438,"HyperDash":false},{"StartTime":90146.0,"Position":310.591949,"HyperDash":false},{"StartTime":90229.0,"Position":298.8366,"HyperDash":false},{"StartTime":90313.0,"Position":341.672577,"HyperDash":false},{"StartTime":90387.0,"Position":379.917847,"HyperDash":false},{"StartTime":90461.0,"Position":364.344666,"HyperDash":false},{"StartTime":90535.0,"Position":383.885345,"HyperDash":false},{"StartTime":90646.0,"Position":425.976227,"HyperDash":false}]},{"StartTime":90980.0,"Objects":[{"StartTime":90980.0,"Position":388.0,"HyperDash":false}]},{"StartTime":91313.0,"Objects":[{"StartTime":91313.0,"Position":312.0,"HyperDash":false},{"StartTime":91396.0,"Position":299.122223,"HyperDash":false},{"StartTime":91479.0,"Position":274.510773,"HyperDash":false},{"StartTime":91562.0,"Position":253.377548,"HyperDash":false},{"StartTime":91646.0,"Position":214.587158,"HyperDash":false},{"StartTime":91720.0,"Position":180.224533,"HyperDash":false},{"StartTime":91794.0,"Position":170.445953,"HyperDash":false},{"StartTime":91868.0,"Position":168.25264,"HyperDash":false},{"StartTime":91979.0,"Position":127.528435,"HyperDash":false}]},{"StartTime":92313.0,"Objects":[{"StartTime":92313.0,"Position":88.0,"HyperDash":false},{"StartTime":92387.0,"Position":105.606987,"HyperDash":false},{"StartTime":92461.0,"Position":128.524353,"HyperDash":false},{"StartTime":92535.0,"Position":143.583023,"HyperDash":false},{"StartTime":92646.0,"Position":182.5748,"HyperDash":false}]},{"StartTime":92980.0,"Objects":[{"StartTime":92980.0,"Position":292.0,"HyperDash":false},{"StartTime":93063.0,"Position":310.7525,"HyperDash":false},{"StartTime":93146.0,"Position":297.521576,"HyperDash":false},{"StartTime":93211.0,"Position":304.3826,"HyperDash":false},{"StartTime":93313.0,"Position":292.0,"HyperDash":false}]},{"StartTime":93647.0,"Objects":[{"StartTime":93647.0,"Position":260.0,"HyperDash":false}]},{"StartTime":93980.0,"Objects":[{"StartTime":93980.0,"Position":392.0,"HyperDash":false}]},{"StartTime":94313.0,"Objects":[{"StartTime":94313.0,"Position":424.0,"HyperDash":false}]},{"StartTime":94647.0,"Objects":[{"StartTime":94647.0,"Position":216.0,"HyperDash":false}]},{"StartTime":94980.0,"Objects":[{"StartTime":94980.0,"Position":200.0,"HyperDash":false},{"StartTime":95063.0,"Position":195.7525,"HyperDash":false},{"StartTime":95146.0,"Position":205.521576,"HyperDash":false},{"StartTime":95211.0,"Position":219.382584,"HyperDash":false},{"StartTime":95313.0,"Position":200.0,"HyperDash":false}]},{"StartTime":95647.0,"Objects":[{"StartTime":95647.0,"Position":80.0,"HyperDash":false}]},{"StartTime":95980.0,"Objects":[{"StartTime":95980.0,"Position":20.0,"HyperDash":false},{"StartTime":96063.0,"Position":23.3388672,"HyperDash":false},{"StartTime":96146.0,"Position":59.53566,"HyperDash":false},{"StartTime":96229.0,"Position":66.5166855,"HyperDash":false},{"StartTime":96313.0,"Position":108.143875,"HyperDash":false},{"StartTime":96387.0,"Position":118.3307,"HyperDash":false},{"StartTime":96461.0,"Position":144.318436,"HyperDash":false},{"StartTime":96535.0,"Position":175.625229,"HyperDash":false},{"StartTime":96646.0,"Position":203.7997,"HyperDash":false}]},{"StartTime":96980.0,"Objects":[{"StartTime":96980.0,"Position":396.0,"HyperDash":false}]},{"StartTime":97313.0,"Objects":[{"StartTime":97313.0,"Position":416.0,"HyperDash":false},{"StartTime":97396.0,"Position":391.7448,"HyperDash":false},{"StartTime":97479.0,"Position":402.383942,"HyperDash":false},{"StartTime":97562.0,"Position":373.653778,"HyperDash":false},{"StartTime":97646.0,"Position":341.410828,"HyperDash":false},{"StartTime":97720.0,"Position":351.982941,"HyperDash":false},{"StartTime":97794.0,"Position":395.896729,"HyperDash":false},{"StartTime":97868.0,"Position":388.3252,"HyperDash":false},{"StartTime":97979.0,"Position":416.0,"HyperDash":false}]},{"StartTime":98146.0,"Objects":[{"StartTime":98146.0,"Position":127.0,"HyperDash":false},{"StartTime":98224.0,"Position":161.0,"HyperDash":false},{"StartTime":98302.0,"Position":332.0,"HyperDash":false},{"StartTime":98380.0,"Position":356.0,"HyperDash":false},{"StartTime":98458.0,"Position":362.0,"HyperDash":false},{"StartTime":98536.0,"Position":347.0,"HyperDash":false},{"StartTime":98614.0,"Position":252.0,"HyperDash":false},{"StartTime":98692.0,"Position":477.0,"HyperDash":false},{"StartTime":98771.0,"Position":358.0,"HyperDash":false},{"StartTime":98849.0,"Position":17.0,"HyperDash":false},{"StartTime":98927.0,"Position":399.0,"HyperDash":false},{"StartTime":99005.0,"Position":280.0,"HyperDash":false},{"StartTime":99083.0,"Position":304.0,"HyperDash":false},{"StartTime":99161.0,"Position":221.0,"HyperDash":false},{"StartTime":99239.0,"Position":407.0,"HyperDash":false},{"StartTime":99317.0,"Position":287.0,"HyperDash":false},{"StartTime":99396.0,"Position":135.0,"HyperDash":false},{"StartTime":99474.0,"Position":437.0,"HyperDash":false},{"StartTime":99552.0,"Position":289.0,"HyperDash":false},{"StartTime":99630.0,"Position":464.0,"HyperDash":false},{"StartTime":99708.0,"Position":36.0,"HyperDash":false},{"StartTime":99786.0,"Position":378.0,"HyperDash":false},{"StartTime":99864.0,"Position":297.0,"HyperDash":false},{"StartTime":99942.0,"Position":418.0,"HyperDash":false},{"StartTime":100021.0,"Position":329.0,"HyperDash":false},{"StartTime":100099.0,"Position":338.0,"HyperDash":false},{"StartTime":100177.0,"Position":394.0,"HyperDash":false},{"StartTime":100255.0,"Position":40.0,"HyperDash":false},{"StartTime":100333.0,"Position":13.0,"HyperDash":false},{"StartTime":100411.0,"Position":80.0,"HyperDash":false},{"StartTime":100489.0,"Position":138.0,"HyperDash":false},{"StartTime":100567.0,"Position":311.0,"HyperDash":false},{"StartTime":100646.0,"Position":216.0,"HyperDash":false}]},{"StartTime":121313.0,"Objects":[{"StartTime":121313.0,"Position":104.0,"HyperDash":false},{"StartTime":121387.0,"Position":130.222229,"HyperDash":false},{"StartTime":121461.0,"Position":155.444443,"HyperDash":false},{"StartTime":121535.0,"Position":183.666672,"HyperDash":false},{"StartTime":121646.0,"Position":204.0,"HyperDash":false}]},{"StartTime":121980.0,"Objects":[{"StartTime":121980.0,"Position":176.0,"HyperDash":false},{"StartTime":122063.0,"Position":189.658371,"HyperDash":false},{"StartTime":122146.0,"Position":232.316742,"HyperDash":false},{"StartTime":122229.0,"Position":235.975128,"HyperDash":false},{"StartTime":122313.0,"Position":266.9065,"HyperDash":false},{"StartTime":122387.0,"Position":295.1079,"HyperDash":false},{"StartTime":122461.0,"Position":303.3094,"HyperDash":false},{"StartTime":122535.0,"Position":343.5108,"HyperDash":false},{"StartTime":122646.0,"Position":357.813,"HyperDash":false}]},{"StartTime":122980.0,"Objects":[{"StartTime":122980.0,"Position":240.0,"HyperDash":false},{"StartTime":123063.0,"Position":249.293518,"HyperDash":false},{"StartTime":123146.0,"Position":284.721375,"HyperDash":false},{"StartTime":123211.0,"Position":269.396881,"HyperDash":false},{"StartTime":123313.0,"Position":240.0,"HyperDash":false}]},{"StartTime":123647.0,"Objects":[{"StartTime":123647.0,"Position":136.0,"HyperDash":false},{"StartTime":123721.0,"Position":175.807312,"HyperDash":false},{"StartTime":123795.0,"Position":177.614624,"HyperDash":false},{"StartTime":123869.0,"Position":204.421951,"HyperDash":false},{"StartTime":123980.0,"Position":229.632919,"HyperDash":false}]},{"StartTime":124313.0,"Objects":[{"StartTime":124313.0,"Position":348.0,"HyperDash":false},{"StartTime":124387.0,"Position":311.12384,"HyperDash":false},{"StartTime":124461.0,"Position":301.247681,"HyperDash":false},{"StartTime":124535.0,"Position":296.371521,"HyperDash":false},{"StartTime":124646.0,"Position":258.557281,"HyperDash":false}]},{"StartTime":124980.0,"Objects":[{"StartTime":124980.0,"Position":132.0,"HyperDash":false}]},{"StartTime":125313.0,"Objects":[{"StartTime":125313.0,"Position":308.0,"HyperDash":false}]},{"StartTime":125647.0,"Objects":[{"StartTime":125647.0,"Position":192.0,"HyperDash":false}]},{"StartTime":125980.0,"Objects":[{"StartTime":125980.0,"Position":256.0,"HyperDash":false},{"StartTime":126063.0,"Position":236.0,"HyperDash":false},{"StartTime":126146.0,"Position":241.0,"HyperDash":false},{"StartTime":126229.0,"Position":259.0,"HyperDash":false},{"StartTime":126313.0,"Position":256.0,"HyperDash":false},{"StartTime":126387.0,"Position":266.0,"HyperDash":false},{"StartTime":126461.0,"Position":262.0,"HyperDash":false},{"StartTime":126535.0,"Position":251.0,"HyperDash":false},{"StartTime":126646.0,"Position":256.0,"HyperDash":false}]},{"StartTime":126980.0,"Objects":[{"StartTime":126980.0,"Position":456.0,"HyperDash":false}]},{"StartTime":127313.0,"Objects":[{"StartTime":127313.0,"Position":240.0,"HyperDash":false},{"StartTime":127396.0,"Position":206.706223,"HyperDash":false},{"StartTime":127479.0,"Position":204.91954,"HyperDash":false},{"StartTime":127562.0,"Position":169.054108,"HyperDash":false},{"StartTime":127646.0,"Position":141.47023,"HyperDash":false},{"StartTime":127720.0,"Position":125.911591,"HyperDash":false},{"StartTime":127794.0,"Position":94.83778,"HyperDash":false},{"StartTime":127868.0,"Position":101.478622,"HyperDash":false},{"StartTime":127979.0,"Position":61.6785927,"HyperDash":false}]},{"StartTime":128313.0,"Objects":[{"StartTime":128313.0,"Position":24.0,"HyperDash":false},{"StartTime":128387.0,"Position":48.1436577,"HyperDash":false},{"StartTime":128461.0,"Position":55.9805756,"HyperDash":false},{"StartTime":128535.0,"Position":105.202553,"HyperDash":false},{"StartTime":128646.0,"Position":122.252655,"HyperDash":false}]},{"StartTime":128980.0,"Objects":[{"StartTime":128980.0,"Position":240.0,"HyperDash":false},{"StartTime":129063.0,"Position":255.475082,"HyperDash":false},{"StartTime":129146.0,"Position":232.928925,"HyperDash":false},{"StartTime":129211.0,"Position":224.668167,"HyperDash":false},{"StartTime":129313.0,"Position":240.0,"HyperDash":false}]},{"StartTime":129647.0,"Objects":[{"StartTime":129647.0,"Position":208.0,"HyperDash":false},{"StartTime":129721.0,"Position":242.2032,"HyperDash":false},{"StartTime":129795.0,"Position":238.131622,"HyperDash":false},{"StartTime":129869.0,"Position":289.174744,"HyperDash":false},{"StartTime":129980.0,"Position":301.803345,"HyperDash":false}]},{"StartTime":130313.0,"Objects":[{"StartTime":130313.0,"Position":464.0,"HyperDash":false}]},{"StartTime":130647.0,"Objects":[{"StartTime":130647.0,"Position":312.0,"HyperDash":false}]},{"StartTime":130980.0,"Objects":[{"StartTime":130980.0,"Position":360.0,"HyperDash":false}]},{"StartTime":131313.0,"Objects":[{"StartTime":131313.0,"Position":312.0,"HyperDash":false}]},{"StartTime":131980.0,"Objects":[{"StartTime":131980.0,"Position":128.0,"HyperDash":false}]},{"StartTime":132313.0,"Objects":[{"StartTime":132313.0,"Position":108.0,"HyperDash":false}]},{"StartTime":132647.0,"Objects":[{"StartTime":132647.0,"Position":128.0,"HyperDash":false},{"StartTime":132721.0,"Position":135.994476,"HyperDash":false},{"StartTime":132795.0,"Position":180.585373,"HyperDash":false},{"StartTime":132869.0,"Position":207.755859,"HyperDash":false},{"StartTime":132980.0,"Position":224.793228,"HyperDash":false}]},{"StartTime":133147.0,"Objects":[{"StartTime":133147.0,"Position":288.0,"HyperDash":false}]},{"StartTime":133313.0,"Objects":[{"StartTime":133313.0,"Position":272.0,"HyperDash":false},{"StartTime":133387.0,"Position":276.649445,"HyperDash":false},{"StartTime":133461.0,"Position":249.773849,"HyperDash":false},{"StartTime":133535.0,"Position":218.139557,"HyperDash":false},{"StartTime":133646.0,"Position":186.0562,"HyperDash":false}]},{"StartTime":133980.0,"Objects":[{"StartTime":133980.0,"Position":68.0,"HyperDash":false}]},{"StartTime":134313.0,"Objects":[{"StartTime":134313.0,"Position":61.0,"HyperDash":false}]},{"StartTime":134647.0,"Objects":[{"StartTime":134647.0,"Position":88.0,"HyperDash":false},{"StartTime":134721.0,"Position":102.674133,"HyperDash":false},{"StartTime":134795.0,"Position":111.358536,"HyperDash":false},{"StartTime":134869.0,"Position":120.496475,"HyperDash":false},{"StartTime":134980.0,"Position":164.774765,"HyperDash":false}]},{"StartTime":135147.0,"Objects":[{"StartTime":135147.0,"Position":232.0,"HyperDash":false}]},{"StartTime":135313.0,"Objects":[{"StartTime":135313.0,"Position":244.0,"HyperDash":false},{"StartTime":135387.0,"Position":257.8205,"HyperDash":false},{"StartTime":135461.0,"Position":293.698364,"HyperDash":false},{"StartTime":135535.0,"Position":319.993317,"HyperDash":false},{"StartTime":135646.0,"Position":330.966125,"HyperDash":false}]},{"StartTime":135980.0,"Objects":[{"StartTime":135980.0,"Position":400.0,"HyperDash":false},{"StartTime":136054.0,"Position":393.3103,"HyperDash":false},{"StartTime":136128.0,"Position":410.291168,"HyperDash":false},{"StartTime":136202.0,"Position":374.1771,"HyperDash":false},{"StartTime":136313.0,"Position":363.078583,"HyperDash":false}]},{"StartTime":136647.0,"Objects":[{"StartTime":136647.0,"Position":168.0,"HyperDash":false}]},{"StartTime":136980.0,"Objects":[{"StartTime":136980.0,"Position":336.0,"HyperDash":false}]},{"StartTime":137313.0,"Objects":[{"StartTime":137313.0,"Position":240.0,"HyperDash":false},{"StartTime":137387.0,"Position":248.065033,"HyperDash":false},{"StartTime":137461.0,"Position":292.435242,"HyperDash":false},{"StartTime":137535.0,"Position":300.6758,"HyperDash":false},{"StartTime":137646.0,"Position":307.714966,"HyperDash":false}]},{"StartTime":137813.0,"Objects":[{"StartTime":137813.0,"Position":288.0,"HyperDash":false}]},{"StartTime":137980.0,"Objects":[{"StartTime":137980.0,"Position":276.0,"HyperDash":false},{"StartTime":138054.0,"Position":257.487183,"HyperDash":false},{"StartTime":138128.0,"Position":243.974365,"HyperDash":false},{"StartTime":138202.0,"Position":212.461533,"HyperDash":false},{"StartTime":138313.0,"Position":183.692291,"HyperDash":false}]},{"StartTime":138647.0,"Objects":[{"StartTime":138647.0,"Position":144.0,"HyperDash":false},{"StartTime":138721.0,"Position":108.367188,"HyperDash":false},{"StartTime":138795.0,"Position":83.73437,"HyperDash":false},{"StartTime":138869.0,"Position":69.10155,"HyperDash":false},{"StartTime":138980.0,"Position":51.1523361,"HyperDash":false}]},{"StartTime":139313.0,"Objects":[{"StartTime":139313.0,"Position":176.0,"HyperDash":false},{"StartTime":139387.0,"Position":150.773682,"HyperDash":false},{"StartTime":139461.0,"Position":141.547363,"HyperDash":false},{"StartTime":139535.0,"Position":131.321045,"HyperDash":false},{"StartTime":139646.0,"Position":111.981567,"HyperDash":false}]},{"StartTime":139980.0,"Objects":[{"StartTime":139980.0,"Position":252.0,"HyperDash":false},{"StartTime":140054.0,"Position":258.226318,"HyperDash":false},{"StartTime":140128.0,"Position":299.452637,"HyperDash":false},{"StartTime":140202.0,"Position":288.678955,"HyperDash":false},{"StartTime":140313.0,"Position":316.018433,"HyperDash":false}]},{"StartTime":140647.0,"Objects":[{"StartTime":140647.0,"Position":436.0,"HyperDash":false},{"StartTime":140730.0,"Position":419.370178,"HyperDash":false},{"StartTime":140813.0,"Position":421.2818,"HyperDash":false},{"StartTime":140896.0,"Position":393.820648,"HyperDash":false},{"StartTime":140980.0,"Position":367.0077,"HyperDash":false},{"StartTime":141054.0,"Position":362.243469,"HyperDash":false},{"StartTime":141128.0,"Position":320.487732,"HyperDash":false},{"StartTime":141202.0,"Position":303.0496,"HyperDash":false},{"StartTime":141313.0,"Position":272.1492,"HyperDash":false}]},{"StartTime":141647.0,"Objects":[{"StartTime":141647.0,"Position":152.0,"HyperDash":false},{"StartTime":141730.0,"Position":140.075073,"HyperDash":false},{"StartTime":141813.0,"Position":102.0,"HyperDash":false},{"StartTime":141878.0,"Position":106.36937,"HyperDash":false},{"StartTime":141980.0,"Position":152.0,"HyperDash":false}]},{"StartTime":142647.0,"Objects":[{"StartTime":142647.0,"Position":388.0,"HyperDash":false},{"StartTime":142730.0,"Position":394.674561,"HyperDash":false},{"StartTime":142813.0,"Position":424.3491,"HyperDash":false},{"StartTime":142896.0,"Position":448.023621,"HyperDash":false},{"StartTime":142980.0,"Position":466.935242,"HyperDash":false},{"StartTime":143054.0,"Position":446.394073,"HyperDash":false},{"StartTime":143128.0,"Position":426.8529,"HyperDash":false},{"StartTime":143202.0,"Position":417.311737,"HyperDash":false},{"StartTime":143313.0,"Position":388.0,"HyperDash":false}]},{"StartTime":143647.0,"Objects":[{"StartTime":143647.0,"Position":272.0,"HyperDash":false},{"StartTime":143721.0,"Position":277.467682,"HyperDash":false},{"StartTime":143795.0,"Position":265.935364,"HyperDash":false},{"StartTime":143869.0,"Position":247.403046,"HyperDash":false},{"StartTime":143980.0,"Position":251.604568,"HyperDash":false}]},{"StartTime":144313.0,"Objects":[{"StartTime":144313.0,"Position":250.0,"HyperDash":false}]},{"StartTime":144647.0,"Objects":[{"StartTime":144647.0,"Position":130.0,"HyperDash":false},{"StartTime":144730.0,"Position":126.174141,"HyperDash":false},{"StartTime":144813.0,"Position":102.264992,"HyperDash":false},{"StartTime":144878.0,"Position":130.009186,"HyperDash":false},{"StartTime":144980.0,"Position":130.0,"HyperDash":false}]},{"StartTime":145313.0,"Objects":[{"StartTime":145313.0,"Position":302.0,"HyperDash":false}]},{"StartTime":145647.0,"Objects":[{"StartTime":145647.0,"Position":98.0,"HyperDash":false}]},{"StartTime":145980.0,"Objects":[{"StartTime":145980.0,"Position":304.0,"HyperDash":false},{"StartTime":146045.0,"Position":329.9953,"HyperDash":false},{"StartTime":146146.0,"Position":349.957245,"HyperDash":false}]},{"StartTime":146480.0,"Objects":[{"StartTime":146480.0,"Position":400.0,"HyperDash":false},{"StartTime":146545.0,"Position":412.621429,"HyperDash":false},{"StartTime":146646.0,"Position":386.263947,"HyperDash":false}]},{"StartTime":146980.0,"Objects":[{"StartTime":146980.0,"Position":160.0,"HyperDash":false}]},{"StartTime":147313.0,"Objects":[{"StartTime":147313.0,"Position":152.0,"HyperDash":false},{"StartTime":147396.0,"Position":112.075073,"HyperDash":false},{"StartTime":147479.0,"Position":102.0,"HyperDash":false},{"StartTime":147562.0,"Position":121.774773,"HyperDash":false},{"StartTime":147646.0,"Position":152.0,"HyperDash":false},{"StartTime":147729.0,"Position":139.075073,"HyperDash":false},{"StartTime":147813.0,"Position":102.0,"HyperDash":false},{"StartTime":147878.0,"Position":112.669662,"HyperDash":false},{"StartTime":147979.0,"Position":152.0,"HyperDash":false}]},{"StartTime":148313.0,"Objects":[{"StartTime":148313.0,"Position":384.0,"HyperDash":false}]},{"StartTime":148647.0,"Objects":[{"StartTime":148647.0,"Position":360.0,"HyperDash":false},{"StartTime":148730.0,"Position":399.623871,"HyperDash":false},{"StartTime":148813.0,"Position":408.1816,"HyperDash":false},{"StartTime":148896.0,"Position":430.2179,"HyperDash":false},{"StartTime":148980.0,"Position":434.200317,"HyperDash":false},{"StartTime":149045.0,"Position":424.324982,"HyperDash":false},{"StartTime":149146.0,"Position":454.563965,"HyperDash":false}]},{"StartTime":149313.0,"Objects":[{"StartTime":149313.0,"Position":396.0,"HyperDash":false},{"StartTime":149396.0,"Position":387.613281,"HyperDash":false},{"StartTime":149479.0,"Position":406.6472,"HyperDash":false},{"StartTime":149562.0,"Position":410.1058,"HyperDash":false},{"StartTime":149646.0,"Position":424.7098,"HyperDash":false},{"StartTime":149711.0,"Position":445.476379,"HyperDash":false},{"StartTime":149813.0,"Position":427.845062,"HyperDash":false}]},{"StartTime":149980.0,"Objects":[{"StartTime":149980.0,"Position":426.0,"HyperDash":false}]},{"StartTime":150313.0,"Objects":[{"StartTime":150313.0,"Position":316.0,"HyperDash":false},{"StartTime":150396.0,"Position":342.7388,"HyperDash":false},{"StartTime":150479.0,"Position":357.6025,"HyperDash":false},{"StartTime":150544.0,"Position":351.486237,"HyperDash":false},{"StartTime":150646.0,"Position":316.0,"HyperDash":false}]},{"StartTime":150980.0,"Objects":[{"StartTime":150980.0,"Position":436.0,"HyperDash":false},{"StartTime":151054.0,"Position":413.307129,"HyperDash":false},{"StartTime":151128.0,"Position":416.6143,"HyperDash":false},{"StartTime":151202.0,"Position":385.921417,"HyperDash":false},{"StartTime":151313.0,"Position":351.882141,"HyperDash":false}]},{"StartTime":151480.0,"Objects":[{"StartTime":151480.0,"Position":296.0,"HyperDash":false},{"StartTime":151545.0,"Position":293.135956,"HyperDash":false},{"StartTime":151646.0,"Position":247.8241,"HyperDash":false}]},{"StartTime":151813.0,"Objects":[{"StartTime":151813.0,"Position":292.0,"HyperDash":false},{"StartTime":151878.0,"Position":304.3741,"HyperDash":false},{"StartTime":151979.0,"Position":287.847717,"HyperDash":false}]},{"StartTime":152147.0,"Objects":[{"StartTime":152147.0,"Position":248.0,"HyperDash":false},{"StartTime":152212.0,"Position":247.426376,"HyperDash":false},{"StartTime":152313.0,"Position":200.565826,"HyperDash":false}]},{"StartTime":152480.0,"Objects":[{"StartTime":152480.0,"Position":244.0,"HyperDash":false},{"StartTime":152545.0,"Position":240.712448,"HyperDash":false},{"StartTime":152646.0,"Position":238.157944,"HyperDash":false}]},{"StartTime":153313.0,"Objects":[{"StartTime":153313.0,"Position":276.0,"HyperDash":false}]},{"StartTime":153647.0,"Objects":[{"StartTime":153647.0,"Position":236.0,"HyperDash":false}]},{"StartTime":153980.0,"Objects":[{"StartTime":153980.0,"Position":256.0,"HyperDash":false},{"StartTime":154063.0,"Position":218.410385,"HyperDash":false},{"StartTime":154146.0,"Position":217.82077,"HyperDash":false},{"StartTime":154229.0,"Position":187.231171,"HyperDash":false},{"StartTime":154313.0,"Position":169.381439,"HyperDash":false},{"StartTime":154387.0,"Position":156.132874,"HyperDash":false},{"StartTime":154461.0,"Position":124.884308,"HyperDash":false},{"StartTime":154535.0,"Position":111.635742,"HyperDash":false},{"StartTime":154646.0,"Position":82.76289,"HyperDash":false}]},{"StartTime":154980.0,"Objects":[{"StartTime":154980.0,"Position":464.0,"HyperDash":false}]},{"StartTime":155313.0,"Objects":[{"StartTime":155313.0,"Position":140.0,"HyperDash":false},{"StartTime":155396.0,"Position":157.959641,"HyperDash":false},{"StartTime":155479.0,"Position":183.919281,"HyperDash":false},{"StartTime":155562.0,"Position":179.8789,"HyperDash":false},{"StartTime":155646.0,"Position":191.99469,"HyperDash":false},{"StartTime":155720.0,"Position":211.549072,"HyperDash":false},{"StartTime":155794.0,"Position":199.103455,"HyperDash":false},{"StartTime":155868.0,"Position":218.6578,"HyperDash":false},{"StartTime":155979.0,"Position":243.9894,"HyperDash":false}]},{"StartTime":156313.0,"Objects":[{"StartTime":156313.0,"Position":28.0,"HyperDash":false}]},{"StartTime":156647.0,"Objects":[{"StartTime":156647.0,"Position":84.0,"HyperDash":false},{"StartTime":156721.0,"Position":99.0253143,"HyperDash":false},{"StartTime":156795.0,"Position":91.05062,"HyperDash":false},{"StartTime":156869.0,"Position":100.075928,"HyperDash":false},{"StartTime":156980.0,"Position":133.613892,"HyperDash":false}]},{"StartTime":157147.0,"Objects":[{"StartTime":157147.0,"Position":180.0,"HyperDash":false}]},{"StartTime":157313.0,"Objects":[{"StartTime":157313.0,"Position":228.0,"HyperDash":false}]},{"StartTime":157647.0,"Objects":[{"StartTime":157647.0,"Position":324.0,"HyperDash":false},{"StartTime":157721.0,"Position":364.239532,"HyperDash":false},{"StartTime":157795.0,"Position":364.479065,"HyperDash":false},{"StartTime":157869.0,"Position":389.7186,"HyperDash":false},{"StartTime":157980.0,"Position":419.577881,"HyperDash":false}]},{"StartTime":158313.0,"Objects":[{"StartTime":158313.0,"Position":336.0,"HyperDash":false},{"StartTime":158387.0,"Position":312.2865,"HyperDash":false},{"StartTime":158461.0,"Position":300.573029,"HyperDash":false},{"StartTime":158535.0,"Position":297.859528,"HyperDash":false},{"StartTime":158646.0,"Position":265.2893,"HyperDash":false}]},{"StartTime":158980.0,"Objects":[{"StartTime":158980.0,"Position":80.0,"HyperDash":false}]},{"StartTime":159313.0,"Objects":[{"StartTime":159313.0,"Position":248.0,"HyperDash":false}]},{"StartTime":159646.0,"Objects":[{"StartTime":159646.0,"Position":48.0,"HyperDash":false},{"StartTime":159729.0,"Position":51.11805,"HyperDash":false},{"StartTime":159812.0,"Position":32.1886139,"HyperDash":false},{"StartTime":159877.0,"Position":24.3137436,"HyperDash":false},{"StartTime":159979.0,"Position":48.0,"HyperDash":false}]},{"StartTime":160313.0,"Objects":[{"StartTime":160313.0,"Position":200.0,"HyperDash":false}]},{"StartTime":160647.0,"Objects":[{"StartTime":160647.0,"Position":248.0,"HyperDash":false}]},{"StartTime":160980.0,"Objects":[{"StartTime":160980.0,"Position":440.0,"HyperDash":false}]},{"StartTime":161313.0,"Objects":[{"StartTime":161313.0,"Position":392.0,"HyperDash":false}]},{"StartTime":161980.0,"Objects":[{"StartTime":161980.0,"Position":120.0,"HyperDash":false}]},{"StartTime":162313.0,"Objects":[{"StartTime":162313.0,"Position":360.0,"HyperDash":false},{"StartTime":162396.0,"Position":370.924927,"HyperDash":false},{"StartTime":162479.0,"Position":394.849854,"HyperDash":false},{"StartTime":162562.0,"Position":440.77478,"HyperDash":false},{"StartTime":162646.0,"Position":460.0,"HyperDash":false},{"StartTime":162720.0,"Position":455.777771,"HyperDash":false},{"StartTime":162794.0,"Position":421.555542,"HyperDash":false},{"StartTime":162868.0,"Position":408.333344,"HyperDash":false},{"StartTime":162979.0,"Position":360.0,"HyperDash":false}]},{"StartTime":163313.0,"Objects":[{"StartTime":163313.0,"Position":48.0,"HyperDash":false}]},{"StartTime":163646.0,"Objects":[{"StartTime":163646.0,"Position":152.0,"HyperDash":false},{"StartTime":163729.0,"Position":137.075073,"HyperDash":false},{"StartTime":163812.0,"Position":112.150146,"HyperDash":false},{"StartTime":163895.0,"Position":86.22523,"HyperDash":false},{"StartTime":163979.0,"Position":52.0,"HyperDash":false},{"StartTime":164053.0,"Position":75.22222,"HyperDash":false},{"StartTime":164127.0,"Position":93.44444,"HyperDash":false},{"StartTime":164201.0,"Position":131.666656,"HyperDash":false},{"StartTime":164312.0,"Position":152.0,"HyperDash":false}]},{"StartTime":164647.0,"Objects":[{"StartTime":164647.0,"Position":256.0,"HyperDash":false}]},{"StartTime":164980.0,"Objects":[{"StartTime":164980.0,"Position":360.0,"HyperDash":false},{"StartTime":165063.0,"Position":391.924927,"HyperDash":false},{"StartTime":165146.0,"Position":415.849854,"HyperDash":false},{"StartTime":165229.0,"Position":439.77478,"HyperDash":false},{"StartTime":165313.0,"Position":460.0,"HyperDash":false},{"StartTime":165387.0,"Position":421.777771,"HyperDash":false},{"StartTime":165461.0,"Position":412.555542,"HyperDash":false},{"StartTime":165535.0,"Position":400.333344,"HyperDash":false},{"StartTime":165646.0,"Position":360.0,"HyperDash":false}]},{"StartTime":165980.0,"Objects":[{"StartTime":165980.0,"Position":48.0,"HyperDash":false}]},{"StartTime":166646.0,"Objects":[{"StartTime":166646.0,"Position":16.0,"HyperDash":false},{"StartTime":166720.0,"Position":33.9701347,"HyperDash":false},{"StartTime":166794.0,"Position":24.45197,"HyperDash":false},{"StartTime":166868.0,"Position":40.2451935,"HyperDash":false},{"StartTime":166979.0,"Position":44.51446,"HyperDash":false}]},{"StartTime":167313.0,"Objects":[{"StartTime":167313.0,"Position":116.0,"HyperDash":false},{"StartTime":167387.0,"Position":129.7839,"HyperDash":false},{"StartTime":167461.0,"Position":169.077835,"HyperDash":false},{"StartTime":167535.0,"Position":179.400436,"HyperDash":false},{"StartTime":167646.0,"Position":209.385559,"HyperDash":false}]},{"StartTime":167814.0,"Objects":[{"StartTime":167814.0,"Position":276.0,"HyperDash":false}]},{"StartTime":167980.0,"Objects":[{"StartTime":167980.0,"Position":288.0,"HyperDash":false},{"StartTime":168054.0,"Position":297.026276,"HyperDash":false},{"StartTime":168128.0,"Position":311.4158,"HyperDash":false},{"StartTime":168202.0,"Position":352.7142,"HyperDash":false},{"StartTime":168313.0,"Position":379.425873,"HyperDash":false}]},{"StartTime":168480.0,"Objects":[{"StartTime":168480.0,"Position":440.0,"HyperDash":false}]},{"StartTime":168647.0,"Objects":[{"StartTime":168647.0,"Position":428.0,"HyperDash":false},{"StartTime":168721.0,"Position":416.346558,"HyperDash":false},{"StartTime":168795.0,"Position":376.215485,"HyperDash":false},{"StartTime":168869.0,"Position":354.074921,"HyperDash":false},{"StartTime":168980.0,"Position":333.4033,"HyperDash":false}]},{"StartTime":169147.0,"Objects":[{"StartTime":169147.0,"Position":292.0,"HyperDash":false}]},{"StartTime":169313.0,"Objects":[{"StartTime":169313.0,"Position":260.0,"HyperDash":false},{"StartTime":169387.0,"Position":226.354462,"HyperDash":false},{"StartTime":169461.0,"Position":218.650589,"HyperDash":false},{"StartTime":169535.0,"Position":188.49968,"HyperDash":false},{"StartTime":169646.0,"Position":162.278046,"HyperDash":false}]},{"StartTime":169814.0,"Objects":[{"StartTime":169814.0,"Position":108.0,"HyperDash":false}]},{"StartTime":169980.0,"Objects":[{"StartTime":169980.0,"Position":88.0,"HyperDash":false},{"StartTime":170054.0,"Position":102.962883,"HyperDash":false},{"StartTime":170128.0,"Position":119.505386,"HyperDash":false},{"StartTime":170202.0,"Position":134.055634,"HyperDash":false},{"StartTime":170313.0,"Position":155.916748,"HyperDash":false}]},{"StartTime":170480.0,"Objects":[{"StartTime":170480.0,"Position":184.0,"HyperDash":false}]},{"StartTime":170647.0,"Objects":[{"StartTime":170647.0,"Position":232.0,"HyperDash":false},{"StartTime":170721.0,"Position":263.15802,"HyperDash":false},{"StartTime":170795.0,"Position":293.183655,"HyperDash":false},{"StartTime":170869.0,"Position":306.346649,"HyperDash":false},{"StartTime":170980.0,"Position":326.30188,"HyperDash":false}]},{"StartTime":171314.0,"Objects":[{"StartTime":171314.0,"Position":424.0,"HyperDash":false}]},{"StartTime":171647.0,"Objects":[{"StartTime":171647.0,"Position":404.0,"HyperDash":false}]},{"StartTime":171980.0,"Objects":[{"StartTime":171980.0,"Position":424.0,"HyperDash":false},{"StartTime":172054.0,"Position":432.217773,"HyperDash":false},{"StartTime":172128.0,"Position":396.404236,"HyperDash":false},{"StartTime":172202.0,"Position":412.493378,"HyperDash":false},{"StartTime":172313.0,"Position":371.9598,"HyperDash":false}]},{"StartTime":172480.0,"Objects":[{"StartTime":172480.0,"Position":312.0,"HyperDash":false}]},{"StartTime":172646.0,"Objects":[{"StartTime":172646.0,"Position":296.0,"HyperDash":false},{"StartTime":172729.0,"Position":266.640961,"HyperDash":false},{"StartTime":172812.0,"Position":246.785126,"HyperDash":false},{"StartTime":172895.0,"Position":204.6299,"HyperDash":false},{"StartTime":172979.0,"Position":199.5078,"HyperDash":false},{"StartTime":173053.0,"Position":230.801788,"HyperDash":false},{"StartTime":173127.0,"Position":226.161774,"HyperDash":false},{"StartTime":173201.0,"Position":272.241882,"HyperDash":false},{"StartTime":173312.0,"Position":296.0,"HyperDash":false}]},{"StartTime":173647.0,"Objects":[{"StartTime":173647.0,"Position":256.0,"HyperDash":false}]},{"StartTime":173980.0,"Objects":[{"StartTime":173980.0,"Position":164.0,"HyperDash":false},{"StartTime":174063.0,"Position":132.238632,"HyperDash":false},{"StartTime":174146.0,"Position":97.919014,"HyperDash":false},{"StartTime":174229.0,"Position":81.1318741,"HyperDash":false},{"StartTime":174313.0,"Position":74.66674,"HyperDash":false},{"StartTime":174387.0,"Position":104.74202,"HyperDash":false},{"StartTime":174461.0,"Position":110.645523,"HyperDash":false},{"StartTime":174535.0,"Position":122.876343,"HyperDash":false},{"StartTime":174646.0,"Position":164.0,"HyperDash":false}]},{"StartTime":174980.0,"Objects":[{"StartTime":174980.0,"Position":132.0,"HyperDash":false},{"StartTime":175054.0,"Position":123.056931,"HyperDash":false},{"StartTime":175128.0,"Position":102.477112,"HyperDash":false},{"StartTime":175202.0,"Position":92.91614,"HyperDash":false},{"StartTime":175313.0,"Position":105.479126,"HyperDash":false}]},{"StartTime":175646.0,"Objects":[{"StartTime":175646.0,"Position":212.0,"HyperDash":false},{"StartTime":175729.0,"Position":240.889877,"HyperDash":false},{"StartTime":175812.0,"Position":250.558151,"HyperDash":false},{"StartTime":175895.0,"Position":278.151367,"HyperDash":false},{"StartTime":175979.0,"Position":273.195679,"HyperDash":false},{"StartTime":176053.0,"Position":272.262177,"HyperDash":false},{"StartTime":176127.0,"Position":241.994537,"HyperDash":false},{"StartTime":176201.0,"Position":248.795273,"HyperDash":false},{"StartTime":176312.0,"Position":212.0,"HyperDash":false}]},{"StartTime":176647.0,"Objects":[{"StartTime":176647.0,"Position":212.0,"HyperDash":false}]},{"StartTime":177313.0,"Objects":[{"StartTime":177313.0,"Position":8.0,"HyperDash":false},{"StartTime":177387.0,"Position":10.2332268,"HyperDash":false},{"StartTime":177461.0,"Position":20.5555458,"HyperDash":false},{"StartTime":177535.0,"Position":40.06486,"HyperDash":false},{"StartTime":177646.0,"Position":79.97232,"HyperDash":false}]},{"StartTime":177980.0,"Objects":[{"StartTime":177980.0,"Position":200.0,"HyperDash":false},{"StartTime":178063.0,"Position":241.128418,"HyperDash":false},{"StartTime":178146.0,"Position":239.256821,"HyperDash":false},{"StartTime":178229.0,"Position":270.385223,"HyperDash":false},{"StartTime":178313.0,"Position":296.804352,"HyperDash":false},{"StartTime":178378.0,"Position":329.7001,"HyperDash":false},{"StartTime":178479.0,"Position":345.061157,"HyperDash":false}]},{"StartTime":178647.0,"Objects":[{"StartTime":178647.0,"Position":344.0,"HyperDash":false},{"StartTime":178730.0,"Position":319.3755,"HyperDash":false},{"StartTime":178813.0,"Position":279.750977,"HyperDash":false},{"StartTime":178896.0,"Position":284.126465,"HyperDash":false},{"StartTime":178980.0,"Position":245.205261,"HyperDash":false},{"StartTime":179045.0,"Position":217.92099,"HyperDash":false},{"StartTime":179147.0,"Position":195.659546,"HyperDash":false}]},{"StartTime":179313.0,"Objects":[{"StartTime":179313.0,"Position":196.0,"HyperDash":false},{"StartTime":179396.0,"Position":204.644592,"HyperDash":false},{"StartTime":179479.0,"Position":247.289169,"HyperDash":false},{"StartTime":179562.0,"Position":284.933777,"HyperDash":false},{"StartTime":179646.0,"Position":294.875275,"HyperDash":false},{"StartTime":179711.0,"Position":321.175262,"HyperDash":false},{"StartTime":179812.0,"Position":344.164429,"HyperDash":false}]},{"StartTime":179980.0,"Objects":[{"StartTime":179980.0,"Position":344.0,"HyperDash":false},{"StartTime":180063.0,"Position":304.223572,"HyperDash":false},{"StartTime":180146.0,"Position":297.447144,"HyperDash":false},{"StartTime":180229.0,"Position":264.670715,"HyperDash":false},{"StartTime":180313.0,"Position":244.595779,"HyperDash":false},{"StartTime":180378.0,"Position":243.192551,"HyperDash":false},{"StartTime":180480.0,"Position":194.744415,"HyperDash":false}]},{"StartTime":180647.0,"Objects":[{"StartTime":180647.0,"Position":136.0,"HyperDash":false},{"StartTime":180730.0,"Position":111.127846,"HyperDash":false},{"StartTime":180813.0,"Position":94.761,"HyperDash":false},{"StartTime":180896.0,"Position":98.9445953,"HyperDash":false},{"StartTime":180980.0,"Position":71.38005,"HyperDash":false},{"StartTime":181063.0,"Position":69.46596,"HyperDash":false},{"StartTime":181147.0,"Position":63.5731277,"HyperDash":false},{"StartTime":181230.0,"Position":82.42001,"HyperDash":false},{"StartTime":181313.0,"Position":71.28203,"HyperDash":false},{"StartTime":181387.0,"Position":81.29693,"HyperDash":false},{"StartTime":181462.0,"Position":106.020226,"HyperDash":false},{"StartTime":181536.0,"Position":117.600555,"HyperDash":false},{"StartTime":181647.0,"Position":136.0,"HyperDash":false}]},{"StartTime":181980.0,"Objects":[{"StartTime":181980.0,"Position":188.0,"HyperDash":false}]},{"StartTime":182647.0,"Objects":[{"StartTime":182647.0,"Position":168.0,"HyperDash":false}]},{"StartTime":182980.0,"Objects":[{"StartTime":182980.0,"Position":76.0,"HyperDash":false},{"StartTime":183063.0,"Position":66.09038,"HyperDash":false},{"StartTime":183146.0,"Position":30.0427513,"HyperDash":false},{"StartTime":183211.0,"Position":32.84601,"HyperDash":false},{"StartTime":183313.0,"Position":76.0,"HyperDash":false}]},{"StartTime":183647.0,"Objects":[{"StartTime":183647.0,"Position":356.0,"HyperDash":false}]},{"StartTime":183980.0,"Objects":[{"StartTime":183980.0,"Position":300.0,"HyperDash":false},{"StartTime":184063.0,"Position":315.398315,"HyperDash":false},{"StartTime":184146.0,"Position":337.263855,"HyperDash":false},{"StartTime":184229.0,"Position":376.114166,"HyperDash":false},{"StartTime":184313.0,"Position":398.933929,"HyperDash":false},{"StartTime":184387.0,"Position":361.090729,"HyperDash":false},{"StartTime":184461.0,"Position":359.967743,"HyperDash":false},{"StartTime":184535.0,"Position":348.762024,"HyperDash":false},{"StartTime":184646.0,"Position":300.0,"HyperDash":false}]},{"StartTime":184980.0,"Objects":[{"StartTime":184980.0,"Position":256.0,"HyperDash":false},{"StartTime":185063.0,"Position":211.878,"HyperDash":false},{"StartTime":185146.0,"Position":210.733841,"HyperDash":false},{"StartTime":185229.0,"Position":193.655289,"HyperDash":false},{"StartTime":185313.0,"Position":174.843628,"HyperDash":false},{"StartTime":185387.0,"Position":205.589539,"HyperDash":false},{"StartTime":185461.0,"Position":212.06926,"HyperDash":false},{"StartTime":185535.0,"Position":226.121918,"HyperDash":false},{"StartTime":185646.0,"Position":256.0,"HyperDash":false}]},{"StartTime":185980.0,"Objects":[{"StartTime":185980.0,"Position":344.0,"HyperDash":false}]},{"StartTime":186647.0,"Objects":[{"StartTime":186647.0,"Position":168.0,"HyperDash":false}]},{"StartTime":186980.0,"Objects":[{"StartTime":186980.0,"Position":316.0,"HyperDash":false}]},{"StartTime":187313.0,"Objects":[{"StartTime":187313.0,"Position":196.0,"HyperDash":false}]},{"StartTime":187980.0,"Objects":[{"StartTime":187980.0,"Position":408.0,"HyperDash":false}]},{"StartTime":188313.0,"Objects":[{"StartTime":188313.0,"Position":456.0,"HyperDash":false}]},{"StartTime":188647.0,"Objects":[{"StartTime":188647.0,"Position":320.0,"HyperDash":false}]},{"StartTime":188980.0,"Objects":[{"StartTime":188980.0,"Position":224.0,"HyperDash":false},{"StartTime":189063.0,"Position":203.261215,"HyperDash":false},{"StartTime":189146.0,"Position":182.397491,"HyperDash":false},{"StartTime":189211.0,"Position":196.513779,"HyperDash":false},{"StartTime":189313.0,"Position":224.0,"HyperDash":false}]},{"StartTime":189647.0,"Objects":[{"StartTime":189647.0,"Position":120.0,"HyperDash":false},{"StartTime":189730.0,"Position":102.325584,"HyperDash":false},{"StartTime":189813.0,"Position":70.5025253,"HyperDash":false},{"StartTime":189878.0,"Position":77.67722,"HyperDash":false},{"StartTime":189980.0,"Position":120.0,"HyperDash":false}]},{"StartTime":190313.0,"Objects":[{"StartTime":190313.0,"Position":96.0,"HyperDash":false},{"StartTime":190396.0,"Position":67.70647,"HyperDash":false},{"StartTime":190479.0,"Position":51.27864,"HyperDash":false},{"StartTime":190544.0,"Position":85.6031342,"HyperDash":false},{"StartTime":190646.0,"Position":96.0,"HyperDash":false}]},{"StartTime":190980.0,"Objects":[{"StartTime":190980.0,"Position":188.0,"HyperDash":false},{"StartTime":191054.0,"Position":204.489685,"HyperDash":false},{"StartTime":191128.0,"Position":220.740356,"HyperDash":false},{"StartTime":191202.0,"Position":229.801239,"HyperDash":false},{"StartTime":191313.0,"Position":258.899475,"HyperDash":false}]},{"StartTime":191646.0,"Objects":[{"StartTime":191646.0,"Position":320.0,"HyperDash":false},{"StartTime":191729.0,"Position":322.9096,"HyperDash":false},{"StartTime":191812.0,"Position":365.957245,"HyperDash":false},{"StartTime":191877.0,"Position":363.154,"HyperDash":false},{"StartTime":191979.0,"Position":320.0,"HyperDash":false}]},{"StartTime":192313.0,"Objects":[{"StartTime":192313.0,"Position":256.0,"HyperDash":false}]},{"StartTime":192646.0,"Objects":[{"StartTime":192646.0,"Position":376.0,"HyperDash":false}]},{"StartTime":192980.0,"Objects":[{"StartTime":192980.0,"Position":264.0,"HyperDash":false}]},{"StartTime":193313.0,"Objects":[{"StartTime":193313.0,"Position":376.0,"HyperDash":false}]},{"StartTime":193647.0,"Objects":[{"StartTime":193647.0,"Position":404.0,"HyperDash":false},{"StartTime":193730.0,"Position":427.956543,"HyperDash":false},{"StartTime":193813.0,"Position":436.009216,"HyperDash":false},{"StartTime":193878.0,"Position":419.609253,"HyperDash":false},{"StartTime":193980.0,"Position":404.0,"HyperDash":false}]},{"StartTime":194313.0,"Objects":[{"StartTime":194313.0,"Position":404.0,"HyperDash":false},{"StartTime":194387.0,"Position":411.0,"HyperDash":false},{"StartTime":194461.0,"Position":401.0,"HyperDash":false},{"StartTime":194535.0,"Position":423.0,"HyperDash":false},{"StartTime":194646.0,"Position":404.0,"HyperDash":false}]},{"StartTime":194980.0,"Objects":[{"StartTime":194980.0,"Position":344.0,"HyperDash":false},{"StartTime":195054.0,"Position":333.802856,"HyperDash":false},{"StartTime":195128.0,"Position":315.605743,"HyperDash":false},{"StartTime":195202.0,"Position":294.4086,"HyperDash":false},{"StartTime":195313.0,"Position":293.612885,"HyperDash":false}]},{"StartTime":195647.0,"Objects":[{"StartTime":195647.0,"Position":300.0,"HyperDash":false},{"StartTime":195721.0,"Position":295.574554,"HyperDash":false},{"StartTime":195795.0,"Position":256.1491,"HyperDash":false},{"StartTime":195869.0,"Position":239.723663,"HyperDash":false},{"StartTime":195980.0,"Position":208.08551,"HyperDash":false}]},{"StartTime":196313.0,"Objects":[{"StartTime":196313.0,"Position":300.0,"HyperDash":false},{"StartTime":196396.0,"Position":270.293732,"HyperDash":false},{"StartTime":196479.0,"Position":253.587433,"HyperDash":false},{"StartTime":196562.0,"Position":235.881165,"HyperDash":false},{"StartTime":196646.0,"Position":200.877213,"HyperDash":false},{"StartTime":196720.0,"Position":215.90448,"HyperDash":false},{"StartTime":196794.0,"Position":240.931778,"HyperDash":false},{"StartTime":196868.0,"Position":248.959076,"HyperDash":false},{"StartTime":196979.0,"Position":300.0,"HyperDash":false}]},{"StartTime":197313.0,"Objects":[{"StartTime":197313.0,"Position":420.0,"HyperDash":false}]},{"StartTime":197647.0,"Objects":[{"StartTime":197647.0,"Position":400.0,"HyperDash":false}]},{"StartTime":197980.0,"Objects":[{"StartTime":197980.0,"Position":300.0,"HyperDash":false},{"StartTime":198054.0,"Position":293.0362,"HyperDash":false},{"StartTime":198128.0,"Position":249.072357,"HyperDash":false},{"StartTime":198202.0,"Position":251.108551,"HyperDash":false},{"StartTime":198313.0,"Position":201.162827,"HyperDash":false}]},{"StartTime":198647.0,"Objects":[{"StartTime":198647.0,"Position":80.0,"HyperDash":false}]},{"StartTime":198980.0,"Objects":[{"StartTime":198980.0,"Position":60.0,"HyperDash":false}]},{"StartTime":199313.0,"Objects":[{"StartTime":199313.0,"Position":200.0,"HyperDash":false},{"StartTime":199387.0,"Position":224.2925,"HyperDash":false},{"StartTime":199461.0,"Position":246.710052,"HyperDash":false},{"StartTime":199535.0,"Position":263.1878,"HyperDash":false},{"StartTime":199646.0,"Position":270.120148,"HyperDash":false}]},{"StartTime":199813.0,"Objects":[{"StartTime":199813.0,"Position":296.0,"HyperDash":false}]},{"StartTime":199980.0,"Objects":[{"StartTime":199980.0,"Position":272.0,"HyperDash":false}]},{"StartTime":200313.0,"Objects":[{"StartTime":200313.0,"Position":56.0,"HyperDash":false}]},{"StartTime":200647.0,"Objects":[{"StartTime":200647.0,"Position":284.0,"HyperDash":false},{"StartTime":200721.0,"Position":297.376343,"HyperDash":false},{"StartTime":200795.0,"Position":265.3053,"HyperDash":false},{"StartTime":200869.0,"Position":263.664337,"HyperDash":false},{"StartTime":200980.0,"Position":247.205276,"HyperDash":false}]},{"StartTime":201147.0,"Objects":[{"StartTime":201147.0,"Position":196.0,"HyperDash":false}]},{"StartTime":201314.0,"Objects":[{"StartTime":201314.0,"Position":156.0,"HyperDash":false}]},{"StartTime":201647.0,"Objects":[{"StartTime":201647.0,"Position":172.0,"HyperDash":false}]},{"StartTime":201980.0,"Objects":[{"StartTime":201980.0,"Position":176.0,"HyperDash":false},{"StartTime":202054.0,"Position":140.8467,"HyperDash":false},{"StartTime":202128.0,"Position":146.271057,"HyperDash":false},{"StartTime":202202.0,"Position":100.283012,"HyperDash":false},{"StartTime":202313.0,"Position":85.75247,"HyperDash":false}]},{"StartTime":202480.0,"Objects":[{"StartTime":202480.0,"Position":48.0,"HyperDash":false}]},{"StartTime":202647.0,"Objects":[{"StartTime":202647.0,"Position":40.0,"HyperDash":false}]},{"StartTime":202980.0,"Objects":[{"StartTime":202980.0,"Position":164.0,"HyperDash":false}]},{"StartTime":203313.0,"Objects":[{"StartTime":203313.0,"Position":44.0,"HyperDash":false},{"StartTime":203396.0,"Position":54.64748,"HyperDash":false},{"StartTime":203479.0,"Position":34.0083046,"HyperDash":false},{"StartTime":203562.0,"Position":34.9480324,"HyperDash":false},{"StartTime":203646.0,"Position":41.9094124,"HyperDash":false},{"StartTime":203720.0,"Position":47.4630432,"HyperDash":false},{"StartTime":203794.0,"Position":38.06579,"HyperDash":false},{"StartTime":203868.0,"Position":41.2230949,"HyperDash":false},{"StartTime":203979.0,"Position":44.0,"HyperDash":false}]},{"StartTime":204313.0,"Objects":[{"StartTime":204313.0,"Position":152.0,"HyperDash":false},{"StartTime":204396.0,"Position":127.075073,"HyperDash":false},{"StartTime":204479.0,"Position":102.0,"HyperDash":false},{"StartTime":204544.0,"Position":132.36937,"HyperDash":false},{"StartTime":204646.0,"Position":152.0,"HyperDash":false}]},{"StartTime":204980.0,"Objects":[{"StartTime":204980.0,"Position":464.0,"HyperDash":false}]},{"StartTime":205313.0,"Objects":[{"StartTime":205313.0,"Position":272.0,"HyperDash":false},{"StartTime":205396.0,"Position":258.4456,"HyperDash":false},{"StartTime":205479.0,"Position":269.674164,"HyperDash":false},{"StartTime":205562.0,"Position":250.012192,"HyperDash":false},{"StartTime":205646.0,"Position":212.531021,"HyperDash":false},{"StartTime":205720.0,"Position":201.934311,"HyperDash":false},{"StartTime":205794.0,"Position":166.713287,"HyperDash":false},{"StartTime":205868.0,"Position":145.157013,"HyperDash":false},{"StartTime":205979.0,"Position":152.274872,"HyperDash":false}]},{"StartTime":206313.0,"Objects":[{"StartTime":206313.0,"Position":152.0,"HyperDash":false},{"StartTime":206396.0,"Position":157.0,"HyperDash":false},{"StartTime":206479.0,"Position":152.0,"HyperDash":false},{"StartTime":206544.0,"Position":163.0,"HyperDash":false},{"StartTime":206646.0,"Position":152.0,"HyperDash":false}]},{"StartTime":206980.0,"Objects":[{"StartTime":206980.0,"Position":172.0,"HyperDash":false}]},{"StartTime":207313.0,"Objects":[{"StartTime":207313.0,"Position":172.0,"HyperDash":false}]},{"StartTime":207646.0,"Objects":[{"StartTime":207646.0,"Position":152.0,"HyperDash":false},{"StartTime":207729.0,"Position":138.0,"HyperDash":false},{"StartTime":207812.0,"Position":152.0,"HyperDash":false},{"StartTime":207877.0,"Position":143.0,"HyperDash":false},{"StartTime":207979.0,"Position":152.0,"HyperDash":false}]},{"StartTime":208313.0,"Objects":[{"StartTime":208313.0,"Position":248.0,"HyperDash":false},{"StartTime":208387.0,"Position":239.45256,"HyperDash":false},{"StartTime":208461.0,"Position":243.221558,"HyperDash":false},{"StartTime":208535.0,"Position":244.170654,"HyperDash":false},{"StartTime":208646.0,"Position":250.445511,"HyperDash":false}]},{"StartTime":208980.0,"Objects":[{"StartTime":208980.0,"Position":353.0,"HyperDash":false},{"StartTime":209042.0,"Position":358.0,"HyperDash":false},{"StartTime":209105.0,"Position":447.0,"HyperDash":false},{"StartTime":209167.0,"Position":222.0,"HyperDash":false},{"StartTime":209230.0,"Position":382.0,"HyperDash":false},{"StartTime":209292.0,"Position":433.0,"HyperDash":false},{"StartTime":209355.0,"Position":450.0,"HyperDash":false},{"StartTime":209417.0,"Position":326.0,"HyperDash":false},{"StartTime":209480.0,"Position":414.0,"HyperDash":false},{"StartTime":209542.0,"Position":285.0,"HyperDash":false},{"StartTime":209605.0,"Position":336.0,"HyperDash":false},{"StartTime":209667.0,"Position":509.0,"HyperDash":false},{"StartTime":209730.0,"Position":334.0,"HyperDash":false},{"StartTime":209792.0,"Position":72.0,"HyperDash":false},{"StartTime":209855.0,"Position":425.0,"HyperDash":false},{"StartTime":209917.0,"Position":451.0,"HyperDash":false},{"StartTime":209980.0,"Position":220.0,"HyperDash":false}]},{"StartTime":210313.0,"Objects":[{"StartTime":210313.0,"Position":25.0,"HyperDash":false},{"StartTime":210375.0,"Position":77.0,"HyperDash":false},{"StartTime":210438.0,"Position":509.0,"HyperDash":false},{"StartTime":210500.0,"Position":90.0,"HyperDash":false},{"StartTime":210563.0,"Position":118.0,"HyperDash":false},{"StartTime":210625.0,"Position":58.0,"HyperDash":false},{"StartTime":210688.0,"Position":12.0,"HyperDash":false},{"StartTime":210750.0,"Position":215.0,"HyperDash":false},{"StartTime":210813.0,"Position":487.0,"HyperDash":false},{"StartTime":210875.0,"Position":446.0,"HyperDash":false},{"StartTime":210938.0,"Position":491.0,"HyperDash":false},{"StartTime":211000.0,"Position":459.0,"HyperDash":false},{"StartTime":211063.0,"Position":37.0,"HyperDash":false},{"StartTime":211125.0,"Position":291.0,"HyperDash":false},{"StartTime":211188.0,"Position":315.0,"HyperDash":false},{"StartTime":211250.0,"Position":35.0,"HyperDash":false},{"StartTime":211313.0,"Position":208.0,"HyperDash":false}]},{"StartTime":211980.0,"Objects":[{"StartTime":211980.0,"Position":440.0,"HyperDash":false},{"StartTime":212054.0,"Position":437.20932,"HyperDash":false},{"StartTime":212128.0,"Position":384.41864,"HyperDash":false},{"StartTime":212202.0,"Position":361.62793,"HyperDash":false},{"StartTime":212313.0,"Position":341.941925,"HyperDash":false}]},{"StartTime":212647.0,"Objects":[{"StartTime":212647.0,"Position":324.0,"HyperDash":false},{"StartTime":212730.0,"Position":307.11853,"HyperDash":false},{"StartTime":212813.0,"Position":283.23703,"HyperDash":false},{"StartTime":212896.0,"Position":247.35556,"HyperDash":false},{"StartTime":212980.0,"Position":236.210449,"HyperDash":false},{"StartTime":213054.0,"Position":224.70166,"HyperDash":false},{"StartTime":213128.0,"Position":185.192871,"HyperDash":false},{"StartTime":213202.0,"Position":194.684082,"HyperDash":false},{"StartTime":213313.0,"Position":148.420883,"HyperDash":false}]},{"StartTime":213647.0,"Objects":[{"StartTime":213647.0,"Position":12.0,"HyperDash":false}]},{"StartTime":213980.0,"Objects":[{"StartTime":213980.0,"Position":192.0,"HyperDash":false}]},{"StartTime":214313.0,"Objects":[{"StartTime":214313.0,"Position":312.0,"HyperDash":false}]},{"StartTime":214647.0,"Objects":[{"StartTime":214647.0,"Position":424.0,"HyperDash":false}]},{"StartTime":214980.0,"Objects":[{"StartTime":214980.0,"Position":472.0,"HyperDash":false},{"StartTime":215063.0,"Position":469.524933,"HyperDash":false},{"StartTime":215146.0,"Position":479.071075,"HyperDash":false},{"StartTime":215211.0,"Position":494.331818,"HyperDash":false},{"StartTime":215313.0,"Position":472.0,"HyperDash":false}]},{"StartTime":215647.0,"Objects":[{"StartTime":215647.0,"Position":352.0,"HyperDash":false},{"StartTime":215730.0,"Position":363.954834,"HyperDash":false},{"StartTime":215813.0,"Position":339.87323,"HyperDash":false},{"StartTime":215878.0,"Position":351.570984,"HyperDash":false},{"StartTime":215980.0,"Position":352.0,"HyperDash":false}]},{"StartTime":216313.0,"Objects":[{"StartTime":216313.0,"Position":256.0,"HyperDash":false}]},{"StartTime":216647.0,"Objects":[{"StartTime":216647.0,"Position":96.0,"HyperDash":false}]},{"StartTime":216980.0,"Objects":[{"StartTime":216980.0,"Position":208.0,"HyperDash":false}]},{"StartTime":217313.0,"Objects":[{"StartTime":217313.0,"Position":336.0,"HyperDash":false}]},{"StartTime":217647.0,"Objects":[{"StartTime":217647.0,"Position":360.0,"HyperDash":false},{"StartTime":217730.0,"Position":379.256866,"HyperDash":false},{"StartTime":217813.0,"Position":378.569519,"HyperDash":false},{"StartTime":217878.0,"Position":356.375916,"HyperDash":false},{"StartTime":217980.0,"Position":360.0,"HyperDash":false}]},{"StartTime":218313.0,"Objects":[{"StartTime":218313.0,"Position":248.0,"HyperDash":false},{"StartTime":218387.0,"Position":227.656219,"HyperDash":false},{"StartTime":218461.0,"Position":211.892563,"HyperDash":false},{"StartTime":218535.0,"Position":191.882538,"HyperDash":false},{"StartTime":218646.0,"Position":190.6999,"HyperDash":false}]},{"StartTime":218980.0,"Objects":[{"StartTime":218980.0,"Position":232.0,"HyperDash":false}]},{"StartTime":219313.0,"Objects":[{"StartTime":219313.0,"Position":152.0,"HyperDash":false}]},{"StartTime":219647.0,"Objects":[{"StartTime":219647.0,"Position":192.0,"HyperDash":false},{"StartTime":219721.0,"Position":214.85907,"HyperDash":false},{"StartTime":219795.0,"Position":222.038834,"HyperDash":false},{"StartTime":219869.0,"Position":223.900543,"HyperDash":false},{"StartTime":219980.0,"Position":247.507462,"HyperDash":false}]},{"StartTime":220313.0,"Objects":[{"StartTime":220313.0,"Position":344.0,"HyperDash":false},{"StartTime":220396.0,"Position":373.282257,"HyperDash":false},{"StartTime":220479.0,"Position":384.686676,"HyperDash":false},{"StartTime":220544.0,"Position":349.925171,"HyperDash":false},{"StartTime":220646.0,"Position":344.0,"HyperDash":false}]},{"StartTime":220980.0,"Objects":[{"StartTime":220980.0,"Position":320.0,"HyperDash":false},{"StartTime":221054.0,"Position":307.766663,"HyperDash":false},{"StartTime":221128.0,"Position":306.876526,"HyperDash":false},{"StartTime":221202.0,"Position":287.838531,"HyperDash":false},{"StartTime":221313.0,"Position":256.301666,"HyperDash":false}]},{"StartTime":221647.0,"Objects":[{"StartTime":221647.0,"Position":140.0,"HyperDash":false},{"StartTime":221730.0,"Position":123.227524,"HyperDash":false},{"StartTime":221813.0,"Position":90.30582,"HyperDash":false},{"StartTime":221878.0,"Position":121.556717,"HyperDash":false},{"StartTime":221980.0,"Position":140.0,"HyperDash":false}]},{"StartTime":222313.0,"Objects":[{"StartTime":222313.0,"Position":436.0,"HyperDash":false}]},{"StartTime":222647.0,"Objects":[{"StartTime":222647.0,"Position":316.0,"HyperDash":false}]},{"StartTime":222980.0,"Objects":[{"StartTime":222980.0,"Position":428.0,"HyperDash":false}]},{"StartTime":223313.0,"Objects":[{"StartTime":223313.0,"Position":252.0,"HyperDash":false}]},{"StartTime":223646.0,"Objects":[{"StartTime":223646.0,"Position":272.0,"HyperDash":false}]},{"StartTime":223980.0,"Objects":[{"StartTime":223980.0,"Position":380.0,"HyperDash":false}]},{"StartTime":224313.0,"Objects":[{"StartTime":224313.0,"Position":212.0,"HyperDash":false}]},{"StartTime":224647.0,"Objects":[{"StartTime":224647.0,"Position":192.0,"HyperDash":false}]},{"StartTime":224980.0,"Objects":[{"StartTime":224980.0,"Position":232.0,"HyperDash":false}]},{"StartTime":225313.0,"Objects":[{"StartTime":225313.0,"Position":232.0,"HyperDash":false}]},{"StartTime":225647.0,"Objects":[{"StartTime":225647.0,"Position":212.0,"HyperDash":false}]},{"StartTime":225980.0,"Objects":[{"StartTime":225980.0,"Position":212.0,"HyperDash":false},{"StartTime":226054.0,"Position":247.605728,"HyperDash":false},{"StartTime":226128.0,"Position":273.6619,"HyperDash":false},{"StartTime":226202.0,"Position":283.86673,"HyperDash":false},{"StartTime":226313.0,"Position":310.620728,"HyperDash":false}]},{"StartTime":226480.0,"Objects":[{"StartTime":226480.0,"Position":380.0,"HyperDash":false}]},{"StartTime":226647.0,"Objects":[{"StartTime":226647.0,"Position":400.0,"HyperDash":false}]},{"StartTime":226980.0,"Objects":[{"StartTime":226980.0,"Position":180.0,"HyperDash":false}]},{"StartTime":227313.0,"Objects":[{"StartTime":227313.0,"Position":372.0,"HyperDash":false},{"StartTime":227387.0,"Position":339.487122,"HyperDash":false},{"StartTime":227461.0,"Position":345.4503,"HyperDash":false},{"StartTime":227535.0,"Position":299.24823,"HyperDash":false},{"StartTime":227646.0,"Position":273.555176,"HyperDash":false}]},{"StartTime":227813.0,"Objects":[{"StartTime":227813.0,"Position":204.0,"HyperDash":false}]},{"StartTime":227980.0,"Objects":[{"StartTime":227980.0,"Position":212.0,"HyperDash":false}]},{"StartTime":228313.0,"Objects":[{"StartTime":228313.0,"Position":300.0,"HyperDash":false}]},{"StartTime":228647.0,"Objects":[{"StartTime":228647.0,"Position":212.0,"HyperDash":false}]},{"StartTime":228980.0,"Objects":[{"StartTime":228980.0,"Position":60.0,"HyperDash":false}]},{"StartTime":229147.0,"Objects":[{"StartTime":229147.0,"Position":136.0,"HyperDash":false}]},{"StartTime":229313.0,"Objects":[{"StartTime":229313.0,"Position":136.0,"HyperDash":false},{"StartTime":229396.0,"Position":126.907516,"HyperDash":false},{"StartTime":229479.0,"Position":112.738968,"HyperDash":false},{"StartTime":229562.0,"Position":135.404449,"HyperDash":false},{"StartTime":229646.0,"Position":130.813385,"HyperDash":false},{"StartTime":229720.0,"Position":122.399216,"HyperDash":false},{"StartTime":229794.0,"Position":152.142029,"HyperDash":false},{"StartTime":229868.0,"Position":137.941391,"HyperDash":false},{"StartTime":229979.0,"Position":150.917847,"HyperDash":false}]},{"StartTime":230313.0,"Objects":[{"StartTime":230313.0,"Position":352.0,"HyperDash":false}]},{"StartTime":230647.0,"Objects":[{"StartTime":230647.0,"Position":352.0,"HyperDash":false},{"StartTime":230730.0,"Position":366.5288,"HyperDash":false},{"StartTime":230813.0,"Position":373.811279,"HyperDash":false},{"StartTime":230896.0,"Position":365.95578,"HyperDash":false},{"StartTime":230980.0,"Position":365.109131,"HyperDash":false},{"StartTime":231054.0,"Position":343.7144,"HyperDash":false},{"StartTime":231128.0,"Position":374.024841,"HyperDash":false},{"StartTime":231202.0,"Position":338.171265,"HyperDash":false},{"StartTime":231313.0,"Position":349.468353,"HyperDash":false}]},{"StartTime":231647.0,"Objects":[{"StartTime":231647.0,"Position":236.0,"HyperDash":false},{"StartTime":231730.0,"Position":222.198776,"HyperDash":false},{"StartTime":231813.0,"Position":186.248138,"HyperDash":false},{"StartTime":231878.0,"Position":214.5214,"HyperDash":false},{"StartTime":231980.0,"Position":236.0,"HyperDash":false}]},{"StartTime":232313.0,"Objects":[{"StartTime":232313.0,"Position":316.0,"HyperDash":false}]},{"StartTime":232647.0,"Objects":[{"StartTime":232647.0,"Position":156.0,"HyperDash":false}]},{"StartTime":233313.0,"Objects":[{"StartTime":233313.0,"Position":256.0,"HyperDash":false},{"StartTime":233387.0,"Position":231.421722,"HyperDash":false},{"StartTime":233461.0,"Position":222.304459,"HyperDash":false},{"StartTime":233535.0,"Position":195.48584,"HyperDash":false},{"StartTime":233646.0,"Position":174.843628,"HyperDash":false}]},{"StartTime":233980.0,"Objects":[{"StartTime":233980.0,"Position":192.0,"HyperDash":false},{"StartTime":234063.0,"Position":220.6892,"HyperDash":false},{"StartTime":234146.0,"Position":257.786133,"HyperDash":false},{"StartTime":234229.0,"Position":260.765076,"HyperDash":false},{"StartTime":234313.0,"Position":285.29007,"HyperDash":false},{"StartTime":234396.0,"Position":317.35672,"HyperDash":false},{"StartTime":234479.0,"Position":321.969574,"HyperDash":false},{"StartTime":234562.0,"Position":349.117,"HyperDash":false},{"StartTime":234646.0,"Position":347.1605,"HyperDash":false},{"StartTime":234729.0,"Position":345.428131,"HyperDash":false},{"StartTime":234813.0,"Position":305.1539,"HyperDash":false},{"StartTime":234896.0,"Position":317.5711,"HyperDash":false},{"StartTime":234980.0,"Position":285.290039,"HyperDash":false},{"StartTime":235054.0,"Position":254.43042,"HyperDash":false},{"StartTime":235128.0,"Position":258.165863,"HyperDash":false},{"StartTime":235202.0,"Position":239.908249,"HyperDash":false},{"StartTime":235313.0,"Position":192.0,"HyperDash":false}]},{"StartTime":235647.0,"Objects":[{"StartTime":235647.0,"Position":164.0,"HyperDash":false}]},{"StartTime":235980.0,"Objects":[{"StartTime":235980.0,"Position":348.0,"HyperDash":false}]},{"StartTime":236313.0,"Objects":[{"StartTime":236313.0,"Position":256.0,"HyperDash":false},{"StartTime":236396.0,"Position":252.0,"HyperDash":false},{"StartTime":236479.0,"Position":256.0,"HyperDash":false},{"StartTime":236544.0,"Position":263.0,"HyperDash":false},{"StartTime":236646.0,"Position":256.0,"HyperDash":false}]},{"StartTime":236980.0,"Objects":[{"StartTime":236980.0,"Position":256.0,"HyperDash":false},{"StartTime":237063.0,"Position":268.0,"HyperDash":false},{"StartTime":237146.0,"Position":256.0,"HyperDash":false},{"StartTime":237211.0,"Position":262.0,"HyperDash":false},{"StartTime":237313.0,"Position":256.0,"HyperDash":false}]},{"StartTime":237647.0,"Objects":[{"StartTime":237647.0,"Position":276.0,"HyperDash":false}]},{"StartTime":237980.0,"Objects":[{"StartTime":237980.0,"Position":276.0,"HyperDash":false}]},{"StartTime":238313.0,"Objects":[{"StartTime":238313.0,"Position":344.0,"HyperDash":false},{"StartTime":238387.0,"Position":349.4431,"HyperDash":false},{"StartTime":238461.0,"Position":367.88623,"HyperDash":false},{"StartTime":238535.0,"Position":402.329346,"HyperDash":false},{"StartTime":238646.0,"Position":417.994019,"HyperDash":false}]},{"StartTime":238980.0,"Objects":[{"StartTime":238980.0,"Position":224.0,"HyperDash":false}]},{"StartTime":239147.0,"Objects":[{"StartTime":239147.0,"Position":328.0,"HyperDash":false}]},{"StartTime":239313.0,"Objects":[{"StartTime":239313.0,"Position":328.0,"HyperDash":false},{"StartTime":239387.0,"Position":303.777771,"HyperDash":false},{"StartTime":239461.0,"Position":283.555542,"HyperDash":false},{"StartTime":239535.0,"Position":243.333313,"HyperDash":false},{"StartTime":239646.0,"Position":228.0,"HyperDash":false}]},{"StartTime":239980.0,"Objects":[{"StartTime":239980.0,"Position":288.0,"HyperDash":false},{"StartTime":240054.0,"Position":273.789337,"HyperDash":false},{"StartTime":240128.0,"Position":255.578659,"HyperDash":false},{"StartTime":240202.0,"Position":211.368,"HyperDash":false},{"StartTime":240313.0,"Position":192.552,"HyperDash":false}]},{"StartTime":240647.0,"Objects":[{"StartTime":240647.0,"Position":72.0,"HyperDash":false}]},{"StartTime":240980.0,"Objects":[{"StartTime":240980.0,"Position":92.0,"HyperDash":false}]},{"StartTime":241313.0,"Objects":[{"StartTime":241313.0,"Position":92.0,"HyperDash":false}]},{"StartTime":241647.0,"Objects":[{"StartTime":241647.0,"Position":52.0,"HyperDash":false}]},{"StartTime":241980.0,"Objects":[{"StartTime":241980.0,"Position":152.0,"HyperDash":false},{"StartTime":242063.0,"Position":152.083969,"HyperDash":false},{"StartTime":242146.0,"Position":194.167923,"HyperDash":false},{"StartTime":242229.0,"Position":202.251892,"HyperDash":false},{"StartTime":242313.0,"Position":216.594238,"HyperDash":false},{"StartTime":242396.0,"Position":191.57486,"HyperDash":false},{"StartTime":242479.0,"Position":179.4909,"HyperDash":false},{"StartTime":242562.0,"Position":169.406937,"HyperDash":false},{"StartTime":242646.0,"Position":152.0,"HyperDash":false},{"StartTime":242720.0,"Position":158.210739,"HyperDash":false},{"StartTime":242795.0,"Position":179.744431,"HyperDash":false},{"StartTime":242869.0,"Position":185.084351,"HyperDash":false},{"StartTime":242980.0,"Position":216.594238,"HyperDash":false}]},{"StartTime":243313.0,"Objects":[{"StartTime":243313.0,"Position":216.0,"HyperDash":false}]},{"StartTime":243980.0,"Objects":[{"StartTime":243980.0,"Position":444.0,"HyperDash":false}]},{"StartTime":244313.0,"Objects":[{"StartTime":244313.0,"Position":292.0,"HyperDash":false}]},{"StartTime":244647.0,"Objects":[{"StartTime":244647.0,"Position":204.0,"HyperDash":false}]},{"StartTime":244980.0,"Objects":[{"StartTime":244980.0,"Position":52.0,"HyperDash":false}]},{"StartTime":245147.0,"Objects":[{"StartTime":245147.0,"Position":128.0,"HyperDash":false}]},{"StartTime":245313.0,"Objects":[{"StartTime":245313.0,"Position":128.0,"HyperDash":false},{"StartTime":245387.0,"Position":95.02887,"HyperDash":false},{"StartTime":245461.0,"Position":102.54911,"HyperDash":false},{"StartTime":245535.0,"Position":83.8343353,"HyperDash":false},{"StartTime":245646.0,"Position":76.92937,"HyperDash":false}]},{"StartTime":245980.0,"Objects":[{"StartTime":245980.0,"Position":52.0,"HyperDash":false}]},{"StartTime":246313.0,"Objects":[{"StartTime":246313.0,"Position":312.0,"HyperDash":false}]},{"StartTime":246480.0,"Objects":[{"StartTime":246480.0,"Position":192.0,"HyperDash":false}]},{"StartTime":246647.0,"Objects":[{"StartTime":246647.0,"Position":192.0,"HyperDash":false},{"StartTime":246730.0,"Position":188.38472,"HyperDash":false},{"StartTime":246813.0,"Position":225.710083,"HyperDash":false},{"StartTime":246896.0,"Position":227.818253,"HyperDash":false},{"StartTime":246980.0,"Position":260.7363,"HyperDash":false},{"StartTime":247054.0,"Position":259.404358,"HyperDash":false},{"StartTime":247128.0,"Position":316.934875,"HyperDash":false},{"StartTime":247202.0,"Position":301.161316,"HyperDash":false},{"StartTime":247313.0,"Position":350.4887,"HyperDash":false}]},{"StartTime":247646.0,"Objects":[{"StartTime":247646.0,"Position":436.0,"HyperDash":false}]},{"StartTime":247813.0,"Objects":[{"StartTime":247813.0,"Position":368.0,"HyperDash":false}]},{"StartTime":247980.0,"Objects":[{"StartTime":247980.0,"Position":402.0,"HyperDash":false},{"StartTime":248054.0,"Position":427.9642,"HyperDash":false},{"StartTime":248128.0,"Position":455.292267,"HyperDash":false},{"StartTime":248202.0,"Position":467.624146,"HyperDash":false},{"StartTime":248313.0,"Position":467.800751,"HyperDash":false}]},{"StartTime":248647.0,"Objects":[{"StartTime":248647.0,"Position":230.0,"HyperDash":false}]},{"StartTime":248980.0,"Objects":[{"StartTime":248980.0,"Position":467.0,"HyperDash":false},{"StartTime":249054.0,"Position":448.114563,"HyperDash":false},{"StartTime":249128.0,"Position":449.648,"HyperDash":false},{"StartTime":249202.0,"Position":452.133575,"HyperDash":false},{"StartTime":249313.0,"Position":426.641052,"HyperDash":false}]},{"StartTime":249647.0,"Objects":[{"StartTime":249647.0,"Position":205.0,"HyperDash":false}]},{"StartTime":249813.0,"Objects":[{"StartTime":249813.0,"Position":307.0,"HyperDash":false}]},{"StartTime":249980.0,"Objects":[{"StartTime":249980.0,"Position":200.0,"HyperDash":false}]},{"StartTime":250313.0,"Objects":[{"StartTime":250313.0,"Position":360.0,"HyperDash":false}]},{"StartTime":250647.0,"Objects":[{"StartTime":250647.0,"Position":200.0,"HyperDash":false}]},{"StartTime":250980.0,"Objects":[{"StartTime":250980.0,"Position":320.0,"HyperDash":false}]},{"StartTime":251313.0,"Objects":[{"StartTime":251313.0,"Position":240.0,"HyperDash":false}]},{"StartTime":251647.0,"Objects":[{"StartTime":251647.0,"Position":152.0,"HyperDash":false}]},{"StartTime":251980.0,"Objects":[{"StartTime":251980.0,"Position":280.0,"HyperDash":false}]},{"StartTime":252647.0,"Objects":[{"StartTime":252647.0,"Position":232.0,"HyperDash":false}]},{"StartTime":253313.0,"Objects":[{"StartTime":253313.0,"Position":280.0,"HyperDash":false}]},{"StartTime":253980.0,"Objects":[{"StartTime":253980.0,"Position":120.0,"HyperDash":false}]},{"StartTime":254646.0,"Objects":[{"StartTime":254646.0,"Position":392.0,"HyperDash":false}]},{"StartTime":255313.0,"Objects":[{"StartTime":255313.0,"Position":120.0,"HyperDash":false}]},{"StartTime":255647.0,"Objects":[{"StartTime":255647.0,"Position":256.0,"HyperDash":false}]},{"StartTime":255813.0,"Objects":[{"StartTime":255813.0,"Position":236.0,"HyperDash":false}]},{"StartTime":255980.0,"Objects":[{"StartTime":255980.0,"Position":276.0,"HyperDash":false}]},{"StartTime":256146.0,"Objects":[{"StartTime":256146.0,"Position":496.0,"HyperDash":false},{"StartTime":256216.0,"Position":27.0,"HyperDash":false},{"StartTime":256286.0,"Position":477.0,"HyperDash":false},{"StartTime":256356.0,"Position":163.0,"HyperDash":false},{"StartTime":256427.0,"Position":260.0,"HyperDash":false},{"StartTime":256497.0,"Position":253.0,"HyperDash":false},{"StartTime":256567.0,"Position":423.0,"HyperDash":false},{"StartTime":256638.0,"Position":367.0,"HyperDash":false},{"StartTime":256708.0,"Position":146.0,"HyperDash":false},{"StartTime":256778.0,"Position":322.0,"HyperDash":false},{"StartTime":256849.0,"Position":169.0,"HyperDash":false},{"StartTime":256919.0,"Position":159.0,"HyperDash":false},{"StartTime":256989.0,"Position":388.0,"HyperDash":false},{"StartTime":257060.0,"Position":67.0,"HyperDash":false},{"StartTime":257130.0,"Position":176.0,"HyperDash":false},{"StartTime":257200.0,"Position":371.0,"HyperDash":false},{"StartTime":257271.0,"Position":365.0,"HyperDash":false},{"StartTime":257341.0,"Position":104.0,"HyperDash":false},{"StartTime":257411.0,"Position":363.0,"HyperDash":false},{"StartTime":257481.0,"Position":75.0,"HyperDash":false},{"StartTime":257552.0,"Position":158.0,"HyperDash":false},{"StartTime":257622.0,"Position":98.0,"HyperDash":false},{"StartTime":257692.0,"Position":30.0,"HyperDash":false},{"StartTime":257763.0,"Position":164.0,"HyperDash":false},{"StartTime":257833.0,"Position":341.0,"HyperDash":false},{"StartTime":257903.0,"Position":18.0,"HyperDash":false},{"StartTime":257974.0,"Position":210.0,"HyperDash":false},{"StartTime":258044.0,"Position":420.0,"HyperDash":false},{"StartTime":258114.0,"Position":447.0,"HyperDash":false},{"StartTime":258185.0,"Position":78.0,"HyperDash":false},{"StartTime":258255.0,"Position":177.0,"HyperDash":false},{"StartTime":258325.0,"Position":305.0,"HyperDash":false},{"StartTime":258396.0,"Position":400.0,"HyperDash":false},{"StartTime":258466.0,"Position":462.0,"HyperDash":false},{"StartTime":258536.0,"Position":64.0,"HyperDash":false},{"StartTime":258606.0,"Position":458.0,"HyperDash":false},{"StartTime":258677.0,"Position":380.0,"HyperDash":false},{"StartTime":258747.0,"Position":65.0,"HyperDash":false},{"StartTime":258817.0,"Position":23.0,"HyperDash":false},{"StartTime":258888.0,"Position":379.0,"HyperDash":false},{"StartTime":258958.0,"Position":44.0,"HyperDash":false},{"StartTime":259028.0,"Position":485.0,"HyperDash":false},{"StartTime":259099.0,"Position":269.0,"HyperDash":false},{"StartTime":259169.0,"Position":155.0,"HyperDash":false},{"StartTime":259239.0,"Position":324.0,"HyperDash":false},{"StartTime":259310.0,"Position":149.0,"HyperDash":false},{"StartTime":259380.0,"Position":351.0,"HyperDash":false},{"StartTime":259450.0,"Position":385.0,"HyperDash":false},{"StartTime":259521.0,"Position":338.0,"HyperDash":false},{"StartTime":259591.0,"Position":322.0,"HyperDash":false},{"StartTime":259661.0,"Position":84.0,"HyperDash":false},{"StartTime":259731.0,"Position":342.0,"HyperDash":false},{"StartTime":259802.0,"Position":395.0,"HyperDash":false},{"StartTime":259872.0,"Position":72.0,"HyperDash":false},{"StartTime":259942.0,"Position":324.0,"HyperDash":false},{"StartTime":260013.0,"Position":67.0,"HyperDash":false},{"StartTime":260083.0,"Position":371.0,"HyperDash":false},{"StartTime":260153.0,"Position":446.0,"HyperDash":false},{"StartTime":260224.0,"Position":29.0,"HyperDash":false},{"StartTime":260294.0,"Position":22.0,"HyperDash":false},{"StartTime":260364.0,"Position":432.0,"HyperDash":false},{"StartTime":260435.0,"Position":12.0,"HyperDash":false},{"StartTime":260505.0,"Position":330.0,"HyperDash":false},{"StartTime":260575.0,"Position":419.0,"HyperDash":false},{"StartTime":260646.0,"Position":278.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/104973.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/104973.osu new file mode 100644 index 0000000000..6edd8229a2 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/104973.osu @@ -0,0 +1,491 @@ +osu file format v9 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:6 +ApproachRate:6 +SliderMultiplier:2 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +2,100846,120263 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +1980,666.666666666667,4,2,2,20,1,0 +12647,-100,4,2,2,42,0,0 +39646,-100,4,2,1,22,0,0 +39813,-100,4,2,2,42,0,0 +40313,-100,4,2,1,22,0,0 +40480,-100,4,2,2,42,0,0 +57980,-100,4,2,2,47,0,1 +75313,-100,4,2,1,22,0,1 +75646,-100,4,2,2,47,0,1 +79646,-100,4,2,1,22,0,1 +79813,-100,4,2,2,47,0,1 +80313,-100,4,2,1,22,0,1 +80480,-100,4,2,2,47,0,1 +80980,-100,4,2,1,22,0,1 +81146,-100,4,2,2,47,0,1 +81646,-100,4,2,1,22,0,1 +81813,-100,4,2,2,47,0,1 +100646,-100,4,2,2,42,0,0 +148980,-100,4,2,1,22,0,0 +149146,-100,4,2,2,42,0,0 +149646,-100,4,2,1,22,0,0 +149813,-100,4,2,2,42,0,0 +167313,-100,4,2,2,47,0,1 +178313,-100,4,2,1,22,0,1 +178480,-100,4,2,2,47,0,1 +178980,-100,4,2,1,22,0,1 +179146,-100,4,2,2,47,0,1 +179646,-100,4,2,1,22,0,1 +179813,-100,4,2,2,47,0,1 +180313,-100,4,2,1,22,0,1 +180480,-100,4,2,2,47,0,1 +187980,-100,4,2,2,42,0,0 +212646,-100,4,2,2,47,0,1 +260646,-100,4,2,2,42,0,0 + +[HitObjects] +152,72,11980,1,0 +248,144,12313,1,0 +132,176,12647,2,0,B|44:112,2,100,0|0|8 +132,176,13646,1,0 +240,232,13980,2,0,B|164:296,2,100,0|0|12 +240,232,14980,1,0 +304,128,15313,6,0,B|416:184,1,100,0|0 +496,240,15980,2,0,B|466:289|384:312,1,100,8|0 +296,304,16647,2,0,B|192:296|128:192,2,200,2|12|0 +296,184,18312,5,0 +296,184,18646,1,8 +416,184,18980,2,0,B|376:64,1,100,0|0 +268,116,19646,1,0 +268,116,19980,1,12 +168,184,20313,2,0,B|80:248,2,100,0|0|2 +232,80,21313,2,0,B|128:56,2,100,8|0|2 +453,174,22647,5,12 +408,284,22980,1,0 +336,188,23313,1,2 +448,236,23647,1,0 +336,188,23980,2,0,B|336:300,2,100,8|0|2 +256,60,24980,1,0 +112,104,25313,5,12 +228,136,25647,1,0 +132,208,25979,1,2 +176,96,26313,1,0 +132,208,26646,2,0,B|252:200,2,100,8|0|2 +256,292,27647,1,0 +404,280,27980,6,0,B|460:256|476:176,1,100,12|0 +348,184,28646,1,2 +348,184,28980,1,0 +336,64,29313,2,0,B|280:72|248:152,1,100,8|0 +304,236,29979,1,2 +304,236,30313,1,0 +304,236,30646,1,12 +24,120,31313,5,2 +60,264,31646,1,0 +96,120,31979,1,8 +132,264,32313,1,0 +264,192,32647,1,2 +488,108,33313,5,12 +488,108,33647,2,0,B|432:236,1,100,0|0 +380,300,34313,2,0,B|356:348,2,50,0|0|8 +312,200,34980,2,0,B|248:208|208:168,1,100,0|2 +116,112,35646,2,0,B|60:112,2,50,0|0|12 +232,80,36313,2,0,B|292:76|340:112,1,100,0|0 +356,156,36813,2,0,B|420:156,2,50,2|0|0 +296,156,37313,1,8 +176,156,37646,2,0,B|120:156,2,50,0|2|0 +176,156,38313,1,2 +60,128,38647,5,12 +168,88,38980,1,0 +60,128,39313,2,0,B|76:216|140:264,1,150,2|0 +148,312,39980,2,0,B|224:316|296:252,1,150,8|0 +285,261,40647,1,2 +392,204,40980,2,0,B|448:192,2,50,2|2|12 +292,140,41647,2,0,B|244:108|164:100,1,100 +176,160,42147,2,0,B|176:256,1,50,2|0 +140,258,42480,2,0,B|76:258,1,50,0|8 +210,258,42980,2,0,B|266:258,2,50,0|2|0 +257,147,43647,1,2 +256,28,43980,5,4 +256,28,44313,1,0 +344,108,44647,2,0,B|464:156,1,100,2|0 +340,216,45313,2,0,B|244:320,1,100,8|0 +236,176,45980,2,0,B|196:80,1,100,2|0 +92,144,46647,2,0,B|64:192|96:244,1,100,12|0 +204,192,47313,1,2 +324,192,47647,2,0,B|380:192,2,50,0|0|8 +212,144,48313,2,0,B|180:192|220:248,1,100,0|2 +324,192,48980,1,0 +324,192,49313,1,12 +256,292,49647,6,0,B|256:340,2,50,0|0|2 +324,192,50313,1,0 +324,192,50647,1,8 +256,92,50980,2,0,B|256:28,2,50,0|0|2 +200,200,51647,2,0,B|304:200,1,100,0|12 +136,24,52647,5,6 +256,112,52980,2,0,B|368:184,2,100,0|2|0 +376,24,53980,1,6 +256,112,54313,2,0,B|144:184,2,100,0|2|0 +256,264,55313,1,6 +256,112,55647,2,0,B|256:0,2,100,0|2|0 +256,112,56647,1,6 +488,48,57313,5,12 +488,48,57647,2,0,B|485:103|448:160,1,100,0|2 +360,72,58313,2,0,B|320:104|312:176,1,100,8|8 +428,200,58980,1,0 +344,288,59313,1,2 +224,288,59647,2,0,B|208:352,2,50,2|2|12 +256,172,60313,1,0 +256,172,60647,1,2 +136,192,60980,2,0,B|64:204,2,50,2|2|8 +256,172,61647,5,0 +352,244,61980,1,2 +420,144,62313,1,8 +324,72,62647,1,12 +204,72,62980,2,0,B|132:80,2,50,2|2|0 +324,72,63647,2,0,B|372:120|324:200,1,100,0|8 +252,244,64313,1,0 +148,184,64647,1,2 +36,224,64980,2,0,B|68:344,2,100,0|12|8 +24,104,65980,6,0,B|81:72|168:144|232:88,1,200,2|8 +340,84,66980,2,0,B|404:92|444:164,1,100,0|2 +436,252,67647,2,0,B|404:292|404:292,2,50,0|0|12 +436,252,68313,1,0 +332,192,68646,6,0,B|248:120,1,100,0|8 +272,248,69313,2,0,B|176:312,1,100,8|0 +208,184,69980,2,0,B|112:112,1,100,0|8 +128,244,70647,2,0,B|40:300,1,100,12|0 +20,180,71313,5,0 +72,72,71647,2,0,B|40:24,2,50,2|2|8 +192,80,72313,1,0 +300,132,72647,1,2 +300,252,72980,1,8 +192,304,73313,1,12 +72,320,73647,2,0,B|16:368,2,50,2|2|0 +112,208,74313,5,2 +112,208,74647,2,0,B|232:96|264:216|384:72,1,300,8|2 +492,104,75980,2,0,B|428:144|477:263|428:304,1,200,12|0 +320,268,76980,2,0,B|360:156,1,100,0|8 +256,76,77646,2,0,B|256:180,1,100,0|2 +192,268,78313,2,0,B|152:156,1,100,8|12 +216,68,78980,5,0 +320,128,79313,2,0,B|392:160|424:252,1,150,2|0 +408,276,79980,2,0,B|325:276|256:356,1,150,8|0 +236,336,80647,2,0,B|180:272|92:272,1,150,2|0 +88,236,81313,2,0,B|120:152|208:116,1,150,8|0 +224,112,81980,1,2 +344,116,82313,6,0,B|408:116,2,50,2|2|8 +252,192,82980,1,8 +344,268,83313,1,2 +436,192,83647,1,2 +344,116,83980,1,12 +228,80,84313,6,0,B|228:24,2,50,2|2|0 +120,132,84980,1,8 +120,252,85313,1,8 +120,132,85647,1,0 +120,252,85980,1,2 +224,192,86313,1,0 +104,192,86647,1,12 +104,192,86980,1,0 +104,192,87313,6,0,B|312:192,2,200,2|8|2 +12,112,88980,1,0 +104,192,89313,1,12 +124,72,89647,1,2 +244,56,89980,6,0,B|355:55|444:144,1,200,2|8 +416,248,90980,1,2 +312,308,91313,2,0,B|216:308|112:228,1,200,2|12 +88,124,92313,2,0,B|102:102|160:116|192:92,1,100,2|2 +292,144,92980,2,0,B|300:216,2,50,0|0|8 +280,24,93647,1,0 +392,68,93980,1,2 +408,188,94313,1,8 +320,272,94647,1,12 +200,284,94980,6,0,B|208:212,2,50,2|2|0 +80,260,95647,1,2 +20,156,95980,2,0,B|108:76|212:140,1,200,8|0 +304,204,96980,1,8 +416,252,97313,2,0,B|392:300|336:316,2,100,12|0|6 +256,192,98146,12,4,100646 +104,104,121313,6,0,B|216:104,1,100,12|0 +176,220,121980,2,0,B|368:132,1,200,2|8 +240,120,122980,2,0,B|320:80,2,50,2|2|0 +136,180,123647,2,0,B|264:228,1,100,0|12 +348,240,124313,2,0,B|252:288,1,100,0|2 +192,184,124980,1,2 +308,160,125313,1,8 +192,132,125647,1,0 +256,32,125980,6,0,B|256:240,1,200,2|12 +356,296,126980,1,0 +240,328,127313,2,0,B|128:360|56:264,1,200,2|8 +24,156,128313,2,0,B|76:148|80:176|128:164,1,100,2|0 +240,192,128980,2,0,B|232:248,2,50,2|2|12 +208,76,129647,2,0,B|268:72|312:112,1,100,2|0 +388,188,130313,1,0 +388,188,130647,1,8 +336,296,130980,1,0 +336,296,131313,1,2 +128,176,131980,5,12 +128,176,132313,1,2 +128,176,132647,2,0,B|171:149|240:168,1,100,2|0 +264,176,133147,1,0 +272,216,133313,2,0,B|239:264|176:256,1,100,8|0 +68,232,133980,1,2 +68,232,134313,1,0 +88,112,134647,6,0,B|115:65|176:48,1,100,12|2 +204,40,135147,1,0 +244,40,135313,2,0,B|316:48|356:120,1,100,2|0 +400,184,135980,2,0,B|408:248|336:292,1,100,8|0 +252,316,136647,1,2 +252,316,136980,1,0 +240,196,137313,6,0,B|288:180|312:116,1,100,12|2 +300,88,137813,1,0 +276,56,137980,2,0,B|180:16,1,100,2|0 +144,152,138647,2,0,B|24:200,1,100,8|0 +176,252,139313,2,0,B|96:348,1,100,2|0 +252,336,139980,2,0,B|332:240,1,100,12|0 +436,252,140647,2,0,B|382:158|258:151,1,200,2|8 +152,152,141647,2,0,B|104:152,2,50,2|2|0 +388,116,142647,6,0,B|496:32,2,100,12|0|2 +272,152,143647,2,0,B|252:248,1,100,2|8 +251,249,144313,1,2 +130,250,144647,2,0,B|98:298,2,50,2|2|0 +200,152,145313,1,12 +200,152,145647,1,2 +304,92,145980,6,0,B|360:68,1,50,0|2 +400,180,146480,2,0,B|384:236,1,50,0|8 +272,192,146980,1,0 +152,192,147313,2,0,B|96:192,4,50,2|0|2|0|12 +240,272,148313,5,0 +360,296,148647,2,0,B|448:240|456:176,1,150,2|0 +396,168,149313,2,0,B|428:120|428:8,1,150,8|0 +427,23,149980,1,2 +316,68,150313,2,0,B|364:36,2,50,2|2|12 +436,76,150980,2,0,B|324:148,1,100 +296,152,151480,6,0,B|224:172,1,50,2|2 +292,208,151813,2,0,B|288:256,1,50,0|8 +248,212,152147,2,0,B|176:236,1,50,2|2 +244,268,152480,2,0,B|236:336,1,50,0|0 +256,76,153313,5,12 +256,76,153647,1,0 +256,76,153980,2,0,B|48:196,1,200,2|8 +256,76,154980,1,0 +140,44,155313,2,0,B|252:228,1,200,2|12 +140,44,156313,1,0 +84,152,156647,6,0,B|148:264,1,100,2|2 +164,264,157147,1,0 +204,272,157313,1,8 +324,268,157647,2,0,B|428:236,1,100,2|2 +336,152,158313,2,0,B|248:64,1,100,0|12 +164,148,158980,5,0 +164,148,159313,1,2 +48,120,159646,2,0,B|24:48,2,50,2|0|8 +112,224,160313,1,0 +224,272,160647,1,2 +344,248,160980,1,0 +416,152,161313,1,12 +256,336,161980,5,6 +360,272,162313,2,0,B|464:272,2,100,0|8|0 +256,216,163313,1,6 +152,152,163646,2,0,B|48:152,2,100,0|8|0 +256,96,164647,1,6 +360,40,164980,2,0,B|464:40,2,100,0|8|0 +256,96,165980,1,6 +16,80,166646,6,0,B|24:136|56:200,1,100,12|0 +116,80,167313,2,0,B|158:111|220:112,1,100,2|2 +248,112,167814,1,0 +288,112,167980,2,0,B|341:115|384:152,1,100,12|8 +412,172,168480,1,0 +428,208,168647,2,0,B|380:248|300:208,1,100,2|2 +296,208,169147,1,0 +260,192,169313,6,0,B|212:168|140:184,1,100,12|2 +124,188,169814,1,0 +88,204,169980,2,0,B|96:260|200:284,1,100,2|2 +192,284,170480,1,0 +232,288,170647,2,0,B|288:296|336:256,1,100,8|8 +424,196,171314,1,2 +424,196,171647,1,2 +424,196,171980,6,0,B|416:136|360:108,1,100,12|0 +336,100,172480,1,2 +296,88,172646,2,0,B|248:72|192:104,2,100,2|0|8 +256,204,173647,1,8 +164,124,173980,2,0,B|108:112|68:164,2,100,0|0|12 +132,240,174980,2,0,B|92:280|108:344,1,100,2|0 +212,280,175646,2,0,B|272:264|276:184,2,100,2|8|2 +212,280,176647,1,2 +8,136,177313,6,0,B|29:82|104:64,1,100,12|0 +200,64,177980,2,0,B|352:104,1,150,2|0 +344,144,178647,2,0,B|184:168,1,150,8|0 +196,208,179313,2,0,B|348:232,1,150,2|0 +344,272,179980,2,0,B|184:288,1,150,12|0 +136,276,180647,2,0,B|58:233|64:140,2,150,2|2|2 +188,168,181980,1,2 +188,168,182647,5,12 +76,124,182980,2,0,B|20:100,2,50,2|2|0 +188,168,183647,1,8 +300,212,183980,2,0,B|356:228|428:204,2,100,8|0|2 +256,324,184980,2,0,B|200:316|168:260,2,100,0|12|0 +256,324,185980,1,2 +256,84,186647,5,8 +316,188,186980,1,0 +196,188,187313,1,2 +408,300,187980,5,12 +432,184,188313,1,0 +320,228,188647,1,2 +224,300,188980,2,0,B|176:332,2,50,0|0|8 +120,240,189647,2,0,B|64:248,2,50,0|0|2 +96,120,190313,2,0,B|48:96,2,50,0|0|12 +188,40,190980,2,0,B|236:60|272:132,1,100 +320,212,191646,2,0,B|376:236,2,50,0|0|8 +316,92,192313,1,0 +316,92,192646,1,2 +320,212,192980,1,2 +320,212,193313,1,12 +404,124,193647,6,0,B|444:76,2,50,0|0|2 +404,244,194313,2,0,B|404:356,1,100,0|8 +344,216,194980,2,0,B|288:312,1,100,0|2 +300,164,195647,2,0,B|188:212,1,100,0|12 +300,96,196313,2,0,B|180:80,2,100,0|2|0 +420,116,197313,1,8 +420,116,197647,1,0 +300,96,197980,2,0,B|196:80,1,100 +80,72,198647,1,12 +80,72,198980,1,0 +200,68,199313,6,0,B|256:88|272:140,1,100,2|0 +284,172,199813,1,0 +284,212,199980,1,8 +164,224,200313,1,8 +284,212,200647,6,0,B|288:276|228:316,1,100,2|0 +212,324,201147,1,0 +176,344,201314,1,12 +164,224,201647,1,8 +176,344,201980,6,0,B|124:352|72:296,1,100,2|0 +60,280,202480,1,0 +44,244,202647,1,8 +164,224,202980,1,8 +44,244,203313,6,0,B|24:196|44:140,2,100,2|0|12 +152,192,204313,2,0,B|80:192,2,50,2|2|0 +272,192,204980,1,2 +272,192,205313,2,0,B|272:104|153:100|152:200,1,200,8|0 +152,312,206313,6,0,B|152:360,2,50,2|2|12 +152,192,206980,1,0 +152,192,207313,1,14 +152,72,207646,2,0,B|152:16,2,50,0|0|2 +248,144,208313,2,0,B|272:192|240:272,1,100,0|12 +256,192,208980,12,12,209980 +256,192,210313,12,12,211313 +440,208,211980,6,0,B|320:184,1,100,12|0 +324,68,212647,2,0,B|148:164,1,200,2|8 +80,264,213647,1,8 +192,312,213980,1,2 +312,296,214313,1,2 +424,256,214647,1,12 +472,144,214980,6,0,B|480:88,2,50,2|2|0 +352,120,215647,2,0,B|336:56,2,50,2|2|8 +296,224,216313,1,0 +176,208,216647,1,0 +152,88,216980,1,8 +272,104,217313,1,12 +360,184,217647,6,0,B|392:264,2,50,2|2|0 +248,144,218313,2,0,B|200:176|184:248,1,100,0|8 +208,344,218980,1,8 +192,224,219313,1,2 +192,224,219647,2,0,B|200:176|248:144,1,100,0|12 +344,72,220313,2,0,B|400:32,2,50,2|0|2 +320,192,220980,2,0,B|296:248|224:288,1,100,0|8 +140,296,221647,2,0,B|68:304,2,50,2|0|2 +252,248,222313,5,0 +316,144,222647,1,12 +372,248,222980,1,0 +252,248,223313,1,2 +252,248,223646,5,8 +316,144,223980,1,2 +212,80,224313,1,0 +212,80,224647,5,2 +212,176,224980,1,8 +212,176,225313,1,12 +212,176,225647,1,0 +212,296,225980,6,0,B|266:312|316:296,1,100,2|0 +348,284,226480,1,2 +380,260,226647,1,8 +280,192,226980,1,8 +372,116,227313,2,0,B|319:99|268:116,1,100,2|0 +236,128,227813,1,2 +208,156,227980,1,12 +256,268,228313,1,0 +256,268,228647,1,2 +136,284,228980,5,2 +136,284,229147,1,0 +136,284,229313,2,0,B|115:183|160:60,1,200,8|0 +256,20,230313,1,0 +352,92,230647,2,0,B|385:194|336:332,1,200,12|0 +236,336,231647,2,0,B|156:344,2,50,0|0|8 +236,336,232313,1,2 +236,336,232647,1,2 +256,96,233313,6,0,B|200:104|168:160,1,100,12|0 +192,268,233980,2,0,B|304:260|352:148,2,200,2|8|2 +164,152,235647,1,0 +256,76,235980,1,12 +256,196,236313,2,0,B|256:260,2,50,2|2|0 +256,76,236980,2,0,B|256:20,2,50,2|2|8 +256,76,237647,1,8 +256,76,237980,1,2 +344,156,238313,2,0,B|432:236,1,100,0|12 +328,304,238980,5,2 +328,304,239147,1,0 +328,304,239313,2,0,B|192:304,1,100,2|0 +288,200,239980,2,0,B|160:160,1,100,8|8 +72,152,240647,1,2 +72,272,240980,1,0 +72,152,241313,1,12 +72,272,241647,1,0 +152,184,241980,2,0,B|240:80,3,100,2|0|8|0 +216,107,243313,1,2 +444,176,243980,5,12 +368,268,244313,1,0 +248,280,244647,1,2 +128,256,244980,1,2 +128,256,245147,1,0 +128,256,245313,2,0,B|80:216|72:144,1,100,8|8 +72,52,245980,5,2 +192,72,246313,1,2 +192,72,246480,1,0 +192,72,246647,2,0,B|248:160|368:192,1,200,12|8 +402,78,247646,5,2 +402,78,247813,1,0 +402,78,247980,2,0,B|453:111|474:166,1,100,8|8 +352,187,248647,1,2 +467,153,248980,2,0,B|459:217|419:249,1,100,0|12 +312,280,249647,5,2 +256,300,249813,1,0 +200,280,249980,1,2 +280,192,250313,1,0 +280,192,250647,1,8 +320,80,250980,1,0 +280,192,251313,1,2 +196,108,251647,1,0 +280,192,251980,1,12 +256,56,252647,5,2 +256,328,253313,1,2 +120,192,253980,1,2 +392,192,254646,1,2 +256,192,255313,1,2 +256,192,255647,1,2 +256,192,255813,1,0 +256,192,255980,1,12 +256,192,256146,12,4,260646 diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1284935-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1284935-expected-conversion.json new file mode 100644 index 0000000000..8976f6b066 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1284935-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":707.0,"Objects":[{"StartTime":707.0,"Position":65.0,"HyperDash":false},{"StartTime":759.0,"Position":482.0,"HyperDash":false},{"StartTime":811.0,"Position":164.0,"HyperDash":false},{"StartTime":863.0,"Position":315.0,"HyperDash":false},{"StartTime":915.0,"Position":145.0,"HyperDash":false},{"StartTime":967.0,"Position":159.0,"HyperDash":false},{"StartTime":1019.0,"Position":310.0,"HyperDash":false},{"StartTime":1071.0,"Position":441.0,"HyperDash":false},{"StartTime":1123.0,"Position":428.0,"HyperDash":false},{"StartTime":1175.0,"Position":243.0,"HyperDash":false},{"StartTime":1227.0,"Position":422.0,"HyperDash":false},{"StartTime":1280.0,"Position":481.0,"HyperDash":false},{"StartTime":1332.0,"Position":104.0,"HyperDash":false},{"StartTime":1384.0,"Position":473.0,"HyperDash":false},{"StartTime":1436.0,"Position":135.0,"HyperDash":false},{"StartTime":1488.0,"Position":360.0,"HyperDash":false},{"StartTime":1540.0,"Position":123.0,"HyperDash":false},{"StartTime":1592.0,"Position":42.0,"HyperDash":false},{"StartTime":1644.0,"Position":393.0,"HyperDash":false},{"StartTime":1696.0,"Position":75.0,"HyperDash":false},{"StartTime":1748.0,"Position":377.0,"HyperDash":false},{"StartTime":1800.0,"Position":354.0,"HyperDash":false},{"StartTime":1853.0,"Position":287.0,"HyperDash":false},{"StartTime":1905.0,"Position":361.0,"HyperDash":false},{"StartTime":1957.0,"Position":479.0,"HyperDash":false},{"StartTime":2009.0,"Position":346.0,"HyperDash":false},{"StartTime":2061.0,"Position":266.0,"HyperDash":false},{"StartTime":2113.0,"Position":400.0,"HyperDash":false},{"StartTime":2165.0,"Position":202.0,"HyperDash":false},{"StartTime":2217.0,"Position":500.0,"HyperDash":false},{"StartTime":2269.0,"Position":80.0,"HyperDash":false},{"StartTime":2321.0,"Position":399.0,"HyperDash":false},{"StartTime":2374.0,"Position":455.0,"HyperDash":false}]},{"StartTime":2707.0,"Objects":[{"StartTime":2707.0,"Position":368.0,"HyperDash":false},{"StartTime":2781.0,"Position":333.777771,"HyperDash":false},{"StartTime":2855.0,"Position":339.555542,"HyperDash":false},{"StartTime":2929.0,"Position":289.3333,"HyperDash":false},{"StartTime":3040.0,"Position":268.0,"HyperDash":false}]},{"StartTime":3207.0,"Objects":[{"StartTime":3207.0,"Position":288.0,"HyperDash":false},{"StartTime":3272.0,"Position":291.748444,"HyperDash":false},{"StartTime":3373.0,"Position":300.12677,"HyperDash":false}]},{"StartTime":3707.0,"Objects":[{"StartTime":3707.0,"Position":192.0,"HyperDash":false},{"StartTime":3790.0,"Position":154.075073,"HyperDash":false},{"StartTime":3873.0,"Position":136.150146,"HyperDash":false},{"StartTime":3956.0,"Position":109.225227,"HyperDash":false},{"StartTime":4040.0,"Position":92.0,"HyperDash":false},{"StartTime":4114.0,"Position":105.222221,"HyperDash":false},{"StartTime":4188.0,"Position":131.444443,"HyperDash":false},{"StartTime":4262.0,"Position":153.666656,"HyperDash":false},{"StartTime":4373.0,"Position":192.0,"HyperDash":false}]},{"StartTime":4707.0,"Objects":[{"StartTime":4707.0,"Position":288.0,"HyperDash":false}]},{"StartTime":5041.0,"Objects":[{"StartTime":5041.0,"Position":144.0,"HyperDash":false}]},{"StartTime":5374.0,"Objects":[{"StartTime":5374.0,"Position":304.0,"HyperDash":false},{"StartTime":5457.0,"Position":335.611359,"HyperDash":false},{"StartTime":5540.0,"Position":342.222717,"HyperDash":false},{"StartTime":5623.0,"Position":344.834076,"HyperDash":false},{"StartTime":5707.0,"Position":374.657623,"HyperDash":false},{"StartTime":5790.0,"Position":377.268982,"HyperDash":false},{"StartTime":5873.0,"Position":405.880341,"HyperDash":false},{"StartTime":5956.0,"Position":430.4917,"HyperDash":false},{"StartTime":6040.0,"Position":445.421326,"HyperDash":false},{"StartTime":6123.0,"Position":408.916077,"HyperDash":false},{"StartTime":6206.0,"Position":410.3047,"HyperDash":false},{"StartTime":6289.0,"Position":405.693359,"HyperDash":false},{"StartTime":6373.0,"Position":374.8698,"HyperDash":false},{"StartTime":6447.0,"Position":375.168121,"HyperDash":false},{"StartTime":6522.0,"Position":334.254242,"HyperDash":false},{"StartTime":6596.0,"Position":316.552521,"HyperDash":false},{"StartTime":6707.0,"Position":304.0,"HyperDash":false}]},{"StartTime":7041.0,"Objects":[{"StartTime":7041.0,"Position":208.0,"HyperDash":false}]},{"StartTime":7374.0,"Objects":[{"StartTime":7374.0,"Position":304.0,"HyperDash":false},{"StartTime":7448.0,"Position":293.1427,"HyperDash":false},{"StartTime":7522.0,"Position":328.2854,"HyperDash":false},{"StartTime":7596.0,"Position":323.4281,"HyperDash":false},{"StartTime":7707.0,"Position":318.142151,"HyperDash":false}]},{"StartTime":8041.0,"Objects":[{"StartTime":8041.0,"Position":160.0,"HyperDash":false},{"StartTime":8115.0,"Position":156.777771,"HyperDash":false},{"StartTime":8189.0,"Position":98.55556,"HyperDash":false},{"StartTime":8263.0,"Position":87.33333,"HyperDash":false},{"StartTime":8374.0,"Position":60.0,"HyperDash":false}]},{"StartTime":8541.0,"Objects":[{"StartTime":8541.0,"Position":176.0,"HyperDash":false}]},{"StartTime":8707.0,"Objects":[{"StartTime":8707.0,"Position":160.0,"HyperDash":false},{"StartTime":8790.0,"Position":189.827057,"HyperDash":false},{"StartTime":8873.0,"Position":214.480759,"HyperDash":false},{"StartTime":8956.0,"Position":199.348236,"HyperDash":false},{"StartTime":9040.0,"Position":211.43425,"HyperDash":false},{"StartTime":9114.0,"Position":182.741974,"HyperDash":false},{"StartTime":9188.0,"Position":188.031326,"HyperDash":false},{"StartTime":9262.0,"Position":150.1092,"HyperDash":false},{"StartTime":9373.0,"Position":131.819717,"HyperDash":false}]},{"StartTime":9707.0,"Objects":[{"StartTime":9707.0,"Position":320.0,"HyperDash":false}]},{"StartTime":10041.0,"Objects":[{"StartTime":10041.0,"Position":352.0,"HyperDash":false},{"StartTime":10115.0,"Position":335.777771,"HyperDash":false},{"StartTime":10189.0,"Position":320.555542,"HyperDash":false},{"StartTime":10263.0,"Position":275.3333,"HyperDash":false},{"StartTime":10374.0,"Position":252.0,"HyperDash":false}]},{"StartTime":10707.0,"Objects":[{"StartTime":10707.0,"Position":416.0,"HyperDash":false},{"StartTime":10790.0,"Position":433.640656,"HyperDash":false},{"StartTime":10873.0,"Position":472.2328,"HyperDash":false},{"StartTime":10956.0,"Position":486.15274,"HyperDash":false},{"StartTime":11040.0,"Position":482.899384,"HyperDash":false},{"StartTime":11114.0,"Position":477.456268,"HyperDash":false},{"StartTime":11188.0,"Position":474.261353,"HyperDash":false},{"StartTime":11262.0,"Position":444.9807,"HyperDash":false},{"StartTime":11373.0,"Position":418.860382,"HyperDash":false}]},{"StartTime":11874.0,"Objects":[{"StartTime":11874.0,"Position":224.0,"HyperDash":false}]},{"StartTime":12041.0,"Objects":[{"StartTime":12041.0,"Position":160.0,"HyperDash":false},{"StartTime":12124.0,"Position":129.476608,"HyperDash":false},{"StartTime":12207.0,"Position":139.62587,"HyperDash":false},{"StartTime":12290.0,"Position":110.133484,"HyperDash":false},{"StartTime":12374.0,"Position":120.566429,"HyperDash":false},{"StartTime":12439.0,"Position":147.187912,"HyperDash":false},{"StartTime":12540.0,"Position":159.8762,"HyperDash":false}]},{"StartTime":12707.0,"Objects":[{"StartTime":12707.0,"Position":288.0,"HyperDash":false}]},{"StartTime":13374.0,"Objects":[{"StartTime":13374.0,"Position":464.0,"HyperDash":false},{"StartTime":13457.0,"Position":423.1,"HyperDash":false},{"StartTime":13540.0,"Position":431.2,"HyperDash":false},{"StartTime":13623.0,"Position":392.3,"HyperDash":false},{"StartTime":13707.0,"Position":364.1,"HyperDash":false},{"StartTime":13772.0,"Position":352.6,"HyperDash":false},{"StartTime":13874.0,"Position":314.0,"HyperDash":false}]},{"StartTime":14041.0,"Objects":[{"StartTime":14041.0,"Position":240.0,"HyperDash":false},{"StartTime":14124.0,"Position":215.182037,"HyperDash":false},{"StartTime":14207.0,"Position":213.6612,"HyperDash":false},{"StartTime":14290.0,"Position":180.052521,"HyperDash":false},{"StartTime":14374.0,"Position":198.218033,"HyperDash":false},{"StartTime":14439.0,"Position":203.99968,"HyperDash":false},{"StartTime":14541.0,"Position":239.397186,"HyperDash":false}]},{"StartTime":14707.0,"Objects":[{"StartTime":14707.0,"Position":320.0,"HyperDash":false},{"StartTime":14781.0,"Position":279.777771,"HyperDash":false},{"StartTime":14855.0,"Position":271.555542,"HyperDash":false},{"StartTime":14929.0,"Position":258.3333,"HyperDash":false},{"StartTime":15040.0,"Position":220.0,"HyperDash":false}]},{"StartTime":15374.0,"Objects":[{"StartTime":15374.0,"Position":320.0,"HyperDash":false},{"StartTime":15448.0,"Position":329.8606,"HyperDash":false},{"StartTime":15522.0,"Position":335.721161,"HyperDash":false},{"StartTime":15596.0,"Position":362.581757,"HyperDash":false},{"StartTime":15707.0,"Position":359.87262,"HyperDash":false}]},{"StartTime":16041.0,"Objects":[{"StartTime":16041.0,"Position":192.0,"HyperDash":false},{"StartTime":16115.0,"Position":166.777771,"HyperDash":false},{"StartTime":16189.0,"Position":161.555557,"HyperDash":false},{"StartTime":16263.0,"Position":112.333328,"HyperDash":false},{"StartTime":16374.0,"Position":92.0,"HyperDash":false}]},{"StartTime":16541.0,"Objects":[{"StartTime":16541.0,"Position":208.0,"HyperDash":false}]},{"StartTime":16707.0,"Objects":[{"StartTime":16707.0,"Position":176.0,"HyperDash":false}]},{"StartTime":17041.0,"Objects":[{"StartTime":17041.0,"Position":336.0,"HyperDash":false}]},{"StartTime":17207.0,"Objects":[{"StartTime":17207.0,"Position":288.0,"HyperDash":false},{"StartTime":17290.0,"Position":262.868042,"HyperDash":false},{"StartTime":17373.0,"Position":233.396667,"HyperDash":false},{"StartTime":17456.0,"Position":240.435333,"HyperDash":false},{"StartTime":17540.0,"Position":242.216,"HyperDash":false},{"StartTime":17605.0,"Position":250.097885,"HyperDash":false},{"StartTime":17707.0,"Position":281.828644,"HyperDash":false}]},{"StartTime":18041.0,"Objects":[{"StartTime":18041.0,"Position":480.0,"HyperDash":false}]},{"StartTime":18374.0,"Objects":[{"StartTime":18374.0,"Position":256.0,"HyperDash":false}]},{"StartTime":18707.0,"Objects":[{"StartTime":18707.0,"Position":416.0,"HyperDash":false},{"StartTime":18790.0,"Position":398.254333,"HyperDash":false},{"StartTime":18873.0,"Position":424.508667,"HyperDash":false},{"StartTime":18956.0,"Position":427.763,"HyperDash":false},{"StartTime":19040.0,"Position":425.044525,"HyperDash":false},{"StartTime":19105.0,"Position":408.809967,"HyperDash":false},{"StartTime":19207.0,"Position":429.580353,"HyperDash":false}]},{"StartTime":19374.0,"Objects":[{"StartTime":19374.0,"Position":336.0,"HyperDash":false},{"StartTime":19448.0,"Position":294.777771,"HyperDash":false},{"StartTime":19522.0,"Position":280.555542,"HyperDash":false},{"StartTime":19596.0,"Position":278.3333,"HyperDash":false},{"StartTime":19707.0,"Position":236.0,"HyperDash":false}]},{"StartTime":20041.0,"Objects":[{"StartTime":20041.0,"Position":256.0,"HyperDash":false},{"StartTime":20124.0,"Position":272.817963,"HyperDash":false},{"StartTime":20207.0,"Position":313.3388,"HyperDash":false},{"StartTime":20290.0,"Position":317.947479,"HyperDash":false},{"StartTime":20374.0,"Position":297.781982,"HyperDash":false},{"StartTime":20439.0,"Position":266.000336,"HyperDash":false},{"StartTime":20541.0,"Position":256.6028,"HyperDash":false}]},{"StartTime":20707.0,"Objects":[{"StartTime":20707.0,"Position":196.0,"HyperDash":false},{"StartTime":20781.0,"Position":169.13942,"HyperDash":false},{"StartTime":20855.0,"Position":192.278839,"HyperDash":false},{"StartTime":20929.0,"Position":170.418243,"HyperDash":false},{"StartTime":21040.0,"Position":156.12738,"HyperDash":false}]},{"StartTime":21374.0,"Objects":[{"StartTime":21374.0,"Position":320.0,"HyperDash":false},{"StartTime":21457.0,"Position":344.0784,"HyperDash":false},{"StartTime":21540.0,"Position":350.913055,"HyperDash":false},{"StartTime":21623.0,"Position":346.822418,"HyperDash":false},{"StartTime":21707.0,"Position":357.019379,"HyperDash":false},{"StartTime":21772.0,"Position":358.883179,"HyperDash":false},{"StartTime":21873.0,"Position":327.8019,"HyperDash":false}]},{"StartTime":22041.0,"Objects":[{"StartTime":22041.0,"Position":224.0,"HyperDash":false},{"StartTime":22115.0,"Position":183.777771,"HyperDash":false},{"StartTime":22189.0,"Position":175.555557,"HyperDash":false},{"StartTime":22263.0,"Position":140.333328,"HyperDash":false},{"StartTime":22374.0,"Position":124.0,"HyperDash":false}]},{"StartTime":22541.0,"Objects":[{"StartTime":22541.0,"Position":272.0,"HyperDash":false}]},{"StartTime":22707.0,"Objects":[{"StartTime":22707.0,"Position":204.0,"HyperDash":false}]},{"StartTime":23041.0,"Objects":[{"StartTime":23041.0,"Position":96.0,"HyperDash":false}]},{"StartTime":23374.0,"Objects":[{"StartTime":23374.0,"Position":208.0,"HyperDash":false},{"StartTime":23448.0,"Position":222.1427,"HyperDash":false},{"StartTime":23522.0,"Position":195.2854,"HyperDash":false},{"StartTime":23596.0,"Position":234.428085,"HyperDash":false},{"StartTime":23707.0,"Position":222.142136,"HyperDash":false}]},{"StartTime":24041.0,"Objects":[{"StartTime":24041.0,"Position":80.0,"HyperDash":false},{"StartTime":24124.0,"Position":113.9,"HyperDash":false},{"StartTime":24207.0,"Position":129.8,"HyperDash":false},{"StartTime":24290.0,"Position":153.7,"HyperDash":false},{"StartTime":24374.0,"Position":179.9,"HyperDash":false},{"StartTime":24439.0,"Position":201.4,"HyperDash":false},{"StartTime":24541.0,"Position":230.0,"HyperDash":false}]},{"StartTime":24707.0,"Objects":[{"StartTime":24707.0,"Position":112.0,"HyperDash":false}]},{"StartTime":25041.0,"Objects":[{"StartTime":25041.0,"Position":256.0,"HyperDash":false},{"StartTime":25106.0,"Position":250.808792,"HyperDash":false},{"StartTime":25207.0,"Position":240.188614,"HyperDash":false}]},{"StartTime":25541.0,"Objects":[{"StartTime":25541.0,"Position":352.0,"HyperDash":false},{"StartTime":25606.0,"Position":340.5016,"HyperDash":false},{"StartTime":25707.0,"Position":355.834839,"HyperDash":false}]},{"StartTime":26041.0,"Objects":[{"StartTime":26041.0,"Position":192.0,"HyperDash":false},{"StartTime":26115.0,"Position":191.8573,"HyperDash":false},{"StartTime":26189.0,"Position":173.7146,"HyperDash":false},{"StartTime":26263.0,"Position":175.571915,"HyperDash":false},{"StartTime":26374.0,"Position":177.857864,"HyperDash":false}]},{"StartTime":26707.0,"Objects":[{"StartTime":26707.0,"Position":272.0,"HyperDash":false},{"StartTime":26781.0,"Position":275.222229,"HyperDash":false},{"StartTime":26855.0,"Position":318.444458,"HyperDash":false},{"StartTime":26929.0,"Position":333.6667,"HyperDash":false},{"StartTime":27040.0,"Position":372.0,"HyperDash":false}]},{"StartTime":27207.0,"Objects":[{"StartTime":27207.0,"Position":256.0,"HyperDash":false}]},{"StartTime":27374.0,"Objects":[{"StartTime":27374.0,"Position":288.0,"HyperDash":false}]},{"StartTime":27707.0,"Objects":[{"StartTime":27707.0,"Position":416.0,"HyperDash":false},{"StartTime":27772.0,"Position":401.748444,"HyperDash":false},{"StartTime":27873.0,"Position":428.12677,"HyperDash":false}]},{"StartTime":28207.0,"Objects":[{"StartTime":28207.0,"Position":288.0,"HyperDash":false},{"StartTime":28281.0,"Position":250.777771,"HyperDash":false},{"StartTime":28355.0,"Position":249.555557,"HyperDash":false},{"StartTime":28429.0,"Position":219.333328,"HyperDash":false},{"StartTime":28540.0,"Position":188.0,"HyperDash":false}]},{"StartTime":28707.0,"Objects":[{"StartTime":28707.0,"Position":256.0,"HyperDash":false},{"StartTime":28781.0,"Position":253.111572,"HyperDash":false},{"StartTime":28855.0,"Position":249.223145,"HyperDash":false},{"StartTime":28929.0,"Position":256.334747,"HyperDash":false},{"StartTime":29040.0,"Position":270.0021,"HyperDash":false}]},{"StartTime":29374.0,"Objects":[{"StartTime":29374.0,"Position":128.0,"HyperDash":false},{"StartTime":29457.0,"Position":97.70407,"HyperDash":false},{"StartTime":29540.0,"Position":72.07541,"HyperDash":false},{"StartTime":29623.0,"Position":69.19281,"HyperDash":false},{"StartTime":29707.0,"Position":64.12629,"HyperDash":false},{"StartTime":29781.0,"Position":68.7450943,"HyperDash":false},{"StartTime":29855.0,"Position":93.5549545,"HyperDash":false},{"StartTime":29929.0,"Position":84.38264,"HyperDash":false},{"StartTime":30040.0,"Position":127.072174,"HyperDash":false}]},{"StartTime":30374.0,"Objects":[{"StartTime":30374.0,"Position":320.0,"HyperDash":false}]},{"StartTime":30707.0,"Objects":[{"StartTime":30707.0,"Position":416.0,"HyperDash":false}]},{"StartTime":30874.0,"Objects":[{"StartTime":30874.0,"Position":432.0,"HyperDash":false},{"StartTime":30948.0,"Position":429.1427,"HyperDash":false},{"StartTime":31022.0,"Position":455.2854,"HyperDash":false},{"StartTime":31096.0,"Position":422.4281,"HyperDash":false},{"StartTime":31207.0,"Position":446.142151,"HyperDash":false}]},{"StartTime":31374.0,"Objects":[{"StartTime":31374.0,"Position":320.0,"HyperDash":false}]},{"StartTime":31707.0,"Objects":[{"StartTime":31707.0,"Position":240.0,"HyperDash":false},{"StartTime":31772.0,"Position":250.251556,"HyperDash":false},{"StartTime":31873.0,"Position":227.873215,"HyperDash":false}]},{"StartTime":32041.0,"Objects":[{"StartTime":32041.0,"Position":304.0,"HyperDash":false},{"StartTime":32124.0,"Position":346.659271,"HyperDash":false},{"StartTime":32207.0,"Position":333.21402,"HyperDash":false},{"StartTime":32290.0,"Position":348.822571,"HyperDash":false},{"StartTime":32374.0,"Position":369.8608,"HyperDash":false},{"StartTime":32448.0,"Position":377.38208,"HyperDash":false},{"StartTime":32522.0,"Position":368.24884,"HyperDash":false},{"StartTime":32596.0,"Position":327.2163,"HyperDash":false},{"StartTime":32707.0,"Position":302.6493,"HyperDash":false}]},{"StartTime":33041.0,"Objects":[{"StartTime":33041.0,"Position":32.0,"HyperDash":false}]},{"StartTime":33374.0,"Objects":[{"StartTime":33374.0,"Position":304.0,"HyperDash":false}]},{"StartTime":33541.0,"Objects":[{"StartTime":33541.0,"Position":368.0,"HyperDash":false},{"StartTime":33615.0,"Position":362.176758,"HyperDash":false},{"StartTime":33689.0,"Position":375.77478,"HyperDash":false},{"StartTime":33763.0,"Position":391.632355,"HyperDash":false},{"StartTime":33874.0,"Position":367.9668,"HyperDash":false}]},{"StartTime":34207.0,"Objects":[{"StartTime":34207.0,"Position":80.0,"HyperDash":false}]},{"StartTime":34374.0,"Objects":[{"StartTime":34374.0,"Position":176.0,"HyperDash":false}]},{"StartTime":34541.0,"Objects":[{"StartTime":34541.0,"Position":80.0,"HyperDash":false}]},{"StartTime":34707.0,"Objects":[{"StartTime":34707.0,"Position":200.0,"HyperDash":false}]},{"StartTime":35041.0,"Objects":[{"StartTime":35041.0,"Position":336.0,"HyperDash":false},{"StartTime":35115.0,"Position":338.1427,"HyperDash":false},{"StartTime":35189.0,"Position":342.2854,"HyperDash":false},{"StartTime":35263.0,"Position":338.4281,"HyperDash":false},{"StartTime":35374.0,"Position":350.142151,"HyperDash":false}]},{"StartTime":35707.0,"Objects":[{"StartTime":35707.0,"Position":208.0,"HyperDash":false},{"StartTime":35772.0,"Position":217.808792,"HyperDash":false},{"StartTime":35873.0,"Position":192.188614,"HyperDash":false}]},{"StartTime":36207.0,"Objects":[{"StartTime":36207.0,"Position":336.0,"HyperDash":false},{"StartTime":36272.0,"Position":351.191223,"HyperDash":false},{"StartTime":36373.0,"Position":351.8114,"HyperDash":false}]},{"StartTime":36707.0,"Objects":[{"StartTime":36707.0,"Position":208.0,"HyperDash":false},{"StartTime":36781.0,"Position":178.777771,"HyperDash":false},{"StartTime":36855.0,"Position":179.555557,"HyperDash":false},{"StartTime":36929.0,"Position":125.333328,"HyperDash":false},{"StartTime":37040.0,"Position":108.0,"HyperDash":false}]},{"StartTime":37374.0,"Objects":[{"StartTime":37374.0,"Position":416.0,"HyperDash":false}]},{"StartTime":37541.0,"Objects":[{"StartTime":37541.0,"Position":320.0,"HyperDash":false},{"StartTime":37615.0,"Position":322.379059,"HyperDash":false},{"StartTime":37689.0,"Position":309.7581,"HyperDash":false},{"StartTime":37763.0,"Position":318.137146,"HyperDash":false},{"StartTime":37874.0,"Position":335.205719,"HyperDash":false}]},{"StartTime":38041.0,"Objects":[{"StartTime":38041.0,"Position":208.0,"HyperDash":false}]},{"StartTime":38374.0,"Objects":[{"StartTime":38374.0,"Position":416.0,"HyperDash":false},{"StartTime":38439.0,"Position":410.191223,"HyperDash":false},{"StartTime":38540.0,"Position":431.8114,"HyperDash":false}]},{"StartTime":38874.0,"Objects":[{"StartTime":38874.0,"Position":288.0,"HyperDash":false},{"StartTime":38939.0,"Position":273.808777,"HyperDash":false},{"StartTime":39040.0,"Position":272.1886,"HyperDash":false}]},{"StartTime":39207.0,"Objects":[{"StartTime":39207.0,"Position":336.0,"HyperDash":false},{"StartTime":39281.0,"Position":360.222229,"HyperDash":false},{"StartTime":39355.0,"Position":369.444458,"HyperDash":false},{"StartTime":39429.0,"Position":419.6667,"HyperDash":false},{"StartTime":39540.0,"Position":436.0,"HyperDash":false}]},{"StartTime":39707.0,"Objects":[{"StartTime":39707.0,"Position":320.0,"HyperDash":false}]},{"StartTime":40041.0,"Objects":[{"StartTime":40041.0,"Position":160.0,"HyperDash":false}]},{"StartTime":40374.0,"Objects":[{"StartTime":40374.0,"Position":384.0,"HyperDash":false},{"StartTime":40448.0,"Position":396.1427,"HyperDash":false},{"StartTime":40522.0,"Position":380.2854,"HyperDash":false},{"StartTime":40596.0,"Position":408.4281,"HyperDash":false},{"StartTime":40707.0,"Position":398.142151,"HyperDash":false}]},{"StartTime":41041.0,"Objects":[{"StartTime":41041.0,"Position":112.0,"HyperDash":false}]},{"StartTime":41374.0,"Objects":[{"StartTime":41374.0,"Position":132.0,"HyperDash":false}]},{"StartTime":41541.0,"Objects":[{"StartTime":41541.0,"Position":48.0,"HyperDash":false},{"StartTime":41615.0,"Position":31.8573036,"HyperDash":false},{"StartTime":41689.0,"Position":31.7146072,"HyperDash":false},{"StartTime":41763.0,"Position":40.571907,"HyperDash":false},{"StartTime":41874.0,"Position":33.8578644,"HyperDash":false}]},{"StartTime":42041.0,"Objects":[{"StartTime":42041.0,"Position":160.0,"HyperDash":false}]},{"StartTime":42374.0,"Objects":[{"StartTime":42374.0,"Position":320.0,"HyperDash":false}]},{"StartTime":42707.0,"Objects":[{"StartTime":42707.0,"Position":96.0,"HyperDash":false},{"StartTime":42790.0,"Position":64.13681,"HyperDash":false},{"StartTime":42873.0,"Position":72.60394,"HyperDash":false},{"StartTime":42956.0,"Position":54.03947,"HyperDash":false},{"StartTime":43040.0,"Position":54.30264,"HyperDash":false},{"StartTime":43105.0,"Position":72.19569,"HyperDash":false},{"StartTime":43206.0,"Position":95.39718,"HyperDash":false}]},{"StartTime":43374.0,"Objects":[{"StartTime":43374.0,"Position":224.0,"HyperDash":false}]},{"StartTime":43707.0,"Objects":[{"StartTime":43707.0,"Position":352.0,"HyperDash":false}]},{"StartTime":44040.0,"Objects":[{"StartTime":44040.0,"Position":160.0,"HyperDash":false}]},{"StartTime":44374.0,"Objects":[{"StartTime":44374.0,"Position":304.0,"HyperDash":false},{"StartTime":44457.0,"Position":309.591553,"HyperDash":false},{"StartTime":44540.0,"Position":325.605743,"HyperDash":false},{"StartTime":44623.0,"Position":365.538,"HyperDash":false},{"StartTime":44707.0,"Position":357.421478,"HyperDash":false},{"StartTime":44781.0,"Position":336.6104,"HyperDash":false},{"StartTime":44855.0,"Position":350.104462,"HyperDash":false},{"StartTime":44929.0,"Position":333.432159,"HyperDash":false},{"StartTime":45040.0,"Position":304.669952,"HyperDash":false}]},{"StartTime":45374.0,"Objects":[{"StartTime":45374.0,"Position":136.0,"HyperDash":false},{"StartTime":45457.0,"Position":127.769325,"HyperDash":false},{"StartTime":45540.0,"Position":88.53865,"HyperDash":false},{"StartTime":45623.0,"Position":83.30798,"HyperDash":false},{"StartTime":45707.0,"Position":70.88176,"HyperDash":false},{"StartTime":45790.0,"Position":61.6510925,"HyperDash":false},{"StartTime":45873.0,"Position":38.3226547,"HyperDash":false},{"StartTime":45956.0,"Position":42.4555435,"HyperDash":false},{"StartTime":46040.0,"Position":70.88177,"HyperDash":false},{"StartTime":46114.0,"Position":83.35248,"HyperDash":false},{"StartTime":46188.0,"Position":98.8232,"HyperDash":false},{"StartTime":46262.0,"Position":107.293922,"HyperDash":false},{"StartTime":46373.0,"Position":136.0,"HyperDash":false}]},{"StartTime":46874.0,"Objects":[{"StartTime":46874.0,"Position":368.0,"HyperDash":false},{"StartTime":46957.0,"Position":368.641052,"HyperDash":false},{"StartTime":47040.0,"Position":388.2821,"HyperDash":false},{"StartTime":47123.0,"Position":392.923126,"HyperDash":false},{"StartTime":47207.0,"Position":378.596,"HyperDash":false},{"StartTime":47272.0,"Position":362.664276,"HyperDash":false},{"StartTime":47374.0,"Position":383.9099,"HyperDash":false}]},{"StartTime":47707.0,"Objects":[{"StartTime":47707.0,"Position":160.0,"HyperDash":false}]},{"StartTime":48041.0,"Objects":[{"StartTime":48041.0,"Position":144.0,"HyperDash":false},{"StartTime":48124.0,"Position":140.536209,"HyperDash":false},{"StartTime":48207.0,"Position":120.072418,"HyperDash":false},{"StartTime":48290.0,"Position":77.60862,"HyperDash":false},{"StartTime":48374.0,"Position":81.95851,"HyperDash":false},{"StartTime":48457.0,"Position":51.4947128,"HyperDash":false},{"StartTime":48541.0,"Position":50.8446045,"HyperDash":false},{"StartTime":48624.0,"Position":47.308403,"HyperDash":false},{"StartTime":48707.0,"Position":81.7722,"HyperDash":false},{"StartTime":48781.0,"Position":97.5592041,"HyperDash":false},{"StartTime":48856.0,"Position":126.5325,"HyperDash":false},{"StartTime":48930.0,"Position":134.3195,"HyperDash":false},{"StartTime":49041.0,"Position":144.0,"HyperDash":false}]},{"StartTime":49374.0,"Objects":[{"StartTime":49374.0,"Position":256.0,"HyperDash":false},{"StartTime":49457.0,"Position":275.705048,"HyperDash":false},{"StartTime":49540.0,"Position":297.414978,"HyperDash":false},{"StartTime":49623.0,"Position":295.170868,"HyperDash":false},{"StartTime":49707.0,"Position":311.122,"HyperDash":false},{"StartTime":49790.0,"Position":299.525726,"HyperDash":false},{"StartTime":49873.0,"Position":296.3256,"HyperDash":false},{"StartTime":49956.0,"Position":290.4679,"HyperDash":false},{"StartTime":50040.0,"Position":301.014038,"HyperDash":false},{"StartTime":50114.0,"Position":289.537323,"HyperDash":false},{"StartTime":50189.0,"Position":285.4608,"HyperDash":false},{"StartTime":50263.0,"Position":241.873749,"HyperDash":false},{"StartTime":50373.0,"Position":235.304214,"HyperDash":false}]},{"StartTime":50707.0,"Objects":[{"StartTime":50707.0,"Position":384.0,"HyperDash":false},{"StartTime":50790.0,"Position":399.712433,"HyperDash":false},{"StartTime":50873.0,"Position":415.424866,"HyperDash":false},{"StartTime":50956.0,"Position":442.137268,"HyperDash":false},{"StartTime":51040.0,"Position":459.075165,"HyperDash":false},{"StartTime":51105.0,"Position":484.729462,"HyperDash":false},{"StartTime":51206.0,"Position":496.5,"HyperDash":false}]},{"StartTime":51374.0,"Objects":[{"StartTime":51374.0,"Position":400.0,"HyperDash":false}]},{"StartTime":51874.0,"Objects":[{"StartTime":51874.0,"Position":244.0,"HyperDash":false},{"StartTime":51957.0,"Position":220.5127,"HyperDash":false},{"StartTime":52040.0,"Position":194.025391,"HyperDash":false},{"StartTime":52123.0,"Position":197.538086,"HyperDash":false},{"StartTime":52207.0,"Position":169.828033,"HyperDash":false},{"StartTime":52272.0,"Position":151.350021,"HyperDash":false},{"StartTime":52374.0,"Position":132.630676,"HyperDash":false}]},{"StartTime":52707.0,"Objects":[{"StartTime":52707.0,"Position":208.0,"HyperDash":false},{"StartTime":52781.0,"Position":217.666672,"HyperDash":false},{"StartTime":52855.0,"Position":252.333344,"HyperDash":false},{"StartTime":52929.0,"Position":248.0,"HyperDash":false},{"StartTime":53040.0,"Position":283.0,"HyperDash":false}]},{"StartTime":53373.0,"Objects":[{"StartTime":53373.0,"Position":368.0,"HyperDash":false},{"StartTime":53447.0,"Position":389.547058,"HyperDash":false},{"StartTime":53521.0,"Position":360.0941,"HyperDash":false},{"StartTime":53595.0,"Position":373.641144,"HyperDash":false},{"StartTime":53706.0,"Position":379.4617,"HyperDash":false}]},{"StartTime":54040.0,"Objects":[{"StartTime":54040.0,"Position":255.0,"HyperDash":false},{"StartTime":54114.0,"Position":252.333328,"HyperDash":false},{"StartTime":54188.0,"Position":226.666656,"HyperDash":false},{"StartTime":54262.0,"Position":195.0,"HyperDash":false},{"StartTime":54373.0,"Position":180.0,"HyperDash":false}]},{"StartTime":54707.0,"Objects":[{"StartTime":54707.0,"Position":368.0,"HyperDash":false}]},{"StartTime":55374.0,"Objects":[{"StartTime":55374.0,"Position":160.0,"HyperDash":false},{"StartTime":55448.0,"Position":163.26001,"HyperDash":false},{"StartTime":55522.0,"Position":156.520035,"HyperDash":false},{"StartTime":55596.0,"Position":132.780045,"HyperDash":false},{"StartTime":55707.0,"Position":147.670074,"HyperDash":false}]},{"StartTime":56041.0,"Objects":[{"StartTime":56041.0,"Position":320.0,"HyperDash":false},{"StartTime":56115.0,"Position":345.222229,"HyperDash":false},{"StartTime":56189.0,"Position":369.444458,"HyperDash":false},{"StartTime":56263.0,"Position":402.6667,"HyperDash":false},{"StartTime":56374.0,"Position":420.0,"HyperDash":false}]},{"StartTime":56707.0,"Objects":[{"StartTime":56707.0,"Position":256.0,"HyperDash":false},{"StartTime":56781.0,"Position":245.8573,"HyperDash":false},{"StartTime":56855.0,"Position":239.7146,"HyperDash":false},{"StartTime":56929.0,"Position":253.571915,"HyperDash":false},{"StartTime":57040.0,"Position":241.857864,"HyperDash":false}]},{"StartTime":57207.0,"Objects":[{"StartTime":57207.0,"Position":328.0,"HyperDash":false},{"StartTime":57290.0,"Position":334.131958,"HyperDash":false},{"StartTime":57373.0,"Position":357.603333,"HyperDash":false},{"StartTime":57456.0,"Position":386.564667,"HyperDash":false},{"StartTime":57540.0,"Position":373.784027,"HyperDash":false},{"StartTime":57605.0,"Position":370.9021,"HyperDash":false},{"StartTime":57707.0,"Position":334.171356,"HyperDash":false}]},{"StartTime":58041.0,"Objects":[{"StartTime":58041.0,"Position":176.0,"HyperDash":false},{"StartTime":58115.0,"Position":153.777771,"HyperDash":false},{"StartTime":58189.0,"Position":113.555557,"HyperDash":false},{"StartTime":58263.0,"Position":124.333328,"HyperDash":false},{"StartTime":58374.0,"Position":76.0,"HyperDash":false}]},{"StartTime":58707.0,"Objects":[{"StartTime":58707.0,"Position":208.0,"HyperDash":false},{"StartTime":58790.0,"Position":235.924927,"HyperDash":false},{"StartTime":58873.0,"Position":258.849854,"HyperDash":false},{"StartTime":58956.0,"Position":280.77478,"HyperDash":false},{"StartTime":59040.0,"Position":308.0,"HyperDash":false},{"StartTime":59114.0,"Position":302.777771,"HyperDash":false},{"StartTime":59188.0,"Position":275.555542,"HyperDash":false},{"StartTime":59262.0,"Position":249.333344,"HyperDash":false},{"StartTime":59373.0,"Position":208.0,"HyperDash":false}]},{"StartTime":59707.0,"Objects":[{"StartTime":59707.0,"Position":64.0,"HyperDash":false}]},{"StartTime":59874.0,"Objects":[{"StartTime":59874.0,"Position":128.0,"HyperDash":false},{"StartTime":59948.0,"Position":144.1427,"HyperDash":false},{"StartTime":60022.0,"Position":119.2854,"HyperDash":false},{"StartTime":60096.0,"Position":142.428085,"HyperDash":false},{"StartTime":60207.0,"Position":142.142136,"HyperDash":false}]},{"StartTime":60374.0,"Objects":[{"StartTime":60374.0,"Position":80.0,"HyperDash":false},{"StartTime":60457.0,"Position":73.37541,"HyperDash":false},{"StartTime":60540.0,"Position":27.7508316,"HyperDash":false},{"StartTime":60623.0,"Position":45.1262474,"HyperDash":false},{"StartTime":60707.0,"Position":9.28933,"HyperDash":false},{"StartTime":60781.0,"Position":31.0028038,"HyperDash":false},{"StartTime":60855.0,"Position":29.71629,"HyperDash":false},{"StartTime":60929.0,"Position":68.42977,"HyperDash":false},{"StartTime":61040.0,"Position":80.0,"HyperDash":false}]},{"StartTime":61374.0,"Objects":[{"StartTime":61374.0,"Position":224.0,"HyperDash":false},{"StartTime":61457.0,"Position":240.728989,"HyperDash":false},{"StartTime":61540.0,"Position":281.499359,"HyperDash":false},{"StartTime":61623.0,"Position":285.145782,"HyperDash":false},{"StartTime":61707.0,"Position":295.647522,"HyperDash":false},{"StartTime":61781.0,"Position":273.401184,"HyperDash":false},{"StartTime":61855.0,"Position":266.0076,"HyperDash":false},{"StartTime":61929.0,"Position":282.02597,"HyperDash":false},{"StartTime":62040.0,"Position":233.9523,"HyperDash":false}]},{"StartTime":62374.0,"Objects":[{"StartTime":62374.0,"Position":96.0,"HyperDash":false}]},{"StartTime":62541.0,"Objects":[{"StartTime":62541.0,"Position":32.0,"HyperDash":false},{"StartTime":62615.0,"Position":2.15351486,"HyperDash":false},{"StartTime":62689.0,"Position":34.9851379,"HyperDash":false},{"StartTime":62763.0,"Position":0.0,"HyperDash":false},{"StartTime":62874.0,"Position":30.1482067,"HyperDash":false}]},{"StartTime":63041.0,"Objects":[{"StartTime":63041.0,"Position":92.0,"HyperDash":false},{"StartTime":63115.0,"Position":114.222221,"HyperDash":false},{"StartTime":63189.0,"Position":131.444443,"HyperDash":false},{"StartTime":63263.0,"Position":144.666672,"HyperDash":false},{"StartTime":63374.0,"Position":192.0,"HyperDash":false}]},{"StartTime":63707.0,"Objects":[{"StartTime":63707.0,"Position":468.0,"HyperDash":false}]},{"StartTime":64041.0,"Objects":[{"StartTime":64041.0,"Position":192.0,"HyperDash":false},{"StartTime":64124.0,"Position":153.075073,"HyperDash":false},{"StartTime":64207.0,"Position":159.150146,"HyperDash":false},{"StartTime":64290.0,"Position":101.225227,"HyperDash":false},{"StartTime":64374.0,"Position":92.0,"HyperDash":false},{"StartTime":64448.0,"Position":132.222229,"HyperDash":false},{"StartTime":64522.0,"Position":126.444443,"HyperDash":false},{"StartTime":64596.0,"Position":160.666656,"HyperDash":false},{"StartTime":64707.0,"Position":192.0,"HyperDash":false}]},{"StartTime":65041.0,"Objects":[{"StartTime":65041.0,"Position":336.0,"HyperDash":false},{"StartTime":65124.0,"Position":375.268738,"HyperDash":false},{"StartTime":65207.0,"Position":395.320221,"HyperDash":false},{"StartTime":65290.0,"Position":394.972534,"HyperDash":false},{"StartTime":65374.0,"Position":395.778046,"HyperDash":false},{"StartTime":65448.0,"Position":382.9742,"HyperDash":false},{"StartTime":65522.0,"Position":392.609863,"HyperDash":false},{"StartTime":65596.0,"Position":364.706543,"HyperDash":false},{"StartTime":65707.0,"Position":339.031464,"HyperDash":false}]},{"StartTime":66041.0,"Objects":[{"StartTime":66041.0,"Position":208.0,"HyperDash":false},{"StartTime":66115.0,"Position":218.222229,"HyperDash":false},{"StartTime":66189.0,"Position":241.444443,"HyperDash":false},{"StartTime":66263.0,"Position":289.6667,"HyperDash":false},{"StartTime":66374.0,"Position":308.0,"HyperDash":false}]},{"StartTime":66707.0,"Objects":[{"StartTime":66707.0,"Position":144.0,"HyperDash":false},{"StartTime":66781.0,"Position":125.777779,"HyperDash":false},{"StartTime":66855.0,"Position":106.555557,"HyperDash":false},{"StartTime":66929.0,"Position":90.33333,"HyperDash":false},{"StartTime":67040.0,"Position":44.0,"HyperDash":false}]},{"StartTime":67373.0,"Objects":[{"StartTime":67373.0,"Position":192.0,"HyperDash":false},{"StartTime":67447.0,"Position":186.1427,"HyperDash":false},{"StartTime":67521.0,"Position":209.2854,"HyperDash":false},{"StartTime":67595.0,"Position":193.428085,"HyperDash":false},{"StartTime":67706.0,"Position":206.142136,"HyperDash":false}]},{"StartTime":67874.0,"Objects":[{"StartTime":67874.0,"Position":120.0,"HyperDash":false},{"StartTime":67957.0,"Position":88.82533,"HyperDash":false},{"StartTime":68040.0,"Position":85.3476257,"HyperDash":false},{"StartTime":68123.0,"Position":65.43532,"HyperDash":false},{"StartTime":68207.0,"Position":74.31434,"HyperDash":false},{"StartTime":68272.0,"Position":73.27857,"HyperDash":false},{"StartTime":68373.0,"Position":113.828613,"HyperDash":false}]},{"StartTime":68707.0,"Objects":[{"StartTime":68707.0,"Position":272.0,"HyperDash":false},{"StartTime":68781.0,"Position":296.222229,"HyperDash":false},{"StartTime":68855.0,"Position":335.444458,"HyperDash":false},{"StartTime":68929.0,"Position":338.6667,"HyperDash":false},{"StartTime":69040.0,"Position":372.0,"HyperDash":false}]},{"StartTime":69374.0,"Objects":[{"StartTime":69374.0,"Position":237.0,"HyperDash":false},{"StartTime":69457.0,"Position":218.076126,"HyperDash":false},{"StartTime":69540.0,"Position":170.152252,"HyperDash":false},{"StartTime":69623.0,"Position":155.228363,"HyperDash":false},{"StartTime":69707.0,"Position":137.004211,"HyperDash":false},{"StartTime":69781.0,"Position":167.2255,"HyperDash":false},{"StartTime":69855.0,"Position":161.446777,"HyperDash":false},{"StartTime":69929.0,"Position":188.66806,"HyperDash":false},{"StartTime":70040.0,"Position":237.0,"HyperDash":false}]},{"StartTime":70373.0,"Objects":[{"StartTime":70373.0,"Position":384.0,"HyperDash":false}]},{"StartTime":70540.0,"Objects":[{"StartTime":70540.0,"Position":448.0,"HyperDash":false},{"StartTime":70614.0,"Position":454.1427,"HyperDash":false},{"StartTime":70688.0,"Position":466.2854,"HyperDash":false},{"StartTime":70762.0,"Position":467.4281,"HyperDash":false},{"StartTime":70873.0,"Position":462.142151,"HyperDash":false}]},{"StartTime":71040.0,"Objects":[{"StartTime":71040.0,"Position":400.0,"HyperDash":false},{"StartTime":71123.0,"Position":381.075073,"HyperDash":false},{"StartTime":71206.0,"Position":345.150146,"HyperDash":false},{"StartTime":71289.0,"Position":316.22522,"HyperDash":false},{"StartTime":71373.0,"Position":300.0,"HyperDash":false},{"StartTime":71447.0,"Position":336.222229,"HyperDash":false},{"StartTime":71521.0,"Position":347.444458,"HyperDash":false},{"StartTime":71595.0,"Position":384.666656,"HyperDash":false},{"StartTime":71706.0,"Position":400.0,"HyperDash":false}]},{"StartTime":72040.0,"Objects":[{"StartTime":72040.0,"Position":256.0,"HyperDash":false},{"StartTime":72123.0,"Position":241.4447,"HyperDash":false},{"StartTime":72206.0,"Position":212.00444,"HyperDash":false},{"StartTime":72289.0,"Position":222.925644,"HyperDash":false},{"StartTime":72373.0,"Position":198.3011,"HyperDash":false},{"StartTime":72447.0,"Position":185.363647,"HyperDash":false},{"StartTime":72521.0,"Position":217.711319,"HyperDash":false},{"StartTime":72595.0,"Position":229.9505,"HyperDash":false},{"StartTime":72706.0,"Position":232.077591,"HyperDash":false}]},{"StartTime":73040.0,"Objects":[{"StartTime":73040.0,"Position":400.0,"HyperDash":false}]},{"StartTime":73207.0,"Objects":[{"StartTime":73207.0,"Position":472.0,"HyperDash":false},{"StartTime":73281.0,"Position":462.583252,"HyperDash":false},{"StartTime":73355.0,"Position":487.166534,"HyperDash":false},{"StartTime":73429.0,"Position":462.7498,"HyperDash":false},{"StartTime":73540.0,"Position":479.1247,"HyperDash":false}]},{"StartTime":73707.0,"Objects":[{"StartTime":73707.0,"Position":416.0,"HyperDash":false},{"StartTime":73781.0,"Position":409.777771,"HyperDash":false},{"StartTime":73855.0,"Position":383.555542,"HyperDash":false},{"StartTime":73929.0,"Position":337.3333,"HyperDash":false},{"StartTime":74040.0,"Position":316.0,"HyperDash":false}]},{"StartTime":74373.0,"Objects":[{"StartTime":74373.0,"Position":36.0,"HyperDash":false}]},{"StartTime":74707.0,"Objects":[{"StartTime":74707.0,"Position":304.0,"HyperDash":false},{"StartTime":74790.0,"Position":334.939941,"HyperDash":false},{"StartTime":74873.0,"Position":359.879883,"HyperDash":false},{"StartTime":74956.0,"Position":382.819824,"HyperDash":false},{"StartTime":75040.0,"Position":384.0,"HyperDash":false},{"StartTime":75114.0,"Position":376.222229,"HyperDash":false},{"StartTime":75188.0,"Position":347.444458,"HyperDash":false},{"StartTime":75262.0,"Position":313.666656,"HyperDash":false},{"StartTime":75373.0,"Position":304.0,"HyperDash":false}]},{"StartTime":75707.0,"Objects":[{"StartTime":75707.0,"Position":160.0,"HyperDash":false},{"StartTime":75790.0,"Position":138.731277,"HyperDash":false},{"StartTime":75873.0,"Position":112.679764,"HyperDash":false},{"StartTime":75956.0,"Position":91.02745,"HyperDash":false},{"StartTime":76040.0,"Position":100.221947,"HyperDash":false},{"StartTime":76114.0,"Position":110.025749,"HyperDash":false},{"StartTime":76188.0,"Position":126.390106,"HyperDash":false},{"StartTime":76262.0,"Position":120.293419,"HyperDash":false},{"StartTime":76373.0,"Position":156.968521,"HyperDash":false}]},{"StartTime":76707.0,"Objects":[{"StartTime":76707.0,"Position":304.0,"HyperDash":false},{"StartTime":76781.0,"Position":341.222229,"HyperDash":false},{"StartTime":76855.0,"Position":337.444458,"HyperDash":false},{"StartTime":76929.0,"Position":358.6667,"HyperDash":false},{"StartTime":77040.0,"Position":404.0,"HyperDash":false}]},{"StartTime":77374.0,"Objects":[{"StartTime":77374.0,"Position":8.0,"HyperDash":false}]},{"StartTime":77707.0,"Objects":[{"StartTime":77707.0,"Position":450.0,"HyperDash":false},{"StartTime":77779.0,"Position":231.0,"HyperDash":false},{"StartTime":77852.0,"Position":118.0,"HyperDash":false},{"StartTime":77925.0,"Position":511.0,"HyperDash":false},{"StartTime":77998.0,"Position":333.0,"HyperDash":false},{"StartTime":78071.0,"Position":234.0,"HyperDash":false},{"StartTime":78144.0,"Position":228.0,"HyperDash":false},{"StartTime":78217.0,"Position":302.0,"HyperDash":false},{"StartTime":78290.0,"Position":390.0,"HyperDash":false},{"StartTime":78363.0,"Position":75.0,"HyperDash":false},{"StartTime":78436.0,"Position":506.0,"HyperDash":false},{"StartTime":78509.0,"Position":3.0,"HyperDash":false},{"StartTime":78582.0,"Position":289.0,"HyperDash":false},{"StartTime":78655.0,"Position":217.0,"HyperDash":false},{"StartTime":78728.0,"Position":447.0,"HyperDash":false},{"StartTime":78801.0,"Position":324.0,"HyperDash":false},{"StartTime":78874.0,"Position":183.0,"HyperDash":false},{"StartTime":78946.0,"Position":279.0,"HyperDash":false},{"StartTime":79019.0,"Position":157.0,"HyperDash":false},{"StartTime":79092.0,"Position":501.0,"HyperDash":false},{"StartTime":79165.0,"Position":215.0,"HyperDash":false},{"StartTime":79238.0,"Position":79.0,"HyperDash":false},{"StartTime":79311.0,"Position":337.0,"HyperDash":false},{"StartTime":79384.0,"Position":380.0,"HyperDash":false},{"StartTime":79457.0,"Position":348.0,"HyperDash":false},{"StartTime":79530.0,"Position":225.0,"HyperDash":false},{"StartTime":79603.0,"Position":363.0,"HyperDash":false},{"StartTime":79676.0,"Position":96.0,"HyperDash":false},{"StartTime":79749.0,"Position":104.0,"HyperDash":false},{"StartTime":79822.0,"Position":173.0,"HyperDash":false},{"StartTime":79895.0,"Position":373.0,"HyperDash":false},{"StartTime":79968.0,"Position":424.0,"HyperDash":false},{"StartTime":80041.0,"Position":268.0,"HyperDash":false}]},{"StartTime":82374.0,"Objects":[{"StartTime":82374.0,"Position":368.0,"HyperDash":false}]},{"StartTime":82707.0,"Objects":[{"StartTime":82707.0,"Position":224.0,"HyperDash":false},{"StartTime":82790.0,"Position":220.606583,"HyperDash":false},{"StartTime":82873.0,"Position":192.709763,"HyperDash":false},{"StartTime":82956.0,"Position":172.4607,"HyperDash":false},{"StartTime":83040.0,"Position":181.183929,"HyperDash":false},{"StartTime":83123.0,"Position":190.276581,"HyperDash":false},{"StartTime":83206.0,"Position":175.345276,"HyperDash":false},{"StartTime":83289.0,"Position":168.272736,"HyperDash":false},{"StartTime":83373.0,"Position":181.259979,"HyperDash":false},{"StartTime":83447.0,"Position":182.439926,"HyperDash":false},{"StartTime":83522.0,"Position":186.502777,"HyperDash":false},{"StartTime":83596.0,"Position":213.74353,"HyperDash":false},{"StartTime":83707.0,"Position":224.286057,"HyperDash":false}]},{"StartTime":84041.0,"Objects":[{"StartTime":84041.0,"Position":368.0,"HyperDash":false},{"StartTime":84124.0,"Position":366.238831,"HyperDash":false},{"StartTime":84207.0,"Position":382.477631,"HyperDash":false},{"StartTime":84290.0,"Position":376.716461,"HyperDash":false},{"StartTime":84374.0,"Position":372.9702,"HyperDash":false},{"StartTime":84457.0,"Position":381.209045,"HyperDash":false},{"StartTime":84540.0,"Position":367.447845,"HyperDash":false},{"StartTime":84623.0,"Position":364.686676,"HyperDash":false},{"StartTime":84707.0,"Position":377.94043,"HyperDash":false},{"StartTime":84781.0,"Position":396.044922,"HyperDash":false},{"StartTime":84856.0,"Position":371.164337,"HyperDash":false},{"StartTime":84930.0,"Position":379.268829,"HyperDash":false},{"StartTime":85041.0,"Position":382.925568,"HyperDash":false}]},{"StartTime":85374.0,"Objects":[{"StartTime":85374.0,"Position":240.0,"HyperDash":false},{"StartTime":85457.0,"Position":214.595383,"HyperDash":false},{"StartTime":85540.0,"Position":206.182877,"HyperDash":false},{"StartTime":85623.0,"Position":175.034424,"HyperDash":false},{"StartTime":85707.0,"Position":168.007141,"HyperDash":false},{"StartTime":85781.0,"Position":185.660355,"HyperDash":false},{"StartTime":85855.0,"Position":200.138123,"HyperDash":false},{"StartTime":85929.0,"Position":194.945816,"HyperDash":false},{"StartTime":86040.0,"Position":235.646591,"HyperDash":false}]},{"StartTime":86374.0,"Objects":[{"StartTime":86374.0,"Position":496.0,"HyperDash":false}]},{"StartTime":86707.0,"Objects":[{"StartTime":86707.0,"Position":224.0,"HyperDash":false},{"StartTime":86781.0,"Position":185.777771,"HyperDash":false},{"StartTime":86855.0,"Position":181.555557,"HyperDash":false},{"StartTime":86929.0,"Position":172.333328,"HyperDash":false},{"StartTime":87040.0,"Position":124.0,"HyperDash":false}]},{"StartTime":87374.0,"Objects":[{"StartTime":87374.0,"Position":256.0,"HyperDash":false},{"StartTime":87448.0,"Position":281.222229,"HyperDash":false},{"StartTime":87522.0,"Position":307.444458,"HyperDash":false},{"StartTime":87596.0,"Position":307.6667,"HyperDash":false},{"StartTime":87707.0,"Position":356.0,"HyperDash":false}]},{"StartTime":88041.0,"Objects":[{"StartTime":88041.0,"Position":184.0,"HyperDash":false}]},{"StartTime":88374.0,"Objects":[{"StartTime":88374.0,"Position":352.0,"HyperDash":false},{"StartTime":88448.0,"Position":358.1427,"HyperDash":false},{"StartTime":88522.0,"Position":353.2854,"HyperDash":false},{"StartTime":88596.0,"Position":361.4281,"HyperDash":false},{"StartTime":88707.0,"Position":366.142151,"HyperDash":false}]},{"StartTime":89041.0,"Objects":[{"StartTime":89041.0,"Position":80.0,"HyperDash":false}]},{"StartTime":89374.0,"Objects":[{"StartTime":89374.0,"Position":366.0,"HyperDash":false},{"StartTime":89457.0,"Position":408.9072,"HyperDash":false},{"StartTime":89540.0,"Position":411.8144,"HyperDash":false},{"StartTime":89623.0,"Position":438.7216,"HyperDash":false},{"StartTime":89707.0,"Position":465.928864,"HyperDash":false},{"StartTime":89781.0,"Position":460.722473,"HyperDash":false},{"StartTime":89855.0,"Position":437.516052,"HyperDash":false},{"StartTime":89929.0,"Position":403.309631,"HyperDash":false},{"StartTime":90040.0,"Position":366.0,"HyperDash":false}]},{"StartTime":90374.0,"Objects":[{"StartTime":90374.0,"Position":24.0,"HyperDash":false}]},{"StartTime":90707.0,"Objects":[{"StartTime":90707.0,"Position":368.0,"HyperDash":false},{"StartTime":90781.0,"Position":386.704376,"HyperDash":false},{"StartTime":90855.0,"Position":388.408722,"HyperDash":false},{"StartTime":90929.0,"Position":374.1131,"HyperDash":false},{"StartTime":91040.0,"Position":375.669647,"HyperDash":false}]},{"StartTime":91374.0,"Objects":[{"StartTime":91374.0,"Position":256.0,"HyperDash":false},{"StartTime":91448.0,"Position":246.777771,"HyperDash":false},{"StartTime":91522.0,"Position":220.555557,"HyperDash":false},{"StartTime":91596.0,"Position":188.333328,"HyperDash":false},{"StartTime":91707.0,"Position":156.0,"HyperDash":false}]},{"StartTime":92041.0,"Objects":[{"StartTime":92041.0,"Position":256.0,"HyperDash":false},{"StartTime":92115.0,"Position":291.222229,"HyperDash":false},{"StartTime":92189.0,"Position":285.444458,"HyperDash":false},{"StartTime":92263.0,"Position":313.6667,"HyperDash":false},{"StartTime":92374.0,"Position":356.0,"HyperDash":false}]},{"StartTime":92707.0,"Objects":[{"StartTime":92707.0,"Position":224.0,"HyperDash":false},{"StartTime":92781.0,"Position":189.777771,"HyperDash":false},{"StartTime":92855.0,"Position":181.555557,"HyperDash":false},{"StartTime":92929.0,"Position":141.333328,"HyperDash":false},{"StartTime":93040.0,"Position":124.0,"HyperDash":false}]},{"StartTime":93374.0,"Objects":[{"StartTime":93374.0,"Position":392.0,"HyperDash":false}]},{"StartTime":93707.0,"Objects":[{"StartTime":93707.0,"Position":128.0,"HyperDash":false},{"StartTime":93790.0,"Position":108.075073,"HyperDash":false},{"StartTime":93873.0,"Position":94.15015,"HyperDash":false},{"StartTime":93956.0,"Position":33.2252274,"HyperDash":false},{"StartTime":94040.0,"Position":28.0,"HyperDash":false},{"StartTime":94114.0,"Position":51.22222,"HyperDash":false},{"StartTime":94188.0,"Position":75.44444,"HyperDash":false},{"StartTime":94262.0,"Position":111.666664,"HyperDash":false},{"StartTime":94373.0,"Position":128.0,"HyperDash":false}]},{"StartTime":94707.0,"Objects":[{"StartTime":94707.0,"Position":256.0,"HyperDash":false},{"StartTime":94781.0,"Position":264.704376,"HyperDash":false},{"StartTime":94855.0,"Position":261.408722,"HyperDash":false},{"StartTime":94929.0,"Position":261.1131,"HyperDash":false},{"StartTime":95040.0,"Position":263.669647,"HyperDash":false}]},{"StartTime":95374.0,"Objects":[{"StartTime":95374.0,"Position":24.0,"HyperDash":false}]},{"StartTime":95540.0,"Objects":[{"StartTime":95540.0,"Position":96.0,"HyperDash":false}]},{"StartTime":95707.0,"Objects":[{"StartTime":95707.0,"Position":48.0,"HyperDash":false}]},{"StartTime":96041.0,"Objects":[{"StartTime":96041.0,"Position":168.0,"HyperDash":false},{"StartTime":96115.0,"Position":188.222229,"HyperDash":false},{"StartTime":96189.0,"Position":219.444443,"HyperDash":false},{"StartTime":96263.0,"Position":222.666672,"HyperDash":false},{"StartTime":96374.0,"Position":268.0,"HyperDash":false}]},{"StartTime":96707.0,"Objects":[{"StartTime":96707.0,"Position":152.0,"HyperDash":false},{"StartTime":96781.0,"Position":144.295639,"HyperDash":false},{"StartTime":96855.0,"Position":165.591263,"HyperDash":false},{"StartTime":96929.0,"Position":143.8869,"HyperDash":false},{"StartTime":97040.0,"Position":144.330353,"HyperDash":false}]},{"StartTime":97374.0,"Objects":[{"StartTime":97374.0,"Position":280.0,"HyperDash":false},{"StartTime":97457.0,"Position":300.248535,"HyperDash":false},{"StartTime":97540.0,"Position":317.463043,"HyperDash":false},{"StartTime":97623.0,"Position":329.187042,"HyperDash":false},{"StartTime":97707.0,"Position":369.215424,"HyperDash":false},{"StartTime":97781.0,"Position":392.887115,"HyperDash":false},{"StartTime":97855.0,"Position":394.493958,"HyperDash":false},{"StartTime":97929.0,"Position":416.841644,"HyperDash":false},{"StartTime":98040.0,"Position":422.157837,"HyperDash":false}]},{"StartTime":98707.0,"Objects":[{"StartTime":98707.0,"Position":144.0,"HyperDash":false}]},{"StartTime":99040.0,"Objects":[{"StartTime":99040.0,"Position":229.0,"HyperDash":false},{"StartTime":99138.0,"Position":51.0,"HyperDash":false},{"StartTime":99237.0,"Position":199.0,"HyperDash":false},{"StartTime":99336.0,"Position":208.0,"HyperDash":false},{"StartTime":99435.0,"Position":173.0,"HyperDash":false},{"StartTime":99534.0,"Position":367.0,"HyperDash":false},{"StartTime":99633.0,"Position":193.0,"HyperDash":false},{"StartTime":99732.0,"Position":488.0,"HyperDash":false},{"StartTime":99831.0,"Position":314.0,"HyperDash":false},{"StartTime":99930.0,"Position":135.0,"HyperDash":false},{"StartTime":100029.0,"Position":399.0,"HyperDash":false},{"StartTime":100128.0,"Position":404.0,"HyperDash":false},{"StartTime":100227.0,"Position":152.0,"HyperDash":false},{"StartTime":100326.0,"Position":353.0,"HyperDash":false},{"StartTime":100425.0,"Position":358.0,"HyperDash":false},{"StartTime":100524.0,"Position":447.0,"HyperDash":false},{"StartTime":100623.0,"Position":222.0,"HyperDash":false},{"StartTime":100722.0,"Position":382.0,"HyperDash":false},{"StartTime":100821.0,"Position":433.0,"HyperDash":false},{"StartTime":100920.0,"Position":450.0,"HyperDash":false},{"StartTime":101019.0,"Position":326.0,"HyperDash":false},{"StartTime":101118.0,"Position":414.0,"HyperDash":false},{"StartTime":101216.0,"Position":285.0,"HyperDash":false},{"StartTime":101315.0,"Position":336.0,"HyperDash":false},{"StartTime":101414.0,"Position":509.0,"HyperDash":false},{"StartTime":101513.0,"Position":334.0,"HyperDash":false},{"StartTime":101612.0,"Position":72.0,"HyperDash":false},{"StartTime":101711.0,"Position":425.0,"HyperDash":false},{"StartTime":101810.0,"Position":451.0,"HyperDash":false},{"StartTime":101909.0,"Position":220.0,"HyperDash":false},{"StartTime":102008.0,"Position":25.0,"HyperDash":false},{"StartTime":102107.0,"Position":77.0,"HyperDash":false},{"StartTime":102206.0,"Position":509.0,"HyperDash":false},{"StartTime":102305.0,"Position":90.0,"HyperDash":false},{"StartTime":102404.0,"Position":118.0,"HyperDash":false},{"StartTime":102503.0,"Position":58.0,"HyperDash":false},{"StartTime":102602.0,"Position":12.0,"HyperDash":false},{"StartTime":102701.0,"Position":215.0,"HyperDash":false},{"StartTime":102800.0,"Position":487.0,"HyperDash":false},{"StartTime":102899.0,"Position":446.0,"HyperDash":false},{"StartTime":102998.0,"Position":491.0,"HyperDash":false},{"StartTime":103097.0,"Position":459.0,"HyperDash":false},{"StartTime":103196.0,"Position":37.0,"HyperDash":false},{"StartTime":103294.0,"Position":291.0,"HyperDash":false},{"StartTime":103393.0,"Position":315.0,"HyperDash":false},{"StartTime":103492.0,"Position":35.0,"HyperDash":false},{"StartTime":103591.0,"Position":208.0,"HyperDash":false},{"StartTime":103690.0,"Position":504.0,"HyperDash":false},{"StartTime":103789.0,"Position":296.0,"HyperDash":false},{"StartTime":103888.0,"Position":105.0,"HyperDash":false},{"StartTime":103987.0,"Position":488.0,"HyperDash":false},{"StartTime":104086.0,"Position":230.0,"HyperDash":false},{"StartTime":104185.0,"Position":446.0,"HyperDash":false},{"StartTime":104284.0,"Position":241.0,"HyperDash":false},{"StartTime":104383.0,"Position":413.0,"HyperDash":false},{"StartTime":104482.0,"Position":357.0,"HyperDash":false},{"StartTime":104581.0,"Position":256.0,"HyperDash":false},{"StartTime":104680.0,"Position":192.0,"HyperDash":false},{"StartTime":104779.0,"Position":116.0,"HyperDash":false},{"StartTime":104878.0,"Position":397.0,"HyperDash":false},{"StartTime":104977.0,"Position":422.0,"HyperDash":false},{"StartTime":105076.0,"Position":230.0,"HyperDash":false},{"StartTime":105175.0,"Position":479.0,"HyperDash":false},{"StartTime":105274.0,"Position":276.0,"HyperDash":false},{"StartTime":105373.0,"Position":423.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1284935.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1284935.osu new file mode 100644 index 0000000000..a0ed6b190e --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1284935.osu @@ -0,0 +1,210 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 2 + +[Difficulty] +HPDrainRate:3 +CircleSize:2.5 +OverallDifficulty:6 +ApproachRate:6 +SliderMultiplier:1 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +2,80241,81249 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +41,333.333333333333,4,2,1,50,1,0 +707,-100,4,2,1,50,0,0 +2707,-100,4,2,1,85,0,0 +12040,-86.9565217391304,4,2,1,85,0,0 +12707,-100,4,2,1,85,0,0 +13374,-100,4,2,1,85,0,0 +34207,-100,4,2,1,75,0,0 +34374,-100,4,2,1,65,0,0 +34540,-100,4,2,1,55,0,0 +34707,-100,4,2,1,85,0,0 +45374,-133.333333333333,4,2,1,85,0,0 +54707,-133.333333333333,4,2,1,30,0,0 +56040,-100,4,2,1,85,0,1 +72040,-125,4,2,1,85,0,0 +72707,-100,4,2,1,85,0,0 +74707,-125,4,2,1,85,0,0 +75207,-100,4,2,1,85,0,0 +82374,-200,4,2,1,85,0,0 +85374,-100,4,2,1,85,0,0 +88040,-100,4,2,1,85,0,1 +98707,-100,4,2,1,85,0,0 +99040,-100,4,2,1,20,0,0 + +[HitObjects] +256,192,707,12,0,2374,0:0:0:0: +368,64,2707,6,2,L|256:64,1,100,2|2,0:0|0:0,0:0:0:0: +288,128,3207,2,2,L|304:192,1,50,2|8,0:0|0:0,0:0:0:0: +192,192,3707,6,2,L|64:192,2,100,2|2|2,0:0|0:0|0:0,0:0:0:0: +288,192,4707,1,8,0:0:0:0: +144,128,5041,1,10,0:0:0:0: +304,288,5374,6,6,L|448:144,2,200,6|8|0,0:0|0:0|0:0,0:0:0:0: +208,288,7041,1,0,0:0:0:0: +304,160,7374,2,10,L|320:48,1,100,10|8,0:0|0:0,0:0:0:0: +160,32,8041,6,6,L|48:32,1,100,6|2,0:0|0:0,0:0:0:0: +112,80,8541,1,0,0:0:0:0: +160,128,8707,2,8,P|208:160|128:232,1,200,8|6,0:0|0:0,0:0:0:0: +224,256,9707,5,2,0:0:0:0: +352,224,10041,2,8,L|240:224,1,100,8|2,0:0|0:0,0:0:0:0: +416,336,10707,6,6,P|464:320|416:216,1,200,6|12,0:0|0:0,0:0:0:0: +224,96,11874,1,2,0:0:0:0: +160,96,12041,2,2,P|116:152|160:232,1,172.500003290176,2|2,0:0|0:0,0:0:0:0: +224,232,12707,1,2,0:0:0:0: +464,64,13374,6,6,L|304:64,1,150,6|2,0:0|0:0,0:0:0:0: +240,64,14041,2,8,P|192:112|240:160,1,150,8|2,0:0|0:0,0:0:0:0: +320,160,14707,6,2,L|208:160,1,100,2|0,0:0|0:0,0:0:0:0: +320,256,15374,2,8,L|360:164,1,100,8|8,0:0|0:0,0:0:0:0: +192,64,16041,6,4,L|80:64,1,100,4|2,0:0|0:0,0:0:0:0: +144,80,16541,1,2,0:0:0:0: +192,96,16707,1,8,0:0:0:0: +336,96,17041,1,2,0:0:0:0: +288,96,17207,6,2,P|240:128|288:192,1,150,2|0,0:0|0:0,0:0:0:0: +384,192,18041,1,8,0:0:0:0: +256,192,18374,1,2,0:0:0:0: +416,192,18707,6,6,L|432:16,1,150,6|2,0:0|0:0,0:0:0:0: +336,32,19374,2,8,L|224:32,1,100,8|0,0:0|0:0,0:0:0:0: +256,32,20041,6,2,P|304:80|256:128,1,150,2|2,0:0|0:0,0:0:0:0: +196,128,20707,2,8,L|156:220,1,100,8|8,0:0|0:0,0:0:0:0: +320,224,21374,6,6,P|360:288|320:352,1,150,6|2,0:0|0:0,0:0:0:0: +224,352,22041,2,8,L|112:352,1,100,8|2,0:0|0:0,0:0:0:0: +192,224,22541,1,2,0:0:0:0: +204,272,22707,5,2,0:0:0:0: +96,288,23041,1,0,0:0:0:0: +208,288,23374,2,8,L|224:176,1,100,8|0,0:0|0:0,0:0:0:0: +80,96,24041,6,6,L|240:96,1,150,6|0,0:0|0:0,0:0:0:0: +176,96,24707,1,8,0:0:0:0: +256,128,25041,6,2,L|240:80,1,50,2|0,0:0|0:0,0:0:0:0: +352,96,25541,2,2,L|356:44,1,50,2|2,0:0|0:0,0:0:0:0: +192,176,26041,2,8,L|176:288,1,100,8|8,0:0|0:0,0:0:0:0: +272,336,26707,6,0,L|384:336,1,100,0|2,0:0|0:0,0:0:0:0: +320,288,27207,1,0,0:0:0:0: +272,240,27374,1,8,0:0:0:0: +416,240,27707,2,2,L|432:176,1,50,2|0,0:0|0:0,0:0:0:0: +288,176,28207,6,2,L|176:176,1,100,2|0,0:0|0:0,0:0:0:0: +256,368,28707,2,8,L|270:269,1,100,8|8,0:0|0:0,0:0:0:0: +128,256,29374,6,6,P|64:192|128:128,1,200,6|8,0:0|0:0,0:0:0:0: +224,128,30374,1,2,0:0:0:0: +368,128,30707,5,6,0:0:0:0: +432,128,30874,2,2,L|448:240,1,100,2|0,0:0|0:0,0:0:0:0: +384,256,31374,1,8,0:0:0:0: +240,256,31707,2,8,L|224:192,1,50,8|0,0:0|0:0,0:0:0:0: +304,192,32041,6,14,P|352:176|288:80,1,200,14|12,0:0|0:0,0:0:0:0: +160,80,33041,1,0,0:0:0:0: +304,80,33374,5,12,0:0:0:0: +368,80,33541,2,2,P|380:128|368:176,1,100,2|2,0:0|0:0,0:0:0:0: +224,176,34207,1,8,3:0:0:0: +176,176,34374,1,8,3:0:0:0: +128,176,34541,1,8,3:0:0:0: +200,144,34707,5,6,0:0:0:0: +336,272,35041,2,8,L|352:160,1,100,8|2,0:0|0:0,0:0:0:0: +208,144,35707,2,8,L|192:192,1,50,8|0,0:0|0:0,0:0:0:0: +336,208,36207,2,2,L|352:160,1,50,2|8,0:0|0:0,0:0:0:0: +208,160,36707,2,2,L|96:160,1,100,2|8,0:0|0:0,0:0:0:0: +256,160,37374,5,2,0:0:0:0: +320,160,37541,2,0,L|336:264,1,100,0|2,0:0|0:0,0:0:0:0: +272,272,38041,1,0,0:0:0:0: +416,272,38374,2,8,L|432:224,1,50,8|0,0:0|0:0,0:0:0:0: +288,224,38874,6,2,L|272:176,1,50,2|8,0:0|0:0,0:0:0:0: +336,160,39207,2,2,L|448:160,1,100,2|2,0:0|0:0,0:0:0:0: +384,160,39707,1,8,0:0:0:0: +240,160,40041,5,4,0:0:0:0: +384,64,40374,2,8,L|400:176,1,100,8|2,0:0|0:0,0:0:0:0: +256,176,41041,1,8,0:0:0:0: +112,176,41374,5,2,0:0:0:0: +48,224,41541,2,0,L|32:112,1,100,0|0,0:0|0:0,0:0:0:0: +96,112,42041,1,2,0:0:0:0: +240,112,42374,1,8,0:0:0:0: +96,112,42707,6,4,P|48:160|96:208,1,150,4|2,0:0|0:0,0:0:0:0: +160,208,43374,1,2,0:0:0:0: +288,208,43707,1,8,0:0:0:0: +160,208,44040,5,6,0:0:0:0: +304,288,44374,2,8,P|352:240|288:128,1,200,8|8,0:0|0:0,0:0:0:0: +136,128,45374,6,6,L|24:192,2,112.500004291535,6|0|0,0:0|0:0|0:0,0:0:0:0: +368,128,46874,2,0,L|384:240,1,112.500004291535,0|2,0:0|0:0,0:0:0:0: +272,256,47707,1,0,0:0:0:0: +144,256,48041,6,6,L|48:191,2,112.500004291535,6|0|0,0:0|0:0|0:0,0:0:0:0: +256,256,49374,2,2,P|304:224|224:112,1,225.000008583069,2|8,0:0|0:0,0:0:0:0: +384,96,50707,6,6,L|496:96,1,112.500004291535,6|0,0:0|0:0,0:0:0:0: +448,96,51374,1,8,0:0:0:0: +244,92,51874,2,2,L|132:108,1,112.500004291535,0|2,0:0|0:0,0:0:0:0: +208,288,52707,2,8,L|288:288,1,75.0000028610231,8|0,0:0|0:0,0:0:0:0: +368,288,53373,6,6,L|383:191,1,75.0000028610231,6|2,0:0|0:0,0:0:0:0: +255,192,54040,2,8,L|176:192,1,75.0000028610231,8|2,0:0|0:0,0:0:0:0: +272,80,54707,1,0,0:0:0:0: +160,272,55374,6,2,L|144:176,1,75.0000028610231,2|2,0:0|0:0,0:0:0:0: +320,144,56041,6,6,L|432:144,1,100,6|8,0:0|0:0,0:0:0:0: +256,240,56707,2,2,L|240:128,1,100,2|8,0:0|0:0,0:0:0:0: +328,112,57207,2,0,P|376:144|328:208,1,150,2|8,0:0|0:0,0:0:0:0: +176,208,58041,2,2,L|64:208,1,100,2|8,0:0|0:0,0:0:0:0: +208,208,58707,6,2,L|320:208,2,100,2|8|2,0:0|0:0|0:0,0:0:0:0: +64,208,59707,1,8,0:0:0:0: +128,208,59874,2,0,L|144:96,1,100,0|0,0:0|0:0,0:0:0:0: +80,96,60374,2,8,L|8:168,2,100,8|2|8,0:0|0:0|0:0,0:0:0:0: +224,96,61374,6,6,P|296:152|224:208,1,200,6|2,0:0|0:0,0:0:0:0: +96,224,62374,1,8,0:0:0:0: +32,224,62541,6,0,P|16:168|32:128,1,100,0|2,0:0|0:0,0:0:0:0: +92,112,63041,2,8,L|204:112,1,100,8|2,0:0|0:0,0:0:0:0: +336,112,63707,1,8,0:0:0:0: +192,112,64041,6,2,L|64:112,2,100,2|8|2,0:0|0:0|0:0,0:0:0:0: +336,112,65041,2,8,P|384:144|336:256,1,200,8|8,0:0|0:0,0:0:0:0: +208,256,66041,2,8,L|320:256,1,100,8|8,0:0|0:0,0:0:0:0: +144,160,66707,6,4,L|32:160,1,100,4|8,0:0|0:0,0:0:0:0: +192,256,67373,2,2,L|208:144,1,100,2|8,0:0|0:0,0:0:0:0: +120,128,67874,2,0,P|72:160|120:224,1,150,0|8,0:0|0:0,0:0:0:0: +272,224,68707,2,2,L|384:224,1,100,2|8,0:0|0:0,0:0:0:0: +237,223,69374,6,2,L|128:224,2,100,2|8|2,0:0|0:0|0:0,0:0:0:0: +384,208,70373,1,0,0:0:0:0: +448,208,70540,2,2,L|464:96,1,100,2|2,0:0|0:0,0:0:0:0: +400,96,71040,2,2,L|288:96,2,100,10|8|10,0:0|0:0|0:0,0:0:0:0: +256,96,72040,6,6,P|200:136|232:208,1,160,6|2,0:0|0:0,0:0:0:0: +400,208,73040,1,2,0:0:0:0: +472,208,73207,6,2,L|480:96,1,100,2|0,0:0|0:0,0:0:0:0: +416,80,73707,2,0,L|316:80,1,100,0|8,0:0|0:0,0:0:0:0: +176,80,74373,1,0,0:0:0:0: +304,80,74707,6,6,L|400:80,2,80,6|0|12,0:0|0:0|0:0,0:0:0:0: +160,80,75707,2,0,P|112:112|160:224,1,200,0|2,0:0|0:0,0:0:0:0: +304,224,76707,6,8,L|416:224,1,100,10|8,0:0|0:0,0:0:0:0: +212,224,77374,1,12,0:0:0:0: +256,192,77707,12,2,80041,0:0:0:0: +368,192,82374,5,0,0:0:0:0: +224,192,82707,2,6,P|176:160|224:104,1,150,6|0,0:0|0:0,0:0:0:0: +368,80,84041,2,6,L|384:240,1,150,6|0,0:0|0:0,0:0:0:0: +240,256,85374,6,6,P|168:212|240:160,1,200,6|10,0:0|0:0,0:0:0:0: +368,160,86374,1,0,0:0:0:0: +224,160,86707,6,8,L|112:160,1,100,8|0,0:0|0:0,0:0:0:0: +256,128,87374,2,8,L|368:128,1,100,8|2,0:0|0:0,0:0:0:0: +184,128,88041,5,6,0:0:0:0: +352,128,88374,2,8,L|368:240,1,100,8|8,0:0|0:0,0:0:0:0: +224,240,89041,1,8,0:0:0:0: +366,228,89374,6,0,L|472:224,2,100,2|8|8,0:0|0:0|0:0,0:0:0:0: +248,240,90374,1,8,0:0:0:0: +368,232,90707,6,0,L|376:128,1,100,2|8,0:0|0:0,0:0:0:0: +256,104,91374,2,0,L|152:104,1,100,8|8,0:0|0:0,0:0:0:0: +256,240,92041,6,2,L|368:240,1,100,2|8,0:0|0:0,0:0:0:0: +224,240,92707,2,8,L|120:240,1,100,8|2,0:0|0:0,0:0:0:0: +256,144,93374,5,6,0:0:0:0: +128,144,93707,2,8,L|16:144,2,100,8|8|8,0:0|0:0|0:0,0:0:0:0: +256,144,94707,6,2,L|264:248,1,100,2|8,0:0|0:0,0:0:0:0: +144,312,95374,1,8,0:0:0:0: +96,312,95540,1,8,0:0:0:0: +48,312,95707,1,8,0:0:0:0: +168,208,96041,6,6,L|272:208,1,100,6|0,0:0|0:0,0:0:0:0: +152,104,96707,2,8,L|144:208,1,100 +280,296,97374,6,8,P|369:254|422:171,1,200,10|8,0:0|0:0,0:0:0:0: +144,144,98707,1,14,0:0:0:0: +256,192,99040,12,0,105373,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1431386-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1431386-expected-conversion.json new file mode 100644 index 0000000000..de879d0d1c --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1431386-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":534.0,"Objects":[{"StartTime":534.0,"Position":333.0,"HyperDash":false},{"StartTime":589.0,"Position":336.445465,"HyperDash":false},{"StartTime":645.0,"Position":359.226318,"HyperDash":false},{"StartTime":701.0,"Position":386.604523,"HyperDash":false},{"StartTime":757.0,"Position":424.50647,"HyperDash":false},{"StartTime":813.0,"Position":446.4084,"HyperDash":false},{"StartTime":869.0,"Position":450.310333,"HyperDash":false},{"StartTime":925.0,"Position":468.21228,"HyperDash":false},{"StartTime":981.0,"Position":489.2919,"HyperDash":false},{"StartTime":1032.0,"Position":456.3446,"HyperDash":false},{"StartTime":1084.0,"Position":444.864258,"HyperDash":false},{"StartTime":1135.0,"Position":422.7393,"HyperDash":false},{"StartTime":1187.0,"Position":412.2589,"HyperDash":false},{"StartTime":1238.0,"Position":412.133942,"HyperDash":false},{"StartTime":1290.0,"Position":383.653564,"HyperDash":false},{"StartTime":1341.0,"Position":353.512756,"HyperDash":false},{"StartTime":1429.0,"Position":333.0,"HyperDash":false}]},{"StartTime":1877.0,"Objects":[{"StartTime":1877.0,"Position":182.0,"HyperDash":false}]},{"StartTime":2325.0,"Objects":[{"StartTime":2325.0,"Position":333.0,"HyperDash":false},{"StartTime":2380.0,"Position":357.239044,"HyperDash":false},{"StartTime":2436.0,"Position":352.8279,"HyperDash":false},{"StartTime":2492.0,"Position":382.475677,"HyperDash":false},{"StartTime":2548.0,"Position":429.244324,"HyperDash":false},{"StartTime":2604.0,"Position":448.013,"HyperDash":false},{"StartTime":2660.0,"Position":463.721436,"HyperDash":false},{"StartTime":2716.0,"Position":462.368317,"HyperDash":false},{"StartTime":2772.0,"Position":490.190643,"HyperDash":false},{"StartTime":2823.0,"Position":455.473358,"HyperDash":false},{"StartTime":2875.0,"Position":467.2298,"HyperDash":false},{"StartTime":2926.0,"Position":431.3082,"HyperDash":false},{"StartTime":2978.0,"Position":421.951569,"HyperDash":false},{"StartTime":3029.0,"Position":384.947937,"HyperDash":false},{"StartTime":3081.0,"Position":377.6223,"HyperDash":false},{"StartTime":3132.0,"Position":362.782471,"HyperDash":false},{"StartTime":3220.0,"Position":333.0,"HyperDash":false}]},{"StartTime":3668.0,"Objects":[{"StartTime":3668.0,"Position":182.0,"HyperDash":false}]},{"StartTime":4116.0,"Objects":[{"StartTime":4116.0,"Position":26.0,"HyperDash":false},{"StartTime":4171.0,"Position":40.9041862,"HyperDash":false},{"StartTime":4227.0,"Position":12.82481,"HyperDash":false},{"StartTime":4283.0,"Position":9.745436,"HyperDash":false},{"StartTime":4339.0,"Position":29.67428,"HyperDash":false},{"StartTime":4433.0,"Position":12.1371651,"HyperDash":false},{"StartTime":4563.0,"Position":26.0,"HyperDash":false}]},{"StartTime":5011.0,"Objects":[{"StartTime":5011.0,"Position":20.0,"HyperDash":false},{"StartTime":5104.0,"Position":66.26816,"HyperDash":false},{"StartTime":5234.0,"Position":97.6557159,"HyperDash":false}]},{"StartTime":5459.0,"Objects":[{"StartTime":5459.0,"Position":178.0,"HyperDash":false},{"StartTime":5552.0,"Position":226.229477,"HyperDash":false},{"StartTime":5682.0,"Position":255.569565,"HyperDash":false}]},{"StartTime":5907.0,"Objects":[{"StartTime":5907.0,"Position":308.0,"HyperDash":false},{"StartTime":5990.0,"Position":336.486633,"HyperDash":false},{"StartTime":6074.0,"Position":360.3285,"HyperDash":false},{"StartTime":6158.0,"Position":396.17038,"HyperDash":false},{"StartTime":6242.0,"Position":427.1899,"HyperDash":false},{"StartTime":6317.0,"Position":419.723,"HyperDash":false},{"StartTime":6392.0,"Position":368.078461,"HyperDash":false},{"StartTime":6467.0,"Position":352.433929,"HyperDash":false},{"StartTime":6578.0,"Position":308.0,"HyperDash":false}]},{"StartTime":6802.0,"Objects":[{"StartTime":6802.0,"Position":224.0,"HyperDash":false},{"StartTime":6853.0,"Position":226.916428,"HyperDash":false},{"StartTime":6904.0,"Position":222.886032,"HyperDash":false},{"StartTime":6956.0,"Position":216.946533,"HyperDash":false},{"StartTime":7007.0,"Position":211.428284,"HyperDash":false},{"StartTime":7058.0,"Position":212.341827,"HyperDash":false},{"StartTime":7110.0,"Position":205.693756,"HyperDash":false},{"StartTime":7161.0,"Position":183.379547,"HyperDash":false},{"StartTime":7249.0,"Position":212.117065,"HyperDash":false}]},{"StartTime":7698.0,"Objects":[{"StartTime":7698.0,"Position":372.0,"HyperDash":false},{"StartTime":7791.0,"Position":392.5109,"HyperDash":false},{"StartTime":7921.0,"Position":392.363617,"HyperDash":false}]},{"StartTime":8145.0,"Objects":[{"StartTime":8145.0,"Position":390.0,"HyperDash":false},{"StartTime":8228.0,"Position":407.6116,"HyperDash":false},{"StartTime":8312.0,"Position":434.579956,"HyperDash":false},{"StartTime":8396.0,"Position":497.5483,"HyperDash":false},{"StartTime":8480.0,"Position":509.695038,"HyperDash":false},{"StartTime":8555.0,"Position":475.115967,"HyperDash":false},{"StartTime":8630.0,"Position":472.3585,"HyperDash":false},{"StartTime":8705.0,"Position":432.601044,"HyperDash":false},{"StartTime":8816.0,"Position":390.0,"HyperDash":false}]},{"StartTime":9041.0,"Objects":[{"StartTime":9041.0,"Position":330.0,"HyperDash":false},{"StartTime":9134.0,"Position":286.7251,"HyperDash":false},{"StartTime":9264.0,"Position":250.211823,"HyperDash":false}]},{"StartTime":9489.0,"Objects":[{"StartTime":9489.0,"Position":171.0,"HyperDash":false},{"StartTime":9582.0,"Position":139.4586,"HyperDash":false},{"StartTime":9712.0,"Position":92.77017,"HyperDash":false}]},{"StartTime":9936.0,"Objects":[{"StartTime":9936.0,"Position":9.0,"HyperDash":false},{"StartTime":10019.0,"Position":0.0,"HyperDash":false},{"StartTime":10103.0,"Position":4.53266668,"HyperDash":false},{"StartTime":10187.0,"Position":0.0,"HyperDash":false},{"StartTime":10271.0,"Position":0.02520752,"HyperDash":false},{"StartTime":10346.0,"Position":0.0,"HyperDash":false},{"StartTime":10421.0,"Position":12.0244074,"HyperDash":false},{"StartTime":10496.0,"Position":0.0,"HyperDash":false},{"StartTime":10607.0,"Position":9.0,"HyperDash":false}]},{"StartTime":10832.0,"Objects":[{"StartTime":10832.0,"Position":28.0,"HyperDash":false},{"StartTime":10925.0,"Position":40.7889824,"HyperDash":false},{"StartTime":11055.0,"Position":105.537766,"HyperDash":false}]},{"StartTime":11280.0,"Objects":[{"StartTime":11280.0,"Position":263.0,"HyperDash":false}]},{"StartTime":11728.0,"Objects":[{"StartTime":11728.0,"Position":343.0,"HyperDash":false},{"StartTime":11811.0,"Position":365.302277,"HyperDash":false},{"StartTime":11895.0,"Position":388.675323,"HyperDash":false},{"StartTime":11979.0,"Position":437.668274,"HyperDash":false},{"StartTime":12063.0,"Position":459.2406,"HyperDash":false},{"StartTime":12138.0,"Position":431.2186,"HyperDash":false},{"StartTime":12213.0,"Position":423.446381,"HyperDash":false},{"StartTime":12288.0,"Position":362.9473,"HyperDash":false},{"StartTime":12399.0,"Position":343.0,"HyperDash":false}]},{"StartTime":12623.0,"Objects":[{"StartTime":12623.0,"Position":290.0,"HyperDash":false},{"StartTime":12716.0,"Position":297.645538,"HyperDash":false},{"StartTime":12846.0,"Position":296.3436,"HyperDash":false}]},{"StartTime":13071.0,"Objects":[{"StartTime":13071.0,"Position":265.0,"HyperDash":false},{"StartTime":13164.0,"Position":251.816544,"HyperDash":false},{"StartTime":13294.0,"Position":186.7354,"HyperDash":false}]},{"StartTime":13519.0,"Objects":[{"StartTime":13519.0,"Position":123.0,"HyperDash":false},{"StartTime":13602.0,"Position":103.378716,"HyperDash":false},{"StartTime":13686.0,"Position":73.40055,"HyperDash":false},{"StartTime":13770.0,"Position":37.4223862,"HyperDash":false},{"StartTime":13854.0,"Position":3.26579285,"HyperDash":false},{"StartTime":13929.0,"Position":36.85356,"HyperDash":false},{"StartTime":14004.0,"Position":72.61978,"HyperDash":false},{"StartTime":14079.0,"Position":68.38599,"HyperDash":false},{"StartTime":14190.0,"Position":123.0,"HyperDash":false}]},{"StartTime":14414.0,"Objects":[{"StartTime":14414.0,"Position":371.0,"HyperDash":false}]},{"StartTime":14862.0,"Objects":[{"StartTime":14862.0,"Position":184.0,"HyperDash":false},{"StartTime":14955.0,"Position":212.186356,"HyperDash":false},{"StartTime":15085.0,"Position":261.4036,"HyperDash":false}]},{"StartTime":15310.0,"Objects":[{"StartTime":15310.0,"Position":343.0,"HyperDash":false},{"StartTime":15393.0,"Position":362.374176,"HyperDash":false},{"StartTime":15477.0,"Position":407.102234,"HyperDash":false},{"StartTime":15561.0,"Position":440.8303,"HyperDash":false},{"StartTime":15645.0,"Position":461.735352,"HyperDash":false},{"StartTime":15720.0,"Position":439.369354,"HyperDash":false},{"StartTime":15795.0,"Position":398.826447,"HyperDash":false},{"StartTime":15870.0,"Position":372.283539,"HyperDash":false},{"StartTime":15981.0,"Position":343.0,"HyperDash":false}]},{"StartTime":16205.0,"Objects":[{"StartTime":16205.0,"Position":128.0,"HyperDash":false}]},{"StartTime":16653.0,"Objects":[{"StartTime":16653.0,"Position":219.0,"HyperDash":false},{"StartTime":16746.0,"Position":193.135818,"HyperDash":false},{"StartTime":16876.0,"Position":141.577332,"HyperDash":false}]},{"StartTime":17101.0,"Objects":[{"StartTime":17101.0,"Position":65.0,"HyperDash":false},{"StartTime":17184.0,"Position":56.4695549,"HyperDash":false},{"StartTime":17268.0,"Position":32.94629,"HyperDash":false},{"StartTime":17352.0,"Position":29.3506489,"HyperDash":false},{"StartTime":17436.0,"Position":17.0841427,"HyperDash":false},{"StartTime":17511.0,"Position":18.8012981,"HyperDash":false},{"StartTime":17586.0,"Position":4.30590057,"HyperDash":false},{"StartTime":17661.0,"Position":19.18378,"HyperDash":false},{"StartTime":17772.0,"Position":65.0,"HyperDash":false}]},{"StartTime":17996.0,"Objects":[{"StartTime":17996.0,"Position":144.0,"HyperDash":false},{"StartTime":18089.0,"Position":144.091827,"HyperDash":false},{"StartTime":18219.0,"Position":137.026642,"HyperDash":false}]},{"StartTime":18444.0,"Objects":[{"StartTime":18444.0,"Position":156.0,"HyperDash":false},{"StartTime":18537.0,"Position":195.74173,"HyperDash":false},{"StartTime":18667.0,"Position":233.945068,"HyperDash":false}]},{"StartTime":18892.0,"Objects":[{"StartTime":18892.0,"Position":309.0,"HyperDash":false},{"StartTime":18975.0,"Position":331.4903,"HyperDash":false},{"StartTime":19059.0,"Position":371.3359,"HyperDash":false},{"StartTime":19143.0,"Position":396.1815,"HyperDash":false},{"StartTime":19227.0,"Position":428.204742,"HyperDash":false},{"StartTime":19302.0,"Position":418.734558,"HyperDash":false},{"StartTime":19377.0,"Position":358.08667,"HyperDash":false},{"StartTime":19452.0,"Position":359.438843,"HyperDash":false},{"StartTime":19563.0,"Position":309.0,"HyperDash":false}]},{"StartTime":19787.0,"Objects":[{"StartTime":19787.0,"Position":237.0,"HyperDash":false},{"StartTime":19880.0,"Position":210.372055,"HyperDash":false},{"StartTime":20010.0,"Position":234.5058,"HyperDash":false}]},{"StartTime":20235.0,"Objects":[{"StartTime":20235.0,"Position":296.0,"HyperDash":false},{"StartTime":20328.0,"Position":335.3686,"HyperDash":false},{"StartTime":20458.0,"Position":374.402649,"HyperDash":false}]},{"StartTime":20683.0,"Objects":[{"StartTime":20683.0,"Position":441.0,"HyperDash":false},{"StartTime":20766.0,"Position":438.742676,"HyperDash":false},{"StartTime":20850.0,"Position":413.918945,"HyperDash":false},{"StartTime":20934.0,"Position":420.274963,"HyperDash":false},{"StartTime":21018.0,"Position":440.574921,"HyperDash":false},{"StartTime":21093.0,"Position":428.433563,"HyperDash":false},{"StartTime":21168.0,"Position":429.064026,"HyperDash":false},{"StartTime":21243.0,"Position":410.101563,"HyperDash":false},{"StartTime":21354.0,"Position":441.0,"HyperDash":false}]},{"StartTime":21578.0,"Objects":[{"StartTime":21578.0,"Position":501.0,"HyperDash":false}]},{"StartTime":22026.0,"Objects":[{"StartTime":22026.0,"Position":386.0,"HyperDash":false},{"StartTime":22081.0,"Position":374.485016,"HyperDash":false},{"StartTime":22137.0,"Position":357.487,"HyperDash":false},{"StartTime":22193.0,"Position":318.665863,"HyperDash":false},{"StartTime":22249.0,"Position":311.3857,"HyperDash":false},{"StartTime":22305.0,"Position":300.98407,"HyperDash":false},{"StartTime":22361.0,"Position":266.708557,"HyperDash":false},{"StartTime":22417.0,"Position":256.6825,"HyperDash":false},{"StartTime":22473.0,"Position":240.899826,"HyperDash":false},{"StartTime":22529.0,"Position":227.386124,"HyperDash":false},{"StartTime":22585.0,"Position":225.861679,"HyperDash":false},{"StartTime":22641.0,"Position":185.350357,"HyperDash":false},{"StartTime":22697.0,"Position":169.039291,"HyperDash":false},{"StartTime":22753.0,"Position":131.207657,"HyperDash":false},{"StartTime":22809.0,"Position":115.215012,"HyperDash":false},{"StartTime":22865.0,"Position":108.42057,"HyperDash":false},{"StartTime":22921.0,"Position":89.93976,"HyperDash":false},{"StartTime":22977.0,"Position":126.071373,"HyperDash":false},{"StartTime":23033.0,"Position":140.858871,"HyperDash":false},{"StartTime":23089.0,"Position":159.8509,"HyperDash":false},{"StartTime":23145.0,"Position":166.689056,"HyperDash":false},{"StartTime":23201.0,"Position":205.013,"HyperDash":false},{"StartTime":23257.0,"Position":197.5373,"HyperDash":false},{"StartTime":23313.0,"Position":239.081787,"HyperDash":false},{"StartTime":23369.0,"Position":240.611664,"HyperDash":false},{"StartTime":23420.0,"Position":243.039688,"HyperDash":false},{"StartTime":23472.0,"Position":272.749237,"HyperDash":false},{"StartTime":23523.0,"Position":272.238831,"HyperDash":false},{"StartTime":23575.0,"Position":317.028137,"HyperDash":false},{"StartTime":23626.0,"Position":306.314117,"HyperDash":false},{"StartTime":23678.0,"Position":335.531525,"HyperDash":false},{"StartTime":23729.0,"Position":341.698853,"HyperDash":false},{"StartTime":23817.0,"Position":386.0,"HyperDash":false}]},{"StartTime":24041.0,"Objects":[{"StartTime":24041.0,"Position":465.0,"HyperDash":false}]},{"StartTime":24265.0,"Objects":[{"StartTime":24265.0,"Position":497.0,"HyperDash":false},{"StartTime":24348.0,"Position":488.55304,"HyperDash":false},{"StartTime":24432.0,"Position":484.0766,"HyperDash":false},{"StartTime":24516.0,"Position":480.600128,"HyperDash":false},{"StartTime":24600.0,"Position":487.108948,"HyperDash":false},{"StartTime":24675.0,"Position":484.305328,"HyperDash":false},{"StartTime":24750.0,"Position":486.516449,"HyperDash":false},{"StartTime":24825.0,"Position":507.727539,"HyperDash":false},{"StartTime":24936.0,"Position":497.0,"HyperDash":false}]},{"StartTime":25160.0,"Objects":[{"StartTime":25160.0,"Position":410.0,"HyperDash":false},{"StartTime":25253.0,"Position":380.109436,"HyperDash":false},{"StartTime":25383.0,"Position":332.0014,"HyperDash":false}]},{"StartTime":25608.0,"Objects":[{"StartTime":25608.0,"Position":262.0,"HyperDash":false},{"StartTime":25701.0,"Position":218.3702,"HyperDash":false},{"StartTime":25831.0,"Position":184.296768,"HyperDash":false}]},{"StartTime":26056.0,"Objects":[{"StartTime":26056.0,"Position":136.0,"HyperDash":false},{"StartTime":26139.0,"Position":126.098541,"HyperDash":false},{"StartTime":26223.0,"Position":125.222366,"HyperDash":false},{"StartTime":26307.0,"Position":138.346191,"HyperDash":false},{"StartTime":26391.0,"Position":144.482666,"HyperDash":false},{"StartTime":26466.0,"Position":145.59903,"HyperDash":false},{"StartTime":26541.0,"Position":121.702759,"HyperDash":false},{"StartTime":26616.0,"Position":138.806488,"HyperDash":false},{"StartTime":26727.0,"Position":136.0,"HyperDash":false}]},{"StartTime":26951.0,"Objects":[{"StartTime":26951.0,"Position":67.0,"HyperDash":false}]},{"StartTime":27399.0,"Objects":[{"StartTime":27399.0,"Position":118.0,"HyperDash":false},{"StartTime":27454.0,"Position":149.263077,"HyperDash":false},{"StartTime":27510.0,"Position":152.985062,"HyperDash":false},{"StartTime":27566.0,"Position":191.9209,"HyperDash":false},{"StartTime":27622.0,"Position":186.1002,"HyperDash":false},{"StartTime":27678.0,"Position":201.49527,"HyperDash":false},{"StartTime":27734.0,"Position":213.367828,"HyperDash":false},{"StartTime":27790.0,"Position":256.814331,"HyperDash":false},{"StartTime":27846.0,"Position":246.461456,"HyperDash":false},{"StartTime":27940.0,"Position":268.489075,"HyperDash":false},{"StartTime":28070.0,"Position":233.472458,"HyperDash":false}]},{"StartTime":28295.0,"Objects":[{"StartTime":28295.0,"Position":162.0,"HyperDash":false},{"StartTime":28350.0,"Position":164.220917,"HyperDash":false},{"StartTime":28406.0,"Position":194.79129,"HyperDash":false},{"StartTime":28462.0,"Position":226.3617,"HyperDash":false},{"StartTime":28518.0,"Position":251.932068,"HyperDash":false},{"StartTime":28574.0,"Position":246.033783,"HyperDash":false},{"StartTime":28630.0,"Position":264.13385,"HyperDash":false},{"StartTime":28686.0,"Position":278.233948,"HyperDash":false},{"StartTime":28742.0,"Position":316.344543,"HyperDash":false},{"StartTime":28836.0,"Position":335.418,"HyperDash":false},{"StartTime":28966.0,"Position":395.157867,"HyperDash":false}]},{"StartTime":29190.0,"Objects":[{"StartTime":29190.0,"Position":481.0,"HyperDash":false}]},{"StartTime":29414.0,"Objects":[{"StartTime":29414.0,"Position":499.0,"HyperDash":false}]},{"StartTime":29638.0,"Objects":[{"StartTime":29638.0,"Position":454.0,"HyperDash":false},{"StartTime":29721.0,"Position":464.071655,"HyperDash":false},{"StartTime":29805.0,"Position":475.192383,"HyperDash":false},{"StartTime":29889.0,"Position":456.3131,"HyperDash":false},{"StartTime":29973.0,"Position":470.458374,"HyperDash":false},{"StartTime":30048.0,"Position":459.80368,"HyperDash":false},{"StartTime":30123.0,"Position":473.124451,"HyperDash":false},{"StartTime":30198.0,"Position":468.445251,"HyperDash":false},{"StartTime":30309.0,"Position":454.0,"HyperDash":false}]},{"StartTime":30533.0,"Objects":[{"StartTime":30533.0,"Position":375.0,"HyperDash":false},{"StartTime":30626.0,"Position":348.741882,"HyperDash":false},{"StartTime":30756.0,"Position":297.814758,"HyperDash":false}]},{"StartTime":30981.0,"Objects":[{"StartTime":30981.0,"Position":220.0,"HyperDash":false},{"StartTime":31036.0,"Position":200.494568,"HyperDash":false},{"StartTime":31092.0,"Position":189.8578,"HyperDash":false},{"StartTime":31148.0,"Position":158.568909,"HyperDash":false},{"StartTime":31204.0,"Position":137.831863,"HyperDash":false},{"StartTime":31260.0,"Position":143.862488,"HyperDash":false},{"StartTime":31316.0,"Position":99.86672,"HyperDash":false},{"StartTime":31372.0,"Position":85.05304,"HyperDash":false},{"StartTime":31428.0,"Position":65.47009,"HyperDash":false},{"StartTime":31479.0,"Position":97.9493561,"HyperDash":false},{"StartTime":31531.0,"Position":85.30683,"HyperDash":false},{"StartTime":31582.0,"Position":136.499527,"HyperDash":false},{"StartTime":31634.0,"Position":141.072418,"HyperDash":false},{"StartTime":31685.0,"Position":152.152847,"HyperDash":false},{"StartTime":31737.0,"Position":182.289108,"HyperDash":false},{"StartTime":31788.0,"Position":190.604156,"HyperDash":false},{"StartTime":31876.0,"Position":220.0,"HyperDash":false}]},{"StartTime":32325.0,"Objects":[{"StartTime":32325.0,"Position":365.0,"HyperDash":false}]},{"StartTime":32772.0,"Objects":[{"StartTime":32772.0,"Position":480.0,"HyperDash":false},{"StartTime":32823.0,"Position":493.32843,"HyperDash":false},{"StartTime":32874.0,"Position":464.65686,"HyperDash":false},{"StartTime":32926.0,"Position":458.9525,"HyperDash":false},{"StartTime":32977.0,"Position":466.280945,"HyperDash":false},{"StartTime":33028.0,"Position":453.609375,"HyperDash":false},{"StartTime":33080.0,"Position":465.905029,"HyperDash":false},{"StartTime":33131.0,"Position":473.233459,"HyperDash":false},{"StartTime":33219.0,"Position":465.349182,"HyperDash":false}]},{"StartTime":33444.0,"Objects":[{"StartTime":33444.0,"Position":322.0,"HyperDash":false}]},{"StartTime":33668.0,"Objects":[{"StartTime":33668.0,"Position":323.0,"HyperDash":false},{"StartTime":33761.0,"Position":290.802338,"HyperDash":false},{"StartTime":33891.0,"Position":243.397018,"HyperDash":false}]},{"StartTime":34116.0,"Objects":[{"StartTime":34116.0,"Position":162.0,"HyperDash":false},{"StartTime":34209.0,"Position":126.802353,"HyperDash":false},{"StartTime":34339.0,"Position":82.39702,"HyperDash":false}]},{"StartTime":34563.0,"Objects":[{"StartTime":34563.0,"Position":31.0,"HyperDash":false},{"StartTime":34618.0,"Position":38.3338165,"HyperDash":false},{"StartTime":34674.0,"Position":12.5252123,"HyperDash":false},{"StartTime":34730.0,"Position":24.94529,"HyperDash":false},{"StartTime":34786.0,"Position":0.0,"HyperDash":false},{"StartTime":34842.0,"Position":7.506119,"HyperDash":false},{"StartTime":34898.0,"Position":0.0,"HyperDash":false},{"StartTime":34954.0,"Position":18.1432285,"HyperDash":false},{"StartTime":35010.0,"Position":21.8685,"HyperDash":false},{"StartTime":35061.0,"Position":25.771328,"HyperDash":false},{"StartTime":35113.0,"Position":7.32367039,"HyperDash":false},{"StartTime":35164.0,"Position":0.0,"HyperDash":false},{"StartTime":35216.0,"Position":12.3119221,"HyperDash":false},{"StartTime":35267.0,"Position":14.6618919,"HyperDash":false},{"StartTime":35319.0,"Position":12.9432926,"HyperDash":false},{"StartTime":35370.0,"Position":0.05334282,"HyperDash":false},{"StartTime":35458.0,"Position":31.0,"HyperDash":false}]},{"StartTime":35907.0,"Objects":[{"StartTime":35907.0,"Position":183.0,"HyperDash":false}]},{"StartTime":36354.0,"Objects":[{"StartTime":36354.0,"Position":336.0,"HyperDash":false},{"StartTime":36409.0,"Position":332.550262,"HyperDash":false},{"StartTime":36465.0,"Position":357.661743,"HyperDash":false},{"StartTime":36521.0,"Position":395.893524,"HyperDash":false},{"StartTime":36577.0,"Position":398.9578,"HyperDash":false},{"StartTime":36633.0,"Position":441.6068,"HyperDash":false},{"StartTime":36689.0,"Position":459.563568,"HyperDash":false},{"StartTime":36745.0,"Position":458.55127,"HyperDash":false},{"StartTime":36801.0,"Position":485.465271,"HyperDash":false},{"StartTime":36852.0,"Position":448.681152,"HyperDash":false},{"StartTime":36904.0,"Position":431.13797,"HyperDash":false},{"StartTime":36955.0,"Position":444.931641,"HyperDash":false},{"StartTime":37007.0,"Position":413.575562,"HyperDash":false},{"StartTime":37058.0,"Position":398.977661,"HyperDash":false},{"StartTime":37110.0,"Position":374.650665,"HyperDash":false},{"StartTime":37161.0,"Position":348.4818,"HyperDash":false},{"StartTime":37249.0,"Position":336.0,"HyperDash":false}]},{"StartTime":37474.0,"Objects":[{"StartTime":37474.0,"Position":278.0,"HyperDash":false}]},{"StartTime":37698.0,"Objects":[{"StartTime":37698.0,"Position":218.0,"HyperDash":false},{"StartTime":37791.0,"Position":186.661133,"HyperDash":false},{"StartTime":37921.0,"Position":141.792221,"HyperDash":false}]},{"StartTime":38145.0,"Objects":[{"StartTime":38145.0,"Position":55.0,"HyperDash":false},{"StartTime":38196.0,"Position":55.39138,"HyperDash":false},{"StartTime":38247.0,"Position":17.7827568,"HyperDash":false},{"StartTime":38299.0,"Position":25.8781147,"HyperDash":false},{"StartTime":38350.0,"Position":15.6772919,"HyperDash":false},{"StartTime":38401.0,"Position":46.47647,"HyperDash":false},{"StartTime":38453.0,"Position":19.3305359,"HyperDash":false},{"StartTime":38504.0,"Position":58.12971,"HyperDash":false},{"StartTime":38592.0,"Position":45.9596672,"HyperDash":false}]},{"StartTime":39041.0,"Objects":[{"StartTime":39041.0,"Position":188.0,"HyperDash":false},{"StartTime":39092.0,"Position":206.608627,"HyperDash":false},{"StartTime":39143.0,"Position":207.217239,"HyperDash":false},{"StartTime":39195.0,"Position":212.121887,"HyperDash":false},{"StartTime":39246.0,"Position":222.322708,"HyperDash":false},{"StartTime":39297.0,"Position":209.523529,"HyperDash":false},{"StartTime":39349.0,"Position":205.669464,"HyperDash":false},{"StartTime":39400.0,"Position":188.870285,"HyperDash":false},{"StartTime":39488.0,"Position":197.040329,"HyperDash":false}]},{"StartTime":39936.0,"Objects":[{"StartTime":39936.0,"Position":305.0,"HyperDash":false},{"StartTime":39987.0,"Position":326.221222,"HyperDash":false},{"StartTime":40038.0,"Position":329.12558,"HyperDash":false},{"StartTime":40090.0,"Position":351.555145,"HyperDash":false},{"StartTime":40141.0,"Position":355.340942,"HyperDash":false},{"StartTime":40192.0,"Position":390.523621,"HyperDash":false},{"StartTime":40244.0,"Position":399.5398,"HyperDash":false},{"StartTime":40295.0,"Position":402.617462,"HyperDash":false},{"StartTime":40383.0,"Position":452.46936,"HyperDash":false}]},{"StartTime":40832.0,"Objects":[{"StartTime":40832.0,"Position":486.0,"HyperDash":false},{"StartTime":40915.0,"Position":469.7972,"HyperDash":false},{"StartTime":40999.0,"Position":481.8138,"HyperDash":false},{"StartTime":41083.0,"Position":457.634216,"HyperDash":false},{"StartTime":41167.0,"Position":437.2155,"HyperDash":false},{"StartTime":41242.0,"Position":451.25293,"HyperDash":false},{"StartTime":41317.0,"Position":459.7593,"HyperDash":false},{"StartTime":41392.0,"Position":473.703156,"HyperDash":false},{"StartTime":41503.0,"Position":486.0,"HyperDash":false}]},{"StartTime":41728.0,"Objects":[{"StartTime":41728.0,"Position":415.0,"HyperDash":false},{"StartTime":41783.0,"Position":390.7221,"HyperDash":false},{"StartTime":41839.0,"Position":366.340027,"HyperDash":false},{"StartTime":41895.0,"Position":357.472321,"HyperDash":false},{"StartTime":41951.0,"Position":323.4682,"HyperDash":false},{"StartTime":42007.0,"Position":318.667938,"HyperDash":false},{"StartTime":42063.0,"Position":313.410736,"HyperDash":false},{"StartTime":42119.0,"Position":269.011841,"HyperDash":false},{"StartTime":42175.0,"Position":262.671448,"HyperDash":false},{"StartTime":42226.0,"Position":272.1187,"HyperDash":false},{"StartTime":42278.0,"Position":312.04538,"HyperDash":false},{"StartTime":42329.0,"Position":293.437958,"HyperDash":false},{"StartTime":42381.0,"Position":345.712128,"HyperDash":false},{"StartTime":42432.0,"Position":366.896667,"HyperDash":false},{"StartTime":42484.0,"Position":350.446564,"HyperDash":false},{"StartTime":42535.0,"Position":369.3803,"HyperDash":false},{"StartTime":42623.0,"Position":415.0,"HyperDash":false}]},{"StartTime":43071.0,"Objects":[{"StartTime":43071.0,"Position":353.0,"HyperDash":false}]},{"StartTime":43519.0,"Objects":[{"StartTime":43519.0,"Position":181.0,"HyperDash":false},{"StartTime":43570.0,"Position":174.8302,"HyperDash":false},{"StartTime":43621.0,"Position":156.660385,"HyperDash":false},{"StartTime":43673.0,"Position":141.134308,"HyperDash":false},{"StartTime":43724.0,"Position":99.9645,"HyperDash":false},{"StartTime":43775.0,"Position":75.79469,"HyperDash":false},{"StartTime":43827.0,"Position":67.26861,"HyperDash":false},{"StartTime":43878.0,"Position":66.0988159,"HyperDash":false},{"StartTime":43966.0,"Position":21.7469788,"HyperDash":false}]},{"StartTime":44414.0,"Objects":[{"StartTime":44414.0,"Position":21.0,"HyperDash":false},{"StartTime":44465.0,"Position":38.1698074,"HyperDash":false},{"StartTime":44516.0,"Position":57.3396149,"HyperDash":false},{"StartTime":44568.0,"Position":68.86569,"HyperDash":false},{"StartTime":44619.0,"Position":110.0355,"HyperDash":false},{"StartTime":44670.0,"Position":121.205307,"HyperDash":false},{"StartTime":44722.0,"Position":123.731384,"HyperDash":false},{"StartTime":44773.0,"Position":164.901184,"HyperDash":false},{"StartTime":44861.0,"Position":180.253021,"HyperDash":false}]},{"StartTime":45086.0,"Objects":[{"StartTime":45086.0,"Position":328.0,"HyperDash":false}]},{"StartTime":45310.0,"Objects":[{"StartTime":45310.0,"Position":329.0,"HyperDash":false},{"StartTime":45365.0,"Position":332.211578,"HyperDash":false},{"StartTime":45421.0,"Position":367.175873,"HyperDash":false},{"StartTime":45477.0,"Position":371.022522,"HyperDash":false},{"StartTime":45533.0,"Position":395.233124,"HyperDash":false},{"StartTime":45589.0,"Position":413.246216,"HyperDash":false},{"StartTime":45645.0,"Position":433.6284,"HyperDash":false},{"StartTime":45701.0,"Position":457.874817,"HyperDash":false},{"StartTime":45757.0,"Position":467.659363,"HyperDash":false},{"StartTime":45813.0,"Position":493.610321,"HyperDash":false},{"StartTime":45869.0,"Position":491.524567,"HyperDash":false},{"StartTime":45925.0,"Position":475.219482,"HyperDash":false},{"StartTime":45981.0,"Position":499.624725,"HyperDash":false},{"StartTime":46037.0,"Position":471.774384,"HyperDash":false},{"StartTime":46093.0,"Position":462.734833,"HyperDash":false},{"StartTime":46149.0,"Position":450.75238,"HyperDash":false},{"StartTime":46205.0,"Position":451.0282,"HyperDash":false},{"StartTime":46256.0,"Position":439.419067,"HyperDash":false},{"StartTime":46308.0,"Position":413.8077,"HyperDash":false},{"StartTime":46359.0,"Position":423.184723,"HyperDash":false},{"StartTime":46411.0,"Position":393.298828,"HyperDash":false},{"StartTime":46462.0,"Position":384.2213,"HyperDash":false},{"StartTime":46514.0,"Position":355.668274,"HyperDash":false},{"StartTime":46565.0,"Position":316.77417,"HyperDash":false},{"StartTime":46653.0,"Position":303.770752,"HyperDash":false}]},{"StartTime":47101.0,"Objects":[{"StartTime":47101.0,"Position":257.0,"HyperDash":false},{"StartTime":47184.0,"Position":212.304276,"HyperDash":false},{"StartTime":47268.0,"Position":213.274872,"HyperDash":false},{"StartTime":47352.0,"Position":179.2254,"HyperDash":false},{"StartTime":47436.0,"Position":142.9541,"HyperDash":false},{"StartTime":47511.0,"Position":150.761337,"HyperDash":false},{"StartTime":47586.0,"Position":198.741776,"HyperDash":false},{"StartTime":47661.0,"Position":220.961136,"HyperDash":false},{"StartTime":47772.0,"Position":257.0,"HyperDash":false}]},{"StartTime":47996.0,"Objects":[{"StartTime":47996.0,"Position":336.0,"HyperDash":false}]},{"StartTime":48220.0,"Objects":[{"StartTime":48220.0,"Position":417.0,"HyperDash":false},{"StartTime":48275.0,"Position":444.6565,"HyperDash":false},{"StartTime":48331.0,"Position":441.67038,"HyperDash":false},{"StartTime":48387.0,"Position":472.684265,"HyperDash":false},{"StartTime":48443.0,"Position":496.876831,"HyperDash":false},{"StartTime":48537.0,"Position":462.460815,"HyperDash":false},{"StartTime":48667.0,"Position":417.0,"HyperDash":false}]},{"StartTime":48892.0,"Objects":[{"StartTime":48892.0,"Position":379.0,"HyperDash":false},{"StartTime":48985.0,"Position":356.006134,"HyperDash":false},{"StartTime":49115.0,"Position":302.860016,"HyperDash":false}]},{"StartTime":49339.0,"Objects":[{"StartTime":49339.0,"Position":218.0,"HyperDash":false},{"StartTime":49422.0,"Position":228.320267,"HyperDash":false},{"StartTime":49506.0,"Position":263.682922,"HyperDash":false},{"StartTime":49590.0,"Position":263.529572,"HyperDash":false},{"StartTime":49674.0,"Position":266.142761,"HyperDash":false},{"StartTime":49749.0,"Position":265.0218,"HyperDash":false},{"StartTime":49824.0,"Position":252.383118,"HyperDash":false},{"StartTime":49899.0,"Position":244.59021,"HyperDash":false},{"StartTime":50010.0,"Position":218.0,"HyperDash":false}]},{"StartTime":50235.0,"Objects":[{"StartTime":50235.0,"Position":142.0,"HyperDash":false},{"StartTime":50328.0,"Position":154.293335,"HyperDash":false},{"StartTime":50458.0,"Position":135.509842,"HyperDash":false}]},{"StartTime":50683.0,"Objects":[{"StartTime":50683.0,"Position":75.0,"HyperDash":false},{"StartTime":50734.0,"Position":106.62645,"HyperDash":false},{"StartTime":50785.0,"Position":89.7852249,"HyperDash":false},{"StartTime":50837.0,"Position":105.419983,"HyperDash":false},{"StartTime":50888.0,"Position":153.41716,"HyperDash":false},{"StartTime":50939.0,"Position":166.651077,"HyperDash":false},{"StartTime":50991.0,"Position":157.985535,"HyperDash":false},{"StartTime":51042.0,"Position":194.261,"HyperDash":false},{"StartTime":51130.0,"Position":222.110641,"HyperDash":false}]},{"StartTime":51354.0,"Objects":[{"StartTime":51354.0,"Position":295.0,"HyperDash":false},{"StartTime":51405.0,"Position":294.626465,"HyperDash":false},{"StartTime":51456.0,"Position":306.785217,"HyperDash":false},{"StartTime":51508.0,"Position":347.419983,"HyperDash":false},{"StartTime":51559.0,"Position":363.417175,"HyperDash":false},{"StartTime":51610.0,"Position":396.6511,"HyperDash":false},{"StartTime":51662.0,"Position":408.985535,"HyperDash":false},{"StartTime":51713.0,"Position":417.261,"HyperDash":false},{"StartTime":51801.0,"Position":442.110657,"HyperDash":false}]},{"StartTime":52026.0,"Objects":[{"StartTime":52026.0,"Position":498.0,"HyperDash":false}]},{"StartTime":52474.0,"Objects":[{"StartTime":52474.0,"Position":404.0,"HyperDash":false},{"StartTime":52567.0,"Position":378.721558,"HyperDash":false},{"StartTime":52697.0,"Position":324.2033,"HyperDash":false}]},{"StartTime":52922.0,"Objects":[{"StartTime":52922.0,"Position":251.0,"HyperDash":false},{"StartTime":53005.0,"Position":216.759811,"HyperDash":false},{"StartTime":53089.0,"Position":195.34903,"HyperDash":false},{"StartTime":53173.0,"Position":148.36676,"HyperDash":false},{"StartTime":53257.0,"Position":135.014374,"HyperDash":false},{"StartTime":53332.0,"Position":141.829834,"HyperDash":false},{"StartTime":53407.0,"Position":167.570328,"HyperDash":false},{"StartTime":53482.0,"Position":217.1065,"HyperDash":false},{"StartTime":53593.0,"Position":251.0,"HyperDash":false}]},{"StartTime":53817.0,"Objects":[{"StartTime":53817.0,"Position":298.0,"HyperDash":false},{"StartTime":53910.0,"Position":296.8232,"HyperDash":false},{"StartTime":54040.0,"Position":295.178223,"HyperDash":false}]},{"StartTime":54265.0,"Objects":[{"StartTime":54265.0,"Position":249.0,"HyperDash":false},{"StartTime":54316.0,"Position":240.835571,"HyperDash":false},{"StartTime":54367.0,"Position":194.671127,"HyperDash":false},{"StartTime":54419.0,"Position":191.150528,"HyperDash":false},{"StartTime":54470.0,"Position":170.708618,"HyperDash":false},{"StartTime":54521.0,"Position":161.552643,"HyperDash":false},{"StartTime":54573.0,"Position":158.896118,"HyperDash":false},{"StartTime":54624.0,"Position":134.782074,"HyperDash":false},{"StartTime":54712.0,"Position":92.52641,"HyperDash":false}]},{"StartTime":55160.0,"Objects":[{"StartTime":55160.0,"Position":8.0,"HyperDash":false},{"StartTime":55253.0,"Position":34.09524,"HyperDash":false},{"StartTime":55383.0,"Position":85.37553,"HyperDash":false}]},{"StartTime":55608.0,"Objects":[{"StartTime":55608.0,"Position":165.0,"HyperDash":false},{"StartTime":55701.0,"Position":183.095245,"HyperDash":false},{"StartTime":55831.0,"Position":242.375519,"HyperDash":false}]},{"StartTime":56056.0,"Objects":[{"StartTime":56056.0,"Position":329.0,"HyperDash":false},{"StartTime":56107.0,"Position":349.227417,"HyperDash":false},{"StartTime":56158.0,"Position":353.454865,"HyperDash":false},{"StartTime":56210.0,"Position":358.902435,"HyperDash":false},{"StartTime":56261.0,"Position":360.282623,"HyperDash":false},{"StartTime":56312.0,"Position":376.968658,"HyperDash":false},{"StartTime":56364.0,"Position":354.628937,"HyperDash":false},{"StartTime":56415.0,"Position":382.314972,"HyperDash":false},{"StartTime":56503.0,"Position":361.04776,"HyperDash":false}]},{"StartTime":56951.0,"Objects":[{"StartTime":56951.0,"Position":189.0,"HyperDash":false},{"StartTime":57044.0,"Position":142.707138,"HyperDash":false},{"StartTime":57174.0,"Position":111.099754,"HyperDash":false}]},{"StartTime":57399.0,"Objects":[{"StartTime":57399.0,"Position":44.0,"HyperDash":false},{"StartTime":57492.0,"Position":42.46508,"HyperDash":false},{"StartTime":57622.0,"Position":57.39981,"HyperDash":false}]},{"StartTime":57847.0,"Objects":[{"StartTime":57847.0,"Position":97.0,"HyperDash":false},{"StartTime":57898.0,"Position":128.653931,"HyperDash":false},{"StartTime":57949.0,"Position":137.733063,"HyperDash":false},{"StartTime":58001.0,"Position":141.3299,"HyperDash":false},{"StartTime":58052.0,"Position":175.3739,"HyperDash":false},{"StartTime":58103.0,"Position":188.865829,"HyperDash":false},{"StartTime":58155.0,"Position":184.813812,"HyperDash":false},{"StartTime":58206.0,"Position":222.592514,"HyperDash":false},{"StartTime":58294.0,"Position":246.818512,"HyperDash":false}]},{"StartTime":58742.0,"Objects":[{"StartTime":58742.0,"Position":396.0,"HyperDash":false},{"StartTime":58835.0,"Position":405.3873,"HyperDash":false},{"StartTime":58965.0,"Position":406.520081,"HyperDash":false}]},{"StartTime":59190.0,"Objects":[{"StartTime":59190.0,"Position":473.0,"HyperDash":false},{"StartTime":59283.0,"Position":484.6127,"HyperDash":false},{"StartTime":59413.0,"Position":462.479919,"HyperDash":false}]},{"StartTime":59638.0,"Objects":[{"StartTime":59638.0,"Position":450.0,"HyperDash":false},{"StartTime":59689.0,"Position":425.546051,"HyperDash":false},{"StartTime":59740.0,"Position":404.6286,"HyperDash":false},{"StartTime":59792.0,"Position":403.0906,"HyperDash":false},{"StartTime":59843.0,"Position":359.851471,"HyperDash":false},{"StartTime":59894.0,"Position":346.7696,"HyperDash":false},{"StartTime":59946.0,"Position":349.71637,"HyperDash":false},{"StartTime":59997.0,"Position":332.582275,"HyperDash":false},{"StartTime":60085.0,"Position":296.934937,"HyperDash":false}]},{"StartTime":60310.0,"Objects":[{"StartTime":60310.0,"Position":137.0,"HyperDash":false}]},{"StartTime":60534.0,"Objects":[{"StartTime":60534.0,"Position":127.0,"HyperDash":false},{"StartTime":60627.0,"Position":133.780716,"HyperDash":false},{"StartTime":60757.0,"Position":121.678482,"HyperDash":false}]},{"StartTime":60981.0,"Objects":[{"StartTime":60981.0,"Position":111.0,"HyperDash":false}]},{"StartTime":61429.0,"Objects":[{"StartTime":61429.0,"Position":110.0,"HyperDash":false},{"StartTime":61512.0,"Position":137.803375,"HyperDash":false},{"StartTime":61596.0,"Position":149.4081,"HyperDash":false},{"StartTime":61680.0,"Position":212.379776,"HyperDash":false},{"StartTime":61764.0,"Position":226.716034,"HyperDash":false},{"StartTime":61839.0,"Position":203.918869,"HyperDash":false},{"StartTime":61914.0,"Position":175.198227,"HyperDash":false},{"StartTime":61989.0,"Position":145.558578,"HyperDash":false},{"StartTime":62100.0,"Position":110.0,"HyperDash":false}]},{"StartTime":62325.0,"Objects":[{"StartTime":62325.0,"Position":22.0,"HyperDash":false},{"StartTime":62418.0,"Position":37.5815735,"HyperDash":false},{"StartTime":62548.0,"Position":18.5988235,"HyperDash":false}]},{"StartTime":62772.0,"Objects":[{"StartTime":62772.0,"Position":2.0,"HyperDash":false}]},{"StartTime":62996.0,"Objects":[{"StartTime":62996.0,"Position":76.0,"HyperDash":false}]},{"StartTime":63220.0,"Objects":[{"StartTime":63220.0,"Position":154.0,"HyperDash":false},{"StartTime":63313.0,"Position":199.111572,"HyperDash":false},{"StartTime":63443.0,"Position":232.57634,"HyperDash":false}]},{"StartTime":63668.0,"Objects":[{"StartTime":63668.0,"Position":307.0,"HyperDash":false},{"StartTime":63751.0,"Position":314.019135,"HyperDash":false},{"StartTime":63835.0,"Position":318.026459,"HyperDash":false},{"StartTime":63919.0,"Position":289.0338,"HyperDash":false},{"StartTime":64003.0,"Position":303.035217,"HyperDash":false},{"StartTime":64078.0,"Position":308.915619,"HyperDash":false},{"StartTime":64153.0,"Position":315.801941,"HyperDash":false},{"StartTime":64228.0,"Position":288.688263,"HyperDash":false},{"StartTime":64339.0,"Position":307.0,"HyperDash":false}]},{"StartTime":64563.0,"Objects":[{"StartTime":64563.0,"Position":311.0,"HyperDash":false},{"StartTime":64656.0,"Position":362.111572,"HyperDash":false},{"StartTime":64786.0,"Position":389.576324,"HyperDash":false}]},{"StartTime":65011.0,"Objects":[{"StartTime":65011.0,"Position":435.0,"HyperDash":false},{"StartTime":65062.0,"Position":440.232056,"HyperDash":false},{"StartTime":65113.0,"Position":422.4641,"HyperDash":false},{"StartTime":65165.0,"Position":444.6811,"HyperDash":false},{"StartTime":65216.0,"Position":423.913147,"HyperDash":false},{"StartTime":65267.0,"Position":441.145172,"HyperDash":false},{"StartTime":65319.0,"Position":427.362183,"HyperDash":false},{"StartTime":65370.0,"Position":412.594238,"HyperDash":false},{"StartTime":65458.0,"Position":428.269135,"HyperDash":false}]},{"StartTime":65683.0,"Objects":[{"StartTime":65683.0,"Position":350.0,"HyperDash":false},{"StartTime":65734.0,"Position":314.27713,"HyperDash":false},{"StartTime":65785.0,"Position":300.566528,"HyperDash":false},{"StartTime":65837.0,"Position":315.7566,"HyperDash":false},{"StartTime":65888.0,"Position":262.77713,"HyperDash":false},{"StartTime":65939.0,"Position":282.5542,"HyperDash":false},{"StartTime":65991.0,"Position":226.007614,"HyperDash":false},{"StartTime":66042.0,"Position":227.1106,"HyperDash":false},{"StartTime":66130.0,"Position":197.703339,"HyperDash":false}]},{"StartTime":66354.0,"Objects":[{"StartTime":66354.0,"Position":36.0,"HyperDash":false}]},{"StartTime":66802.0,"Objects":[{"StartTime":66802.0,"Position":44.0,"HyperDash":false},{"StartTime":66895.0,"Position":34.5778,"HyperDash":false},{"StartTime":67025.0,"Position":49.4306221,"HyperDash":false}]},{"StartTime":67250.0,"Objects":[{"StartTime":67250.0,"Position":131.0,"HyperDash":false},{"StartTime":67333.0,"Position":87.4688339,"HyperDash":false},{"StartTime":67417.0,"Position":59.51071,"HyperDash":false},{"StartTime":67501.0,"Position":67.3197,"HyperDash":false},{"StartTime":67585.0,"Position":34.3176,"HyperDash":false},{"StartTime":67660.0,"Position":32.04751,"HyperDash":false},{"StartTime":67735.0,"Position":85.74523,"HyperDash":false},{"StartTime":67810.0,"Position":81.77102,"HyperDash":false},{"StartTime":67921.0,"Position":131.0,"HyperDash":false}]},{"StartTime":68145.0,"Objects":[{"StartTime":68145.0,"Position":206.0,"HyperDash":false},{"StartTime":68238.0,"Position":241.281784,"HyperDash":false},{"StartTime":68368.0,"Position":285.804718,"HyperDash":false}]},{"StartTime":68593.0,"Objects":[{"StartTime":68593.0,"Position":354.0,"HyperDash":false},{"StartTime":68644.0,"Position":371.9797,"HyperDash":false},{"StartTime":68695.0,"Position":374.9594,"HyperDash":false},{"StartTime":68747.0,"Position":363.977966,"HyperDash":false},{"StartTime":68798.0,"Position":348.931732,"HyperDash":false},{"StartTime":68849.0,"Position":335.783875,"HyperDash":false},{"StartTime":68901.0,"Position":349.448822,"HyperDash":false},{"StartTime":68952.0,"Position":338.5818,"HyperDash":false},{"StartTime":69040.0,"Position":346.262146,"HyperDash":false}]},{"StartTime":69489.0,"Objects":[{"StartTime":69489.0,"Position":479.0,"HyperDash":false},{"StartTime":69582.0,"Position":463.7517,"HyperDash":false},{"StartTime":69712.0,"Position":471.2111,"HyperDash":false}]},{"StartTime":69936.0,"Objects":[{"StartTime":69936.0,"Position":395.0,"HyperDash":false},{"StartTime":70029.0,"Position":351.9091,"HyperDash":false},{"StartTime":70159.0,"Position":317.104523,"HyperDash":false}]},{"StartTime":70384.0,"Objects":[{"StartTime":70384.0,"Position":239.0,"HyperDash":false},{"StartTime":70435.0,"Position":235.932266,"HyperDash":false},{"StartTime":70486.0,"Position":206.714127,"HyperDash":false},{"StartTime":70538.0,"Position":191.116684,"HyperDash":false},{"StartTime":70589.0,"Position":179.00943,"HyperDash":false},{"StartTime":70640.0,"Position":139.19429,"HyperDash":false},{"StartTime":70692.0,"Position":141.486526,"HyperDash":false},{"StartTime":70743.0,"Position":106.327019,"HyperDash":false},{"StartTime":70831.0,"Position":87.14302,"HyperDash":false}]},{"StartTime":71280.0,"Objects":[{"StartTime":71280.0,"Position":11.0,"HyperDash":false},{"StartTime":71373.0,"Position":37.1006241,"HyperDash":false},{"StartTime":71503.0,"Position":90.3703156,"HyperDash":false}]},{"StartTime":71728.0,"Objects":[{"StartTime":71728.0,"Position":152.0,"HyperDash":false},{"StartTime":71821.0,"Position":193.100616,"HyperDash":false},{"StartTime":71951.0,"Position":231.370316,"HyperDash":false}]},{"StartTime":72175.0,"Objects":[{"StartTime":72175.0,"Position":271.0,"HyperDash":false},{"StartTime":72226.0,"Position":263.6878,"HyperDash":false},{"StartTime":72277.0,"Position":283.464,"HyperDash":false},{"StartTime":72329.0,"Position":257.4186,"HyperDash":false},{"StartTime":72380.0,"Position":278.35257,"HyperDash":false},{"StartTime":72431.0,"Position":304.125275,"HyperDash":false},{"StartTime":72483.0,"Position":296.814362,"HyperDash":false},{"StartTime":72534.0,"Position":316.538055,"HyperDash":false},{"StartTime":72622.0,"Position":338.266479,"HyperDash":false}]},{"StartTime":72847.0,"Objects":[{"StartTime":72847.0,"Position":505.0,"HyperDash":false}]},{"StartTime":73071.0,"Objects":[{"StartTime":73071.0,"Position":489.0,"HyperDash":false},{"StartTime":73164.0,"Position":469.365631,"HyperDash":false},{"StartTime":73294.0,"Position":482.683167,"HyperDash":false}]},{"StartTime":73519.0,"Objects":[{"StartTime":73519.0,"Position":408.0,"HyperDash":false},{"StartTime":73612.0,"Position":403.634369,"HyperDash":false},{"StartTime":73742.0,"Position":414.316833,"HyperDash":false}]},{"StartTime":73966.0,"Objects":[{"StartTime":73966.0,"Position":482.0,"HyperDash":false},{"StartTime":74017.0,"Position":472.133667,"HyperDash":false},{"StartTime":74068.0,"Position":425.9474,"HyperDash":false},{"StartTime":74120.0,"Position":412.437225,"HyperDash":false},{"StartTime":74171.0,"Position":412.766479,"HyperDash":false},{"StartTime":74222.0,"Position":404.367828,"HyperDash":false},{"StartTime":74274.0,"Position":384.1732,"HyperDash":false},{"StartTime":74325.0,"Position":361.954468,"HyperDash":false},{"StartTime":74413.0,"Position":325.429016,"HyperDash":false}]},{"StartTime":74862.0,"Objects":[{"StartTime":74862.0,"Position":157.0,"HyperDash":false},{"StartTime":74917.0,"Position":132.397827,"HyperDash":false},{"StartTime":74973.0,"Position":108.439255,"HyperDash":false},{"StartTime":75029.0,"Position":111.480682,"HyperDash":false},{"StartTime":75085.0,"Position":77.3439,"HyperDash":false},{"StartTime":75179.0,"Position":113.667587,"HyperDash":false},{"StartTime":75309.0,"Position":157.0,"HyperDash":false}]},{"StartTime":75534.0,"Objects":[{"StartTime":75534.0,"Position":381.0,"HyperDash":false}]},{"StartTime":75757.0,"Objects":[{"StartTime":75757.0,"Position":288.0,"HyperDash":false},{"StartTime":75812.0,"Position":322.1354,"HyperDash":false},{"StartTime":75868.0,"Position":327.117,"HyperDash":false},{"StartTime":75924.0,"Position":334.290924,"HyperDash":false},{"StartTime":75980.0,"Position":378.117737,"HyperDash":false},{"StartTime":76036.0,"Position":383.1031,"HyperDash":false},{"StartTime":76092.0,"Position":388.735718,"HyperDash":false},{"StartTime":76148.0,"Position":435.4911,"HyperDash":false},{"StartTime":76204.0,"Position":437.060059,"HyperDash":false},{"StartTime":76255.0,"Position":440.4263,"HyperDash":false},{"StartTime":76307.0,"Position":393.159027,"HyperDash":false},{"StartTime":76358.0,"Position":398.4255,"HyperDash":false},{"StartTime":76410.0,"Position":353.908081,"HyperDash":false},{"StartTime":76461.0,"Position":365.736755,"HyperDash":false},{"StartTime":76513.0,"Position":343.5878,"HyperDash":false},{"StartTime":76564.0,"Position":302.556519,"HyperDash":false},{"StartTime":76652.0,"Position":288.0,"HyperDash":false}]},{"StartTime":76877.0,"Objects":[{"StartTime":76877.0,"Position":225.0,"HyperDash":false},{"StartTime":76932.0,"Position":237.844727,"HyperDash":false},{"StartTime":76988.0,"Position":244.722977,"HyperDash":false},{"StartTime":77044.0,"Position":249.601242,"HyperDash":false},{"StartTime":77100.0,"Position":232.496277,"HyperDash":false},{"StartTime":77194.0,"Position":239.360245,"HyperDash":false},{"StartTime":77324.0,"Position":225.0,"HyperDash":false}]},{"StartTime":77548.0,"Objects":[{"StartTime":77548.0,"Position":172.0,"HyperDash":false},{"StartTime":77599.0,"Position":161.128448,"HyperDash":false},{"StartTime":77650.0,"Position":135.2569,"HyperDash":false},{"StartTime":77702.0,"Position":147.846878,"HyperDash":false},{"StartTime":77753.0,"Position":143.823837,"HyperDash":false},{"StartTime":77804.0,"Position":137.800812,"HyperDash":false},{"StartTime":77856.0,"Position":146.836151,"HyperDash":false},{"StartTime":77907.0,"Position":164.81311,"HyperDash":false},{"StartTime":77995.0,"Position":162.949844,"HyperDash":false}]},{"StartTime":78444.0,"Objects":[{"StartTime":78444.0,"Position":9.0,"HyperDash":false},{"StartTime":78495.0,"Position":32.8715477,"HyperDash":false},{"StartTime":78546.0,"Position":21.7430954,"HyperDash":false},{"StartTime":78598.0,"Position":50.15313,"HyperDash":false},{"StartTime":78649.0,"Position":21.1761589,"HyperDash":false},{"StartTime":78700.0,"Position":17.19919,"HyperDash":false},{"StartTime":78752.0,"Position":41.16385,"HyperDash":false},{"StartTime":78803.0,"Position":32.186882,"HyperDash":false},{"StartTime":78891.0,"Position":18.05015,"HyperDash":false}]},{"StartTime":79339.0,"Objects":[{"StartTime":79339.0,"Position":186.0,"HyperDash":false},{"StartTime":79390.0,"Position":199.306229,"HyperDash":false},{"StartTime":79441.0,"Position":219.682114,"HyperDash":false},{"StartTime":79493.0,"Position":224.118561,"HyperDash":false},{"StartTime":79544.0,"Position":227.689743,"HyperDash":false},{"StartTime":79595.0,"Position":241.25592,"HyperDash":false},{"StartTime":79647.0,"Position":265.72113,"HyperDash":false},{"StartTime":79698.0,"Position":285.940369,"HyperDash":false},{"StartTime":79786.0,"Position":327.296021,"HyperDash":false}]},{"StartTime":80011.0,"Objects":[{"StartTime":80011.0,"Position":461.0,"HyperDash":false}]},{"StartTime":80235.0,"Objects":[{"StartTime":80235.0,"Position":482.0,"HyperDash":false},{"StartTime":80328.0,"Position":471.961243,"HyperDash":false},{"StartTime":80458.0,"Position":472.315643,"HyperDash":false}]},{"StartTime":80683.0,"Objects":[{"StartTime":80683.0,"Position":392.0,"HyperDash":false},{"StartTime":80776.0,"Position":394.038757,"HyperDash":false},{"StartTime":80906.0,"Position":401.684357,"HyperDash":false}]},{"StartTime":81131.0,"Objects":[{"StartTime":81131.0,"Position":474.0,"HyperDash":false},{"StartTime":81182.0,"Position":450.511719,"HyperDash":false},{"StartTime":81233.0,"Position":460.8919,"HyperDash":false},{"StartTime":81285.0,"Position":418.0802,"HyperDash":false},{"StartTime":81336.0,"Position":403.0688,"HyperDash":false},{"StartTime":81387.0,"Position":402.833557,"HyperDash":false},{"StartTime":81439.0,"Position":375.354675,"HyperDash":false},{"StartTime":81490.0,"Position":367.6674,"HyperDash":false},{"StartTime":81578.0,"Position":323.0545,"HyperDash":false}]},{"StartTime":82026.0,"Objects":[{"StartTime":82026.0,"Position":148.0,"HyperDash":false},{"StartTime":82077.0,"Position":153.363663,"HyperDash":false},{"StartTime":82128.0,"Position":124.853226,"HyperDash":false},{"StartTime":82180.0,"Position":123.51664,"HyperDash":false},{"StartTime":82231.0,"Position":135.651062,"HyperDash":false},{"StartTime":82282.0,"Position":107.183319,"HyperDash":false},{"StartTime":82334.0,"Position":111.284645,"HyperDash":false},{"StartTime":82385.0,"Position":125.730865,"HyperDash":false},{"StartTime":82473.0,"Position":141.718521,"HyperDash":false}]},{"StartTime":82922.0,"Objects":[{"StartTime":82922.0,"Position":287.0,"HyperDash":false},{"StartTime":82977.0,"Position":306.298553,"HyperDash":false},{"StartTime":83033.0,"Position":321.504669,"HyperDash":false},{"StartTime":83089.0,"Position":339.161163,"HyperDash":false},{"StartTime":83145.0,"Position":367.092316,"HyperDash":false},{"StartTime":83201.0,"Position":394.0961,"HyperDash":false},{"StartTime":83257.0,"Position":406.969055,"HyperDash":false},{"StartTime":83313.0,"Position":412.5311,"HyperDash":false},{"StartTime":83369.0,"Position":442.643555,"HyperDash":false},{"StartTime":83425.0,"Position":445.5296,"HyperDash":false},{"StartTime":83481.0,"Position":420.6327,"HyperDash":false},{"StartTime":83537.0,"Position":415.785919,"HyperDash":false},{"StartTime":83593.0,"Position":369.41394,"HyperDash":false},{"StartTime":83649.0,"Position":368.030121,"HyperDash":false},{"StartTime":83705.0,"Position":376.311218,"HyperDash":false},{"StartTime":83761.0,"Position":349.831451,"HyperDash":false},{"StartTime":83817.0,"Position":357.0095,"HyperDash":false},{"StartTime":83868.0,"Position":377.510834,"HyperDash":false},{"StartTime":83920.0,"Position":394.548126,"HyperDash":false},{"StartTime":83971.0,"Position":406.9447,"HyperDash":false},{"StartTime":84023.0,"Position":383.802063,"HyperDash":false},{"StartTime":84074.0,"Position":391.380249,"HyperDash":false},{"StartTime":84126.0,"Position":407.693,"HyperDash":false},{"StartTime":84177.0,"Position":408.468567,"HyperDash":false},{"StartTime":84265.0,"Position":418.7769,"HyperDash":false}]},{"StartTime":84713.0,"Objects":[{"StartTime":84713.0,"Position":242.0,"HyperDash":false},{"StartTime":84796.0,"Position":214.531952,"HyperDash":false},{"StartTime":84880.0,"Position":201.708862,"HyperDash":false},{"StartTime":84964.0,"Position":158.885773,"HyperDash":false},{"StartTime":85048.0,"Position":122.885155,"HyperDash":false},{"StartTime":85123.0,"Position":159.3354,"HyperDash":false},{"StartTime":85198.0,"Position":171.963165,"HyperDash":false},{"StartTime":85273.0,"Position":220.590912,"HyperDash":false},{"StartTime":85384.0,"Position":242.0,"HyperDash":false}]},{"StartTime":85608.0,"Objects":[{"StartTime":85608.0,"Position":277.0,"HyperDash":false},{"StartTime":85659.0,"Position":273.8022,"HyperDash":false},{"StartTime":85710.0,"Position":272.42923,"HyperDash":false},{"StartTime":85762.0,"Position":256.8426,"HyperDash":false},{"StartTime":85813.0,"Position":245.819153,"HyperDash":false},{"StartTime":85864.0,"Position":210.419479,"HyperDash":false},{"StartTime":85916.0,"Position":177.694885,"HyperDash":false},{"StartTime":85967.0,"Position":180.692947,"HyperDash":false},{"StartTime":86055.0,"Position":144.3092,"HyperDash":false}]},{"StartTime":86504.0,"Objects":[{"StartTime":86504.0,"Position":11.0,"HyperDash":false}]},{"StartTime":93668.0,"Objects":[{"StartTime":93668.0,"Position":321.0,"HyperDash":false},{"StartTime":93723.0,"Position":305.388947,"HyperDash":false},{"StartTime":93779.0,"Position":291.399963,"HyperDash":false},{"StartTime":93835.0,"Position":280.52063,"HyperDash":false},{"StartTime":93891.0,"Position":248.606445,"HyperDash":false},{"StartTime":93947.0,"Position":235.53479,"HyperDash":false},{"StartTime":94003.0,"Position":224.107117,"HyperDash":false},{"StartTime":94059.0,"Position":224.84407,"HyperDash":false},{"StartTime":94115.0,"Position":200.017365,"HyperDash":false},{"StartTime":94171.0,"Position":199.067291,"HyperDash":false},{"StartTime":94227.0,"Position":212.384537,"HyperDash":false},{"StartTime":94283.0,"Position":199.112579,"HyperDash":false},{"StartTime":94339.0,"Position":222.5897,"HyperDash":false},{"StartTime":94395.0,"Position":253.0729,"HyperDash":false},{"StartTime":94451.0,"Position":253.947144,"HyperDash":false},{"StartTime":94507.0,"Position":271.304932,"HyperDash":false},{"StartTime":94563.0,"Position":305.412964,"HyperDash":false},{"StartTime":94619.0,"Position":307.6401,"HyperDash":false},{"StartTime":94675.0,"Position":267.302582,"HyperDash":false},{"StartTime":94731.0,"Position":251.416916,"HyperDash":false},{"StartTime":94787.0,"Position":222.898773,"HyperDash":false},{"StartTime":94843.0,"Position":211.3582,"HyperDash":false},{"StartTime":94899.0,"Position":213.529022,"HyperDash":false},{"StartTime":94955.0,"Position":210.1259,"HyperDash":false},{"StartTime":95011.0,"Position":199.942,"HyperDash":false},{"StartTime":95062.0,"Position":191.884583,"HyperDash":false},{"StartTime":95114.0,"Position":201.545059,"HyperDash":false},{"StartTime":95165.0,"Position":236.775665,"HyperDash":false},{"StartTime":95217.0,"Position":265.954834,"HyperDash":false},{"StartTime":95268.0,"Position":272.007324,"HyperDash":false},{"StartTime":95320.0,"Position":299.217743,"HyperDash":false},{"StartTime":95371.0,"Position":310.421265,"HyperDash":false},{"StartTime":95459.0,"Position":321.0,"HyperDash":false}]},{"StartTime":97250.0,"Objects":[{"StartTime":97250.0,"Position":321.0,"HyperDash":false},{"StartTime":97305.0,"Position":349.148315,"HyperDash":false},{"StartTime":97361.0,"Position":367.604675,"HyperDash":false},{"StartTime":97417.0,"Position":379.5581,"HyperDash":false},{"StartTime":97473.0,"Position":395.380951,"HyperDash":false},{"StartTime":97529.0,"Position":430.504242,"HyperDash":false},{"StartTime":97585.0,"Position":442.613251,"HyperDash":false},{"StartTime":97641.0,"Position":458.59317,"HyperDash":false},{"StartTime":97697.0,"Position":467.732544,"HyperDash":false},{"StartTime":97753.0,"Position":444.03418,"HyperDash":false},{"StartTime":97809.0,"Position":450.705536,"HyperDash":false},{"StartTime":97865.0,"Position":456.036621,"HyperDash":false},{"StartTime":97921.0,"Position":460.436,"HyperDash":false},{"StartTime":97977.0,"Position":445.266327,"HyperDash":false},{"StartTime":98033.0,"Position":456.866272,"HyperDash":false},{"StartTime":98089.0,"Position":449.4119,"HyperDash":false},{"StartTime":98145.0,"Position":462.917175,"HyperDash":false},{"StartTime":98201.0,"Position":468.532471,"HyperDash":false},{"StartTime":98257.0,"Position":451.935547,"HyperDash":false},{"StartTime":98313.0,"Position":433.2847,"HyperDash":false},{"StartTime":98369.0,"Position":426.406769,"HyperDash":false},{"StartTime":98425.0,"Position":449.975067,"HyperDash":false},{"StartTime":98481.0,"Position":460.606018,"HyperDash":false},{"StartTime":98537.0,"Position":447.910065,"HyperDash":false},{"StartTime":98593.0,"Position":467.586945,"HyperDash":false},{"StartTime":98644.0,"Position":441.353149,"HyperDash":false},{"StartTime":98696.0,"Position":439.723267,"HyperDash":false},{"StartTime":98747.0,"Position":415.4601,"HyperDash":false},{"StartTime":98799.0,"Position":412.9643,"HyperDash":false},{"StartTime":98850.0,"Position":398.1049,"HyperDash":false},{"StartTime":98902.0,"Position":376.557465,"HyperDash":false},{"StartTime":98953.0,"Position":359.5229,"HyperDash":false},{"StartTime":99041.0,"Position":321.0,"HyperDash":false}]},{"StartTime":100832.0,"Objects":[{"StartTime":100832.0,"Position":321.0,"HyperDash":false},{"StartTime":100887.0,"Position":321.469482,"HyperDash":false},{"StartTime":100943.0,"Position":295.742432,"HyperDash":false},{"StartTime":100999.0,"Position":267.1522,"HyperDash":false},{"StartTime":101055.0,"Position":261.835083,"HyperDash":false},{"StartTime":101111.0,"Position":216.475037,"HyperDash":false},{"StartTime":101167.0,"Position":227.328217,"HyperDash":false},{"StartTime":101223.0,"Position":189.1814,"HyperDash":false},{"StartTime":101279.0,"Position":176.034576,"HyperDash":false},{"StartTime":101335.0,"Position":138.745392,"HyperDash":false},{"StartTime":101391.0,"Position":148.387146,"HyperDash":false},{"StartTime":101447.0,"Position":103.028908,"HyperDash":false},{"StartTime":101503.0,"Position":107.670639,"HyperDash":false},{"StartTime":101559.0,"Position":73.03934,"HyperDash":false},{"StartTime":101615.0,"Position":45.58867,"HyperDash":false},{"StartTime":101671.0,"Position":34.2676964,"HyperDash":false},{"StartTime":101727.0,"Position":31.1845322,"HyperDash":false},{"StartTime":101783.0,"Position":59.98834,"HyperDash":false},{"StartTime":101839.0,"Position":63.2845459,"HyperDash":false},{"StartTime":101895.0,"Position":71.71911,"HyperDash":false},{"StartTime":101951.0,"Position":103.324966,"HyperDash":false},{"StartTime":102007.0,"Position":111.683212,"HyperDash":false},{"StartTime":102063.0,"Position":126.041473,"HyperDash":false},{"StartTime":102119.0,"Position":162.399689,"HyperDash":false},{"StartTime":102175.0,"Position":175.71051,"HyperDash":false},{"StartTime":102226.0,"Position":204.237076,"HyperDash":false},{"StartTime":102278.0,"Position":214.0877,"HyperDash":false},{"StartTime":102329.0,"Position":210.614273,"HyperDash":false},{"StartTime":102381.0,"Position":249.4649,"HyperDash":false},{"StartTime":102432.0,"Position":251.954224,"HyperDash":false},{"StartTime":102484.0,"Position":265.549072,"HyperDash":false},{"StartTime":102535.0,"Position":284.1342,"HyperDash":false},{"StartTime":102623.0,"Position":321.0,"HyperDash":false}]},{"StartTime":102847.0,"Objects":[{"StartTime":102847.0,"Position":385.0,"HyperDash":false}]},{"StartTime":103071.0,"Objects":[{"StartTime":103071.0,"Position":322.0,"HyperDash":false},{"StartTime":103154.0,"Position":309.4082,"HyperDash":false},{"StartTime":103238.0,"Position":253.459869,"HyperDash":false},{"StartTime":103322.0,"Position":230.511536,"HyperDash":false},{"StartTime":103406.0,"Position":202.384949,"HyperDash":false},{"StartTime":103481.0,"Position":227.946259,"HyperDash":false},{"StartTime":103556.0,"Position":269.685852,"HyperDash":false},{"StartTime":103631.0,"Position":282.4254,"HyperDash":false},{"StartTime":103742.0,"Position":322.0,"HyperDash":false}]},{"StartTime":103966.0,"Objects":[{"StartTime":103966.0,"Position":404.0,"HyperDash":false},{"StartTime":104059.0,"Position":389.203644,"HyperDash":false},{"StartTime":104189.0,"Position":389.111877,"HyperDash":false}]},{"StartTime":104414.0,"Objects":[{"StartTime":104414.0,"Position":308.0,"HyperDash":false},{"StartTime":104507.0,"Position":288.7421,"HyperDash":false},{"StartTime":104637.0,"Position":230.940414,"HyperDash":false}]},{"StartTime":104862.0,"Objects":[{"StartTime":104862.0,"Position":164.0,"HyperDash":false},{"StartTime":104945.0,"Position":150.511658,"HyperDash":false},{"StartTime":105029.0,"Position":96.6680145,"HyperDash":false},{"StartTime":105113.0,"Position":58.8243866,"HyperDash":false},{"StartTime":105197.0,"Position":44.8031158,"HyperDash":false},{"StartTime":105272.0,"Position":73.2715759,"HyperDash":false},{"StartTime":105347.0,"Position":112.917679,"HyperDash":false},{"StartTime":105422.0,"Position":127.563766,"HyperDash":false},{"StartTime":105533.0,"Position":164.0,"HyperDash":false}]},{"StartTime":105757.0,"Objects":[{"StartTime":105757.0,"Position":369.0,"HyperDash":false}]},{"StartTime":106205.0,"Objects":[{"StartTime":106205.0,"Position":276.0,"HyperDash":false},{"StartTime":106260.0,"Position":301.5404,"HyperDash":false},{"StartTime":106316.0,"Position":299.28067,"HyperDash":false},{"StartTime":106372.0,"Position":337.27,"HyperDash":false},{"StartTime":106428.0,"Position":348.8408,"HyperDash":false},{"StartTime":106484.0,"Position":372.279419,"HyperDash":false},{"StartTime":106540.0,"Position":407.057281,"HyperDash":false},{"StartTime":106596.0,"Position":399.472778,"HyperDash":false},{"StartTime":106652.0,"Position":415.2087,"HyperDash":false},{"StartTime":106746.0,"Position":444.522675,"HyperDash":false},{"StartTime":106876.0,"Position":427.9771,"HyperDash":false}]},{"StartTime":107101.0,"Objects":[{"StartTime":107101.0,"Position":354.0,"HyperDash":false},{"StartTime":107156.0,"Position":351.361053,"HyperDash":false},{"StartTime":107212.0,"Position":319.711761,"HyperDash":false},{"StartTime":107268.0,"Position":312.725647,"HyperDash":false},{"StartTime":107324.0,"Position":292.795166,"HyperDash":false},{"StartTime":107380.0,"Position":257.278931,"HyperDash":false},{"StartTime":107436.0,"Position":250.434189,"HyperDash":false},{"StartTime":107492.0,"Position":228.3952,"HyperDash":false},{"StartTime":107548.0,"Position":202.1942,"HyperDash":false},{"StartTime":107642.0,"Position":183.650848,"HyperDash":false},{"StartTime":107772.0,"Position":130.2209,"HyperDash":false}]},{"StartTime":107996.0,"Objects":[{"StartTime":107996.0,"Position":55.0,"HyperDash":false}]},{"StartTime":108220.0,"Objects":[{"StartTime":108220.0,"Position":0.0,"HyperDash":false}]},{"StartTime":108444.0,"Objects":[{"StartTime":108444.0,"Position":43.0,"HyperDash":false},{"StartTime":108527.0,"Position":26.517498,"HyperDash":false},{"StartTime":108611.0,"Position":31.01714,"HyperDash":false},{"StartTime":108695.0,"Position":26.516777,"HyperDash":false},{"StartTime":108779.0,"Position":37.0074844,"HyperDash":false},{"StartTime":108854.0,"Position":40.33816,"HyperDash":false},{"StartTime":108929.0,"Position":23.6777725,"HyperDash":false},{"StartTime":109004.0,"Position":46.01738,"HyperDash":false},{"StartTime":109115.0,"Position":43.0,"HyperDash":false}]},{"StartTime":109339.0,"Objects":[{"StartTime":109339.0,"Position":128.0,"HyperDash":false},{"StartTime":109432.0,"Position":177.210678,"HyperDash":false},{"StartTime":109562.0,"Position":204.080414,"HyperDash":false}]},{"StartTime":109787.0,"Objects":[{"StartTime":109787.0,"Position":242.0,"HyperDash":false},{"StartTime":109842.0,"Position":213.635727,"HyperDash":false},{"StartTime":109898.0,"Position":229.2922,"HyperDash":false},{"StartTime":109954.0,"Position":229.500839,"HyperDash":false},{"StartTime":110010.0,"Position":245.173721,"HyperDash":false},{"StartTime":110066.0,"Position":240.366425,"HyperDash":false},{"StartTime":110122.0,"Position":243.8476,"HyperDash":false},{"StartTime":110178.0,"Position":253.385529,"HyperDash":false},{"StartTime":110234.0,"Position":267.757416,"HyperDash":false},{"StartTime":110285.0,"Position":252.804428,"HyperDash":false},{"StartTime":110337.0,"Position":250.689026,"HyperDash":false},{"StartTime":110388.0,"Position":223.27919,"HyperDash":false},{"StartTime":110440.0,"Position":223.56842,"HyperDash":false},{"StartTime":110491.0,"Position":243.800873,"HyperDash":false},{"StartTime":110543.0,"Position":223.941116,"HyperDash":false},{"StartTime":110594.0,"Position":226.059952,"HyperDash":false},{"StartTime":110682.0,"Position":242.0,"HyperDash":false}]},{"StartTime":111131.0,"Objects":[{"StartTime":111131.0,"Position":411.0,"HyperDash":false}]},{"StartTime":111578.0,"Objects":[{"StartTime":111578.0,"Position":503.0,"HyperDash":false},{"StartTime":111629.0,"Position":490.995636,"HyperDash":false},{"StartTime":111680.0,"Position":478.9913,"HyperDash":false},{"StartTime":111732.0,"Position":511.947632,"HyperDash":false},{"StartTime":111783.0,"Position":502.9433,"HyperDash":false},{"StartTime":111834.0,"Position":488.938934,"HyperDash":false},{"StartTime":111886.0,"Position":497.8953,"HyperDash":false},{"StartTime":111937.0,"Position":485.89093,"HyperDash":false},{"StartTime":112025.0,"Position":485.432434,"HyperDash":false}]},{"StartTime":112250.0,"Objects":[{"StartTime":112250.0,"Position":326.0,"HyperDash":false}]},{"StartTime":112474.0,"Objects":[{"StartTime":112474.0,"Position":333.0,"HyperDash":false},{"StartTime":112567.0,"Position":318.79068,"HyperDash":false},{"StartTime":112697.0,"Position":253.369049,"HyperDash":false}]},{"StartTime":112922.0,"Objects":[{"StartTime":112922.0,"Position":175.0,"HyperDash":false},{"StartTime":113015.0,"Position":142.79068,"HyperDash":false},{"StartTime":113145.0,"Position":95.36904,"HyperDash":false}]},{"StartTime":113369.0,"Objects":[{"StartTime":113369.0,"Position":28.0,"HyperDash":false},{"StartTime":113424.0,"Position":14.5683556,"HyperDash":false},{"StartTime":113480.0,"Position":0.0,"HyperDash":false},{"StartTime":113536.0,"Position":28.3534565,"HyperDash":false},{"StartTime":113592.0,"Position":8.926472,"HyperDash":false},{"StartTime":113648.0,"Position":14.8988361,"HyperDash":false},{"StartTime":113704.0,"Position":13.3887749,"HyperDash":false},{"StartTime":113760.0,"Position":28.1702747,"HyperDash":false},{"StartTime":113816.0,"Position":34.34165,"HyperDash":false},{"StartTime":113867.0,"Position":36.0318,"HyperDash":false},{"StartTime":113919.0,"Position":12.4058609,"HyperDash":false},{"StartTime":113970.0,"Position":10.89321,"HyperDash":false},{"StartTime":114022.0,"Position":0.0,"HyperDash":false},{"StartTime":114073.0,"Position":10.8660927,"HyperDash":false},{"StartTime":114125.0,"Position":28.46455,"HyperDash":false},{"StartTime":114176.0,"Position":10.1406345,"HyperDash":false},{"StartTime":114264.0,"Position":28.0,"HyperDash":false}]},{"StartTime":114713.0,"Objects":[{"StartTime":114713.0,"Position":190.0,"HyperDash":false}]},{"StartTime":115160.0,"Objects":[{"StartTime":115160.0,"Position":349.0,"HyperDash":false},{"StartTime":115215.0,"Position":385.515045,"HyperDash":false},{"StartTime":115271.0,"Position":399.481323,"HyperDash":false},{"StartTime":115327.0,"Position":411.652283,"HyperDash":false},{"StartTime":115383.0,"Position":433.181549,"HyperDash":false},{"StartTime":115439.0,"Position":451.6266,"HyperDash":false},{"StartTime":115495.0,"Position":475.6881,"HyperDash":false},{"StartTime":115551.0,"Position":468.64,"HyperDash":false},{"StartTime":115607.0,"Position":501.696655,"HyperDash":false},{"StartTime":115658.0,"Position":478.782867,"HyperDash":false},{"StartTime":115710.0,"Position":452.2243,"HyperDash":false},{"StartTime":115761.0,"Position":461.569977,"HyperDash":false},{"StartTime":115813.0,"Position":418.9793,"HyperDash":false},{"StartTime":115864.0,"Position":433.325836,"HyperDash":false},{"StartTime":115916.0,"Position":398.248627,"HyperDash":false},{"StartTime":115967.0,"Position":379.308319,"HyperDash":false},{"StartTime":116055.0,"Position":349.0,"HyperDash":false}]},{"StartTime":116280.0,"Objects":[{"StartTime":116280.0,"Position":265.0,"HyperDash":false}]},{"StartTime":116504.0,"Objects":[{"StartTime":116504.0,"Position":224.0,"HyperDash":false},{"StartTime":116597.0,"Position":239.949112,"HyperDash":false},{"StartTime":116727.0,"Position":235.867233,"HyperDash":false}]},{"StartTime":116951.0,"Objects":[{"StartTime":116951.0,"Position":320.0,"HyperDash":false},{"StartTime":117002.0,"Position":342.006653,"HyperDash":false},{"StartTime":117053.0,"Position":362.0133,"HyperDash":false},{"StartTime":117105.0,"Position":374.373047,"HyperDash":false},{"StartTime":117156.0,"Position":403.3797,"HyperDash":false},{"StartTime":117207.0,"Position":400.386353,"HyperDash":false},{"StartTime":117259.0,"Position":441.7282,"HyperDash":false},{"StartTime":117310.0,"Position":459.303955,"HyperDash":false},{"StartTime":117398.0,"Position":476.6307,"HyperDash":false}]},{"StartTime":117847.0,"Objects":[{"StartTime":117847.0,"Position":501.0,"HyperDash":false},{"StartTime":117898.0,"Position":485.993347,"HyperDash":false},{"StartTime":117949.0,"Position":475.9867,"HyperDash":false},{"StartTime":118001.0,"Position":440.626953,"HyperDash":false},{"StartTime":118052.0,"Position":413.6203,"HyperDash":false},{"StartTime":118103.0,"Position":415.613647,"HyperDash":false},{"StartTime":118155.0,"Position":403.2718,"HyperDash":false},{"StartTime":118206.0,"Position":374.696045,"HyperDash":false},{"StartTime":118294.0,"Position":344.3693,"HyperDash":false}]},{"StartTime":118742.0,"Objects":[{"StartTime":118742.0,"Position":200.0,"HyperDash":false},{"StartTime":118793.0,"Position":169.013748,"HyperDash":false},{"StartTime":118844.0,"Position":149.781937,"HyperDash":false},{"StartTime":118896.0,"Position":136.378891,"HyperDash":false},{"StartTime":118947.0,"Position":111.942886,"HyperDash":false},{"StartTime":118998.0,"Position":96.68911,"HyperDash":false},{"StartTime":119050.0,"Position":81.5406,"HyperDash":false},{"StartTime":119101.0,"Position":83.7234955,"HyperDash":false},{"StartTime":119189.0,"Position":45.337368,"HyperDash":false}]},{"StartTime":119638.0,"Objects":[{"StartTime":119638.0,"Position":16.0,"HyperDash":false},{"StartTime":119721.0,"Position":11.22777,"HyperDash":false},{"StartTime":119805.0,"Position":40.49443,"HyperDash":false},{"StartTime":119889.0,"Position":35.76109,"HyperDash":false},{"StartTime":119973.0,"Position":29.0471916,"HyperDash":false},{"StartTime":120048.0,"Position":34.1499748,"HyperDash":false},{"StartTime":120123.0,"Position":6.23331642,"HyperDash":false},{"StartTime":120198.0,"Position":34.316658,"HyperDash":false},{"StartTime":120309.0,"Position":16.0,"HyperDash":false}]},{"StartTime":120534.0,"Objects":[{"StartTime":120534.0,"Position":88.0,"HyperDash":false},{"StartTime":120589.0,"Position":99.09209,"HyperDash":false},{"StartTime":120645.0,"Position":138.513123,"HyperDash":false},{"StartTime":120701.0,"Position":128.008957,"HyperDash":false},{"StartTime":120757.0,"Position":153.9049,"HyperDash":false},{"StartTime":120813.0,"Position":191.800842,"HyperDash":false},{"StartTime":120869.0,"Position":199.696777,"HyperDash":false},{"StartTime":120925.0,"Position":239.592712,"HyperDash":false},{"StartTime":120981.0,"Position":242.66629,"HyperDash":false},{"StartTime":121032.0,"Position":217.724426,"HyperDash":false},{"StartTime":121084.0,"Position":218.249634,"HyperDash":false},{"StartTime":121135.0,"Position":197.130127,"HyperDash":false},{"StartTime":121187.0,"Position":167.6553,"HyperDash":false},{"StartTime":121238.0,"Position":142.5358,"HyperDash":false},{"StartTime":121290.0,"Position":147.723633,"HyperDash":false},{"StartTime":121341.0,"Position":129.947342,"HyperDash":false},{"StartTime":121429.0,"Position":88.0,"HyperDash":false}]},{"StartTime":121877.0,"Objects":[{"StartTime":121877.0,"Position":172.0,"HyperDash":false}]},{"StartTime":122325.0,"Objects":[{"StartTime":122325.0,"Position":322.0,"HyperDash":false},{"StartTime":122376.0,"Position":324.2495,"HyperDash":false},{"StartTime":122427.0,"Position":355.02713,"HyperDash":false},{"StartTime":122479.0,"Position":331.196777,"HyperDash":false},{"StartTime":122530.0,"Position":366.3613,"HyperDash":false},{"StartTime":122581.0,"Position":338.580322,"HyperDash":false},{"StartTime":122633.0,"Position":352.663971,"HyperDash":false},{"StartTime":122684.0,"Position":329.8923,"HyperDash":false},{"StartTime":122772.0,"Position":326.6841,"HyperDash":false}]},{"StartTime":123220.0,"Objects":[{"StartTime":123220.0,"Position":150.0,"HyperDash":false},{"StartTime":123271.0,"Position":143.7505,"HyperDash":false},{"StartTime":123322.0,"Position":113.97287,"HyperDash":false},{"StartTime":123374.0,"Position":108.803215,"HyperDash":false},{"StartTime":123425.0,"Position":131.6387,"HyperDash":false},{"StartTime":123476.0,"Position":132.419678,"HyperDash":false},{"StartTime":123528.0,"Position":126.336021,"HyperDash":false},{"StartTime":123579.0,"Position":126.1077,"HyperDash":false},{"StartTime":123667.0,"Position":145.315887,"HyperDash":false}]},{"StartTime":123892.0,"Objects":[{"StartTime":123892.0,"Position":238.0,"HyperDash":false}]},{"StartTime":124116.0,"Objects":[{"StartTime":124116.0,"Position":277.0,"HyperDash":false},{"StartTime":124171.0,"Position":313.7125,"HyperDash":false},{"StartTime":124227.0,"Position":317.3544,"HyperDash":false},{"StartTime":124283.0,"Position":342.320557,"HyperDash":false},{"StartTime":124339.0,"Position":372.249237,"HyperDash":false},{"StartTime":124395.0,"Position":379.760651,"HyperDash":false},{"StartTime":124451.0,"Position":399.5314,"HyperDash":false},{"StartTime":124507.0,"Position":411.37323,"HyperDash":false},{"StartTime":124563.0,"Position":421.66275,"HyperDash":false},{"StartTime":124619.0,"Position":395.6441,"HyperDash":false},{"StartTime":124675.0,"Position":426.7424,"HyperDash":false},{"StartTime":124731.0,"Position":411.428467,"HyperDash":false},{"StartTime":124787.0,"Position":416.085144,"HyperDash":false},{"StartTime":124843.0,"Position":392.878662,"HyperDash":false},{"StartTime":124899.0,"Position":418.688171,"HyperDash":false},{"StartTime":124955.0,"Position":407.149261,"HyperDash":false},{"StartTime":125011.0,"Position":431.788025,"HyperDash":false},{"StartTime":125062.0,"Position":407.509033,"HyperDash":false},{"StartTime":125114.0,"Position":382.834656,"HyperDash":false},{"StartTime":125165.0,"Position":397.090271,"HyperDash":false},{"StartTime":125217.0,"Position":376.698334,"HyperDash":false},{"StartTime":125268.0,"Position":346.500427,"HyperDash":false},{"StartTime":125320.0,"Position":337.9348,"HyperDash":false},{"StartTime":125371.0,"Position":290.849426,"HyperDash":false},{"StartTime":125459.0,"Position":276.2673,"HyperDash":false}]},{"StartTime":125907.0,"Objects":[{"StartTime":125907.0,"Position":121.0,"HyperDash":false},{"StartTime":125990.0,"Position":113.836914,"HyperDash":false},{"StartTime":126074.0,"Position":142.708008,"HyperDash":false},{"StartTime":126158.0,"Position":144.5791,"HyperDash":false},{"StartTime":126242.0,"Position":132.467285,"HyperDash":false},{"StartTime":126317.0,"Position":121.9209,"HyperDash":false},{"StartTime":126392.0,"Position":135.357422,"HyperDash":false},{"StartTime":126467.0,"Position":109.793945,"HyperDash":false},{"StartTime":126578.0,"Position":121.0,"HyperDash":false}]},{"StartTime":126802.0,"Objects":[{"StartTime":126802.0,"Position":75.0,"HyperDash":false}]},{"StartTime":127026.0,"Objects":[{"StartTime":127026.0,"Position":88.0,"HyperDash":false},{"StartTime":127081.0,"Position":65.86594,"HyperDash":false},{"StartTime":127137.0,"Position":35.8985558,"HyperDash":false},{"StartTime":127193.0,"Position":35.9736977,"HyperDash":false},{"StartTime":127249.0,"Position":9.451545,"HyperDash":false},{"StartTime":127343.0,"Position":22.10696,"HyperDash":false},{"StartTime":127473.0,"Position":88.0,"HyperDash":false}]},{"StartTime":127698.0,"Objects":[{"StartTime":127698.0,"Position":171.0,"HyperDash":false},{"StartTime":127791.0,"Position":186.182022,"HyperDash":false},{"StartTime":127921.0,"Position":250.565491,"HyperDash":false}]},{"StartTime":128145.0,"Objects":[{"StartTime":128145.0,"Position":333.0,"HyperDash":false},{"StartTime":128228.0,"Position":360.710541,"HyperDash":false},{"StartTime":128312.0,"Position":382.321838,"HyperDash":false},{"StartTime":128396.0,"Position":416.1943,"HyperDash":false},{"StartTime":128480.0,"Position":447.073883,"HyperDash":false},{"StartTime":128555.0,"Position":406.767181,"HyperDash":false},{"StartTime":128630.0,"Position":392.017242,"HyperDash":false},{"StartTime":128705.0,"Position":359.007629,"HyperDash":false},{"StartTime":128816.0,"Position":333.0,"HyperDash":false}]},{"StartTime":129041.0,"Objects":[{"StartTime":129041.0,"Position":318.0,"HyperDash":false},{"StartTime":129134.0,"Position":308.0215,"HyperDash":false},{"StartTime":129264.0,"Position":313.2559,"HyperDash":false}]},{"StartTime":129489.0,"Objects":[{"StartTime":129489.0,"Position":304.0,"HyperDash":false},{"StartTime":129540.0,"Position":336.395416,"HyperDash":false},{"StartTime":129591.0,"Position":329.216034,"HyperDash":false},{"StartTime":129643.0,"Position":344.559479,"HyperDash":false},{"StartTime":129694.0,"Position":350.5508,"HyperDash":false},{"StartTime":129745.0,"Position":370.245239,"HyperDash":false},{"StartTime":129797.0,"Position":405.714478,"HyperDash":false},{"StartTime":129848.0,"Position":411.953125,"HyperDash":false},{"StartTime":129936.0,"Position":450.890564,"HyperDash":false}]},{"StartTime":130160.0,"Objects":[{"StartTime":130160.0,"Position":506.0,"HyperDash":false},{"StartTime":130211.0,"Position":502.234955,"HyperDash":false},{"StartTime":130262.0,"Position":491.46994,"HyperDash":false},{"StartTime":130314.0,"Position":497.6703,"HyperDash":false},{"StartTime":130365.0,"Position":512.0,"HyperDash":false},{"StartTime":130416.0,"Position":496.1402,"HyperDash":false},{"StartTime":130468.0,"Position":479.340546,"HyperDash":false},{"StartTime":130519.0,"Position":512.0,"HyperDash":false},{"StartTime":130607.0,"Position":490.529968,"HyperDash":false}]},{"StartTime":130832.0,"Objects":[{"StartTime":130832.0,"Position":477.0,"HyperDash":false}]},{"StartTime":131280.0,"Objects":[{"StartTime":131280.0,"Position":308.0,"HyperDash":false},{"StartTime":131373.0,"Position":272.2126,"HyperDash":false},{"StartTime":131503.0,"Position":230.958725,"HyperDash":false}]},{"StartTime":131728.0,"Objects":[{"StartTime":131728.0,"Position":142.0,"HyperDash":false},{"StartTime":131811.0,"Position":136.278381,"HyperDash":false},{"StartTime":131895.0,"Position":128.596268,"HyperDash":false},{"StartTime":131979.0,"Position":142.914154,"HyperDash":false},{"StartTime":132063.0,"Position":155.251785,"HyperDash":false},{"StartTime":132138.0,"Position":138.309143,"HyperDash":false},{"StartTime":132213.0,"Position":167.346741,"HyperDash":false},{"StartTime":132288.0,"Position":142.384354,"HyperDash":false},{"StartTime":132399.0,"Position":142.0,"HyperDash":false}]},{"StartTime":132623.0,"Objects":[{"StartTime":132623.0,"Position":55.0,"HyperDash":false},{"StartTime":132716.0,"Position":62.0249329,"HyperDash":false},{"StartTime":132846.0,"Position":45.4683838,"HyperDash":false}]},{"StartTime":133071.0,"Objects":[{"StartTime":133071.0,"Position":33.0,"HyperDash":false},{"StartTime":133122.0,"Position":34.36902,"HyperDash":false},{"StartTime":133173.0,"Position":49.2077179,"HyperDash":false},{"StartTime":133225.0,"Position":74.56708,"HyperDash":false},{"StartTime":133276.0,"Position":53.8811874,"HyperDash":false},{"StartTime":133327.0,"Position":100.066032,"HyperDash":false},{"StartTime":133379.0,"Position":104.080582,"HyperDash":false},{"StartTime":133430.0,"Position":129.9765,"HyperDash":false},{"StartTime":133518.0,"Position":146.874619,"HyperDash":false}]},{"StartTime":133966.0,"Objects":[{"StartTime":133966.0,"Position":275.0,"HyperDash":false},{"StartTime":134059.0,"Position":303.328827,"HyperDash":false},{"StartTime":134189.0,"Position":354.91748,"HyperDash":false}]},{"StartTime":134414.0,"Objects":[{"StartTime":134414.0,"Position":389.0,"HyperDash":false},{"StartTime":134507.0,"Position":407.328827,"HyperDash":false},{"StartTime":134637.0,"Position":468.91748,"HyperDash":false}]},{"StartTime":134862.0,"Objects":[{"StartTime":134862.0,"Position":503.0,"HyperDash":false},{"StartTime":134913.0,"Position":500.255981,"HyperDash":false},{"StartTime":134964.0,"Position":512.0,"HyperDash":false},{"StartTime":135016.0,"Position":512.0,"HyperDash":false},{"StartTime":135067.0,"Position":510.048553,"HyperDash":false},{"StartTime":135118.0,"Position":501.304535,"HyperDash":false},{"StartTime":135170.0,"Position":512.0,"HyperDash":false},{"StartTime":135221.0,"Position":497.906158,"HyperDash":false},{"StartTime":135309.0,"Position":492.781982,"HyperDash":false}]},{"StartTime":135757.0,"Objects":[{"StartTime":135757.0,"Position":318.0,"HyperDash":false},{"StartTime":135850.0,"Position":298.671173,"HyperDash":false},{"StartTime":135980.0,"Position":238.08252,"HyperDash":false}]},{"StartTime":136205.0,"Objects":[{"StartTime":136205.0,"Position":204.0,"HyperDash":false},{"StartTime":136298.0,"Position":187.671188,"HyperDash":false},{"StartTime":136428.0,"Position":124.082512,"HyperDash":false}]},{"StartTime":136653.0,"Objects":[{"StartTime":136653.0,"Position":49.0,"HyperDash":false},{"StartTime":136704.0,"Position":21.2460976,"HyperDash":false},{"StartTime":136755.0,"Position":43.23652,"HyperDash":false},{"StartTime":136807.0,"Position":42.04418,"HyperDash":false},{"StartTime":136858.0,"Position":2.98967361,"HyperDash":false},{"StartTime":136909.0,"Position":20.9194527,"HyperDash":false},{"StartTime":136961.0,"Position":10.7384281,"HyperDash":false},{"StartTime":137012.0,"Position":13.6708527,"HyperDash":false},{"StartTime":137100.0,"Position":38.2821579,"HyperDash":false}]},{"StartTime":137548.0,"Objects":[{"StartTime":137548.0,"Position":200.0,"HyperDash":false},{"StartTime":137641.0,"Position":223.082932,"HyperDash":false},{"StartTime":137771.0,"Position":220.570145,"HyperDash":false}]},{"StartTime":137996.0,"Objects":[{"StartTime":137996.0,"Position":204.0,"HyperDash":false},{"StartTime":138089.0,"Position":193.917068,"HyperDash":false},{"StartTime":138219.0,"Position":183.429855,"HyperDash":false}]},{"StartTime":138444.0,"Objects":[{"StartTime":138444.0,"Position":270.0,"HyperDash":false},{"StartTime":138495.0,"Position":302.4524,"HyperDash":false},{"StartTime":138546.0,"Position":317.9048,"HyperDash":false},{"StartTime":138598.0,"Position":317.679779,"HyperDash":false},{"StartTime":138649.0,"Position":319.346863,"HyperDash":false},{"StartTime":138700.0,"Position":371.213379,"HyperDash":false},{"StartTime":138752.0,"Position":387.4302,"HyperDash":false},{"StartTime":138803.0,"Position":406.2967,"HyperDash":false},{"StartTime":138891.0,"Position":422.1252,"HyperDash":false}]},{"StartTime":139116.0,"Objects":[{"StartTime":139116.0,"Position":490.0,"HyperDash":false}]},{"StartTime":139339.0,"Objects":[{"StartTime":139339.0,"Position":504.0,"HyperDash":false},{"StartTime":139432.0,"Position":500.723053,"HyperDash":false},{"StartTime":139562.0,"Position":490.562256,"HyperDash":false}]},{"StartTime":139787.0,"Objects":[{"StartTime":139787.0,"Position":370.0,"HyperDash":false}]},{"StartTime":140235.0,"Objects":[{"StartTime":140235.0,"Position":268.0,"HyperDash":false},{"StartTime":140318.0,"Position":268.7403,"HyperDash":false},{"StartTime":140402.0,"Position":257.822449,"HyperDash":false},{"StartTime":140486.0,"Position":262.227783,"HyperDash":false},{"StartTime":140570.0,"Position":276.9804,"HyperDash":false},{"StartTime":140645.0,"Position":293.53894,"HyperDash":false},{"StartTime":140720.0,"Position":260.337555,"HyperDash":false},{"StartTime":140795.0,"Position":257.3968,"HyperDash":false},{"StartTime":140906.0,"Position":268.0,"HyperDash":false}]},{"StartTime":141131.0,"Objects":[{"StartTime":141131.0,"Position":207.0,"HyperDash":false},{"StartTime":141224.0,"Position":178.663437,"HyperDash":false},{"StartTime":141354.0,"Position":127.063927,"HyperDash":false}]},{"StartTime":141578.0,"Objects":[{"StartTime":141578.0,"Position":39.0,"HyperDash":false}]},{"StartTime":141802.0,"Objects":[{"StartTime":141802.0,"Position":8.0,"HyperDash":false}]},{"StartTime":142026.0,"Objects":[{"StartTime":142026.0,"Position":71.0,"HyperDash":false},{"StartTime":142119.0,"Position":106.114151,"HyperDash":false},{"StartTime":142249.0,"Position":149.484,"HyperDash":false}]},{"StartTime":142474.0,"Objects":[{"StartTime":142474.0,"Position":220.0,"HyperDash":false},{"StartTime":142557.0,"Position":238.606583,"HyperDash":false},{"StartTime":142641.0,"Position":276.5699,"HyperDash":false},{"StartTime":142725.0,"Position":317.533142,"HyperDash":false},{"StartTime":142809.0,"Position":339.6748,"HyperDash":false},{"StartTime":142884.0,"Position":301.10022,"HyperDash":false},{"StartTime":142959.0,"Position":303.3473,"HyperDash":false},{"StartTime":143034.0,"Position":253.59436,"HyperDash":false},{"StartTime":143145.0,"Position":220.0,"HyperDash":false}]},{"StartTime":143369.0,"Objects":[{"StartTime":143369.0,"Position":158.0,"HyperDash":false},{"StartTime":143462.0,"Position":168.4163,"HyperDash":false},{"StartTime":143592.0,"Position":155.389526,"HyperDash":false}]},{"StartTime":143817.0,"Objects":[{"StartTime":143817.0,"Position":192.0,"HyperDash":false},{"StartTime":143868.0,"Position":227.725708,"HyperDash":false},{"StartTime":143919.0,"Position":234.856445,"HyperDash":false},{"StartTime":143971.0,"Position":256.358948,"HyperDash":false},{"StartTime":144022.0,"Position":248.750854,"HyperDash":false},{"StartTime":144073.0,"Position":277.396729,"HyperDash":false},{"StartTime":144125.0,"Position":293.7474,"HyperDash":false},{"StartTime":144176.0,"Position":303.68158,"HyperDash":false},{"StartTime":144264.0,"Position":346.96463,"HyperDash":false}]},{"StartTime":144489.0,"Objects":[{"StartTime":144489.0,"Position":431.0,"HyperDash":false},{"StartTime":144540.0,"Position":448.9708,"HyperDash":false},{"StartTime":144591.0,"Position":446.9416,"HyperDash":false},{"StartTime":144643.0,"Position":435.9314,"HyperDash":false},{"StartTime":144694.0,"Position":443.9022,"HyperDash":false},{"StartTime":144745.0,"Position":416.873,"HyperDash":false},{"StartTime":144797.0,"Position":434.8628,"HyperDash":false},{"StartTime":144848.0,"Position":447.8336,"HyperDash":false},{"StartTime":144936.0,"Position":439.508667,"HyperDash":false}]},{"StartTime":145160.0,"Objects":[{"StartTime":145160.0,"Position":456.0,"HyperDash":false}]},{"StartTime":145608.0,"Objects":[{"StartTime":145608.0,"Position":272.0,"HyperDash":false},{"StartTime":145701.0,"Position":244.790558,"HyperDash":false},{"StartTime":145831.0,"Position":193.216751,"HyperDash":false}]},{"StartTime":146056.0,"Objects":[{"StartTime":146056.0,"Position":127.0,"HyperDash":false},{"StartTime":146139.0,"Position":105.417236,"HyperDash":false},{"StartTime":146223.0,"Position":79.47805,"HyperDash":false},{"StartTime":146307.0,"Position":53.5388641,"HyperDash":false},{"StartTime":146391.0,"Position":7.421463,"HyperDash":false},{"StartTime":146466.0,"Position":38.974678,"HyperDash":false},{"StartTime":146541.0,"Position":69.7061,"HyperDash":false},{"StartTime":146616.0,"Position":94.4375,"HyperDash":false},{"StartTime":146727.0,"Position":127.0,"HyperDash":false}]},{"StartTime":146951.0,"Objects":[{"StartTime":146951.0,"Position":193.0,"HyperDash":false},{"StartTime":147044.0,"Position":209.412018,"HyperDash":false},{"StartTime":147174.0,"Position":186.467926,"HyperDash":false}]},{"StartTime":147399.0,"Objects":[{"StartTime":147399.0,"Position":109.0,"HyperDash":false},{"StartTime":147450.0,"Position":145.151154,"HyperDash":false},{"StartTime":147501.0,"Position":151.302292,"HyperDash":false},{"StartTime":147553.0,"Position":163.809341,"HyperDash":false},{"StartTime":147604.0,"Position":170.440277,"HyperDash":false},{"StartTime":147655.0,"Position":216.034668,"HyperDash":false},{"StartTime":147707.0,"Position":210.1249,"HyperDash":false},{"StartTime":147758.0,"Position":230.252777,"HyperDash":false},{"StartTime":147846.0,"Position":266.5323,"HyperDash":false}]},{"StartTime":148295.0,"Objects":[{"StartTime":148295.0,"Position":441.0,"HyperDash":false},{"StartTime":148388.0,"Position":425.532318,"HyperDash":false},{"StartTime":148518.0,"Position":444.6743,"HyperDash":false}]},{"StartTime":148742.0,"Objects":[{"StartTime":148742.0,"Position":482.0,"HyperDash":false},{"StartTime":148835.0,"Position":486.467682,"HyperDash":false},{"StartTime":148965.0,"Position":478.3257,"HyperDash":false}]},{"StartTime":149190.0,"Objects":[{"StartTime":149190.0,"Position":390.0,"HyperDash":false},{"StartTime":149241.0,"Position":390.926971,"HyperDash":false},{"StartTime":149292.0,"Position":346.853943,"HyperDash":false},{"StartTime":149344.0,"Position":355.206665,"HyperDash":false},{"StartTime":149395.0,"Position":318.011047,"HyperDash":false},{"StartTime":149446.0,"Position":311.81546,"HyperDash":false},{"StartTime":149498.0,"Position":296.263062,"HyperDash":false},{"StartTime":149549.0,"Position":268.067444,"HyperDash":false},{"StartTime":149637.0,"Position":235.671082,"HyperDash":false}]},{"StartTime":150086.0,"Objects":[{"StartTime":150086.0,"Position":59.0,"HyperDash":false},{"StartTime":150179.0,"Position":44.27435,"HyperDash":false},{"StartTime":150309.0,"Position":42.77816,"HyperDash":false}]},{"StartTime":150534.0,"Objects":[{"StartTime":150534.0,"Position":94.0,"HyperDash":false},{"StartTime":150627.0,"Position":87.7256546,"HyperDash":false},{"StartTime":150757.0,"Position":110.221848,"HyperDash":false}]},{"StartTime":150981.0,"Objects":[{"StartTime":150981.0,"Position":42.0,"HyperDash":false},{"StartTime":151032.0,"Position":70.85617,"HyperDash":false},{"StartTime":151083.0,"Position":55.8612671,"HyperDash":false},{"StartTime":151135.0,"Position":104.001328,"HyperDash":false},{"StartTime":151186.0,"Position":120.188065,"HyperDash":false},{"StartTime":151237.0,"Position":126.371735,"HyperDash":false},{"StartTime":151289.0,"Position":155.4776,"HyperDash":false},{"StartTime":151340.0,"Position":163.413391,"HyperDash":false},{"StartTime":151428.0,"Position":190.731277,"HyperDash":false}]},{"StartTime":151653.0,"Objects":[{"StartTime":151653.0,"Position":324.0,"HyperDash":false}]},{"StartTime":151877.0,"Objects":[{"StartTime":151877.0,"Position":335.0,"HyperDash":false},{"StartTime":151970.0,"Position":335.9098,"HyperDash":false},{"StartTime":152100.0,"Position":327.590118,"HyperDash":false}]},{"StartTime":152325.0,"Objects":[{"StartTime":152325.0,"Position":264.0,"HyperDash":false},{"StartTime":152418.0,"Position":284.0902,"HyperDash":false},{"StartTime":152548.0,"Position":271.409882,"HyperDash":false}]},{"StartTime":152772.0,"Objects":[{"StartTime":152772.0,"Position":318.0,"HyperDash":false},{"StartTime":152823.0,"Position":332.202423,"HyperDash":false},{"StartTime":152874.0,"Position":339.075562,"HyperDash":false},{"StartTime":152926.0,"Position":384.6346,"HyperDash":false},{"StartTime":152977.0,"Position":390.811829,"HyperDash":false},{"StartTime":153028.0,"Position":421.607452,"HyperDash":false},{"StartTime":153080.0,"Position":434.969727,"HyperDash":false},{"StartTime":153131.0,"Position":424.9186,"HyperDash":false},{"StartTime":153219.0,"Position":465.022461,"HyperDash":false}]},{"StartTime":153668.0,"Objects":[{"StartTime":153668.0,"Position":494.0,"HyperDash":false},{"StartTime":153723.0,"Position":509.7584,"HyperDash":false},{"StartTime":153779.0,"Position":498.566925,"HyperDash":false},{"StartTime":153835.0,"Position":490.375458,"HyperDash":false},{"StartTime":153891.0,"Position":505.209076,"HyperDash":false},{"StartTime":153985.0,"Position":512.0,"HyperDash":false},{"StartTime":154115.0,"Position":494.0,"HyperDash":false}]},{"StartTime":154339.0,"Objects":[{"StartTime":154339.0,"Position":317.0,"HyperDash":false}]},{"StartTime":154563.0,"Objects":[{"StartTime":154563.0,"Position":332.0,"HyperDash":false},{"StartTime":154618.0,"Position":328.824219,"HyperDash":false},{"StartTime":154674.0,"Position":290.48703,"HyperDash":false},{"StartTime":154730.0,"Position":281.624817,"HyperDash":false},{"StartTime":154786.0,"Position":266.622284,"HyperDash":false},{"StartTime":154842.0,"Position":240.852814,"HyperDash":false},{"StartTime":154898.0,"Position":204.669556,"HyperDash":false},{"StartTime":154954.0,"Position":191.449188,"HyperDash":false},{"StartTime":155010.0,"Position":180.362961,"HyperDash":false},{"StartTime":155061.0,"Position":184.570953,"HyperDash":false},{"StartTime":155113.0,"Position":203.339157,"HyperDash":false},{"StartTime":155164.0,"Position":238.630051,"HyperDash":false},{"StartTime":155216.0,"Position":245.871323,"HyperDash":false},{"StartTime":155267.0,"Position":266.0493,"HyperDash":false},{"StartTime":155319.0,"Position":266.5972,"HyperDash":false},{"StartTime":155370.0,"Position":309.515717,"HyperDash":false},{"StartTime":155458.0,"Position":332.0,"HyperDash":false}]},{"StartTime":155683.0,"Objects":[{"StartTime":155683.0,"Position":413.0,"HyperDash":false},{"StartTime":155738.0,"Position":442.436737,"HyperDash":false},{"StartTime":155794.0,"Position":439.2269,"HyperDash":false},{"StartTime":155850.0,"Position":485.017029,"HyperDash":false},{"StartTime":155906.0,"Position":491.9839,"HyperDash":false},{"StartTime":156000.0,"Position":476.9414,"HyperDash":false},{"StartTime":156130.0,"Position":413.0,"HyperDash":false}]},{"StartTime":156354.0,"Objects":[{"StartTime":156354.0,"Position":379.0,"HyperDash":false},{"StartTime":156405.0,"Position":353.171,"HyperDash":false},{"StartTime":156456.0,"Position":333.342,"HyperDash":false},{"StartTime":156508.0,"Position":342.93924,"HyperDash":false},{"StartTime":156559.0,"Position":316.817322,"HyperDash":false},{"StartTime":156610.0,"Position":287.6954,"HyperDash":false},{"StartTime":156662.0,"Position":273.21814,"HyperDash":false},{"StartTime":156713.0,"Position":261.096252,"HyperDash":false},{"StartTime":156801.0,"Position":228.827026,"HyperDash":false}]},{"StartTime":157250.0,"Objects":[{"StartTime":157250.0,"Position":103.0,"HyperDash":false},{"StartTime":157301.0,"Position":109.828995,"HyperDash":false},{"StartTime":157352.0,"Position":131.65799,"HyperDash":false},{"StartTime":157404.0,"Position":139.06076,"HyperDash":false},{"StartTime":157455.0,"Position":150.182678,"HyperDash":false},{"StartTime":157506.0,"Position":189.3046,"HyperDash":false},{"StartTime":157558.0,"Position":199.78186,"HyperDash":false},{"StartTime":157609.0,"Position":219.903763,"HyperDash":false},{"StartTime":157697.0,"Position":253.172974,"HyperDash":false}]},{"StartTime":158145.0,"Objects":[{"StartTime":158145.0,"Position":131.0,"HyperDash":false},{"StartTime":158196.0,"Position":95.01886,"HyperDash":false},{"StartTime":158247.0,"Position":97.78887,"HyperDash":false},{"StartTime":158299.0,"Position":63.4222565,"HyperDash":false},{"StartTime":158350.0,"Position":75.0872,"HyperDash":false},{"StartTime":158401.0,"Position":22.8652954,"HyperDash":false},{"StartTime":158453.0,"Position":45.94365,"HyperDash":false},{"StartTime":158504.0,"Position":0.0,"HyperDash":false},{"StartTime":158592.0,"Position":0.0,"HyperDash":false}]},{"StartTime":158817.0,"Objects":[{"StartTime":158817.0,"Position":29.0,"HyperDash":false}]},{"StartTime":159041.0,"Objects":[{"StartTime":159041.0,"Position":54.0,"HyperDash":false},{"StartTime":159134.0,"Position":95.1591644,"HyperDash":false},{"StartTime":159264.0,"Position":133.5107,"HyperDash":false}]},{"StartTime":159489.0,"Objects":[{"StartTime":159489.0,"Position":194.0,"HyperDash":false},{"StartTime":159582.0,"Position":246.159164,"HyperDash":false},{"StartTime":159712.0,"Position":273.510681,"HyperDash":false}]},{"StartTime":159936.0,"Objects":[{"StartTime":159936.0,"Position":354.0,"HyperDash":false},{"StartTime":159987.0,"Position":380.1903,"HyperDash":false},{"StartTime":160038.0,"Position":355.7923,"HyperDash":false},{"StartTime":160090.0,"Position":369.8352,"HyperDash":false},{"StartTime":160141.0,"Position":386.028046,"HyperDash":false},{"StartTime":160192.0,"Position":388.432159,"HyperDash":false},{"StartTime":160244.0,"Position":401.998,"HyperDash":false},{"StartTime":160295.0,"Position":400.752838,"HyperDash":false},{"StartTime":160383.0,"Position":376.419128,"HyperDash":false}]},{"StartTime":160832.0,"Objects":[{"StartTime":160832.0,"Position":242.0,"HyperDash":false},{"StartTime":160883.0,"Position":217.809677,"HyperDash":false},{"StartTime":160934.0,"Position":242.2077,"HyperDash":false},{"StartTime":160986.0,"Position":224.16481,"HyperDash":false},{"StartTime":161037.0,"Position":196.971954,"HyperDash":false},{"StartTime":161088.0,"Position":210.567825,"HyperDash":false},{"StartTime":161140.0,"Position":211.002029,"HyperDash":false},{"StartTime":161191.0,"Position":221.247162,"HyperDash":false},{"StartTime":161279.0,"Position":219.580887,"HyperDash":false}]},{"StartTime":161728.0,"Objects":[{"StartTime":161728.0,"Position":481.0,"HyperDash":false}]},{"StartTime":162175.0,"Objects":[{"StartTime":162175.0,"Position":182.0,"HyperDash":false},{"StartTime":162268.0,"Position":165.752014,"HyperDash":false},{"StartTime":162398.0,"Position":102.276337,"HyperDash":false}]},{"StartTime":162623.0,"Objects":[{"StartTime":162623.0,"Position":22.0,"HyperDash":false},{"StartTime":162706.0,"Position":2.907238,"HyperDash":false},{"StartTime":162790.0,"Position":4.71697235,"HyperDash":false},{"StartTime":162874.0,"Position":13.9768333,"HyperDash":false},{"StartTime":162958.0,"Position":19.6685238,"HyperDash":false},{"StartTime":163033.0,"Position":4.88709259,"HyperDash":false},{"StartTime":163108.0,"Position":2.06014729,"HyperDash":false},{"StartTime":163183.0,"Position":22.1771469,"HyperDash":false},{"StartTime":163294.0,"Position":22.0,"HyperDash":false}]},{"StartTime":163519.0,"Objects":[{"StartTime":163519.0,"Position":176.0,"HyperDash":false}]},{"StartTime":163966.0,"Objects":[{"StartTime":163966.0,"Position":202.0,"HyperDash":false},{"StartTime":164059.0,"Position":221.322418,"HyperDash":false},{"StartTime":164189.0,"Position":281.902161,"HyperDash":false}]},{"StartTime":164414.0,"Objects":[{"StartTime":164414.0,"Position":355.0,"HyperDash":false},{"StartTime":164497.0,"Position":383.562683,"HyperDash":false},{"StartTime":164581.0,"Position":395.4695,"HyperDash":false},{"StartTime":164665.0,"Position":445.511963,"HyperDash":false},{"StartTime":164749.0,"Position":470.7404,"HyperDash":false},{"StartTime":164824.0,"Position":455.970947,"HyperDash":false},{"StartTime":164899.0,"Position":418.028564,"HyperDash":false},{"StartTime":164974.0,"Position":389.1983,"HyperDash":false},{"StartTime":165085.0,"Position":355.0,"HyperDash":false}]},{"StartTime":165310.0,"Objects":[{"StartTime":165310.0,"Position":76.0,"HyperDash":false}]},{"StartTime":165757.0,"Objects":[{"StartTime":165757.0,"Position":110.0,"HyperDash":false},{"StartTime":165850.0,"Position":113.949112,"HyperDash":false},{"StartTime":165980.0,"Position":121.867233,"HyperDash":false}]},{"StartTime":166205.0,"Objects":[{"StartTime":166205.0,"Position":188.0,"HyperDash":false},{"StartTime":166288.0,"Position":201.562683,"HyperDash":false},{"StartTime":166372.0,"Position":258.4695,"HyperDash":false},{"StartTime":166456.0,"Position":274.511963,"HyperDash":false},{"StartTime":166540.0,"Position":303.7404,"HyperDash":false},{"StartTime":166615.0,"Position":289.970947,"HyperDash":false},{"StartTime":166690.0,"Position":270.028564,"HyperDash":false},{"StartTime":166765.0,"Position":234.1983,"HyperDash":false},{"StartTime":166876.0,"Position":188.0,"HyperDash":false}]},{"StartTime":167101.0,"Objects":[{"StartTime":167101.0,"Position":206.0,"HyperDash":false},{"StartTime":167156.0,"Position":213.034912,"HyperDash":false},{"StartTime":167212.0,"Position":234.0148,"HyperDash":false},{"StartTime":167268.0,"Position":239.378357,"HyperDash":false},{"StartTime":167324.0,"Position":266.7099,"HyperDash":false},{"StartTime":167380.0,"Position":287.58194,"HyperDash":false},{"StartTime":167436.0,"Position":299.568817,"HyperDash":false},{"StartTime":167492.0,"Position":321.242737,"HyperDash":false},{"StartTime":167548.0,"Position":354.299927,"HyperDash":false},{"StartTime":167599.0,"Position":342.30304,"HyperDash":false},{"StartTime":167651.0,"Position":309.123352,"HyperDash":false},{"StartTime":167702.0,"Position":297.94458,"HyperDash":false},{"StartTime":167754.0,"Position":296.413849,"HyperDash":false},{"StartTime":167805.0,"Position":257.5692,"HyperDash":false},{"StartTime":167857.0,"Position":266.036133,"HyperDash":false},{"StartTime":167908.0,"Position":246.8481,"HyperDash":false},{"StartTime":167996.0,"Position":206.0,"HyperDash":false}]},{"StartTime":168332.0,"Objects":[{"StartTime":168332.0,"Position":98.0,"HyperDash":false},{"StartTime":168406.0,"Position":81.25128,"HyperDash":false},{"StartTime":168481.0,"Position":82.4519,"HyperDash":false},{"StartTime":168556.0,"Position":92.65252,"HyperDash":false},{"StartTime":168667.0,"Position":81.0294342,"HyperDash":false}]},{"StartTime":168892.0,"Objects":[{"StartTime":168892.0,"Position":70.0,"HyperDash":false}]},{"StartTime":169339.0,"Objects":[{"StartTime":169339.0,"Position":246.0,"HyperDash":false},{"StartTime":169432.0,"Position":292.1293,"HyperDash":false},{"StartTime":169562.0,"Position":325.439056,"HyperDash":false}]},{"StartTime":169787.0,"Objects":[{"StartTime":169787.0,"Position":385.0,"HyperDash":false},{"StartTime":169870.0,"Position":405.562683,"HyperDash":false},{"StartTime":169954.0,"Position":452.4695,"HyperDash":false},{"StartTime":170038.0,"Position":472.511963,"HyperDash":false},{"StartTime":170122.0,"Position":500.7404,"HyperDash":false},{"StartTime":170197.0,"Position":462.970947,"HyperDash":false},{"StartTime":170272.0,"Position":454.028564,"HyperDash":false},{"StartTime":170347.0,"Position":408.1983,"HyperDash":false},{"StartTime":170458.0,"Position":385.0,"HyperDash":false}]},{"StartTime":170683.0,"Objects":[{"StartTime":170683.0,"Position":106.0,"HyperDash":false}]},{"StartTime":171131.0,"Objects":[{"StartTime":171131.0,"Position":161.0,"HyperDash":false},{"StartTime":171224.0,"Position":131.715057,"HyperDash":false},{"StartTime":171354.0,"Position":81.18773,"HyperDash":false}]},{"StartTime":171578.0,"Objects":[{"StartTime":171578.0,"Position":22.0,"HyperDash":false},{"StartTime":171661.0,"Position":0.907238,"HyperDash":false},{"StartTime":171745.0,"Position":5.71697235,"HyperDash":false},{"StartTime":171829.0,"Position":4.97683334,"HyperDash":false},{"StartTime":171913.0,"Position":19.6685238,"HyperDash":false},{"StartTime":171988.0,"Position":4.88709259,"HyperDash":false},{"StartTime":172063.0,"Position":20.0601463,"HyperDash":false},{"StartTime":172138.0,"Position":0.0,"HyperDash":false},{"StartTime":172249.0,"Position":22.0,"HyperDash":false}]},{"StartTime":172474.0,"Objects":[{"StartTime":172474.0,"Position":196.0,"HyperDash":false}]},{"StartTime":172922.0,"Objects":[{"StartTime":172922.0,"Position":279.0,"HyperDash":false},{"StartTime":173015.0,"Position":321.282318,"HyperDash":false},{"StartTime":173145.0,"Position":358.80603,"HyperDash":false}]},{"StartTime":173369.0,"Objects":[{"StartTime":173369.0,"Position":385.0,"HyperDash":false},{"StartTime":173452.0,"Position":403.562683,"HyperDash":false},{"StartTime":173536.0,"Position":426.4695,"HyperDash":false},{"StartTime":173620.0,"Position":488.511963,"HyperDash":false},{"StartTime":173704.0,"Position":500.7404,"HyperDash":false},{"StartTime":173779.0,"Position":482.970947,"HyperDash":false},{"StartTime":173854.0,"Position":456.028564,"HyperDash":false},{"StartTime":173929.0,"Position":421.1983,"HyperDash":false},{"StartTime":174040.0,"Position":385.0,"HyperDash":false}]},{"StartTime":174265.0,"Objects":[{"StartTime":174265.0,"Position":307.0,"HyperDash":false},{"StartTime":174358.0,"Position":261.853668,"HyperDash":false},{"StartTime":174488.0,"Position":227.52005,"HyperDash":false}]},{"StartTime":174713.0,"Objects":[{"StartTime":174713.0,"Position":148.0,"HyperDash":false},{"StartTime":174796.0,"Position":106.520546,"HyperDash":false},{"StartTime":174880.0,"Position":80.68592,"HyperDash":false},{"StartTime":174964.0,"Position":68.8512955,"HyperDash":false},{"StartTime":175048.0,"Position":28.83908,"HyperDash":false},{"StartTime":175123.0,"Position":59.2995529,"HyperDash":false},{"StartTime":175198.0,"Position":89.9376144,"HyperDash":false},{"StartTime":175273.0,"Position":125.575668,"HyperDash":false},{"StartTime":175384.0,"Position":148.0,"HyperDash":true}]},{"StartTime":175608.0,"Objects":[{"StartTime":175608.0,"Position":439.0,"HyperDash":false}]},{"StartTime":176056.0,"Objects":[{"StartTime":176056.0,"Position":387.0,"HyperDash":false},{"StartTime":176139.0,"Position":390.25885,"HyperDash":false},{"StartTime":176223.0,"Position":407.5621,"HyperDash":false},{"StartTime":176307.0,"Position":401.7594,"HyperDash":false},{"StartTime":176391.0,"Position":410.638336,"HyperDash":false},{"StartTime":176466.0,"Position":423.467438,"HyperDash":false},{"StartTime":176541.0,"Position":433.8284,"HyperDash":false},{"StartTime":176616.0,"Position":391.564453,"HyperDash":false},{"StartTime":176727.0,"Position":387.0,"HyperDash":false}]},{"StartTime":176951.0,"Objects":[{"StartTime":176951.0,"Position":302.0,"HyperDash":false},{"StartTime":177002.0,"Position":276.291016,"HyperDash":false},{"StartTime":177053.0,"Position":253.17688,"HyperDash":false},{"StartTime":177105.0,"Position":259.690979,"HyperDash":false},{"StartTime":177156.0,"Position":233.457672,"HyperDash":false},{"StartTime":177207.0,"Position":200.839844,"HyperDash":false},{"StartTime":177259.0,"Position":195.088776,"HyperDash":false},{"StartTime":177310.0,"Position":160.934647,"HyperDash":false},{"StartTime":177398.0,"Position":146.743591,"HyperDash":false}]},{"StartTime":177623.0,"Objects":[{"StartTime":177623.0,"Position":10.0,"HyperDash":false}]},{"StartTime":177847.0,"Objects":[{"StartTime":177847.0,"Position":93.0,"HyperDash":false},{"StartTime":177902.0,"Position":121.613495,"HyperDash":false},{"StartTime":177958.0,"Position":114.5836,"HyperDash":false},{"StartTime":178014.0,"Position":147.553711,"HyperDash":false},{"StartTime":178070.0,"Position":172.702118,"HyperDash":false},{"StartTime":178164.0,"Position":121.359177,"HyperDash":false},{"StartTime":178294.0,"Position":93.0,"HyperDash":false}]},{"StartTime":178519.0,"Objects":[{"StartTime":178519.0,"Position":20.0,"HyperDash":false},{"StartTime":178570.0,"Position":12.202383,"HyperDash":false},{"StartTime":178621.0,"Position":17.4776764,"HyperDash":false},{"StartTime":178673.0,"Position":22.8649712,"HyperDash":false},{"StartTime":178724.0,"Position":53.9413567,"HyperDash":false},{"StartTime":178775.0,"Position":54.51453,"HyperDash":false},{"StartTime":178827.0,"Position":76.44822,"HyperDash":false},{"StartTime":178878.0,"Position":73.7426147,"HyperDash":false},{"StartTime":178966.0,"Position":117.336555,"HyperDash":false}]},{"StartTime":179190.0,"Objects":[{"StartTime":179190.0,"Position":260.0,"HyperDash":false}]},{"StartTime":179638.0,"Objects":[{"StartTime":179638.0,"Position":381.0,"HyperDash":false},{"StartTime":179731.0,"Position":403.239,"HyperDash":false},{"StartTime":179861.0,"Position":460.702118,"HyperDash":false}]},{"StartTime":180086.0,"Objects":[{"StartTime":180086.0,"Position":499.0,"HyperDash":false},{"StartTime":180169.0,"Position":492.7418,"HyperDash":false},{"StartTime":180253.0,"Position":479.880341,"HyperDash":false},{"StartTime":180337.0,"Position":491.247253,"HyperDash":false},{"StartTime":180421.0,"Position":476.24173,"HyperDash":false},{"StartTime":180496.0,"Position":474.9095,"HyperDash":false},{"StartTime":180571.0,"Position":490.40033,"HyperDash":false},{"StartTime":180646.0,"Position":497.418976,"HyperDash":false},{"StartTime":180757.0,"Position":499.0,"HyperDash":false}]},{"StartTime":180981.0,"Objects":[{"StartTime":180981.0,"Position":350.0,"HyperDash":false}]},{"StartTime":181429.0,"Objects":[{"StartTime":181429.0,"Position":237.0,"HyperDash":false},{"StartTime":181522.0,"Position":219.747375,"HyperDash":false},{"StartTime":181652.0,"Position":157.265228,"HyperDash":false}]},{"StartTime":181877.0,"Objects":[{"StartTime":181877.0,"Position":69.0,"HyperDash":false},{"StartTime":181960.0,"Position":62.7165451,"HyperDash":false},{"StartTime":182044.0,"Position":51.0702744,"HyperDash":false},{"StartTime":182128.0,"Position":58.38651,"HyperDash":false},{"StartTime":182212.0,"Position":46.79955,"HyperDash":false},{"StartTime":182287.0,"Position":57.2412,"HyperDash":false},{"StartTime":182362.0,"Position":36.87273,"HyperDash":false},{"StartTime":182437.0,"Position":68.721756,"HyperDash":false},{"StartTime":182548.0,"Position":69.0,"HyperDash":false}]},{"StartTime":182772.0,"Objects":[{"StartTime":182772.0,"Position":156.0,"HyperDash":false}]},{"StartTime":182996.0,"Objects":[{"StartTime":182996.0,"Position":188.0,"HyperDash":false}]},{"StartTime":183220.0,"Objects":[{"StartTime":183220.0,"Position":258.0,"HyperDash":false},{"StartTime":183271.0,"Position":290.116547,"HyperDash":false},{"StartTime":183322.0,"Position":294.3538,"HyperDash":false},{"StartTime":183374.0,"Position":307.583344,"HyperDash":false},{"StartTime":183425.0,"Position":340.892883,"HyperDash":false},{"StartTime":183476.0,"Position":330.0654,"HyperDash":false},{"StartTime":183528.0,"Position":366.9192,"HyperDash":false},{"StartTime":183579.0,"Position":359.8023,"HyperDash":false},{"StartTime":183667.0,"Position":410.248352,"HyperDash":false}]},{"StartTime":184116.0,"Objects":[{"StartTime":184116.0,"Position":500.0,"HyperDash":false},{"StartTime":184199.0,"Position":507.066162,"HyperDash":false},{"StartTime":184283.0,"Position":497.157227,"HyperDash":false},{"StartTime":184367.0,"Position":504.2483,"HyperDash":false},{"StartTime":184451.0,"Position":508.3518,"HyperDash":false},{"StartTime":184526.0,"Position":505.497223,"HyperDash":false},{"StartTime":184601.0,"Position":509.630219,"HyperDash":false},{"StartTime":184676.0,"Position":505.763184,"HyperDash":false},{"StartTime":184787.0,"Position":500.0,"HyperDash":false}]},{"StartTime":185011.0,"Objects":[{"StartTime":185011.0,"Position":424.0,"HyperDash":false},{"StartTime":185104.0,"Position":408.773682,"HyperDash":false},{"StartTime":185234.0,"Position":345.858856,"HyperDash":false}]},{"StartTime":185459.0,"Objects":[{"StartTime":185459.0,"Position":273.0,"HyperDash":false},{"StartTime":185533.0,"Position":247.05632,"HyperDash":false},{"StartTime":185608.0,"Position":204.745392,"HyperDash":false},{"StartTime":185683.0,"Position":207.012665,"HyperDash":false},{"StartTime":185794.0,"Position":159.200455,"HyperDash":false}]},{"StartTime":186131.0,"Objects":[{"StartTime":186131.0,"Position":66.0,"HyperDash":false},{"StartTime":186182.0,"Position":77.52162,"HyperDash":false},{"StartTime":186233.0,"Position":97.0432358,"HyperDash":false},{"StartTime":186285.0,"Position":113.692917,"HyperDash":false},{"StartTime":186336.0,"Position":147.840286,"HyperDash":false},{"StartTime":186387.0,"Position":162.98764,"HyperDash":false},{"StartTime":186439.0,"Position":155.490845,"HyperDash":false},{"StartTime":186490.0,"Position":179.638214,"HyperDash":false},{"StartTime":186578.0,"Position":217.951324,"HyperDash":false}]},{"StartTime":186802.0,"Objects":[{"StartTime":186802.0,"Position":301.0,"HyperDash":false},{"StartTime":186895.0,"Position":319.187317,"HyperDash":false},{"StartTime":187025.0,"Position":380.578247,"HyperDash":false}]},{"StartTime":187250.0,"Objects":[{"StartTime":187250.0,"Position":468.0,"HyperDash":false},{"StartTime":187333.0,"Position":477.219818,"HyperDash":false},{"StartTime":187417.0,"Position":470.918518,"HyperDash":false},{"StartTime":187501.0,"Position":480.95874,"HyperDash":false},{"StartTime":187585.0,"Position":487.3309,"HyperDash":false},{"StartTime":187660.0,"Position":500.324768,"HyperDash":false},{"StartTime":187735.0,"Position":496.985931,"HyperDash":false},{"StartTime":187810.0,"Position":459.305664,"HyperDash":false},{"StartTime":187921.0,"Position":468.0,"HyperDash":false}]},{"StartTime":188145.0,"Objects":[{"StartTime":188145.0,"Position":372.0,"HyperDash":false}]},{"StartTime":188593.0,"Objects":[{"StartTime":188593.0,"Position":255.0,"HyperDash":false},{"StartTime":188686.0,"Position":237.844971,"HyperDash":false},{"StartTime":188816.0,"Position":175.499252,"HyperDash":false}]},{"StartTime":189041.0,"Objects":[{"StartTime":189041.0,"Position":140.0,"HyperDash":false},{"StartTime":189124.0,"Position":120.208252,"HyperDash":false},{"StartTime":189208.0,"Position":79.71341,"HyperDash":false},{"StartTime":189292.0,"Position":62.945713,"HyperDash":false},{"StartTime":189376.0,"Position":21.8198166,"HyperDash":false},{"StartTime":189451.0,"Position":43.38784,"HyperDash":false},{"StartTime":189526.0,"Position":60.00197,"HyperDash":false},{"StartTime":189601.0,"Position":110.413246,"HyperDash":false},{"StartTime":189712.0,"Position":140.0,"HyperDash":false}]},{"StartTime":189936.0,"Objects":[{"StartTime":189936.0,"Position":409.0,"HyperDash":false}]},{"StartTime":190384.0,"Objects":[{"StartTime":190384.0,"Position":297.0,"HyperDash":false},{"StartTime":190467.0,"Position":334.5554,"HyperDash":false},{"StartTime":190551.0,"Position":360.466858,"HyperDash":false},{"StartTime":190635.0,"Position":367.378357,"HyperDash":false},{"StartTime":190719.0,"Position":416.4679,"HyperDash":false},{"StartTime":190794.0,"Position":383.93924,"HyperDash":false},{"StartTime":190869.0,"Position":350.232544,"HyperDash":false},{"StartTime":190944.0,"Position":345.525879,"HyperDash":false},{"StartTime":191055.0,"Position":297.0,"HyperDash":false}]},{"StartTime":191280.0,"Objects":[{"StartTime":191280.0,"Position":233.0,"HyperDash":false},{"StartTime":191335.0,"Position":238.967834,"HyperDash":false},{"StartTime":191391.0,"Position":212.5211,"HyperDash":false},{"StartTime":191447.0,"Position":237.94754,"HyperDash":false},{"StartTime":191503.0,"Position":229.915482,"HyperDash":false},{"StartTime":191559.0,"Position":237.2686,"HyperDash":false},{"StartTime":191615.0,"Position":275.501129,"HyperDash":false},{"StartTime":191671.0,"Position":284.155334,"HyperDash":false},{"StartTime":191727.0,"Position":303.59848,"HyperDash":false},{"StartTime":191821.0,"Position":353.735931,"HyperDash":false},{"StartTime":191951.0,"Position":381.505432,"HyperDash":false}]},{"StartTime":192175.0,"Objects":[{"StartTime":192175.0,"Position":468.0,"HyperDash":false},{"StartTime":192258.0,"Position":482.7641,"HyperDash":false},{"StartTime":192342.0,"Position":450.513336,"HyperDash":false},{"StartTime":192426.0,"Position":466.262543,"HyperDash":false},{"StartTime":192510.0,"Position":463.004333,"HyperDash":false},{"StartTime":192585.0,"Position":465.113647,"HyperDash":false},{"StartTime":192660.0,"Position":470.2304,"HyperDash":false},{"StartTime":192735.0,"Position":465.3472,"HyperDash":false},{"StartTime":192846.0,"Position":468.0,"HyperDash":false}]},{"StartTime":193071.0,"Objects":[{"StartTime":193071.0,"Position":497.0,"HyperDash":false},{"StartTime":193126.0,"Position":512.0,"HyperDash":false},{"StartTime":193182.0,"Position":490.965454,"HyperDash":false},{"StartTime":193238.0,"Position":505.365143,"HyperDash":false},{"StartTime":193294.0,"Position":498.1796,"HyperDash":false},{"StartTime":193350.0,"Position":458.746429,"HyperDash":false},{"StartTime":193406.0,"Position":465.362274,"HyperDash":false},{"StartTime":193462.0,"Position":428.6823,"HyperDash":false},{"StartTime":193518.0,"Position":425.1735,"HyperDash":false},{"StartTime":193612.0,"Position":399.024475,"HyperDash":false},{"StartTime":193742.0,"Position":347.213928,"HyperDash":false}]},{"StartTime":193966.0,"Objects":[{"StartTime":193966.0,"Position":292.0,"HyperDash":false},{"StartTime":194049.0,"Position":284.2359,"HyperDash":false},{"StartTime":194133.0,"Position":296.486664,"HyperDash":false},{"StartTime":194217.0,"Position":289.737457,"HyperDash":false},{"StartTime":194301.0,"Position":296.995667,"HyperDash":false},{"StartTime":194376.0,"Position":298.886353,"HyperDash":false},{"StartTime":194451.0,"Position":301.7696,"HyperDash":false},{"StartTime":194526.0,"Position":303.6528,"HyperDash":false},{"StartTime":194637.0,"Position":292.0,"HyperDash":false}]},{"StartTime":194862.0,"Objects":[{"StartTime":194862.0,"Position":233.0,"HyperDash":false},{"StartTime":194917.0,"Position":233.672577,"HyperDash":false},{"StartTime":194973.0,"Position":188.020615,"HyperDash":false},{"StartTime":195029.0,"Position":185.026245,"HyperDash":false},{"StartTime":195085.0,"Position":146.409729,"HyperDash":false},{"StartTime":195141.0,"Position":122.932129,"HyperDash":false},{"StartTime":195197.0,"Position":132.166672,"HyperDash":false},{"StartTime":195253.0,"Position":111.858551,"HyperDash":false},{"StartTime":195309.0,"Position":94.3505554,"HyperDash":false},{"StartTime":195403.0,"Position":84.74842,"HyperDash":false},{"StartTime":195533.0,"Position":83.93751,"HyperDash":false}]},{"StartTime":195757.0,"Objects":[{"StartTime":195757.0,"Position":156.0,"HyperDash":false}]},{"StartTime":196205.0,"Objects":[{"StartTime":196205.0,"Position":292.0,"HyperDash":false},{"StartTime":196288.0,"Position":315.547729,"HyperDash":false},{"StartTime":196372.0,"Position":356.451416,"HyperDash":false},{"StartTime":196456.0,"Position":363.355164,"HyperDash":false},{"StartTime":196540.0,"Position":411.436859,"HyperDash":false},{"StartTime":196615.0,"Position":378.9151,"HyperDash":false},{"StartTime":196690.0,"Position":351.215363,"HyperDash":false},{"StartTime":196765.0,"Position":317.515625,"HyperDash":false},{"StartTime":196876.0,"Position":292.0,"HyperDash":false}]},{"StartTime":197101.0,"Objects":[{"StartTime":197101.0,"Position":224.0,"HyperDash":false},{"StartTime":197194.0,"Position":208.802353,"HyperDash":false},{"StartTime":197324.0,"Position":144.397034,"HyperDash":false}]},{"StartTime":197548.0,"Objects":[{"StartTime":197548.0,"Position":66.0,"HyperDash":false},{"StartTime":197631.0,"Position":48.2919579,"HyperDash":false},{"StartTime":197715.0,"Position":35.05251,"HyperDash":false},{"StartTime":197799.0,"Position":38.5374374,"HyperDash":false},{"StartTime":197883.0,"Position":11.4361858,"HyperDash":false},{"StartTime":197958.0,"Position":13.2977371,"HyperDash":false},{"StartTime":198033.0,"Position":27.7316284,"HyperDash":false},{"StartTime":198108.0,"Position":56.1405029,"HyperDash":false},{"StartTime":198219.0,"Position":66.0,"HyperDash":false}]},{"StartTime":198444.0,"Objects":[{"StartTime":198444.0,"Position":42.0,"HyperDash":false},{"StartTime":198499.0,"Position":55.76585,"HyperDash":false},{"StartTime":198555.0,"Position":34.16329,"HyperDash":false},{"StartTime":198611.0,"Position":54.15536,"HyperDash":false},{"StartTime":198667.0,"Position":77.49657,"HyperDash":false},{"StartTime":198723.0,"Position":71.00532,"HyperDash":false},{"StartTime":198779.0,"Position":105.424828,"HyperDash":false},{"StartTime":198835.0,"Position":125.435341,"HyperDash":false},{"StartTime":198891.0,"Position":136.756622,"HyperDash":false},{"StartTime":198985.0,"Position":174.4071,"HyperDash":false},{"StartTime":199115.0,"Position":215.646362,"HyperDash":false}]},{"StartTime":199339.0,"Objects":[{"StartTime":199339.0,"Position":292.0,"HyperDash":false},{"StartTime":199422.0,"Position":330.217377,"HyperDash":false},{"StartTime":199506.0,"Position":361.968842,"HyperDash":false},{"StartTime":199590.0,"Position":372.687,"HyperDash":false},{"StartTime":199674.0,"Position":408.582062,"HyperDash":false},{"StartTime":199749.0,"Position":367.224884,"HyperDash":false},{"StartTime":199824.0,"Position":343.6908,"HyperDash":false},{"StartTime":199899.0,"Position":338.7365,"HyperDash":false},{"StartTime":200010.0,"Position":292.0,"HyperDash":false}]},{"StartTime":200235.0,"Objects":[{"StartTime":200235.0,"Position":235.0,"HyperDash":false},{"StartTime":200290.0,"Position":235.309448,"HyperDash":false},{"StartTime":200346.0,"Position":241.240967,"HyperDash":false},{"StartTime":200402.0,"Position":245.969574,"HyperDash":false},{"StartTime":200458.0,"Position":247.421249,"HyperDash":false},{"StartTime":200514.0,"Position":241.446747,"HyperDash":false},{"StartTime":200570.0,"Position":272.996338,"HyperDash":false},{"StartTime":200626.0,"Position":270.733429,"HyperDash":false},{"StartTime":200682.0,"Position":286.54422,"HyperDash":false},{"StartTime":200776.0,"Position":331.074341,"HyperDash":false},{"StartTime":200906.0,"Position":359.601563,"HyperDash":false}]},{"StartTime":201131.0,"Objects":[{"StartTime":201131.0,"Position":447.0,"HyperDash":false}]},{"StartTime":201578.0,"Objects":[{"StartTime":201578.0,"Position":472.0,"HyperDash":false},{"StartTime":201671.0,"Position":420.90976,"HyperDash":false},{"StartTime":201801.0,"Position":392.654541,"HyperDash":false}]},{"StartTime":202026.0,"Objects":[{"StartTime":202026.0,"Position":323.0,"HyperDash":false},{"StartTime":202109.0,"Position":280.374054,"HyperDash":false},{"StartTime":202193.0,"Position":263.163239,"HyperDash":false},{"StartTime":202277.0,"Position":238.104523,"HyperDash":false},{"StartTime":202361.0,"Position":213.4443,"HyperDash":false},{"StartTime":202436.0,"Position":215.121521,"HyperDash":false},{"StartTime":202511.0,"Position":262.801849,"HyperDash":false},{"StartTime":202586.0,"Position":278.475128,"HyperDash":false},{"StartTime":202697.0,"Position":323.0,"HyperDash":false}]},{"StartTime":202922.0,"Objects":[{"StartTime":202922.0,"Position":370.0,"HyperDash":false}]},{"StartTime":203369.0,"Objects":[{"StartTime":203369.0,"Position":472.0,"HyperDash":false},{"StartTime":203462.0,"Position":457.79657,"HyperDash":false},{"StartTime":203592.0,"Position":459.52298,"HyperDash":false}]},{"StartTime":203817.0,"Objects":[{"StartTime":203817.0,"Position":373.0,"HyperDash":false},{"StartTime":203900.0,"Position":398.412079,"HyperDash":false},{"StartTime":203984.0,"Position":390.1198,"HyperDash":false},{"StartTime":204068.0,"Position":402.6163,"HyperDash":false},{"StartTime":204152.0,"Position":398.979218,"HyperDash":false},{"StartTime":204227.0,"Position":415.909515,"HyperDash":false},{"StartTime":204302.0,"Position":375.485352,"HyperDash":false},{"StartTime":204377.0,"Position":384.754333,"HyperDash":false},{"StartTime":204488.0,"Position":373.0,"HyperDash":false}]},{"StartTime":204713.0,"Objects":[{"StartTime":204713.0,"Position":294.0,"HyperDash":false},{"StartTime":204764.0,"Position":285.134979,"HyperDash":false},{"StartTime":204815.0,"Position":243.269958,"HyperDash":false},{"StartTime":204867.0,"Position":248.074249,"HyperDash":false},{"StartTime":204918.0,"Position":228.209229,"HyperDash":false},{"StartTime":204969.0,"Position":192.333786,"HyperDash":false},{"StartTime":205021.0,"Position":173.952545,"HyperDash":false},{"StartTime":205072.0,"Position":183.9248,"HyperDash":false},{"StartTime":205160.0,"Position":140.818085,"HyperDash":false}]},{"StartTime":205608.0,"Objects":[{"StartTime":205608.0,"Position":29.0,"HyperDash":false},{"StartTime":205659.0,"Position":63.86502,"HyperDash":false},{"StartTime":205710.0,"Position":61.7300453,"HyperDash":false},{"StartTime":205762.0,"Position":82.92575,"HyperDash":false},{"StartTime":205813.0,"Position":97.79077,"HyperDash":false},{"StartTime":205864.0,"Position":130.666214,"HyperDash":false},{"StartTime":205916.0,"Position":148.047455,"HyperDash":false},{"StartTime":205967.0,"Position":142.0752,"HyperDash":false},{"StartTime":206055.0,"Position":182.181915,"HyperDash":false}]},{"StartTime":206280.0,"Objects":[{"StartTime":206280.0,"Position":322.0,"HyperDash":false}]},{"StartTime":206504.0,"Objects":[{"StartTime":206504.0,"Position":344.0,"HyperDash":false},{"StartTime":206587.0,"Position":365.904449,"HyperDash":false},{"StartTime":206671.0,"Position":418.4734,"HyperDash":false},{"StartTime":206755.0,"Position":413.206177,"HyperDash":false},{"StartTime":206839.0,"Position":457.994324,"HyperDash":false},{"StartTime":206914.0,"Position":431.638641,"HyperDash":false},{"StartTime":206989.0,"Position":403.264984,"HyperDash":false},{"StartTime":207064.0,"Position":377.594177,"HyperDash":false},{"StartTime":207175.0,"Position":344.0,"HyperDash":false}]},{"StartTime":207399.0,"Objects":[{"StartTime":207399.0,"Position":294.0,"HyperDash":false},{"StartTime":207454.0,"Position":297.1099,"HyperDash":false},{"StartTime":207510.0,"Position":290.9207,"HyperDash":false},{"StartTime":207566.0,"Position":289.514343,"HyperDash":false},{"StartTime":207622.0,"Position":319.350433,"HyperDash":false},{"StartTime":207678.0,"Position":342.16394,"HyperDash":false},{"StartTime":207734.0,"Position":371.241455,"HyperDash":false},{"StartTime":207790.0,"Position":385.045563,"HyperDash":false},{"StartTime":207846.0,"Position":390.7907,"HyperDash":false},{"StartTime":207897.0,"Position":395.987244,"HyperDash":false},{"StartTime":207949.0,"Position":428.121765,"HyperDash":false},{"StartTime":208000.0,"Position":447.949615,"HyperDash":false},{"StartTime":208052.0,"Position":476.569366,"HyperDash":false},{"StartTime":208103.0,"Position":470.83667,"HyperDash":false},{"StartTime":208155.0,"Position":476.915344,"HyperDash":false},{"StartTime":208206.0,"Position":511.044159,"HyperDash":false},{"StartTime":208294.0,"Position":498.7854,"HyperDash":false}]},{"StartTime":215459.0,"Objects":[{"StartTime":215459.0,"Position":479.0,"HyperDash":false},{"StartTime":215542.0,"Position":229.0,"HyperDash":false},{"StartTime":215626.0,"Position":331.0,"HyperDash":false},{"StartTime":215710.0,"Position":226.0,"HyperDash":false},{"StartTime":215794.0,"Position":205.0,"HyperDash":false},{"StartTime":215878.0,"Position":472.0,"HyperDash":false},{"StartTime":215962.0,"Position":426.0,"HyperDash":false},{"StartTime":216046.0,"Position":340.0,"HyperDash":false},{"StartTime":216130.0,"Position":379.0,"HyperDash":false},{"StartTime":216214.0,"Position":21.0,"HyperDash":false},{"StartTime":216298.0,"Position":302.0,"HyperDash":false},{"StartTime":216382.0,"Position":148.0,"HyperDash":false},{"StartTime":216466.0,"Position":431.0,"HyperDash":false},{"StartTime":216550.0,"Position":424.0,"HyperDash":false},{"StartTime":216634.0,"Position":14.0,"HyperDash":false},{"StartTime":216718.0,"Position":423.0,"HyperDash":false},{"StartTime":216802.0,"Position":16.0,"HyperDash":false},{"StartTime":216885.0,"Position":284.0,"HyperDash":false},{"StartTime":216969.0,"Position":201.0,"HyperDash":false},{"StartTime":217053.0,"Position":29.0,"HyperDash":false},{"StartTime":217137.0,"Position":203.0,"HyperDash":false},{"StartTime":217221.0,"Position":129.0,"HyperDash":false},{"StartTime":217305.0,"Position":285.0,"HyperDash":false},{"StartTime":217389.0,"Position":254.0,"HyperDash":false},{"StartTime":217473.0,"Position":145.0,"HyperDash":false},{"StartTime":217557.0,"Position":230.0,"HyperDash":false},{"StartTime":217641.0,"Position":466.0,"HyperDash":false},{"StartTime":217725.0,"Position":86.0,"HyperDash":false},{"StartTime":217809.0,"Position":434.0,"HyperDash":false},{"StartTime":217893.0,"Position":159.0,"HyperDash":false},{"StartTime":217977.0,"Position":493.0,"HyperDash":false},{"StartTime":218061.0,"Position":191.0,"HyperDash":false},{"StartTime":218145.0,"Position":200.0,"HyperDash":false}]},{"StartTime":219041.0,"Objects":[{"StartTime":219041.0,"Position":205.0,"HyperDash":false},{"StartTime":219092.0,"Position":176.805145,"HyperDash":false},{"StartTime":219143.0,"Position":178.610275,"HyperDash":false},{"StartTime":219195.0,"Position":155.058655,"HyperDash":false},{"StartTime":219246.0,"Position":127.8638,"HyperDash":false},{"StartTime":219297.0,"Position":96.12851,"HyperDash":false},{"StartTime":219349.0,"Position":102.176147,"HyperDash":false},{"StartTime":219400.0,"Position":75.54979,"HyperDash":false},{"StartTime":219488.0,"Position":51.8611755,"HyperDash":false}]},{"StartTime":219936.0,"Objects":[{"StartTime":219936.0,"Position":75.0,"HyperDash":false},{"StartTime":219987.0,"Position":82.19486,"HyperDash":false},{"StartTime":220038.0,"Position":115.389725,"HyperDash":false},{"StartTime":220090.0,"Position":110.941345,"HyperDash":false},{"StartTime":220141.0,"Position":143.1362,"HyperDash":false},{"StartTime":220192.0,"Position":161.87149,"HyperDash":false},{"StartTime":220244.0,"Position":186.823853,"HyperDash":false},{"StartTime":220295.0,"Position":188.4502,"HyperDash":false},{"StartTime":220383.0,"Position":228.138824,"HyperDash":false}]},{"StartTime":220832.0,"Objects":[{"StartTime":220832.0,"Position":337.0,"HyperDash":false},{"StartTime":220915.0,"Position":317.352051,"HyperDash":false},{"StartTime":220999.0,"Position":312.6722,"HyperDash":false},{"StartTime":221083.0,"Position":337.992371,"HyperDash":false},{"StartTime":221167.0,"Position":326.29657,"HyperDash":false},{"StartTime":221242.0,"Position":334.67334,"HyperDash":false},{"StartTime":221317.0,"Position":327.066071,"HyperDash":false},{"StartTime":221392.0,"Position":352.458771,"HyperDash":false},{"StartTime":221503.0,"Position":337.0,"HyperDash":false}]},{"StartTime":221951.0,"Objects":[{"StartTime":221951.0,"Position":457.0,"HyperDash":false},{"StartTime":222006.0,"Position":446.041077,"HyperDash":false},{"StartTime":222062.0,"Position":457.04657,"HyperDash":false},{"StartTime":222118.0,"Position":470.052032,"HyperDash":false},{"StartTime":222174.0,"Position":449.0397,"HyperDash":false},{"StartTime":222268.0,"Position":464.369843,"HyperDash":false},{"StartTime":222398.0,"Position":457.0,"HyperDash":false}]},{"StartTime":222623.0,"Objects":[{"StartTime":222623.0,"Position":495.0,"HyperDash":false}]},{"StartTime":223071.0,"Objects":[{"StartTime":223071.0,"Position":331.0,"HyperDash":false},{"StartTime":223154.0,"Position":317.6592,"HyperDash":false},{"StartTime":223238.0,"Position":271.751648,"HyperDash":false},{"StartTime":223322.0,"Position":250.900024,"HyperDash":false},{"StartTime":223406.0,"Position":215.870728,"HyperDash":false},{"StartTime":223481.0,"Position":250.346268,"HyperDash":false},{"StartTime":223556.0,"Position":287.9995,"HyperDash":false},{"StartTime":223631.0,"Position":302.435822,"HyperDash":false},{"StartTime":223742.0,"Position":331.0,"HyperDash":false}]},{"StartTime":223966.0,"Objects":[{"StartTime":223966.0,"Position":399.0,"HyperDash":false}]},{"StartTime":224414.0,"Objects":[{"StartTime":224414.0,"Position":471.0,"HyperDash":false},{"StartTime":224488.0,"Position":457.712158,"HyperDash":false},{"StartTime":224563.0,"Position":447.379883,"HyperDash":false},{"StartTime":224638.0,"Position":456.0476,"HyperDash":false},{"StartTime":224749.0,"Position":456.115845,"HyperDash":false}]},{"StartTime":225086.0,"Objects":[{"StartTime":225086.0,"Position":326.0,"HyperDash":false},{"StartTime":225137.0,"Position":300.208832,"HyperDash":false},{"StartTime":225188.0,"Position":275.417664,"HyperDash":false},{"StartTime":225240.0,"Position":290.316833,"HyperDash":false},{"StartTime":225291.0,"Position":243.612946,"HyperDash":false},{"StartTime":225342.0,"Position":241.580139,"HyperDash":false},{"StartTime":225394.0,"Position":232.193756,"HyperDash":false},{"StartTime":225445.0,"Position":210.16098,"HyperDash":false},{"StartTime":225533.0,"Position":175.045547,"HyperDash":false}]},{"StartTime":225757.0,"Objects":[{"StartTime":225757.0,"Position":88.0,"HyperDash":false},{"StartTime":225850.0,"Position":65.83169,"HyperDash":false},{"StartTime":225980.0,"Position":74.3185,"HyperDash":false}]},{"StartTime":226205.0,"Objects":[{"StartTime":226205.0,"Position":140.0,"HyperDash":false},{"StartTime":226298.0,"Position":123.645569,"HyperDash":false},{"StartTime":226428.0,"Position":143.945816,"HyperDash":false}]},{"StartTime":226653.0,"Objects":[{"StartTime":226653.0,"Position":116.0,"HyperDash":false},{"StartTime":226736.0,"Position":106.660728,"HyperDash":false},{"StartTime":226820.0,"Position":50.7313728,"HyperDash":false},{"StartTime":226904.0,"Position":15.8698654,"HyperDash":false},{"StartTime":226988.0,"Position":3.21379948,"HyperDash":false},{"StartTime":227063.0,"Position":23.62399,"HyperDash":false},{"StartTime":227138.0,"Position":46.0249748,"HyperDash":false},{"StartTime":227213.0,"Position":81.71385,"HyperDash":false},{"StartTime":227324.0,"Position":116.0,"HyperDash":false}]},{"StartTime":227548.0,"Objects":[{"StartTime":227548.0,"Position":202.0,"HyperDash":false},{"StartTime":227641.0,"Position":228.322632,"HyperDash":false},{"StartTime":227771.0,"Position":281.902618,"HyperDash":false}]},{"StartTime":227996.0,"Objects":[{"StartTime":227996.0,"Position":370.0,"HyperDash":false},{"StartTime":228047.0,"Position":379.322418,"HyperDash":false},{"StartTime":228098.0,"Position":404.644836,"HyperDash":false},{"StartTime":228150.0,"Position":412.9706,"HyperDash":false},{"StartTime":228201.0,"Position":407.2122,"HyperDash":false},{"StartTime":228252.0,"Position":406.4538,"HyperDash":false},{"StartTime":228304.0,"Position":390.660919,"HyperDash":false},{"StartTime":228355.0,"Position":399.902527,"HyperDash":false},{"StartTime":228443.0,"Position":393.8684,"HyperDash":false}]},{"StartTime":228892.0,"Objects":[{"StartTime":228892.0,"Position":291.0,"HyperDash":false},{"StartTime":228985.0,"Position":255.7421,"HyperDash":false},{"StartTime":229115.0,"Position":211.252533,"HyperDash":false}]},{"StartTime":229339.0,"Objects":[{"StartTime":229339.0,"Position":136.0,"HyperDash":false},{"StartTime":229432.0,"Position":97.7420959,"HyperDash":false},{"StartTime":229562.0,"Position":56.25254,"HyperDash":false}]},{"StartTime":229787.0,"Objects":[{"StartTime":229787.0,"Position":20.0,"HyperDash":false},{"StartTime":229838.0,"Position":17.0399265,"HyperDash":false},{"StartTime":229889.0,"Position":21.079855,"HyperDash":false},{"StartTime":229941.0,"Position":25.0421333,"HyperDash":false},{"StartTime":229992.0,"Position":22.7113285,"HyperDash":false},{"StartTime":230043.0,"Position":24.4840775,"HyperDash":false},{"StartTime":230095.0,"Position":14.3101463,"HyperDash":false},{"StartTime":230146.0,"Position":9.403353,"HyperDash":false},{"StartTime":230234.0,"Position":15.3877077,"HyperDash":false}]},{"StartTime":230683.0,"Objects":[{"StartTime":230683.0,"Position":156.0,"HyperDash":false},{"StartTime":230776.0,"Position":173.746826,"HyperDash":false},{"StartTime":230906.0,"Position":186.4041,"HyperDash":false}]},{"StartTime":231131.0,"Objects":[{"StartTime":231131.0,"Position":264.0,"HyperDash":false},{"StartTime":231224.0,"Position":253.253189,"HyperDash":false},{"StartTime":231354.0,"Position":233.595917,"HyperDash":false}]},{"StartTime":231578.0,"Objects":[{"StartTime":231578.0,"Position":262.0,"HyperDash":false},{"StartTime":231629.0,"Position":267.8308,"HyperDash":false},{"StartTime":231680.0,"Position":297.661621,"HyperDash":false},{"StartTime":231732.0,"Position":320.886,"HyperDash":false},{"StartTime":231783.0,"Position":341.016968,"HyperDash":false},{"StartTime":231834.0,"Position":347.147919,"HyperDash":false},{"StartTime":231886.0,"Position":352.6344,"HyperDash":false},{"StartTime":231937.0,"Position":373.76535,"HyperDash":false},{"StartTime":232025.0,"Position":417.05014,"HyperDash":false}]},{"StartTime":232250.0,"Objects":[{"StartTime":232250.0,"Position":479.0,"HyperDash":false}]},{"StartTime":232474.0,"Objects":[{"StartTime":232474.0,"Position":500.0,"HyperDash":false},{"StartTime":232567.0,"Position":485.105865,"HyperDash":false},{"StartTime":232697.0,"Position":481.10556,"HyperDash":false}]},{"StartTime":232922.0,"Objects":[{"StartTime":232922.0,"Position":396.0,"HyperDash":false},{"StartTime":233015.0,"Position":344.7835,"HyperDash":false},{"StartTime":233145.0,"Position":320.1601,"HyperDash":false}]},{"StartTime":233369.0,"Objects":[{"StartTime":233369.0,"Position":264.0,"HyperDash":false},{"StartTime":233420.0,"Position":256.891846,"HyperDash":false},{"StartTime":233471.0,"Position":238.654755,"HyperDash":false},{"StartTime":233523.0,"Position":225.308167,"HyperDash":false},{"StartTime":233574.0,"Position":201.429947,"HyperDash":false},{"StartTime":233625.0,"Position":188.241165,"HyperDash":false},{"StartTime":233677.0,"Position":164.716415,"HyperDash":false},{"StartTime":233728.0,"Position":147.656219,"HyperDash":false},{"StartTime":233816.0,"Position":109.216957,"HyperDash":false}]},{"StartTime":234265.0,"Objects":[{"StartTime":234265.0,"Position":39.0,"HyperDash":false},{"StartTime":234320.0,"Position":18.3255081,"HyperDash":false},{"StartTime":234376.0,"Position":20.620575,"HyperDash":false},{"StartTime":234432.0,"Position":27.915638,"HyperDash":false},{"StartTime":234488.0,"Position":32.19548,"HyperDash":false},{"StartTime":234582.0,"Position":41.0421143,"HyperDash":false},{"StartTime":234712.0,"Position":39.0,"HyperDash":false}]},{"StartTime":234936.0,"Objects":[{"StartTime":234936.0,"Position":214.0,"HyperDash":false}]},{"StartTime":235160.0,"Objects":[{"StartTime":235160.0,"Position":206.0,"HyperDash":false},{"StartTime":235215.0,"Position":221.503036,"HyperDash":false},{"StartTime":235271.0,"Position":257.307831,"HyperDash":false},{"StartTime":235327.0,"Position":245.8396,"HyperDash":false},{"StartTime":235383.0,"Position":280.764069,"HyperDash":false},{"StartTime":235439.0,"Position":285.755646,"HyperDash":false},{"StartTime":235495.0,"Position":305.4811,"HyperDash":false},{"StartTime":235551.0,"Position":349.636,"HyperDash":false},{"StartTime":235607.0,"Position":359.014679,"HyperDash":false},{"StartTime":235658.0,"Position":335.625427,"HyperDash":false},{"StartTime":235710.0,"Position":307.9622,"HyperDash":false},{"StartTime":235761.0,"Position":298.080017,"HyperDash":false},{"StartTime":235813.0,"Position":295.555237,"HyperDash":false},{"StartTime":235864.0,"Position":258.349457,"HyperDash":false},{"StartTime":235916.0,"Position":266.998871,"HyperDash":false},{"StartTime":235967.0,"Position":250.476349,"HyperDash":false},{"StartTime":236055.0,"Position":206.0,"HyperDash":false}]},{"StartTime":236280.0,"Objects":[{"StartTime":236280.0,"Position":136.0,"HyperDash":false},{"StartTime":236335.0,"Position":133.3588,"HyperDash":false},{"StartTime":236391.0,"Position":108.360489,"HyperDash":false},{"StartTime":236447.0,"Position":81.36217,"HyperDash":false},{"StartTime":236503.0,"Position":56.1853027,"HyperDash":false},{"StartTime":236597.0,"Position":85.57534,"HyperDash":false},{"StartTime":236727.0,"Position":136.0,"HyperDash":false}]},{"StartTime":236951.0,"Objects":[{"StartTime":236951.0,"Position":203.0,"HyperDash":false},{"StartTime":237002.0,"Position":235.515,"HyperDash":false},{"StartTime":237053.0,"Position":231.03,"HyperDash":false},{"StartTime":237105.0,"Position":235.849213,"HyperDash":false},{"StartTime":237156.0,"Position":257.413,"HyperDash":false},{"StartTime":237207.0,"Position":301.49884,"HyperDash":false},{"StartTime":237259.0,"Position":304.939331,"HyperDash":false},{"StartTime":237310.0,"Position":305.025177,"HyperDash":false},{"StartTime":237398.0,"Position":353.232178,"HyperDash":false}]},{"StartTime":237847.0,"Objects":[{"StartTime":237847.0,"Position":468.0,"HyperDash":false},{"StartTime":237898.0,"Position":450.485,"HyperDash":false},{"StartTime":237949.0,"Position":421.97,"HyperDash":false},{"StartTime":238001.0,"Position":401.1508,"HyperDash":false},{"StartTime":238052.0,"Position":410.587,"HyperDash":false},{"StartTime":238103.0,"Position":391.50116,"HyperDash":false},{"StartTime":238155.0,"Position":374.060669,"HyperDash":false},{"StartTime":238206.0,"Position":362.974823,"HyperDash":false},{"StartTime":238294.0,"Position":317.767822,"HyperDash":false}]},{"StartTime":238742.0,"Objects":[{"StartTime":238742.0,"Position":180.0,"HyperDash":false},{"StartTime":238793.0,"Position":173.605637,"HyperDash":false},{"StartTime":238844.0,"Position":127.565094,"HyperDash":false},{"StartTime":238896.0,"Position":130.980515,"HyperDash":false},{"StartTime":238947.0,"Position":94.05988,"HyperDash":false},{"StartTime":238998.0,"Position":89.93131,"HyperDash":false},{"StartTime":239050.0,"Position":62.7224731,"HyperDash":false},{"StartTime":239101.0,"Position":61.4846268,"HyperDash":false},{"StartTime":239189.0,"Position":40.9435463,"HyperDash":false}]},{"StartTime":239414.0,"Objects":[{"StartTime":239414.0,"Position":1.0,"HyperDash":false}]},{"StartTime":239638.0,"Objects":[{"StartTime":239638.0,"Position":65.0,"HyperDash":false},{"StartTime":239731.0,"Position":90.205574,"HyperDash":false},{"StartTime":239861.0,"Position":144.621979,"HyperDash":false}]},{"StartTime":240086.0,"Objects":[{"StartTime":240086.0,"Position":205.0,"HyperDash":false},{"StartTime":240179.0,"Position":248.205566,"HyperDash":false},{"StartTime":240309.0,"Position":284.621979,"HyperDash":false}]},{"StartTime":240534.0,"Objects":[{"StartTime":240534.0,"Position":366.0,"HyperDash":false},{"StartTime":240585.0,"Position":363.81723,"HyperDash":false},{"StartTime":240636.0,"Position":373.2125,"HyperDash":false},{"StartTime":240688.0,"Position":391.223755,"HyperDash":false},{"StartTime":240739.0,"Position":374.622253,"HyperDash":false},{"StartTime":240790.0,"Position":403.4788,"HyperDash":false},{"StartTime":240842.0,"Position":402.731842,"HyperDash":false},{"StartTime":240893.0,"Position":374.42746,"HyperDash":false},{"StartTime":240981.0,"Position":383.571564,"HyperDash":false}]},{"StartTime":241429.0,"Objects":[{"StartTime":241429.0,"Position":238.0,"HyperDash":false},{"StartTime":241480.0,"Position":236.18277,"HyperDash":false},{"StartTime":241531.0,"Position":235.787491,"HyperDash":false},{"StartTime":241583.0,"Position":219.776245,"HyperDash":false},{"StartTime":241634.0,"Position":209.377747,"HyperDash":false},{"StartTime":241685.0,"Position":209.52121,"HyperDash":false},{"StartTime":241737.0,"Position":227.268158,"HyperDash":false},{"StartTime":241788.0,"Position":224.572525,"HyperDash":false},{"StartTime":241876.0,"Position":220.428436,"HyperDash":false}]},{"StartTime":242325.0,"Objects":[{"StartTime":242325.0,"Position":297.0,"HyperDash":false},{"StartTime":242380.0,"Position":294.727844,"HyperDash":false},{"StartTime":242436.0,"Position":344.7645,"HyperDash":false},{"StartTime":242492.0,"Position":365.6436,"HyperDash":false},{"StartTime":242548.0,"Position":374.1311,"HyperDash":false},{"StartTime":242604.0,"Position":400.993958,"HyperDash":false},{"StartTime":242660.0,"Position":429.0038,"HyperDash":false},{"StartTime":242716.0,"Position":433.924957,"HyperDash":false},{"StartTime":242772.0,"Position":449.6772,"HyperDash":false},{"StartTime":242823.0,"Position":429.0385,"HyperDash":false},{"StartTime":242875.0,"Position":395.5763,"HyperDash":false},{"StartTime":242926.0,"Position":382.350677,"HyperDash":false},{"StartTime":242978.0,"Position":386.8408,"HyperDash":false},{"StartTime":243029.0,"Position":359.934326,"HyperDash":false},{"StartTime":243081.0,"Position":325.105682,"HyperDash":false},{"StartTime":243132.0,"Position":316.240143,"HyperDash":false},{"StartTime":243220.0,"Position":297.0,"HyperDash":false}]},{"StartTime":243444.0,"Objects":[{"StartTime":243444.0,"Position":216.0,"HyperDash":false}]},{"StartTime":243668.0,"Objects":[{"StartTime":243668.0,"Position":136.0,"HyperDash":false},{"StartTime":243761.0,"Position":118.763763,"HyperDash":false},{"StartTime":243891.0,"Position":56.3044968,"HyperDash":false}]},{"StartTime":244116.0,"Objects":[{"StartTime":244116.0,"Position":2.0,"HyperDash":false},{"StartTime":244167.0,"Position":18.7359352,"HyperDash":false},{"StartTime":244218.0,"Position":16.5715733,"HyperDash":false},{"StartTime":244270.0,"Position":30.5787716,"HyperDash":false},{"StartTime":244321.0,"Position":26.5514565,"HyperDash":false},{"StartTime":244372.0,"Position":31.5832443,"HyperDash":false},{"StartTime":244424.0,"Position":12.6607952,"HyperDash":false},{"StartTime":244475.0,"Position":44.7331161,"HyperDash":false},{"StartTime":244563.0,"Position":29.33616,"HyperDash":false}]},{"StartTime":244787.0,"Objects":[{"StartTime":244787.0,"Position":5.0,"HyperDash":false}]},{"StartTime":245011.0,"Objects":[{"StartTime":245011.0,"Position":64.0,"HyperDash":false},{"StartTime":245104.0,"Position":111.355347,"HyperDash":false},{"StartTime":245234.0,"Position":143.98111,"HyperDash":false}]},{"StartTime":245459.0,"Objects":[{"StartTime":245459.0,"Position":223.0,"HyperDash":false},{"StartTime":245552.0,"Position":239.355347,"HyperDash":false},{"StartTime":245682.0,"Position":302.9811,"HyperDash":false}]},{"StartTime":245907.0,"Objects":[{"StartTime":245907.0,"Position":379.0,"HyperDash":false},{"StartTime":245958.0,"Position":388.482635,"HyperDash":false},{"StartTime":246009.0,"Position":389.9838,"HyperDash":false},{"StartTime":246061.0,"Position":389.548859,"HyperDash":false},{"StartTime":246112.0,"Position":388.03,"HyperDash":false},{"StartTime":246163.0,"Position":411.496918,"HyperDash":false},{"StartTime":246215.0,"Position":407.907623,"HyperDash":false},{"StartTime":246266.0,"Position":408.277283,"HyperDash":false},{"StartTime":246354.0,"Position":392.807251,"HyperDash":false}]},{"StartTime":246802.0,"Objects":[{"StartTime":246802.0,"Position":240.0,"HyperDash":false},{"StartTime":246895.0,"Position":229.351563,"HyperDash":false},{"StartTime":247025.0,"Position":219.99736,"HyperDash":false}]},{"StartTime":247250.0,"Objects":[{"StartTime":247250.0,"Position":152.0,"HyperDash":false},{"StartTime":247343.0,"Position":152.648453,"HyperDash":false},{"StartTime":247473.0,"Position":172.00264,"HyperDash":false}]},{"StartTime":247698.0,"Objects":[{"StartTime":247698.0,"Position":118.0,"HyperDash":false},{"StartTime":247749.0,"Position":132.050278,"HyperDash":false},{"StartTime":247800.0,"Position":136.635315,"HyperDash":false},{"StartTime":247852.0,"Position":178.833282,"HyperDash":false},{"StartTime":247903.0,"Position":187.755432,"HyperDash":false},{"StartTime":247954.0,"Position":198.440247,"HyperDash":false},{"StartTime":248006.0,"Position":210.910767,"HyperDash":false},{"StartTime":248057.0,"Position":231.14679,"HyperDash":false},{"StartTime":248145.0,"Position":263.981781,"HyperDash":false}]},{"StartTime":248593.0,"Objects":[{"StartTime":248593.0,"Position":427.0,"HyperDash":false},{"StartTime":248644.0,"Position":435.745178,"HyperDash":false},{"StartTime":248695.0,"Position":436.695557,"HyperDash":false},{"StartTime":248747.0,"Position":452.8285,"HyperDash":false},{"StartTime":248798.0,"Position":483.6382,"HyperDash":false},{"StartTime":248849.0,"Position":486.1087,"HyperDash":false},{"StartTime":248901.0,"Position":482.262,"HyperDash":false},{"StartTime":248952.0,"Position":487.997559,"HyperDash":false},{"StartTime":249040.0,"Position":466.941925,"HyperDash":false}]},{"StartTime":249489.0,"Objects":[{"StartTime":249489.0,"Position":411.0,"HyperDash":false},{"StartTime":249544.0,"Position":390.345581,"HyperDash":false},{"StartTime":249600.0,"Position":379.354645,"HyperDash":false},{"StartTime":249656.0,"Position":353.452728,"HyperDash":false},{"StartTime":249712.0,"Position":332.7199,"HyperDash":false},{"StartTime":249768.0,"Position":300.245636,"HyperDash":false},{"StartTime":249824.0,"Position":302.125122,"HyperDash":false},{"StartTime":249880.0,"Position":276.454742,"HyperDash":false},{"StartTime":249936.0,"Position":257.2194,"HyperDash":false},{"StartTime":249992.0,"Position":266.046967,"HyperDash":false},{"StartTime":250048.0,"Position":259.874542,"HyperDash":false},{"StartTime":250104.0,"Position":287.702118,"HyperDash":false},{"StartTime":250160.0,"Position":273.529663,"HyperDash":false},{"StartTime":250216.0,"Position":266.357239,"HyperDash":false},{"StartTime":250272.0,"Position":273.1848,"HyperDash":false},{"StartTime":250328.0,"Position":293.0124,"HyperDash":false},{"StartTime":250384.0,"Position":303.839966,"HyperDash":false},{"StartTime":250435.0,"Position":300.715057,"HyperDash":false},{"StartTime":250487.0,"Position":268.3105,"HyperDash":false},{"StartTime":250538.0,"Position":273.694855,"HyperDash":false},{"StartTime":250590.0,"Position":252.267029,"HyperDash":false},{"StartTime":250641.0,"Position":220.7485,"HyperDash":false},{"StartTime":250693.0,"Position":222.549347,"HyperDash":false},{"StartTime":250744.0,"Position":192.463943,"HyperDash":false},{"StartTime":250832.0,"Position":161.041138,"HyperDash":false}]},{"StartTime":251280.0,"Objects":[{"StartTime":251280.0,"Position":21.0,"HyperDash":false},{"StartTime":251363.0,"Position":30.73253,"HyperDash":false},{"StartTime":251447.0,"Position":6.497982,"HyperDash":false},{"StartTime":251531.0,"Position":32.2634354,"HyperDash":false},{"StartTime":251615.0,"Position":32.04535,"HyperDash":false},{"StartTime":251690.0,"Position":26.5926552,"HyperDash":false},{"StartTime":251765.0,"Position":30.1235,"HyperDash":false},{"StartTime":251840.0,"Position":42.65435,"HyperDash":false},{"StartTime":251951.0,"Position":21.0,"HyperDash":false}]},{"StartTime":252175.0,"Objects":[{"StartTime":252175.0,"Position":2.0,"HyperDash":false},{"StartTime":252226.0,"Position":0.0,"HyperDash":false},{"StartTime":252277.0,"Position":0.0,"HyperDash":false},{"StartTime":252329.0,"Position":0.0,"HyperDash":false},{"StartTime":252380.0,"Position":21.3323555,"HyperDash":false},{"StartTime":252431.0,"Position":0.887104034,"HyperDash":false},{"StartTime":252483.0,"Position":30.8665218,"HyperDash":false},{"StartTime":252534.0,"Position":43.70045,"HyperDash":false},{"StartTime":252622.0,"Position":61.3145256,"HyperDash":false}]},{"StartTime":253071.0,"Objects":[{"StartTime":253071.0,"Position":388.0,"HyperDash":false}]},{"StartTime":259563.0,"Objects":[{"StartTime":259563.0,"Position":293.0,"HyperDash":false},{"StartTime":259618.0,"Position":312.5664,"HyperDash":false},{"StartTime":259674.0,"Position":329.488525,"HyperDash":false},{"StartTime":259730.0,"Position":338.410675,"HyperDash":false},{"StartTime":259786.0,"Position":372.510681,"HyperDash":false},{"StartTime":259880.0,"Position":328.247833,"HyperDash":false},{"StartTime":260010.0,"Position":293.0,"HyperDash":false}]},{"StartTime":260235.0,"Objects":[{"StartTime":260235.0,"Position":46.0,"HyperDash":false}]},{"StartTime":260683.0,"Objects":[{"StartTime":260683.0,"Position":115.0,"HyperDash":false},{"StartTime":260766.0,"Position":105.132309,"HyperDash":false},{"StartTime":260850.0,"Position":48.58029,"HyperDash":false},{"StartTime":260934.0,"Position":44.7662964,"HyperDash":false},{"StartTime":261018.0,"Position":0.7381573,"HyperDash":false},{"StartTime":261093.0,"Position":18.1906548,"HyperDash":false},{"StartTime":261168.0,"Position":39.9074936,"HyperDash":false},{"StartTime":261243.0,"Position":61.8353424,"HyperDash":false},{"StartTime":261354.0,"Position":115.0,"HyperDash":false}]},{"StartTime":261578.0,"Objects":[{"StartTime":261578.0,"Position":189.0,"HyperDash":false},{"StartTime":261671.0,"Position":204.326355,"HyperDash":false},{"StartTime":261801.0,"Position":268.91156,"HyperDash":false}]},{"StartTime":262026.0,"Objects":[{"StartTime":262026.0,"Position":334.0,"HyperDash":false},{"StartTime":262119.0,"Position":377.326355,"HyperDash":false},{"StartTime":262249.0,"Position":413.91156,"HyperDash":false}]},{"StartTime":262474.0,"Objects":[{"StartTime":262474.0,"Position":480.0,"HyperDash":false},{"StartTime":262557.0,"Position":478.9318,"HyperDash":false},{"StartTime":262641.0,"Position":487.9834,"HyperDash":false},{"StartTime":262725.0,"Position":487.311127,"HyperDash":false},{"StartTime":262809.0,"Position":470.9604,"HyperDash":false},{"StartTime":262884.0,"Position":456.461121,"HyperDash":false},{"StartTime":262959.0,"Position":484.5402,"HyperDash":false},{"StartTime":263034.0,"Position":483.23175,"HyperDash":false},{"StartTime":263145.0,"Position":480.0,"HyperDash":false}]},{"StartTime":263369.0,"Objects":[{"StartTime":263369.0,"Position":497.0,"HyperDash":false},{"StartTime":263462.0,"Position":512.0,"HyperDash":false},{"StartTime":263592.0,"Position":495.6382,"HyperDash":false}]},{"StartTime":263817.0,"Objects":[{"StartTime":263817.0,"Position":374.0,"HyperDash":false}]},{"StartTime":264265.0,"Objects":[{"StartTime":264265.0,"Position":262.0,"HyperDash":false},{"StartTime":264348.0,"Position":218.500381,"HyperDash":false},{"StartTime":264432.0,"Position":198.645355,"HyperDash":false},{"StartTime":264516.0,"Position":171.790344,"HyperDash":false},{"StartTime":264600.0,"Position":142.7576,"HyperDash":false},{"StartTime":264675.0,"Position":162.23616,"HyperDash":false},{"StartTime":264750.0,"Position":212.892441,"HyperDash":false},{"StartTime":264825.0,"Position":225.5487,"HyperDash":false},{"StartTime":264936.0,"Position":262.0,"HyperDash":false}]},{"StartTime":265160.0,"Objects":[{"StartTime":265160.0,"Position":329.0,"HyperDash":false},{"StartTime":265253.0,"Position":325.012848,"HyperDash":false},{"StartTime":265383.0,"Position":319.4394,"HyperDash":false}]},{"StartTime":265608.0,"Objects":[{"StartTime":265608.0,"Position":254.0,"HyperDash":false},{"StartTime":265701.0,"Position":204.112839,"HyperDash":false},{"StartTime":265831.0,"Position":176.4014,"HyperDash":false}]},{"StartTime":266056.0,"Objects":[{"StartTime":266056.0,"Position":95.0,"HyperDash":false},{"StartTime":266139.0,"Position":98.58954,"HyperDash":false},{"StartTime":266223.0,"Position":69.13798,"HyperDash":false},{"StartTime":266307.0,"Position":66.6864243,"HyperDash":false},{"StartTime":266391.0,"Position":81.214325,"HyperDash":false},{"StartTime":266466.0,"Position":67.27553,"HyperDash":false},{"StartTime":266541.0,"Position":104.357269,"HyperDash":false},{"StartTime":266616.0,"Position":108.439018,"HyperDash":false},{"StartTime":266727.0,"Position":95.0,"HyperDash":false}]},{"StartTime":266951.0,"Objects":[{"StartTime":266951.0,"Position":146.0,"HyperDash":false}]},{"StartTime":267175.0,"Objects":[{"StartTime":267175.0,"Position":210.0,"HyperDash":false}]},{"StartTime":267399.0,"Objects":[{"StartTime":267399.0,"Position":264.0,"HyperDash":false},{"StartTime":267492.0,"Position":314.92868,"HyperDash":false},{"StartTime":267622.0,"Position":342.981537,"HyperDash":false}]},{"StartTime":267847.0,"Objects":[{"StartTime":267847.0,"Position":395.0,"HyperDash":false},{"StartTime":267930.0,"Position":404.894226,"HyperDash":false},{"StartTime":268014.0,"Position":439.331,"HyperDash":false},{"StartTime":268098.0,"Position":471.236,"HyperDash":false},{"StartTime":268182.0,"Position":509.962616,"HyperDash":false},{"StartTime":268257.0,"Position":484.739044,"HyperDash":false},{"StartTime":268332.0,"Position":448.118866,"HyperDash":false},{"StartTime":268407.0,"Position":434.531128,"HyperDash":false},{"StartTime":268518.0,"Position":395.0,"HyperDash":false}]},{"StartTime":268742.0,"Objects":[{"StartTime":268742.0,"Position":326.0,"HyperDash":false},{"StartTime":268797.0,"Position":303.009155,"HyperDash":false},{"StartTime":268853.0,"Position":275.691162,"HyperDash":false},{"StartTime":268909.0,"Position":273.2251,"HyperDash":false},{"StartTime":268965.0,"Position":238.87439,"HyperDash":false},{"StartTime":269021.0,"Position":234.523651,"HyperDash":false},{"StartTime":269077.0,"Position":225.172928,"HyperDash":false},{"StartTime":269133.0,"Position":194.8222,"HyperDash":false},{"StartTime":269189.0,"Position":174.471481,"HyperDash":false},{"StartTime":269283.0,"Position":186.943192,"HyperDash":false},{"StartTime":269413.0,"Position":214.870941,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1431386.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1431386.osu new file mode 100644 index 0000000000..aa82b6ef8c --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1431386.osu @@ -0,0 +1,560 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:4 +CircleSize:3.3 +OverallDifficulty:4 +ApproachRate:5 +SliderMultiplier:1.6 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +2,86704,92468 +2,208494,214259 +2,253271,258363 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +534,447.761194029851,4,2,1,40,1,0 +4116,-100,4,2,1,20,0,0 +4563,-100,4,2,1,40,0,0 +5011,-100,4,2,1,60,0,0 +5459,-100,4,2,1,80,0,0 +5907,-100,4,2,1,80,0,0 +6242,-100,4,2,1,80,0,0 +6578,-100,4,2,1,80,0,0 +6802,-100,4,2,1,80,0,0 +7250,-100,4,2,1,80,0,0 +7922,-100,4,2,1,80,0,0 +14862,-100,4,2,1,80,0,0 +16653,-100,4,2,1,80,0,0 +22922,-100,4,2,1,80,0,0 +23369,-100,4,2,1,80,0,0 +24041,-100,4,2,1,80,0,0 +24265,-100,4,2,1,80,0,0 +26728,-100,4,2,1,80,0,0 +26951,-100,4,2,1,80,0,0 +27175,-100,4,2,1,80,0,0 +27399,-100,4,2,1,80,0,0 +29414,-100,4,2,1,80,0,0 +30981,-100,4,2,1,80,0,0 +38145,-100,4,2,1,80,0,0 +40832,-100,4,2,1,80,0,0 +41728,-100,4,2,1,80,0,0 +45310,-100,4,2,1,40,0,0 +45757,-100,4,2,1,40,0,0 +46205,-100,4,2,1,60,0,0 +46653,-100,4,2,1,80,0,0 +47101,-100,4,2,1,80,0,1 +47436,-100,4,2,1,80,0,1 +47772,-100,4,2,1,80,0,1 +47996,-100,4,2,1,80,0,0 +48892,-100,4,2,1,80,0,1 +56504,-100,4,2,1,80,0,1 +56728,-100,4,2,1,80,0,1 +58295,-100,4,2,1,80,0,1 +58519,-100,4,2,1,80,0,1 +62772,-100,4,2,1,80,0,1 +63220,-100,4,2,1,80,0,1 +63444,-100,4,2,1,80,0,1 +63668,-100,4,2,1,80,0,1 +70832,-100,4,2,1,80,0,1 +71056,-100,4,2,1,80,0,1 +72623,-100,4,2,1,80,0,1 +73071,-100,4,2,1,80,0,1 +73519,-100,4,2,1,80,0,1 +73966,-100,4,2,1,80,0,1 +75757,-100,4,2,1,80,0,1 +75981,-100,4,2,1,80,0,1 +77325,-100,4,2,1,80,0,1 +77548,-100,4,2,1,80,0,0 +80235,-100,4,2,1,80,0,0 +81131,-100,4,2,1,80,0,0 +82922,-100,4,2,1,80,0,0 +84713,-100,4,2,1,80,0,1 +85048,-100,4,2,1,80,0,1 +85384,-100,4,2,1,80,0,1 +85608,-100,4,2,1,80,0,1 +86056,-100,4,2,1,80,0,0 +93444,-100,4,2,1,80,0,0 +93668,-100,4,2,1,80,0,0 +101728,-100,4,2,1,80,0,0 +102175,-100,4,2,0,80,0,0 +102623,-100,4,2,1,80,0,0 +102847,-100,4,2,1,80,0,0 +103071,-100,4,2,1,80,0,0 +116951,-100,4,2,1,80,0,0 +119638,-100,4,2,1,80,0,0 +120534,-100,4,2,1,80,0,0 +124116,-100,4,2,0,80,0,0 +125459,-100,4,2,1,80,0,0 +125907,-100,4,2,1,80,0,1 +126242,-100,4,2,1,80,0,1 +126578,-100,4,2,1,80,0,1 +126802,-100,4,2,1,80,0,0 +127474,-100,4,2,1,80,0,0 +127698,-100,4,2,1,80,0,1 +135310,-100,4,2,1,80,0,1 +135534,-100,4,2,1,80,0,1 +137101,-100,4,2,1,80,0,1 +137325,-100,4,2,1,80,0,1 +142250,-100,4,2,1,80,0,1 +142474,-100,4,2,1,80,0,1 +149638,-100,4,2,1,80,0,1 +149862,-100,4,2,1,80,0,1 +151429,-100,4,2,1,80,0,1 +151877,-100,4,2,1,80,0,1 +152325,-100,4,2,1,80,0,1 +152772,-100,4,2,1,80,0,1 +154563,-100,4,2,1,80,0,1 +154787,-100,4,2,1,80,0,1 +156354,-100,4,2,1,80,0,0 +159041,-100,4,2,1,80,0,0 +159936,-100,4,2,1,80,0,0 +161168,-100,4,2,0,80,0,0 +161728,-100,4,2,1,80,0,0 +162623,-100,4,2,1,80,0,1 +163519,-100,4,2,1,80,0,0 +164414,-100,4,2,1,80,0,1 +165310,-100,4,2,1,80,0,0 +166205,-100,4,2,1,80,0,1 +167101,-100,4,2,1,80,0,0 +168332,-100,4,2,1,80,0,0 +168892,-100,4,2,1,80,0,0 +169787,-100,4,2,1,80,0,1 +170683,-100,4,2,1,80,0,0 +171578,-100,4,2,1,80,0,1 +172474,-100,4,2,1,80,0,0 +173369,-100,4,2,1,80,0,1 +173705,-100,4,2,1,80,0,0 +173929,-100,4,2,0,80,0,0 +174265,-100,4,2,1,80,0,0 +175048,-100,4,2,1,80,0,0 +175608,-100,4,2,1,80,0,0 +175832,-100,4,2,1,80,0,0 +176056,-100,4,2,1,80,0,1 +186131,-100,4,2,1,80,0,1 +186354,-100,4,2,1,80,0,1 +190384,-100,4,2,1,80,0,0 +206504,-100,4,2,1,80,0,0 +206839,-100,4,2,1,80,0,0 +207175,-100,4,2,1,80,0,0 +207399,-100,4,2,1,80,0,0 +207623,-100,4,2,1,80,0,0 +208071,-100,4,2,1,80,0,0 +208295,-100,4,2,1,80,0,0 +219041,-100,4,2,1,80,0,0 +220832,-100,4,2,1,80,0,0 +223071,-100,4,2,1,80,0,1 +223407,-100,4,2,1,80,0,0 +224414,-100,4,2,1,80,0,1 +225086,-100,4,2,1,80,0,1 +225422,-100,4,2,1,80,0,0 +226205,-100,4,2,1,80,0,1 +230235,-100,4,2,1,80,0,1 +230683,-100,4,2,1,80,0,1 +232026,-100,4,2,1,80,0,1 +232474,-100,4,2,1,80,0,1 +232922,-100,4,2,1,80,0,1 +233369,-100,4,2,1,80,0,1 +236951,-100,4,2,1,80,0,0 +239414,-100,4,2,1,80,0,0 +240533,-100,4,2,1,80,0,0 +244116,-100,4,2,1,80,0,0 +247698,-100,4,2,1,80,0,0 +250384,-100,4,2,1,80,0,0 +251280,-100,4,2,1,80,0,0 +251616,-100,4,2,1,80,0,0 +251951,-100,4,2,1,80,0,0 +252175,-100,4,2,1,80,0,0 +252623,-100,4,2,1,80,0,0 +253071,-100,4,2,1,80,0,0 +259563,-100,4,2,1,80,0,0 +260235,-100,4,2,1,80,0,0 +263593,-100,4,2,1,80,0,0 +263817,-100,4,2,1,80,0,0 +267399,-100,4,2,1,80,0,0 +268742,-100,4,2,1,80,0,0 +269414,-100,4,2,1,5,0,0 + +[HitObjects] +333,114,534,6,0,B|379:97|379:97|497:110,2,160,4|0|0,3:2|0:2|0:2,0:0:0:0: +182,204,1877,1,0,0:2:0:0: +333,290,2325,6,0,B|385:301|385:301|441:292|441:292|497:303,2,160,4|0|0,3:2|0:2|0:2,0:0:0:0: +182,204,3668,1,0,0:2:0:0: +26,121,4116,6,0,L|30:34,2,80,12|8|8,3:2|0:2|0:2,0:0:0:0: +20,297,5011,2,0,P|58:297|100:311,1,80,8|8,0:2|0:2,0:0:0:0: +178,348,5459,2,0,P|217:335|258:335,1,80,8|8,0:2|0:2,0:0:0:0: +308,264,5907,6,0,L|445:280,2,120,12|12|12,3:2|3:2|3:2,0:0:0:0: +224,234,6802,2,0,P|197:171|223:66,1,160,12|12,3:2|0:2,0:0:0:0: +372,10,7698,6,0,P|389:44|391:94,1,80,4|8,3:2|0:2,0:0:0:0: +390,173,8145,2,0,L|516:164,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +330,237,9041,2,0,L|234:230,1,80,0|8,3:2|0:2,0:0:0:0: +171,190,9489,6,0,P|118:184|79:197,1,80,0|8,3:2|0:2,0:0:0:0: +9,219,9936,2,0,L|0:99,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +28,305,10832,2,0,P|67:300|105:311,1,80,0|8,3:2|0:2,0:0:0:0: +184,353,11280,5,0,3:2:0:0: +343,277,11728,2,0,P|404:295|470:285,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +290,206,12623,2,0,L|297:118,1,80,0|8,3:2|0:2,0:0:0:0: +265,43,13071,6,0,P|222:34|179:42,1,80,0|8,3:2|0:2,0:0:0:0: +123,100,13519,2,0,L|3:92,2,120,0|0|0,3:2|0:2|0:2,0:0:0:0: +187,160,14414,1,8,0:2:0:0: +184,336,14862,6,0,P|218:348|274:342,1,80,4|2,3:2|0:2,0:0:0:0: +343,310,15310,2,0,L|466:328,2,120,2|2|0,0:2|0:2|3:2,0:0:0:0: +297,234,16205,1,8,0:2:0:0: +219,76,16653,6,0,P|176:72|131:90,1,80,4|8,3:2|0:2,0:0:0:0: +65,129,17101,2,0,P|26:85|17:27,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +144,170,17996,2,0,L|137:250,1,80,0|8,3:2|0:2,0:0:0:0: +156,336,18444,6,0,P|198:347|241:341,1,80,0|8,3:2|0:2,0:0:0:0: +309,296,18892,2,0,L|430:310,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +237,245,19787,2,0,P|229:197|236:162,1,80,0|8,3:2|0:2,0:0:0:0: +296,103,20235,6,0,P|344:95|379:102,1,80,0|8,3:2|0:2,0:0:0:0: +441,157,20683,2,0,P|423:95|448:35,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +501,220,21578,1,0,3:2:0:0: +386,353,22026,6,0,B|304:367|241:304|241:304|164:362|79:328,2,320,0|2|0,3:2|3:2|3:2,0:0:0:0: +465,315,24041,5,12,0:2:0:0: +497,233,24265,2,0,L|486:100,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +410,247,25160,2,0,P|365:251|331:241,1,80,0|8,3:2|0:2,0:0:0:0: +262,187,25608,6,0,P|223:176|183:181,1,80,0|8,3:2|0:2,0:0:0:0: +136,254,26056,2,0,L|145:381,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +67,198,26951,1,0,3:2:0:0: +118,29,27399,6,0,P|170:19|228:167,1,240,4|8,3:2|0:2,0:0:0:0: +162,107,28295,2,0,B|240:90|240:90|316:114|316:114|409:97,1,240,4|8,3:2|0:2,0:0:0:0: +481,84,29190,5,0,3:2:0:0: +499,170,29414,1,12,0:2:0:0: +454,246,29638,2,0,L|472:376,2,120,8|8|0,0:2|3:2|0:0,0:0:0:0: +375,205,30533,2,0,P|329:207|286:227,1,80,8|8,0:2|0:2,0:0:0:0: +220,263,30981,6,0,P|144:238|52:250,2,160,4|2|2,3:3|3:3|3:3,0:0:0:0: +365,362,32325,1,2,3:3:0:0: +480,229,32772,6,0,L|464:55,1,160,2|2,3:3|3:3,0:0:0:0: +393,18,33444,1,10,0:3:0:0: +323,72,33668,2,0,L|243:64,1,80,2|10,3:3|0:3,0:0:0:0: +162,27,34116,2,0,L|82:35,1,80,2|10,3:3|0:3,0:0:0:0: +31,106,34563,6,0,P|9:176|23:263,2,160,2|2|2,3:3|3:3|3:3,0:0:0:0: +183,194,35907,1,2,3:3:0:0: +336,278,36354,6,0,P|407:241|496:243,2,160,2|2|0,3:3|3:3|3:2,0:0:0:0: +278,344,37474,1,0,3:2:0:0: +218,278,37698,2,0,P|180:262|137:257,1,80,8|0,0:2|0:2,0:0:0:0: +55,272,38145,6,0,B|29:230|29:230|47:114,1,160,4|8,3:2|0:2,0:0:0:0: +188,16,39041,2,0,B|214:58|214:58|196:174,1,160,2|8,3:3|0:2,0:0:0:0: +305,306,39936,6,0,B|348:305|380:330|380:330|405:305|459:300,1,160,2|8,3:3|0:2,0:0:0:0: +486,127,40832,2,0,P|475:67|430:19,2,120,4|12|12,3:2|0:2|0:2,0:0:0:0: +415,180,41728,6,0,P|334:166|260:194,2,160,8|8|8,0:2|0:2|0:2,0:0:0:0: +353,344,43071,1,8,0:2:0:0: +181,303,43519,6,0,L|16:319,1,160,8|8,0:2|0:2,0:0:0:0: +21,142,44414,2,0,L|186:158,1,160,8|8,0:2|0:2,0:0:0:0: +257,114,45086,1,0,3:2:0:0: +329,63,45310,6,0,P|485:169|281:268,1,480,12|8,0:2|0:2,0:0:0:0: +257,114,47101,6,0,B|200:92|200:92|130:110,2,120,12|12|12,0:2|0:2|0:2,0:0:0:0: +336,151,47996,1,12,0:2:0:0: +417,185,48220,2,0,L|507:180,2,80,8|8|8,0:2|0:2|0:2,0:0:0:0: +379,264,48892,6,0,P|338:281|294:280,1,80,4|10,3:2|0:2,0:0:0:0: +218,257,49339,2,0,P|257:302|263:376,2,120,0|0|10,3:2|0:2|0:2,0:0:0:0: +142,210,50235,2,0,L|135:124,1,80,0|10,3:2|0:2,0:0:0:0: +75,65,50683,6,0,P|132:92|231:60,1,160,0|0,3:2|0:0,0:0:0:0: +295,116,51354,2,0,P|352:89|451:121,1,160,10|10,0:2|0:2,0:0:0:0: +498,180,52026,1,0,3:2:0:0: +404,329,52474,6,0,L|320:323,1,80,0|10,3:2|0:2,0:0:0:0: +251,272,52922,2,0,P|206:288|132:281,2,120,0|0|10,3:2|0:2|0:2,0:0:0:0: +298,196,53817,2,0,L|295:111,1,80,0|10,3:2|0:2,0:0:0:0: +249,40,54265,6,0,B|189:34|189:34|145:50|145:50|73:41,1,160,0|0,3:2|3:2,0:0:0:0: +8,197,55160,2,0,P|46:210|95:206,1,80,0|10,3:2|0:2,0:0:0:0: +165,171,55608,2,0,P|203:158|252:162,1,80,0|10,3:2|0:2,0:0:0:0: +329,173,56056,6,0,B|368:223|368:223|361:320,1,160,4|0,3:2|3:2,0:0:0:0: +189,360,56951,2,0,P|146:358|102:342,1,80,0|10,3:2|0:2,0:0:0:0: +44,288,57399,2,0,P|46:245|62:201,1,80,0|10,3:2|0:2,0:0:0:0: +97,131,57847,6,0,B|153:113|203:139|203:139|258:107,1,160,0|0,3:2|3:2,0:0:0:0: +396,20,58742,2,0,L|409:118,1,80,0|10,3:2|0:2,0:0:0:0: +473,156,59190,2,0,L|460:254,1,80,0|10,3:2|0:2,0:0:0:0: +450,322,59638,6,0,P|380:312|293:343,1,160,4|4,3:2|3:2,0:0:0:0: +215,373,60310,1,10,0:2:0:0: +127,363,60534,2,0,L|121:273,1,80,4|10,3:2|0:0,0:0:0:0: +116,195,60981,1,4,3:2:0:0: +110,18,61429,6,0,P|166:33|232:23,2,120,4|0|10,3:2|0:2|0:2,0:0:0:0: +22,13,62325,2,0,L|18:107,1,80,0|0,3:2|0:2,0:0:0:0: +10,180,62772,1,8,0:2:0:0: +76,238,62996,1,0,3:2:0:0: +154,197,63220,6,0,P|194:194|242:207,1,80,0|14,3:2|0:2,0:0:0:0: +307,250,63668,2,0,L|303:371,2,120,0|0|10,3:2|0:2|0:2,0:0:0:0: +311,162,64563,2,0,P|351:159|399:172,1,80,0|10,3:2|0:2,0:0:0:0: +435,243,65011,6,0,L|427:53,1,160,0|0,3:2|0:0,0:0:0:0: +350,41,65683,2,0,P|282:66|196:50,1,160,10|10,0:2|0:2,0:0:0:0: +116,17,66354,1,0,3:2:0:0: +44,177,66802,6,0,P|40:219|56:268,1,80,0|10,3:2|0:2,0:0:0:0: +131,287,67250,2,0,P|83:294|29:360,2,120,0|0|10,3:2|0:2|0:2,0:0:0:0: +206,332,68145,2,0,L|306:325,1,80,0|10,3:2|0:2,0:0:0:0: +354,270,68593,6,0,B|360:215|360:215|340:168|340:168|348:100,1,160,0|0,3:2|3:2,0:0:0:0: +479,230,69489,2,0,L|470:322,1,80,0|10,3:2|0:2,0:0:0:0: +395,354,69936,2,0,P|357:363|307:352,1,80,0|10,3:2|0:2,0:0:0:0: +239,314,70384,6,0,B|179:303|125:325|125:325|84:301,1,160,4|0,3:2|3:2,0:0:0:0: +11,143,71280,2,0,L|114:130,1,80,0|10,3:2|0:2,0:0:0:0: +152,69,71728,2,0,L|255:82,1,80,0|10,3:2|0:0,0:0:0:0: +271,157,72175,6,0,P|271:100|345:26,1,160,4|4,3:2|3:2,0:0:0:0: +425,16,72847,1,10,0:2:0:0: +489,75,73071,2,0,L|481:176,1,80,4|10,3:2|0:2,0:0:0:0: +408,203,73519,2,0,L|416:304,1,80,4|0,3:2|0:0,0:0:0:0: +482,338,73966,6,0,B|402:317|398:370|320:339,1,160,4|0,3:2|3:2,0:0:0:0: +157,287,74862,2,0,L|71:295,2,80,0|10|0,3:2|0:2|3:2,0:0:0:0: +226,231,75534,1,10,0:2:0:0: +288,169,75757,6,0,P|357:197|451:165,2,160,4|0|0,3:2|3:2|3:2,0:0:0:0: +225,106,76877,2,0,L|233:21,2,80,0|8|0,0:0|0:2|3:2,0:0:0:0: +172,176,77548,6,0,B|145:218|145:218|165:339,1,160,4|12,3:2|0:2,0:0:0:0: +9,239,78444,2,0,B|36:197|36:197|16:76,1,160,0|12,3:2|0:2,0:0:0:0: +186,37,79339,6,0,P|236:79|349:68,1,160,0|12,3:2|0:2,0:0:0:0: +405,37,80011,1,0,0:2:0:0: +482,77,80235,2,0,L|472:159,1,80,0|0,3:2|3:2,0:0:0:0: +392,195,80683,2,0,L|402:277,1,80,12|8,0:2|0:2,0:0:0:0: +474,324,81131,6,0,P|422:301|298:337,1,160,6|2,3:2|0:2,0:0:0:0: +148,296,82026,2,0,P|125:244|161:120,1,160,6|2,3:2|0:2,0:0:0:0: +287,44,82922,6,0,B|364:10|444:39|444:39|356:94|357:165|357:165|441:232|413:331,1,480,6|10,3:2|0:2,0:0:0:0: +242,304,84713,6,0,L|111:320,2,120,12|12|12,3:2|3:2|3:2,0:0:0:0: +277,223,85608,2,0,P|214:163|127:159,1,160,12|8,3:2|0:2,0:0:0:0: +11,270,86504,5,4,3:2:0:0: +321,111,93668,6,0,P|261:76|315:180,2,320,4|0|4,3:2|0:0|3:2,0:0:0:0: +321,111,97250,6,0,B|393:147|468:85|468:85|424:181|468:248,2,320,0|0|0,3:2|0:0|3:2,0:0:0:0: +321,111,100832,6,0,B|284:85|246:78|246:78|175:111|175:111|91:89|91:89|56:104|31:129,2,320,0|2|0,3:2|0:2|3:2,0:0:0:0: +385,170,102847,5,12,0:2:0:0: +322,231,103071,2,0,L|185:220,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +404,262,103966,2,0,P|401:311|382:350,1,80,0|8,3:2|0:2,0:0:0:0: +308,374,104414,6,0,P|259:371|220:352,1,80,0|8,3:2|0:2,0:0:0:0: +164,300,104862,2,0,L|35:315,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +202,221,105757,1,0,3:2:0:0: +276,61,106205,6,0,P|371:63|426:190,1,240,4|8,3:2|0:2,0:0:0:0: +354,230,107101,2,0,B|280:260|202:209|202:209|162:220|122:249,1,240,4|8,3:2|0:2,0:0:0:0: +55,290,107996,5,0,3:2:0:0: +0,220,108220,1,12,0:2:0:0: +43,143,108444,2,0,L|37:23,2,120,8|8|0,0:2|3:2|0:2,0:0:0:0: +128,164,109339,2,0,P|167:161|212:139,1,80,8|8,0:2|0:2,0:0:0:0: +242,64,109787,6,0,P|227:110|276:215,2,160,4|2|2,3:3|3:3|3:3,0:0:0:0: +411,14,111131,1,2,3:3:0:0: +503,163,111578,6,0,L|484:335,1,160,2|2,3:3|3:3,0:0:0:0: +405,360,112250,1,10,0:3:0:0: +333,308,112474,2,0,L|250:316,1,80,2|10,3:3|0:3,0:0:0:0: +175,357,112922,2,0,L|92:349,1,80,2|10,3:3|0:3,0:0:0:0: +28,292,113369,6,0,P|13:201|47:120,2,160,2|2|2,3:3|3:3|3:3,0:0:0:0: +190,222,114713,1,2,3:3:0:0: +349,148,115160,6,0,B|433:133|419:192|504:176,2,160,2|2|0,3:3|3:3|3:2,0:0:0:0: +265,176,116280,1,0,3:2:0:0: +224,254,116504,2,0,L|239:354,1,80,8|0,0:2|0:2,0:0:0:0: +320,357,116951,6,0,B|428:339|428:339|485:355,1,160,4|8,3:2|0:2,0:0:0:0: +501,176,117847,2,0,B|393:194|393:194|336:178,1,160,2|8,3:3|0:2,0:0:0:0: +200,78,118742,6,0,B|159:68|120:86|120:86|86:64|44:71,1,160,2|8,3:3|0:2,0:0:0:0: +16,244,119638,2,0,L|30:372,2,120,4|12|12,3:2|0:2|0:2,0:0:0:0: +88,193,120534,6,0,B|142:216|142:216|266:202,2,160,8|8|8,0:2|0:2|0:2,0:0:0:0: +172,38,121877,1,8,0:2:0:0: +322,129,122325,6,0,P|351:191|322:281,1,160,8|8,0:2|0:2,0:0:0:0: +150,284,123220,2,0,P|121:222|150:132,1,160,8|8,0:2|0:2,0:0:0:0: +194,63,123892,1,0,3:2:0:0: +277,35,124116,6,0,B|353:64|424:16|424:16|380:99|432:172|432:172|347:133|256:169,1,480,4|8,0:2|0:2,0:0:0:0: +121,246,125907,6,0,L|133:371,2,120,12|12|12,3:2|3:2|3:2,0:0:0:0: +104,160,126802,1,12,3:2:0:0: +88,72,127026,2,0,P|49:66|10:73,2,80,8|8|8,0:2|0:2|0:2,0:0:0:0: +171,103,127698,6,0,L|257:94,1,80,4|10,3:2|0:2,0:0:0:0: +333,66,128145,2,0,P|395:41|463:49,2,120,0|0|10,3:2|0:2|0:2,0:0:0:0: +318,153,129041,2,0,L|312:254,1,80,0|10,3:2|0:2,0:0:0:0: +304,320,129489,6,0,P|367:359|471:352,1,160,0|0,3:2|0:0,0:0:0:0: +506,291,130160,2,0,L|489:116,1,160,10|10,0:2|0:2,0:0:0:0: +483,43,130832,1,0,3:2:0:0: +308,67,131280,6,0,P|270:81|216:76,1,80,0|10,3:2|0:2,0:0:0:0: +142,85,131728,2,0,L|157:220,2,120,0|0|10,3:2|0:2|0:2,0:0:0:0: +55,69,132623,2,0,L|43:169,1,80,0|10,3:2|0:2,0:0:0:0: +33,235,133071,6,0,P|65:294|164:331,1,160,0|0,3:2|3:2,0:0:0:0: +275,210,133966,2,0,L|363:214,1,80,0|10,3:2|0:2,0:0:0:0: +389,294,134414,2,0,L|477:290,1,80,0|10,3:2|0:2,0:0:0:0: +503,208,134862,6,0,B|511:92|511:92|489:44,1,160,4|0,3:2|3:2,0:0:0:0: +318,30,135757,2,0,L|230:34,1,80,0|10,3:2|0:2,0:0:0:0: +204,114,136205,2,0,L|116:110,1,80,0|10,3:2|0:2,0:0:0:0: +49,62,136653,6,0,B|15:110|19:171|19:171|42:219,1,160,0|0,3:2|3:2,0:0:0:0: +200,278,137548,2,0,P|215:245|220:193,1,80,0|10,3:2|0:2,0:0:0:0: +204,114,137996,2,0,P|189:81|184:29,1,80,0|10,3:2|0:2,0:0:0:0: +270,23,138444,6,0,B|322:48|322:48|446:22,1,160,4|4,3:2|3:2,0:0:0:0: +490,83,139116,1,10,0:2:0:0: +504,169,139339,2,0,P|503:213|486:254,1,80,4|10,3:2|0:2,0:0:0:0: +428,309,139787,1,4,3:2:0:0: +268,241,140235,6,0,P|272:303|278:371,2,120,4|0|10,3:2|0:2|0:2,0:0:0:0: +207,176,141131,2,0,L|107:180,1,80,0|0,3:2|0:2,0:0:0:0: +39,184,141578,1,8,0:2:0:0: +8,101,141802,1,0,3:2:0:0: +71,40,142026,6,0,P|127:30|162:36,1,80,0|14,3:2|0:2,0:0:0:0: +220,85,142474,2,0,L|342:76,2,120,0|0|10,3:2|0:2|0:2,0:0:0:0: +158,148,143369,2,0,P|150:196|161:241,1,80,0|10,3:2|0:2,0:0:0:0: +192,306,143817,6,0,B|282:279|272:350|373:314,1,160,0|0,3:2|0:0,0:0:0:0: +431,294,144489,2,0,L|440:125,1,160,10|10,0:2|0:2,0:0:0:0: +448,46,145160,1,0,3:2:0:0: +272,31,145608,6,0,P|223:30|180:43,1,80,0|10,3:2|0:2,0:0:0:0: +127,96,146056,2,0,L|8:86,2,120,0|0|10,3:2|0:2|0:2,0:0:0:0: +193,154,146951,2,0,P|194:203|181:246,1,80,0|10,3:2|0:2,0:0:0:0: +109,276,147399,6,0,B|165:270|165:270|212:283|212:283|271:276,1,160,0|0,3:2|3:2,0:0:0:0: +441,253,148295,2,0,L|445:166,1,80,0|10,3:2|0:2,0:0:0:0: +482,93,148742,2,0,L|478:6,1,80,0|10,3:2|0:2,0:0:0:0: +390,23,149190,6,0,B|351:44|351:44|215:33,1,160,4|0,3:2|3:2,0:0:0:0: +59,21,150086,2,0,P|43:61|44:104,1,80,0|10,3:2|0:2,0:0:0:0: +94,169,150534,2,0,P|110:209|109:252,1,80,0|10,3:2|0:2,0:0:0:0: +42,301,150981,6,0,P|112:280|190:309,1,160,4|4,3:2|3:2,0:0:0:0: +257,368,151653,1,10,0:2:0:0: +335,327,151877,2,0,L|327:241,1,80,4|10,3:2|0:2,0:0:0:0: +264,185,152325,2,0,L|272:99,1,80,12|8,0:0|0:0,0:0:0:0: +318,30,152772,6,0,P|392:21|479:78,1,160,4|0,3:2|3:2,0:0:0:0: +494,234,153668,2,0,L|509:340,2,80,0|10|0,3:2|0:2|3:2,0:0:0:0: +413,198,154339,1,10,0:2:0:0: +332,234,154563,6,0,P|275:249|179:220,2,160,4|0|0,3:2|3:2|3:2,0:0:0:0: +413,198,155683,2,0,L|500:212,2,80,0|8|0,0:2|0:2|3:2,0:0:0:0: +379,116,156354,6,0,B|340:88|340:88|200:105,1,160,4|12,3:2|0:2,0:0:0:0: +103,225,157250,2,0,B|142:197|142:197|282:214,1,160,0|12,3:2|0:2,0:0:0:0: +131,338,158145,6,0,P|53:330|-1:274,1,160,0|12,3:2|0:2,0:0:0:0: +14,187,158817,1,0,0:2:0:0: +54,108,159041,2,0,L|144:98,1,80,0|0,3:2|3:2,0:0:0:0: +194,35,159489,2,0,L|284:45,1,80,12|8,0:2|0:2,0:0:0:0: +354,78,159936,6,0,P|379:136|369:252,1,160,4|0,3:2|0:2,0:0:0:0: +242,346,160832,2,0,P|217:288|227:172,1,160,4|0,3:2|0:2,0:0:0:0: +354,78,161728,5,4,3:2:0:0: +182,37,162175,2,0,L|98:30,1,80,10|2,0:2|3:2,0:0:0:0: +22,68,162623,2,0,B|5:128|5:128|20:185,2,120,12|12|12,0:2|0:2|0:2,0:0:0:0: +98,112,163519,5,4,3:2:0:0: +202,253,163966,2,0,L|303:248,1,80,10|2,0:2|3:2,0:0:0:0: +355,199,164414,2,0,B|415:182|415:182|472:197,2,120,12|12|12,0:2|0:2|0:2,0:0:0:0: +274,161,165310,5,4,3:2:0:0: +110,225,165757,2,0,L|125:325,1,80,10|2,0:2|3:2,0:0:0:0: +188,362,166205,2,0,B|248:379|248:379|305:364,2,120,12|12|12,0:2|0:2|0:2,0:0:0:0: +206,275,167101,6,0,P|262:242|380:262,2,160,4|12|12,3:2|0:2|0:2,0:0:0:0: +98,352,168332,2,0,L|78:212,1,120,4|8,3:2|0:2,0:0:0:0: +74,144,168892,5,4,3:2:0:0: +246,110,169339,2,0,L|330:120,1,80,10|2,0:2|3:2,0:0:0:0: +385,184,169787,2,0,B|445:167|445:167|502:182,2,120,12|12|12,0:2|0:2|0:2,0:0:0:0: +304,221,170683,5,4,3:2:0:0: +161,117,171131,2,0,L|59:124,1,80,10|2,0:2|3:2,0:0:0:0: +22,188,171578,2,0,B|5:248|5:248|20:305,2,120,12|12|12,0:2|0:2|0:2,0:0:0:0: +108,207,172474,1,4,3:2:0:0: +279,244,172922,6,0,L|365:238,1,80,10|2,0:2|3:2,0:0:0:0: +385,154,173369,2,0,B|445:171|445:171|502:156,2,120,12|12|4,0:2|0:2|0:3,0:0:0:0: +307,111,174265,6,0,L|211:122,1,80,6|2,0:2|3:2,0:0:0:0: +148,159,174713,2,0,L|5:142,2,120,14|6|0,0:2|3:2|0:2,0:0:0:0: +222,206,175608,1,8,0:2:0:0: +387,266,176056,6,0,P|416:206|409:150,2,120,6|2|8,3:2|0:2|0:2,0:0:0:0: +302,291,176951,2,0,B|212:264|234:332|122:296,1,160,0|2,3:2|3:2,0:0:0:0: +66,266,177623,1,10,0:2:0:0: +93,182,177847,6,0,L|197:173,2,80,2|10|2,3:2|0:2|3:2,0:0:0:0: +20,131,178519,2,0,P|47:58|150:26,1,160,10|8,0:2|0:2,0:0:0:0: +205,17,179190,1,0,3:2:0:0: +381,12,179638,6,0,L|485:21,1,80,2|10,3:2|0:2,0:0:0:0: +499,99,180086,2,0,P|500:152|472:220,2,120,2|2|10,3:2|0:2|0:2,0:0:0:0: +411,111,180981,1,2,3:2:0:0: +237,142,181429,6,0,L|139:134,1,80,2|10,3:2|0:2,0:0:0:0: +69,124,181877,2,0,P|48:55|48:-4,2,120,2|2|12,3:2|0:2|0:2,0:0:0:0: +102,205,182772,1,4,3:2:0:0: +172,258,182996,1,12,0:2:0:0: +258,276,183220,6,0,B|350:261|319:316|412:306,1,160,2|2,3:2|3:2,0:0:0:0: +500,154,184116,2,0,L|509:25,2,120,2|2|10,3:2|0:2|0:2,0:0:0:0: +424,198,185011,6,0,P|354:203|335:196,1,80,2|10,3:2|0:2,0:0:0:0: +273,148,185459,2,0,P|185:136|141:162,1,120,2|2,3:2|0:2,0:0:0:0: +66,243,186131,2,0,B|108:269|108:269|218:257,1,160,14|10,0:2|0:2,0:0:0:0: +301,230,186802,6,0,L|398:240,1,80,2|10,3:2|0:2,0:0:0:0: +468,250,187250,2,0,P|483:178|488:111,2,120,2|2|10,3:2|0:2|0:2,0:0:0:0: +430,329,188145,1,2,3:2:0:0: +255,364,188593,6,0,L|157:353,1,80,2|14,3:2|0:2,0:0:0:0: +140,274,189041,2,0,P|68:289|1:294,2,120,2|2|0,3:2|3:2|0:0,0:0:0:0: +205,215,189936,1,0,3:2:0:0: +297,64,190384,6,0,L|424:52,2,120,4|0|10,3:3|0:2|0:3,0:0:0:0: +233,125,191280,2,0,P|263:228|384:244,1,240,2|10,3:3|0:3,0:0:0:0: +468,231,192175,6,0,L|462:375,2,120,2|0|10,3:3|0:2|0:3,0:0:0:0: +497,146,193071,2,0,P|461:39|348:26,1,240,2|10,3:3|0:3,0:0:0:0: +292,94,193966,6,0,L|298:238,2,120,2|0|10,3:3|0:2|0:3,0:0:0:0: +233,27,194862,2,0,P|120:39|84:147,1,240,2|10,3:3|0:3,0:0:0:0: +120,227,195757,5,2,3:3:0:0: +292,261,196205,2,0,L|436:247,2,120,2|0|10,3:3|0:2|0:3,0:0:0:0: +224,317,197101,2,0,L|124:307,1,80,2|10,3:3|0:3,0:0:0:0: +66,267,197548,6,0,P|49:324|12:370,2,120,6|0|10,3:3|0:2|0:3,0:0:0:0: +42,181,198444,2,0,P|104:79|251:69,1,240,2|10,3:3|0:3,0:0:0:0: +292,100,199339,6,0,B|344:83|344:83|418:94,2,120,2|0|10,3:3|0:2|0:3,0:0:0:0: +235,168,200235,2,0,P|259:282|359:341,1,240,2|10,3:3|0:3,0:0:0:0: +447,330,201131,5,6,3:3:0:0: +472,156,201578,2,0,L|371:143,1,80,2|10,3:3|0:3,0:0:0:0: +323,90,202026,2,0,P|264:83|212:50,2,120,6|0|10,3:3|0:2|0:3,0:0:0:0: +370,15,202922,5,6,3:3:0:0: +472,156,203369,2,0,L|457:251,1,80,2|10,3:3|0:3,0:0:0:0: +373,256,203817,2,0,P|397:327|399:371,2,120,6|0|10,3:3|0:2|0:3,0:0:0:0: +294,214,204713,6,0,B|224:243|224:243|111:225,1,160,6|6,3:3|3:3,0:0:0:0: +29,93,205608,2,0,B|99:64|99:64|212:82,1,160,6|10,3:3|0:3,0:0:0:0: +267,99,206280,1,2,3:3:0:0: +344,141,206504,6,0,P|407:124|472:149,2,120,12|12|12,3:2|3:2|3:2,0:0:0:0: +294,214,207399,2,0,P|325:292|499:216,1,320,12|4,3:2|3:2,0:0:0:0: +256,192,215459,12,6,218145,3:2:0:0: +205,114,219041,6,0,B|119:107|119:107|44:141,1,160,12|12,3:2|3:2,0:0:0:0: +75,311,219936,2,0,B|161:318|161:318|236:284,1,160,12|12,3:2|3:2,0:0:0:0: +337,149,220832,6,0,L|325:15,2,120,12|12|12,3:2|3:2|3:2,0:0:0:0: +457,277,221951,2,0,L|447:377,2,80,0|8|8,3:2|0:2|0:2,0:0:0:0: +471,189,222623,5,2,0:2:0:0: +331,81,223071,2,0,B|279:103|279:103|200:94,2,120,4|8|0,3:2|0:2|3:2,0:0:0:0: +399,26,223966,1,8,0:2:0:0: +471,189,224414,6,0,L|453:333,1,120,12|12,3:2|3:2,0:0:0:0: +326,335,225086,2,0,B|276:306|276:306|149:326,1,160,12|0,3:2|0:2,0:0:0:0: +88,340,225757,2,0,P|75:299|76:251,1,80,8|8,0:2|0:2,0:0:0:0: +140,204,226205,6,0,L|144:123,1,80,4|10,3:2|0:2,0:0:0:0: +116,40,226653,2,0,P|58:49|3:25,2,120,0|0|10,3:2|0:2|0:2,0:0:0:0: +202,21,227548,2,0,L|283:25,1,80,0|10,3:2|0:2,0:0:0:0: +370,29,227996,6,0,B|404:72|404:72|392:196,1,160,0|0,3:2|3:2,0:0:0:0: +291,320,228892,2,0,L|178:329,1,80,0|10,3:2|0:2,0:0:0:0: +136,373,229339,2,0,L|23:364,1,80,0|10,3:2|0:2,0:0:0:0: +20,285,229787,6,0,B|8:231|8:231|24:183|24:183|14:121,1,160,4|0,3:2|3:2,0:0:0:0: +156,24,230683,2,0,P|182:74|187:103,1,80,0|10,3:2|0:2,0:0:0:0: +264,138,231131,2,0,P|238:188|233:217,1,80,0|10,3:2|0:2,0:0:0:0: +262,293,231578,6,0,B|312:314|312:314|440:299,1,160,4|4,3:2|3:2,0:0:0:0: +479,239,232250,1,10,0:2:0:0: +500,153,232474,2,0,P|499:119|481:77,1,80,4|10,3:2|0:2,0:0:0:0: +396,50,232922,2,0,P|362:51|320:69,1,80,4|0,3:2|0:0,0:0:0:0: +264,138,233369,6,0,B|173:153|201:102|101:116,1,160,4|0,3:2|3:2,0:0:0:0: +39,277,234265,2,0,L|32:359,2,80,0|10|0,3:2|0:2|3:2,0:0:0:0: +123,252,234936,1,10,0:2:0:0: +206,225,235160,6,0,P|261:245|383:213,2,160,4|0|0,3:2|3:2|3:2,0:0:0:0: +136,169,236280,2,0,L|48:175,2,80,0|8|0,0:2|0:2|3:2,0:0:0:0: +203,112,236951,6,0,B|253:81|253:81|377:98,1,160,4|12,3:2|0:2,0:0:0:0: +468,228,237847,2,0,B|418:197|418:197|294:214,1,160,0|12,3:2|0:2,0:0:0:0: +180,321,238742,6,0,P|120:328|31:252,1,160,0|12,3:2|0:2,0:0:0:0: +16,188,239414,1,0,0:2:0:0: +65,115,239638,2,0,L|147:107,1,80,0|0,3:2|3:2,0:0:0:0: +205,43,240086,2,0,L|287:51,1,80,12|0,0:2|0:2,0:0:0:0: +366,83,240534,6,0,P|389:155|382:244,1,160,0|12,3:2|0:2,0:0:0:0: +238,338,241429,2,0,P|215:266|222:177,1,160,0|12,3:2|0:2,0:0:0:0: +297,24,242325,6,0,P|369:54|462:47,2,160,0|12|0,3:2|0:2|3:2,0:0:0:0: +216,60,243444,1,0,3:2:0:0: +136,96,243668,2,0,L|56:89,1,80,8|8,0:2|0:2,0:0:0:0: +2,18,244116,6,0,P|26:102|26:206,1,160,4|0,3:2|3:2,0:0:0:0: +5,259,244787,1,8,0:2:0:0: +64,324,245011,2,0,L|156:326,1,80,0|8,3:2|0:2,0:0:0:0: +223,364,245459,2,0,L|315:362,1,80,0|8,3:2|0:2,0:0:0:0: +379,318,245907,6,0,P|395:247|390:145,1,160,4|0,3:2|3:2,0:0:0:0: +240,72,246802,2,0,P|225:116|220:149,1,80,8|8,3:2|3:2,0:0:0:0: +152,205,247250,2,0,P|167:249|172:282,1,80,8|8,3:2|3:2,0:0:0:0: +118,352,247698,6,0,P|174:314|275:316,1,160,4|0,3:2|0:0,0:0:0:0: +427,377,248593,2,0,P|465:321|463:220,1,160,4|0,3:2|0:0,0:0:0:0: +411,63,249489,6,0,B|326:66|257:31|257:31|306:192|306:192|227:143|142:154,1,480,4|10,3:2|0:2,0:0:0:0: +21,259,251280,6,0,L|32:378,2,120,12|12|12,3:2|3:2|3:2,0:0:0:0: +2,173,252175,2,0,P|19:77|84:24,1,160,12|12,3:2|0:2,0:0:0:0: +236,14,253071,5,4,3:2:0:0: +293,276,259563,6,0,L|392:265,2,80,12|4|12,0:2|3:2|0:2,0:0:0:0: +219,324,260235,5,0,3:2:0:0: +115,181,260683,2,0,P|59:205|-18:200,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +189,133,261578,2,0,L|274:137,1,80,0|8,3:2|0:2,0:0:0:0: +334,195,262026,6,0,L|419:191,1,80,0|8,3:2|0:2,0:0:0:0: +480,132,262474,2,0,P|469:74|471:13,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +497,218,263369,2,0,P|500:258|494:305,1,80,0|8,3:2|0:2,0:0:0:0: +434,361,263817,5,0,3:2:0:0: +262,319,264265,2,0,L|138:333,2,120,0|0|8,3:2|0:2|0:2,0:0:0:0: +329,262,265160,2,0,L|316:154,1,80,0|8,3:2|0:2,0:0:0:0: +254,123,265608,6,0,P|205:120|161:140,1,80,0|8,3:2|0:2,0:0:0:0: +95,164,266056,2,0,L|78:17,2,120,0|0|0,3:2|0:2|0:2,0:0:0:0: +112,250,266951,1,8,0:2:0:0: +178,308,267175,1,8,0:2:0:0: +264,289,267399,6,0,P|301:284|368:300,1,80,2|2,3:2|0:2,0:0:0:0: +395,218,267847,2,0,P|451:236|510:225,2,120,2|2|0,0:2|0:2|3:2,0:0:0:0: +326,162,268742,6,0,B|274:185|274:185|158:154|158:154|231:119,1,240,12|0,0:2|0:0,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1597806-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1597806-expected-conversion.json new file mode 100644 index 0000000000..c14bdf1453 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1597806-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":42.0,"Objects":[{"StartTime":42.0,"Position":288.0,"HyperDash":false},{"StartTime":124.0,"Position":246.095245,"HyperDash":false},{"StartTime":242.0,"Position":217.560577,"HyperDash":false}]},{"StartTime":443.0,"Objects":[{"StartTime":443.0,"Position":125.0,"HyperDash":false},{"StartTime":525.0,"Position":139.98082,"HyperDash":false},{"StartTime":643.0,"Position":171.8503,"HyperDash":false}]},{"StartTime":845.0,"Objects":[{"StartTime":845.0,"Position":95.0,"HyperDash":false},{"StartTime":927.0,"Position":92.32658,"HyperDash":false},{"StartTime":1045.0,"Position":117.385254,"HyperDash":false}]},{"StartTime":1247.0,"Objects":[{"StartTime":1247.0,"Position":250.0,"HyperDash":false},{"StartTime":1329.0,"Position":235.381714,"HyperDash":false},{"StartTime":1447.0,"Position":177.760284,"HyperDash":false}]},{"StartTime":1649.0,"Objects":[{"StartTime":1649.0,"Position":277.0,"HyperDash":false},{"StartTime":1731.0,"Position":323.6183,"HyperDash":false},{"StartTime":1849.0,"Position":349.239716,"HyperDash":false}]},{"StartTime":2051.0,"Objects":[{"StartTime":2051.0,"Position":448.0,"HyperDash":false},{"StartTime":2133.0,"Position":419.48,"HyperDash":false},{"StartTime":2251.0,"Position":376.0,"HyperDash":false}]},{"StartTime":2453.0,"Objects":[{"StartTime":2453.0,"Position":499.0,"HyperDash":false},{"StartTime":2535.0,"Position":501.8066,"HyperDash":false},{"StartTime":2653.0,"Position":496.029449,"HyperDash":false}]},{"StartTime":2855.0,"Objects":[{"StartTime":2855.0,"Position":397.0,"HyperDash":false},{"StartTime":2937.0,"Position":385.11322,"HyperDash":false},{"StartTime":3055.0,"Position":393.946869,"HyperDash":false}]},{"StartTime":3257.0,"Objects":[{"StartTime":3257.0,"Position":295.0,"HyperDash":false},{"StartTime":3339.0,"Position":290.097748,"HyperDash":false},{"StartTime":3457.0,"Position":291.69043,"HyperDash":false}]},{"StartTime":3658.0,"Objects":[{"StartTime":3658.0,"Position":134.0,"HyperDash":false},{"StartTime":3740.0,"Position":152.636856,"HyperDash":false},{"StartTime":3858.0,"Position":208.724045,"HyperDash":false}]},{"StartTime":4060.0,"Objects":[{"StartTime":4060.0,"Position":95.0,"HyperDash":false},{"StartTime":4142.0,"Position":103.823456,"HyperDash":false},{"StartTime":4260.0,"Position":126.276718,"HyperDash":false}]},{"StartTime":4462.0,"Objects":[{"StartTime":4462.0,"Position":217.0,"HyperDash":false},{"StartTime":4544.0,"Position":213.344086,"HyperDash":false},{"StartTime":4662.0,"Position":173.936813,"HyperDash":false}]},{"StartTime":4864.0,"Objects":[{"StartTime":4864.0,"Position":268.0,"HyperDash":false},{"StartTime":4946.0,"Position":265.3267,"HyperDash":false},{"StartTime":5064.0,"Position":265.68042,"HyperDash":false}]},{"StartTime":5266.0,"Objects":[{"StartTime":5266.0,"Position":418.0,"HyperDash":false},{"StartTime":5348.0,"Position":385.913544,"HyperDash":false},{"StartTime":5466.0,"Position":354.807678,"HyperDash":false}]},{"StartTime":5668.0,"Objects":[{"StartTime":5668.0,"Position":356.0,"HyperDash":false},{"StartTime":5750.0,"Position":390.300568,"HyperDash":false},{"StartTime":5868.0,"Position":421.002045,"HyperDash":false}]},{"StartTime":6070.0,"Objects":[{"StartTime":6070.0,"Position":265.0,"HyperDash":false},{"StartTime":6152.0,"Position":215.764069,"HyperDash":false},{"StartTime":6270.0,"Position":191.253845,"HyperDash":false}]},{"StartTime":6472.0,"Objects":[{"StartTime":6472.0,"Position":35.0,"HyperDash":false},{"StartTime":6554.0,"Position":56.2359238,"HyperDash":false},{"StartTime":6672.0,"Position":108.746155,"HyperDash":false}]},{"StartTime":6873.0,"Objects":[{"StartTime":6873.0,"Position":265.0,"HyperDash":false},{"StartTime":6955.0,"Position":252.764084,"HyperDash":false},{"StartTime":7073.0,"Position":191.253845,"HyperDash":false}]},{"StartTime":7275.0,"Objects":[{"StartTime":7275.0,"Position":323.0,"HyperDash":false},{"StartTime":7357.0,"Position":370.397949,"HyperDash":false},{"StartTime":7475.0,"Position":390.779846,"HyperDash":false}]},{"StartTime":7677.0,"Objects":[{"StartTime":7677.0,"Position":493.0,"HyperDash":false},{"StartTime":7759.0,"Position":475.602051,"HyperDash":false},{"StartTime":7877.0,"Position":425.220154,"HyperDash":false}]},{"StartTime":8079.0,"Objects":[{"StartTime":8079.0,"Position":323.0,"HyperDash":false},{"StartTime":8161.0,"Position":345.397949,"HyperDash":false},{"StartTime":8279.0,"Position":390.779846,"HyperDash":false}]},{"StartTime":8481.0,"Objects":[{"StartTime":8481.0,"Position":273.0,"HyperDash":false}]},{"StartTime":8682.0,"Objects":[{"StartTime":8682.0,"Position":187.0,"HyperDash":false}]},{"StartTime":8883.0,"Objects":[{"StartTime":8883.0,"Position":101.0,"HyperDash":false}]},{"StartTime":9084.0,"Objects":[{"StartTime":9084.0,"Position":187.0,"HyperDash":false}]},{"StartTime":9285.0,"Objects":[{"StartTime":9285.0,"Position":101.0,"HyperDash":false}]},{"StartTime":9486.0,"Objects":[{"StartTime":9486.0,"Position":15.0,"HyperDash":false}]},{"StartTime":9687.0,"Objects":[{"StartTime":9687.0,"Position":187.0,"HyperDash":false},{"StartTime":9769.0,"Position":140.010742,"HyperDash":false},{"StartTime":9887.0,"Position":113.855469,"HyperDash":false}]},{"StartTime":10088.0,"Objects":[{"StartTime":10088.0,"Position":264.0,"HyperDash":false},{"StartTime":10170.0,"Position":285.762848,"HyperDash":false},{"StartTime":10288.0,"Position":285.372772,"HyperDash":false}]},{"StartTime":10490.0,"Objects":[{"StartTime":10490.0,"Position":287.0,"HyperDash":false},{"StartTime":10572.0,"Position":302.9239,"HyperDash":false},{"StartTime":10690.0,"Position":338.033844,"HyperDash":false}]},{"StartTime":10892.0,"Objects":[{"StartTime":10892.0,"Position":422.0,"HyperDash":false},{"StartTime":10974.0,"Position":429.5159,"HyperDash":false},{"StartTime":11092.0,"Position":417.753174,"HyperDash":false}]},{"StartTime":11294.0,"Objects":[{"StartTime":11294.0,"Position":287.0,"HyperDash":false},{"StartTime":11376.0,"Position":299.820526,"HyperDash":false},{"StartTime":11494.0,"Position":348.207428,"HyperDash":false}]},{"StartTime":11696.0,"Objects":[{"StartTime":11696.0,"Position":166.0,"HyperDash":false},{"StartTime":11778.0,"Position":186.67955,"HyperDash":false},{"StartTime":11896.0,"Position":232.26709,"HyperDash":false}]},{"StartTime":12098.0,"Objects":[{"StartTime":12098.0,"Position":332.0,"HyperDash":false},{"StartTime":12180.0,"Position":300.8351,"HyperDash":false},{"StartTime":12298.0,"Position":258.427124,"HyperDash":false}]},{"StartTime":12500.0,"Objects":[{"StartTime":12500.0,"Position":394.0,"HyperDash":false},{"StartTime":12582.0,"Position":438.1649,"HyperDash":false},{"StartTime":12700.0,"Position":467.572876,"HyperDash":false}]},{"StartTime":12902.0,"Objects":[{"StartTime":12902.0,"Position":332.0,"HyperDash":false},{"StartTime":12984.0,"Position":286.8351,"HyperDash":false},{"StartTime":13102.0,"Position":258.427124,"HyperDash":false}]},{"StartTime":13303.0,"Objects":[{"StartTime":13303.0,"Position":413.0,"HyperDash":false},{"StartTime":13385.0,"Position":402.2547,"HyperDash":false},{"StartTime":13503.0,"Position":417.4853,"HyperDash":false}]},{"StartTime":13705.0,"Objects":[{"StartTime":13705.0,"Position":327.0,"HyperDash":false},{"StartTime":13787.0,"Position":319.2547,"HyperDash":false},{"StartTime":13905.0,"Position":331.4853,"HyperDash":false}]},{"StartTime":14107.0,"Objects":[{"StartTime":14107.0,"Position":241.0,"HyperDash":false},{"StartTime":14189.0,"Position":262.25473,"HyperDash":false},{"StartTime":14307.0,"Position":245.485291,"HyperDash":false}]},{"StartTime":14509.0,"Objects":[{"StartTime":14509.0,"Position":118.0,"HyperDash":false},{"StartTime":14591.0,"Position":165.460083,"HyperDash":false},{"StartTime":14709.0,"Position":192.2929,"HyperDash":false}]},{"StartTime":14911.0,"Objects":[{"StartTime":14911.0,"Position":297.0,"HyperDash":false},{"StartTime":14993.0,"Position":276.830261,"HyperDash":false},{"StartTime":15111.0,"Position":250.244568,"HyperDash":false}]},{"StartTime":15313.0,"Objects":[{"StartTime":15313.0,"Position":273.0,"HyperDash":false},{"StartTime":15395.0,"Position":254.357025,"HyperDash":false},{"StartTime":15513.0,"Position":244.602539,"HyperDash":false}]},{"StartTime":15715.0,"Objects":[{"StartTime":15715.0,"Position":235.0,"HyperDash":false},{"StartTime":15797.0,"Position":262.7597,"HyperDash":false},{"StartTime":15915.0,"Position":306.7758,"HyperDash":false}]},{"StartTime":16117.0,"Objects":[{"StartTime":16117.0,"Position":441.0,"HyperDash":false},{"StartTime":16199.0,"Position":431.2403,"HyperDash":false},{"StartTime":16317.0,"Position":369.2242,"HyperDash":false}]},{"StartTime":16518.0,"Objects":[{"StartTime":16518.0,"Position":235.0,"HyperDash":false},{"StartTime":16600.0,"Position":257.7597,"HyperDash":false},{"StartTime":16718.0,"Position":306.7758,"HyperDash":false}]},{"StartTime":16920.0,"Objects":[{"StartTime":16920.0,"Position":436.0,"HyperDash":false},{"StartTime":17002.0,"Position":444.7306,"HyperDash":false},{"StartTime":17120.0,"Position":445.098969,"HyperDash":false}]},{"StartTime":17322.0,"Objects":[{"StartTime":17322.0,"Position":345.0,"HyperDash":false},{"StartTime":17404.0,"Position":386.333862,"HyperDash":false},{"StartTime":17522.0,"Position":414.106964,"HyperDash":false}]},{"StartTime":17724.0,"Objects":[{"StartTime":17724.0,"Position":208.0,"HyperDash":false},{"StartTime":17806.0,"Position":249.6,"HyperDash":false},{"StartTime":17924.0,"Position":268.0,"HyperDash":false}]},{"StartTime":18126.0,"Objects":[{"StartTime":18126.0,"Position":187.0,"HyperDash":false}]},{"StartTime":18528.0,"Objects":[{"StartTime":18528.0,"Position":187.0,"HyperDash":false}]},{"StartTime":18930.0,"Objects":[{"StartTime":18930.0,"Position":187.0,"HyperDash":false}]},{"StartTime":19332.0,"Objects":[{"StartTime":19332.0,"Position":187.0,"HyperDash":false}]},{"StartTime":19532.0,"Objects":[{"StartTime":19532.0,"Position":187.0,"HyperDash":false}]},{"StartTime":19733.0,"Objects":[{"StartTime":19733.0,"Position":345.0,"HyperDash":false}]},{"StartTime":19933.0,"Objects":[{"StartTime":19933.0,"Position":257.0,"HyperDash":false}]},{"StartTime":20135.0,"Objects":[{"StartTime":20135.0,"Position":471.0,"HyperDash":false}]},{"StartTime":20335.0,"Objects":[{"StartTime":20335.0,"Position":384.0,"HyperDash":false}]},{"StartTime":20537.0,"Objects":[{"StartTime":20537.0,"Position":284.0,"HyperDash":false}]},{"StartTime":20737.0,"Objects":[{"StartTime":20737.0,"Position":371.0,"HyperDash":false}]},{"StartTime":20938.0,"Objects":[{"StartTime":20938.0,"Position":157.0,"HyperDash":false}]},{"StartTime":21140.0,"Objects":[{"StartTime":21140.0,"Position":244.0,"HyperDash":false}]},{"StartTime":21340.0,"Objects":[{"StartTime":21340.0,"Position":188.0,"HyperDash":false}]},{"StartTime":21542.0,"Objects":[{"StartTime":21542.0,"Position":188.0,"HyperDash":false}]},{"StartTime":21743.0,"Objects":[{"StartTime":21743.0,"Position":345.0,"HyperDash":false}]},{"StartTime":21944.0,"Objects":[{"StartTime":21944.0,"Position":250.0,"HyperDash":false}]},{"StartTime":22145.0,"Objects":[{"StartTime":22145.0,"Position":419.0,"HyperDash":false},{"StartTime":22227.0,"Position":405.25,"HyperDash":false},{"StartTime":22345.0,"Position":344.0,"HyperDash":false}]},{"StartTime":22547.0,"Objects":[{"StartTime":22547.0,"Position":196.0,"HyperDash":false},{"StartTime":22629.0,"Position":241.75,"HyperDash":false},{"StartTime":22747.0,"Position":271.0,"HyperDash":false}]},{"StartTime":22948.0,"Objects":[{"StartTime":22948.0,"Position":419.0,"HyperDash":false}]},{"StartTime":23149.0,"Objects":[{"StartTime":23149.0,"Position":344.0,"HyperDash":false}]},{"StartTime":23350.0,"Objects":[{"StartTime":23350.0,"Position":305.0,"HyperDash":false},{"StartTime":23432.0,"Position":326.616516,"HyperDash":false},{"StartTime":23550.0,"Position":306.871063,"HyperDash":false}]},{"StartTime":23752.0,"Objects":[{"StartTime":23752.0,"Position":240.0,"HyperDash":false},{"StartTime":23834.0,"Position":244.383484,"HyperDash":false},{"StartTime":23952.0,"Position":238.128937,"HyperDash":false}]},{"StartTime":24154.0,"Objects":[{"StartTime":24154.0,"Position":429.0,"HyperDash":false},{"StartTime":24236.0,"Position":381.322876,"HyperDash":false},{"StartTime":24354.0,"Position":354.177734,"HyperDash":false}]},{"StartTime":24556.0,"Objects":[{"StartTime":24556.0,"Position":232.0,"HyperDash":false},{"StartTime":24638.0,"Position":267.677124,"HyperDash":false},{"StartTime":24756.0,"Position":306.822266,"HyperDash":false}]},{"StartTime":24958.0,"Objects":[{"StartTime":24958.0,"Position":429.0,"HyperDash":false},{"StartTime":25040.0,"Position":386.322876,"HyperDash":false},{"StartTime":25158.0,"Position":354.177734,"HyperDash":false}]},{"StartTime":25360.0,"Objects":[{"StartTime":25360.0,"Position":501.0,"HyperDash":false}]},{"StartTime":25561.0,"Objects":[{"StartTime":25561.0,"Position":429.0,"HyperDash":false}]},{"StartTime":25762.0,"Objects":[{"StartTime":25762.0,"Position":491.0,"HyperDash":false},{"StartTime":25844.0,"Position":475.629547,"HyperDash":false},{"StartTime":25962.0,"Position":490.096466,"HyperDash":false}]},{"StartTime":26163.0,"Objects":[{"StartTime":26163.0,"Position":372.0,"HyperDash":false},{"StartTime":26245.0,"Position":390.370453,"HyperDash":false},{"StartTime":26363.0,"Position":372.903534,"HyperDash":false}]},{"StartTime":26565.0,"Objects":[{"StartTime":26565.0,"Position":372.0,"HyperDash":false}]},{"StartTime":26766.0,"Objects":[{"StartTime":26766.0,"Position":431.0,"HyperDash":false}]},{"StartTime":26967.0,"Objects":[{"StartTime":26967.0,"Position":372.0,"HyperDash":false}]},{"StartTime":27168.0,"Objects":[{"StartTime":27168.0,"Position":314.0,"HyperDash":false}]},{"StartTime":27369.0,"Objects":[{"StartTime":27369.0,"Position":254.0,"HyperDash":false}]},{"StartTime":27570.0,"Objects":[{"StartTime":27570.0,"Position":313.0,"HyperDash":false}]},{"StartTime":27771.0,"Objects":[{"StartTime":27771.0,"Position":372.0,"HyperDash":false},{"StartTime":27821.0,"Position":382.6753,"HyperDash":false},{"StartTime":27871.0,"Position":425.3506,"HyperDash":false},{"StartTime":27921.0,"Position":431.0259,"HyperDash":false},{"StartTime":27971.0,"Position":436.7012,"HyperDash":false},{"StartTime":28021.0,"Position":466.3765,"HyperDash":false},{"StartTime":28071.0,"Position":463.0,"HyperDash":false},{"StartTime":28121.0,"Position":473.0,"HyperDash":false},{"StartTime":28172.0,"Position":473.0,"HyperDash":false},{"StartTime":28222.0,"Position":457.0,"HyperDash":false},{"StartTime":28272.0,"Position":481.0,"HyperDash":false},{"StartTime":28322.0,"Position":460.0,"HyperDash":false},{"StartTime":28373.0,"Position":444.1494,"HyperDash":false},{"StartTime":28423.0,"Position":440.474121,"HyperDash":false},{"StartTime":28473.0,"Position":415.7988,"HyperDash":false},{"StartTime":28523.0,"Position":416.1235,"HyperDash":false},{"StartTime":28574.0,"Position":389.0747,"HyperDash":false},{"StartTime":28656.0,"Position":375.447235,"HyperDash":false},{"StartTime":28775.0,"Position":314.0,"HyperDash":false}]},{"StartTime":28977.0,"Objects":[{"StartTime":28977.0,"Position":185.0,"HyperDash":false},{"StartTime":29068.0,"Position":200.514755,"HyperDash":false},{"StartTime":29159.0,"Position":255.02951,"HyperDash":false},{"StartTime":29250.0,"Position":301.5764,"HyperDash":false},{"StartTime":29378.0,"Position":328.3668,"HyperDash":false}]},{"StartTime":29579.0,"Objects":[{"StartTime":29579.0,"Position":256.0,"HyperDash":false},{"StartTime":29679.0,"Position":256.0,"HyperDash":false},{"StartTime":29779.0,"Position":256.0,"HyperDash":false}]},{"StartTime":30182.0,"Objects":[{"StartTime":30182.0,"Position":467.0,"HyperDash":false}]},{"StartTime":30383.0,"Objects":[{"StartTime":30383.0,"Position":395.0,"HyperDash":false}]},{"StartTime":30584.0,"Objects":[{"StartTime":30584.0,"Position":323.0,"HyperDash":false}]},{"StartTime":30785.0,"Objects":[{"StartTime":30785.0,"Position":251.0,"HyperDash":false}]},{"StartTime":30986.0,"Objects":[{"StartTime":30986.0,"Position":179.0,"HyperDash":false}]},{"StartTime":31187.0,"Objects":[{"StartTime":31187.0,"Position":107.0,"HyperDash":false}]},{"StartTime":31388.0,"Objects":[{"StartTime":31388.0,"Position":35.0,"HyperDash":false},{"StartTime":31479.0,"Position":45.0,"HyperDash":false},{"StartTime":31570.0,"Position":45.0,"HyperDash":false},{"StartTime":31661.0,"Position":39.0,"HyperDash":false},{"StartTime":31789.0,"Position":35.0,"HyperDash":false}]},{"StartTime":31991.0,"Objects":[{"StartTime":31991.0,"Position":105.0,"HyperDash":false},{"StartTime":32091.0,"Position":142.5,"HyperDash":false},{"StartTime":32191.0,"Position":105.0,"HyperDash":false}]},{"StartTime":32593.0,"Objects":[{"StartTime":32593.0,"Position":314.0,"HyperDash":false}]},{"StartTime":32794.0,"Objects":[{"StartTime":32794.0,"Position":434.0,"HyperDash":false}]},{"StartTime":32995.0,"Objects":[{"StartTime":32995.0,"Position":314.0,"HyperDash":false}]},{"StartTime":33196.0,"Objects":[{"StartTime":33196.0,"Position":434.0,"HyperDash":false}]},{"StartTime":33397.0,"Objects":[{"StartTime":33397.0,"Position":314.0,"HyperDash":false}]},{"StartTime":33598.0,"Objects":[{"StartTime":33598.0,"Position":434.0,"HyperDash":false}]},{"StartTime":33799.0,"Objects":[{"StartTime":33799.0,"Position":314.0,"HyperDash":false},{"StartTime":33881.0,"Position":336.929565,"HyperDash":false},{"StartTime":33999.0,"Position":352.8526,"HyperDash":false}]},{"StartTime":34201.0,"Objects":[{"StartTime":34201.0,"Position":117.0,"HyperDash":false},{"StartTime":34283.0,"Position":163.741074,"HyperDash":false},{"StartTime":34401.0,"Position":191.978241,"HyperDash":false}]},{"StartTime":34603.0,"Objects":[{"StartTime":34603.0,"Position":56.0,"HyperDash":false},{"StartTime":34685.0,"Position":55.48987,"HyperDash":false},{"StartTime":34803.0,"Position":91.34114,"HyperDash":false}]},{"StartTime":35005.0,"Objects":[{"StartTime":35005.0,"Position":192.0,"HyperDash":false},{"StartTime":35087.0,"Position":172.904892,"HyperDash":false},{"StartTime":35205.0,"Position":152.743652,"HyperDash":false}]},{"StartTime":35407.0,"Objects":[{"StartTime":35407.0,"Position":389.0,"HyperDash":false},{"StartTime":35489.0,"Position":348.2696,"HyperDash":false},{"StartTime":35607.0,"Position":314.0478,"HyperDash":false}]},{"StartTime":35808.0,"Objects":[{"StartTime":35808.0,"Position":450.0,"HyperDash":false},{"StartTime":35890.0,"Position":440.377838,"HyperDash":false},{"StartTime":36008.0,"Position":414.3362,"HyperDash":false}]},{"StartTime":36210.0,"Objects":[{"StartTime":36210.0,"Position":314.0,"HyperDash":false}]},{"StartTime":36612.0,"Objects":[{"StartTime":36612.0,"Position":123.0,"HyperDash":false}]},{"StartTime":36813.0,"Objects":[{"StartTime":36813.0,"Position":230.0,"HyperDash":false}]},{"StartTime":37014.0,"Objects":[{"StartTime":37014.0,"Position":337.0,"HyperDash":false}]},{"StartTime":37215.0,"Objects":[{"StartTime":37215.0,"Position":230.0,"HyperDash":false}]},{"StartTime":37416.0,"Objects":[{"StartTime":37416.0,"Position":232.0,"HyperDash":false}]},{"StartTime":37516.0,"Objects":[{"StartTime":37516.0,"Position":232.0,"HyperDash":false}]},{"StartTime":37617.0,"Objects":[{"StartTime":37617.0,"Position":232.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1597806.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1597806.osu new file mode 100644 index 0000000000..b9ce7a927d --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/1597806.osu @@ -0,0 +1,152 @@ +osu file format v14 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +HPDrainRate:6 +CircleSize:7 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:1.5 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +42,401.875418620228,3,1,0,50,1,0 +18528,-100,3,1,0,40,0,0 +18930,-100,3,1,0,30,0,0 +19332,-100,3,1,0,50,0,1 +24154,-100,3,1,0,50,0,1 +27771,-100,3,1,0,50,0,0 +28977,-100,3,1,0,50,0,0 +30182,-100,3,1,0,50,0,0 +31388,-100,3,1,0,50,0,0 +32593,-100,3,1,0,50,0,0 +33799,-100,3,1,0,50,0,0 + +[HitObjects] +288,165,42,6,0,P|255:181|209:175,1,75,4|0,0:0|0:0,0:0:0:0: +125,38,443,2,0,P|155:58|173:101,1,75,8|0,0:0|0:0,0:0:0:0: +95,236,845,2,0,P|97:199|125:162,1,75,8|0,0:0|0:0,0:0:0:0: +250,271,1247,6,0,L|164:295,1,75,8|0,0:0|0:0,0:0:0:0: +277,199,1649,2,0,L|363:175,1,75,8|0,0:0|0:0,0:0:0:0: +448,85,2051,2,0,L|376:106,1,75,8|0,0:0|0:0,0:0:0:0: +499,211,2453,6,0,P|502:258|491:298,1,75,4|0,0:0|0:0,0:0:0:0: +397,374,2855,2,0,P|400:336|394:300,1,75,8|0,0:0|0:0,0:0:0:0: +295,211,3257,2,0,P|298:248|292:284,1,75,8|0,0:0|0:0,0:0:0:0: +134,307,3658,6,0,L|227:315,1,75,8|0,0:0|0:0,0:0:0:0: +95,143,4060,2,0,L|134:228,1,75,8|0,0:0|0:0,0:0:0:0: +217,28,4462,2,0,L|163:105,1,75,8|0,0:0|0:0,0:0:0:0: +268,199,4864,6,0,P|261:152|270:113,1,75,4|0,0:0|0:0,0:0:0:0: +418,214,5266,2,0,P|380:243|342:255,1,75,8|0,0:0|0:0,0:0:0:0: +356,76,5668,2,0,P|400:93|429:120,1,75,4|0,0:0|0:0,0:0:0:0: +265,125,6070,6,0,L|184:140,1,75,8|0,0:0|0:0,0:0:0:0: +35,204,6472,2,0,L|116:219,1,75,8|0,0:0|0:0,0:0:0:0: +265,283,6873,2,0,L|184:298,1,75,4|0,0:0|0:0,0:0:0:0: +323,195,7275,6,0,P|366:203|403:237,1,75,8|0,0:0|0:0,0:0:0:0: +493,117,7677,2,0,P|450:125|413:159,1,75,8|0,0:0|0:0,0:0:0:0: +323,39,8079,2,0,P|366:47|403:81,1,75,8|0,0:0|0:0,0:0:0:0: +273,140,8481,5,4,0:0:0:0: +187,31,8682,1,0,0:0:0:0: +101,140,8883,1,8,0:0:0:0: +187,249,9084,1,0,0:0:0:0: +101,358,9285,1,8,0:0:0:0: +15,249,9486,1,0,0:0:0:0: +187,249,9687,6,0,L|112:266,1,75,4|0,0:0|0:0,0:0:0:0: +264,181,10088,2,0,L|286:107,1,75,8|0,0:0|0:0,0:0:0:0: +287,283,10490,2,0,L|339:339,1,75,8|0,0:0|0:0,0:0:0:0: +422,222,10892,6,0,P|425:180|411:133,1,75,8|0,0:0|0:0,0:0:0:0: +287,283,11294,2,0,P|324:264|358:228,1,75,8|0,0:0|0:0,0:0:0:0: +166,196,11696,2,0,P|200:219|248:230,1,75,8|0,0:0|0:0,0:0:0:0: +332,83,12098,6,0,L|236:102,1,75,4|0,0:0|0:0,0:0:0:0: +394,139,12500,2,0,L|490:158,1,75,8|0,0:0|0:0,0:0:0:0: +332,195,12902,2,0,L|236:214,1,75,8|0,0:0|0:0,0:0:0:0: +413,321,13303,6,0,P|419:253|399:213,1,75,8|0,0:0|0:0,0:0:0:0: +327,121,13705,2,0,P|333:189|313:229,1,75,8|0,0:0|0:0,0:0:0:0: +241,321,14107,2,0,P|247:253|227:213,1,75,8|0,0:0|0:0,0:0:0:0: +118,175,14509,6,0,L|212:188,1,75,4|0,0:0|0:0,0:0:0:0: +297,100,14911,2,0,L|238:174,1,75,8|0,0:0|0:0,0:0:0:0: +273,292,15313,2,0,L|237:204,1,75,4|0,0:0|0:0,0:0:0:0: +235,357,15715,6,0,P|272:368|321:351,1,75,8|0,0:0|0:0,0:0:0:0: +441,286,16117,2,0,P|404:297|355:280,1,75,8|0,0:0|0:0,0:0:0:0: +235,215,16518,2,0,P|272:226|321:209,1,75,4|0,0:0|0:0,0:0:0:0: +436,127,16920,6,0,L|447:217,1,75,8|0,0:0|0:0,0:0:0:0: +345,22,17322,2,0,L|428:57,1,75,8|0,0:0|0:0,0:0:0:0: +208,48,17724,2,0,L|280:-6,1,75,4|0,0:0|0:0,0:0:0:0: +187,162,18126,5,4,0:0:0:0: +187,162,18528,1,8,0:0:0:0: +187,162,18930,1,8,0:0:0:0: +187,162,19332,5,4,0:0:0:0: +187,263,19532,1,0,0:0:0:0: +345,107,19733,1,8,0:0:0:0: +257,157,19933,1,0,0:0:0:0: +471,216,20135,1,8,0:0:0:0: +384,165,20335,1,0,0:0:0:0: +284,300,20537,5,4,0:0:0:0: +371,249,20737,1,0,0:0:0:0: +157,190,20938,1,8,0:0:0:0: +244,241,21140,1,0,0:0:0:0: +188,27,21340,1,4,0:0:0:0: +188,127,21542,1,0,0:0:0:0: +345,40,21743,5,4,0:0:0:0: +250,77,21944,1,0,0:0:0:0: +419,147,22145,2,0,L|328:147,1,75,4|0,0:0|0:0,0:0:0:0: +196,219,22547,2,0,L|287:219,1,75,8|0,0:0|0:0,0:0:0:0: +419,291,22948,5,4,0:0:0:0: +344,224,23149,1,0,0:0:0:0: +305,352,23350,2,0,P|310:313|305:269,1,75,4|0,0:0|0:0,0:0:0:0: +240,122,23752,2,0,P|235:161|240:205,1,75,8|0,0:0|0:0,0:0:0:0: +429,207,24154,6,0,L|342:213,1,75,8|0,0:0|0:0,0:0:0:0: +232,272,24556,2,0,L|319:278,1,75,8|0,0:0|0:0,0:0:0:0: +429,337,24958,2,0,L|342:343,1,75,8|0,0:0|0:0,0:0:0:0: +501,280,25360,5,4,0:0:0:0: +429,207,25561,1,0,0:0:0:0: +491,62,25762,2,0,L|490:145,1,75,4|0,0:0|0:0,0:0:0:0: +372,236,26163,2,0,L|373:153,1,75,8|0,0:0|0:0,0:0:0:0: +372,7,26565,5,4,0:0:0:0: +431,121,26766,1,0,0:0:0:0: +372,236,26967,1,8,0:0:0:0: +314,121,27168,1,0,0:0:0:0: +254,236,27369,1,8,0:0:0:0: +313,351,27570,1,0,0:0:0:0: +372,236,27771,6,0,B|473:236|473:236|473:121|473:121|306:121,1,375,4|0,0:0|0:0,0:0:0:0: +185,192,28977,6,0,B|256:214|256:214|328:192,1,150,4|0,0:0|0:0,0:0:0:0: +256,94,29579,6,0,L|256:43,2,37.5,4|0|8,0:0|0:0|0:0,0:0:0:0: +467,188,30182,5,4,0:0:0:0: +395,307,30383,1,0,0:0:0:0: +323,188,30584,1,8,0:0:0:0: +251,307,30785,1,0,0:0:0:0: +179,188,30986,1,8,0:0:0:0: +107,307,31187,1,0,0:0:0:0: +35,188,31388,6,0,L|35:39,1,150,4|0,0:0|0:0,0:0:0:0: +105,116,31991,2,0,L|154:116,2,37.5,4|0|8,0:0|0:0|0:0,0:0:0:0: +314,4,32593,5,4,0:0:0:0: +434,66,32794,1,0,0:0:0:0: +314,128,32995,1,8,0:0:0:0: +434,190,33196,1,0,0:0:0:0: +314,252,33397,1,8,0:0:0:0: +434,314,33598,1,0,0:0:0:0: +314,384,33799,6,0,L|357:313,1,75,4|0,0:0|0:0,0:0:0:0: +117,340,34201,2,0,L|200:342,1,75,8|0,0:0|0:0,0:0:0:0: +56,148,34603,2,0,L|95:221,1,75,8|0,0:0|0:0,0:0:0:0: +192,0,35005,2,0,L|149:70,1,75,4|0,0:0|0:0,0:0:0:0: +389,42,35407,2,0,L|305:39,1,75,8|0,0:0|0:0,0:0:0:0: +450,234,35808,2,0,L|410:160,1,75,8|0,0:0|0:0,0:0:0:0: +314,384,36210,1,4,0:0:0:0: +123,192,36612,5,4,0:0:0:0: +230,327,36813,1,0,0:0:0:0: +337,192,37014,1,8,0:0:0:0: +230,57,37215,1,0,0:0:0:0: +232,193,37416,5,4,0:0:0:0: +232,193,37516,1,0,0:0:0:0: +232,193,37617,1,8,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2190499-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2190499-expected-conversion.json new file mode 100644 index 0000000000..fb919302d9 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2190499-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":1739.0,"Objects":[{"StartTime":1739.0,"Position":367.0,"HyperDash":false},{"StartTime":1817.0,"Position":334.444244,"HyperDash":false},{"StartTime":1896.0,"Position":321.501648,"HyperDash":false},{"StartTime":1975.0,"Position":314.851929,"HyperDash":false},{"StartTime":2054.0,"Position":307.22052,"HyperDash":false},{"StartTime":2124.0,"Position":315.042236,"HyperDash":false},{"StartTime":2194.0,"Position":286.526184,"HyperDash":false},{"StartTime":2264.0,"Position":269.750366,"HyperDash":false},{"StartTime":2370.0,"Position":246.8734,"HyperDash":false}]},{"StartTime":3002.0,"Objects":[{"StartTime":3002.0,"Position":161.0,"HyperDash":false},{"StartTime":3080.0,"Position":160.934677,"HyperDash":false},{"StartTime":3159.0,"Position":181.699768,"HyperDash":false},{"StartTime":3238.0,"Position":189.906845,"HyperDash":false},{"StartTime":3317.0,"Position":212.345535,"HyperDash":false},{"StartTime":3387.0,"Position":233.989639,"HyperDash":false},{"StartTime":3457.0,"Position":230.043228,"HyperDash":false},{"StartTime":3527.0,"Position":221.436966,"HyperDash":false},{"StartTime":3633.0,"Position":233.8208,"HyperDash":false}]},{"StartTime":4265.0,"Objects":[{"StartTime":4265.0,"Position":47.0,"HyperDash":false},{"StartTime":4334.0,"Position":63.4803925,"HyperDash":false},{"StartTime":4404.0,"Position":52.9349747,"HyperDash":false},{"StartTime":4474.0,"Position":77.8079,"HyperDash":false},{"StartTime":4580.0,"Position":111.004265,"HyperDash":false}]},{"StartTime":4897.0,"Objects":[{"StartTime":4897.0,"Position":235.0,"HyperDash":false},{"StartTime":4975.0,"Position":269.900024,"HyperDash":false},{"StartTime":5054.0,"Position":287.131927,"HyperDash":false},{"StartTime":5133.0,"Position":301.489746,"HyperDash":false},{"StartTime":5212.0,"Position":305.972,"HyperDash":false},{"StartTime":5273.0,"Position":301.195129,"HyperDash":false},{"StartTime":5370.0,"Position":334.383362,"HyperDash":false}]},{"StartTime":5528.0,"Objects":[{"StartTime":5528.0,"Position":372.0,"HyperDash":false},{"StartTime":5606.0,"Position":378.156158,"HyperDash":false},{"StartTime":5685.0,"Position":354.0074,"HyperDash":false},{"StartTime":5764.0,"Position":357.6568,"HyperDash":false},{"StartTime":5843.0,"Position":349.1154,"HyperDash":false},{"StartTime":5913.0,"Position":341.790833,"HyperDash":false},{"StartTime":5983.0,"Position":342.120453,"HyperDash":false},{"StartTime":6053.0,"Position":355.1087,"HyperDash":false},{"StartTime":6159.0,"Position":339.322418,"HyperDash":false}]},{"StartTime":6791.0,"Objects":[{"StartTime":6791.0,"Position":55.0,"HyperDash":false},{"StartTime":6860.0,"Position":49.3883972,"HyperDash":false},{"StartTime":6930.0,"Position":57.4187775,"HyperDash":false},{"StartTime":7000.0,"Position":74.47069,"HyperDash":false},{"StartTime":7106.0,"Position":114.237465,"HyperDash":false}]},{"StartTime":7423.0,"Objects":[{"StartTime":7423.0,"Position":240.0,"HyperDash":false},{"StartTime":7501.0,"Position":237.8742,"HyperDash":false},{"StartTime":7580.0,"Position":228.717819,"HyperDash":false},{"StartTime":7659.0,"Position":200.180328,"HyperDash":false},{"StartTime":7738.0,"Position":193.621948,"HyperDash":false},{"StartTime":7799.0,"Position":189.067337,"HyperDash":false},{"StartTime":7896.0,"Position":188.528091,"HyperDash":false}]},{"StartTime":8054.0,"Objects":[{"StartTime":8054.0,"Position":273.0,"HyperDash":false},{"StartTime":8132.0,"Position":306.8755,"HyperDash":false},{"StartTime":8211.0,"Position":298.36087,"HyperDash":false},{"StartTime":8290.0,"Position":326.902954,"HyperDash":false},{"StartTime":8369.0,"Position":340.2283,"HyperDash":false},{"StartTime":8439.0,"Position":337.1718,"HyperDash":false},{"StartTime":8509.0,"Position":325.47818,"HyperDash":false},{"StartTime":8579.0,"Position":310.550323,"HyperDash":false},{"StartTime":8685.0,"Position":273.0,"HyperDash":false}]},{"StartTime":9002.0,"Objects":[{"StartTime":9002.0,"Position":147.0,"HyperDash":false},{"StartTime":9062.0,"Position":147.914444,"HyperDash":false},{"StartTime":9159.0,"Position":110.615738,"HyperDash":false}]},{"StartTime":9318.0,"Objects":[{"StartTime":9318.0,"Position":59.0,"HyperDash":false},{"StartTime":9396.0,"Position":72.10824,"HyperDash":false},{"StartTime":9475.0,"Position":59.51161,"HyperDash":false},{"StartTime":9554.0,"Position":28.2636833,"HyperDash":false},{"StartTime":9633.0,"Position":39.3327179,"HyperDash":false},{"StartTime":9703.0,"Position":49.56317,"HyperDash":false},{"StartTime":9773.0,"Position":37.3222733,"HyperDash":false},{"StartTime":9843.0,"Position":38.56619,"HyperDash":false},{"StartTime":9949.0,"Position":59.0,"HyperDash":false}]},{"StartTime":10265.0,"Objects":[{"StartTime":10265.0,"Position":133.0,"HyperDash":false}]},{"StartTime":10581.0,"Objects":[{"StartTime":10581.0,"Position":494.0,"HyperDash":false},{"StartTime":10659.0,"Position":135.0,"HyperDash":false},{"StartTime":10738.0,"Position":30.0,"HyperDash":false},{"StartTime":10817.0,"Position":11.0,"HyperDash":false},{"StartTime":10896.0,"Position":239.0,"HyperDash":false},{"StartTime":10975.0,"Position":505.0,"HyperDash":false},{"StartTime":11054.0,"Position":353.0,"HyperDash":false},{"StartTime":11133.0,"Position":136.0,"HyperDash":false},{"StartTime":11212.0,"Position":135.0,"HyperDash":false},{"StartTime":11291.0,"Position":346.0,"HyperDash":false},{"StartTime":11370.0,"Position":39.0,"HyperDash":false},{"StartTime":11449.0,"Position":300.0,"HyperDash":false},{"StartTime":11528.0,"Position":398.0,"HyperDash":false},{"StartTime":11607.0,"Position":151.0,"HyperDash":false},{"StartTime":11686.0,"Position":73.0,"HyperDash":false},{"StartTime":11765.0,"Position":311.0,"HyperDash":false},{"StartTime":11844.0,"Position":90.0,"HyperDash":false}]},{"StartTime":13107.0,"Objects":[{"StartTime":13107.0,"Position":264.0,"HyperDash":false},{"StartTime":13185.0,"Position":477.0,"HyperDash":false},{"StartTime":13264.0,"Position":473.0,"HyperDash":false},{"StartTime":13343.0,"Position":120.0,"HyperDash":false},{"StartTime":13422.0,"Position":115.0,"HyperDash":false},{"StartTime":13501.0,"Position":163.0,"HyperDash":false},{"StartTime":13580.0,"Position":447.0,"HyperDash":false},{"StartTime":13659.0,"Position":72.0,"HyperDash":false},{"StartTime":13738.0,"Position":257.0,"HyperDash":false},{"StartTime":13817.0,"Position":153.0,"HyperDash":false},{"StartTime":13896.0,"Position":388.0,"HyperDash":false},{"StartTime":13975.0,"Position":336.0,"HyperDash":false},{"StartTime":14054.0,"Position":13.0,"HyperDash":false},{"StartTime":14133.0,"Position":429.0,"HyperDash":false},{"StartTime":14212.0,"Position":381.0,"HyperDash":false},{"StartTime":14291.0,"Position":186.0,"HyperDash":false},{"StartTime":14370.0,"Position":267.0,"HyperDash":false}]},{"StartTime":15633.0,"Objects":[{"StartTime":15633.0,"Position":74.0,"HyperDash":false},{"StartTime":15711.0,"Position":95.2118149,"HyperDash":false},{"StartTime":15790.0,"Position":106.218536,"HyperDash":false},{"StartTime":15869.0,"Position":134.014709,"HyperDash":false},{"StartTime":15948.0,"Position":138.984512,"HyperDash":false},{"StartTime":16008.0,"Position":164.493423,"HyperDash":false},{"StartTime":16068.0,"Position":149.9466,"HyperDash":false},{"StartTime":16128.0,"Position":159.583786,"HyperDash":false},{"StartTime":16225.0,"Position":198.210388,"HyperDash":false}]},{"StartTime":17844.0,"Objects":[{"StartTime":17844.0,"Position":189.0,"HyperDash":false}]},{"StartTime":18160.0,"Objects":[{"StartTime":18160.0,"Position":189.0,"HyperDash":false},{"StartTime":18220.0,"Position":203.848145,"HyperDash":false},{"StartTime":18317.0,"Position":255.355225,"HyperDash":false}]},{"StartTime":18476.0,"Objects":[{"StartTime":18476.0,"Position":402.0,"HyperDash":false},{"StartTime":18536.0,"Position":355.556671,"HyperDash":false},{"StartTime":18633.0,"Position":335.176544,"HyperDash":false}]},{"StartTime":18791.0,"Objects":[{"StartTime":18791.0,"Position":383.0,"HyperDash":false},{"StartTime":18860.0,"Position":387.0126,"HyperDash":false},{"StartTime":18930.0,"Position":402.196167,"HyperDash":false},{"StartTime":19000.0,"Position":402.402679,"HyperDash":false},{"StartTime":19106.0,"Position":403.979065,"HyperDash":false}]},{"StartTime":19265.0,"Objects":[{"StartTime":19265.0,"Position":254.0,"HyperDash":false}]},{"StartTime":19423.0,"Objects":[{"StartTime":19423.0,"Position":178.0,"HyperDash":false},{"StartTime":19483.0,"Position":149.544052,"HyperDash":false},{"StartTime":19580.0,"Position":105.085159,"HyperDash":false}]},{"StartTime":19739.0,"Objects":[{"StartTime":19739.0,"Position":245.0,"HyperDash":false},{"StartTime":19799.0,"Position":290.012634,"HyperDash":false},{"StartTime":19896.0,"Position":317.941528,"HyperDash":false}]},{"StartTime":20054.0,"Objects":[{"StartTime":20054.0,"Position":287.0,"HyperDash":false},{"StartTime":20123.0,"Position":275.9874,"HyperDash":false},{"StartTime":20193.0,"Position":286.803833,"HyperDash":false},{"StartTime":20263.0,"Position":255.597321,"HyperDash":false},{"StartTime":20369.0,"Position":266.020935,"HyperDash":false}]},{"StartTime":20528.0,"Objects":[{"StartTime":20528.0,"Position":167.0,"HyperDash":false}]},{"StartTime":20686.0,"Objects":[{"StartTime":20686.0,"Position":110.0,"HyperDash":false},{"StartTime":20746.0,"Position":87.56889,"HyperDash":false},{"StartTime":20843.0,"Position":44.4256668,"HyperDash":false}]},{"StartTime":21002.0,"Objects":[{"StartTime":21002.0,"Position":158.0,"HyperDash":false},{"StartTime":21062.0,"Position":174.0545,"HyperDash":false},{"StartTime":21159.0,"Position":223.260239,"HyperDash":false}]},{"StartTime":21318.0,"Objects":[{"StartTime":21318.0,"Position":105.0,"HyperDash":false},{"StartTime":21378.0,"Position":86.56889,"HyperDash":false},{"StartTime":21475.0,"Position":39.4256668,"HyperDash":false}]},{"StartTime":21634.0,"Objects":[{"StartTime":21634.0,"Position":153.0,"HyperDash":false},{"StartTime":21694.0,"Position":191.0545,"HyperDash":false},{"StartTime":21791.0,"Position":218.260239,"HyperDash":false}]},{"StartTime":21949.0,"Objects":[{"StartTime":21949.0,"Position":321.0,"HyperDash":false}]},{"StartTime":22107.0,"Objects":[{"StartTime":22107.0,"Position":372.0,"HyperDash":false}]},{"StartTime":22265.0,"Objects":[{"StartTime":22265.0,"Position":345.0,"HyperDash":false},{"StartTime":22325.0,"Position":332.141785,"HyperDash":false},{"StartTime":22422.0,"Position":327.377563,"HyperDash":false}]},{"StartTime":22581.0,"Objects":[{"StartTime":22581.0,"Position":413.0,"HyperDash":false}]},{"StartTime":22739.0,"Objects":[{"StartTime":22739.0,"Position":442.0,"HyperDash":false}]},{"StartTime":22897.0,"Objects":[{"StartTime":22897.0,"Position":409.0,"HyperDash":false},{"StartTime":22957.0,"Position":364.564,"HyperDash":false},{"StartTime":23054.0,"Position":338.834,"HyperDash":false}]},{"StartTime":23212.0,"Objects":[{"StartTime":23212.0,"Position":205.0,"HyperDash":false},{"StartTime":23272.0,"Position":218.780579,"HyperDash":false},{"StartTime":23369.0,"Position":224.420334,"HyperDash":false}]},{"StartTime":23528.0,"Objects":[{"StartTime":23528.0,"Position":73.0,"HyperDash":false},{"StartTime":23588.0,"Position":68.21941,"HyperDash":false},{"StartTime":23685.0,"Position":53.5796623,"HyperDash":false}]},{"StartTime":23844.0,"Objects":[{"StartTime":23844.0,"Position":240.0,"HyperDash":false},{"StartTime":23904.0,"Position":234.999878,"HyperDash":false},{"StartTime":24001.0,"Position":220.82193,"HyperDash":false}]},{"StartTime":24160.0,"Objects":[{"StartTime":24160.0,"Position":88.0,"HyperDash":false},{"StartTime":24220.0,"Position":60.3964767,"HyperDash":false},{"StartTime":24317.0,"Position":68.93455,"HyperDash":false}]},{"StartTime":24476.0,"Objects":[{"StartTime":24476.0,"Position":206.0,"HyperDash":false},{"StartTime":24536.0,"Position":215.970291,"HyperDash":false},{"StartTime":24633.0,"Position":281.8056,"HyperDash":false}]},{"StartTime":24791.0,"Objects":[{"StartTime":24791.0,"Position":425.0,"HyperDash":false},{"StartTime":24851.0,"Position":385.029724,"HyperDash":false},{"StartTime":24948.0,"Position":349.1944,"HyperDash":false}]},{"StartTime":25107.0,"Objects":[{"StartTime":25107.0,"Position":196.0,"HyperDash":false},{"StartTime":25167.0,"Position":233.970291,"HyperDash":false},{"StartTime":25264.0,"Position":271.8056,"HyperDash":false}]},{"StartTime":25423.0,"Objects":[{"StartTime":25423.0,"Position":415.0,"HyperDash":false}]},{"StartTime":25581.0,"Objects":[{"StartTime":25581.0,"Position":363.0,"HyperDash":false}]},{"StartTime":25739.0,"Objects":[{"StartTime":25739.0,"Position":263.0,"HyperDash":false},{"StartTime":25799.0,"Position":263.286316,"HyperDash":false},{"StartTime":25896.0,"Position":278.260681,"HyperDash":false}]},{"StartTime":26054.0,"Objects":[{"StartTime":26054.0,"Position":418.0,"HyperDash":false},{"StartTime":26114.0,"Position":438.267456,"HyperDash":false},{"StartTime":26211.0,"Position":432.8865,"HyperDash":false}]},{"StartTime":26370.0,"Objects":[{"StartTime":26370.0,"Position":251.0,"HyperDash":false},{"StartTime":26430.0,"Position":272.286316,"HyperDash":false},{"StartTime":26527.0,"Position":266.260681,"HyperDash":false}]},{"StartTime":26686.0,"Objects":[{"StartTime":26686.0,"Position":406.0,"HyperDash":false},{"StartTime":26746.0,"Position":403.267456,"HyperDash":false},{"StartTime":26843.0,"Position":420.8865,"HyperDash":false}]},{"StartTime":27002.0,"Objects":[{"StartTime":27002.0,"Position":326.0,"HyperDash":false},{"StartTime":27102.0,"Position":263.416656,"HyperDash":false},{"StartTime":27238.0,"Position":217.484329,"HyperDash":false}]},{"StartTime":27318.0,"Objects":[{"StartTime":27318.0,"Position":215.0,"HyperDash":false},{"StartTime":27418.0,"Position":244.682343,"HyperDash":false},{"StartTime":27554.0,"Position":323.347046,"HyperDash":false}]},{"StartTime":27633.0,"Objects":[{"StartTime":27633.0,"Position":324.0,"HyperDash":false},{"StartTime":27702.0,"Position":307.889435,"HyperDash":false},{"StartTime":27772.0,"Position":261.760223,"HyperDash":false},{"StartTime":27842.0,"Position":231.054062,"HyperDash":false},{"StartTime":27948.0,"Position":179.503586,"HyperDash":false}]},{"StartTime":28265.0,"Objects":[{"StartTime":28265.0,"Position":65.0,"HyperDash":false},{"StartTime":28334.0,"Position":61.494606,"HyperDash":false},{"StartTime":28404.0,"Position":60.7236862,"HyperDash":false},{"StartTime":28474.0,"Position":89.86115,"HyperDash":false},{"StartTime":28580.0,"Position":97.86348,"HyperDash":false}]},{"StartTime":28739.0,"Objects":[{"StartTime":28739.0,"Position":153.0,"HyperDash":false}]},{"StartTime":28897.0,"Objects":[{"StartTime":28897.0,"Position":153.0,"HyperDash":false}]},{"StartTime":29054.0,"Objects":[{"StartTime":29054.0,"Position":215.0,"HyperDash":false},{"StartTime":29114.0,"Position":247.582169,"HyperDash":false},{"StartTime":29211.0,"Position":279.019775,"HyperDash":false}]},{"StartTime":29370.0,"Objects":[{"StartTime":29370.0,"Position":332.0,"HyperDash":false},{"StartTime":29430.0,"Position":288.006042,"HyperDash":false},{"StartTime":29527.0,"Position":267.917969,"HyperDash":false}]},{"StartTime":29686.0,"Objects":[{"StartTime":29686.0,"Position":371.0,"HyperDash":false}]},{"StartTime":29844.0,"Objects":[{"StartTime":29844.0,"Position":371.0,"HyperDash":false}]},{"StartTime":30002.0,"Objects":[{"StartTime":30002.0,"Position":444.0,"HyperDash":false}]},{"StartTime":30160.0,"Objects":[{"StartTime":30160.0,"Position":444.0,"HyperDash":false},{"StartTime":30220.0,"Position":451.1502,"HyperDash":false},{"StartTime":30317.0,"Position":463.328674,"HyperDash":false}]},{"StartTime":30476.0,"Objects":[{"StartTime":30476.0,"Position":393.0,"HyperDash":false},{"StartTime":30536.0,"Position":364.8498,"HyperDash":false},{"StartTime":30633.0,"Position":373.671326,"HyperDash":false}]},{"StartTime":30791.0,"Objects":[{"StartTime":30791.0,"Position":265.0,"HyperDash":false},{"StartTime":30851.0,"Position":250.101547,"HyperDash":false},{"StartTime":30948.0,"Position":197.232391,"HyperDash":false}]},{"StartTime":31107.0,"Objects":[{"StartTime":31107.0,"Position":80.0,"HyperDash":false},{"StartTime":31167.0,"Position":86.86766,"HyperDash":false},{"StartTime":31264.0,"Position":147.687042,"HyperDash":false}]},{"StartTime":31423.0,"Objects":[{"StartTime":31423.0,"Position":124.0,"HyperDash":false},{"StartTime":31483.0,"Position":115.084091,"HyperDash":false},{"StartTime":31580.0,"Position":56.1867,"HyperDash":false}]},{"StartTime":31739.0,"Objects":[{"StartTime":31739.0,"Position":164.0,"HyperDash":false}]},{"StartTime":31897.0,"Objects":[{"StartTime":31897.0,"Position":164.0,"HyperDash":false},{"StartTime":31957.0,"Position":198.867661,"HyperDash":false},{"StartTime":32054.0,"Position":231.687042,"HyperDash":false}]},{"StartTime":32212.0,"Objects":[{"StartTime":32212.0,"Position":365.0,"HyperDash":false}]},{"StartTime":32370.0,"Objects":[{"StartTime":32370.0,"Position":365.0,"HyperDash":false},{"StartTime":32430.0,"Position":375.563446,"HyperDash":false},{"StartTime":32527.0,"Position":383.7374,"HyperDash":false}]},{"StartTime":32686.0,"Objects":[{"StartTime":32686.0,"Position":488.0,"HyperDash":false},{"StartTime":32755.0,"Position":478.588562,"HyperDash":false},{"StartTime":32825.0,"Position":485.259918,"HyperDash":false},{"StartTime":32895.0,"Position":471.216827,"HyperDash":false},{"StartTime":33001.0,"Position":467.5074,"HyperDash":false}]},{"StartTime":33160.0,"Objects":[{"StartTime":33160.0,"Position":406.0,"HyperDash":false}]},{"StartTime":33318.0,"Objects":[{"StartTime":33318.0,"Position":277.0,"HyperDash":false},{"StartTime":33387.0,"Position":249.144089,"HyperDash":false},{"StartTime":33457.0,"Position":211.528214,"HyperDash":false},{"StartTime":33527.0,"Position":207.367355,"HyperDash":false},{"StartTime":33633.0,"Position":161.097717,"HyperDash":false}]},{"StartTime":33791.0,"Objects":[{"StartTime":33791.0,"Position":283.0,"HyperDash":false}]},{"StartTime":33949.0,"Objects":[{"StartTime":33949.0,"Position":283.0,"HyperDash":false}]},{"StartTime":34107.0,"Objects":[{"StartTime":34107.0,"Position":158.0,"HyperDash":false},{"StartTime":34167.0,"Position":122.620682,"HyperDash":false},{"StartTime":34264.0,"Position":93.20762,"HyperDash":false}]},{"StartTime":34423.0,"Objects":[{"StartTime":34423.0,"Position":19.0,"HyperDash":false},{"StartTime":34483.0,"Position":37.9395447,"HyperDash":false},{"StartTime":34580.0,"Position":83.68275,"HyperDash":false}]},{"StartTime":34739.0,"Objects":[{"StartTime":34739.0,"Position":158.0,"HyperDash":false}]},{"StartTime":34897.0,"Objects":[{"StartTime":34897.0,"Position":158.0,"HyperDash":false}]},{"StartTime":35054.0,"Objects":[{"StartTime":35054.0,"Position":204.0,"HyperDash":false}]},{"StartTime":35212.0,"Objects":[{"StartTime":35212.0,"Position":204.0,"HyperDash":false},{"StartTime":35272.0,"Position":191.310013,"HyperDash":false},{"StartTime":35369.0,"Position":216.139023,"HyperDash":false}]},{"StartTime":35528.0,"Objects":[{"StartTime":35528.0,"Position":345.0,"HyperDash":false},{"StartTime":35588.0,"Position":339.8689,"HyperDash":false},{"StartTime":35685.0,"Position":332.011932,"HyperDash":false}]},{"StartTime":35844.0,"Objects":[{"StartTime":35844.0,"Position":461.0,"HyperDash":false},{"StartTime":35922.0,"Position":426.3106,"HyperDash":false},{"StartTime":36001.0,"Position":379.89856,"HyperDash":false},{"StartTime":36080.0,"Position":350.1098,"HyperDash":false},{"StartTime":36159.0,"Position":330.750031,"HyperDash":false},{"StartTime":36229.0,"Position":366.784668,"HyperDash":false},{"StartTime":36299.0,"Position":388.860931,"HyperDash":false},{"StartTime":36369.0,"Position":414.029144,"HyperDash":false},{"StartTime":36475.0,"Position":461.0,"HyperDash":false}]},{"StartTime":36791.0,"Objects":[{"StartTime":36791.0,"Position":248.0,"HyperDash":false}]},{"StartTime":36949.0,"Objects":[{"StartTime":36949.0,"Position":248.0,"HyperDash":false},{"StartTime":37009.0,"Position":242.674042,"HyperDash":false},{"StartTime":37106.0,"Position":261.06012,"HyperDash":false}]},{"StartTime":37265.0,"Objects":[{"StartTime":37265.0,"Position":189.0,"HyperDash":false}]},{"StartTime":37423.0,"Objects":[{"StartTime":37423.0,"Position":130.0,"HyperDash":false},{"StartTime":37483.0,"Position":98.5721054,"HyperDash":false},{"StartTime":37580.0,"Position":66.22712,"HyperDash":false}]},{"StartTime":37739.0,"Objects":[{"StartTime":37739.0,"Position":32.0,"HyperDash":false}]},{"StartTime":37897.0,"Objects":[{"StartTime":37897.0,"Position":79.0,"HyperDash":false}]},{"StartTime":38054.0,"Objects":[{"StartTime":38054.0,"Position":126.0,"HyperDash":false}]},{"StartTime":38212.0,"Objects":[{"StartTime":38212.0,"Position":67.0,"HyperDash":false}]},{"StartTime":38370.0,"Objects":[{"StartTime":38370.0,"Position":189.0,"HyperDash":false},{"StartTime":38439.0,"Position":208.518677,"HyperDash":false},{"StartTime":38509.0,"Position":230.192429,"HyperDash":false},{"StartTime":38579.0,"Position":265.933716,"HyperDash":false},{"StartTime":38685.0,"Position":294.0829,"HyperDash":false}]},{"StartTime":38844.0,"Objects":[{"StartTime":38844.0,"Position":281.0,"HyperDash":false},{"StartTime":38922.0,"Position":240.8632,"HyperDash":false},{"StartTime":39001.0,"Position":224.955368,"HyperDash":false},{"StartTime":39062.0,"Position":248.9081,"HyperDash":false},{"StartTime":39159.0,"Position":281.0,"HyperDash":false}]},{"StartTime":39318.0,"Objects":[{"StartTime":39318.0,"Position":367.0,"HyperDash":false},{"StartTime":39378.0,"Position":404.815552,"HyperDash":false},{"StartTime":39475.0,"Position":423.320465,"HyperDash":false}]},{"StartTime":39633.0,"Objects":[{"StartTime":39633.0,"Position":493.0,"HyperDash":false}]},{"StartTime":39791.0,"Objects":[{"StartTime":39791.0,"Position":493.0,"HyperDash":false},{"StartTime":39851.0,"Position":484.550964,"HyperDash":false},{"StartTime":39948.0,"Position":499.675018,"HyperDash":false}]},{"StartTime":40107.0,"Objects":[{"StartTime":40107.0,"Position":450.0,"HyperDash":false},{"StartTime":40167.0,"Position":441.449036,"HyperDash":false},{"StartTime":40264.0,"Position":443.324982,"HyperDash":false}]},{"StartTime":40423.0,"Objects":[{"StartTime":40423.0,"Position":379.0,"HyperDash":false}]},{"StartTime":40581.0,"Objects":[{"StartTime":40581.0,"Position":379.0,"HyperDash":false}]},{"StartTime":40739.0,"Objects":[{"StartTime":40739.0,"Position":312.0,"HyperDash":false},{"StartTime":40808.0,"Position":302.798431,"HyperDash":false},{"StartTime":40878.0,"Position":242.555145,"HyperDash":false},{"StartTime":40948.0,"Position":257.265869,"HyperDash":false},{"StartTime":41054.0,"Position":204.051636,"HyperDash":false}]},{"StartTime":41212.0,"Objects":[{"StartTime":41212.0,"Position":120.0,"HyperDash":false},{"StartTime":41290.0,"Position":125.193611,"HyperDash":false},{"StartTime":41369.0,"Position":107.002182,"HyperDash":false},{"StartTime":41430.0,"Position":88.91298,"HyperDash":false},{"StartTime":41527.0,"Position":120.0,"HyperDash":false}]},{"StartTime":41686.0,"Objects":[{"StartTime":41686.0,"Position":195.0,"HyperDash":false},{"StartTime":41746.0,"Position":179.895126,"HyperDash":false},{"StartTime":41843.0,"Position":181.9226,"HyperDash":false}]},{"StartTime":42002.0,"Objects":[{"StartTime":42002.0,"Position":81.0,"HyperDash":false}]},{"StartTime":42160.0,"Objects":[{"StartTime":42160.0,"Position":81.0,"HyperDash":false}]},{"StartTime":42318.0,"Objects":[{"StartTime":42318.0,"Position":157.0,"HyperDash":false}]},{"StartTime":42476.0,"Objects":[{"StartTime":42476.0,"Position":157.0,"HyperDash":false},{"StartTime":42536.0,"Position":192.028351,"HyperDash":false},{"StartTime":42633.0,"Position":217.2575,"HyperDash":false}]},{"StartTime":42791.0,"Objects":[{"StartTime":42791.0,"Position":314.0,"HyperDash":false},{"StartTime":42851.0,"Position":349.048828,"HyperDash":false},{"StartTime":42948.0,"Position":374.311127,"HyperDash":false}]},{"StartTime":43107.0,"Objects":[{"StartTime":43107.0,"Position":224.0,"HyperDash":false},{"StartTime":43176.0,"Position":212.567841,"HyperDash":false},{"StartTime":43246.0,"Position":162.7526,"HyperDash":false},{"StartTime":43316.0,"Position":129.937347,"HyperDash":false},{"StartTime":43422.0,"Position":103.331413,"HyperDash":false}]},{"StartTime":43581.0,"Objects":[{"StartTime":43581.0,"Position":18.0,"HyperDash":false},{"StartTime":43659.0,"Position":17.9562778,"HyperDash":false},{"StartTime":43738.0,"Position":25.988636,"HyperDash":false},{"StartTime":43799.0,"Position":35.9199829,"HyperDash":false},{"StartTime":43896.0,"Position":18.0,"HyperDash":false}]},{"StartTime":44054.0,"Objects":[{"StartTime":44054.0,"Position":118.0,"HyperDash":false},{"StartTime":44114.0,"Position":113.573334,"HyperDash":false},{"StartTime":44211.0,"Position":109.033562,"HyperDash":false}]},{"StartTime":44370.0,"Objects":[{"StartTime":44370.0,"Position":32.0,"HyperDash":false}]},{"StartTime":44528.0,"Objects":[{"StartTime":44528.0,"Position":32.0,"HyperDash":false},{"StartTime":44588.0,"Position":34.55097,"HyperDash":false},{"StartTime":44685.0,"Position":38.6750336,"HyperDash":false}]},{"StartTime":44844.0,"Objects":[{"StartTime":44844.0,"Position":131.0,"HyperDash":false}]},{"StartTime":45002.0,"Objects":[{"StartTime":45002.0,"Position":131.0,"HyperDash":false},{"StartTime":45062.0,"Position":121.323151,"HyperDash":false},{"StartTime":45159.0,"Position":123.99559,"HyperDash":false}]},{"StartTime":45318.0,"Objects":[{"StartTime":45318.0,"Position":215.0,"HyperDash":false}]},{"StartTime":45476.0,"Objects":[{"StartTime":45476.0,"Position":215.0,"HyperDash":false},{"StartTime":45536.0,"Position":253.997345,"HyperDash":false},{"StartTime":45633.0,"Position":275.176361,"HyperDash":false}]},{"StartTime":45791.0,"Objects":[{"StartTime":45791.0,"Position":362.0,"HyperDash":false}]},{"StartTime":45949.0,"Objects":[{"StartTime":45949.0,"Position":362.0,"HyperDash":false}]},{"StartTime":46107.0,"Objects":[{"StartTime":46107.0,"Position":350.0,"HyperDash":false},{"StartTime":46167.0,"Position":360.8421,"HyperDash":false},{"StartTime":46264.0,"Position":354.8202,"HyperDash":false}]},{"StartTime":46423.0,"Objects":[{"StartTime":46423.0,"Position":421.0,"HyperDash":false}]},{"StartTime":46581.0,"Objects":[{"StartTime":46581.0,"Position":421.0,"HyperDash":false}]},{"StartTime":46739.0,"Objects":[{"StartTime":46739.0,"Position":343.0,"HyperDash":false},{"StartTime":46799.0,"Position":312.973572,"HyperDash":false},{"StartTime":46896.0,"Position":282.7475,"HyperDash":false}]},{"StartTime":47054.0,"Objects":[{"StartTime":47054.0,"Position":212.0,"HyperDash":false}]},{"StartTime":47212.0,"Objects":[{"StartTime":47212.0,"Position":176.0,"HyperDash":false}]},{"StartTime":47370.0,"Objects":[{"StartTime":47370.0,"Position":104.0,"HyperDash":false}]},{"StartTime":47449.0,"Objects":[{"StartTime":47449.0,"Position":104.0,"HyperDash":false}]},{"StartTime":47528.0,"Objects":[{"StartTime":47528.0,"Position":104.0,"HyperDash":false},{"StartTime":47628.0,"Position":115.182846,"HyperDash":false},{"StartTime":47764.0,"Position":88.40525,"HyperDash":false}]},{"StartTime":47844.0,"Objects":[{"StartTime":47844.0,"Position":73.0,"HyperDash":false},{"StartTime":47944.0,"Position":61.8171539,"HyperDash":false},{"StartTime":48080.0,"Position":88.59475,"HyperDash":false}]},{"StartTime":48160.0,"Objects":[{"StartTime":48160.0,"Position":108.0,"HyperDash":false}]},{"StartTime":48476.0,"Objects":[{"StartTime":48476.0,"Position":108.0,"HyperDash":false},{"StartTime":48536.0,"Position":130.401276,"HyperDash":false},{"StartTime":48633.0,"Position":176.902069,"HyperDash":false}]},{"StartTime":48791.0,"Objects":[{"StartTime":48791.0,"Position":259.0,"HyperDash":false},{"StartTime":48851.0,"Position":213.142151,"HyperDash":false},{"StartTime":48948.0,"Position":190.198868,"HyperDash":false}]},{"StartTime":49107.0,"Objects":[{"StartTime":49107.0,"Position":329.0,"HyperDash":false},{"StartTime":49176.0,"Position":349.21228,"HyperDash":false},{"StartTime":49246.0,"Position":377.302368,"HyperDash":false},{"StartTime":49316.0,"Position":400.9881,"HyperDash":false},{"StartTime":49422.0,"Position":453.358215,"HyperDash":false}]},{"StartTime":49581.0,"Objects":[{"StartTime":49581.0,"Position":328.0,"HyperDash":false}]},{"StartTime":49739.0,"Objects":[{"StartTime":49739.0,"Position":472.0,"HyperDash":false},{"StartTime":49799.0,"Position":465.138245,"HyperDash":false},{"StartTime":49896.0,"Position":454.808044,"HyperDash":false}]},{"StartTime":50054.0,"Objects":[{"StartTime":50054.0,"Position":324.0,"HyperDash":false},{"StartTime":50114.0,"Position":308.1143,"HyperDash":false},{"StartTime":50211.0,"Position":306.059082,"HyperDash":false}]},{"StartTime":50370.0,"Objects":[{"StartTime":50370.0,"Position":190.0,"HyperDash":false},{"StartTime":50439.0,"Position":173.841064,"HyperDash":false},{"StartTime":50509.0,"Position":134.478058,"HyperDash":false},{"StartTime":50579.0,"Position":91.0187149,"HyperDash":false},{"StartTime":50685.0,"Position":84.73538,"HyperDash":false}]},{"StartTime":50844.0,"Objects":[{"StartTime":50844.0,"Position":206.0,"HyperDash":false}]},{"StartTime":51002.0,"Objects":[{"StartTime":51002.0,"Position":313.0,"HyperDash":false},{"StartTime":51062.0,"Position":334.872467,"HyperDash":false},{"StartTime":51159.0,"Position":326.793732,"HyperDash":false}]},{"StartTime":51318.0,"Objects":[{"StartTime":51318.0,"Position":223.0,"HyperDash":false},{"StartTime":51378.0,"Position":206.316574,"HyperDash":false},{"StartTime":51475.0,"Position":208.626953,"HyperDash":false}]},{"StartTime":51633.0,"Objects":[{"StartTime":51633.0,"Position":268.0,"HyperDash":false},{"StartTime":51693.0,"Position":280.674469,"HyperDash":false},{"StartTime":51790.0,"Position":337.344574,"HyperDash":false}]},{"StartTime":51949.0,"Objects":[{"StartTime":51949.0,"Position":382.0,"HyperDash":false},{"StartTime":52009.0,"Position":340.1904,"HyperDash":false},{"StartTime":52106.0,"Position":312.41745,"HyperDash":false}]},{"StartTime":52265.0,"Objects":[{"StartTime":52265.0,"Position":191.0,"HyperDash":false},{"StartTime":52334.0,"Position":178.67337,"HyperDash":false},{"StartTime":52404.0,"Position":200.576569,"HyperDash":false},{"StartTime":52474.0,"Position":208.688736,"HyperDash":false},{"StartTime":52580.0,"Position":218.40831,"HyperDash":false}]},{"StartTime":52739.0,"Objects":[{"StartTime":52739.0,"Position":145.0,"HyperDash":false}]},{"StartTime":52897.0,"Objects":[{"StartTime":52897.0,"Position":75.0,"HyperDash":false},{"StartTime":52957.0,"Position":116.384956,"HyperDash":false},{"StartTime":53054.0,"Position":143.260788,"HyperDash":false}]},{"StartTime":53212.0,"Objects":[{"StartTime":53212.0,"Position":223.0,"HyperDash":false},{"StartTime":53272.0,"Position":252.329483,"HyperDash":false},{"StartTime":53369.0,"Position":291.407745,"HyperDash":false}]},{"StartTime":53528.0,"Objects":[{"StartTime":53528.0,"Position":423.0,"HyperDash":false}]},{"StartTime":53686.0,"Objects":[{"StartTime":53686.0,"Position":383.0,"HyperDash":false},{"StartTime":53746.0,"Position":384.076538,"HyperDash":false},{"StartTime":53843.0,"Position":360.5784,"HyperDash":false}]},{"StartTime":54002.0,"Objects":[{"StartTime":54002.0,"Position":445.0,"HyperDash":false},{"StartTime":54062.0,"Position":446.261871,"HyperDash":false},{"StartTime":54159.0,"Position":421.7946,"HyperDash":false}]},{"StartTime":54318.0,"Objects":[{"StartTime":54318.0,"Position":346.0,"HyperDash":false}]},{"StartTime":54476.0,"Objects":[{"StartTime":54476.0,"Position":268.0,"HyperDash":false},{"StartTime":54536.0,"Position":223.1805,"HyperDash":false},{"StartTime":54633.0,"Position":196.522339,"HyperDash":false}]},{"StartTime":54791.0,"Objects":[{"StartTime":54791.0,"Position":79.0,"HyperDash":false},{"StartTime":54860.0,"Position":75.98414,"HyperDash":false},{"StartTime":54930.0,"Position":115.252335,"HyperDash":false},{"StartTime":55000.0,"Position":116.421585,"HyperDash":false},{"StartTime":55106.0,"Position":110.769684,"HyperDash":false}]},{"StartTime":55265.0,"Objects":[{"StartTime":55265.0,"Position":38.0,"HyperDash":false},{"StartTime":55325.0,"Position":23.5258217,"HyperDash":false},{"StartTime":55422.0,"Position":54.2080231,"HyperDash":false}]},{"StartTime":55581.0,"Objects":[{"StartTime":55581.0,"Position":189.0,"HyperDash":false}]},{"StartTime":55739.0,"Objects":[{"StartTime":55739.0,"Position":125.0,"HyperDash":false},{"StartTime":55799.0,"Position":137.109589,"HyperDash":false},{"StartTime":55896.0,"Position":141.0302,"HyperDash":false}]},{"StartTime":56054.0,"Objects":[{"StartTime":56054.0,"Position":279.0,"HyperDash":false},{"StartTime":56114.0,"Position":308.761017,"HyperDash":false},{"StartTime":56211.0,"Position":351.217346,"HyperDash":false}]},{"StartTime":56370.0,"Objects":[{"StartTime":56370.0,"Position":470.0,"HyperDash":false},{"StartTime":56430.0,"Position":449.3282,"HyperDash":false},{"StartTime":56527.0,"Position":397.632721,"HyperDash":false}]},{"StartTime":56686.0,"Objects":[{"StartTime":56686.0,"Position":438.0,"HyperDash":false},{"StartTime":56746.0,"Position":427.2124,"HyperDash":false},{"StartTime":56843.0,"Position":445.736,"HyperDash":false}]},{"StartTime":57002.0,"Objects":[{"StartTime":57002.0,"Position":287.0,"HyperDash":false},{"StartTime":57062.0,"Position":284.352478,"HyperDash":false},{"StartTime":57159.0,"Position":294.1198,"HyperDash":false}]},{"StartTime":57318.0,"Objects":[{"StartTime":57318.0,"Position":334.0,"HyperDash":false},{"StartTime":57396.0,"Position":298.0179,"HyperDash":false},{"StartTime":57475.0,"Position":334.0,"HyperDash":false},{"StartTime":57554.0,"Position":298.0179,"HyperDash":false}]},{"StartTime":57633.0,"Objects":[{"StartTime":57633.0,"Position":230.0,"HyperDash":false},{"StartTime":57693.0,"Position":208.152359,"HyperDash":false},{"StartTime":57790.0,"Position":164.896362,"HyperDash":false}]},{"StartTime":57949.0,"Objects":[{"StartTime":57949.0,"Position":42.0,"HyperDash":false},{"StartTime":58009.0,"Position":61.24403,"HyperDash":false},{"StartTime":58106.0,"Position":66.01679,"HyperDash":false}]},{"StartTime":58265.0,"Objects":[{"StartTime":58265.0,"Position":188.0,"HyperDash":false},{"StartTime":58325.0,"Position":163.755981,"HyperDash":false},{"StartTime":58422.0,"Position":163.983215,"HyperDash":false}]},{"StartTime":58581.0,"Objects":[{"StartTime":58581.0,"Position":230.0,"HyperDash":false},{"StartTime":58641.0,"Position":261.006683,"HyperDash":false},{"StartTime":58738.0,"Position":299.391846,"HyperDash":false}]},{"StartTime":58897.0,"Objects":[{"StartTime":58897.0,"Position":146.0,"HyperDash":false},{"StartTime":58957.0,"Position":115.275429,"HyperDash":false},{"StartTime":59054.0,"Position":76.5043,"HyperDash":false}]},{"StartTime":59212.0,"Objects":[{"StartTime":59212.0,"Position":293.0,"HyperDash":false},{"StartTime":59281.0,"Position":302.5204,"HyperDash":false},{"StartTime":59351.0,"Position":304.419159,"HyperDash":false},{"StartTime":59421.0,"Position":314.467,"HyperDash":false},{"StartTime":59527.0,"Position":318.606537,"HyperDash":false}]},{"StartTime":59686.0,"Objects":[{"StartTime":59686.0,"Position":224.0,"HyperDash":false}]},{"StartTime":59844.0,"Objects":[{"StartTime":59844.0,"Position":405.0,"HyperDash":false},{"StartTime":59904.0,"Position":420.876434,"HyperDash":false},{"StartTime":60001.0,"Position":412.2616,"HyperDash":false}]},{"StartTime":60160.0,"Objects":[{"StartTime":60160.0,"Position":500.0,"HyperDash":false},{"StartTime":60220.0,"Position":492.536743,"HyperDash":false},{"StartTime":60317.0,"Position":429.739,"HyperDash":false}]},{"StartTime":60476.0,"Objects":[{"StartTime":60476.0,"Position":303.0,"HyperDash":false},{"StartTime":60545.0,"Position":319.948517,"HyperDash":false},{"StartTime":60615.0,"Position":348.183044,"HyperDash":false},{"StartTime":60685.0,"Position":402.9306,"HyperDash":false},{"StartTime":60791.0,"Position":439.958466,"HyperDash":false}]},{"StartTime":60949.0,"Objects":[{"StartTime":60949.0,"Position":311.0,"HyperDash":false}]},{"StartTime":61107.0,"Objects":[{"StartTime":61107.0,"Position":143.0,"HyperDash":false},{"StartTime":61167.0,"Position":158.994171,"HyperDash":false},{"StartTime":61264.0,"Position":156.7843,"HyperDash":false}]},{"StartTime":61423.0,"Objects":[{"StartTime":61423.0,"Position":63.0,"HyperDash":false},{"StartTime":61483.0,"Position":43.900074,"HyperDash":false},{"StartTime":61580.0,"Position":76.12121,"HyperDash":false}]},{"StartTime":61739.0,"Objects":[{"StartTime":61739.0,"Position":160.0,"HyperDash":false},{"StartTime":61799.0,"Position":167.994171,"HyperDash":false},{"StartTime":61896.0,"Position":173.7843,"HyperDash":false}]},{"StartTime":62055.0,"Objects":[{"StartTime":62055.0,"Position":80.0,"HyperDash":false},{"StartTime":62115.0,"Position":65.90008,"HyperDash":false},{"StartTime":62212.0,"Position":93.12121,"HyperDash":false}]},{"StartTime":62370.0,"Objects":[{"StartTime":62370.0,"Position":184.0,"HyperDash":false},{"StartTime":62439.0,"Position":195.225571,"HyperDash":false},{"StartTime":62509.0,"Position":239.72702,"HyperDash":false},{"StartTime":62579.0,"Position":260.956116,"HyperDash":false},{"StartTime":62685.0,"Position":306.492645,"HyperDash":false}]},{"StartTime":62844.0,"Objects":[{"StartTime":62844.0,"Position":406.0,"HyperDash":false}]},{"StartTime":63002.0,"Objects":[{"StartTime":63002.0,"Position":473.0,"HyperDash":false},{"StartTime":63062.0,"Position":481.5252,"HyperDash":false},{"StartTime":63159.0,"Position":455.637146,"HyperDash":false}]},{"StartTime":63318.0,"Objects":[{"StartTime":63318.0,"Position":331.0,"HyperDash":false},{"StartTime":63378.0,"Position":349.711639,"HyperDash":false},{"StartTime":63475.0,"Position":347.2463,"HyperDash":false}]},{"StartTime":63633.0,"Objects":[{"StartTime":63633.0,"Position":234.0,"HyperDash":false}]},{"StartTime":63791.0,"Objects":[{"StartTime":63791.0,"Position":160.0,"HyperDash":false},{"StartTime":63851.0,"Position":187.355438,"HyperDash":false},{"StartTime":63948.0,"Position":231.69101,"HyperDash":false}]},{"StartTime":64107.0,"Objects":[{"StartTime":64107.0,"Position":147.0,"HyperDash":false},{"StartTime":64167.0,"Position":126.032143,"HyperDash":false},{"StartTime":64264.0,"Position":74.96641,"HyperDash":false}]},{"StartTime":64423.0,"Objects":[{"StartTime":64423.0,"Position":35.0,"HyperDash":false}]},{"StartTime":64581.0,"Objects":[{"StartTime":64581.0,"Position":148.0,"HyperDash":false},{"StartTime":64641.0,"Position":112.032143,"HyperDash":false},{"StartTime":64738.0,"Position":75.96641,"HyperDash":false}]},{"StartTime":64897.0,"Objects":[{"StartTime":64897.0,"Position":18.0,"HyperDash":false}]},{"StartTime":65054.0,"Objects":[{"StartTime":65054.0,"Position":133.0,"HyperDash":false},{"StartTime":65114.0,"Position":141.7638,"HyperDash":false},{"StartTime":65211.0,"Position":148.7033,"HyperDash":false}]},{"StartTime":65370.0,"Objects":[{"StartTime":65370.0,"Position":224.0,"HyperDash":false},{"StartTime":65439.0,"Position":210.371689,"HyperDash":false},{"StartTime":65509.0,"Position":211.28067,"HyperDash":false},{"StartTime":65579.0,"Position":215.692825,"HyperDash":false},{"StartTime":65685.0,"Position":246.723145,"HyperDash":false}]},{"StartTime":65844.0,"Objects":[{"StartTime":65844.0,"Position":367.0,"HyperDash":false},{"StartTime":65904.0,"Position":394.7037,"HyperDash":false},{"StartTime":66001.0,"Position":437.557922,"HyperDash":false}]},{"StartTime":66160.0,"Objects":[{"StartTime":66160.0,"Position":456.0,"HyperDash":false},{"StartTime":66220.0,"Position":443.412323,"HyperDash":false},{"StartTime":66317.0,"Position":430.542175,"HyperDash":false}]},{"StartTime":66476.0,"Objects":[{"StartTime":66476.0,"Position":310.0,"HyperDash":false},{"StartTime":66536.0,"Position":332.587646,"HyperDash":false},{"StartTime":66633.0,"Position":335.457825,"HyperDash":false}]},{"StartTime":66791.0,"Objects":[{"StartTime":66791.0,"Position":452.0,"HyperDash":false},{"StartTime":66851.0,"Position":421.412354,"HyperDash":false},{"StartTime":66948.0,"Position":426.542175,"HyperDash":false}]},{"StartTime":67107.0,"Objects":[{"StartTime":67107.0,"Position":250.0,"HyperDash":false},{"StartTime":67167.0,"Position":259.587646,"HyperDash":false},{"StartTime":67264.0,"Position":275.457825,"HyperDash":false}]},{"StartTime":67423.0,"Objects":[{"StartTime":67423.0,"Position":143.0,"HyperDash":false},{"StartTime":67483.0,"Position":107.965904,"HyperDash":false},{"StartTime":67580.0,"Position":67.02744,"HyperDash":false}]},{"StartTime":67739.0,"Objects":[{"StartTime":67739.0,"Position":8.0,"HyperDash":false},{"StartTime":67799.0,"Position":39.0340958,"HyperDash":false},{"StartTime":67896.0,"Position":83.97256,"HyperDash":false}]},{"StartTime":68054.0,"Objects":[{"StartTime":68054.0,"Position":153.0,"HyperDash":false},{"StartTime":68123.0,"Position":136.712723,"HyperDash":false},{"StartTime":68193.0,"Position":96.94302,"HyperDash":false},{"StartTime":68263.0,"Position":47.1733246,"HyperDash":false},{"StartTime":68369.0,"Position":1.03634644,"HyperDash":false}]},{"StartTime":68686.0,"Objects":[{"StartTime":68686.0,"Position":162.0,"HyperDash":false},{"StartTime":68764.0,"Position":140.279373,"HyperDash":false},{"StartTime":68843.0,"Position":149.194489,"HyperDash":false},{"StartTime":68904.0,"Position":160.855484,"HyperDash":false},{"StartTime":69001.0,"Position":162.0,"HyperDash":false}]},{"StartTime":69160.0,"Objects":[{"StartTime":69160.0,"Position":264.0,"HyperDash":false}]},{"StartTime":69318.0,"Objects":[{"StartTime":69318.0,"Position":264.0,"HyperDash":false},{"StartTime":69387.0,"Position":293.878754,"HyperDash":false},{"StartTime":69457.0,"Position":308.962463,"HyperDash":false},{"StartTime":69527.0,"Position":326.259735,"HyperDash":false},{"StartTime":69633.0,"Position":376.5735,"HyperDash":false}]},{"StartTime":69791.0,"Objects":[{"StartTime":69791.0,"Position":477.0,"HyperDash":false},{"StartTime":69851.0,"Position":473.9998,"HyperDash":false},{"StartTime":69948.0,"Position":451.330017,"HyperDash":false}]},{"StartTime":70107.0,"Objects":[{"StartTime":70107.0,"Position":352.0,"HyperDash":false}]},{"StartTime":70265.0,"Objects":[{"StartTime":70265.0,"Position":352.0,"HyperDash":false},{"StartTime":70325.0,"Position":355.88562,"HyperDash":false},{"StartTime":70422.0,"Position":377.063232,"HyperDash":false}]},{"StartTime":70581.0,"Objects":[{"StartTime":70581.0,"Position":252.0,"HyperDash":false},{"StartTime":70650.0,"Position":243.345444,"HyperDash":false},{"StartTime":70720.0,"Position":190.933167,"HyperDash":false},{"StartTime":70790.0,"Position":181.559875,"HyperDash":false},{"StartTime":70896.0,"Position":139.968262,"HyperDash":false}]},{"StartTime":71212.0,"Objects":[{"StartTime":71212.0,"Position":139.0,"HyperDash":false},{"StartTime":71272.0,"Position":142.551361,"HyperDash":false},{"StartTime":71369.0,"Position":117.237366,"HyperDash":false}]},{"StartTime":71528.0,"Objects":[{"StartTime":71528.0,"Position":197.0,"HyperDash":false}]},{"StartTime":71686.0,"Objects":[{"StartTime":71686.0,"Position":197.0,"HyperDash":false}]},{"StartTime":71844.0,"Objects":[{"StartTime":71844.0,"Position":246.0,"HyperDash":false},{"StartTime":71904.0,"Position":274.251373,"HyperDash":false},{"StartTime":72001.0,"Position":310.825043,"HyperDash":false}]},{"StartTime":72160.0,"Objects":[{"StartTime":72160.0,"Position":382.0,"HyperDash":false}]},{"StartTime":72318.0,"Objects":[{"StartTime":72318.0,"Position":382.0,"HyperDash":false},{"StartTime":72387.0,"Position":375.660431,"HyperDash":false},{"StartTime":72457.0,"Position":397.292725,"HyperDash":false},{"StartTime":72527.0,"Position":400.838165,"HyperDash":false},{"StartTime":72633.0,"Position":413.841949,"HyperDash":false}]},{"StartTime":72791.0,"Objects":[{"StartTime":72791.0,"Position":483.0,"HyperDash":false},{"StartTime":72851.0,"Position":443.387177,"HyperDash":false},{"StartTime":72948.0,"Position":422.070221,"HyperDash":false}]},{"StartTime":73107.0,"Objects":[{"StartTime":73107.0,"Position":316.0,"HyperDash":false}]},{"StartTime":73265.0,"Objects":[{"StartTime":73265.0,"Position":316.0,"HyperDash":false}]},{"StartTime":73423.0,"Objects":[{"StartTime":73423.0,"Position":213.0,"HyperDash":false},{"StartTime":73483.0,"Position":236.624237,"HyperDash":false},{"StartTime":73580.0,"Position":274.115784,"HyperDash":false}]},{"StartTime":73739.0,"Objects":[{"StartTime":73739.0,"Position":151.0,"HyperDash":false},{"StartTime":73808.0,"Position":168.107178,"HyperDash":false},{"StartTime":73878.0,"Position":188.948715,"HyperDash":false},{"StartTime":73948.0,"Position":173.253,"HyperDash":false},{"StartTime":74054.0,"Position":186.276779,"HyperDash":false}]},{"StartTime":74212.0,"Objects":[{"StartTime":74212.0,"Position":71.0,"HyperDash":false}]},{"StartTime":74370.0,"Objects":[{"StartTime":74370.0,"Position":71.0,"HyperDash":false},{"StartTime":74439.0,"Position":73.10717,"HyperDash":false},{"StartTime":74509.0,"Position":99.94872,"HyperDash":false},{"StartTime":74579.0,"Position":82.253,"HyperDash":false},{"StartTime":74685.0,"Position":106.276787,"HyperDash":false}]},{"StartTime":74844.0,"Objects":[{"StartTime":74844.0,"Position":217.0,"HyperDash":false},{"StartTime":74904.0,"Position":195.833557,"HyperDash":false},{"StartTime":75001.0,"Position":203.228043,"HyperDash":false}]},{"StartTime":75160.0,"Objects":[{"StartTime":75160.0,"Position":292.0,"HyperDash":false},{"StartTime":75220.0,"Position":322.137878,"HyperDash":false},{"StartTime":75317.0,"Position":355.583832,"HyperDash":false}]},{"StartTime":75476.0,"Objects":[{"StartTime":75476.0,"Position":470.0,"HyperDash":false}]},{"StartTime":75633.0,"Objects":[{"StartTime":75633.0,"Position":470.0,"HyperDash":false},{"StartTime":75702.0,"Position":451.070618,"HyperDash":false},{"StartTime":75772.0,"Position":423.6466,"HyperDash":false},{"StartTime":75842.0,"Position":385.354156,"HyperDash":false},{"StartTime":75948.0,"Position":339.91098,"HyperDash":false}]},{"StartTime":76265.0,"Objects":[{"StartTime":76265.0,"Position":339.0,"HyperDash":false},{"StartTime":76325.0,"Position":330.2449,"HyperDash":false},{"StartTime":76422.0,"Position":356.729736,"HyperDash":false}]},{"StartTime":76581.0,"Objects":[{"StartTime":76581.0,"Position":274.0,"HyperDash":false}]},{"StartTime":76739.0,"Objects":[{"StartTime":76739.0,"Position":274.0,"HyperDash":false}]},{"StartTime":76897.0,"Objects":[{"StartTime":76897.0,"Position":196.0,"HyperDash":false},{"StartTime":76957.0,"Position":202.336975,"HyperDash":false},{"StartTime":77054.0,"Position":177.609283,"HyperDash":false}]},{"StartTime":77212.0,"Objects":[{"StartTime":77212.0,"Position":76.0,"HyperDash":false},{"StartTime":77272.0,"Position":87.663,"HyperDash":false},{"StartTime":77369.0,"Position":94.3907,"HyperDash":false}]},{"StartTime":77528.0,"Objects":[{"StartTime":77528.0,"Position":193.0,"HyperDash":false},{"StartTime":77588.0,"Position":215.246429,"HyperDash":false},{"StartTime":77685.0,"Position":255.401413,"HyperDash":false}]},{"StartTime":77844.0,"Objects":[{"StartTime":77844.0,"Position":363.0,"HyperDash":false},{"StartTime":77904.0,"Position":335.063263,"HyperDash":false},{"StartTime":78001.0,"Position":300.441956,"HyperDash":false}]},{"StartTime":78160.0,"Objects":[{"StartTime":78160.0,"Position":424.0,"HyperDash":false},{"StartTime":78229.0,"Position":425.201782,"HyperDash":false},{"StartTime":78299.0,"Position":406.9763,"HyperDash":false},{"StartTime":78369.0,"Position":392.725616,"HyperDash":false},{"StartTime":78475.0,"Position":375.221161,"HyperDash":false}]},{"StartTime":78791.0,"Objects":[{"StartTime":78791.0,"Position":375.0,"HyperDash":false}]},{"StartTime":87633.0,"Objects":[{"StartTime":87633.0,"Position":59.0,"HyperDash":false},{"StartTime":87733.0,"Position":109.695786,"HyperDash":false},{"StartTime":87869.0,"Position":154.94931,"HyperDash":false}]},{"StartTime":87949.0,"Objects":[{"StartTime":87949.0,"Position":157.0,"HyperDash":false},{"StartTime":88049.0,"Position":98.90486,"HyperDash":false},{"StartTime":88185.0,"Position":61.01484,"HyperDash":false}]},{"StartTime":88265.0,"Objects":[{"StartTime":88265.0,"Position":65.0,"HyperDash":false},{"StartTime":88365.0,"Position":107.226257,"HyperDash":false},{"StartTime":88501.0,"Position":160.443985,"HyperDash":false}]},{"StartTime":88581.0,"Objects":[{"StartTime":88581.0,"Position":162.0,"HyperDash":false}]},{"StartTime":88897.0,"Objects":[{"StartTime":88897.0,"Position":410.0,"HyperDash":false},{"StartTime":88957.0,"Position":434.139282,"HyperDash":false},{"StartTime":89054.0,"Position":430.5437,"HyperDash":false}]},{"StartTime":89212.0,"Objects":[{"StartTime":89212.0,"Position":329.0,"HyperDash":false}]},{"StartTime":89370.0,"Objects":[{"StartTime":89370.0,"Position":237.0,"HyperDash":false},{"StartTime":89430.0,"Position":206.860718,"HyperDash":false},{"StartTime":89527.0,"Position":216.4563,"HyperDash":false}]},{"StartTime":89686.0,"Objects":[{"StartTime":89686.0,"Position":412.0,"HyperDash":false},{"StartTime":89746.0,"Position":427.040955,"HyperDash":false},{"StartTime":89843.0,"Position":390.8584,"HyperDash":false}]},{"StartTime":90002.0,"Objects":[{"StartTime":90002.0,"Position":224.0,"HyperDash":false},{"StartTime":90071.0,"Position":193.575424,"HyperDash":false},{"StartTime":90141.0,"Position":140.9065,"HyperDash":false},{"StartTime":90211.0,"Position":134.488129,"HyperDash":false},{"StartTime":90317.0,"Position":98.64927,"HyperDash":false}]},{"StartTime":90476.0,"Objects":[{"StartTime":90476.0,"Position":198.0,"HyperDash":false}]},{"StartTime":90633.0,"Objects":[{"StartTime":90633.0,"Position":197.0,"HyperDash":false}]},{"StartTime":90791.0,"Objects":[{"StartTime":90791.0,"Position":85.0,"HyperDash":false},{"StartTime":90851.0,"Position":83.48808,"HyperDash":false},{"StartTime":90948.0,"Position":98.0172348,"HyperDash":false}]},{"StartTime":91107.0,"Objects":[{"StartTime":91107.0,"Position":308.0,"HyperDash":false},{"StartTime":91167.0,"Position":319.751,"HyperDash":false},{"StartTime":91264.0,"Position":319.957062,"HyperDash":false}]},{"StartTime":91423.0,"Objects":[{"StartTime":91423.0,"Position":210.0,"HyperDash":false},{"StartTime":91483.0,"Position":236.879288,"HyperDash":false},{"StartTime":91580.0,"Position":290.540375,"HyperDash":false}]},{"StartTime":91739.0,"Objects":[{"StartTime":91739.0,"Position":196.0,"HyperDash":false}]},{"StartTime":91897.0,"Objects":[{"StartTime":91897.0,"Position":305.0,"HyperDash":false},{"StartTime":91957.0,"Position":317.8793,"HyperDash":false},{"StartTime":92054.0,"Position":385.540375,"HyperDash":false}]},{"StartTime":92212.0,"Objects":[{"StartTime":92212.0,"Position":212.0,"HyperDash":false},{"StartTime":92272.0,"Position":221.879288,"HyperDash":false},{"StartTime":92369.0,"Position":292.540375,"HyperDash":false}]},{"StartTime":92528.0,"Objects":[{"StartTime":92528.0,"Position":446.0,"HyperDash":false},{"StartTime":92597.0,"Position":444.5924,"HyperDash":false},{"StartTime":92667.0,"Position":489.460175,"HyperDash":false},{"StartTime":92737.0,"Position":462.152466,"HyperDash":false},{"StartTime":92843.0,"Position":484.6515,"HyperDash":false}]},{"StartTime":93002.0,"Objects":[{"StartTime":93002.0,"Position":286.0,"HyperDash":false}]},{"StartTime":93160.0,"Objects":[{"StartTime":93160.0,"Position":368.0,"HyperDash":false}]},{"StartTime":93318.0,"Objects":[{"StartTime":93318.0,"Position":268.0,"HyperDash":false},{"StartTime":93378.0,"Position":258.177734,"HyperDash":false},{"StartTime":93475.0,"Position":188.322281,"HyperDash":false}]},{"StartTime":93633.0,"Objects":[{"StartTime":93633.0,"Position":349.0,"HyperDash":false},{"StartTime":93693.0,"Position":301.103668,"HyperDash":false},{"StartTime":93790.0,"Position":269.135223,"HyperDash":false}]},{"StartTime":93949.0,"Objects":[{"StartTime":93949.0,"Position":138.0,"HyperDash":false},{"StartTime":94009.0,"Position":122.494843,"HyperDash":false},{"StartTime":94106.0,"Position":107.101913,"HyperDash":false}]},{"StartTime":94265.0,"Objects":[{"StartTime":94265.0,"Position":148.0,"HyperDash":false}]},{"StartTime":94423.0,"Objects":[{"StartTime":94423.0,"Position":22.0,"HyperDash":false},{"StartTime":94483.0,"Position":32.5051575,"HyperDash":false},{"StartTime":94580.0,"Position":52.89809,"HyperDash":false}]},{"StartTime":94739.0,"Objects":[{"StartTime":94739.0,"Position":243.0,"HyperDash":false},{"StartTime":94799.0,"Position":236.5184,"HyperDash":false},{"StartTime":94896.0,"Position":272.894073,"HyperDash":false}]},{"StartTime":95054.0,"Objects":[{"StartTime":95054.0,"Position":438.0,"HyperDash":false},{"StartTime":95123.0,"Position":388.7492,"HyperDash":false},{"StartTime":95193.0,"Position":392.3104,"HyperDash":false},{"StartTime":95263.0,"Position":331.956,"HyperDash":false},{"StartTime":95369.0,"Position":294.5527,"HyperDash":false}]},{"StartTime":95528.0,"Objects":[{"StartTime":95528.0,"Position":254.0,"HyperDash":false},{"StartTime":95588.0,"Position":290.0384,"HyperDash":false},{"StartTime":95685.0,"Position":283.842346,"HyperDash":false}]},{"StartTime":95844.0,"Objects":[{"StartTime":95844.0,"Position":427.0,"HyperDash":false},{"StartTime":95904.0,"Position":416.4857,"HyperDash":false},{"StartTime":96001.0,"Position":442.904083,"HyperDash":false}]},{"StartTime":96160.0,"Objects":[{"StartTime":96160.0,"Position":279.0,"HyperDash":false},{"StartTime":96220.0,"Position":299.0384,"HyperDash":false},{"StartTime":96317.0,"Position":308.842346,"HyperDash":false}]},{"StartTime":96476.0,"Objects":[{"StartTime":96476.0,"Position":225.0,"HyperDash":false},{"StartTime":96536.0,"Position":210.338287,"HyperDash":false},{"StartTime":96633.0,"Position":143.344467,"HyperDash":false}]},{"StartTime":96791.0,"Objects":[{"StartTime":96791.0,"Position":288.0,"HyperDash":false}]},{"StartTime":96949.0,"Objects":[{"StartTime":96949.0,"Position":180.0,"HyperDash":false},{"StartTime":97009.0,"Position":166.338287,"HyperDash":false},{"StartTime":97106.0,"Position":98.3444748,"HyperDash":false}]},{"StartTime":97265.0,"Objects":[{"StartTime":97265.0,"Position":274.0,"HyperDash":false},{"StartTime":97325.0,"Position":309.692352,"HyperDash":false},{"StartTime":97422.0,"Position":355.842438,"HyperDash":false}]},{"StartTime":97581.0,"Objects":[{"StartTime":97581.0,"Position":417.0,"HyperDash":false}]},{"StartTime":97739.0,"Objects":[{"StartTime":97739.0,"Position":420.0,"HyperDash":false},{"StartTime":97799.0,"Position":396.8472,"HyperDash":false},{"StartTime":97896.0,"Position":380.9233,"HyperDash":false}]},{"StartTime":98054.0,"Objects":[{"StartTime":98054.0,"Position":346.0,"HyperDash":false}]},{"StartTime":98212.0,"Objects":[{"StartTime":98212.0,"Position":299.0,"HyperDash":false}]},{"StartTime":98370.0,"Objects":[{"StartTime":98370.0,"Position":337.0,"HyperDash":false}]},{"StartTime":98528.0,"Objects":[{"StartTime":98528.0,"Position":290.0,"HyperDash":false}]},{"StartTime":98686.0,"Objects":[{"StartTime":98686.0,"Position":170.0,"HyperDash":false},{"StartTime":98746.0,"Position":129.894,"HyperDash":false},{"StartTime":98843.0,"Position":88.38194,"HyperDash":false}]},{"StartTime":99002.0,"Objects":[{"StartTime":99002.0,"Position":45.0,"HyperDash":false},{"StartTime":99062.0,"Position":73.99868,"HyperDash":false},{"StartTime":99159.0,"Position":87.32532,"HyperDash":false}]},{"StartTime":99318.0,"Objects":[{"StartTime":99318.0,"Position":164.0,"HyperDash":false}]},{"StartTime":99476.0,"Objects":[{"StartTime":99476.0,"Position":146.0,"HyperDash":false},{"StartTime":99536.0,"Position":113.96389,"HyperDash":false},{"StartTime":99633.0,"Position":66.87661,"HyperDash":false}]},{"StartTime":99791.0,"Objects":[{"StartTime":99791.0,"Position":163.0,"HyperDash":false},{"StartTime":99851.0,"Position":182.9796,"HyperDash":false},{"StartTime":99948.0,"Position":242.314056,"HyperDash":false}]},{"StartTime":100107.0,"Objects":[{"StartTime":100107.0,"Position":306.0,"HyperDash":false},{"StartTime":100176.0,"Position":272.841949,"HyperDash":false},{"StartTime":100246.0,"Position":282.58606,"HyperDash":false},{"StartTime":100316.0,"Position":262.382751,"HyperDash":false},{"StartTime":100422.0,"Position":258.4074,"HyperDash":false}]},{"StartTime":100581.0,"Objects":[{"StartTime":100581.0,"Position":446.0,"HyperDash":false}]},{"StartTime":100739.0,"Objects":[{"StartTime":100739.0,"Position":376.0,"HyperDash":false},{"StartTime":100799.0,"Position":361.111847,"HyperDash":false},{"StartTime":100896.0,"Position":305.5532,"HyperDash":false}]},{"StartTime":101054.0,"Objects":[{"StartTime":101054.0,"Position":236.0,"HyperDash":false}]},{"StartTime":101212.0,"Objects":[{"StartTime":101212.0,"Position":402.0,"HyperDash":false},{"StartTime":101272.0,"Position":446.655,"HyperDash":false},{"StartTime":101369.0,"Position":481.3611,"HyperDash":false}]},{"StartTime":101528.0,"Objects":[{"StartTime":101528.0,"Position":334.0,"HyperDash":false},{"StartTime":101588.0,"Position":334.394165,"HyperDash":false},{"StartTime":101685.0,"Position":350.023041,"HyperDash":false}]},{"StartTime":101844.0,"Objects":[{"StartTime":101844.0,"Position":219.0,"HyperDash":false}]},{"StartTime":102002.0,"Objects":[{"StartTime":102002.0,"Position":177.0,"HyperDash":false},{"StartTime":102062.0,"Position":159.9585,"HyperDash":false},{"StartTime":102159.0,"Position":98.64363,"HyperDash":false}]},{"StartTime":102318.0,"Objects":[{"StartTime":102318.0,"Position":140.0,"HyperDash":false},{"StartTime":102378.0,"Position":163.494385,"HyperDash":false},{"StartTime":102475.0,"Position":218.169327,"HyperDash":false}]},{"StartTime":102633.0,"Objects":[{"StartTime":102633.0,"Position":22.0,"HyperDash":false},{"StartTime":102702.0,"Position":31.6368866,"HyperDash":false},{"StartTime":102772.0,"Position":59.88773,"HyperDash":false},{"StartTime":102842.0,"Position":57.2475433,"HyperDash":false},{"StartTime":102948.0,"Position":67.89443,"HyperDash":false}]},{"StartTime":103107.0,"Objects":[{"StartTime":103107.0,"Position":182.0,"HyperDash":false}]},{"StartTime":103265.0,"Objects":[{"StartTime":103265.0,"Position":200.0,"HyperDash":false},{"StartTime":103325.0,"Position":221.459839,"HyperDash":false},{"StartTime":103422.0,"Position":217.979309,"HyperDash":false}]},{"StartTime":103581.0,"Objects":[{"StartTime":103581.0,"Position":337.0,"HyperDash":false}]},{"StartTime":103739.0,"Objects":[{"StartTime":103739.0,"Position":331.0,"HyperDash":false},{"StartTime":103799.0,"Position":312.540161,"HyperDash":false},{"StartTime":103896.0,"Position":313.0207,"HyperDash":false}]},{"StartTime":104054.0,"Objects":[{"StartTime":104054.0,"Position":194.0,"HyperDash":false},{"StartTime":104123.0,"Position":231.002213,"HyperDash":false},{"StartTime":104193.0,"Position":276.3082,"HyperDash":false},{"StartTime":104263.0,"Position":277.368225,"HyperDash":false},{"StartTime":104369.0,"Position":325.1272,"HyperDash":false}]},{"StartTime":104528.0,"Objects":[{"StartTime":104528.0,"Position":142.0,"HyperDash":false},{"StartTime":104588.0,"Position":118.666763,"HyperDash":false},{"StartTime":104685.0,"Position":61.4790764,"HyperDash":false}]},{"StartTime":104844.0,"Objects":[{"StartTime":104844.0,"Position":187.0,"HyperDash":false},{"StartTime":104904.0,"Position":140.796371,"HyperDash":false},{"StartTime":105001.0,"Position":106.642426,"HyperDash":false}]},{"StartTime":105160.0,"Objects":[{"StartTime":105160.0,"Position":210.0,"HyperDash":false},{"StartTime":105220.0,"Position":216.543152,"HyperDash":false},{"StartTime":105317.0,"Position":232.886017,"HyperDash":false}]},{"StartTime":105476.0,"Objects":[{"StartTime":105476.0,"Position":339.0,"HyperDash":false},{"StartTime":105536.0,"Position":350.726563,"HyperDash":false},{"StartTime":105633.0,"Position":361.889038,"HyperDash":false}]},{"StartTime":105791.0,"Objects":[{"StartTime":105791.0,"Position":309.0,"HyperDash":false}]},{"StartTime":105949.0,"Objects":[{"StartTime":105949.0,"Position":454.0,"HyperDash":false},{"StartTime":106009.0,"Position":420.0147,"HyperDash":false},{"StartTime":106106.0,"Position":430.975983,"HyperDash":false}]},{"StartTime":106265.0,"Objects":[{"StartTime":106265.0,"Position":246.0,"HyperDash":false},{"StartTime":106325.0,"Position":244.2446,"HyperDash":false},{"StartTime":106422.0,"Position":268.0487,"HyperDash":false}]},{"StartTime":106581.0,"Objects":[{"StartTime":106581.0,"Position":133.0,"HyperDash":false},{"StartTime":106641.0,"Position":103.963638,"HyperDash":false},{"StartTime":106738.0,"Position":49.17154,"HyperDash":false}]},{"StartTime":106897.0,"Objects":[{"StartTime":106897.0,"Position":260.0,"HyperDash":false},{"StartTime":106957.0,"Position":304.036346,"HyperDash":false},{"StartTime":107054.0,"Position":343.828461,"HyperDash":false}]},{"StartTime":107212.0,"Objects":[{"StartTime":107212.0,"Position":127.0,"HyperDash":false},{"StartTime":107272.0,"Position":104.963638,"HyperDash":false},{"StartTime":107369.0,"Position":43.17154,"HyperDash":false}]},{"StartTime":107528.0,"Objects":[{"StartTime":107528.0,"Position":254.0,"HyperDash":false},{"StartTime":107588.0,"Position":292.036346,"HyperDash":false},{"StartTime":107685.0,"Position":337.828461,"HyperDash":false}]},{"StartTime":107844.0,"Objects":[{"StartTime":107844.0,"Position":479.0,"HyperDash":false}]},{"StartTime":108002.0,"Objects":[{"StartTime":108002.0,"Position":411.0,"HyperDash":false}]},{"StartTime":108160.0,"Objects":[{"StartTime":108160.0,"Position":400.0,"HyperDash":false}]},{"StartTime":108318.0,"Objects":[{"StartTime":108318.0,"Position":488.0,"HyperDash":false}]},{"StartTime":108476.0,"Objects":[{"StartTime":108476.0,"Position":319.0,"HyperDash":false},{"StartTime":108536.0,"Position":311.9797,"HyperDash":false},{"StartTime":108633.0,"Position":313.713531,"HyperDash":false}]},{"StartTime":108791.0,"Objects":[{"StartTime":108791.0,"Position":298.0,"HyperDash":false}]},{"StartTime":108949.0,"Objects":[{"StartTime":108949.0,"Position":220.0,"HyperDash":false}]},{"StartTime":109107.0,"Objects":[{"StartTime":109107.0,"Position":163.0,"HyperDash":false}]},{"StartTime":110212.0,"Objects":[{"StartTime":110212.0,"Position":160.0,"HyperDash":false}]},{"StartTime":111002.0,"Objects":[{"StartTime":111002.0,"Position":160.0,"HyperDash":false},{"StartTime":111102.0,"Position":170.38269,"HyperDash":false},{"StartTime":111238.0,"Position":193.050369,"HyperDash":false}]},{"StartTime":111318.0,"Objects":[{"StartTime":111318.0,"Position":214.0,"HyperDash":false},{"StartTime":111378.0,"Position":214.7743,"HyperDash":false},{"StartTime":111475.0,"Position":186.350418,"HyperDash":false}]},{"StartTime":111554.0,"Objects":[{"StartTime":111554.0,"Position":202.0,"HyperDash":false}]},{"StartTime":111633.0,"Objects":[{"StartTime":111633.0,"Position":202.0,"HyperDash":false}]},{"StartTime":112739.0,"Objects":[{"StartTime":112739.0,"Position":197.0,"HyperDash":false}]},{"StartTime":113528.0,"Objects":[{"StartTime":113528.0,"Position":197.0,"HyperDash":false},{"StartTime":113628.0,"Position":234.305908,"HyperDash":false},{"StartTime":113764.0,"Position":282.864716,"HyperDash":false}]},{"StartTime":113844.0,"Objects":[{"StartTime":113844.0,"Position":293.0,"HyperDash":false},{"StartTime":113904.0,"Position":330.591919,"HyperDash":false},{"StartTime":114001.0,"Position":348.628937,"HyperDash":false}]},{"StartTime":114081.0,"Objects":[{"StartTime":114081.0,"Position":413.0,"HyperDash":false}]},{"StartTime":114160.0,"Objects":[{"StartTime":114160.0,"Position":413.0,"HyperDash":false},{"StartTime":114220.0,"Position":422.708557,"HyperDash":false},{"StartTime":114317.0,"Position":432.737671,"HyperDash":false}]},{"StartTime":114476.0,"Objects":[{"StartTime":114476.0,"Position":328.0,"HyperDash":false}]},{"StartTime":114633.0,"Objects":[{"StartTime":114633.0,"Position":388.0,"HyperDash":false},{"StartTime":114693.0,"Position":376.2914,"HyperDash":false},{"StartTime":114790.0,"Position":368.262329,"HyperDash":false}]},{"StartTime":114949.0,"Objects":[{"StartTime":114949.0,"Position":218.0,"HyperDash":false},{"StartTime":115009.0,"Position":239.708572,"HyperDash":false},{"StartTime":115106.0,"Position":237.737671,"HyperDash":false}]},{"StartTime":115265.0,"Objects":[{"StartTime":115265.0,"Position":114.0,"HyperDash":false},{"StartTime":115334.0,"Position":111.665535,"HyperDash":false},{"StartTime":115404.0,"Position":90.91377,"HyperDash":false},{"StartTime":115474.0,"Position":109.772278,"HyperDash":false},{"StartTime":115580.0,"Position":75.52312,"HyperDash":false}]},{"StartTime":115739.0,"Objects":[{"StartTime":115739.0,"Position":206.0,"HyperDash":false},{"StartTime":115799.0,"Position":204.020508,"HyperDash":false},{"StartTime":115896.0,"Position":138.2174,"HyperDash":false}]},{"StartTime":116054.0,"Objects":[{"StartTime":116054.0,"Position":247.0,"HyperDash":false},{"StartTime":116114.0,"Position":266.224854,"HyperDash":false},{"StartTime":116211.0,"Position":314.45578,"HyperDash":false}]},{"StartTime":116370.0,"Objects":[{"StartTime":116370.0,"Position":406.0,"HyperDash":false},{"StartTime":116430.0,"Position":420.447754,"HyperDash":false},{"StartTime":116527.0,"Position":416.6286,"HyperDash":false}]},{"StartTime":116686.0,"Objects":[{"StartTime":116686.0,"Position":477.0,"HyperDash":false},{"StartTime":116746.0,"Position":430.998718,"HyperDash":false},{"StartTime":116843.0,"Position":396.846619,"HyperDash":false}]},{"StartTime":117002.0,"Objects":[{"StartTime":117002.0,"Position":286.0,"HyperDash":false}]},{"StartTime":117160.0,"Objects":[{"StartTime":117160.0,"Position":210.0,"HyperDash":false},{"StartTime":117220.0,"Position":257.991272,"HyperDash":false},{"StartTime":117317.0,"Position":289.721649,"HyperDash":false}]},{"StartTime":117476.0,"Objects":[{"StartTime":117476.0,"Position":205.0,"HyperDash":false},{"StartTime":117536.0,"Position":227.503632,"HyperDash":false},{"StartTime":117633.0,"Position":225.386322,"HyperDash":false}]},{"StartTime":117791.0,"Objects":[{"StartTime":117791.0,"Position":80.0,"HyperDash":false},{"StartTime":117860.0,"Position":86.16029,"HyperDash":false},{"StartTime":117930.0,"Position":123.197845,"HyperDash":false},{"StartTime":118000.0,"Position":137.463959,"HyperDash":false},{"StartTime":118106.0,"Position":126.215904,"HyperDash":false}]},{"StartTime":118265.0,"Objects":[{"StartTime":118265.0,"Position":279.0,"HyperDash":false}]},{"StartTime":118423.0,"Objects":[{"StartTime":118423.0,"Position":243.0,"HyperDash":false}]},{"StartTime":118581.0,"Objects":[{"StartTime":118581.0,"Position":306.0,"HyperDash":false}]},{"StartTime":118739.0,"Objects":[{"StartTime":118739.0,"Position":325.0,"HyperDash":false}]},{"StartTime":118897.0,"Objects":[{"StartTime":118897.0,"Position":330.0,"HyperDash":false}]},{"StartTime":119054.0,"Objects":[{"StartTime":119054.0,"Position":402.0,"HyperDash":false}]},{"StartTime":119528.0,"Objects":[{"StartTime":119528.0,"Position":402.0,"HyperDash":false},{"StartTime":119606.0,"Position":383.853424,"HyperDash":false},{"StartTime":119685.0,"Position":340.13028,"HyperDash":false},{"StartTime":119764.0,"Position":306.988525,"HyperDash":false},{"StartTime":119843.0,"Position":292.3493,"HyperDash":false},{"StartTime":119893.0,"Position":278.4925,"HyperDash":false},{"StartTime":119943.0,"Position":251.240692,"HyperDash":false},{"StartTime":119993.0,"Position":219.893433,"HyperDash":false},{"StartTime":120080.0,"Position":193.130447,"HyperDash":false}]},{"StartTime":120160.0,"Objects":[{"StartTime":120160.0,"Position":184.0,"HyperDash":false},{"StartTime":120238.0,"Position":212.882935,"HyperDash":false},{"StartTime":120317.0,"Position":263.114868,"HyperDash":false},{"StartTime":120396.0,"Position":296.998444,"HyperDash":false},{"StartTime":120475.0,"Position":297.759338,"HyperDash":false},{"StartTime":120525.0,"Position":324.634857,"HyperDash":false},{"StartTime":120575.0,"Position":317.226563,"HyperDash":false},{"StartTime":120625.0,"Position":338.548218,"HyperDash":false},{"StartTime":120712.0,"Position":391.822418,"HyperDash":false}]},{"StartTime":120791.0,"Objects":[{"StartTime":120791.0,"Position":385.0,"HyperDash":false},{"StartTime":120869.0,"Position":362.920959,"HyperDash":false},{"StartTime":120948.0,"Position":322.3987,"HyperDash":false},{"StartTime":121027.0,"Position":293.5699,"HyperDash":false},{"StartTime":121106.0,"Position":276.9177,"HyperDash":false},{"StartTime":121156.0,"Position":259.442749,"HyperDash":false},{"StartTime":121206.0,"Position":226.077545,"HyperDash":false},{"StartTime":121256.0,"Position":199.502151,"HyperDash":false},{"StartTime":121343.0,"Position":177.427231,"HyperDash":false}]},{"StartTime":121423.0,"Objects":[{"StartTime":121423.0,"Position":171.0,"HyperDash":false},{"StartTime":121501.0,"Position":194.8829,"HyperDash":false},{"StartTime":121580.0,"Position":232.113251,"HyperDash":false},{"StartTime":121659.0,"Position":263.966827,"HyperDash":false},{"StartTime":121738.0,"Position":284.605957,"HyperDash":false},{"StartTime":121788.0,"Position":288.396973,"HyperDash":false},{"StartTime":121838.0,"Position":333.9621,"HyperDash":false},{"StartTime":121888.0,"Position":331.2913,"HyperDash":false},{"StartTime":121975.0,"Position":378.575928,"HyperDash":false}]},{"StartTime":122054.0,"Objects":[{"StartTime":122054.0,"Position":373.0,"HyperDash":false},{"StartTime":122132.0,"Position":332.9511,"HyperDash":false},{"StartTime":122211.0,"Position":316.390533,"HyperDash":false},{"StartTime":122290.0,"Position":285.1506,"HyperDash":false},{"StartTime":122369.0,"Position":264.59256,"HyperDash":false},{"StartTime":122419.0,"Position":264.4234,"HyperDash":false},{"StartTime":122469.0,"Position":223.247314,"HyperDash":false},{"StartTime":122519.0,"Position":204.743011,"HyperDash":false},{"StartTime":122606.0,"Position":165.707657,"HyperDash":false}]},{"StartTime":122686.0,"Objects":[{"StartTime":122686.0,"Position":156.0,"HyperDash":false},{"StartTime":122786.0,"Position":141.985443,"HyperDash":false},{"StartTime":122922.0,"Position":110.21537,"HyperDash":false}]},{"StartTime":123002.0,"Objects":[{"StartTime":123002.0,"Position":129.0,"HyperDash":false},{"StartTime":123062.0,"Position":124.04425,"HyperDash":false},{"StartTime":123159.0,"Position":151.706512,"HyperDash":false}]},{"StartTime":123318.0,"Objects":[{"StartTime":123318.0,"Position":247.0,"HyperDash":false}]},{"StartTime":123475.0,"Objects":[{"StartTime":123475.0,"Position":278.0,"HyperDash":false}]},{"StartTime":123633.0,"Objects":[{"StartTime":123633.0,"Position":339.0,"HyperDash":false}]},{"StartTime":123791.0,"Objects":[{"StartTime":123791.0,"Position":272.0,"HyperDash":false}]},{"StartTime":123949.0,"Objects":[{"StartTime":123949.0,"Position":224.0,"HyperDash":false}]},{"StartTime":124107.0,"Objects":[{"StartTime":124107.0,"Position":286.0,"HyperDash":false}]},{"StartTime":124265.0,"Objects":[{"StartTime":124265.0,"Position":374.0,"HyperDash":false},{"StartTime":124325.0,"Position":390.564056,"HyperDash":false},{"StartTime":124422.0,"Position":454.897156,"HyperDash":false}]},{"StartTime":124581.0,"Objects":[{"StartTime":124581.0,"Position":368.0,"HyperDash":false}]},{"StartTime":124739.0,"Objects":[{"StartTime":124739.0,"Position":222.0,"HyperDash":false},{"StartTime":124799.0,"Position":189.435959,"HyperDash":false},{"StartTime":124896.0,"Position":141.102829,"HyperDash":false}]},{"StartTime":125054.0,"Objects":[{"StartTime":125054.0,"Position":62.0,"HyperDash":false},{"StartTime":125114.0,"Position":76.30468,"HyperDash":false},{"StartTime":125211.0,"Position":87.89828,"HyperDash":false}]},{"StartTime":125370.0,"Objects":[{"StartTime":125370.0,"Position":261.0,"HyperDash":false},{"StartTime":125430.0,"Position":244.695313,"HyperDash":false},{"StartTime":125527.0,"Position":235.101715,"HyperDash":false}]},{"StartTime":125686.0,"Objects":[{"StartTime":125686.0,"Position":86.0,"HyperDash":false},{"StartTime":125746.0,"Position":49.1613235,"HyperDash":false},{"StartTime":125843.0,"Position":5.8006506,"HyperDash":false}]},{"StartTime":126002.0,"Objects":[{"StartTime":126002.0,"Position":164.0,"HyperDash":false}]},{"StartTime":126160.0,"Objects":[{"StartTime":126160.0,"Position":235.0,"HyperDash":false},{"StartTime":126220.0,"Position":269.911163,"HyperDash":false},{"StartTime":126317.0,"Position":315.594666,"HyperDash":false}]},{"StartTime":126476.0,"Objects":[{"StartTime":126476.0,"Position":454.0,"HyperDash":false},{"StartTime":126536.0,"Position":435.099762,"HyperDash":false},{"StartTime":126633.0,"Position":373.83255,"HyperDash":false}]},{"StartTime":126791.0,"Objects":[{"StartTime":126791.0,"Position":407.0,"HyperDash":false},{"StartTime":126851.0,"Position":407.6067,"HyperDash":false},{"StartTime":126948.0,"Position":400.7375,"HyperDash":false}]},{"StartTime":127107.0,"Objects":[{"StartTime":127107.0,"Position":274.0,"HyperDash":false},{"StartTime":127167.0,"Position":260.302338,"HyperDash":false},{"StartTime":127264.0,"Position":266.941132,"HyperDash":false}]},{"StartTime":127423.0,"Objects":[{"StartTime":127423.0,"Position":421.0,"HyperDash":false},{"StartTime":127483.0,"Position":428.6067,"HyperDash":false},{"StartTime":127580.0,"Position":414.7375,"HyperDash":false}]},{"StartTime":127739.0,"Objects":[{"StartTime":127739.0,"Position":288.0,"HyperDash":false},{"StartTime":127799.0,"Position":302.302338,"HyperDash":false},{"StartTime":127896.0,"Position":280.941132,"HyperDash":false}]},{"StartTime":128054.0,"Objects":[{"StartTime":128054.0,"Position":247.0,"HyperDash":false}]},{"StartTime":128212.0,"Objects":[{"StartTime":128212.0,"Position":212.0,"HyperDash":false}]},{"StartTime":128370.0,"Objects":[{"StartTime":128370.0,"Position":251.0,"HyperDash":false}]},{"StartTime":128528.0,"Objects":[{"StartTime":128528.0,"Position":216.0,"HyperDash":false}]},{"StartTime":128686.0,"Objects":[{"StartTime":128686.0,"Position":81.0,"HyperDash":false},{"StartTime":128746.0,"Position":91.28703,"HyperDash":false},{"StartTime":128843.0,"Position":86.9844,"HyperDash":false}]},{"StartTime":129002.0,"Objects":[{"StartTime":129002.0,"Position":100.0,"HyperDash":false}]},{"StartTime":129160.0,"Objects":[{"StartTime":129160.0,"Position":163.0,"HyperDash":false}]},{"StartTime":129318.0,"Objects":[{"StartTime":129318.0,"Position":91.0,"HyperDash":false}]},{"StartTime":134370.0,"Objects":[{"StartTime":134370.0,"Position":300.0,"HyperDash":false}]},{"StartTime":135633.0,"Objects":[{"StartTime":135633.0,"Position":300.0,"HyperDash":false}]},{"StartTime":136897.0,"Objects":[{"StartTime":136897.0,"Position":300.0,"HyperDash":false},{"StartTime":136997.0,"Position":279.788757,"HyperDash":false},{"StartTime":137133.0,"Position":203.92157,"HyperDash":false}]},{"StartTime":137212.0,"Objects":[{"StartTime":137212.0,"Position":200.0,"HyperDash":false},{"StartTime":137312.0,"Position":227.884033,"HyperDash":false},{"StartTime":137448.0,"Position":295.992767,"HyperDash":false}]},{"StartTime":137528.0,"Objects":[{"StartTime":137528.0,"Position":293.0,"HyperDash":false},{"StartTime":137628.0,"Position":249.348679,"HyperDash":false},{"StartTime":137764.0,"Position":196.522751,"HyperDash":false}]},{"StartTime":137844.0,"Objects":[{"StartTime":137844.0,"Position":193.0,"HyperDash":false}]},{"StartTime":138160.0,"Objects":[{"StartTime":138160.0,"Position":337.0,"HyperDash":false},{"StartTime":138220.0,"Position":361.473083,"HyperDash":false},{"StartTime":138317.0,"Position":359.068726,"HyperDash":false}]},{"StartTime":138476.0,"Objects":[{"StartTime":138476.0,"Position":277.0,"HyperDash":false}]},{"StartTime":138633.0,"Objects":[{"StartTime":138633.0,"Position":355.0,"HyperDash":false},{"StartTime":138702.0,"Position":356.575073,"HyperDash":false},{"StartTime":138772.0,"Position":392.665771,"HyperDash":false},{"StartTime":138842.0,"Position":386.999573,"HyperDash":false},{"StartTime":138948.0,"Position":381.707275,"HyperDash":false}]},{"StartTime":139107.0,"Objects":[{"StartTime":139107.0,"Position":276.0,"HyperDash":false}]},{"StartTime":139265.0,"Objects":[{"StartTime":139265.0,"Position":276.0,"HyperDash":false}]},{"StartTime":139423.0,"Objects":[{"StartTime":139423.0,"Position":209.0,"HyperDash":false},{"StartTime":139483.0,"Position":198.122162,"HyperDash":false},{"StartTime":139580.0,"Position":145.227173,"HyperDash":false}]},{"StartTime":139739.0,"Objects":[{"StartTime":139739.0,"Position":68.0,"HyperDash":false}]},{"StartTime":139896.0,"Objects":[{"StartTime":139896.0,"Position":213.0,"HyperDash":false},{"StartTime":139965.0,"Position":198.023071,"HyperDash":false},{"StartTime":140035.0,"Position":135.780731,"HyperDash":false},{"StartTime":140105.0,"Position":105.911324,"HyperDash":false},{"StartTime":140211.0,"Position":80.0672455,"HyperDash":false}]},{"StartTime":140370.0,"Objects":[{"StartTime":140370.0,"Position":207.0,"HyperDash":false}]},{"StartTime":140528.0,"Objects":[{"StartTime":140528.0,"Position":207.0,"HyperDash":false}]},{"StartTime":140686.0,"Objects":[{"StartTime":140686.0,"Position":308.0,"HyperDash":false},{"StartTime":140746.0,"Position":291.8725,"HyperDash":false},{"StartTime":140843.0,"Position":295.128967,"HyperDash":false}]},{"StartTime":141002.0,"Objects":[{"StartTime":141002.0,"Position":421.0,"HyperDash":false}]},{"StartTime":141160.0,"Objects":[{"StartTime":141160.0,"Position":293.0,"HyperDash":false},{"StartTime":141229.0,"Position":272.132019,"HyperDash":false},{"StartTime":141299.0,"Position":276.853546,"HyperDash":false},{"StartTime":141369.0,"Position":287.3533,"HyperDash":false},{"StartTime":141475.0,"Position":261.940155,"HyperDash":false}]},{"StartTime":141633.0,"Objects":[{"StartTime":141633.0,"Position":392.0,"HyperDash":false}]},{"StartTime":141791.0,"Objects":[{"StartTime":141791.0,"Position":392.0,"HyperDash":false}]},{"StartTime":142265.0,"Objects":[{"StartTime":142265.0,"Position":392.0,"HyperDash":false},{"StartTime":142365.0,"Position":391.062164,"HyperDash":false},{"StartTime":142501.0,"Position":338.346161,"HyperDash":false}]},{"StartTime":142581.0,"Objects":[{"StartTime":142581.0,"Position":326.0,"HyperDash":false},{"StartTime":142650.0,"Position":311.6683,"HyperDash":false},{"StartTime":142720.0,"Position":268.562744,"HyperDash":false},{"StartTime":142790.0,"Position":260.483276,"HyperDash":false},{"StartTime":142896.0,"Position":203.358475,"HyperDash":false}]},{"StartTime":143212.0,"Objects":[{"StartTime":143212.0,"Position":203.0,"HyperDash":false}]},{"StartTime":144476.0,"Objects":[{"StartTime":144476.0,"Position":214.0,"HyperDash":false}]},{"StartTime":145739.0,"Objects":[{"StartTime":145739.0,"Position":214.0,"HyperDash":false},{"StartTime":145839.0,"Position":245.348236,"HyperDash":false},{"StartTime":145975.0,"Position":258.064423,"HyperDash":false}]},{"StartTime":146054.0,"Objects":[{"StartTime":146054.0,"Position":248.0,"HyperDash":false},{"StartTime":146154.0,"Position":238.651749,"HyperDash":false},{"StartTime":146290.0,"Position":203.935547,"HyperDash":false}]},{"StartTime":146370.0,"Objects":[{"StartTime":146370.0,"Position":218.0,"HyperDash":false},{"StartTime":146470.0,"Position":271.72702,"HyperDash":false},{"StartTime":146606.0,"Position":316.220551,"HyperDash":false}]},{"StartTime":146686.0,"Objects":[{"StartTime":146686.0,"Position":326.0,"HyperDash":false}]},{"StartTime":147002.0,"Objects":[{"StartTime":147002.0,"Position":440.0,"HyperDash":false},{"StartTime":147062.0,"Position":454.914642,"HyperDash":false},{"StartTime":147159.0,"Position":431.926636,"HyperDash":false}]},{"StartTime":147318.0,"Objects":[{"StartTime":147318.0,"Position":346.0,"HyperDash":false}]},{"StartTime":147476.0,"Objects":[{"StartTime":147476.0,"Position":457.0,"HyperDash":false},{"StartTime":147545.0,"Position":452.315582,"HyperDash":false},{"StartTime":147615.0,"Position":433.5778,"HyperDash":false},{"StartTime":147685.0,"Position":450.839966,"HyperDash":false},{"StartTime":147791.0,"Position":440.179871,"HyperDash":false}]},{"StartTime":147949.0,"Objects":[{"StartTime":147949.0,"Position":326.0,"HyperDash":false}]},{"StartTime":148107.0,"Objects":[{"StartTime":148107.0,"Position":326.0,"HyperDash":false}]},{"StartTime":148265.0,"Objects":[{"StartTime":148265.0,"Position":170.0,"HyperDash":false},{"StartTime":148325.0,"Position":169.085358,"HyperDash":false},{"StartTime":148422.0,"Position":178.073349,"HyperDash":false}]},{"StartTime":148581.0,"Objects":[{"StartTime":148581.0,"Position":264.0,"HyperDash":false}]},{"StartTime":148739.0,"Objects":[{"StartTime":148739.0,"Position":153.0,"HyperDash":false},{"StartTime":148808.0,"Position":154.6844,"HyperDash":false},{"StartTime":148878.0,"Position":166.422211,"HyperDash":false},{"StartTime":148948.0,"Position":158.160019,"HyperDash":false},{"StartTime":149054.0,"Position":169.820129,"HyperDash":false}]},{"StartTime":149212.0,"Objects":[{"StartTime":149212.0,"Position":284.0,"HyperDash":false}]},{"StartTime":149370.0,"Objects":[{"StartTime":149370.0,"Position":284.0,"HyperDash":false}]},{"StartTime":149528.0,"Objects":[{"StartTime":149528.0,"Position":403.0,"HyperDash":false},{"StartTime":149588.0,"Position":399.914642,"HyperDash":false},{"StartTime":149685.0,"Position":394.926636,"HyperDash":false}]},{"StartTime":149844.0,"Objects":[{"StartTime":149844.0,"Position":309.0,"HyperDash":false}]},{"StartTime":150002.0,"Objects":[{"StartTime":150002.0,"Position":420.0,"HyperDash":false},{"StartTime":150071.0,"Position":421.315582,"HyperDash":false},{"StartTime":150141.0,"Position":430.5778,"HyperDash":false},{"StartTime":150211.0,"Position":409.839966,"HyperDash":false},{"StartTime":150317.0,"Position":403.179871,"HyperDash":false}]},{"StartTime":150475.0,"Objects":[{"StartTime":150475.0,"Position":289.0,"HyperDash":false}]},{"StartTime":150633.0,"Objects":[{"StartTime":150633.0,"Position":289.0,"HyperDash":false}]},{"StartTime":151107.0,"Objects":[{"StartTime":151107.0,"Position":97.0,"HyperDash":false},{"StartTime":151207.0,"Position":135.296875,"HyperDash":false},{"StartTime":151343.0,"Position":191.738083,"HyperDash":false}]},{"StartTime":151423.0,"Objects":[{"StartTime":151423.0,"Position":198.0,"HyperDash":false},{"StartTime":151492.0,"Position":183.569153,"HyperDash":false},{"StartTime":151562.0,"Position":141.428131,"HyperDash":false},{"StartTime":151632.0,"Position":136.803146,"HyperDash":false},{"StartTime":151738.0,"Position":137.3734,"HyperDash":false}]},{"StartTime":152054.0,"Objects":[{"StartTime":152054.0,"Position":297.0,"HyperDash":false},{"StartTime":152123.0,"Position":331.7846,"HyperDash":false},{"StartTime":152193.0,"Position":338.116882,"HyperDash":false},{"StartTime":152263.0,"Position":352.270721,"HyperDash":false},{"StartTime":152369.0,"Position":408.0906,"HyperDash":false}]},{"StartTime":152528.0,"Objects":[{"StartTime":152528.0,"Position":281.0,"HyperDash":false}]},{"StartTime":152686.0,"Objects":[{"StartTime":152686.0,"Position":446.0,"HyperDash":false},{"StartTime":152755.0,"Position":492.2877,"HyperDash":false},{"StartTime":152825.0,"Position":490.710327,"HyperDash":false},{"StartTime":152895.0,"Position":503.465729,"HyperDash":false},{"StartTime":153001.0,"Position":492.445526,"HyperDash":false}]},{"StartTime":153160.0,"Objects":[{"StartTime":153160.0,"Position":343.0,"HyperDash":false}]},{"StartTime":153318.0,"Objects":[{"StartTime":153318.0,"Position":297.0,"HyperDash":false},{"StartTime":153387.0,"Position":262.8003,"HyperDash":false},{"StartTime":153457.0,"Position":234.212128,"HyperDash":false},{"StartTime":153527.0,"Position":214.138336,"HyperDash":false},{"StartTime":153633.0,"Position":166.492523,"HyperDash":false}]},{"StartTime":153791.0,"Objects":[{"StartTime":153791.0,"Position":116.0,"HyperDash":false},{"StartTime":153860.0,"Position":144.280365,"HyperDash":false},{"StartTime":153930.0,"Position":132.43898,"HyperDash":false},{"StartTime":154000.0,"Position":140.923447,"HyperDash":false},{"StartTime":154106.0,"Position":158.507156,"HyperDash":false}]},{"StartTime":154265.0,"Objects":[{"StartTime":154265.0,"Position":264.0,"HyperDash":false},{"StartTime":154325.0,"Position":233.864212,"HyperDash":false},{"StartTime":154422.0,"Position":235.824112,"HyperDash":false}]},{"StartTime":154581.0,"Objects":[{"StartTime":154581.0,"Position":152.0,"HyperDash":false},{"StartTime":154650.0,"Position":125.809914,"HyperDash":false},{"StartTime":154720.0,"Position":104.5544,"HyperDash":false},{"StartTime":154790.0,"Position":63.7936554,"HyperDash":false},{"StartTime":154896.0,"Position":32.2917,"HyperDash":false}]},{"StartTime":155054.0,"Objects":[{"StartTime":155054.0,"Position":191.0,"HyperDash":false}]},{"StartTime":155212.0,"Objects":[{"StartTime":155212.0,"Position":264.0,"HyperDash":false},{"StartTime":155281.0,"Position":311.2232,"HyperDash":false},{"StartTime":155351.0,"Position":339.435272,"HyperDash":false},{"StartTime":155421.0,"Position":368.023529,"HyperDash":false},{"StartTime":155527.0,"Position":382.984161,"HyperDash":false}]},{"StartTime":155686.0,"Objects":[{"StartTime":155686.0,"Position":212.0,"HyperDash":false}]},{"StartTime":155844.0,"Objects":[{"StartTime":155844.0,"Position":405.0,"HyperDash":false},{"StartTime":155913.0,"Position":398.1627,"HyperDash":false},{"StartTime":155983.0,"Position":377.19342,"HyperDash":false},{"StartTime":156053.0,"Position":363.4817,"HyperDash":false},{"StartTime":156159.0,"Position":358.190857,"HyperDash":false}]},{"StartTime":156318.0,"Objects":[{"StartTime":156318.0,"Position":158.0,"HyperDash":false},{"StartTime":156387.0,"Position":166.012711,"HyperDash":false},{"StartTime":156457.0,"Position":151.858978,"HyperDash":false},{"StartTime":156527.0,"Position":139.665756,"HyperDash":false},{"StartTime":156633.0,"Position":111.16011,"HyperDash":false}]},{"StartTime":156791.0,"Objects":[{"StartTime":156791.0,"Position":9.0,"HyperDash":false},{"StartTime":156851.0,"Position":37.5505524,"HyperDash":false},{"StartTime":156948.0,"Position":77.09072,"HyperDash":false}]},{"StartTime":157107.0,"Objects":[{"StartTime":157107.0,"Position":270.0,"HyperDash":false},{"StartTime":157176.0,"Position":221.1834,"HyperDash":false},{"StartTime":157246.0,"Position":202.467163,"HyperDash":false},{"StartTime":157316.0,"Position":188.839691,"HyperDash":false},{"StartTime":157422.0,"Position":171.541748,"HyperDash":false}]},{"StartTime":157581.0,"Objects":[{"StartTime":157581.0,"Position":288.0,"HyperDash":false},{"StartTime":157650.0,"Position":334.9065,"HyperDash":false},{"StartTime":157720.0,"Position":351.398132,"HyperDash":false},{"StartTime":157790.0,"Position":383.620148,"HyperDash":false},{"StartTime":157896.0,"Position":385.24826,"HyperDash":false}]},{"StartTime":158054.0,"Objects":[{"StartTime":158054.0,"Position":248.0,"HyperDash":false},{"StartTime":158114.0,"Position":269.238434,"HyperDash":false},{"StartTime":158211.0,"Position":320.739136,"HyperDash":false}]},{"StartTime":158370.0,"Objects":[{"StartTime":158370.0,"Position":490.0,"HyperDash":false},{"StartTime":158439.0,"Position":483.703064,"HyperDash":false},{"StartTime":158509.0,"Position":456.281219,"HyperDash":false},{"StartTime":158579.0,"Position":428.409576,"HyperDash":false},{"StartTime":158685.0,"Position":432.63913,"HyperDash":false}]},{"StartTime":158844.0,"Objects":[{"StartTime":158844.0,"Position":467.0,"HyperDash":false},{"StartTime":158913.0,"Position":441.987579,"HyperDash":false},{"StartTime":158983.0,"Position":453.374176,"HyperDash":false},{"StartTime":159053.0,"Position":445.3904,"HyperDash":false},{"StartTime":159159.0,"Position":409.514771,"HyperDash":false}]},{"StartTime":159318.0,"Objects":[{"StartTime":159318.0,"Position":248.0,"HyperDash":false},{"StartTime":159378.0,"Position":264.964264,"HyperDash":false},{"StartTime":159475.0,"Position":321.196442,"HyperDash":false}]},{"StartTime":159633.0,"Objects":[{"StartTime":159633.0,"Position":320.0,"HyperDash":false}]},{"StartTime":160897.0,"Objects":[{"StartTime":160897.0,"Position":118.0,"HyperDash":false},{"StartTime":160997.0,"Position":109.104843,"HyperDash":false},{"StartTime":161133.0,"Position":125.327431,"HyperDash":false}]},{"StartTime":161212.0,"Objects":[{"StartTime":161212.0,"Position":146.0,"HyperDash":false},{"StartTime":161312.0,"Position":129.61203,"HyperDash":false},{"StartTime":161448.0,"Position":138.0044,"HyperDash":false}]},{"StartTime":161528.0,"Objects":[{"StartTime":161528.0,"Position":158.0,"HyperDash":false},{"StartTime":161628.0,"Position":162.38797,"HyperDash":false},{"StartTime":161764.0,"Position":165.9956,"HyperDash":false}]},{"StartTime":161844.0,"Objects":[{"StartTime":161844.0,"Position":185.0,"HyperDash":false}]},{"StartTime":162160.0,"Objects":[{"StartTime":162160.0,"Position":39.0,"HyperDash":false},{"StartTime":162220.0,"Position":18.8607216,"HyperDash":false},{"StartTime":162317.0,"Position":18.4563026,"HyperDash":false}]},{"StartTime":162475.0,"Objects":[{"StartTime":162475.0,"Position":153.0,"HyperDash":false}]},{"StartTime":162633.0,"Objects":[{"StartTime":162633.0,"Position":221.0,"HyperDash":false},{"StartTime":162693.0,"Position":242.139282,"HyperDash":false},{"StartTime":162790.0,"Position":241.543686,"HyperDash":false}]},{"StartTime":162949.0,"Objects":[{"StartTime":162949.0,"Position":64.0,"HyperDash":false},{"StartTime":163009.0,"Position":77.95903,"HyperDash":false},{"StartTime":163106.0,"Position":85.14159,"HyperDash":false}]},{"StartTime":163265.0,"Objects":[{"StartTime":163265.0,"Position":244.0,"HyperDash":false},{"StartTime":163334.0,"Position":288.4246,"HyperDash":false},{"StartTime":163404.0,"Position":325.0935,"HyperDash":false},{"StartTime":163474.0,"Position":362.511841,"HyperDash":false},{"StartTime":163580.0,"Position":369.3507,"HyperDash":false}]},{"StartTime":163739.0,"Objects":[{"StartTime":163739.0,"Position":322.0,"HyperDash":false}]},{"StartTime":163896.0,"Objects":[{"StartTime":163896.0,"Position":282.0,"HyperDash":false}]},{"StartTime":164054.0,"Objects":[{"StartTime":164054.0,"Position":419.0,"HyperDash":false},{"StartTime":164114.0,"Position":421.511932,"HyperDash":false},{"StartTime":164211.0,"Position":405.982758,"HyperDash":false}]},{"StartTime":164370.0,"Objects":[{"StartTime":164370.0,"Position":214.0,"HyperDash":false},{"StartTime":164430.0,"Position":211.248978,"HyperDash":false},{"StartTime":164527.0,"Position":202.042938,"HyperDash":false}]},{"StartTime":164686.0,"Objects":[{"StartTime":164686.0,"Position":295.0,"HyperDash":false},{"StartTime":164746.0,"Position":278.1207,"HyperDash":false},{"StartTime":164843.0,"Position":214.459625,"HyperDash":false}]},{"StartTime":165002.0,"Objects":[{"StartTime":165002.0,"Position":305.0,"HyperDash":false}]},{"StartTime":165160.0,"Objects":[{"StartTime":165160.0,"Position":209.0,"HyperDash":false},{"StartTime":165220.0,"Position":165.120712,"HyperDash":false},{"StartTime":165317.0,"Position":128.459641,"HyperDash":false}]},{"StartTime":165475.0,"Objects":[{"StartTime":165475.0,"Position":294.0,"HyperDash":false},{"StartTime":165535.0,"Position":279.1207,"HyperDash":false},{"StartTime":165632.0,"Position":213.459625,"HyperDash":false}]},{"StartTime":165791.0,"Objects":[{"StartTime":165791.0,"Position":66.0,"HyperDash":false},{"StartTime":165860.0,"Position":45.40761,"HyperDash":false},{"StartTime":165930.0,"Position":38.5398445,"HyperDash":false},{"StartTime":166000.0,"Position":12.8475342,"HyperDash":false},{"StartTime":166106.0,"Position":27.3485184,"HyperDash":false}]},{"StartTime":166265.0,"Objects":[{"StartTime":166265.0,"Position":226.0,"HyperDash":false}]},{"StartTime":166423.0,"Objects":[{"StartTime":166423.0,"Position":144.0,"HyperDash":false}]},{"StartTime":166581.0,"Objects":[{"StartTime":166581.0,"Position":244.0,"HyperDash":false},{"StartTime":166641.0,"Position":283.822266,"HyperDash":false},{"StartTime":166738.0,"Position":323.677734,"HyperDash":false}]},{"StartTime":166896.0,"Objects":[{"StartTime":166896.0,"Position":163.0,"HyperDash":false},{"StartTime":166956.0,"Position":176.896317,"HyperDash":false},{"StartTime":167053.0,"Position":242.864777,"HyperDash":false}]},{"StartTime":167212.0,"Objects":[{"StartTime":167212.0,"Position":374.0,"HyperDash":false},{"StartTime":167272.0,"Position":398.505157,"HyperDash":false},{"StartTime":167369.0,"Position":404.8981,"HyperDash":false}]},{"StartTime":167528.0,"Objects":[{"StartTime":167528.0,"Position":364.0,"HyperDash":false}]},{"StartTime":167686.0,"Objects":[{"StartTime":167686.0,"Position":490.0,"HyperDash":false},{"StartTime":167746.0,"Position":467.494843,"HyperDash":false},{"StartTime":167843.0,"Position":459.101929,"HyperDash":false}]},{"StartTime":168002.0,"Objects":[{"StartTime":168002.0,"Position":269.0,"HyperDash":false},{"StartTime":168062.0,"Position":244.4816,"HyperDash":false},{"StartTime":168159.0,"Position":239.105927,"HyperDash":false}]},{"StartTime":168317.0,"Objects":[{"StartTime":168317.0,"Position":74.0,"HyperDash":false},{"StartTime":168386.0,"Position":97.2507858,"HyperDash":false},{"StartTime":168456.0,"Position":150.689621,"HyperDash":false},{"StartTime":168526.0,"Position":165.044,"HyperDash":false},{"StartTime":168632.0,"Position":217.447281,"HyperDash":false}]},{"StartTime":168791.0,"Objects":[{"StartTime":168791.0,"Position":258.0,"HyperDash":false},{"StartTime":168851.0,"Position":231.961609,"HyperDash":false},{"StartTime":168948.0,"Position":228.157639,"HyperDash":false}]},{"StartTime":169107.0,"Objects":[{"StartTime":169107.0,"Position":85.0,"HyperDash":false},{"StartTime":169167.0,"Position":90.51432,"HyperDash":false},{"StartTime":169264.0,"Position":69.0959244,"HyperDash":false}]},{"StartTime":169423.0,"Objects":[{"StartTime":169423.0,"Position":233.0,"HyperDash":false},{"StartTime":169483.0,"Position":208.961609,"HyperDash":false},{"StartTime":169580.0,"Position":203.157639,"HyperDash":false}]},{"StartTime":169739.0,"Objects":[{"StartTime":169739.0,"Position":296.0,"HyperDash":false},{"StartTime":169799.0,"Position":315.6617,"HyperDash":false},{"StartTime":169896.0,"Position":377.655518,"HyperDash":false}]},{"StartTime":170054.0,"Objects":[{"StartTime":170054.0,"Position":224.0,"HyperDash":false}]},{"StartTime":170212.0,"Objects":[{"StartTime":170212.0,"Position":331.0,"HyperDash":false},{"StartTime":170272.0,"Position":367.6617,"HyperDash":false},{"StartTime":170369.0,"Position":412.655518,"HyperDash":false}]},{"StartTime":170528.0,"Objects":[{"StartTime":170528.0,"Position":238.0,"HyperDash":false},{"StartTime":170588.0,"Position":203.307648,"HyperDash":false},{"StartTime":170685.0,"Position":156.157562,"HyperDash":false}]},{"StartTime":170844.0,"Objects":[{"StartTime":170844.0,"Position":95.0,"HyperDash":false}]},{"StartTime":171002.0,"Objects":[{"StartTime":171002.0,"Position":92.0,"HyperDash":false},{"StartTime":171062.0,"Position":123.152824,"HyperDash":false},{"StartTime":171159.0,"Position":131.076691,"HyperDash":false}]},{"StartTime":171317.0,"Objects":[{"StartTime":171317.0,"Position":243.0,"HyperDash":false}]},{"StartTime":171475.0,"Objects":[{"StartTime":171475.0,"Position":218.0,"HyperDash":false}]},{"StartTime":171633.0,"Objects":[{"StartTime":171633.0,"Position":237.0,"HyperDash":false}]},{"StartTime":171791.0,"Objects":[{"StartTime":171791.0,"Position":212.0,"HyperDash":false}]},{"StartTime":171949.0,"Objects":[{"StartTime":171949.0,"Position":328.0,"HyperDash":false},{"StartTime":172009.0,"Position":361.2498,"HyperDash":false},{"StartTime":172106.0,"Position":407.426453,"HyperDash":false}]},{"StartTime":172265.0,"Objects":[{"StartTime":172265.0,"Position":447.0,"HyperDash":false},{"StartTime":172325.0,"Position":412.0013,"HyperDash":false},{"StartTime":172422.0,"Position":404.674683,"HyperDash":false}]},{"StartTime":172581.0,"Objects":[{"StartTime":172581.0,"Position":349.0,"HyperDash":false}]},{"StartTime":172739.0,"Objects":[{"StartTime":172739.0,"Position":337.0,"HyperDash":false},{"StartTime":172799.0,"Position":372.2498,"HyperDash":false},{"StartTime":172896.0,"Position":416.426453,"HyperDash":false}]},{"StartTime":173054.0,"Objects":[{"StartTime":173054.0,"Position":335.0,"HyperDash":false},{"StartTime":173114.0,"Position":295.0204,"HyperDash":false},{"StartTime":173211.0,"Position":255.685944,"HyperDash":false}]},{"StartTime":173370.0,"Objects":[{"StartTime":173370.0,"Position":195.0,"HyperDash":false},{"StartTime":173439.0,"Position":205.158081,"HyperDash":false},{"StartTime":173509.0,"Position":223.41394,"HyperDash":false},{"StartTime":173579.0,"Position":259.617249,"HyperDash":false},{"StartTime":173685.0,"Position":242.5926,"HyperDash":false}]},{"StartTime":173844.0,"Objects":[{"StartTime":173844.0,"Position":66.0,"HyperDash":false}]},{"StartTime":174002.0,"Objects":[{"StartTime":174002.0,"Position":125.0,"HyperDash":false},{"StartTime":174062.0,"Position":137.888153,"HyperDash":false},{"StartTime":174159.0,"Position":195.446823,"HyperDash":false}]},{"StartTime":174317.0,"Objects":[{"StartTime":174317.0,"Position":276.0,"HyperDash":false}]},{"StartTime":174475.0,"Objects":[{"StartTime":174475.0,"Position":104.0,"HyperDash":false},{"StartTime":174535.0,"Position":70.345,"HyperDash":false},{"StartTime":174632.0,"Position":24.6388855,"HyperDash":false}]},{"StartTime":174791.0,"Objects":[{"StartTime":174791.0,"Position":178.0,"HyperDash":false},{"StartTime":174851.0,"Position":167.60582,"HyperDash":false},{"StartTime":174948.0,"Position":161.976974,"HyperDash":false}]},{"StartTime":175107.0,"Objects":[{"StartTime":175107.0,"Position":293.0,"HyperDash":false}]},{"StartTime":175265.0,"Objects":[{"StartTime":175265.0,"Position":335.0,"HyperDash":false},{"StartTime":175325.0,"Position":377.0415,"HyperDash":false},{"StartTime":175422.0,"Position":413.356354,"HyperDash":false}]},{"StartTime":175581.0,"Objects":[{"StartTime":175581.0,"Position":366.0,"HyperDash":false},{"StartTime":175641.0,"Position":334.5056,"HyperDash":false},{"StartTime":175738.0,"Position":287.8307,"HyperDash":false}]},{"StartTime":175896.0,"Objects":[{"StartTime":175896.0,"Position":490.0,"HyperDash":false},{"StartTime":175965.0,"Position":458.363129,"HyperDash":false},{"StartTime":176035.0,"Position":474.112274,"HyperDash":false},{"StartTime":176105.0,"Position":466.752441,"HyperDash":false},{"StartTime":176211.0,"Position":444.10556,"HyperDash":false}]},{"StartTime":176370.0,"Objects":[{"StartTime":176370.0,"Position":330.0,"HyperDash":false}]},{"StartTime":176528.0,"Objects":[{"StartTime":176528.0,"Position":312.0,"HyperDash":false},{"StartTime":176588.0,"Position":298.540161,"HyperDash":false},{"StartTime":176685.0,"Position":294.0207,"HyperDash":false}]},{"StartTime":176844.0,"Objects":[{"StartTime":176844.0,"Position":175.0,"HyperDash":false}]},{"StartTime":177002.0,"Objects":[{"StartTime":177002.0,"Position":181.0,"HyperDash":false},{"StartTime":177062.0,"Position":170.459839,"HyperDash":false},{"StartTime":177159.0,"Position":198.979309,"HyperDash":false}]},{"StartTime":177317.0,"Objects":[{"StartTime":177317.0,"Position":318.0,"HyperDash":false},{"StartTime":177386.0,"Position":284.9978,"HyperDash":false},{"StartTime":177456.0,"Position":269.6918,"HyperDash":false},{"StartTime":177526.0,"Position":237.63176,"HyperDash":false},{"StartTime":177632.0,"Position":186.8728,"HyperDash":false}]},{"StartTime":177791.0,"Objects":[{"StartTime":177791.0,"Position":370.0,"HyperDash":false},{"StartTime":177851.0,"Position":406.333221,"HyperDash":false},{"StartTime":177948.0,"Position":450.520935,"HyperDash":false}]},{"StartTime":178107.0,"Objects":[{"StartTime":178107.0,"Position":325.0,"HyperDash":false},{"StartTime":178167.0,"Position":339.2036,"HyperDash":false},{"StartTime":178264.0,"Position":405.357574,"HyperDash":false}]},{"StartTime":178423.0,"Objects":[{"StartTime":178423.0,"Position":302.0,"HyperDash":false},{"StartTime":178483.0,"Position":291.456818,"HyperDash":false},{"StartTime":178580.0,"Position":279.113953,"HyperDash":false}]},{"StartTime":178739.0,"Objects":[{"StartTime":178739.0,"Position":173.0,"HyperDash":false},{"StartTime":178799.0,"Position":164.273453,"HyperDash":false},{"StartTime":178896.0,"Position":150.110962,"HyperDash":false}]},{"StartTime":179054.0,"Objects":[{"StartTime":179054.0,"Position":203.0,"HyperDash":false}]},{"StartTime":179212.0,"Objects":[{"StartTime":179212.0,"Position":58.0,"HyperDash":false},{"StartTime":179272.0,"Position":71.98529,"HyperDash":false},{"StartTime":179369.0,"Position":81.024,"HyperDash":false}]},{"StartTime":179528.0,"Objects":[{"StartTime":179528.0,"Position":266.0,"HyperDash":false},{"StartTime":179588.0,"Position":270.755371,"HyperDash":false},{"StartTime":179685.0,"Position":243.9513,"HyperDash":false}]},{"StartTime":179844.0,"Objects":[{"StartTime":179844.0,"Position":379.0,"HyperDash":false},{"StartTime":179904.0,"Position":407.036346,"HyperDash":false},{"StartTime":180001.0,"Position":462.828461,"HyperDash":false}]},{"StartTime":180160.0,"Objects":[{"StartTime":180160.0,"Position":252.0,"HyperDash":false},{"StartTime":180220.0,"Position":217.963638,"HyperDash":false},{"StartTime":180317.0,"Position":168.171539,"HyperDash":false}]},{"StartTime":180475.0,"Objects":[{"StartTime":180475.0,"Position":385.0,"HyperDash":false},{"StartTime":180535.0,"Position":434.036346,"HyperDash":false},{"StartTime":180632.0,"Position":468.828461,"HyperDash":false}]},{"StartTime":180791.0,"Objects":[{"StartTime":180791.0,"Position":258.0,"HyperDash":false},{"StartTime":180851.0,"Position":241.963638,"HyperDash":false},{"StartTime":180948.0,"Position":174.171539,"HyperDash":false}]},{"StartTime":181107.0,"Objects":[{"StartTime":181107.0,"Position":295.0,"HyperDash":false}]},{"StartTime":181265.0,"Objects":[{"StartTime":181265.0,"Position":334.0,"HyperDash":false}]},{"StartTime":181423.0,"Objects":[{"StartTime":181423.0,"Position":306.0,"HyperDash":false}]},{"StartTime":181581.0,"Objects":[{"StartTime":181581.0,"Position":347.0,"HyperDash":false}]},{"StartTime":181739.0,"Objects":[{"StartTime":181739.0,"Position":317.0,"HyperDash":false},{"StartTime":181799.0,"Position":323.0203,"HyperDash":false},{"StartTime":181896.0,"Position":322.286469,"HyperDash":false}]},{"StartTime":182054.0,"Objects":[{"StartTime":182054.0,"Position":237.0,"HyperDash":false}]},{"StartTime":182212.0,"Objects":[{"StartTime":182212.0,"Position":440.0,"HyperDash":false}]},{"StartTime":182370.0,"Objects":[{"StartTime":182370.0,"Position":225.0,"HyperDash":false}]},{"StartTime":183476.0,"Objects":[{"StartTime":183476.0,"Position":173.0,"HyperDash":false}]},{"StartTime":184265.0,"Objects":[{"StartTime":184265.0,"Position":173.0,"HyperDash":false},{"StartTime":184365.0,"Position":228.359283,"HyperDash":false},{"StartTime":184501.0,"Position":263.5279,"HyperDash":false}]},{"StartTime":184581.0,"Objects":[{"StartTime":184581.0,"Position":266.0,"HyperDash":false},{"StartTime":184641.0,"Position":259.91507,"HyperDash":false},{"StartTime":184738.0,"Position":205.594452,"HyperDash":false}]},{"StartTime":184818.0,"Objects":[{"StartTime":184818.0,"Position":180.0,"HyperDash":false}]},{"StartTime":184897.0,"Objects":[{"StartTime":184897.0,"Position":180.0,"HyperDash":false}]},{"StartTime":186002.0,"Objects":[{"StartTime":186002.0,"Position":402.0,"HyperDash":false}]},{"StartTime":186791.0,"Objects":[{"StartTime":186791.0,"Position":402.0,"HyperDash":false},{"StartTime":186891.0,"Position":364.639435,"HyperDash":false},{"StartTime":187027.0,"Position":311.469055,"HyperDash":false}]},{"StartTime":187107.0,"Objects":[{"StartTime":187107.0,"Position":309.0,"HyperDash":false},{"StartTime":187167.0,"Position":345.0628,"HyperDash":false},{"StartTime":187264.0,"Position":369.347656,"HyperDash":false}]},{"StartTime":187344.0,"Objects":[{"StartTime":187344.0,"Position":432.0,"HyperDash":false}]},{"StartTime":187423.0,"Objects":[{"StartTime":187423.0,"Position":432.0,"HyperDash":false},{"StartTime":187483.0,"Position":431.965149,"HyperDash":false},{"StartTime":187580.0,"Position":414.448761,"HyperDash":false}]},{"StartTime":187739.0,"Objects":[{"StartTime":187739.0,"Position":460.0,"HyperDash":false}]},{"StartTime":187897.0,"Objects":[{"StartTime":187897.0,"Position":270.0,"HyperDash":false},{"StartTime":187957.0,"Position":264.1196,"HyperDash":false},{"StartTime":188054.0,"Position":252.031967,"HyperDash":false}]},{"StartTime":188212.0,"Objects":[{"StartTime":188212.0,"Position":345.0,"HyperDash":false},{"StartTime":188272.0,"Position":362.0573,"HyperDash":false},{"StartTime":188369.0,"Position":362.009369,"HyperDash":false}]},{"StartTime":188528.0,"Objects":[{"StartTime":188528.0,"Position":223.0,"HyperDash":false},{"StartTime":188597.0,"Position":173.194031,"HyperDash":false},{"StartTime":188667.0,"Position":151.2194,"HyperDash":false},{"StartTime":188737.0,"Position":127.234268,"HyperDash":false},{"StartTime":188843.0,"Position":90.09637,"HyperDash":false}]},{"StartTime":189002.0,"Objects":[{"StartTime":189002.0,"Position":195.0,"HyperDash":false},{"StartTime":189062.0,"Position":228.972458,"HyperDash":false},{"StartTime":189159.0,"Position":277.218262,"HyperDash":false}]},{"StartTime":189318.0,"Objects":[{"StartTime":189318.0,"Position":315.0,"HyperDash":false},{"StartTime":189378.0,"Position":267.027557,"HyperDash":false},{"StartTime":189475.0,"Position":232.781723,"HyperDash":false}]},{"StartTime":189633.0,"Objects":[{"StartTime":189633.0,"Position":426.0,"HyperDash":false},{"StartTime":189693.0,"Position":416.778778,"HyperDash":false},{"StartTime":189790.0,"Position":397.035126,"HyperDash":false}]},{"StartTime":189949.0,"Objects":[{"StartTime":189949.0,"Position":370.0,"HyperDash":false},{"StartTime":190018.0,"Position":378.220642,"HyperDash":false},{"StartTime":190088.0,"Position":331.990845,"HyperDash":false},{"StartTime":190158.0,"Position":340.3975,"HyperDash":false},{"StartTime":190264.0,"Position":316.019745,"HyperDash":false}]},{"StartTime":190423.0,"Objects":[{"StartTime":190423.0,"Position":190.0,"HyperDash":false},{"StartTime":190483.0,"Position":164.497772,"HyperDash":false},{"StartTime":190580.0,"Position":110.287689,"HyperDash":false}]},{"StartTime":190739.0,"Objects":[{"StartTime":190739.0,"Position":221.0,"HyperDash":false},{"StartTime":190799.0,"Position":269.972839,"HyperDash":false},{"StartTime":190896.0,"Position":300.6956,"HyperDash":false}]},{"StartTime":191054.0,"Objects":[{"StartTime":191054.0,"Position":189.0,"HyperDash":false}]},{"StartTime":191212.0,"Objects":[{"StartTime":191212.0,"Position":378.0,"HyperDash":false},{"StartTime":191281.0,"Position":369.800842,"HyperDash":false},{"StartTime":191351.0,"Position":353.057861,"HyperDash":false},{"StartTime":191421.0,"Position":343.24173,"HyperDash":false},{"StartTime":191527.0,"Position":338.951782,"HyperDash":false}]},{"StartTime":191686.0,"Objects":[{"StartTime":191686.0,"Position":465.0,"HyperDash":false}]},{"StartTime":191844.0,"Objects":[{"StartTime":191844.0,"Position":363.0,"HyperDash":false},{"StartTime":191904.0,"Position":354.1089,"HyperDash":false},{"StartTime":192001.0,"Position":353.0403,"HyperDash":false}]},{"StartTime":192160.0,"Objects":[{"StartTime":192160.0,"Position":421.0,"HyperDash":false}]},{"StartTime":192318.0,"Objects":[{"StartTime":192318.0,"Position":421.0,"HyperDash":false}]},{"StartTime":192791.0,"Objects":[{"StartTime":192791.0,"Position":221.0,"HyperDash":false},{"StartTime":192869.0,"Position":265.146576,"HyperDash":false},{"StartTime":192948.0,"Position":280.86972,"HyperDash":false},{"StartTime":193027.0,"Position":309.011475,"HyperDash":false},{"StartTime":193106.0,"Position":330.6507,"HyperDash":false},{"StartTime":193156.0,"Position":343.5075,"HyperDash":false},{"StartTime":193206.0,"Position":362.759338,"HyperDash":false},{"StartTime":193256.0,"Position":379.106567,"HyperDash":false},{"StartTime":193343.0,"Position":429.869537,"HyperDash":false}]},{"StartTime":193423.0,"Objects":[{"StartTime":193423.0,"Position":439.0,"HyperDash":false},{"StartTime":193501.0,"Position":382.117065,"HyperDash":false},{"StartTime":193580.0,"Position":381.885132,"HyperDash":false},{"StartTime":193659.0,"Position":348.001556,"HyperDash":false},{"StartTime":193738.0,"Position":325.240662,"HyperDash":false},{"StartTime":193788.0,"Position":320.365143,"HyperDash":false},{"StartTime":193838.0,"Position":291.773438,"HyperDash":false},{"StartTime":193888.0,"Position":291.451782,"HyperDash":false},{"StartTime":193975.0,"Position":231.177582,"HyperDash":false}]},{"StartTime":194054.0,"Objects":[{"StartTime":194054.0,"Position":238.0,"HyperDash":false},{"StartTime":194132.0,"Position":276.079041,"HyperDash":false},{"StartTime":194211.0,"Position":303.6013,"HyperDash":false},{"StartTime":194290.0,"Position":315.4301,"HyperDash":false},{"StartTime":194369.0,"Position":346.0823,"HyperDash":false},{"StartTime":194419.0,"Position":358.557251,"HyperDash":false},{"StartTime":194469.0,"Position":382.922455,"HyperDash":false},{"StartTime":194519.0,"Position":399.497833,"HyperDash":false},{"StartTime":194606.0,"Position":445.572784,"HyperDash":false}]},{"StartTime":194686.0,"Objects":[{"StartTime":194686.0,"Position":452.0,"HyperDash":false},{"StartTime":194764.0,"Position":418.1171,"HyperDash":false},{"StartTime":194843.0,"Position":389.886749,"HyperDash":false},{"StartTime":194922.0,"Position":346.033173,"HyperDash":false},{"StartTime":195001.0,"Position":338.394043,"HyperDash":false},{"StartTime":195051.0,"Position":308.603027,"HyperDash":false},{"StartTime":195101.0,"Position":307.0379,"HyperDash":false},{"StartTime":195151.0,"Position":300.7087,"HyperDash":false},{"StartTime":195238.0,"Position":244.424088,"HyperDash":false}]},{"StartTime":195317.0,"Objects":[{"StartTime":195317.0,"Position":250.0,"HyperDash":false},{"StartTime":195395.0,"Position":280.0489,"HyperDash":false},{"StartTime":195474.0,"Position":338.609467,"HyperDash":false},{"StartTime":195553.0,"Position":354.8494,"HyperDash":false},{"StartTime":195632.0,"Position":358.40744,"HyperDash":false},{"StartTime":195682.0,"Position":381.576569,"HyperDash":false},{"StartTime":195732.0,"Position":402.7527,"HyperDash":false},{"StartTime":195782.0,"Position":430.257,"HyperDash":false},{"StartTime":195869.0,"Position":457.292328,"HyperDash":false}]},{"StartTime":195949.0,"Objects":[{"StartTime":195949.0,"Position":461.0,"HyperDash":false},{"StartTime":196049.0,"Position":446.167847,"HyperDash":false},{"StartTime":196185.0,"Position":438.391785,"HyperDash":false}]},{"StartTime":196265.0,"Objects":[{"StartTime":196265.0,"Position":411.0,"HyperDash":false},{"StartTime":196325.0,"Position":383.214722,"HyperDash":false},{"StartTime":196422.0,"Position":343.5428,"HyperDash":false}]},{"StartTime":196581.0,"Objects":[{"StartTime":196581.0,"Position":136.0,"HyperDash":false}]},{"StartTime":196739.0,"Objects":[{"StartTime":196739.0,"Position":314.0,"HyperDash":false}]},{"StartTime":196897.0,"Objects":[{"StartTime":196897.0,"Position":120.0,"HyperDash":false}]},{"StartTime":197055.0,"Objects":[{"StartTime":197055.0,"Position":298.0,"HyperDash":false}]},{"StartTime":197212.0,"Objects":[{"StartTime":197212.0,"Position":104.0,"HyperDash":false},{"StartTime":197272.0,"Position":85.28295,"HyperDash":false},{"StartTime":197369.0,"Position":92.47838,"HyperDash":false}]},{"StartTime":197528.0,"Objects":[{"StartTime":197528.0,"Position":136.0,"HyperDash":false},{"StartTime":197588.0,"Position":176.664658,"HyperDash":false},{"StartTime":197685.0,"Position":211.9784,"HyperDash":false}]},{"StartTime":197844.0,"Objects":[{"StartTime":197844.0,"Position":384.0,"HyperDash":false}]},{"StartTime":198002.0,"Objects":[{"StartTime":198002.0,"Position":317.0,"HyperDash":false},{"StartTime":198062.0,"Position":278.335327,"HyperDash":false},{"StartTime":198159.0,"Position":241.0216,"HyperDash":false}]},{"StartTime":198318.0,"Objects":[{"StartTime":198318.0,"Position":373.0,"HyperDash":false},{"StartTime":198378.0,"Position":422.153229,"HyperDash":false},{"StartTime":198475.0,"Position":448.229248,"HyperDash":false}]},{"StartTime":198633.0,"Objects":[{"StartTime":198633.0,"Position":436.0,"HyperDash":false},{"StartTime":198693.0,"Position":422.984,"HyperDash":false},{"StartTime":198790.0,"Position":412.4418,"HyperDash":false}]},{"StartTime":198949.0,"Objects":[{"StartTime":198949.0,"Position":264.0,"HyperDash":false},{"StartTime":199009.0,"Position":276.016,"HyperDash":false},{"StartTime":199106.0,"Position":287.5582,"HyperDash":false}]},{"StartTime":199265.0,"Objects":[{"StartTime":199265.0,"Position":242.0,"HyperDash":false}]},{"StartTime":199423.0,"Objects":[{"StartTime":199423.0,"Position":414.0,"HyperDash":false},{"StartTime":199483.0,"Position":411.984,"HyperDash":false},{"StartTime":199580.0,"Position":390.4418,"HyperDash":false}]},{"StartTime":199739.0,"Objects":[{"StartTime":199739.0,"Position":214.0,"HyperDash":false},{"StartTime":199799.0,"Position":212.821,"HyperDash":false},{"StartTime":199896.0,"Position":190.064774,"HyperDash":false}]},{"StartTime":200054.0,"Objects":[{"StartTime":200054.0,"Position":38.0,"HyperDash":false},{"StartTime":200114.0,"Position":47.9374542,"HyperDash":false},{"StartTime":200211.0,"Position":48.30301,"HyperDash":false}]},{"StartTime":200370.0,"Objects":[{"StartTime":200370.0,"Position":86.0,"HyperDash":false},{"StartTime":200430.0,"Position":89.79463,"HyperDash":false},{"StartTime":200527.0,"Position":95.92929,"HyperDash":false}]},{"StartTime":200686.0,"Objects":[{"StartTime":200686.0,"Position":48.0,"HyperDash":false},{"StartTime":200746.0,"Position":62.9374542,"HyperDash":false},{"StartTime":200843.0,"Position":58.30301,"HyperDash":false}]},{"StartTime":201002.0,"Objects":[{"StartTime":201002.0,"Position":96.0,"HyperDash":false},{"StartTime":201062.0,"Position":89.79463,"HyperDash":false},{"StartTime":201159.0,"Position":105.929291,"HyperDash":false}]},{"StartTime":201318.0,"Objects":[{"StartTime":201318.0,"Position":223.0,"HyperDash":false}]},{"StartTime":201476.0,"Objects":[{"StartTime":201476.0,"Position":211.0,"HyperDash":false}]},{"StartTime":201633.0,"Objects":[{"StartTime":201633.0,"Position":239.0,"HyperDash":false}]},{"StartTime":201791.0,"Objects":[{"StartTime":201791.0,"Position":227.0,"HyperDash":false}]},{"StartTime":201949.0,"Objects":[{"StartTime":201949.0,"Position":255.0,"HyperDash":false},{"StartTime":202009.0,"Position":263.68692,"HyperDash":false},{"StartTime":202106.0,"Position":243.714127,"HyperDash":false}]},{"StartTime":202265.0,"Objects":[{"StartTime":202265.0,"Position":218.0,"HyperDash":false}]},{"StartTime":202423.0,"Objects":[{"StartTime":202423.0,"Position":309.0,"HyperDash":false}]},{"StartTime":202581.0,"Objects":[{"StartTime":202581.0,"Position":328.0,"HyperDash":false}]},{"StartTime":203528.0,"Objects":[{"StartTime":203528.0,"Position":459.0,"HyperDash":false},{"StartTime":203588.0,"Position":448.977936,"HyperDash":false},{"StartTime":203685.0,"Position":398.758942,"HyperDash":false}]},{"StartTime":203844.0,"Objects":[{"StartTime":203844.0,"Position":305.0,"HyperDash":false}]},{"StartTime":204002.0,"Objects":[{"StartTime":204002.0,"Position":305.0,"HyperDash":false}]},{"StartTime":204160.0,"Objects":[{"StartTime":204160.0,"Position":264.0,"HyperDash":false}]},{"StartTime":204318.0,"Objects":[{"StartTime":204318.0,"Position":264.0,"HyperDash":false}]},{"StartTime":204476.0,"Objects":[{"StartTime":204476.0,"Position":210.0,"HyperDash":false}]},{"StartTime":204633.0,"Objects":[{"StartTime":204633.0,"Position":210.0,"HyperDash":false},{"StartTime":204693.0,"Position":211.007629,"HyperDash":false},{"StartTime":204790.0,"Position":204.786621,"HyperDash":false}]},{"StartTime":204949.0,"Objects":[{"StartTime":204949.0,"Position":62.0,"HyperDash":false},{"StartTime":205009.0,"Position":74.99237,"HyperDash":false},{"StartTime":205106.0,"Position":67.21338,"HyperDash":false}]},{"StartTime":205265.0,"Objects":[{"StartTime":205265.0,"Position":192.0,"HyperDash":false},{"StartTime":205325.0,"Position":214.8626,"HyperDash":false},{"StartTime":205422.0,"Position":262.080139,"HyperDash":false}]},{"StartTime":205581.0,"Objects":[{"StartTime":205581.0,"Position":398.0,"HyperDash":false},{"StartTime":205641.0,"Position":358.8581,"HyperDash":false},{"StartTime":205738.0,"Position":327.74704,"HyperDash":false}]},{"StartTime":205897.0,"Objects":[{"StartTime":205897.0,"Position":407.0,"HyperDash":false}]},{"StartTime":206054.0,"Objects":[{"StartTime":206054.0,"Position":493.0,"HyperDash":false},{"StartTime":206114.0,"Position":493.732544,"HyperDash":false},{"StartTime":206211.0,"Position":478.1135,"HyperDash":false}]},{"StartTime":206370.0,"Objects":[{"StartTime":206370.0,"Position":311.0,"HyperDash":false},{"StartTime":206430.0,"Position":296.786255,"HyperDash":false},{"StartTime":206527.0,"Position":239.579437,"HyperDash":false}]},{"StartTime":206686.0,"Objects":[{"StartTime":206686.0,"Position":76.0,"HyperDash":false}]},{"StartTime":206844.0,"Objects":[{"StartTime":206844.0,"Position":76.0,"HyperDash":false}]},{"StartTime":207002.0,"Objects":[{"StartTime":207002.0,"Position":186.0,"HyperDash":false}]},{"StartTime":207160.0,"Objects":[{"StartTime":207160.0,"Position":186.0,"HyperDash":false},{"StartTime":207220.0,"Position":211.157623,"HyperDash":false},{"StartTime":207317.0,"Position":257.432068,"HyperDash":false}]},{"StartTime":207476.0,"Objects":[{"StartTime":207476.0,"Position":102.0,"HyperDash":false},{"StartTime":207545.0,"Position":104.631119,"HyperDash":false},{"StartTime":207615.0,"Position":116.053741,"HyperDash":false},{"StartTime":207685.0,"Position":129.854782,"HyperDash":false},{"StartTime":207791.0,"Position":145.055069,"HyperDash":false}]},{"StartTime":207949.0,"Objects":[{"StartTime":207949.0,"Position":73.0,"HyperDash":false}]},{"StartTime":208107.0,"Objects":[{"StartTime":208107.0,"Position":73.0,"HyperDash":false}]},{"StartTime":208265.0,"Objects":[{"StartTime":208265.0,"Position":188.0,"HyperDash":false}]},{"StartTime":208423.0,"Objects":[{"StartTime":208423.0,"Position":188.0,"HyperDash":false},{"StartTime":208483.0,"Position":197.04393,"HyperDash":false},{"StartTime":208580.0,"Position":259.303467,"HyperDash":false}]},{"StartTime":208739.0,"Objects":[{"StartTime":208739.0,"Position":356.0,"HyperDash":false}]},{"StartTime":208897.0,"Objects":[{"StartTime":208897.0,"Position":428.0,"HyperDash":false},{"StartTime":208957.0,"Position":429.1922,"HyperDash":false},{"StartTime":209054.0,"Position":459.666473,"HyperDash":false}]},{"StartTime":209212.0,"Objects":[{"StartTime":209212.0,"Position":320.0,"HyperDash":false}]},{"StartTime":209370.0,"Objects":[{"StartTime":209370.0,"Position":320.0,"HyperDash":false}]},{"StartTime":209528.0,"Objects":[{"StartTime":209528.0,"Position":347.0,"HyperDash":false}]},{"StartTime":209686.0,"Objects":[{"StartTime":209686.0,"Position":347.0,"HyperDash":false}]},{"StartTime":209844.0,"Objects":[{"StartTime":209844.0,"Position":228.0,"HyperDash":false}]},{"StartTime":210002.0,"Objects":[{"StartTime":210002.0,"Position":135.0,"HyperDash":false},{"StartTime":210071.0,"Position":121.854248,"HyperDash":false},{"StartTime":210141.0,"Position":131.1977,"HyperDash":false},{"StartTime":210211.0,"Position":101.2941,"HyperDash":false},{"StartTime":210317.0,"Position":107.741356,"HyperDash":false}]},{"StartTime":210476.0,"Objects":[{"StartTime":210476.0,"Position":226.0,"HyperDash":false}]},{"StartTime":210633.0,"Objects":[{"StartTime":210633.0,"Position":226.0,"HyperDash":false}]},{"StartTime":210791.0,"Objects":[{"StartTime":210791.0,"Position":188.0,"HyperDash":false},{"StartTime":210851.0,"Position":221.829361,"HyperDash":false},{"StartTime":210948.0,"Position":216.115952,"HyperDash":false}]},{"StartTime":211107.0,"Objects":[{"StartTime":211107.0,"Position":289.0,"HyperDash":false}]},{"StartTime":211265.0,"Objects":[{"StartTime":211265.0,"Position":289.0,"HyperDash":false}]},{"StartTime":211423.0,"Objects":[{"StartTime":211423.0,"Position":357.0,"HyperDash":false},{"StartTime":211483.0,"Position":351.170654,"HyperDash":false},{"StartTime":211580.0,"Position":328.884064,"HyperDash":false}]},{"StartTime":211739.0,"Objects":[{"StartTime":211739.0,"Position":320.0,"HyperDash":false}]},{"StartTime":211897.0,"Objects":[{"StartTime":211897.0,"Position":420.0,"HyperDash":false},{"StartTime":211966.0,"Position":438.684967,"HyperDash":false},{"StartTime":212036.0,"Position":420.642761,"HyperDash":false},{"StartTime":212106.0,"Position":454.598969,"HyperDash":false},{"StartTime":212212.0,"Position":437.382416,"HyperDash":false}]},{"StartTime":212370.0,"Objects":[{"StartTime":212370.0,"Position":330.0,"HyperDash":false}]},{"StartTime":212528.0,"Objects":[{"StartTime":212528.0,"Position":188.0,"HyperDash":false},{"StartTime":212597.0,"Position":177.5667,"HyperDash":false},{"StartTime":212667.0,"Position":199.229538,"HyperDash":false},{"StartTime":212737.0,"Position":175.06488,"HyperDash":false},{"StartTime":212843.0,"Position":205.139709,"HyperDash":false}]},{"StartTime":213002.0,"Objects":[{"StartTime":213002.0,"Position":89.0,"HyperDash":false}]},{"StartTime":213160.0,"Objects":[{"StartTime":213160.0,"Position":89.0,"HyperDash":false}]},{"StartTime":213318.0,"Objects":[{"StartTime":213318.0,"Position":205.0,"HyperDash":false},{"StartTime":213378.0,"Position":224.953186,"HyperDash":false},{"StartTime":213475.0,"Position":276.3385,"HyperDash":false}]},{"StartTime":213633.0,"Objects":[{"StartTime":213633.0,"Position":355.0,"HyperDash":false}]},{"StartTime":213791.0,"Objects":[{"StartTime":213791.0,"Position":355.0,"HyperDash":false}]},{"StartTime":213949.0,"Objects":[{"StartTime":213949.0,"Position":377.0,"HyperDash":false},{"StartTime":214009.0,"Position":374.1648,"HyperDash":false},{"StartTime":214106.0,"Position":356.636047,"HyperDash":false}]},{"StartTime":214265.0,"Objects":[{"StartTime":214265.0,"Position":229.0,"HyperDash":false},{"StartTime":214325.0,"Position":222.07782,"HyperDash":false},{"StartTime":214422.0,"Position":207.805984,"HyperDash":false}]},{"StartTime":214581.0,"Objects":[{"StartTime":214581.0,"Position":109.0,"HyperDash":false}]},{"StartTime":214739.0,"Objects":[{"StartTime":214739.0,"Position":109.0,"HyperDash":false}]},{"StartTime":214897.0,"Objects":[{"StartTime":214897.0,"Position":176.0,"HyperDash":false},{"StartTime":214957.0,"Position":219.19249,"HyperDash":false},{"StartTime":215054.0,"Position":248.6392,"HyperDash":false}]},{"StartTime":215212.0,"Objects":[{"StartTime":215212.0,"Position":343.0,"HyperDash":false}]},{"StartTime":215370.0,"Objects":[{"StartTime":215370.0,"Position":343.0,"HyperDash":false}]},{"StartTime":215528.0,"Objects":[{"StartTime":215528.0,"Position":304.0,"HyperDash":false}]},{"StartTime":215686.0,"Objects":[{"StartTime":215686.0,"Position":304.0,"HyperDash":false}]},{"StartTime":215844.0,"Objects":[{"StartTime":215844.0,"Position":425.0,"HyperDash":false},{"StartTime":215904.0,"Position":443.940369,"HyperDash":false},{"StartTime":216001.0,"Position":497.363678,"HyperDash":false}]},{"StartTime":216160.0,"Objects":[{"StartTime":216160.0,"Position":386.0,"HyperDash":false},{"StartTime":216220.0,"Position":369.1159,"HyperDash":false},{"StartTime":216317.0,"Position":313.428955,"HyperDash":false}]},{"StartTime":216476.0,"Objects":[{"StartTime":216476.0,"Position":269.0,"HyperDash":false},{"StartTime":216545.0,"Position":292.429657,"HyperDash":false},{"StartTime":216615.0,"Position":293.77887,"HyperDash":false},{"StartTime":216685.0,"Position":296.7586,"HyperDash":false},{"StartTime":216791.0,"Position":316.2445,"HyperDash":false}]},{"StartTime":216949.0,"Objects":[{"StartTime":216949.0,"Position":343.0,"HyperDash":false}]},{"StartTime":217107.0,"Objects":[{"StartTime":217107.0,"Position":192.0,"HyperDash":false},{"StartTime":217167.0,"Position":199.294876,"HyperDash":false},{"StartTime":217264.0,"Position":180.090454,"HyperDash":false}]},{"StartTime":217423.0,"Objects":[{"StartTime":217423.0,"Position":73.0,"HyperDash":false}]},{"StartTime":217581.0,"Objects":[{"StartTime":217581.0,"Position":73.0,"HyperDash":false}]},{"StartTime":217739.0,"Objects":[{"StartTime":217739.0,"Position":197.0,"HyperDash":false},{"StartTime":217808.0,"Position":242.080475,"HyperDash":false},{"StartTime":217878.0,"Position":248.160492,"HyperDash":false},{"StartTime":217948.0,"Position":291.815369,"HyperDash":false},{"StartTime":218054.0,"Position":323.144318,"HyperDash":false}]},{"StartTime":218212.0,"Objects":[{"StartTime":218212.0,"Position":194.0,"HyperDash":false}]},{"StartTime":218370.0,"Objects":[{"StartTime":218370.0,"Position":345.0,"HyperDash":false},{"StartTime":218430.0,"Position":355.6937,"HyperDash":false},{"StartTime":218527.0,"Position":419.238617,"HyperDash":false}]},{"StartTime":218686.0,"Objects":[{"StartTime":218686.0,"Position":416.0,"HyperDash":false},{"StartTime":218746.0,"Position":402.107758,"HyperDash":false},{"StartTime":218843.0,"Position":341.536041,"HyperDash":false}]},{"StartTime":219002.0,"Objects":[{"StartTime":219002.0,"Position":485.0,"HyperDash":false},{"StartTime":219071.0,"Position":454.952484,"HyperDash":false},{"StartTime":219141.0,"Position":458.110535,"HyperDash":false},{"StartTime":219211.0,"Position":430.9237,"HyperDash":false},{"StartTime":219317.0,"Position":435.739746,"HyperDash":false}]},{"StartTime":219476.0,"Objects":[{"StartTime":219476.0,"Position":339.0,"HyperDash":false}]},{"StartTime":219633.0,"Objects":[{"StartTime":219633.0,"Position":374.0,"HyperDash":false},{"StartTime":219702.0,"Position":396.047546,"HyperDash":false},{"StartTime":219772.0,"Position":388.889465,"HyperDash":false},{"StartTime":219842.0,"Position":400.076324,"HyperDash":false},{"StartTime":219948.0,"Position":423.260254,"HyperDash":false}]},{"StartTime":220107.0,"Objects":[{"StartTime":220107.0,"Position":248.0,"HyperDash":false}]},{"StartTime":220265.0,"Objects":[{"StartTime":220265.0,"Position":201.0,"HyperDash":false}]},{"StartTime":220423.0,"Objects":[{"StartTime":220423.0,"Position":201.0,"HyperDash":false}]},{"StartTime":220581.0,"Objects":[{"StartTime":220581.0,"Position":239.0,"HyperDash":false}]},{"StartTime":220739.0,"Objects":[{"StartTime":220739.0,"Position":239.0,"HyperDash":false}]},{"StartTime":220897.0,"Objects":[{"StartTime":220897.0,"Position":122.0,"HyperDash":false},{"StartTime":220957.0,"Position":106.407677,"HyperDash":false},{"StartTime":221054.0,"Position":49.1845436,"HyperDash":false}]},{"StartTime":221212.0,"Objects":[{"StartTime":221212.0,"Position":257.0,"HyperDash":false},{"StartTime":221272.0,"Position":297.787933,"HyperDash":false},{"StartTime":221369.0,"Position":329.733826,"HyperDash":false}]},{"StartTime":221528.0,"Objects":[{"StartTime":221528.0,"Position":442.0,"HyperDash":false},{"StartTime":221588.0,"Position":442.869934,"HyperDash":false},{"StartTime":221685.0,"Position":436.426361,"HyperDash":false}]},{"StartTime":221844.0,"Objects":[{"StartTime":221844.0,"Position":417.0,"HyperDash":false},{"StartTime":221904.0,"Position":411.709747,"HyperDash":false},{"StartTime":222001.0,"Position":411.0072,"HyperDash":false}]},{"StartTime":222160.0,"Objects":[{"StartTime":222160.0,"Position":336.0,"HyperDash":false},{"StartTime":222220.0,"Position":351.869934,"HyperDash":false},{"StartTime":222317.0,"Position":330.426361,"HyperDash":false}]},{"StartTime":222476.0,"Objects":[{"StartTime":222476.0,"Position":311.0,"HyperDash":false},{"StartTime":222536.0,"Position":310.709747,"HyperDash":false},{"StartTime":222633.0,"Position":305.0072,"HyperDash":false}]},{"StartTime":222791.0,"Objects":[{"StartTime":222791.0,"Position":165.0,"HyperDash":false}]},{"StartTime":222949.0,"Objects":[{"StartTime":222949.0,"Position":143.0,"HyperDash":false}]},{"StartTime":223107.0,"Objects":[{"StartTime":223107.0,"Position":156.0,"HyperDash":false}]},{"StartTime":223265.0,"Objects":[{"StartTime":223265.0,"Position":125.0,"HyperDash":false}]},{"StartTime":223423.0,"Objects":[{"StartTime":223423.0,"Position":142.0,"HyperDash":false},{"StartTime":223483.0,"Position":119.964447,"HyperDash":false},{"StartTime":223580.0,"Position":66.02364,"HyperDash":false}]},{"StartTime":223739.0,"Objects":[{"StartTime":223739.0,"Position":209.0,"HyperDash":false}]},{"StartTime":223897.0,"Objects":[{"StartTime":223897.0,"Position":3.0,"HyperDash":false}]},{"StartTime":224054.0,"Objects":[{"StartTime":224054.0,"Position":111.0,"HyperDash":false}]},{"StartTime":234160.0,"Objects":[{"StartTime":234160.0,"Position":82.0,"HyperDash":false}]},{"StartTime":234476.0,"Objects":[{"StartTime":234476.0,"Position":82.0,"HyperDash":false}]},{"StartTime":234791.0,"Objects":[{"StartTime":234791.0,"Position":82.0,"HyperDash":false}]},{"StartTime":235107.0,"Objects":[{"StartTime":235107.0,"Position":82.0,"HyperDash":false}]},{"StartTime":235423.0,"Objects":[{"StartTime":235423.0,"Position":312.0,"HyperDash":false},{"StartTime":235483.0,"Position":357.5692,"HyperDash":false},{"StartTime":235580.0,"Position":391.4958,"HyperDash":false}]},{"StartTime":235739.0,"Objects":[{"StartTime":235739.0,"Position":262.0,"HyperDash":false}]},{"StartTime":235897.0,"Objects":[{"StartTime":235897.0,"Position":170.0,"HyperDash":false},{"StartTime":235957.0,"Position":146.430771,"HyperDash":false},{"StartTime":236054.0,"Position":90.5042,"HyperDash":false}]},{"StartTime":236212.0,"Objects":[{"StartTime":236212.0,"Position":83.0,"HyperDash":false},{"StartTime":236272.0,"Position":102.111885,"HyperDash":false},{"StartTime":236369.0,"Position":108.48745,"HyperDash":false}]},{"StartTime":236528.0,"Objects":[{"StartTime":236528.0,"Position":258.0,"HyperDash":false},{"StartTime":236597.0,"Position":241.951874,"HyperDash":false},{"StartTime":236667.0,"Position":212.802032,"HyperDash":false},{"StartTime":236737.0,"Position":200.97171,"HyperDash":false},{"StartTime":236843.0,"Position":210.516815,"HyperDash":false}]},{"StartTime":237002.0,"Objects":[{"StartTime":237002.0,"Position":327.0,"HyperDash":false}]},{"StartTime":237160.0,"Objects":[{"StartTime":237160.0,"Position":170.0,"HyperDash":false}]},{"StartTime":237318.0,"Objects":[{"StartTime":237318.0,"Position":316.0,"HyperDash":false},{"StartTime":237378.0,"Position":364.7829,"HyperDash":false},{"StartTime":237475.0,"Position":397.227,"HyperDash":false}]},{"StartTime":237633.0,"Objects":[{"StartTime":237633.0,"Position":417.0,"HyperDash":false},{"StartTime":237693.0,"Position":394.217072,"HyperDash":false},{"StartTime":237790.0,"Position":335.773,"HyperDash":false}]},{"StartTime":237949.0,"Objects":[{"StartTime":237949.0,"Position":153.0,"HyperDash":false},{"StartTime":238018.0,"Position":178.837616,"HyperDash":false},{"StartTime":238088.0,"Position":163.454758,"HyperDash":false},{"StartTime":238158.0,"Position":190.438675,"HyperDash":false},{"StartTime":238264.0,"Position":188.068771,"HyperDash":false}]},{"StartTime":238423.0,"Objects":[{"StartTime":238423.0,"Position":81.0,"HyperDash":false},{"StartTime":238483.0,"Position":68.7763062,"HyperDash":false},{"StartTime":238580.0,"Position":95.3198,"HyperDash":false}]},{"StartTime":238739.0,"Objects":[{"StartTime":238739.0,"Position":277.0,"HyperDash":false},{"StartTime":238799.0,"Position":285.009674,"HyperDash":false},{"StartTime":238896.0,"Position":291.003174,"HyperDash":false}]},{"StartTime":239054.0,"Objects":[{"StartTime":239054.0,"Position":429.0,"HyperDash":false},{"StartTime":239123.0,"Position":409.879852,"HyperDash":false},{"StartTime":239193.0,"Position":394.502,"HyperDash":false},{"StartTime":239263.0,"Position":421.1194,"HyperDash":false},{"StartTime":239369.0,"Position":401.762024,"HyperDash":false}]},{"StartTime":239528.0,"Objects":[{"StartTime":239528.0,"Position":252.0,"HyperDash":false}]},{"StartTime":239686.0,"Objects":[{"StartTime":239686.0,"Position":383.0,"HyperDash":false}]},{"StartTime":239844.0,"Objects":[{"StartTime":239844.0,"Position":224.0,"HyperDash":false},{"StartTime":239904.0,"Position":248.6068,"HyperDash":false},{"StartTime":240001.0,"Position":243.923813,"HyperDash":false}]},{"StartTime":240160.0,"Objects":[{"StartTime":240160.0,"Position":282.0,"HyperDash":false},{"StartTime":240220.0,"Position":294.4477,"HyperDash":false},{"StartTime":240317.0,"Position":300.9552,"HyperDash":false}]},{"StartTime":240476.0,"Objects":[{"StartTime":240476.0,"Position":155.0,"HyperDash":false},{"StartTime":240536.0,"Position":139.565125,"HyperDash":false},{"StartTime":240633.0,"Position":75.8260956,"HyperDash":false}]},{"StartTime":240791.0,"Objects":[{"StartTime":240791.0,"Position":177.0,"HyperDash":false}]},{"StartTime":240949.0,"Objects":[{"StartTime":240949.0,"Position":285.0,"HyperDash":false},{"StartTime":241009.0,"Position":297.434875,"HyperDash":false},{"StartTime":241106.0,"Position":364.1739,"HyperDash":false}]},{"StartTime":241265.0,"Objects":[{"StartTime":241265.0,"Position":190.0,"HyperDash":false},{"StartTime":241325.0,"Position":151.565109,"HyperDash":false},{"StartTime":241422.0,"Position":110.826096,"HyperDash":true}]},{"StartTime":241581.0,"Objects":[{"StartTime":241581.0,"Position":350.0,"HyperDash":false},{"StartTime":241650.0,"Position":379.1303,"HyperDash":false},{"StartTime":241720.0,"Position":386.2259,"HyperDash":false},{"StartTime":241790.0,"Position":365.848328,"HyperDash":false},{"StartTime":241896.0,"Position":367.289581,"HyperDash":false}]},{"StartTime":242054.0,"Objects":[{"StartTime":242054.0,"Position":172.0,"HyperDash":false},{"StartTime":242114.0,"Position":207.784363,"HyperDash":false},{"StartTime":242211.0,"Position":249.567841,"HyperDash":false}]},{"StartTime":242370.0,"Objects":[{"StartTime":242370.0,"Position":94.0,"HyperDash":false},{"StartTime":242430.0,"Position":107.155533,"HyperDash":false},{"StartTime":242527.0,"Position":172.076752,"HyperDash":false}]},{"StartTime":242686.0,"Objects":[{"StartTime":242686.0,"Position":256.0,"HyperDash":false},{"StartTime":242746.0,"Position":221.664886,"HyperDash":false},{"StartTime":242843.0,"Position":177.734055,"HyperDash":false}]},{"StartTime":243002.0,"Objects":[{"StartTime":243002.0,"Position":291.0,"HyperDash":false},{"StartTime":243062.0,"Position":288.7001,"HyperDash":false},{"StartTime":243159.0,"Position":309.460449,"HyperDash":false}]},{"StartTime":243318.0,"Objects":[{"StartTime":243318.0,"Position":386.0,"HyperDash":false}]},{"StartTime":243476.0,"Objects":[{"StartTime":243476.0,"Position":225.0,"HyperDash":false},{"StartTime":243536.0,"Position":221.299881,"HyperDash":false},{"StartTime":243633.0,"Position":206.539551,"HyperDash":false}]},{"StartTime":243791.0,"Objects":[{"StartTime":243791.0,"Position":406.0,"HyperDash":false},{"StartTime":243851.0,"Position":381.939,"HyperDash":false},{"StartTime":243948.0,"Position":386.849457,"HyperDash":false}]},{"StartTime":244107.0,"Objects":[{"StartTime":244107.0,"Position":308.0,"HyperDash":false}]},{"StartTime":244265.0,"Objects":[{"StartTime":244265.0,"Position":246.0,"HyperDash":false},{"StartTime":244325.0,"Position":196.524536,"HyperDash":false},{"StartTime":244422.0,"Position":163.999634,"HyperDash":false}]},{"StartTime":244581.0,"Objects":[{"StartTime":244581.0,"Position":89.0,"HyperDash":false}]},{"StartTime":244739.0,"Objects":[{"StartTime":244739.0,"Position":89.0,"HyperDash":false}]},{"StartTime":244897.0,"Objects":[{"StartTime":244897.0,"Position":242.0,"HyperDash":false},{"StartTime":244957.0,"Position":212.524536,"HyperDash":false},{"StartTime":245054.0,"Position":159.999634,"HyperDash":false}]},{"StartTime":245212.0,"Objects":[{"StartTime":245212.0,"Position":189.0,"HyperDash":false}]},{"StartTime":245370.0,"Objects":[{"StartTime":245370.0,"Position":189.0,"HyperDash":false}]},{"StartTime":245528.0,"Objects":[{"StartTime":245528.0,"Position":311.0,"HyperDash":false},{"StartTime":245588.0,"Position":334.7987,"HyperDash":false},{"StartTime":245685.0,"Position":390.993317,"HyperDash":false}]},{"StartTime":245844.0,"Objects":[{"StartTime":245844.0,"Position":400.0,"HyperDash":false}]},{"StartTime":246002.0,"Objects":[{"StartTime":246002.0,"Position":250.0,"HyperDash":false},{"StartTime":246062.0,"Position":220.210785,"HyperDash":false},{"StartTime":246159.0,"Position":170.042587,"HyperDash":false}]},{"StartTime":246318.0,"Objects":[{"StartTime":246318.0,"Position":320.0,"HyperDash":false},{"StartTime":246378.0,"Position":337.9858,"HyperDash":false},{"StartTime":246475.0,"Position":399.7238,"HyperDash":false}]},{"StartTime":246633.0,"Objects":[{"StartTime":246633.0,"Position":488.0,"HyperDash":false},{"StartTime":246693.0,"Position":475.33725,"HyperDash":false},{"StartTime":246790.0,"Position":466.066925,"HyperDash":false}]},{"StartTime":246949.0,"Objects":[{"StartTime":246949.0,"Position":314.0,"HyperDash":false},{"StartTime":247009.0,"Position":298.7121,"HyperDash":false},{"StartTime":247106.0,"Position":292.039,"HyperDash":false}]},{"StartTime":247265.0,"Objects":[{"StartTime":247265.0,"Position":202.0,"HyperDash":false},{"StartTime":247334.0,"Position":159.634674,"HyperDash":false},{"StartTime":247404.0,"Position":149.680634,"HyperDash":false},{"StartTime":247474.0,"Position":95.09981,"HyperDash":false},{"StartTime":247580.0,"Position":69.26001,"HyperDash":false}]},{"StartTime":247739.0,"Objects":[{"StartTime":247739.0,"Position":190.0,"HyperDash":false}]},{"StartTime":247897.0,"Objects":[{"StartTime":247897.0,"Position":200.0,"HyperDash":false}]},{"StartTime":248054.0,"Objects":[{"StartTime":248054.0,"Position":188.0,"HyperDash":false},{"StartTime":248114.0,"Position":208.2239,"HyperDash":false},{"StartTime":248211.0,"Position":262.024536,"HyperDash":false}]},{"StartTime":248370.0,"Objects":[{"StartTime":248370.0,"Position":342.0,"HyperDash":false}]},{"StartTime":248528.0,"Objects":[{"StartTime":248528.0,"Position":338.0,"HyperDash":false},{"StartTime":248588.0,"Position":338.277985,"HyperDash":false},{"StartTime":248685.0,"Position":366.8771,"HyperDash":false}]},{"StartTime":248844.0,"Objects":[{"StartTime":248844.0,"Position":290.0,"HyperDash":false},{"StartTime":248904.0,"Position":284.053131,"HyperDash":false},{"StartTime":249001.0,"Position":319.062073,"HyperDash":false}]},{"StartTime":249160.0,"Objects":[{"StartTime":249160.0,"Position":432.0,"HyperDash":false},{"StartTime":249220.0,"Position":451.277985,"HyperDash":false},{"StartTime":249317.0,"Position":460.877136,"HyperDash":false}]},{"StartTime":249476.0,"Objects":[{"StartTime":249476.0,"Position":384.0,"HyperDash":false},{"StartTime":249536.0,"Position":383.053131,"HyperDash":false},{"StartTime":249633.0,"Position":413.062042,"HyperDash":false}]},{"StartTime":249791.0,"Objects":[{"StartTime":249791.0,"Position":449.0,"HyperDash":false},{"StartTime":249860.0,"Position":463.458252,"HyperDash":false},{"StartTime":249930.0,"Position":466.69632,"HyperDash":false},{"StartTime":250000.0,"Position":482.1586,"HyperDash":false},{"StartTime":250106.0,"Position":487.1767,"HyperDash":false}]},{"StartTime":250265.0,"Objects":[{"StartTime":250265.0,"Position":351.0,"HyperDash":false}]},{"StartTime":250423.0,"Objects":[{"StartTime":250423.0,"Position":312.0,"HyperDash":false}]},{"StartTime":250581.0,"Objects":[{"StartTime":250581.0,"Position":196.0,"HyperDash":false},{"StartTime":250641.0,"Position":227.257263,"HyperDash":false},{"StartTime":250738.0,"Position":222.828583,"HyperDash":false}]},{"StartTime":250897.0,"Objects":[{"StartTime":250897.0,"Position":161.0,"HyperDash":false}]},{"StartTime":251054.0,"Objects":[{"StartTime":251054.0,"Position":88.0,"HyperDash":false},{"StartTime":251114.0,"Position":72.74277,"HyperDash":false},{"StartTime":251211.0,"Position":61.1714363,"HyperDash":false}]},{"StartTime":251370.0,"Objects":[{"StartTime":251370.0,"Position":188.0,"HyperDash":false},{"StartTime":251430.0,"Position":165.064133,"HyperDash":false},{"StartTime":251527.0,"Position":160.9748,"HyperDash":false}]},{"StartTime":251686.0,"Objects":[{"StartTime":251686.0,"Position":206.0,"HyperDash":false},{"StartTime":251746.0,"Position":254.490585,"HyperDash":false},{"StartTime":251843.0,"Position":286.597961,"HyperDash":false}]},{"StartTime":252002.0,"Objects":[{"StartTime":252002.0,"Position":381.0,"HyperDash":false},{"StartTime":252062.0,"Position":344.076172,"HyperDash":false},{"StartTime":252159.0,"Position":300.619,"HyperDash":false}]},{"StartTime":252318.0,"Objects":[{"StartTime":252318.0,"Position":430.0,"HyperDash":false}]},{"StartTime":252476.0,"Objects":[{"StartTime":252476.0,"Position":440.0,"HyperDash":false},{"StartTime":252536.0,"Position":447.263672,"HyperDash":false},{"StartTime":252633.0,"Position":467.223053,"HyperDash":false}]},{"StartTime":252791.0,"Objects":[{"StartTime":252791.0,"Position":349.0,"HyperDash":false},{"StartTime":252851.0,"Position":324.82547,"HyperDash":false},{"StartTime":252948.0,"Position":321.497559,"HyperDash":false}]},{"StartTime":253107.0,"Objects":[{"StartTime":253107.0,"Position":217.0,"HyperDash":false}]},{"StartTime":253265.0,"Objects":[{"StartTime":253265.0,"Position":229.0,"HyperDash":false}]},{"StartTime":253423.0,"Objects":[{"StartTime":253423.0,"Position":235.0,"HyperDash":false}]},{"StartTime":253581.0,"Objects":[{"StartTime":253581.0,"Position":225.0,"HyperDash":false},{"StartTime":253641.0,"Position":189.989166,"HyperDash":false},{"StartTime":253738.0,"Position":150.638168,"HyperDash":false}]},{"StartTime":253897.0,"Objects":[{"StartTime":253897.0,"Position":318.0,"HyperDash":false}]},{"StartTime":254054.0,"Objects":[{"StartTime":254054.0,"Position":337.0,"HyperDash":false}]},{"StartTime":254212.0,"Objects":[{"StartTime":254212.0,"Position":407.0,"HyperDash":false}]},{"StartTime":254291.0,"Objects":[{"StartTime":254291.0,"Position":407.0,"HyperDash":false}]},{"StartTime":254370.0,"Objects":[{"StartTime":254370.0,"Position":407.0,"HyperDash":false},{"StartTime":254430.0,"Position":396.4197,"HyperDash":false},{"StartTime":254527.0,"Position":415.948242,"HyperDash":false}]},{"StartTime":254686.0,"Objects":[{"StartTime":254686.0,"Position":282.0,"HyperDash":false}]},{"StartTime":254844.0,"Objects":[{"StartTime":254844.0,"Position":314.0,"HyperDash":false},{"StartTime":254904.0,"Position":328.5803,"HyperDash":false},{"StartTime":255001.0,"Position":305.051758,"HyperDash":false}]},{"StartTime":255160.0,"Objects":[{"StartTime":255160.0,"Position":150.0,"HyperDash":false}]},{"StartTime":255318.0,"Objects":[{"StartTime":255318.0,"Position":297.0,"HyperDash":true}]},{"StartTime":255476.0,"Objects":[{"StartTime":255476.0,"Position":74.0,"HyperDash":false}]},{"StartTime":255633.0,"Objects":[{"StartTime":255633.0,"Position":184.0,"HyperDash":false}]},{"StartTime":259423.0,"Objects":[{"StartTime":259423.0,"Position":66.0,"HyperDash":false},{"StartTime":259483.0,"Position":83.09656,"HyperDash":false},{"StartTime":259580.0,"Position":123.771538,"HyperDash":false}]},{"StartTime":259739.0,"Objects":[{"StartTime":259739.0,"Position":227.0,"HyperDash":false},{"StartTime":259799.0,"Position":259.148071,"HyperDash":false},{"StartTime":259896.0,"Position":284.876556,"HyperDash":false}]},{"StartTime":260054.0,"Objects":[{"StartTime":260054.0,"Position":374.0,"HyperDash":false}]},{"StartTime":260212.0,"Objects":[{"StartTime":260212.0,"Position":399.0,"HyperDash":false}]},{"StartTime":260370.0,"Objects":[{"StartTime":260370.0,"Position":455.0,"HyperDash":false}]},{"StartTime":260528.0,"Objects":[{"StartTime":260528.0,"Position":396.0,"HyperDash":false}]},{"StartTime":260686.0,"Objects":[{"StartTime":260686.0,"Position":288.0,"HyperDash":false},{"StartTime":260746.0,"Position":257.008453,"HyperDash":false},{"StartTime":260843.0,"Position":211.3641,"HyperDash":false}]},{"StartTime":261002.0,"Objects":[{"StartTime":261002.0,"Position":83.0,"HyperDash":false}]},{"StartTime":261160.0,"Objects":[{"StartTime":261160.0,"Position":120.0,"HyperDash":false},{"StartTime":261220.0,"Position":138.656952,"HyperDash":false},{"StartTime":261317.0,"Position":149.021484,"HyperDash":false}]},{"StartTime":261476.0,"Objects":[{"StartTime":261476.0,"Position":168.0,"HyperDash":false},{"StartTime":261536.0,"Position":191.8636,"HyperDash":false},{"StartTime":261633.0,"Position":196.8266,"HyperDash":false}]},{"StartTime":261791.0,"Objects":[{"StartTime":261791.0,"Position":300.0,"HyperDash":false},{"StartTime":261860.0,"Position":319.492554,"HyperDash":false},{"StartTime":261930.0,"Position":380.197144,"HyperDash":false},{"StartTime":262000.0,"Position":391.054535,"HyperDash":false},{"StartTime":262106.0,"Position":437.8109,"HyperDash":false}]},{"StartTime":262265.0,"Objects":[{"StartTime":262265.0,"Position":319.0,"HyperDash":false},{"StartTime":262325.0,"Position":323.6614,"HyperDash":false},{"StartTime":262422.0,"Position":301.140259,"HyperDash":false}]},{"StartTime":262581.0,"Objects":[{"StartTime":262581.0,"Position":160.0,"HyperDash":false},{"StartTime":262641.0,"Position":149.948944,"HyperDash":false},{"StartTime":262738.0,"Position":141.732,"HyperDash":false}]},{"StartTime":262897.0,"Objects":[{"StartTime":262897.0,"Position":297.0,"HyperDash":false},{"StartTime":262957.0,"Position":272.6614,"HyperDash":false},{"StartTime":263054.0,"Position":279.140259,"HyperDash":false}]},{"StartTime":263212.0,"Objects":[{"StartTime":263212.0,"Position":430.0,"HyperDash":false},{"StartTime":263272.0,"Position":455.104431,"HyperDash":false},{"StartTime":263369.0,"Position":510.4512,"HyperDash":false}]},{"StartTime":263528.0,"Objects":[{"StartTime":263528.0,"Position":401.0,"HyperDash":false}]},{"StartTime":263686.0,"Objects":[{"StartTime":263686.0,"Position":282.0,"HyperDash":false},{"StartTime":263746.0,"Position":270.895569,"HyperDash":false},{"StartTime":263843.0,"Position":201.548782,"HyperDash":false}]},{"StartTime":264002.0,"Objects":[{"StartTime":264002.0,"Position":124.0,"HyperDash":false},{"StartTime":264062.0,"Position":170.993927,"HyperDash":false},{"StartTime":264159.0,"Position":204.329208,"HyperDash":false}]},{"StartTime":264318.0,"Objects":[{"StartTime":264318.0,"Position":93.0,"HyperDash":false}]},{"StartTime":264476.0,"Objects":[{"StartTime":264476.0,"Position":61.0,"HyperDash":false},{"StartTime":264536.0,"Position":72.74982,"HyperDash":false},{"StartTime":264633.0,"Position":76.9942856,"HyperDash":false}]},{"StartTime":264791.0,"Objects":[{"StartTime":264791.0,"Position":229.0,"HyperDash":false},{"StartTime":264851.0,"Position":210.380234,"HyperDash":false},{"StartTime":264948.0,"Position":212.7894,"HyperDash":false}]},{"StartTime":265107.0,"Objects":[{"StartTime":265107.0,"Position":358.0,"HyperDash":false},{"StartTime":265167.0,"Position":382.749847,"HyperDash":false},{"StartTime":265264.0,"Position":373.9943,"HyperDash":false}]},{"StartTime":265423.0,"Objects":[{"StartTime":265423.0,"Position":470.0,"HyperDash":false}]},{"StartTime":265581.0,"Objects":[{"StartTime":265581.0,"Position":470.0,"HyperDash":false}]},{"StartTime":266054.0,"Objects":[{"StartTime":266054.0,"Position":149.0,"HyperDash":false},{"StartTime":266132.0,"Position":167.136108,"HyperDash":false},{"StartTime":266211.0,"Position":211.849609,"HyperDash":false},{"StartTime":266290.0,"Position":233.369949,"HyperDash":false},{"StartTime":266369.0,"Position":230.355377,"HyperDash":false},{"StartTime":266419.0,"Position":248.772461,"HyperDash":false},{"StartTime":266469.0,"Position":258.240936,"HyperDash":false},{"StartTime":266519.0,"Position":225.7301,"HyperDash":false},{"StartTime":266606.0,"Position":243.291763,"HyperDash":false}]},{"StartTime":266686.0,"Objects":[{"StartTime":266686.0,"Position":253.0,"HyperDash":false},{"StartTime":266764.0,"Position":255.375717,"HyperDash":false},{"StartTime":266843.0,"Position":240.91098,"HyperDash":false},{"StartTime":266922.0,"Position":228.636566,"HyperDash":false},{"StartTime":267001.0,"Position":225.662109,"HyperDash":false},{"StartTime":267051.0,"Position":218.509918,"HyperDash":false},{"StartTime":267101.0,"Position":217.651337,"HyperDash":false},{"StartTime":267151.0,"Position":202.171875,"HyperDash":false},{"StartTime":267238.0,"Position":158.415985,"HyperDash":false}]},{"StartTime":267318.0,"Objects":[{"StartTime":267318.0,"Position":168.0,"HyperDash":false},{"StartTime":267396.0,"Position":192.136108,"HyperDash":false},{"StartTime":267475.0,"Position":198.849609,"HyperDash":false},{"StartTime":267554.0,"Position":251.369949,"HyperDash":false},{"StartTime":267633.0,"Position":249.355377,"HyperDash":false},{"StartTime":267683.0,"Position":270.772461,"HyperDash":false},{"StartTime":267733.0,"Position":256.240936,"HyperDash":false},{"StartTime":267783.0,"Position":261.7301,"HyperDash":false},{"StartTime":267870.0,"Position":262.291779,"HyperDash":false}]},{"StartTime":267949.0,"Objects":[{"StartTime":267949.0,"Position":272.0,"HyperDash":false},{"StartTime":268027.0,"Position":258.375732,"HyperDash":false},{"StartTime":268106.0,"Position":255.91098,"HyperDash":false},{"StartTime":268185.0,"Position":277.636566,"HyperDash":false},{"StartTime":268264.0,"Position":244.662109,"HyperDash":false},{"StartTime":268314.0,"Position":220.509918,"HyperDash":false},{"StartTime":268364.0,"Position":225.651337,"HyperDash":false},{"StartTime":268414.0,"Position":209.171875,"HyperDash":false},{"StartTime":268501.0,"Position":177.415985,"HyperDash":false}]},{"StartTime":268581.0,"Objects":[{"StartTime":268581.0,"Position":187.0,"HyperDash":false},{"StartTime":268659.0,"Position":202.237671,"HyperDash":false},{"StartTime":268738.0,"Position":233.0073,"HyperDash":false},{"StartTime":268817.0,"Position":262.5497,"HyperDash":false},{"StartTime":268896.0,"Position":268.5099,"HyperDash":false},{"StartTime":268946.0,"Position":291.870758,"HyperDash":false},{"StartTime":268996.0,"Position":273.257019,"HyperDash":false},{"StartTime":269046.0,"Position":299.637756,"HyperDash":false},{"StartTime":269133.0,"Position":280.97876,"HyperDash":false}]},{"StartTime":269212.0,"Objects":[{"StartTime":269212.0,"Position":294.0,"HyperDash":false},{"StartTime":269312.0,"Position":315.9435,"HyperDash":false},{"StartTime":269448.0,"Position":321.1469,"HyperDash":false}]},{"StartTime":269528.0,"Objects":[{"StartTime":269528.0,"Position":340.0,"HyperDash":false},{"StartTime":269588.0,"Position":365.320465,"HyperDash":false},{"StartTime":269685.0,"Position":377.5064,"HyperDash":false}]},{"StartTime":269844.0,"Objects":[{"StartTime":269844.0,"Position":447.0,"HyperDash":false}]},{"StartTime":270002.0,"Objects":[{"StartTime":270002.0,"Position":465.0,"HyperDash":false}]},{"StartTime":270160.0,"Objects":[{"StartTime":270160.0,"Position":450.0,"HyperDash":false}]},{"StartTime":270318.0,"Objects":[{"StartTime":270318.0,"Position":468.0,"HyperDash":false}]},{"StartTime":270476.0,"Objects":[{"StartTime":270476.0,"Position":344.0,"HyperDash":false},{"StartTime":270536.0,"Position":326.693817,"HyperDash":false},{"StartTime":270633.0,"Position":270.128,"HyperDash":false}]},{"StartTime":270791.0,"Objects":[{"StartTime":270791.0,"Position":146.0,"HyperDash":false},{"StartTime":270851.0,"Position":119.05838,"HyperDash":false},{"StartTime":270948.0,"Position":124.892738,"HyperDash":false}]},{"StartTime":271107.0,"Objects":[{"StartTime":271107.0,"Position":264.0,"HyperDash":false}]},{"StartTime":271265.0,"Objects":[{"StartTime":271265.0,"Position":218.0,"HyperDash":false},{"StartTime":271325.0,"Position":192.312866,"HyperDash":false},{"StartTime":271422.0,"Position":147.21402,"HyperDash":false}]},{"StartTime":271581.0,"Objects":[{"StartTime":271581.0,"Position":245.0,"HyperDash":false},{"StartTime":271641.0,"Position":271.938019,"HyperDash":false},{"StartTime":271738.0,"Position":315.481079,"HyperDash":false}]},{"StartTime":271897.0,"Objects":[{"StartTime":271897.0,"Position":349.0,"HyperDash":false},{"StartTime":271957.0,"Position":327.700134,"HyperDash":false},{"StartTime":272054.0,"Position":336.267517,"HyperDash":false}]},{"StartTime":272212.0,"Objects":[{"StartTime":272212.0,"Position":446.0,"HyperDash":false},{"StartTime":272272.0,"Position":462.1508,"HyperDash":false},{"StartTime":272369.0,"Position":432.882324,"HyperDash":false}]},{"StartTime":272528.0,"Objects":[{"StartTime":272528.0,"Position":324.0,"HyperDash":false}]},{"StartTime":272686.0,"Objects":[{"StartTime":272686.0,"Position":415.0,"HyperDash":false},{"StartTime":272746.0,"Position":460.961884,"HyperDash":false},{"StartTime":272843.0,"Position":493.6076,"HyperDash":false}]},{"StartTime":273002.0,"Objects":[{"StartTime":273002.0,"Position":349.0,"HyperDash":false},{"StartTime":273062.0,"Position":319.039642,"HyperDash":false},{"StartTime":273159.0,"Position":270.206818,"HyperDash":false}]},{"StartTime":273318.0,"Objects":[{"StartTime":273318.0,"Position":148.0,"HyperDash":false},{"StartTime":273378.0,"Position":142.55928,"HyperDash":false},{"StartTime":273475.0,"Position":125.789063,"HyperDash":false}]},{"StartTime":273633.0,"Objects":[{"StartTime":273633.0,"Position":199.0,"HyperDash":false}]},{"StartTime":273791.0,"Objects":[{"StartTime":273791.0,"Position":247.0,"HyperDash":false},{"StartTime":273851.0,"Position":242.4407,"HyperDash":false},{"StartTime":273948.0,"Position":269.210938,"HyperDash":false}]},{"StartTime":274107.0,"Objects":[{"StartTime":274107.0,"Position":242.0,"HyperDash":false}]},{"StartTime":274265.0,"Objects":[{"StartTime":274265.0,"Position":143.0,"HyperDash":false},{"StartTime":274325.0,"Position":126.55928,"HyperDash":false},{"StartTime":274422.0,"Position":120.789063,"HyperDash":false}]},{"StartTime":274581.0,"Objects":[{"StartTime":274581.0,"Position":272.0,"HyperDash":false},{"StartTime":274641.0,"Position":314.038574,"HyperDash":false},{"StartTime":274738.0,"Position":355.8343,"HyperDash":false}]},{"StartTime":274897.0,"Objects":[{"StartTime":274897.0,"Position":488.0,"HyperDash":false},{"StartTime":274957.0,"Position":461.961426,"HyperDash":false},{"StartTime":275054.0,"Position":404.1657,"HyperDash":false}]},{"StartTime":275212.0,"Objects":[{"StartTime":275212.0,"Position":285.0,"HyperDash":false}]},{"StartTime":275370.0,"Objects":[{"StartTime":275370.0,"Position":315.0,"HyperDash":false}]},{"StartTime":275528.0,"Objects":[{"StartTime":275528.0,"Position":283.0,"HyperDash":false}]},{"StartTime":275686.0,"Objects":[{"StartTime":275686.0,"Position":313.0,"HyperDash":false}]},{"StartTime":275844.0,"Objects":[{"StartTime":275844.0,"Position":254.0,"HyperDash":false}]},{"StartTime":278370.0,"Objects":[{"StartTime":278370.0,"Position":71.0,"HyperDash":false},{"StartTime":278470.0,"Position":130.124451,"HyperDash":false},{"StartTime":278606.0,"Position":152.874481,"HyperDash":false}]},{"StartTime":278686.0,"Objects":[{"StartTime":278686.0,"Position":256.0,"HyperDash":false},{"StartTime":278786.0,"Position":279.959045,"HyperDash":false},{"StartTime":278922.0,"Position":336.141327,"HyperDash":false}]},{"StartTime":279002.0,"Objects":[{"StartTime":279002.0,"Position":351.0,"HyperDash":false},{"StartTime":279102.0,"Position":306.33313,"HyperDash":false},{"StartTime":279238.0,"Position":260.928619,"HyperDash":false}]},{"StartTime":279318.0,"Objects":[{"StartTime":279318.0,"Position":149.0,"HyperDash":false},{"StartTime":279418.0,"Position":144.369186,"HyperDash":false},{"StartTime":279554.0,"Position":58.3702,"HyperDash":true}]},{"StartTime":279633.0,"Objects":[{"StartTime":279633.0,"Position":205.0,"HyperDash":false}]},{"StartTime":280265.0,"Objects":[{"StartTime":280265.0,"Position":480.0,"HyperDash":false},{"StartTime":280343.0,"Position":474.7398,"HyperDash":false},{"StartTime":280422.0,"Position":458.350433,"HyperDash":false},{"StartTime":280501.0,"Position":451.037842,"HyperDash":false},{"StartTime":280580.0,"Position":422.829529,"HyperDash":false},{"StartTime":280659.0,"Position":414.7673,"HyperDash":false},{"StartTime":280738.0,"Position":394.904449,"HyperDash":false},{"StartTime":280817.0,"Position":370.3106,"HyperDash":false},{"StartTime":280896.0,"Position":368.073456,"HyperDash":false},{"StartTime":280975.0,"Position":348.296478,"HyperDash":false},{"StartTime":281054.0,"Position":338.1456,"HyperDash":false},{"StartTime":281133.0,"Position":314.726532,"HyperDash":false},{"StartTime":281212.0,"Position":321.195465,"HyperDash":false},{"StartTime":281291.0,"Position":328.64563,"HyperDash":false},{"StartTime":281370.0,"Position":292.093872,"HyperDash":false},{"StartTime":281449.0,"Position":310.49472,"HyperDash":false},{"StartTime":281528.0,"Position":288.733521,"HyperDash":false},{"StartTime":281606.0,"Position":290.7206,"HyperDash":false},{"StartTime":281685.0,"Position":288.1208,"HyperDash":false},{"StartTime":281764.0,"Position":272.7766,"HyperDash":false},{"StartTime":281843.0,"Position":266.504364,"HyperDash":false},{"StartTime":281922.0,"Position":241.107452,"HyperDash":false},{"StartTime":282001.0,"Position":268.358948,"HyperDash":false},{"StartTime":282080.0,"Position":230.079391,"HyperDash":false},{"StartTime":282159.0,"Position":242.0971,"HyperDash":false},{"StartTime":282238.0,"Position":243.277161,"HyperDash":false},{"StartTime":282317.0,"Position":222.536377,"HyperDash":false},{"StartTime":282396.0,"Position":223.8562,"HyperDash":false},{"StartTime":282475.0,"Position":205.2843,"HyperDash":false},{"StartTime":282554.0,"Position":197.88031,"HyperDash":false},{"StartTime":282633.0,"Position":198.803864,"HyperDash":false},{"StartTime":282712.0,"Position":166.135483,"HyperDash":false},{"StartTime":282791.0,"Position":156.019943,"HyperDash":false},{"StartTime":282870.0,"Position":155.553528,"HyperDash":false},{"StartTime":282949.0,"Position":129.81575,"HyperDash":false},{"StartTime":283028.0,"Position":128.8722,"HyperDash":false},{"StartTime":283107.0,"Position":100.768196,"HyperDash":false},{"StartTime":283176.0,"Position":72.3494644,"HyperDash":false},{"StartTime":283246.0,"Position":88.6788,"HyperDash":false},{"StartTime":283316.0,"Position":61.952446,"HyperDash":false},{"StartTime":283422.0,"Position":43.60075,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2190499.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2190499.osu new file mode 100644 index 0000000000..c0df81b7e4 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2190499.osu @@ -0,0 +1,977 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:4.7 +CircleSize:3.7 +OverallDifficulty:8.4 +ApproachRate:9 +SliderMultiplier:1.57 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +2,78991,87033 +2,129518,133770 +2,224254,233560 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +476,315.789473684211,4,2,1,50,1,0 +1739,-212.76595744681,4,2,1,50,0,0 +17054,-212.76595744681,4,2,1,5,0,0 +17133,-212.76595744681,4,2,1,50,0,0 +17370,-212.76595744681,4,2,1,5,0,0 +17449,-212.76595744681,4,2,1,50,0,0 +17686,-212.76595744681,4,2,1,50,0,0 +17765,-212.76595744681,4,2,1,5,0,0 +17844,-212.76595744681,4,2,1,50,0,0 +18160,-103.092783505155,4,2,1,70,0,0 +27239,-212.76595744681,4,2,1,70,0,0 +27318,-103.092783505155,4,2,1,70,0,0 +27554,-212.76595744681,4,2,1,70,0,0 +27633,-103.092783505155,4,2,1,70,0,0 +28265,-114.942528735633,4,2,1,60,0,0 +38370,-129.87012987013,4,2,1,50,0,0 +47765,-129.87012987013,4,2,1,5,0,0 +47844,-129.87012987013,4,2,1,50,0,0 +48081,-129.87012987013,4,2,1,5,0,0 +48160,-129.87012987013,4,2,1,50,0,0 +48476,-103.092783505155,4,2,1,70,0,0 +68686,-114.942528735633,4,2,1,60,0,0 +78791,-129.87012987013,4,2,1,50,0,0 +79344,-129.87012987013,4,2,1,5,0,0 +79423,-129.87012987013,4,2,1,50,0,0 +81870,-129.87012987013,4,2,1,5,0,0 +81949,-129.87012987013,4,2,1,50,0,0 +82502,-129.87012987013,4,2,1,5,0,0 +82581,-129.87012987013,4,2,1,50,0,0 +87633,-114.942528735633,4,2,1,60,0,0 +87870,-114.942528735633,4,2,1,5,0,0 +87949,-114.942528735633,4,2,1,60,0,0 +88186,-114.942528735633,4,2,1,5,0,0 +88265,-114.942528735633,4,2,1,60,0,0 +88502,-114.942528735633,4,2,1,5,0,0 +88581,-114.942528735633,4,2,1,60,0,0 +88897,-93.4579439252336,4,2,1,75,0,0 +109107,-129.87012987013,4,2,1,70,0,0 +111239,-129.87012987013,4,2,1,5,0,0 +111318,-129.87012987013,4,2,1,70,0,0 +113765,-129.87012987013,4,2,1,5,0,0 +113844,-129.87012987013,4,2,1,70,0,0 +114160,-93.4579439252336,4,2,1,75,0,0 +119054,-103.092783505155,4,2,1,75,0,0 +119212,-103.092783505155,4,2,1,70,0,0 +119449,-103.092783505155,4,2,1,5,0,0 +119528,-103.092783505155,4,2,1,70,0,0 +120081,-103.092783505155,4,2,1,5,0,0 +120160,-103.092783505155,4,2,1,70,0,0 +120712,-103.092783505155,4,2,1,5,0,0 +120791,-103.092783505155,4,2,1,70,0,0 +121344,-103.092783505155,4,2,1,5,0,0 +121423,-103.092783505155,4,2,1,70,0,0 +121976,-103.092783505155,4,2,1,5,0,0 +122054,-103.092783505155,4,2,1,70,0,0 +122607,-103.092783505155,4,2,1,5,0,0 +122686,-103.092783505155,4,2,1,70,0,0 +122923,-103.092783505155,4,2,1,5,0,0 +123002,-103.092783505155,4,2,1,70,0,0 +124265,-93.4579439252336,4,2,1,75,0,0 +129318,-129.87012987013,4,2,1,50,0,0 +133502,-129.87012987013,4,2,1,5,0,0 +133581,-129.87012987013,4,2,1,50,0,0 +134370,-114.942528735633,4,2,1,70,0,0 +137133,-114.942528735633,4,2,1,5,0,0 +137212,-114.942528735633,4,2,1,70,0,0 +137449,-114.942528735633,4,2,1,5,0,0 +137528,-114.942528735633,4,2,1,70,0,0 +137765,-114.942528735633,4,2,1,5,0,0 +137844,-114.942528735633,4,2,1,70,0,0 +142502,-114.942528735633,4,2,1,5,0,0 +142581,-114.942528735633,4,2,1,70,0,0 +145976,-114.942528735633,4,2,1,5,0,0 +146054,-114.942528735633,4,2,1,70,0,0 +146291,-114.942528735633,4,2,1,5,0,0 +146370,-114.942528735633,4,2,1,70,0,0 +146607,-114.942528735633,4,2,1,5,0,0 +146686,-114.942528735633,4,2,1,70,0,0 +151344,-114.942528735633,4,2,1,5,0,0 +151423,-114.942528735633,4,2,1,70,0,0 +152054,-103.092783505155,4,2,1,70,0,0 +161133,-103.092783505155,4,2,1,5,0,0 +161212,-103.092783505155,4,2,1,70,0,0 +161449,-103.092783505155,4,2,1,5,0,0 +161528,-103.092783505155,4,2,1,70,0,0 +161765,-103.092783505155,4,2,1,5,0,0 +161844,-103.092783505155,4,2,1,70,0,0 +162160,-93.4579439252336,4,2,1,75,0,0 +182370,-129.87012987013,4,2,1,70,0,0 +184502,-129.87012987013,4,2,1,5,0,0 +184581,-129.87012987013,4,2,1,70,0,0 +187028,-129.87012987013,4,2,1,5,0,0 +187107,-129.87012987013,4,2,1,70,0,0 +187423,-93.4579439252336,4,2,1,75,0,0 +192318,-103.092783505155,4,2,1,75,0,0 +192476,-103.092783505155,4,2,1,70,0,0 +192712,-103.092783505155,4,2,1,5,0,0 +192791,-103.092783505155,4,2,1,70,0,0 +193344,-103.092783505155,4,2,1,5,0,0 +193423,-103.092783505155,4,2,1,70,0,0 +193976,-103.092783505155,4,2,1,5,0,0 +194054,-103.092783505155,4,2,1,70,0,0 +194607,-103.092783505155,4,2,1,5,0,0 +194686,-103.092783505155,4,2,1,70,0,0 +195239,-103.092783505155,4,2,1,5,0,0 +195318,-103.092783505155,4,2,1,70,0,0 +195870,-103.092783505155,4,2,1,5,0,0 +195949,-103.092783505155,4,2,1,70,0,0 +196186,-103.092783505155,4,2,1,5,0,0 +196265,-103.092783505155,4,2,1,70,0,0 +197528,-93.4579439252336,4,2,1,75,0,0 +202581,-129.87012987013,4,2,1,70,0,0 +203844,-103.092783505155,4,2,1,70,0,0 +224054,-129.87012987013,4,2,1,60,0,0 +235423,-93.4579439252336,4,2,1,75,0,0 +255633,-129.87012987013,4,2,1,60,0,0 +260686,-93.4579439252336,4,2,1,75,0,0 +265581,-103.092783505155,4,2,1,75,0,0 +265739,-103.092783505155,4,2,1,70,0,0 +265976,-103.092783505155,4,2,1,5,0,0 +266054,-103.092783505155,4,2,1,70,0,0 +266607,-103.092783505155,4,2,1,5,0,0 +266686,-103.092783505155,4,2,1,70,0,0 +267239,-103.092783505155,4,2,1,5,0,0 +267318,-103.092783505155,4,2,1,70,0,0 +267870,-103.092783505155,4,2,1,5,0,0 +267949,-103.092783505155,4,2,1,70,0,0 +268502,-103.092783505155,4,2,1,5,0,0 +268581,-103.092783505155,4,2,1,70,0,0 +269133,-103.092783505155,4,2,1,5,0,0 +269212,-103.092783505155,4,2,1,70,0,0 +269449,-103.092783505155,4,2,1,5,0,0 +269528,-103.092783505155,4,2,1,70,0,0 +270791,-93.4579439252336,4,2,1,75,0,0 +275844,-129.87012987013,4,2,1,60,0,0 +278370,-93.4579439252336,4,2,1,75,0,0 +278607,-93.4579439252336,4,2,1,5,0,0 +278686,-93.4579439252336,4,2,1,75,0,0 +278923,-93.4579439252336,4,2,1,5,0,0 +279002,-78.7401574803149,4,2,1,75,0,0 +279239,-78.7401574803149,4,2,1,5,0,0 +279318,-78.7401574803149,4,2,1,75,0,0 +279554,-78.7401574803149,4,2,1,5,0,0 +279633,-129.87012987013,4,2,1,70,0,0 +280265,-270.270270270271,4,2,1,70,0,0 +283423,-270.270270270271,4,2,1,10,0,0 + +[HitObjects] +367,158,1739,6,0,B|277:179|338:219|236:236,1,147.579997748108,2|2,0:0|0:0,0:0:0:0: +161,20,3002,6,0,P|188:41|234:156,1,147.579997748108,2|2,0:0|0:0,0:0:0:0: +47,263,4265,6,0,P|91:234|115:230,1,73.789998874054,2|2,0:0|0:0,0:0:0:0: +235,344,4897,2,0,P|299:349|342:311,1,110.684998311081,2|2,0:0|0:0,0:0:0:0: +372,233,5528,2,0,P|351:171|339:79,1,147.579997748108,2|0,0:0|0:0,0:0:0:0: +55,109,6791,6,0,P|89:141|126:149,1,73.789998874054,2|2,0:0|0:0,0:0:0:0: +240,23,7423,2,0,P|203:58|189:121,1,110.684998311081,2|2,0:0|0:0,0:0:0:0: +273,203,8054,2,0,P|300:186|348:175,2,73.789998874054,2|2|2,0:0|0:0|0:0,0:0:0:0: +147,324,9002,2,0,P|124:323|97:314,1,36.894999437027,2|2,0:0|0:0,0:0:0:0: +59,247,9318,6,0,P|51:213|39:175,2,73.789998874054,2|2|2,0:0|0:0|0:0,0:0:0:0: +133,53,10265,1,2,0:0:0:0: +256,192,10581,12,0,11844,0:0:0:0: +256,192,13107,12,0,14370,0:0:0:0: +74,66,15633,6,0,B|151:62|120:116|198:112,1,138.356247888851,2|2,0:0|0:0,0:0:0:0: +189,105,17844,5,4,0:0:0:0: +189,105,18160,6,0,P|222:130|274:136,1,76.1450018009148,6|2,1:2|0:0,0:0:0:0: +402,27,18476,2,0,P|365:36|335:59,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +383,259,18791,2,0,P|400:173|404:106,1,152.29000360183,2|10,1:2|0:2,0:0:0:0: +254,55,19265,1,0,0:0:0:0: +178,227,19423,6,0,P|140:242|92:242,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +245,84,19739,2,0,P|282:86|317:100,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +287,315,20054,2,0,P|270:229|266:162,1,152.29000360183,2|8,1:2|0:2,0:0:0:0: +167,252,20528,1,0,0:0:0:0: +110,91,20686,6,0,P|77:65|24:58,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +158,225,21002,2,0,P|194:214|223:190,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +105,73,21318,2,0,P|72:47|19:40,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +153,207,21634,2,0,P|189:196|218:172,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +321,19,21949,5,6,1:2:0:0: +372,198,22107,1,2,0:0:0:0: +345,14,22265,2,0,P|334:50|326:104,1,76.1450018009148,10|2,0:2|0:0,0:0:0:0: +413,295,22581,1,6,1:2:0:0: +442,141,22739,1,10,0:2:0:0: +409,316,22897,2,0,P|370:337|316:337,1,76.1450018009148,10|2,0:2|0:0,0:0:0:0: +205,239,23212,6,0,P|219:282|226:330,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +73,189,23528,2,0,P|59:232|52:280,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +240,312,23844,2,0,P|233:275|221:239,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +88,189,24160,2,0,P|76:225|69:262,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +206,54,24476,6,0,L|301:45,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +425,174,24791,2,0,L|330:165,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +196,41,25107,2,0,L|291:32,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +415,161,25423,1,10,0:2:0:0: +363,43,25581,1,0,0:0:0:0: +263,180,25739,6,0,P|272:216|279:261,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +418,374,26054,2,0,P|424:336|433:299,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +251,184,26370,2,0,P|260:220|267:265,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +406,378,26686,2,0,P|412:340|421:303,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +326,119,27002,6,0,P|266:96|196:111,1,114.217502701372,14|0,0:2|0:0,0:0:0:0: +215,85,27318,2,0,P|271:80|323:102,1,114.217502701372,8|0,0:2|0:0,0:0:0:0: +324,89,27633,2,0,P|250:68|174:92,1,152.29000360183,12|4,0:2|0:2,0:0:0:0: +65,343,28265,6,0,B|57:248|105:312|97:183,1,136.590001146309,6|8,1:2|0:2,0:0:0:0: +153,332,28739,1,2,1:2:0:0: +153,332,28897,1,2,0:0:0:0: +215,226,29054,2,0,P|247:210|288:209,1,68.2950005731545,2|8,1:2|0:2,0:0:0:0: +332,322,29370,2,0,P|298:319|267:303,1,68.2950005731545,2|0,0:0|1:2,0:0:0:0: +371,217,29686,1,2,0:0:0:0: +371,217,29844,1,10,0:2:0:0: +444,302,30002,1,2,1:2:0:0: +444,302,30160,2,0,P|460:262|462:211,1,68.2950005731545,2|0,0:0|1:2,0:0:0:0: +393,130,30476,2,0,P|377:90|375:39,1,68.2950005731545,10|0,0:2|0:0,0:0:0:0: +265,134,30791,6,0,L|169:122,1,68.2950005731545,2|0,1:2|0:0,0:0:0:0: +80,53,31107,2,0,L|147:44,1,68.2950005731545,10|0,0:2|1:2,0:0:0:0: +124,189,31423,2,0,L|57:181,1,68.2950005731545,2|0,0:0|1:2,0:0:0:0: +164,296,31739,1,10,0:2:0:0: +164,296,31897,2,0,L|231:287,1,68.2950005731545,2|0,0:0|1:2,0:0:0:0: +365,211,32212,1,2,0:0:0:0: +365,211,32370,2,0,P|379:246|384:289,1,68.2950005731545,10|0,0:2|1:2,0:0:0:0: +488,162,32686,2,0,P|472:228|468:310,1,136.590001146309,2|8,0:0|0:2,0:0:0:0: +406,132,33160,1,0,1:2:0:0: +277,224,33318,6,0,B|197:212|245:168|149:160,1,136.590001146309,6|8,1:2|0:2,0:0:0:0: +283,146,33791,1,2,1:2:0:0: +283,146,33949,1,2,0:0:0:0: +158,238,34107,2,0,P|123:253|68:253,1,68.2950005731545,2|8,1:2|0:2,0:0:0:0: +19,126,34423,2,0,P|52:130|83:144,1,68.2950005731545,2|0,1:2|1:2,0:0:0:0: +158,238,34739,1,2,0:0:0:0: +158,238,34897,1,10,0:2:0:0: +204,124,35054,1,2,1:2:0:0: +204,124,35212,2,0,P|213:84|217:31,1,68.2950005731545,2|2,0:0|1:2,0:0:0:0: +345,175,35528,2,0,P|336:141|332:108,1,68.2950005731545,10|0,0:2|1:2,0:0:0:0: +461,237,35844,6,0,P|424:218|324:207,2,136.590001146309,2|10|2,1:2|0:2|1:2,0:0:0:0: +248,360,36791,1,10,0:2:0:0: +248,360,36949,2,0,P|259:318|261:281,1,68.2950005731545,2|8,0:0|0:2,0:0:0:0: +189,145,37265,5,2,1:2:0:0: +130,295,37423,2,0,P|96:312|48:311,1,68.2950005731545,10|0,0:2|1:2,0:0:0:0: +32,119,37739,5,10,0:2:0:0: +79,229,37897,1,0,1:2:0:0: +126,47,38054,5,12,0:2:0:0: +67,202,38212,1,0,1:2:0:0: +189,145,38370,6,0,P|236:139|304:205,1,120.889997601975,4|2,1:2|0:0,0:0:0:0: +281,297,38844,2,0,P|256:311|215:316,2,60.4449988009873,2|2|2,0:0|0:0|0:0,0:0:0:0: +367,240,39318,2,0,P|396:245|423:259,1,60.4449988009873,2|2,0:0|0:0,0:0:0:0: +493,325,39633,1,2,0:0:0:0: +493,325,39791,2,0,L|500:262,1,60.4449988009873,2|2,1:2|0:0,0:0:0:0: +450,183,40107,2,0,L|443:120,1,60.4449988009873,2|2,0:0|0:0,0:0:0:0: +379,41,40423,1,2,1:2:0:0: +379,41,40581,1,2,0:0:0:0: +312,120,40739,6,0,B|229:114|279:80|188:72,1,120.889997601975,2|2,0:0|0:0,0:0:0:0: +120,125,41212,2,0,P|107:98|107:68,2,60.4449988009873,2|2|2,0:0|0:0|0:0,0:0:0:0: +195,158,41686,2,0,P|195:187|182:215,1,60.4449988009873,2|2,0:0|0:0,0:0:0:0: +81,267,42002,1,2,0:0:0:0: +81,267,42160,1,2,0:0:0:0: +157,335,42318,1,2,1:2:0:0: +157,335,42476,2,0,L|233:329,1,60.4449988009873,2|0,0:0|0:0,0:0:0:0: +314,250,42791,2,0,L|374:254,1,60.4449988009873,2|2,1:2|0:0,0:0:0:0: +224,343,43107,6,0,L|92:351,1,120.889997601975,2|0,0:0|0:0,0:0:0:0: +18,308,43581,2,0,L|26:248,2,60.4449988009873,2|2|2,1:2|0:0|0:0,0:0:0:0: +118,245,44054,2,0,L|109:185,1,60.4449988009873,2|2,0:0|0:0,0:0:0:0: +32,119,44370,1,2,0:0:0:0: +32,119,44528,2,0,L|39:56,1,60.4449988009873,2|2,0:0|0:0,0:0:0:0: +131,30,44844,1,2,1:2:0:0: +131,30,45002,2,0,L|124:90,1,60.4449988009873,2|2,0:0|0:0,0:0:0:0: +215,147,45318,1,2,0:0:0:0: +215,147,45476,2,0,L|289:140,1,60.4449988009873,2|2,1:2|0:0,0:0:0:0: +362,98,45791,5,2,0:0:0:0: +362,98,45949,1,2,1:2:0:0: +350,203,46107,2,0,L|356:278,1,60.4449988009873,2|0,0:0|0:0,0:0:0:0: +421,352,46423,1,2,0:0:0:0: +421,352,46581,1,2,1:2:0:0: +343,276,46739,2,0,L|268:282,1,60.4449988009873,2|0,0:0|0:0,0:0:0:0: +212,353,47054,5,2,0:0:0:0: +176,245,47212,1,2,1:2:0:0: +104,346,47370,1,2,0:0:0:0: +104,346,47449,1,2,0:0:0:0: +104,346,47528,2,0,P|96:290|81:231,1,90.6674982014809,2|0,1:2|0:0,0:0:0:0: +73,246,47844,2,0,P|81:190|96:131,1,90.6674982014809,2|0,1:2|0:0,0:0:0:0: +108,144,48160,1,4,0:2:0:0: +108,144,48476,6,0,P|146:167|197:167,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +259,24,48791,2,0,P|221:29|190:50,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +329,179,49107,2,0,B|429:161|369:117|469:97,1,152.29000360183,2|8,0:0|0:2,0:0:0:0: +328,96,49581,1,0,0:0:0:0: +472,190,49739,6,0,P|462:222|454:274,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +324,372,50054,2,0,P|317:334|306:298,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +190,174,50370,2,0,P|128:184|85:268,1,152.29000360183,2|8,0:0|0:2,0:0:0:0: +206,294,50844,1,0,0:0:0:0: +313,170,51002,6,0,P|323:125|328:78,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +223,271,51318,2,0,P|212:226|208:179,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +268,40,51633,2,0,P|302:19|358:19,1,76.1450018009148,2|0,0:0|1:2,0:0:0:0: +382,195,51949,2,0,P|344:189|312:169,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +191,14,52265,6,0,B|176:109|235:65|217:167,1,152.29000360183,6|10,1:2|0:2,0:0:0:0: +145,291,52739,1,0,1:2:0:0: +75,165,52897,2,0,P|106:144|152:135,1,76.1450018009148,2|0,0:0|1:2,0:0:0:0: +223,271,53212,2,0,P|254:292|291:300,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +423,166,53528,5,2,1:2:0:0: +383,316,53686,2,0,P|364:275|364:218,1,76.1450018009148,2|8,0:0|0:2,0:0:0:0: +445,94,54002,2,0,P|439:131|422:165,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +346,37,54318,1,2,1:2:0:0: +268,179,54476,2,0,P|230:173|196:156,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +79,28,54791,6,0,P|101:82|110:184,1,152.29000360183,2|10,1:2|0:2,0:0:0:0: +38,334,55265,2,0,P|44:293|61:244,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +189,362,55581,1,0,1:2:0:0: +125,198,55739,2,0,P|135:234|141:272,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +279,380,56054,6,0,P|329:379|372:344,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +470,222,56370,2,0,P|432:219|397:234,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +438,384,56686,2,0,P|444:338|446:293,1,76.1450018009148,2|0,0:0|1:2,0:0:0:0: +287,222,57002,2,0,P|289:259|294:297,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +334,124,57318,6,0,P|311:115|285:110,3,38.0725009004574,6|2|2|2,1:2|0:0|0:0|0:0,0:0:0:0: +230,148,57633,2,0,P|201:173|146:180,1,76.1450018009148,6|2,1:2|0:0,0:0:0:0: +42,81,57949,2,0,P|56:112|68:176,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +188,17,58265,2,0,P|174:48|162:112,1,76.1450018009148,14|0,0:2|0:0,0:0:0:0: +230,245,58581,6,0,P|265:266|320:270,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +146,162,58897,2,0,P|108:169|76:189,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +293,188,59212,2,0,P|315:102|318:24,1,152.29000360183,2|8,0:2|0:2,0:0:0:0: +224,147,59686,1,0,0:0:0:0: +405,82,59844,6,0,P|407:124|415:170,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +500,268,60160,2,0,P|467:249|410:247,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +303,384,60476,2,0,B|401:376|349:337|442:328,1,152.29000360183,2|8,0:0|0:2,0:0:0:0: +311,298,60949,1,0,0:0:0:0: +143,368,61107,6,0,P|155:325|155:273,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +63,156,61423,2,0,P|65:193|76:230,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +160,367,61739,2,0,P|172:324|172:272,1,76.1450018009148,2|0,0:0|1:2,0:0:0:0: +80,155,62055,2,0,P|82:192|93:229,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +184,86,62370,6,0,B|260:109|205:146|318:171,1,152.29000360183,2|10,1:2|0:2,0:0:0:0: +406,65,62844,1,0,1:2:0:0: +473,202,63002,2,0,P|462:240|454:292,1,76.1450018009148,2|0,0:0|1:2,0:0:0:0: +331,146,63318,2,0,P|341:184|349:236,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +234,347,63633,1,2,1:2:0:0: +160,216,63791,6,0,P|202:198|234:200,1,76.1450018009148,2|8,0:0|0:2,0:0:0:0: +147,367,64107,2,0,P|109:366|75:350,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +35,213,64423,1,2,1:2:0:0: +148,349,64581,2,0,P|110:348|76:332,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +18,190,64897,5,2,1:2:0:0: +133,269,65054,2,0,P|143:231|150:180,1,76.1450018009148,2|8,0:0|0:2,0:0:0:0: +224,55,65370,2,0,P|231:127|249:214,1,152.29000360183,2|0,1:2|1:2,0:0:0:0: +367,345,65844,2,0,P|405:365|463:364,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +456,181,66160,6,0,P|439:219|428:272,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +310,127,66476,2,0,P|327:165|338:218,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +452,31,66791,2,0,P|435:69|424:122,1,76.1450018009148,2|0,0:0|1:2,0:0:0:0: +250,41,67107,2,0,P|267:79|278:132,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +143,235,67423,6,0,L|54:241,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +8,75,67739,2,0,L|97:81,1,76.1450018009148,4|0,1:2|0:0,0:0:0:0: +153,254,68054,2,0,L|-30:266,1,152.29000360183,4|8,1:2|0:2,0:0:0:0: +162,272,68686,6,0,P|153:306|149:343,2,68.2950005731545,6|2|10,1:2|0:0|0:2,0:0:0:0: +264,197,69160,1,2,1:2:0:0: +264,197,69318,2,0,B|339:217|287:248|378:266,1,136.590001146309,2|10,1:2|0:2,0:0:0:0: +477,162,69791,2,0,P|462:186|451:227,1,68.2950005731545,2|0,0:0|1:2,0:0:0:0: +352,127,70107,1,2,1:2:0:0: +352,127,70265,2,0,P|369:156|377:189,1,68.2950005731545,10|0,0:2|1:2,0:0:0:0: +252,75,70581,2,0,B|176:96|234:131|127:146,1,136.590001146309,2|8,1:2|0:2,0:0:0:0: +139,143,71212,6,0,P|125:177|114:231,1,68.2950005731545,2|0,1:2|0:0,0:0:0:0: +197,312,71528,1,10,0:2:0:0: +197,312,71686,1,2,1:2:0:0: +246,212,71844,2,0,P|281:197|322:197,1,68.2950005731545,2|2,1:2|0:0,0:0:0:0: +382,297,72160,1,10,0:2:0:0: +382,297,72318,2,0,P|395:222|417:157,1,136.590001146309,2|0,0:0|1:2,0:0:0:0: +483,40,72791,2,0,P|454:60|408:66,1,68.2950005731545,10|0,0:2|1:2,0:0:0:0: +316,8,73107,1,2,1:2:0:0: +316,8,73265,1,8,0:2:0:0: +213,106,73423,2,0,P|240:125|273:132,1,68.2950005731545,8|0,0:2|0:0,0:0:0:0: +151,36,73739,6,0,P|176:103|187:195,1,136.590001146309,2|10,1:2|0:2,0:0:0:0: +71,297,74212,1,2,1:2:0:0: +71,297,74370,2,0,P|96:230|107:138,1,136.590001146309,2|8,1:2|0:2,0:0:0:0: +217,308,74844,2,0,P|205:264|205:212,1,68.2950005731545,2|0,0:0|1:2,0:0:0:0: +292,129,75160,2,0,P|321:113|364:113,1,68.2950005731545,2|8,1:2|0:2,0:0:0:0: +470,226,75476,1,2,1:2:0:0: +470,226,75633,2,0,P|407:200|322:187,1,136.590001146309,2|10,1:2|0:2,0:0:0:0: +339,187,76265,6,0,P|351:221|357:255,1,68.2950005731545,2|2,1:2|0:0,0:0:0:0: +274,344,76581,1,10,0:2:0:0: +274,344,76739,1,2,1:2:0:0: +196,237,76897,2,0,P|183:277|174:332,1,68.2950005731545,2|0,1:2|0:0,0:0:0:0: +76,200,77212,2,0,P|89:240|98:295,1,68.2950005731545,10|0,0:2|0:0,0:0:0:0: +193,110,77528,6,0,P|225:91|266:91,1,68.2950005731545,2|2,1:2|0:0,0:0:0:0: +363,209,77844,2,0,P|329:205|300:187,1,68.2950005731545,2|2,1:2|0:0,0:0:0:0: +424,69,78160,2,0,P|392:129|373:223,1,136.590001146309,2|10,1:2|0:2,0:0:0:0: +375,195,78791,5,6,0:0:0:0: +59,101,87633,6,0,P|100:79|160:79,1,102.442500859732,2|0,1:2|0:0,0:0:0:0: +157,92,87949,2,0,P|106:92|61:115,1,102.442500859732,2|0,1:2|0:0,0:0:0:0: +65,127,88265,2,0,P|110:103|160:103,1,102.442500859732,2|0,1:2|0:0,0:0:0:0: +162,116,88581,1,6,0:2:0:0: +410,340,88897,6,0,P|428:292|428:236,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +329,109,89212,1,10,0:2:0:0: +237,283,89370,2,0,P|219:235|219:179,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +412,90,89686,2,0,P|407:131|391:170,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +224,11,90002,6,0,P|132:31|99:124,1,167.989994873352,2|2,0:2|0:2,0:0:0:0: +198,242,90476,1,8,0:2:0:0: +197,90,90633,1,2,0:2:0:0: +85,257,90791,2,0,P|94:304|99:355,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +308,229,91107,2,0,P|311:187|320:146,1,83.9949974366761,14|0,0:2|0:0,0:0:0:0: +210,341,91423,6,0,P|251:326|325:317,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +196,202,91739,1,10,0:2:0:0: +305,335,91897,2,0,P|346:350|420:359,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +212,222,92212,2,0,P|253:207|327:198,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +446,275,92528,6,0,P|480:177|483:88,1,167.989994873352,2|2,0:2|0:2,0:0:0:0: +286,70,93002,1,8,0:2:0:0: +368,232,93160,1,2,0:2:0:0: +268,50,93318,2,0,P|230:33|158:30,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +349,208,93633,2,0,P|310:225|269:230,1,83.9949974366761,14|0,0:2|0:0,0:0:0:0: +138,89,93949,6,0,P|116:133|104:208,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +148,304,94265,1,10,0:2:0:0: +22,167,94423,2,0,P|44:211|56:286,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +243,347,94739,2,0,P|254:306|273:269,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +438,109,95054,6,0,B|340:127|418:167|266:192,1,167.989994873352,6|0,0:2|0:0,0:0:0:0: +254,24,95528,2,0,P|277:62|282:122,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +427,285,95844,2,0,P|428:243|443:204,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +279,25,96160,2,0,P|302:63|307:123,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +225,237,96476,6,0,P|184:225|105:216,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +288,132,96791,1,10,0:2:0:0: +180,316,96949,2,0,P|139:328|60:337,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +274,159,97265,2,0,P|315:166|355:177,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +417,302,97581,1,2,0:2:0:0: +420,94,97739,6,0,P|393:134|376:202,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +346,384,98054,1,10,0:2:0:0: +299,208,98212,1,0,0:0:0:0: +337,355,98370,1,2,1:2:0:0: +290,179,98528,1,0,0:0:0:0: +170,364,98686,2,0,P|124:378|65:374,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +45,139,99002,6,0,P|70:172|96:263,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +164,51,99318,1,10,0:2:0:0: +146,275,99476,2,0,P|106:294|39:288,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +163,76,99791,2,0,P|204:78|242:96,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +306,272,100107,6,0,P|261:187|261:103,1,167.989994873352,6|2,0:2|0:2,0:0:0:0: +446,105,100581,1,8,0:2:0:0: +376,319,100739,2,0,P|345:348|305:358,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +236,147,101054,1,0,0:0:0:0: +402,242,101212,2,0,P|443:245|481:228,1,83.9949974366761,8|0,0:2|0:0,0:0:0:0: +334,39,101528,6,0,P|346:82|350:135,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +219,239,101844,1,10,0:2:0:0: +177,71,102002,2,0,P|137:51|71:45,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +140,267,102318,2,0,P|181:258|218:239,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +22,135,102633,6,0,P|64:254|68:317,1,167.989994873352,6|2,0:2|0:2,0:0:0:0: +182,139,103107,1,8,0:2:0:0: +200,320,103265,2,0,P|209:272|222:225,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +337,118,103581,1,0,0:0:0:0: +331,305,103739,2,0,P|322:257|309:210,1,83.9949974366761,8|0,0:2|0:0,0:0:0:0: +194,51,104054,6,0,B|300:74|225:123|355:155,1,167.989994873352,6|10,1:2|0:2,0:0:0:0: +142,226,104528,2,0,P|91:244|21:238,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +187,83,104844,2,0,P|148:67|106:63,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +210,283,105160,6,0,P|229:235|232:181,1,83.9949974366761,6|0,0:2|1:2,0:0:0:0: +339,35,105476,2,0,P|345:76|362:115,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +309,282,105791,1,2,0:2:0:0: +454,125,105949,2,0,P|437:163|431:204,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +246,91,106265,2,0,P|262:129|268:170,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +133,354,106581,6,0,L|22:361,1,83.9949974366761,6|2,1:2|0:2,0:0:0:0: +260,193,106897,2,0,L|371:200,1,83.9949974366761,10|2,0:2|0:2,0:0:0:0: +127,339,107212,2,0,L|16:346,1,83.9949974366761,2|2,1:2|0:2,0:0:0:0: +254,178,107528,2,0,L|365:185,1,83.9949974366761,10|2,0:2|0:2,0:0:0:0: +479,344,107844,5,2,1:2:0:0: +411,172,108002,1,10,0:2:0:0: +400,363,108160,1,2,1:2:0:0: +488,188,108318,1,2,0:2:0:0: +319,384,108476,2,0,L|312:273,1,83.9949974366761,10|10,0:2|0:2,0:0:0:0: +298,87,108791,1,2,1:2:0:0: +220,275,108949,1,10,0:2:0:0: +163,74,109107,5,14,0:2:0:0: +160,0,110212,5,14,0:2:0:0: +160,0,111002,6,0,P|188:57|194:109,1,90.6674982014809,14|0,0:2|0:0,0:0:0:0: +214,98,111318,2,0,P|191:137|182:176,1,60.4449988009873,14|0,0:2|1:2,0:0:0:0: +202,188,111554,1,0,1:2:0:0: +202,188,111633,1,6,1:2:0:0: +197,204,112739,5,14,0:2:0:0: +197,204,113528,2,0,P|242:224|311:221,1,90.6674982014809,14|0,0:0|0:0,0:0:0:0: +293,200,113844,2,0,P|333:181|366:180,1,60.4449988009873,14|0,0:0|1:0,0:0:0:0: +413,235,114081,5,0,1:0:0:0: +413,235,114160,2,0,P|420:193|433:153,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +328,286,114476,1,10,0:2:0:0: +388,95,114633,2,0,P|381:53|368:13,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +218,171,114949,2,0,P|225:129|238:89,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +114,267,115265,6,0,P|99:177|71:93,1,167.989994873352,6|0,0:0|0:0,0:0:0:0: +206,327,115739,2,0,P|174:359|99:370,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +247,175,116054,2,0,P|285:190|314:220,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +406,380,116370,2,0,P|411:328|422:274,1,83.9949974366761,8|0,0:0|0:0,0:0:0:0: +477,101,116686,6,0,P|432:104|382:131,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +286,270,117002,1,10,0:2:0:0: +210,82,117160,2,0,P|251:84|289:101,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +205,284,117476,2,0,P|220:236|227:166,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +80,62,117791,6,0,P|113:131|123:259,1,167.989994873352,2|2,0:0|0:0,0:0:0:0: +279,362,118265,5,10,0:2:0:0: +243,170,118423,1,2,0:0:0:0: +306,359,118581,5,2,1:2:0:0: +325,169,118739,1,2,0:0:0:0: +330,355,118897,5,8,0:2:0:0: +402,171,119054,1,10,0:2:0:0: +402,171,119528,6,0,B|239:156|377:58|170:31,1,266.507506303202,12|0,0:2|0:0,0:0:0:0: +184,44,120160,2,0,B|357:69|233:164|392:180,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +385,190,120791,2,0,B|227:174|351:79|178:54,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +171,64,121423,2,0,B|344:89|220:184|378:200,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +373,211,122054,2,0,B|214:194|338:99|165:74,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +156,90,122686,2,0,P|127:134|109:220,1,114.217502701372,12|0,0:0|0:0,0:0:0:0: +129,218,123002,6,0,P|144:261|158:324,1,76.1450018009148,0|10,1:2|0:2,0:0:0:0: +247,142,123318,1,8,0:2:0:0: +278,283,123475,1,2,1:2:0:0: +339,100,123633,1,8,0:2:0:0: +272,251,123791,1,8,0:2:0:0: +224,58,123949,1,8,0:2:0:0: +286,225,124107,1,8,0:2:0:0: +374,24,124265,6,0,P|414:9|473:9,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +368,190,124581,1,10,0:2:0:0: +222,28,124739,2,0,P|182:13|123:13,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +62,237,125054,2,0,P|82:187|90:129,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +261,271,125370,2,0,P|241:221|233:163,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +86,328,125686,2,0,P|37:328|-12:298,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +164,160,126002,1,2,0:0:0:0: +235,355,126160,2,0,P|276:356|315:341,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +454,180,126476,2,0,P|415:164|373:166,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +407,347,126791,6,0,L|399:240,1,83.9949974366761,6|2,1:2|0:2,0:0:0:0: +274,71,127107,2,0,L|267:154,1,83.9949974366761,14|2,0:2|0:0,0:0:0:0: +421,337,127423,2,0,L|413:230,1,83.9949974366761,6|2,1:2|0:0,0:0:0:0: +288,61,127739,2,0,L|281:144,1,83.9949974366761,14|2,0:2|0:0,0:0:0:0: +247,369,128054,5,2,1:2:0:0: +212,184,128212,1,10,0:2:0:0: +251,384,128370,1,10,0:2:0:0: +216,204,128528,1,2,0:0:0:0: +81,380,128686,2,0,L|87:296,1,83.9949974366761,10|10,0:2|0:2,0:0:0:0: +100,65,129002,1,2,1:2:0:0: +163,261,129160,1,10,0:2:0:0: +91,165,129318,5,4,0:2:0:0: +300,51,134370,5,6,1:2:0:0: +300,51,135633,5,4,1:2:0:0: +300,51,136897,6,0,P|260:72|200:81,1,102.442500859732,4|2,1:2|0:0,0:0:0:0: +200,72,137212,2,0,P|250:64|296:41,1,102.442500859732,4|0,1:2|0:0,0:0:0:0: +293,33,137528,2,0,P|247:55|196:63,1,102.442500859732,6|2,1:2|0:0,0:0:0:0: +193,44,137844,1,12,0:2:0:0: +337,298,138160,6,0,P|355:259|359:217,1,68.2950005731545,6|0,1:2|0:0,0:0:0:0: +277,157,138476,1,10,0:2:0:0: +355,302,138633,2,0,P|379:215|380:139,1,136.590001146309,2|2,1:2|1:2,0:0:0:0: +276,58,139107,1,10,0:2:0:0: +276,58,139265,1,2,1:2:0:0: +209,217,139423,6,0,P|170:235|128:239,1,68.2950005731545,6|0,1:2|0:0,0:0:0:0: +68,157,139739,1,10,0:2:0:0: +213,235,139896,2,0,P|126:259|50:260,1,136.590001146309,2|2,1:2|1:2,0:0:0:0: +207,118,140370,1,10,0:2:0:0: +207,118,140528,1,2,1:2:0:0: +308,272,140686,6,0,P|299:306|295:361,1,68.2950005731545,6|0,1:2|0:0,0:0:0:0: +421,220,141002,1,10,0:2:0:0: +293,252,141160,2,0,P|273:317|262:384,1,136.590001146309,2|2,1:2|1:2,0:0:0:0: +392,137,141633,1,10,0:2:0:0: +392,137,141791,1,2,1:2:0:0: +392,137,142265,6,0,P|384:93|322:62,1,102.442500859732,6|0,1:2|0:0,0:0:0:0: +326,79,142581,2,0,P|281:103|200:75,1,136.590001146309,6|12,1:2|0:2,0:0:0:0: +203,78,143212,5,6,1:2:0:0: +214,89,144476,5,4,1:2:0:0: +214,90,145739,6,0,P|249:146|260:207,1,102.442500859732,6|0,1:2|0:0,0:0:0:0: +248,201,146054,2,0,P|213:257|202:318,1,102.442500859732,6|0,1:2|0:0,0:0:0:0: +218,313,146370,2,0,P|265:294|316:291,1,102.442500859732,6|0,1:2|0:0,0:0:0:0: +326,305,146686,1,14,0:2:0:0: +440,83,147002,6,0,L|430:167,1,68.2950005731545,6|0,1:2|0:0,0:0:0:0: +346,18,147318,1,10,0:2:0:0: +457,94,147476,2,0,L|440:231,1,136.590001146309,2|2,1:2|1:2,0:0:0:0: +326,305,147949,1,10,0:2:0:0: +326,305,148107,1,2,1:2:0:0: +170,162,148265,6,0,L|180:246,1,68.2950005731545,6|0,1:2|0:0,0:0:0:0: +264,97,148581,1,10,0:2:0:0: +153,173,148739,2,0,L|170:310,1,136.590001146309,2|2,1:2|1:2,0:0:0:0: +284,384,149212,1,10,0:2:0:0: +284,384,149370,1,2,1:2:0:0: +403,159,149528,6,0,L|393:243,1,68.2950005731545,6|0,1:2|0:0,0:0:0:0: +309,94,149844,1,10,0:2:0:0: +420,170,150002,2,0,L|403:307,1,136.590001146309,2|2,1:2|1:2,0:0:0:0: +289,381,150475,1,10,0:2:0:0: +289,381,150633,1,2,1:2:0:0: +97,68,151107,6,0,P|140:48|196:63,1,102.442500859732,4|0,1:2|0:0,0:0:0:0: +198,79,151423,2,0,P|154:129|139:218,1,136.590001146309,4|12,1:2|0:0,0:0:0:0: +297,317,152054,6,0,B|391:288|336:242|424:215,1,152.29000360183,6|8,1:2|0:2,0:0:0:0: +281,212,152528,1,0,1:2:0:0: +446,306,152686,2,0,P|490:265|476:153,1,152.29000360183,2|8,0:0|0:2,0:0:0:0: +343,142,153160,1,2,1:2:0:0: +297,317,153318,6,0,P|226:345|155:276,1,152.29000360183,6|8,1:2|0:2,0:0:0:0: +116,157,153791,2,0,P|150:228|158:309,1,152.29000360183,0|0,1:2|1:2,0:0:0:0: +264,170,154265,2,0,P|244:206|235:263,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +152,77,154581,6,0,P|84:75|30:158,1,152.29000360183,6|8,1:2|0:2,0:0:0:0: +191,214,155054,1,0,1:2:0:0: +264,60,155212,2,0,P|331:58|385:141,1,152.29000360183,2|8,0:0|0:2,0:0:0:0: +212,171,155686,1,0,1:2:0:0: +405,112,155844,6,0,P|379:165|357:279,1,152.29000360183,6|8,1:2|0:2,0:0:0:0: +158,360,156318,2,0,P|142:285|111:216,1,152.29000360183,0|0,1:2|1:2,0:0:0:0: +9,64,156791,2,0,P|45:87|104:95,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +270,12,157107,6,0,P|187:35|179:115,1,152.29000360183,6|8,1:2|0:2,0:0:0:0: +288,228,157581,2,0,P|370:204|378:124,1,152.29000360183,0|0,1:2|1:2,0:0:0:0: +248,83,158054,2,0,P|280:97|327:97,1,76.1450018009148,10|0,0:2|1:2,0:0:0:0: +490,16,158370,6,0,P|451:77|433:186,1,152.29000360183,6|8,1:2|0:2,0:0:0:0: +467,312,158844,2,0,P|449:238|409:173,1,152.29000360183,0|0,1:2|1:2,0:0:0:0: +248,208,159318,2,0,P|292:207|331:188,1,76.1450018009148,8|0,0:2|1:2,0:0:0:0: +320,98,159633,5,2,0:0:0:0: +118,79,160897,6,0,L|127:219,1,114.217502701372,2|0,1:2|0:0,0:0:0:0: +146,197,161212,2,0,L|138:83,1,114.217502701372,2|0,1:2|0:0,0:0:0:0: +158,87,161528,2,0,L|166:201,1,114.217502701372,2|0,1:2|0:0,0:0:0:0: +185,203,161844,1,6,0:2:0:0: +39,359,162160,6,0,P|21:311|21:255,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +153,158,162475,1,10,0:2:0:0: +221,372,162633,2,0,P|239:324|239:268,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +64,135,162949,2,0,P|69:176|85:215,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +244,41,163265,6,0,P|336:61|369:154,1,167.989994873352,2|2,0:2|0:2,0:0:0:0: +322,264,163739,1,8,0:2:0:0: +282,124,163896,1,2,0:2:0:0: +419,289,164054,2,0,P|410:336|405:387,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +214,234,164370,2,0,P|211:192|202:151,1,83.9949974366761,14|0,0:2|0:0,0:0:0:0: +295,355,164686,6,0,P|254:340|180:331,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +305,196,165002,1,10,0:2:0:0: +209,350,165160,2,0,P|168:365|94:374,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +294,219,165475,2,0,P|253:204|179:195,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +66,275,165791,6,0,P|32:177|29:88,1,167.989994873352,2|2,0:2|0:2,0:0:0:0: +226,70,166265,1,8,0:2:0:0: +144,232,166423,1,2,0:2:0:0: +244,50,166581,2,0,P|282:33|354:30,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +163,208,166896,2,0,P|202:225|243:230,1,83.9949974366761,14|0,0:2|0:0,0:0:0:0: +374,89,167212,6,0,P|396:133|408:208,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +364,304,167528,1,10,0:2:0:0: +490,167,167686,2,0,P|468:211|456:286,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +269,347,168002,2,0,P|258:306|239:269,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +74,109,168317,6,0,B|172:127|94:167|246:192,1,167.989994873352,6|0,0:2|0:0,0:0:0:0: +258,24,168791,2,0,P|235:62|230:122,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +85,285,169107,2,0,P|84:243|69:204,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +233,25,169423,2,0,P|210:63|205:123,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +296,252,169739,6,0,P|337:240|416:231,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +224,132,170054,1,10,0:2:0:0: +331,331,170212,2,0,P|372:343|451:352,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +238,159,170528,2,0,P|197:166|157:177,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +95,302,170844,1,2,0:2:0:0: +92,94,171002,6,0,P|119:134|136:202,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +243,353,171317,1,10,0:2:0:0: +218,162,171475,1,0,0:0:0:0: +237,323,171633,1,2,1:2:0:0: +212,132,171791,1,0,0:0:0:0: +328,311,171949,2,0,P|372:330|433:321,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +447,131,172265,6,0,P|422:164|396:255,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +349,97,172581,1,10,0:2:0:0: +337,298,172739,2,0,P|381:317|442:308,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +335,81,173054,2,0,P|294:83|256:101,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +195,272,173370,6,0,P|240:187|240:103,1,167.989994873352,6|2,0:2|0:2,0:0:0:0: +66,105,173844,1,8,0:2:0:0: +125,318,174002,2,0,P|156:347|196:357,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +276,147,174317,1,0,0:0:0:0: +104,236,174475,2,0,P|63:239|25:222,1,83.9949974366761,8|0,0:2|0:0,0:0:0:0: +178,39,174791,6,0,P|166:82|162:135,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +293,239,175107,1,10,0:2:0:0: +335,71,175265,2,0,P|375:51|441:45,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +366,284,175581,2,0,P|325:275|288:256,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +490,135,175896,6,0,P|448:254|444:317,1,167.989994873352,6|2,0:2|0:2,0:0:0:0: +330,139,176370,1,8,0:2:0:0: +312,320,176528,2,0,P|303:272|290:225,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +175,118,176844,1,0,0:0:0:0: +181,305,177002,2,0,P|190:257|203:210,1,83.9949974366761,8|0,0:2|0:0,0:0:0:0: +318,51,177317,6,0,B|212:74|287:123|157:155,1,167.989994873352,6|10,1:2|0:2,0:0:0:0: +370,226,177791,2,0,P|421:244|491:238,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +325,83,178107,2,0,P|364:67|406:63,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +302,283,178423,6,0,P|283:235|280:181,1,83.9949974366761,6|0,0:2|1:2,0:0:0:0: +173,35,178739,2,0,P|167:76|150:115,1,83.9949974366761,2|8,0:2|0:2,0:0:0:0: +203,282,179054,1,2,0:2:0:0: +58,125,179212,2,0,P|75:163|81:204,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +266,91,179528,2,0,P|250:129|244:170,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +379,354,179844,6,0,L|490:361,1,83.9949974366761,6|2,1:2|0:2,0:0:0:0: +252,193,180160,2,0,L|141:200,1,83.9949974366761,10|2,0:2|0:2,0:0:0:0: +385,339,180475,2,0,L|496:346,1,83.9949974366761,2|2,1:2|0:2,0:0:0:0: +258,178,180791,2,0,L|147:185,1,83.9949974366761,10|2,0:2|0:2,0:0:0:0: +295,333,181107,5,2,1:2:0:0: +334,153,181265,1,10,0:2:0:0: +306,325,181423,1,2,1:2:0:0: +347,148,181581,1,2,0:2:0:0: +317,319,181739,2,0,L|324:208,1,83.9949974366761,10|10,0:2|0:2,0:0:0:0: +237,65,182054,1,2,1:2:0:0: +440,112,182212,1,10,0:2:0:0: +225,77,182370,5,14,0:2:0:0: +173,281,183476,5,14,0:2:0:0: +173,281,184265,6,0,L|263:276,1,90.6674982014809,14|0,0:2|0:0,0:0:0:0: +266,265,184581,2,0,L|183:268,1,60.4449988009873,14|0,0:2|1:2,0:0:0:0: +180,254,184818,1,0,1:2:0:0: +180,254,184897,1,6,1:2:0:0: +402,65,186002,5,14,0:2:0:0: +402,65,186791,6,0,L|311:60,1,90.6674982014809,14|0,0:0|0:0,0:0:0:0: +309,49,187107,2,0,L|397:54,1,60.4449988009873,14|0,0:0|1:0,0:0:0:0: +432,107,187344,5,0,1:0:0:0: +432,107,187423,2,0,P|420:151|413:220,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +460,324,187739,1,10,0:2:0:0: +270,233,187897,2,0,P|263:191|252:151,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +345,361,188212,2,0,P|351:319|362:279,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +223,129,188528,6,0,B|121:153|190:198|70:228,1,167.989994873352,6|0,0:0|0:0,0:0:0:0: +195,36,189002,2,0,P|245:36|304:61,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +315,225,189318,2,0,P|265:225|206:200,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +426,87,189633,2,0,P|406:131|393:207,1,83.9949974366761,8|0,0:0|0:0,0:0:0:0: +370,384,189949,6,0,P|344:289|302:200,1,167.989994873352,6|10,1:2|0:2,0:0:0:0: +190,82,190423,2,0,P|153:65|108:64,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +221,254,190739,2,0,P|262:253|300:236,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +189,116,191054,1,2,0:0:0:0: +378,11,191212,6,0,P|348:92|339:178,1,167.989994873352,2|10,1:2|0:2,0:0:0:0: +465,289,191686,1,2,0:0:0:0: +363,105,191844,2,0,P|354:153|356:219,1,83.9949974366761,2|2,1:2|0:0,0:0:0:0: +421,369,192160,1,8,0:2:0:0: +421,369,192318,1,10,0:2:0:0: +221,263,192791,6,0,B|384:248|246:150|453:123,1,266.507506303202,12|0,0:2|0:0,0:0:0:0: +439,136,193423,2,0,B|266:161|390:256|231:272,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +238,282,194054,2,0,B|396:266|272:171|445:146,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +452,156,194686,2,0,B|279:181|403:276|245:292,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +250,303,195317,2,0,B|409:286|285:191|458:166,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +461,188,195949,2,0,P|458:133|433:68,1,114.217502701372,12|0,0:0|0:0,0:0:0:0: +411,61,196265,6,0,P|376:85|317:90,1,76.1450018009148,0|10,1:2|0:2,0:0:0:0: +136,47,196581,1,8,0:2:0:0: +314,7,196739,1,2,1:2:0:0: +120,52,196897,1,8,0:2:0:0: +298,12,197055,1,8,0:2:0:0: +104,58,197212,2,0,P|96:101|91:167,1,76.1450018009148,8|8,0:2|0:2,0:0:0:0: +136,317,197528,6,0,P|205:285|247:284,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +384,371,197844,1,10,0:2:0:0: +317,207,198002,2,0,P|248:175|206:174,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +373,345,198318,2,0,P|413:334|448:311,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +436,127,198633,6,0,P|419:169|412:229,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +264,23,198949,2,0,P|281:65|288:125,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +242,254,199265,1,2,0:0:0:0: +414,124,199423,2,0,P|397:166|390:226,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +214,266,199739,2,0,P|206:224|190:186,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +38,39,200054,6,0,L|49:128,1,83.9949974366761,6|2,1:2|0:2,0:0:0:0: +86,302,200370,2,0,L|96:218,1,83.9949974366761,14|2,0:2|0:0,0:0:0:0: +48,34,200686,2,0,L|59:123,1,83.9949974366761,6|2,1:2|0:0,0:0:0:0: +96,297,201002,2,0,L|106:213,1,83.9949974366761,14|2,0:2|0:0,0:0:0:0: +223,68,201318,5,2,1:2:0:0: +211,238,201476,1,10,0:2:0:0: +239,61,201633,1,10,0:2:0:0: +227,231,201791,1,2,0:0:0:0: +255,52,201949,2,0,L|239:170,1,83.9949974366761,10|10,0:2|0:2,0:0:0:0: +218,340,202265,1,2,1:2:0:0: +309,179,202423,1,10,0:2:0:0: +328,301,202581,5,6,0:2:0:0: +459,23,203528,6,0,L|374:30,1,60.4449988009873,14|2,0:2|0:2,0:0:0:0: +305,177,203844,5,6,1:2:0:0: +305,177,204002,1,2,0:2:0:0: +264,26,204160,1,10,0:2:0:0: +264,26,204318,1,2,0:2:0:0: +210,186,204476,1,2,1:2:0:0: +210,186,204633,2,0,L|203:288,1,76.1450018009148,2|8,0:2|0:2,0:0:0:0: +62,159,204949,6,0,L|69:261,1,76.1450018009148,2|0,0:0|1:2,0:0:0:0: +192,357,205265,2,0,P|232:356|272:325,1,76.1450018009148,2|8,0:0|0:2,0:0:0:0: +398,216,205581,2,0,P|365:197|327:197,1,76.1450018009148,2|4,0:0|1:2,0:0:0:0: +407,341,205897,1,2,0:0:0:0: +493,184,206054,2,0,P|487:146|478:109,1,76.1450018009148,14|2,0:2|0:0,0:0:0:0: +311,23,206370,6,0,P|278:40|225:40,1,76.1450018009148,4|2,1:2|0:0,0:0:0:0: +76,13,206686,1,10,0:2:0:0: +76,13,206844,1,2,0:0:0:0: +186,145,207002,1,2,1:2:0:0: +186,145,207160,2,0,P|219:162|257:164,1,76.1450018009148,2|10,0:0|0:2,0:0:0:0: +102,30,207476,6,0,P|132:97|145:198,1,152.29000360183,2|0,0:0|0:0,0:0:0:0: +73,352,207949,1,8,0:2:0:0: +73,352,208107,1,0,0:0:0:0: +188,244,208265,1,6,1:2:0:0: +188,244,208423,2,0,P|245:224|279:232,1,76.1450018009148,2|14,0:0|0:2,0:0:0:0: +356,326,208739,1,2,0:0:0:0: +428,170,208897,6,0,P|450:206|462:261,1,76.1450018009148,6|2,1:2|0:0,0:0:0:0: +320,106,209212,1,10,0:2:0:0: +320,106,209370,1,2,0:0:0:0: +347,275,209528,1,2,1:2:0:0: +347,275,209686,1,2,0:0:0:0: +228,135,209844,1,10,0:2:0:0: +135,283,210002,6,0,B|142:192|95:232|109:126,1,152.29000360183,6|2,0:0|0:0,0:0:0:0: +226,12,210476,1,8,0:2:0:0: +226,12,210633,1,2,0:0:0:0: +188,167,210791,2,0,P|210:206|215:264,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +289,102,211107,1,10,0:2:0:0: +289,102,211265,1,2,0:0:0:0: +357,254,211423,6,0,P|335:293|330:351,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +320,177,211739,1,8,0:2:0:0: +420,337,211897,2,0,P|438:270|437:185,1,152.29000360183,2|2,0:0|0:0,0:0:0:0: +330,24,212370,1,8,0:2:0:0: +188,167,212528,6,0,P|186:242|205:316,1,152.29000360183,6|0,0:0|0:0,0:0:0:0: +89,221,213002,1,12,0:2:0:0: +89,221,213160,1,0,0:0:0:0: +205,316,213318,2,0,P|247:311|292:280,1,76.1450018009148,4|0,1:2|0:0,0:0:0:0: +355,148,213633,1,12,0:2:0:0: +355,148,213791,1,0,0:0:0:0: +377,310,213949,6,0,P|360:265|358:210,1,76.1450018009148,6|2,1:2|0:0,0:0:0:0: +229,84,214265,2,0,P|223:121|208:156,1,76.1450018009148,10|0,0:2|0:0,0:0:0:0: +109,231,214581,1,2,1:2:0:0: +109,231,214739,1,2,0:0:0:0: +176,22,214897,2,0,P|211:7|249:5,1,76.1450018009148,10|4,0:2|0:0,0:0:0:0: +343,176,215212,5,2,1:2:0:0: +343,176,215370,1,2,0:0:0:0: +304,15,215528,1,10,0:2:0:0: +304,15,215686,1,2,0:0:0:0: +425,197,215844,2,0,P|459:212|516:204,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +386,33,216160,2,0,P|348:32|313:47,1,76.1450018009148,8|0,0:2|0:0,0:0:0:0: +269,217,216476,6,0,P|303:301|320:394,1,152.29000360183,6|10,1:2|0:2,0:0:0:0: +343,178,216949,1,0,0:0:0:0: +192,259,217107,2,0,P|183:301|180:354,1,76.1450018009148,2|2,1:2|0:0,0:0:0:0: +73,212,217423,1,10,0:2:0:0: +73,212,217581,1,4,0:0:0:0: +197,75,217739,6,0,B|295:94|237:137|354:161,1,152.29000360183,2|8,1:2|0:2,0:0:0:0: +194,159,218212,1,0,0:0:0:0: +345,61,218370,2,0,P|394:48|452:48,1,76.1450018009148,6|0,1:2|0:0,0:0:0:0: +416,260,218686,2,0,P|378:255|341:245,1,76.1450018009148,14|0,0:2|0:0,0:0:0:0: +485,93,219002,6,0,P|451:161|435:252,1,152.29000360183,6|8,1:2|0:2,0:0:0:0: +339,360,219476,1,0,0:0:0:0: +374,147,219633,2,0,P|408:215|424:306,1,152.29000360183,0|10,1:2|0:2,0:0:0:0: +248,368,220107,1,6,0:0:0:0: +201,179,220265,5,2,1:2:0:0: +201,179,220423,1,2,0:0:0:0: +239,341,220581,1,10,0:2:0:0: +239,341,220739,1,2,0:0:0:0: +122,203,220897,2,0,P|88:189|38:184,1,76.1450018009148,2|0,1:2|0:0,0:0:0:0: +257,253,221212,2,0,P|294:247|329:233,1,76.1450018009148,8|0,0:2|0:0,0:0:0:0: +442,40,221528,6,0,L|434:149,1,76.1450018009148,6|2,1:2|0:0,0:0:0:0: +417,284,221844,2,0,L|411:208,1,76.1450018009148,10|2,0:2|0:0,0:0:0:0: +336,36,222160,2,0,L|328:145,1,76.1450018009148,2|2,1:2|0:0,0:0:0:0: +311,280,222476,2,0,L|305:204,1,76.1450018009148,10|2,0:2|0:0,0:0:0:0: +165,91,222791,5,2,1:2:0:0: +143,229,222949,1,10,0:2:0:0: +156,57,223107,1,10,0:2:0:0: +125,249,223265,1,2,0:0:0:0: +142,30,223423,2,0,L|67:25,1,76.1450018009148,2|10,1:2|0:2,0:0:0:0: +209,171,223739,1,2,1:2:0:0: +3,159,223897,1,10,0:2:0:0: +111,129,224054,5,6,0:2:0:0: +82,60,234160,5,2,1:2:0:0: +82,60,234476,5,2,1:2:0:0: +82,60,234791,5,2,1:2:0:0: +82,60,235107,5,6,0:2:0:0: +312,238,235423,6,0,P|360:258|414:258,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +262,105,235739,1,10,0:2:0:0: +170,284,235897,2,0,P|122:304|68:304,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +83,113,236212,2,0,P|101:157|110:208,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +258,40,236528,6,0,P|226:117|210:207,1,167.989994873352,2|2,0:0|0:0,0:0:0:0: +327,323,237002,1,10,0:2:0:0: +170,284,237160,1,2,0:0:0:0: +316,147,237318,2,0,P|361:132|413:134,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +417,319,237633,2,0,P|372:304|320:306,1,83.9949974366761,14|0,0:2|0:0,0:0:0:0: +153,376,237949,6,0,P|177:308|188:208,1,167.989994873352,6|10,1:2|0:2,0:0:0:0: +81,67,238423,2,0,P|85:113|102:165,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +277,190,238739,2,0,P|288:149|291:107,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +429,281,239054,6,0,P|412:222|402:108,1,167.989994873352,2|2,0:0|0:0,0:0:0:0: +252,12,239528,1,10,0:2:0:0: +383,93,239686,1,2,0:0:0:0: +224,0,239844,2,0,P|237:44|245:90,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +282,275,240160,2,0,P|294:234|301:193,1,83.9949974366761,14|0,0:2|0:0,0:0:0:0: +155,74,240476,6,0,P|110:54|58:51,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +177,214,240791,1,10,0:2:0:0: +285,27,240949,2,0,P|330:7|382:4,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +190,181,241265,2,0,P|145:161|93:158,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +350,91,241581,6,0,P|370:154|363:271,1,167.989994873352,6|0,0:0|0:0,0:0:0:0: +172,349,242054,2,0,P|212:328|267:318,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +94,180,242370,2,0,P|134:200|189:210,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +256,347,242686,2,0,P|215:357|177:376,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +291,209,243002,6,0,P|306:160|309:104,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +386,277,243318,1,10,0:2:0:0: +225,165,243476,2,0,P|210:116|207:60,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +406,36,243791,2,0,P|400:77|387:117,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +308,225,244107,1,2,0:0:0:0: +246,15,244265,6,0,P|196:10|149:27,1,83.9949974366761,2|2,1:2|0:0,0:0:0:0: +89,217,244581,1,10,0:2:0:0: +89,217,244739,1,2,0:0:0:0: +242,41,244897,2,0,P|192:36|145:53,1,83.9949974366761,2|2,1:2|0:0,0:0:0:0: +189,240,245212,1,10,0:2:0:0: +189,240,245370,1,2,0:0:0:0: +311,93,245528,6,0,P|355:75|401:75,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +400,292,245844,1,10,0:2:0:0: +250,154,246002,2,0,P|211:137|170:134,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +320,311,246318,2,0,P|361:308|399:291,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +488,108,246633,6,0,P|474:150|464:206,1,83.9949974366761,6|0,0:0|1:2,0:0:0:0: +314,323,246949,2,0,P|305:281|292:242,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +202,67,247265,2,0,P|163:54|67:145,1,167.989994873352,2|0,0:0|0:0,0:0:0:0: +190,256,247739,1,8,0:2:0:0: +200,100,247897,1,0,0:0:0:0: +188,283,248054,6,0,P|228:311|277:313,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +342,145,248370,1,10,0:2:0:0: +338,350,248528,2,0,P|359:307|368:260,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +290,80,248844,2,0,P|300:120|319:158,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +432,320,249160,6,0,P|453:277|462:230,1,83.9949974366761,6|0,0:0|1:2,0:0:0:0: +384,50,249476,2,0,P|394:90|413:128,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +449,329,249791,2,0,P|479:256|487:165,1,167.989994873352,2|0,0:0|0:0,0:0:0:0: +351,34,250265,1,12,0:2:0:0: +312,187,250423,1,0,0:0:0:0: +196,18,250581,6,0,P|215:60|224:126,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +161,257,250897,1,10,0:2:0:0: +88,110,251054,2,0,P|69:152|60:218,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +188,336,251370,2,0,P|178:295|161:257,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +206,65,251686,6,0,P|265:46|305:46,1,83.9949974366761,6|0,0:0|1:2,0:0:0:0: +381,245,252002,2,0,P|339:240|300:224,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +430,103,252318,1,2,0:0:0:0: +440,308,252476,2,0,P|463:261|466:209,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +349,86,252791,2,0,P|342:127|322:163,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +217,345,253107,5,6,1:2:0:0: +229,190,253265,1,2,0:2:0:0: +235,365,253423,1,10,0:2:0:0: +225,169,253581,2,0,P|187:144|119:129,1,83.9949974366761,2|2,0:2|1:2,0:0:0:0: +318,271,253897,1,2,0:2:0:0: +337,90,254054,1,10,0:2:0:0: +407,267,254212,5,2,0:2:0:0: +407,267,254291,1,2,0:2:0:0: +407,267,254370,2,0,L|419:155,1,83.9949974366761,2|10,1:2|0:2,0:0:0:0: +282,25,254686,1,10,0:2:0:0: +314,248,254844,2,0,L|302:136,1,83.9949974366761,2|10,0:2|0:2,0:0:0:0: +150,22,255160,1,10,0:2:0:0: +297,137,255318,1,2,1:2:0:0: +74,180,255476,1,10,0:2:0:0: +184,109,255633,5,6,0:0:0:0: +66,184,259423,6,0,P|114:169|135:169,1,60.4449988009873,6|0,1:2|1:2,0:0:0:0: +227,278,259739,2,0,P|254:289|284:293,1,60.4449988009873,0|0,1:2|1:2,0:0:0:0: +374,106,260054,1,6,1:2:0:0: +399,293,260212,1,2,1:2:0:0: +455,78,260370,1,8,0:2:0:0: +396,261,260528,1,8,0:2:0:0: +288,83,260686,6,0,P|242:58|191:51,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +83,215,261002,1,10,0:2:0:0: +120,39,261160,2,0,P|139:75|150:135,1,83.9949974366761,2|0,0:2|1:2,0:0:0:0: +168,286,261476,2,0,P|177:245|197:208,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +300,62,261791,6,0,B|402:90|337:130|449:151,1,167.989994873352,6|0,0:0|0:0,0:0:0:0: +319,285,262265,2,0,P|306:238|300:177,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +160,42,262581,2,0,P|153:83|142:123,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +297,272,262897,2,0,P|284:225|278:164,1,83.9949974366761,8|0,0:0|0:0,0:0:0:0: +430,55,263212,6,0,P|470:39|518:40,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +401,194,263528,1,10,0:2:0:0: +282,28,263686,2,0,P|242:12|194:13,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +124,200,264002,2,0,P|165:199|204:183,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +93,85,264318,1,2,0:0:0:0: +61,277,264476,6,0,P|72:313|77:364,1,83.9949974366761,2|2,1:2|0:0,0:0:0:0: +229,203,264791,2,0,P|217:239|213:290,1,83.9949974366761,10|2,0:2|0:0,0:0:0:0: +358,126,265107,2,0,P|369:162|374:213,1,83.9949974366761,2|2,1:2|0:0,0:0:0:0: +470,69,265423,1,8,0:2:0:0: +470,69,265581,1,10,0:2:0:0: +149,40,266054,6,0,P|184:78|242:292,1,266.507506303202,12|0,0:2|0:0,0:0:0:0: +253,277,266686,2,0,P|233:146|158:37,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +168,33,267318,2,0,P|203:71|261:285,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +272,270,267949,2,0,P|252:139|177:30,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +187,23,268581,2,0,P|262:131|281:262,1,266.507506303202,12|0,0:0|0:0,0:0:0:0: +294,261,269212,2,0,P|303:193|327:142,1,114.217502701372,12|0,0:0|0:0,0:0:0:0: +340,145,269528,6,0,P|363:175|378:212,1,76.1450018009148,0|10,1:2|0:2,0:0:0:0: +447,373,269844,1,8,0:2:0:0: +465,198,270002,1,2,1:2:0:0: +450,358,270160,1,8,0:2:0:0: +468,183,270318,1,8,0:2:0:0: +344,367,270476,2,0,P|303:380|248:380,1,76.1450018009148,8|8,0:2|0:2,0:0:0:0: +146,242,270791,6,0,P|129:193|126:127,1,83.9949974366761,6|0,1:2|0:0,0:0:0:0: +264,74,271107,1,10,0:2:0:0: +218,287,271265,2,0,P|181:318|119:326,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +245,153,271581,2,0,P|284:165|315:193,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +349,382,271897,6,0,P|337:335|337:292,1,83.9949974366761,2|0,0:0|1:2,0:0:0:0: +446,128,272212,2,0,P|444:169|433:210,1,83.9949974366761,2|8,0:0|0:2,0:0:0:0: +324,72,272528,1,2,0:0:0:0: +415,294,272686,2,0,P|464:289|506:260,1,83.9949974366761,2|0,1:2|0:0,0:0:0:0: +349,149,273002,2,0,P|307:151|270:170,1,83.9949974366761,10|0,0:2|0:0,0:0:0:0: +148,303,273318,6,0,P|129:259|127:210,1,83.9949974366761,6|2,1:2|0:2,0:0:0:0: +199,70,273633,1,14,0:2:0:0: +247,249,273791,2,0,P|266:205|268:156,1,83.9949974366761,2|6,0:0|1:2,0:0:0:0: +242,3,274107,1,2,0:0:0:0: +143,195,274265,2,0,P|124:151|122:102,1,83.9949974366761,14|2,0:2|0:0,0:0:0:0: +272,13,274581,6,0,L|385:20,1,83.9949974366761,2|10,1:2|0:2,0:0:0:0: +488,195,274897,2,0,L|375:202,1,83.9949974366761,10|2,0:2|0:0,0:0:0:0: +285,37,275212,1,10,0:2:0:0: +315,233,275370,1,10,0:2:0:0: +283,20,275528,1,2,1:2:0:0: +313,216,275686,1,10,0:2:0:0: +254,127,275844,5,6,0:2:0:0: +71,80,278370,6,0,B|118:74|166:40|166:40|130:88,1,125.992496155014,12|0,0:0|0:0,0:0:0:0: +256,61,278686,2,0,B|242:43|242:43|291:77|337:83,1,125.992496155014,8|0,0:0|0:0,0:0:0:0: +351,186,279002,2,0,B|297:179|242:141|242:141|261:165,1,149.542498859081,12|0,0:0|0:0,0:0:0:0: +149,163,279318,2,0,B|167:138|167:138|112:176|59:183,1,149.542498859081,8|0,0:0|0:0,0:0:0:0: +205,229,279633,5,14,0:2:0:0: +480,25,280265,6,0,B|160:57|384:313|32:345,1,580.900014182129,6|0,1:2|0:0,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2571731-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2571731-expected-conversion.json new file mode 100644 index 0000000000..1817ef4742 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2571731-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":1254.0,"Objects":[{"StartTime":1254.0,"Position":229.0,"HyperDash":false},{"StartTime":1332.0,"Position":187.422012,"HyperDash":false},{"StartTime":1410.0,"Position":169.115814,"HyperDash":false},{"StartTime":1488.0,"Position":162.374466,"HyperDash":false},{"StartTime":1566.0,"Position":160.452332,"HyperDash":false},{"StartTime":1635.0,"Position":181.787521,"HyperDash":false},{"StartTime":1704.0,"Position":177.835266,"HyperDash":false},{"StartTime":1773.0,"Position":194.2059,"HyperDash":false},{"StartTime":1878.0,"Position":230.367538,"HyperDash":false}]},{"StartTime":2191.0,"Objects":[{"StartTime":2191.0,"Position":470.0,"HyperDash":false}]},{"StartTime":2504.0,"Objects":[{"StartTime":2504.0,"Position":228.0,"HyperDash":false},{"StartTime":2573.0,"Position":188.673691,"HyperDash":false},{"StartTime":2642.0,"Position":171.347382,"HyperDash":false},{"StartTime":2711.0,"Position":143.021057,"HyperDash":false},{"StartTime":2816.0,"Position":118.002762,"HyperDash":false}]},{"StartTime":3129.0,"Objects":[{"StartTime":3129.0,"Position":231.0,"HyperDash":false},{"StartTime":3198.0,"Position":251.326309,"HyperDash":false},{"StartTime":3267.0,"Position":293.652618,"HyperDash":false},{"StartTime":3336.0,"Position":307.978943,"HyperDash":false},{"StartTime":3441.0,"Position":340.997253,"HyperDash":false}]},{"StartTime":3754.0,"Objects":[{"StartTime":3754.0,"Position":465.0,"HyperDash":false},{"StartTime":3832.0,"Position":458.7602,"HyperDash":false},{"StartTime":3910.0,"Position":469.2429,"HyperDash":false},{"StartTime":3988.0,"Position":435.549255,"HyperDash":false},{"StartTime":4066.0,"Position":439.174347,"HyperDash":false},{"StartTime":4135.0,"Position":439.5499,"HyperDash":false},{"StartTime":4204.0,"Position":417.896637,"HyperDash":false},{"StartTime":4273.0,"Position":391.087067,"HyperDash":false},{"StartTime":4379.0,"Position":341.072845,"HyperDash":false}]},{"StartTime":4691.0,"Objects":[{"StartTime":4691.0,"Position":131.0,"HyperDash":false}]},{"StartTime":5004.0,"Objects":[{"StartTime":5004.0,"Position":365.0,"HyperDash":false},{"StartTime":5073.0,"Position":357.3771,"HyperDash":false},{"StartTime":5142.0,"Position":348.754242,"HyperDash":false},{"StartTime":5211.0,"Position":379.131348,"HyperDash":false},{"StartTime":5316.0,"Position":366.705231,"HyperDash":false}]},{"StartTime":5629.0,"Objects":[{"StartTime":5629.0,"Position":228.0,"HyperDash":false},{"StartTime":5698.0,"Position":247.324,"HyperDash":false},{"StartTime":5767.0,"Position":280.648,"HyperDash":false},{"StartTime":5836.0,"Position":285.972,"HyperDash":false},{"StartTime":5941.0,"Position":337.9868,"HyperDash":false}]},{"StartTime":6254.0,"Objects":[{"StartTime":6254.0,"Position":197.0,"HyperDash":false},{"StartTime":6332.0,"Position":165.6015,"HyperDash":false},{"StartTime":6410.0,"Position":141.904709,"HyperDash":false},{"StartTime":6488.0,"Position":132.042267,"HyperDash":false},{"StartTime":6566.0,"Position":96.16125,"HyperDash":false},{"StartTime":6635.0,"Position":60.9876633,"HyperDash":false},{"StartTime":6704.0,"Position":51.4006424,"HyperDash":false},{"StartTime":6773.0,"Position":71.82723,"HyperDash":false},{"StartTime":6879.0,"Position":54.095974,"HyperDash":false}]},{"StartTime":7191.0,"Objects":[{"StartTime":7191.0,"Position":283.0,"HyperDash":false}]},{"StartTime":7504.0,"Objects":[{"StartTime":7504.0,"Position":290.0,"HyperDash":false},{"StartTime":7573.0,"Position":310.169,"HyperDash":false},{"StartTime":7642.0,"Position":295.338,"HyperDash":false},{"StartTime":7711.0,"Position":292.507019,"HyperDash":false},{"StartTime":7816.0,"Position":304.3294,"HyperDash":false}]},{"StartTime":8129.0,"Objects":[{"StartTime":8129.0,"Position":48.0,"HyperDash":false}]},{"StartTime":8441.0,"Objects":[{"StartTime":8441.0,"Position":308.0,"HyperDash":false}]},{"StartTime":8754.0,"Objects":[{"StartTime":8754.0,"Position":168.0,"HyperDash":false},{"StartTime":8823.0,"Position":142.687775,"HyperDash":false},{"StartTime":8892.0,"Position":138.375549,"HyperDash":false},{"StartTime":8961.0,"Position":89.06334,"HyperDash":false},{"StartTime":9066.0,"Position":58.0664749,"HyperDash":false}]},{"StartTime":9379.0,"Objects":[{"StartTime":9379.0,"Position":226.0,"HyperDash":false},{"StartTime":9448.0,"Position":255.312714,"HyperDash":false},{"StartTime":9517.0,"Position":287.625427,"HyperDash":false},{"StartTime":9586.0,"Position":315.938171,"HyperDash":false},{"StartTime":9691.0,"Position":335.9358,"HyperDash":false}]},{"StartTime":10004.0,"Objects":[{"StartTime":10004.0,"Position":477.0,"HyperDash":false},{"StartTime":10062.0,"Position":43.0,"HyperDash":false},{"StartTime":10121.0,"Position":494.0,"HyperDash":false},{"StartTime":10179.0,"Position":135.0,"HyperDash":false},{"StartTime":10238.0,"Position":30.0,"HyperDash":false},{"StartTime":10296.0,"Position":11.0,"HyperDash":false},{"StartTime":10355.0,"Position":239.0,"HyperDash":false},{"StartTime":10413.0,"Position":505.0,"HyperDash":false},{"StartTime":10472.0,"Position":353.0,"HyperDash":false},{"StartTime":10531.0,"Position":136.0,"HyperDash":false},{"StartTime":10589.0,"Position":135.0,"HyperDash":false},{"StartTime":10648.0,"Position":346.0,"HyperDash":false},{"StartTime":10706.0,"Position":39.0,"HyperDash":false},{"StartTime":10765.0,"Position":300.0,"HyperDash":false},{"StartTime":10823.0,"Position":398.0,"HyperDash":false},{"StartTime":10882.0,"Position":151.0,"HyperDash":false},{"StartTime":10941.0,"Position":73.0,"HyperDash":false}]},{"StartTime":11254.0,"Objects":[{"StartTime":11254.0,"Position":173.0,"HyperDash":false},{"StartTime":11332.0,"Position":151.138428,"HyperDash":false},{"StartTime":11410.0,"Position":132.025146,"HyperDash":false},{"StartTime":11488.0,"Position":91.37633,"HyperDash":false},{"StartTime":11566.0,"Position":80.290535,"HyperDash":false},{"StartTime":11635.0,"Position":61.6581879,"HyperDash":false},{"StartTime":11704.0,"Position":80.94798,"HyperDash":false},{"StartTime":11773.0,"Position":108.710762,"HyperDash":false},{"StartTime":11879.0,"Position":120.303291,"HyperDash":false}]},{"StartTime":12191.0,"Objects":[{"StartTime":12191.0,"Position":348.0,"HyperDash":false}]},{"StartTime":12504.0,"Objects":[{"StartTime":12504.0,"Position":119.0,"HyperDash":false},{"StartTime":12573.0,"Position":113.579384,"HyperDash":false},{"StartTime":12642.0,"Position":114.15876,"HyperDash":false},{"StartTime":12711.0,"Position":93.7381439,"HyperDash":false},{"StartTime":12816.0,"Position":108.054588,"HyperDash":false}]},{"StartTime":13129.0,"Objects":[{"StartTime":13129.0,"Position":246.0,"HyperDash":false},{"StartTime":13198.0,"Position":207.673676,"HyperDash":false},{"StartTime":13267.0,"Position":196.347351,"HyperDash":false},{"StartTime":13336.0,"Position":190.021027,"HyperDash":false},{"StartTime":13441.0,"Position":136.002686,"HyperDash":false}]},{"StartTime":13754.0,"Objects":[{"StartTime":13754.0,"Position":290.0,"HyperDash":false},{"StartTime":13832.0,"Position":327.611237,"HyperDash":false},{"StartTime":13910.0,"Position":353.671875,"HyperDash":false},{"StartTime":13988.0,"Position":363.8803,"HyperDash":false},{"StartTime":14066.0,"Position":380.478455,"HyperDash":false},{"StartTime":14135.0,"Position":374.964325,"HyperDash":false},{"StartTime":14204.0,"Position":392.930969,"HyperDash":false},{"StartTime":14273.0,"Position":350.32254,"HyperDash":false},{"StartTime":14379.0,"Position":329.753876,"HyperDash":false}]},{"StartTime":14691.0,"Objects":[{"StartTime":14691.0,"Position":80.0,"HyperDash":false}]},{"StartTime":15004.0,"Objects":[{"StartTime":15004.0,"Position":335.0,"HyperDash":false},{"StartTime":15082.0,"Position":297.345367,"HyperDash":false},{"StartTime":15160.0,"Position":285.690735,"HyperDash":false},{"StartTime":15238.0,"Position":243.036133,"HyperDash":false},{"StartTime":15316.0,"Position":228.3815,"HyperDash":false},{"StartTime":15385.0,"Position":192.802429,"HyperDash":false},{"StartTime":15454.0,"Position":180.223328,"HyperDash":false},{"StartTime":15523.0,"Position":144.644241,"HyperDash":false},{"StartTime":15628.0,"Position":121.763031,"HyperDash":false}]},{"StartTime":15941.0,"Objects":[{"StartTime":15941.0,"Position":475.0,"HyperDash":false}]},{"StartTime":16254.0,"Objects":[{"StartTime":16254.0,"Position":120.0,"HyperDash":false},{"StartTime":16332.0,"Position":112.4255,"HyperDash":false},{"StartTime":16410.0,"Position":115.254448,"HyperDash":false},{"StartTime":16488.0,"Position":125.264832,"HyperDash":false},{"StartTime":16566.0,"Position":150.934219,"HyperDash":false},{"StartTime":16635.0,"Position":164.5551,"HyperDash":false},{"StartTime":16704.0,"Position":205.001038,"HyperDash":false},{"StartTime":16773.0,"Position":217.329178,"HyperDash":false},{"StartTime":16879.0,"Position":251.47258,"HyperDash":false}]},{"StartTime":17191.0,"Objects":[{"StartTime":17191.0,"Position":405.0,"HyperDash":false}]},{"StartTime":17504.0,"Objects":[{"StartTime":17504.0,"Position":250.0,"HyperDash":false},{"StartTime":17582.0,"Position":210.776382,"HyperDash":false},{"StartTime":17660.0,"Position":183.552765,"HyperDash":false},{"StartTime":17738.0,"Position":153.329163,"HyperDash":false},{"StartTime":17816.0,"Position":141.10556,"HyperDash":false},{"StartTime":17885.0,"Position":161.187988,"HyperDash":false},{"StartTime":17954.0,"Position":192.2704,"HyperDash":false},{"StartTime":18023.0,"Position":194.352844,"HyperDash":false},{"StartTime":18128.0,"Position":250.0,"HyperDash":false}]},{"StartTime":18441.0,"Objects":[{"StartTime":18441.0,"Position":403.0,"HyperDash":false}]},{"StartTime":18754.0,"Objects":[{"StartTime":18754.0,"Position":250.0,"HyperDash":false},{"StartTime":18832.0,"Position":222.847168,"HyperDash":false},{"StartTime":18910.0,"Position":212.124878,"HyperDash":false},{"StartTime":18988.0,"Position":180.067841,"HyperDash":false},{"StartTime":19066.0,"Position":177.484818,"HyperDash":false},{"StartTime":19135.0,"Position":173.0804,"HyperDash":false},{"StartTime":19204.0,"Position":184.026764,"HyperDash":false},{"StartTime":19273.0,"Position":200.533371,"HyperDash":false},{"StartTime":19378.0,"Position":250.553848,"HyperDash":false}]},{"StartTime":19691.0,"Objects":[{"StartTime":19691.0,"Position":404.0,"HyperDash":false}]},{"StartTime":20004.0,"Objects":[{"StartTime":20004.0,"Position":249.0,"HyperDash":false}]},{"StartTime":20316.0,"Objects":[{"StartTime":20316.0,"Position":241.0,"HyperDash":false}]},{"StartTime":20629.0,"Objects":[{"StartTime":20629.0,"Position":239.0,"HyperDash":false}]},{"StartTime":20941.0,"Objects":[{"StartTime":20941.0,"Position":399.0,"HyperDash":false}]},{"StartTime":21254.0,"Objects":[{"StartTime":21254.0,"Position":240.0,"HyperDash":false},{"StartTime":21332.0,"Position":230.589066,"HyperDash":false},{"StartTime":21410.0,"Position":196.0483,"HyperDash":false},{"StartTime":21488.0,"Position":180.546143,"HyperDash":false},{"StartTime":21566.0,"Position":140.131409,"HyperDash":false},{"StartTime":21635.0,"Position":118.503845,"HyperDash":false},{"StartTime":21704.0,"Position":118.550331,"HyperDash":false},{"StartTime":21773.0,"Position":116.676407,"HyperDash":false},{"StartTime":21878.0,"Position":101.092834,"HyperDash":false}]},{"StartTime":22191.0,"Objects":[{"StartTime":22191.0,"Position":372.0,"HyperDash":false}]},{"StartTime":22504.0,"Objects":[{"StartTime":22504.0,"Position":386.0,"HyperDash":false},{"StartTime":22573.0,"Position":375.75766,"HyperDash":false},{"StartTime":22642.0,"Position":377.51532,"HyperDash":false},{"StartTime":22711.0,"Position":375.273,"HyperDash":false},{"StartTime":22816.0,"Position":398.469452,"HyperDash":false}]},{"StartTime":23129.0,"Objects":[{"StartTime":23129.0,"Position":264.0,"HyperDash":false},{"StartTime":23198.0,"Position":242.675385,"HyperDash":false},{"StartTime":23267.0,"Position":201.350769,"HyperDash":false},{"StartTime":23336.0,"Position":197.026169,"HyperDash":false},{"StartTime":23441.0,"Position":154.010468,"HyperDash":false}]},{"StartTime":23754.0,"Objects":[{"StartTime":23754.0,"Position":292.0,"HyperDash":false},{"StartTime":23832.0,"Position":331.63,"HyperDash":false},{"StartTime":23910.0,"Position":331.0578,"HyperDash":false},{"StartTime":23988.0,"Position":348.8628,"HyperDash":false},{"StartTime":24066.0,"Position":360.9073,"HyperDash":false},{"StartTime":24135.0,"Position":365.5993,"HyperDash":false},{"StartTime":24204.0,"Position":351.536926,"HyperDash":false},{"StartTime":24273.0,"Position":324.124176,"HyperDash":false},{"StartTime":24378.0,"Position":290.904083,"HyperDash":false}]},{"StartTime":24691.0,"Objects":[{"StartTime":24691.0,"Position":24.0,"HyperDash":false}]},{"StartTime":25004.0,"Objects":[{"StartTime":25004.0,"Position":285.0,"HyperDash":false},{"StartTime":25082.0,"Position":313.548859,"HyperDash":false},{"StartTime":25160.0,"Position":324.0977,"HyperDash":false},{"StartTime":25238.0,"Position":345.646545,"HyperDash":false},{"StartTime":25316.0,"Position":375.195374,"HyperDash":false},{"StartTime":25385.0,"Position":374.248322,"HyperDash":false},{"StartTime":25454.0,"Position":330.30127,"HyperDash":false},{"StartTime":25523.0,"Position":315.354218,"HyperDash":false},{"StartTime":25628.0,"Position":285.0,"HyperDash":false}]},{"StartTime":25941.0,"Objects":[{"StartTime":25941.0,"Position":465.0,"HyperDash":false}]},{"StartTime":26254.0,"Objects":[{"StartTime":26254.0,"Position":284.0,"HyperDash":false},{"StartTime":26332.0,"Position":297.848755,"HyperDash":false},{"StartTime":26410.0,"Position":271.5647,"HyperDash":false},{"StartTime":26488.0,"Position":314.9722,"HyperDash":false},{"StartTime":26566.0,"Position":313.667419,"HyperDash":false},{"StartTime":26635.0,"Position":327.281128,"HyperDash":false},{"StartTime":26704.0,"Position":359.75528,"HyperDash":false},{"StartTime":26773.0,"Position":373.334259,"HyperDash":false},{"StartTime":26879.0,"Position":411.1939,"HyperDash":false}]},{"StartTime":27191.0,"Objects":[{"StartTime":27191.0,"Position":108.0,"HyperDash":false}]},{"StartTime":27504.0,"Objects":[{"StartTime":27504.0,"Position":124.0,"HyperDash":false},{"StartTime":27573.0,"Position":136.610931,"HyperDash":false},{"StartTime":27642.0,"Position":107.221848,"HyperDash":false},{"StartTime":27711.0,"Position":100.832764,"HyperDash":false},{"StartTime":27816.0,"Position":113.197212,"HyperDash":false}]},{"StartTime":28129.0,"Objects":[{"StartTime":28129.0,"Position":250.0,"HyperDash":false},{"StartTime":28198.0,"Position":219.04863,"HyperDash":false},{"StartTime":28267.0,"Position":184.097244,"HyperDash":false},{"StartTime":28336.0,"Position":174.145874,"HyperDash":false},{"StartTime":28441.0,"Position":141.69812,"HyperDash":false}]},{"StartTime":28754.0,"Objects":[{"StartTime":28754.0,"Position":284.0,"HyperDash":false},{"StartTime":28832.0,"Position":316.245941,"HyperDash":false},{"StartTime":28910.0,"Position":338.3189,"HyperDash":false},{"StartTime":28988.0,"Position":359.1247,"HyperDash":false},{"StartTime":29066.0,"Position":381.5727,"HyperDash":false},{"StartTime":29135.0,"Position":393.2293,"HyperDash":false},{"StartTime":29204.0,"Position":421.124542,"HyperDash":false},{"StartTime":29273.0,"Position":400.894043,"HyperDash":false},{"StartTime":29379.0,"Position":415.7917,"HyperDash":false}]},{"StartTime":29691.0,"Objects":[{"StartTime":29691.0,"Position":135.0,"HyperDash":false}]},{"StartTime":30004.0,"Objects":[{"StartTime":30004.0,"Position":416.0,"HyperDash":false}]},{"StartTime":30316.0,"Objects":[{"StartTime":30316.0,"Position":456.0,"HyperDash":false}]},{"StartTime":30629.0,"Objects":[{"StartTime":30629.0,"Position":294.0,"HyperDash":false},{"StartTime":30698.0,"Position":268.673065,"HyperDash":false},{"StartTime":30767.0,"Position":233.346161,"HyperDash":false},{"StartTime":30836.0,"Position":224.019226,"HyperDash":false},{"StartTime":30941.0,"Position":184.0,"HyperDash":false}]},{"StartTime":31254.0,"Objects":[{"StartTime":31254.0,"Position":351.0,"HyperDash":false},{"StartTime":31332.0,"Position":388.3603,"HyperDash":false},{"StartTime":31410.0,"Position":400.4129,"HyperDash":false},{"StartTime":31488.0,"Position":406.2265,"HyperDash":false},{"StartTime":31566.0,"Position":441.396118,"HyperDash":false},{"StartTime":31635.0,"Position":452.909637,"HyperDash":false},{"StartTime":31704.0,"Position":447.317657,"HyperDash":false},{"StartTime":31773.0,"Position":421.671265,"HyperDash":false},{"StartTime":31878.0,"Position":416.158752,"HyperDash":false}]},{"StartTime":32191.0,"Objects":[{"StartTime":32191.0,"Position":149.0,"HyperDash":false}]},{"StartTime":32504.0,"Objects":[{"StartTime":32504.0,"Position":144.0,"HyperDash":false},{"StartTime":32582.0,"Position":152.122482,"HyperDash":false},{"StartTime":32660.0,"Position":187.244965,"HyperDash":false},{"StartTime":32738.0,"Position":234.367462,"HyperDash":false},{"StartTime":32816.0,"Position":252.489944,"HyperDash":false},{"StartTime":32885.0,"Position":292.4829,"HyperDash":false},{"StartTime":32954.0,"Position":314.4759,"HyperDash":false},{"StartTime":33023.0,"Position":317.468872,"HyperDash":false},{"StartTime":33129.0,"Position":361.327637,"HyperDash":false}]},{"StartTime":33440.0,"Objects":[{"StartTime":33440.0,"Position":201.0,"HyperDash":false}]},{"StartTime":33597.0,"Objects":[{"StartTime":33597.0,"Position":140.0,"HyperDash":false},{"StartTime":33675.0,"Position":97.84354,"HyperDash":false},{"StartTime":33753.0,"Position":83.79872,"HyperDash":false},{"StartTime":33831.0,"Position":111.743484,"HyperDash":false},{"StartTime":33909.0,"Position":105.502647,"HyperDash":false},{"StartTime":33969.0,"Position":122.537575,"HyperDash":false},{"StartTime":34065.0,"Position":148.435272,"HyperDash":false}]},{"StartTime":34379.0,"Objects":[{"StartTime":34379.0,"Position":239.0,"HyperDash":false},{"StartTime":34448.0,"Position":211.884613,"HyperDash":false},{"StartTime":34517.0,"Position":177.769226,"HyperDash":false},{"StartTime":34586.0,"Position":165.653839,"HyperDash":false},{"StartTime":34691.0,"Position":129.956512,"HyperDash":false}]},{"StartTime":35004.0,"Objects":[{"StartTime":35004.0,"Position":264.0,"HyperDash":false},{"StartTime":35073.0,"Position":298.150146,"HyperDash":false},{"StartTime":35142.0,"Position":294.300323,"HyperDash":false},{"StartTime":35211.0,"Position":332.45047,"HyperDash":false},{"StartTime":35316.0,"Position":373.2007,"HyperDash":false}]},{"StartTime":35629.0,"Objects":[{"StartTime":35629.0,"Position":223.0,"HyperDash":false},{"StartTime":35698.0,"Position":205.019867,"HyperDash":false},{"StartTime":35767.0,"Position":232.039749,"HyperDash":false},{"StartTime":35836.0,"Position":201.059616,"HyperDash":false},{"StartTime":35941.0,"Position":218.568115,"HyperDash":false}]},{"StartTime":36254.0,"Objects":[{"StartTime":36254.0,"Position":379.0,"HyperDash":false}]},{"StartTime":37035.0,"Objects":[{"StartTime":37035.0,"Position":398.0,"HyperDash":false},{"StartTime":37134.0,"Position":416.853973,"HyperDash":false},{"StartTime":37269.0,"Position":402.3821,"HyperDash":false}]},{"StartTime":37504.0,"Objects":[{"StartTime":37504.0,"Position":284.0,"HyperDash":false},{"StartTime":37573.0,"Position":268.747223,"HyperDash":false},{"StartTime":37642.0,"Position":235.494461,"HyperDash":false},{"StartTime":37711.0,"Position":210.2417,"HyperDash":false},{"StartTime":37816.0,"Position":174.335327,"HyperDash":false}]},{"StartTime":38129.0,"Objects":[{"StartTime":38129.0,"Position":305.0,"HyperDash":false},{"StartTime":38198.0,"Position":341.240051,"HyperDash":false},{"StartTime":38267.0,"Position":355.480072,"HyperDash":false},{"StartTime":38336.0,"Position":378.7201,"HyperDash":false},{"StartTime":38441.0,"Position":414.607117,"HyperDash":false}]},{"StartTime":38597.0,"Objects":[{"StartTime":38597.0,"Position":415.0,"HyperDash":false},{"StartTime":38675.0,"Position":374.605652,"HyperDash":false},{"StartTime":38753.0,"Position":363.2113,"HyperDash":false},{"StartTime":38831.0,"Position":320.816956,"HyperDash":false},{"StartTime":38909.0,"Position":305.4226,"HyperDash":false},{"StartTime":38969.0,"Position":265.350037,"HyperDash":false},{"StartTime":39065.0,"Position":250.633942,"HyperDash":false}]},{"StartTime":39379.0,"Objects":[{"StartTime":39379.0,"Position":113.0,"HyperDash":false},{"StartTime":39448.0,"Position":113.075348,"HyperDash":false},{"StartTime":39517.0,"Position":104.150688,"HyperDash":false},{"StartTime":39586.0,"Position":88.2260361,"HyperDash":false},{"StartTime":39691.0,"Position":104.297211,"HyperDash":false}]},{"StartTime":40004.0,"Objects":[{"StartTime":40004.0,"Position":385.0,"HyperDash":false}]},{"StartTime":40316.0,"Objects":[{"StartTime":40316.0,"Position":250.0,"HyperDash":false}]},{"StartTime":40629.0,"Objects":[{"StartTime":40629.0,"Position":256.0,"HyperDash":false}]},{"StartTime":40941.0,"Objects":[{"StartTime":40941.0,"Position":89.0,"HyperDash":false}]},{"StartTime":41254.0,"Objects":[{"StartTime":41254.0,"Position":256.0,"HyperDash":false},{"StartTime":41332.0,"Position":267.961151,"HyperDash":false},{"StartTime":41410.0,"Position":316.030029,"HyperDash":false},{"StartTime":41488.0,"Position":332.626129,"HyperDash":false},{"StartTime":41566.0,"Position":354.210022,"HyperDash":false},{"StartTime":41644.0,"Position":353.98996,"HyperDash":false},{"StartTime":41722.0,"Position":371.766144,"HyperDash":false},{"StartTime":41800.0,"Position":358.108276,"HyperDash":false},{"StartTime":41879.0,"Position":354.210022,"HyperDash":false},{"StartTime":41948.0,"Position":326.1343,"HyperDash":false},{"StartTime":42017.0,"Position":316.00824,"HyperDash":false},{"StartTime":42086.0,"Position":278.452332,"HyperDash":false},{"StartTime":42191.0,"Position":256.0,"HyperDash":false}]},{"StartTime":42504.0,"Objects":[{"StartTime":42504.0,"Position":98.0,"HyperDash":false},{"StartTime":42582.0,"Position":125.961151,"HyperDash":false},{"StartTime":42660.0,"Position":167.030014,"HyperDash":false},{"StartTime":42738.0,"Position":165.626129,"HyperDash":false},{"StartTime":42816.0,"Position":196.210022,"HyperDash":false},{"StartTime":42894.0,"Position":225.98996,"HyperDash":false},{"StartTime":42972.0,"Position":213.766159,"HyperDash":false},{"StartTime":43050.0,"Position":190.108276,"HyperDash":false},{"StartTime":43129.0,"Position":196.210022,"HyperDash":false},{"StartTime":43198.0,"Position":197.134308,"HyperDash":false},{"StartTime":43267.0,"Position":141.00824,"HyperDash":false},{"StartTime":43336.0,"Position":118.452332,"HyperDash":false},{"StartTime":43441.0,"Position":98.0,"HyperDash":false}]},{"StartTime":43754.0,"Objects":[{"StartTime":43754.0,"Position":249.0,"HyperDash":false},{"StartTime":43832.0,"Position":233.529724,"HyperDash":false},{"StartTime":43910.0,"Position":206.059448,"HyperDash":false},{"StartTime":43988.0,"Position":181.589172,"HyperDash":false},{"StartTime":44066.0,"Position":139.1189,"HyperDash":false},{"StartTime":44144.0,"Position":97.64862,"HyperDash":false},{"StartTime":44222.0,"Position":84.00227,"HyperDash":false},{"StartTime":44300.0,"Position":107.296448,"HyperDash":false},{"StartTime":44378.0,"Position":138.766724,"HyperDash":false},{"StartTime":44447.0,"Position":162.067352,"HyperDash":false},{"StartTime":44516.0,"Position":187.367981,"HyperDash":false},{"StartTime":44585.0,"Position":204.66861,"HyperDash":false},{"StartTime":44691.0,"Position":249.0,"HyperDash":false}]},{"StartTime":45004.0,"Objects":[{"StartTime":45004.0,"Position":466.0,"HyperDash":false},{"StartTime":45062.0,"Position":56.0,"HyperDash":false},{"StartTime":45121.0,"Position":109.0,"HyperDash":false},{"StartTime":45179.0,"Position":482.0,"HyperDash":false},{"StartTime":45238.0,"Position":147.0,"HyperDash":false},{"StartTime":45296.0,"Position":285.0,"HyperDash":false},{"StartTime":45355.0,"Position":452.0,"HyperDash":false},{"StartTime":45413.0,"Position":419.0,"HyperDash":false},{"StartTime":45472.0,"Position":269.0,"HyperDash":false},{"StartTime":45531.0,"Position":249.0,"HyperDash":false},{"StartTime":45589.0,"Position":233.0,"HyperDash":false},{"StartTime":45648.0,"Position":449.0,"HyperDash":false},{"StartTime":45706.0,"Position":411.0,"HyperDash":false},{"StartTime":45765.0,"Position":75.0,"HyperDash":false},{"StartTime":45823.0,"Position":474.0,"HyperDash":false},{"StartTime":45882.0,"Position":176.0,"HyperDash":false},{"StartTime":45941.0,"Position":1.0,"HyperDash":false}]},{"StartTime":46254.0,"Objects":[{"StartTime":46254.0,"Position":332.0,"HyperDash":false},{"StartTime":46332.0,"Position":341.35257,"HyperDash":false},{"StartTime":46410.0,"Position":382.281,"HyperDash":false},{"StartTime":46488.0,"Position":401.661774,"HyperDash":false},{"StartTime":46566.0,"Position":424.539063,"HyperDash":false},{"StartTime":46644.0,"Position":451.522461,"HyperDash":false},{"StartTime":46722.0,"Position":436.847626,"HyperDash":false},{"StartTime":46800.0,"Position":445.602142,"HyperDash":false},{"StartTime":46879.0,"Position":424.539063,"HyperDash":false},{"StartTime":46948.0,"Position":396.89386,"HyperDash":false},{"StartTime":47017.0,"Position":399.831055,"HyperDash":false},{"StartTime":47086.0,"Position":370.600555,"HyperDash":false},{"StartTime":47191.0,"Position":332.0,"HyperDash":false}]},{"StartTime":47504.0,"Objects":[{"StartTime":47504.0,"Position":180.0,"HyperDash":false},{"StartTime":47582.0,"Position":148.647415,"HyperDash":false},{"StartTime":47660.0,"Position":119.718979,"HyperDash":false},{"StartTime":47738.0,"Position":115.338226,"HyperDash":false},{"StartTime":47816.0,"Position":87.4609146,"HyperDash":false},{"StartTime":47894.0,"Position":96.4775,"HyperDash":false},{"StartTime":47972.0,"Position":75.15235,"HyperDash":false},{"StartTime":48050.0,"Position":64.3978348,"HyperDash":false},{"StartTime":48129.0,"Position":87.4609146,"HyperDash":false},{"StartTime":48198.0,"Position":116.106155,"HyperDash":false},{"StartTime":48267.0,"Position":126.168938,"HyperDash":false},{"StartTime":48336.0,"Position":133.399445,"HyperDash":false},{"StartTime":48441.0,"Position":180.0,"HyperDash":false}]},{"StartTime":48754.0,"Objects":[{"StartTime":48754.0,"Position":335.0,"HyperDash":false},{"StartTime":48832.0,"Position":313.529724,"HyperDash":false},{"StartTime":48910.0,"Position":282.059448,"HyperDash":false},{"StartTime":48988.0,"Position":233.589188,"HyperDash":false},{"StartTime":49066.0,"Position":225.118927,"HyperDash":false},{"StartTime":49144.0,"Position":202.648651,"HyperDash":false},{"StartTime":49222.0,"Position":170.002289,"HyperDash":false},{"StartTime":49300.0,"Position":213.296478,"HyperDash":false},{"StartTime":49379.0,"Position":225.118927,"HyperDash":false},{"StartTime":49448.0,"Position":239.41954,"HyperDash":false},{"StartTime":49517.0,"Position":280.720154,"HyperDash":false},{"StartTime":49586.0,"Position":280.020782,"HyperDash":false},{"StartTime":49691.0,"Position":335.0,"HyperDash":false}]},{"StartTime":50004.0,"Objects":[{"StartTime":50004.0,"Position":93.0,"HyperDash":false},{"StartTime":50062.0,"Position":267.0,"HyperDash":false},{"StartTime":50121.0,"Position":276.0,"HyperDash":false},{"StartTime":50179.0,"Position":367.0,"HyperDash":false},{"StartTime":50238.0,"Position":409.0,"HyperDash":false},{"StartTime":50296.0,"Position":117.0,"HyperDash":false},{"StartTime":50355.0,"Position":226.0,"HyperDash":false},{"StartTime":50413.0,"Position":469.0,"HyperDash":false},{"StartTime":50472.0,"Position":267.0,"HyperDash":false},{"StartTime":50531.0,"Position":477.0,"HyperDash":false},{"StartTime":50589.0,"Position":282.0,"HyperDash":false},{"StartTime":50648.0,"Position":216.0,"HyperDash":false},{"StartTime":50706.0,"Position":106.0,"HyperDash":false},{"StartTime":50765.0,"Position":353.0,"HyperDash":false},{"StartTime":50823.0,"Position":162.0,"HyperDash":false},{"StartTime":50882.0,"Position":473.0,"HyperDash":false},{"StartTime":50941.0,"Position":260.0,"HyperDash":false}]},{"StartTime":51254.0,"Objects":[{"StartTime":51254.0,"Position":119.0,"HyperDash":false},{"StartTime":51353.0,"Position":121.8725,"HyperDash":false},{"StartTime":51488.0,"Position":106.880455,"HyperDash":false}]},{"StartTime":51722.0,"Objects":[{"StartTime":51722.0,"Position":230.0,"HyperDash":false},{"StartTime":51800.0,"Position":213.529388,"HyperDash":false},{"StartTime":51878.0,"Position":222.058777,"HyperDash":false},{"StartTime":51956.0,"Position":243.588165,"HyperDash":false},{"StartTime":52034.0,"Position":244.117569,"HyperDash":false},{"StartTime":52094.0,"Position":256.8325,"HyperDash":false},{"StartTime":52190.0,"Position":251.176346,"HyperDash":false}]},{"StartTime":52504.0,"Objects":[{"StartTime":52504.0,"Position":373.0,"HyperDash":false},{"StartTime":52603.0,"Position":384.1275,"HyperDash":false},{"StartTime":52738.0,"Position":385.119537,"HyperDash":false}]},{"StartTime":52972.0,"Objects":[{"StartTime":52972.0,"Position":269.0,"HyperDash":false},{"StartTime":53050.0,"Position":243.9549,"HyperDash":false},{"StartTime":53128.0,"Position":227.363922,"HyperDash":false},{"StartTime":53206.0,"Position":246.705673,"HyperDash":false},{"StartTime":53284.0,"Position":238.662781,"HyperDash":false},{"StartTime":53344.0,"Position":267.1444,"HyperDash":false},{"StartTime":53440.0,"Position":274.832428,"HyperDash":false}]},{"StartTime":53754.0,"Objects":[{"StartTime":53754.0,"Position":424.0,"HyperDash":false},{"StartTime":53853.0,"Position":394.183075,"HyperDash":false},{"StartTime":53988.0,"Position":341.705444,"HyperDash":false}]},{"StartTime":54222.0,"Objects":[{"StartTime":54222.0,"Position":228.0,"HyperDash":false},{"StartTime":54300.0,"Position":248.405014,"HyperDash":false},{"StartTime":54378.0,"Position":299.810028,"HyperDash":false},{"StartTime":54456.0,"Position":312.215027,"HyperDash":false},{"StartTime":54534.0,"Position":337.620056,"HyperDash":false},{"StartTime":54594.0,"Position":374.7008,"HyperDash":false},{"StartTime":54690.0,"Position":392.430054,"HyperDash":false}]},{"StartTime":55004.0,"Objects":[{"StartTime":55004.0,"Position":241.0,"HyperDash":false},{"StartTime":55103.0,"Position":294.816925,"HyperDash":false},{"StartTime":55238.0,"Position":323.294556,"HyperDash":false}]},{"StartTime":55472.0,"Objects":[{"StartTime":55472.0,"Position":437.0,"HyperDash":false},{"StartTime":55550.0,"Position":398.595,"HyperDash":false},{"StartTime":55628.0,"Position":394.189972,"HyperDash":false},{"StartTime":55706.0,"Position":340.784973,"HyperDash":false},{"StartTime":55784.0,"Position":327.379944,"HyperDash":false},{"StartTime":55844.0,"Position":318.2992,"HyperDash":false},{"StartTime":55940.0,"Position":272.569946,"HyperDash":false}]},{"StartTime":56254.0,"Objects":[{"StartTime":56254.0,"Position":3.0,"HyperDash":false}]},{"StartTime":56488.0,"Objects":[{"StartTime":56488.0,"Position":260.0,"HyperDash":false},{"StartTime":56587.0,"Position":252.118042,"HyperDash":false},{"StartTime":56722.0,"Position":269.733521,"HyperDash":false}]},{"StartTime":56957.0,"Objects":[{"StartTime":56957.0,"Position":162.0,"HyperDash":false},{"StartTime":57056.0,"Position":138.870941,"HyperDash":false},{"StartTime":57191.0,"Position":81.3313,"HyperDash":false}]},{"StartTime":57504.0,"Objects":[{"StartTime":57504.0,"Position":402.0,"HyperDash":false}]},{"StartTime":57738.0,"Objects":[{"StartTime":57738.0,"Position":363.0,"HyperDash":false},{"StartTime":57837.0,"Position":344.432251,"HyperDash":false},{"StartTime":57972.0,"Position":281.2944,"HyperDash":false}]},{"StartTime":58207.0,"Objects":[{"StartTime":58207.0,"Position":174.0,"HyperDash":false},{"StartTime":58306.0,"Position":158.870941,"HyperDash":false},{"StartTime":58441.0,"Position":93.3313,"HyperDash":false}]},{"StartTime":58754.0,"Objects":[{"StartTime":58754.0,"Position":261.0,"HyperDash":false},{"StartTime":58832.0,"Position":243.6731,"HyperDash":false},{"StartTime":58910.0,"Position":205.346176,"HyperDash":false},{"StartTime":58988.0,"Position":162.019257,"HyperDash":false},{"StartTime":59066.0,"Position":151.692352,"HyperDash":false},{"StartTime":59144.0,"Position":119.365463,"HyperDash":false},{"StartTime":59222.0,"Position":96.86337,"HyperDash":false},{"StartTime":59300.0,"Position":110.015121,"HyperDash":false},{"StartTime":59379.0,"Position":151.692352,"HyperDash":false},{"StartTime":59448.0,"Position":182.866165,"HyperDash":false},{"StartTime":59517.0,"Position":214.039978,"HyperDash":false},{"StartTime":59586.0,"Position":217.213776,"HyperDash":false},{"StartTime":59691.0,"Position":261.0,"HyperDash":false}]},{"StartTime":60004.0,"Objects":[{"StartTime":60004.0,"Position":456.0,"HyperDash":false},{"StartTime":60062.0,"Position":371.0,"HyperDash":false},{"StartTime":60121.0,"Position":73.0,"HyperDash":false},{"StartTime":60179.0,"Position":190.0,"HyperDash":false},{"StartTime":60238.0,"Position":180.0,"HyperDash":false},{"StartTime":60296.0,"Position":461.0,"HyperDash":false},{"StartTime":60355.0,"Position":433.0,"HyperDash":false},{"StartTime":60413.0,"Position":275.0,"HyperDash":false},{"StartTime":60472.0,"Position":395.0,"HyperDash":false},{"StartTime":60531.0,"Position":473.0,"HyperDash":false},{"StartTime":60589.0,"Position":192.0,"HyperDash":false},{"StartTime":60648.0,"Position":362.0,"HyperDash":false},{"StartTime":60706.0,"Position":7.0,"HyperDash":false},{"StartTime":60765.0,"Position":500.0,"HyperDash":false},{"StartTime":60823.0,"Position":487.0,"HyperDash":false},{"StartTime":60882.0,"Position":487.0,"HyperDash":false},{"StartTime":60941.0,"Position":213.0,"HyperDash":false}]},{"StartTime":71254.0,"Objects":[{"StartTime":71254.0,"Position":258.0,"HyperDash":false}]},{"StartTime":71722.0,"Objects":[{"StartTime":71722.0,"Position":69.0,"HyperDash":false},{"StartTime":71800.0,"Position":67.48298,"HyperDash":false},{"StartTime":71878.0,"Position":70.96595,"HyperDash":false},{"StartTime":71956.0,"Position":82.44893,"HyperDash":false},{"StartTime":72034.0,"Position":62.9319038,"HyperDash":false},{"StartTime":72094.0,"Position":61.76496,"HyperDash":false},{"StartTime":72190.0,"Position":59.8978577,"HyperDash":false}]},{"StartTime":72504.0,"Objects":[{"StartTime":72504.0,"Position":381.0,"HyperDash":false}]},{"StartTime":73754.0,"Objects":[{"StartTime":73754.0,"Position":254.0,"HyperDash":false}]},{"StartTime":74222.0,"Objects":[{"StartTime":74222.0,"Position":443.0,"HyperDash":false},{"StartTime":74300.0,"Position":452.517029,"HyperDash":false},{"StartTime":74378.0,"Position":430.034058,"HyperDash":false},{"StartTime":74456.0,"Position":439.5511,"HyperDash":false},{"StartTime":74534.0,"Position":449.068085,"HyperDash":false},{"StartTime":74594.0,"Position":435.235046,"HyperDash":false},{"StartTime":74690.0,"Position":452.102142,"HyperDash":false}]},{"StartTime":75004.0,"Objects":[{"StartTime":75004.0,"Position":131.0,"HyperDash":false}]},{"StartTime":76254.0,"Objects":[{"StartTime":76254.0,"Position":136.0,"HyperDash":false}]},{"StartTime":76722.0,"Objects":[{"StartTime":76722.0,"Position":349.0,"HyperDash":false},{"StartTime":76800.0,"Position":304.5004,"HyperDash":false},{"StartTime":76878.0,"Position":313.000824,"HyperDash":false},{"StartTime":76956.0,"Position":264.501221,"HyperDash":false},{"StartTime":77034.0,"Position":239.001617,"HyperDash":false},{"StartTime":77094.0,"Position":209.848083,"HyperDash":false},{"StartTime":77190.0,"Position":184.002426,"HyperDash":false}]},{"StartTime":77504.0,"Objects":[{"StartTime":77504.0,"Position":350.0,"HyperDash":false}]},{"StartTime":78754.0,"Objects":[{"StartTime":78754.0,"Position":376.0,"HyperDash":false}]},{"StartTime":79222.0,"Objects":[{"StartTime":79222.0,"Position":163.0,"HyperDash":false},{"StartTime":79300.0,"Position":186.499588,"HyperDash":false},{"StartTime":79378.0,"Position":226.999191,"HyperDash":false},{"StartTime":79456.0,"Position":256.498779,"HyperDash":false},{"StartTime":79534.0,"Position":272.998383,"HyperDash":false},{"StartTime":79594.0,"Position":280.151917,"HyperDash":false},{"StartTime":79690.0,"Position":327.997559,"HyperDash":false}]},{"StartTime":80004.0,"Objects":[{"StartTime":80004.0,"Position":11.0,"HyperDash":false}]},{"StartTime":80316.0,"Objects":[{"StartTime":80316.0,"Position":165.0,"HyperDash":false}]},{"StartTime":80629.0,"Objects":[{"StartTime":80629.0,"Position":11.0,"HyperDash":false}]},{"StartTime":80941.0,"Objects":[{"StartTime":80941.0,"Position":192.0,"HyperDash":false}]},{"StartTime":81254.0,"Objects":[{"StartTime":81254.0,"Position":336.0,"HyperDash":false},{"StartTime":81323.0,"Position":296.6767,"HyperDash":false},{"StartTime":81392.0,"Position":293.3534,"HyperDash":false},{"StartTime":81461.0,"Position":281.03006,"HyperDash":false},{"StartTime":81566.0,"Position":226.016357,"HyperDash":false}]},{"StartTime":81879.0,"Objects":[{"StartTime":81879.0,"Position":366.0,"HyperDash":false},{"StartTime":81948.0,"Position":396.3233,"HyperDash":false},{"StartTime":82017.0,"Position":429.6466,"HyperDash":false},{"StartTime":82086.0,"Position":448.96994,"HyperDash":false},{"StartTime":82191.0,"Position":475.983643,"HyperDash":false}]},{"StartTime":82504.0,"Objects":[{"StartTime":82504.0,"Position":156.0,"HyperDash":false}]},{"StartTime":82816.0,"Objects":[{"StartTime":82816.0,"Position":292.0,"HyperDash":false}]},{"StartTime":83129.0,"Objects":[{"StartTime":83129.0,"Position":100.0,"HyperDash":false}]},{"StartTime":83441.0,"Objects":[{"StartTime":83441.0,"Position":248.0,"HyperDash":false}]},{"StartTime":83754.0,"Objects":[{"StartTime":83754.0,"Position":390.0,"HyperDash":false},{"StartTime":83832.0,"Position":372.544983,"HyperDash":false},{"StartTime":83910.0,"Position":344.089935,"HyperDash":false},{"StartTime":83988.0,"Position":308.634918,"HyperDash":false},{"StartTime":84066.0,"Position":280.003876,"HyperDash":false},{"StartTime":84135.0,"Position":301.115051,"HyperDash":false},{"StartTime":84204.0,"Position":341.4022,"HyperDash":false},{"StartTime":84273.0,"Position":359.6893,"HyperDash":false},{"StartTime":84379.0,"Position":390.0,"HyperDash":false}]},{"StartTime":85004.0,"Objects":[{"StartTime":85004.0,"Position":104.0,"HyperDash":false}]},{"StartTime":86254.0,"Objects":[{"StartTime":86254.0,"Position":324.0,"HyperDash":false}]},{"StartTime":86566.0,"Objects":[{"StartTime":86566.0,"Position":422.0,"HyperDash":false}]},{"StartTime":86879.0,"Objects":[{"StartTime":86879.0,"Position":470.0,"HyperDash":false}]},{"StartTime":87191.0,"Objects":[{"StartTime":87191.0,"Position":352.0,"HyperDash":false}]},{"StartTime":87504.0,"Objects":[{"StartTime":87504.0,"Position":287.0,"HyperDash":false},{"StartTime":87573.0,"Position":317.323242,"HyperDash":false},{"StartTime":87642.0,"Position":340.646484,"HyperDash":false},{"StartTime":87711.0,"Position":343.969727,"HyperDash":false},{"StartTime":87816.0,"Position":396.983368,"HyperDash":false}]},{"StartTime":88129.0,"Objects":[{"StartTime":88129.0,"Position":265.0,"HyperDash":false},{"StartTime":88198.0,"Position":237.676239,"HyperDash":false},{"StartTime":88267.0,"Position":223.352478,"HyperDash":false},{"StartTime":88336.0,"Position":206.028717,"HyperDash":false},{"StartTime":88441.0,"Position":155.014313,"HyperDash":false}]},{"StartTime":88754.0,"Objects":[{"StartTime":88754.0,"Position":475.0,"HyperDash":false}]},{"StartTime":89066.0,"Objects":[{"StartTime":89066.0,"Position":341.0,"HyperDash":false}]},{"StartTime":89379.0,"Objects":[{"StartTime":89379.0,"Position":432.0,"HyperDash":false}]},{"StartTime":89691.0,"Objects":[{"StartTime":89691.0,"Position":264.0,"HyperDash":false}]},{"StartTime":90004.0,"Objects":[{"StartTime":90004.0,"Position":255.0,"HyperDash":false},{"StartTime":90062.0,"Position":294.0,"HyperDash":false},{"StartTime":90121.0,"Position":354.0,"HyperDash":false},{"StartTime":90179.0,"Position":270.0,"HyperDash":false},{"StartTime":90238.0,"Position":362.0,"HyperDash":false},{"StartTime":90296.0,"Position":255.0,"HyperDash":false},{"StartTime":90355.0,"Position":203.0,"HyperDash":false},{"StartTime":90413.0,"Position":67.0,"HyperDash":false},{"StartTime":90472.0,"Position":112.0,"HyperDash":false},{"StartTime":90531.0,"Position":326.0,"HyperDash":false},{"StartTime":90589.0,"Position":219.0,"HyperDash":false},{"StartTime":90648.0,"Position":351.0,"HyperDash":false},{"StartTime":90706.0,"Position":477.0,"HyperDash":false},{"StartTime":90765.0,"Position":439.0,"HyperDash":false},{"StartTime":90823.0,"Position":471.0,"HyperDash":false},{"StartTime":90882.0,"Position":449.0,"HyperDash":false},{"StartTime":90941.0,"Position":295.0,"HyperDash":false}]},{"StartTime":91254.0,"Objects":[{"StartTime":91254.0,"Position":140.0,"HyperDash":false},{"StartTime":91332.0,"Position":109.180054,"HyperDash":false},{"StartTime":91410.0,"Position":88.36357,"HyperDash":false},{"StartTime":91488.0,"Position":92.07944,"HyperDash":false},{"StartTime":91566.0,"Position":69.79061,"HyperDash":false},{"StartTime":91635.0,"Position":78.01627,"HyperDash":false},{"StartTime":91704.0,"Position":103.148987,"HyperDash":false},{"StartTime":91773.0,"Position":121.717133,"HyperDash":false},{"StartTime":91878.0,"Position":140.090958,"HyperDash":false}]},{"StartTime":92191.0,"Objects":[{"StartTime":92191.0,"Position":380.0,"HyperDash":false}]},{"StartTime":92504.0,"Objects":[{"StartTime":92504.0,"Position":405.0,"HyperDash":false},{"StartTime":92573.0,"Position":381.6738,"HyperDash":false},{"StartTime":92642.0,"Position":369.347565,"HyperDash":false},{"StartTime":92711.0,"Position":341.021362,"HyperDash":false},{"StartTime":92816.0,"Position":295.0032,"HyperDash":false}]},{"StartTime":93129.0,"Objects":[{"StartTime":93129.0,"Position":154.0,"HyperDash":false},{"StartTime":93198.0,"Position":177.324478,"HyperDash":false},{"StartTime":93267.0,"Position":215.648956,"HyperDash":false},{"StartTime":93336.0,"Position":211.973434,"HyperDash":false},{"StartTime":93441.0,"Position":263.988922,"HyperDash":false}]},{"StartTime":93754.0,"Objects":[{"StartTime":93754.0,"Position":135.0,"HyperDash":false},{"StartTime":93832.0,"Position":153.455765,"HyperDash":false},{"StartTime":93910.0,"Position":177.911545,"HyperDash":false},{"StartTime":93988.0,"Position":219.36731,"HyperDash":false},{"StartTime":94066.0,"Position":244.82309,"HyperDash":false},{"StartTime":94135.0,"Position":274.1109,"HyperDash":false},{"StartTime":94204.0,"Position":309.398682,"HyperDash":false},{"StartTime":94273.0,"Position":297.686462,"HyperDash":false},{"StartTime":94379.0,"Position":354.998169,"HyperDash":false}]},{"StartTime":94691.0,"Objects":[{"StartTime":94691.0,"Position":98.0,"HyperDash":false}]},{"StartTime":95004.0,"Objects":[{"StartTime":95004.0,"Position":354.0,"HyperDash":false},{"StartTime":95073.0,"Position":330.775818,"HyperDash":false},{"StartTime":95142.0,"Position":308.551636,"HyperDash":false},{"StartTime":95211.0,"Position":298.327454,"HyperDash":false},{"StartTime":95316.0,"Position":244.464569,"HyperDash":false}]},{"StartTime":95629.0,"Objects":[{"StartTime":95629.0,"Position":97.0,"HyperDash":false},{"StartTime":95698.0,"Position":100.173164,"HyperDash":false},{"StartTime":95767.0,"Position":91.34632,"HyperDash":false},{"StartTime":95836.0,"Position":85.5194855,"HyperDash":false},{"StartTime":95941.0,"Position":79.69604,"HyperDash":false}]},{"StartTime":96254.0,"Objects":[{"StartTime":96254.0,"Position":238.0,"HyperDash":false},{"StartTime":96332.0,"Position":263.193329,"HyperDash":false},{"StartTime":96410.0,"Position":297.398376,"HyperDash":false},{"StartTime":96488.0,"Position":296.3632,"HyperDash":false},{"StartTime":96566.0,"Position":315.395416,"HyperDash":false},{"StartTime":96635.0,"Position":328.299622,"HyperDash":false},{"StartTime":96704.0,"Position":295.141235,"HyperDash":false},{"StartTime":96773.0,"Position":274.06,"HyperDash":false},{"StartTime":96878.0,"Position":241.776031,"HyperDash":false}]},{"StartTime":97191.0,"Objects":[{"StartTime":97191.0,"Position":497.0,"HyperDash":false}]},{"StartTime":97504.0,"Objects":[{"StartTime":97504.0,"Position":252.0,"HyperDash":false},{"StartTime":97582.0,"Position":214.922928,"HyperDash":false},{"StartTime":97660.0,"Position":177.845856,"HyperDash":false},{"StartTime":97738.0,"Position":185.7688,"HyperDash":false},{"StartTime":97816.0,"Position":143.518143,"HyperDash":false},{"StartTime":97885.0,"Position":175.297363,"HyperDash":false},{"StartTime":97954.0,"Position":187.250168,"HyperDash":false},{"StartTime":98023.0,"Position":222.202957,"HyperDash":false},{"StartTime":98129.0,"Position":252.0,"HyperDash":false}]},{"StartTime":98441.0,"Objects":[{"StartTime":98441.0,"Position":363.0,"HyperDash":false}]},{"StartTime":98754.0,"Objects":[{"StartTime":98754.0,"Position":223.0,"HyperDash":false},{"StartTime":98823.0,"Position":195.875366,"HyperDash":false},{"StartTime":98892.0,"Position":193.750732,"HyperDash":false},{"StartTime":98961.0,"Position":151.626083,"HyperDash":false},{"StartTime":99066.0,"Position":113.914696,"HyperDash":false}]},{"StartTime":99379.0,"Objects":[{"StartTime":99379.0,"Position":494.0,"HyperDash":false}]},{"StartTime":99691.0,"Objects":[{"StartTime":99691.0,"Position":298.0,"HyperDash":false}]},{"StartTime":100004.0,"Objects":[{"StartTime":100004.0,"Position":236.0,"HyperDash":false},{"StartTime":100082.0,"Position":203.256851,"HyperDash":false},{"StartTime":100160.0,"Position":168.009674,"HyperDash":false},{"StartTime":100238.0,"Position":177.094879,"HyperDash":false},{"StartTime":100316.0,"Position":141.201492,"HyperDash":false},{"StartTime":100385.0,"Position":127.635262,"HyperDash":false},{"StartTime":100454.0,"Position":110.29274,"HyperDash":false},{"StartTime":100523.0,"Position":110.453743,"HyperDash":false},{"StartTime":100628.0,"Position":102.687973,"HyperDash":false}]},{"StartTime":100941.0,"Objects":[{"StartTime":100941.0,"Position":349.0,"HyperDash":false}]},{"StartTime":101254.0,"Objects":[{"StartTime":101254.0,"Position":383.0,"HyperDash":false},{"StartTime":101332.0,"Position":419.957733,"HyperDash":false},{"StartTime":101410.0,"Position":426.299622,"HyperDash":false},{"StartTime":101488.0,"Position":445.490662,"HyperDash":false},{"StartTime":101566.0,"Position":455.829254,"HyperDash":false},{"StartTime":101635.0,"Position":451.601563,"HyperDash":false},{"StartTime":101704.0,"Position":456.379,"HyperDash":false},{"StartTime":101773.0,"Position":416.516266,"HyperDash":false},{"StartTime":101879.0,"Position":388.265564,"HyperDash":false}]},{"StartTime":102191.0,"Objects":[{"StartTime":102191.0,"Position":135.0,"HyperDash":false}]},{"StartTime":102504.0,"Objects":[{"StartTime":102504.0,"Position":123.0,"HyperDash":false},{"StartTime":102573.0,"Position":136.53656,"HyperDash":false},{"StartTime":102642.0,"Position":127.073128,"HyperDash":false},{"StartTime":102711.0,"Position":116.609695,"HyperDash":false},{"StartTime":102816.0,"Position":107.339249,"HyperDash":false}]},{"StartTime":103129.0,"Objects":[{"StartTime":103129.0,"Position":252.0,"HyperDash":false},{"StartTime":103198.0,"Position":284.326935,"HyperDash":false},{"StartTime":103267.0,"Position":309.653839,"HyperDash":false},{"StartTime":103336.0,"Position":338.980774,"HyperDash":false},{"StartTime":103441.0,"Position":362.0,"HyperDash":false}]},{"StartTime":103754.0,"Objects":[{"StartTime":103754.0,"Position":215.0,"HyperDash":false},{"StartTime":103832.0,"Position":174.651,"HyperDash":false},{"StartTime":103910.0,"Position":155.666367,"HyperDash":false},{"StartTime":103988.0,"Position":122.457794,"HyperDash":false},{"StartTime":104066.0,"Position":113.4155,"HyperDash":false},{"StartTime":104135.0,"Position":85.44563,"HyperDash":false},{"StartTime":104204.0,"Position":103.505188,"HyperDash":false},{"StartTime":104273.0,"Position":78.09056,"HyperDash":false},{"StartTime":104379.0,"Position":76.11086,"HyperDash":false}]},{"StartTime":104691.0,"Objects":[{"StartTime":104691.0,"Position":353.0,"HyperDash":false}]},{"StartTime":105004.0,"Objects":[{"StartTime":105004.0,"Position":359.0,"HyperDash":false},{"StartTime":105073.0,"Position":360.169861,"HyperDash":false},{"StartTime":105142.0,"Position":374.3397,"HyperDash":false},{"StartTime":105211.0,"Position":369.509552,"HyperDash":false},{"StartTime":105316.0,"Position":368.8115,"HyperDash":false}]},{"StartTime":105629.0,"Objects":[{"StartTime":105629.0,"Position":215.0,"HyperDash":false},{"StartTime":105698.0,"Position":245.2746,"HyperDash":false},{"StartTime":105767.0,"Position":263.5492,"HyperDash":false},{"StartTime":105836.0,"Position":298.8238,"HyperDash":false},{"StartTime":105941.0,"Position":324.7634,"HyperDash":false}]},{"StartTime":106254.0,"Objects":[{"StartTime":106254.0,"Position":164.0,"HyperDash":false},{"StartTime":106332.0,"Position":181.330521,"HyperDash":false},{"StartTime":106410.0,"Position":231.661041,"HyperDash":false},{"StartTime":106488.0,"Position":258.991547,"HyperDash":false},{"StartTime":106566.0,"Position":273.322083,"HyperDash":false},{"StartTime":106635.0,"Position":308.499084,"HyperDash":false},{"StartTime":106704.0,"Position":315.6761,"HyperDash":false},{"StartTime":106773.0,"Position":330.8531,"HyperDash":false},{"StartTime":106878.0,"Position":382.644135,"HyperDash":false}]},{"StartTime":107191.0,"Objects":[{"StartTime":107191.0,"Position":64.0,"HyperDash":false}]},{"StartTime":107504.0,"Objects":[{"StartTime":107504.0,"Position":390.0,"HyperDash":false},{"StartTime":107582.0,"Position":367.983734,"HyperDash":false},{"StartTime":107660.0,"Position":346.967468,"HyperDash":false},{"StartTime":107738.0,"Position":308.9512,"HyperDash":false},{"StartTime":107816.0,"Position":281.934967,"HyperDash":false},{"StartTime":107885.0,"Position":292.833954,"HyperDash":false},{"StartTime":107954.0,"Position":315.732941,"HyperDash":false},{"StartTime":108023.0,"Position":344.631958,"HyperDash":false},{"StartTime":108128.0,"Position":390.0,"HyperDash":false}]},{"StartTime":108441.0,"Objects":[{"StartTime":108441.0,"Position":219.0,"HyperDash":false}]},{"StartTime":108754.0,"Objects":[{"StartTime":108754.0,"Position":99.0,"HyperDash":false},{"StartTime":108823.0,"Position":80.78087,"HyperDash":false},{"StartTime":108892.0,"Position":79.56174,"HyperDash":false},{"StartTime":108961.0,"Position":79.34261,"HyperDash":false},{"StartTime":109066.0,"Position":88.9656754,"HyperDash":false}]},{"StartTime":109379.0,"Objects":[{"StartTime":109379.0,"Position":228.0,"HyperDash":false},{"StartTime":109448.0,"Position":259.2614,"HyperDash":false},{"StartTime":109517.0,"Position":268.522858,"HyperDash":false},{"StartTime":109586.0,"Position":318.7843,"HyperDash":false},{"StartTime":109691.0,"Position":337.703827,"HyperDash":false}]},{"StartTime":110004.0,"Objects":[{"StartTime":110004.0,"Position":183.0,"HyperDash":false},{"StartTime":110082.0,"Position":220.45372,"HyperDash":false},{"StartTime":110160.0,"Position":245.90744,"HyperDash":false},{"StartTime":110238.0,"Position":248.361145,"HyperDash":false},{"StartTime":110316.0,"Position":292.81488,"HyperDash":false},{"StartTime":110385.0,"Position":310.10083,"HyperDash":false},{"StartTime":110454.0,"Position":355.386841,"HyperDash":false},{"StartTime":110523.0,"Position":350.6728,"HyperDash":false},{"StartTime":110628.0,"Position":402.6297,"HyperDash":false}]},{"StartTime":110941.0,"Objects":[{"StartTime":110941.0,"Position":108.0,"HyperDash":false}]},{"StartTime":111254.0,"Objects":[{"StartTime":111254.0,"Position":114.0,"HyperDash":false},{"StartTime":111323.0,"Position":102.780869,"HyperDash":false},{"StartTime":111392.0,"Position":118.561737,"HyperDash":false},{"StartTime":111461.0,"Position":104.342613,"HyperDash":false},{"StartTime":111566.0,"Position":103.965675,"HyperDash":false}]},{"StartTime":111879.0,"Objects":[{"StartTime":111879.0,"Position":243.0,"HyperDash":false},{"StartTime":111948.0,"Position":284.2614,"HyperDash":false},{"StartTime":112017.0,"Position":284.522858,"HyperDash":false},{"StartTime":112086.0,"Position":327.7843,"HyperDash":false},{"StartTime":112191.0,"Position":352.703827,"HyperDash":false}]},{"StartTime":112504.0,"Objects":[{"StartTime":112504.0,"Position":198.0,"HyperDash":false},{"StartTime":112582.0,"Position":234.45372,"HyperDash":false},{"StartTime":112660.0,"Position":250.90744,"HyperDash":false},{"StartTime":112738.0,"Position":271.361145,"HyperDash":false},{"StartTime":112816.0,"Position":307.81488,"HyperDash":false},{"StartTime":112885.0,"Position":345.10083,"HyperDash":false},{"StartTime":112954.0,"Position":347.386841,"HyperDash":false},{"StartTime":113023.0,"Position":393.6728,"HyperDash":false},{"StartTime":113128.0,"Position":417.6297,"HyperDash":false}]},{"StartTime":113441.0,"Objects":[{"StartTime":113441.0,"Position":123.0,"HyperDash":false}]},{"StartTime":113754.0,"Objects":[{"StartTime":113754.0,"Position":151.0,"HyperDash":false}]},{"StartTime":113910.0,"Objects":[{"StartTime":113910.0,"Position":103.0,"HyperDash":false}]},{"StartTime":114379.0,"Objects":[{"StartTime":114379.0,"Position":314.0,"HyperDash":false},{"StartTime":114478.0,"Position":299.4618,"HyperDash":false},{"StartTime":114613.0,"Position":319.818817,"HyperDash":false}]},{"StartTime":114847.0,"Objects":[{"StartTime":114847.0,"Position":211.0,"HyperDash":false},{"StartTime":114925.0,"Position":253.437714,"HyperDash":false},{"StartTime":115003.0,"Position":252.875427,"HyperDash":false},{"StartTime":115081.0,"Position":297.313171,"HyperDash":false},{"StartTime":115159.0,"Position":320.7509,"HyperDash":false},{"StartTime":115219.0,"Position":346.8568,"HyperDash":false},{"StartTime":115315.0,"Position":375.6263,"HyperDash":false}]},{"StartTime":115629.0,"Objects":[{"StartTime":115629.0,"Position":141.0,"HyperDash":false}]},{"StartTime":115941.0,"Objects":[{"StartTime":115941.0,"Position":407.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2571731.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2571731.osu new file mode 100644 index 0000000000..bef278e769 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2571731.osu @@ -0,0 +1,277 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 2 + +[Difficulty] +HPDrainRate:2 +CircleSize:2 +OverallDifficulty:6 +ApproachRate:6 +SliderMultiplier:1.1 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +2,61250,70204 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +4,312.5,4,2,1,70,1,0 +7425,-100,4,2,1,75,0,0 +8754,-100,4,2,1,80,0,0 +9379,-100,4,2,1,80,0,0 +10004,-100,4,2,1,40,0,0 +10472,-100,4,2,1,55,0,0 +10629,-100,4,2,1,65,0,0 +10941,-100,4,2,1,70,0,0 +11254,-100,4,2,3,65,0,0 +12504,-100,4,2,3,65,0,0 +21254,-100,4,2,1,70,0,0 +30629,-100,4,2,1,70,0,0 +30941,-100,4,2,1,70,0,0 +31254,-100,4,2,1,70,0,0 +32191,-100,4,2,1,70,0,0 +32504,-100,4,2,1,70,0,0 +35629,-100,4,2,1,70,0,0 +36254,-100,4,2,1,70,0,0 +37035,-100,4,2,1,70,0,0 +37504,-100,4,2,1,70,0,0 +41722,-100,4,2,1,70,0,0 +42504,-100,4,2,1,70,0,0 +42816,-100,4,2,1,70,0,0 +43754,-100,4,2,1,70,0,0 +44222,-100,4,2,1,70,0,0 +44691,-100,4,2,1,70,0,0 +45004,-100,4,2,1,70,0,0 +45941,-100,4,2,1,70,0,0 +46254,-100,4,2,1,70,0,0 +46722,-100,4,2,1,70,0,0 +47504,-100,4,2,1,70,0,0 +47816,-100,4,2,1,70,0,0 +48441,-100,4,2,1,70,0,0 +48754,-100,4,2,1,70,0,0 +49222,-100,4,2,1,70,0,0 +50004,-100,4,2,1,70,0,0 +50082,-100,4,2,1,25,0,0 +50160,-100,4,2,1,25,0,0 +50238,-100,4,2,1,25,0,0 +50316,-100,4,2,1,25,0,0 +50394,-100,4,2,1,25,0,0 +50472,-100,4,2,1,25,0,0 +50550,-100,4,2,1,25,0,0 +50629,-100,4,2,1,25,0,0 +50707,-100,4,2,1,25,0,0 +50785,-100,4,2,1,25,0,0 +50863,-100,4,2,1,25,0,0 +50941,-100,4,2,1,70,0,0 +51254,-100,4,2,1,70,0,0 +53754,-100,4,2,1,70,0,0 +55004,-100,4,2,1,70,0,0 +56722,-100,4,2,1,70,0,0 +57972,-100,4,2,1,70,0,0 +58754,-100,4,2,1,70,0,0 +59847,-100,4,1,1,65,0,0 +71254,-100,4,2,1,70,0,0 +81254,-100,4,2,1,70,0,0 +83754,-100,4,2,1,70,0,0 +86254,-100,4,2,1,70,0,0 +87504,-100,4,2,1,70,0,0 +91254,-100,4,2,1,70,0,1 +93754,-100,4,2,1,70,0,1 +95004,-100,4,2,1,70,0,1 +100004,-100,4,2,1,70,0,1 +100550,-100,4,2,1,70,0,1 +100941,-100,4,2,1,70,0,1 +103754,-100,4,2,1,70,0,1 +105004,-100,4,2,1,70,0,1 +108754,-100,4,2,1,70,0,1 +110004,-100,4,2,1,70,0,1 +110629,-100,4,2,1,70,0,1 +112504,-100,4,2,1,70,0,1 +113129,-100,4,2,1,70,0,1 +113754,-100,4,2,1,70,0,1 +114379,-100,4,2,1,70,0,1 +114847,-100,4,2,1,70,0,1 +115863,-100,4,2,1,70,0,0 +115941,-100,4,2,1,70,0,1 +116019,-100,4,2,1,70,0,0 + +[HitObjects] +229,264,1254,6,0,P|161:183|254:125,1,220,4|8,1:2|0:0,0:0:0:0: +362,120,2191,1,2,0:0:0:0: +228,119,2504,2,0,L|87:118,1,110,0|2,1:0|0:0,0:0:0:0: +231,216,3129,2,0,L|372:215,1,110,8|2,0:0|0:0,0:0:0:0: +465,214,3754,6,0,P|439:111|303:80,1,220,0|10,1:0|0:0,0:0:0:0: +217,117,4691,1,2,0:0:0:0: +365,123,5004,2,0,L|367:252,1,110,0|2,1:0|0:0,0:0:0:0: +228,313,5629,2,0,L|357:315,1,110,8|2,2:0|0:0,0:0:0:0: +197,303,6254,6,0,P|98:270|59:136,1,220,4|8,1:2|0:0,0:0:0:0: +171,156,7191,1,2,0:0:0:0: +290,138,7504,2,0,L|308:275,1,110,0|2,1:0|0:0,0:0:0:0: +178,249,8129,1,8,0:0:0:0: +308,247,8441,1,2,0:0:0:0: +168,249,8754,6,0,L|53:245,1,110,4|0,1:2|3:0,0:0:0:0: +226,153,9379,2,0,L|343:149,1,110,2|2,1:3|3:2,0:0:0:0: +256,192,10004,12,2,10941,1:3:0:0: +173,329,11254,6,0,P|79:249|178:220,1,220,4|2,0:0|1:3,0:0:0:0: +263,211,12191,1,10,0:0:0:0: +119,212,12504,2,0,L|103:52,1,110,2|8,1:3|0:0,0:0:0:0: +246,65,13129,2,0,L|103:66,1,110,2|8,1:3|0:0,0:0:0:0: +290,64,13754,6,0,P|384:120|284:162,1,220,6|2,1:3|1:3,0:0:0:0: +182,220,14691,1,8,0:0:0:0: +335,208,15004,2,0,L|75:142,1,220,2|2,1:3|1:3,0:0:0:0: +275,153,15941,1,8,0:0:0:0: +120,151,16254,6,0,P|157:258|268:282,1,220,6|2,1:3|1:3,0:0:0:0: +405,290,17191,1,10,0:0:0:0: +250,286,17504,2,0,L|96:264,2,110,6|8|2,1:3|0:0|1:3,0:0:0:0: +403,275,18441,1,8,0:0:0:0: +250,286,18754,6,0,P|186:189|264:160,1,220,6|6,1:3|1:3,0:0:0:0: +404,157,19691,1,8,0:0:0:0: +249,151,20004,5,6,1:3:0:0: +245,233,20316,1,8,0:0:0:0: +240,317,20629,1,8,0:0:0:0: +399,222,20941,1,4,1:2:0:0: +240,317,21254,6,0,P|140:279|114:128,1,220,4|2,1:2|1:3,0:0:0:0: +243,184,22191,1,8,1:0:0:0: +386,178,22504,2,0,L|403:327,1,110,0|8,1:0|1:0,0:0:0:0: +264,338,23129,2,0,L|119:336,1,110,2|8,1:3|1:0,0:0:0:0: +292,300,23754,6,0,P|361:228|270:161,1,220,0|2,1:0|1:3,0:0:0:0: +147,160,24691,1,8,1:0:0:0: +285,124,25004,2,0,L|391:50,2,110,4|10|2,1:0|1:0|1:0,0:0:0:0: +428,128,25941,1,0,1:0:0:0: +284,130,26254,6,0,P|319:238|428:274,1,220,4|2,1:2|1:3,0:0:0:0: +268,276,27191,1,8,1:0:0:0: +124,277,27504,2,0,L|109:125,1,110,0|8,1:0|0:0,0:0:0:0: +250,126,28129,2,0,L|115:102,1,110,2|8,1:3|1:0,0:0:0:0: +284,96,28754,6,0,P|385:143|411:266,1,220,4|2,1:0|1:0,0:0:0:0: +273,240,29691,1,8,1:0:0:0: +416,236,30004,5,4,1:2:0:0: +436,94,30316,1,8,1:0:0:0: +294,75,30629,2,0,L|144:75,1,110,6|0,1:0|3:0,0:0:0:0: +351,138,31254,6,0,P|441:184|405:291,1,220,4|8,0:0|0:0,0:0:0:0: +277,246,32191,1,2,1:2:0:0: +144,299,32504,2,0,L|411:257,1,220,4|8,1:0|1:0,0:0:0:0: +201,244,33440,1,4,1:0:0:0: +140,283,33597,6,0,P|98:231|162:160,1,165,4|8,1:2|0:0,0:0:0:0: +239,112,34379,2,0,L|126:97,1,110,4|8,3:0|1:0,0:0:0:0: +264,173,35004,6,0,L|396:189,1,110,0|8,1:0|1:0,0:0:0:0: +223,227,35629,2,0,L|218:103,1,110,10|2,1:0|1:2,0:0:0:0: +379,115,36254,1,4,1:2:0:0: +398,117,37035,2,0,L|403:211,1,82.5,4|4,1:0|1:0,0:0:0:0: +284,252,37504,6,0,L|169:243,1,110,0|2,1:0|0:0,0:0:0:0: +305,327,38129,2,0,L|423:317,1,110,2|8,0:0|1:2,0:0:0:0: +415,223,38597,6,0,L|233:207,1,165,4|14,1:2|1:0,0:0:0:0: +113,252,39379,2,0,L|103:126,1,110,6|6,1:0|1:0,0:0:0:0: +244,114,40004,5,4,1:2:0:0: +250,185,40316,1,2,2:0:0:0: +253,260,40629,1,2,1:2:0:0: +89,272,40941,1,0,1:0:0:0: +256,305,41254,6,0,P|342:288|366:191,2,165,4|4|8,1:0|1:0|2:0,0:0:0:0: +98,202,42504,2,0,P|184:185|208:88,2,165,4|4|8,1:0|1:0|1:0,0:0:0:0: +249,82,43754,6,0,L|58:83,2,165,4|4|8,1:0|1:0|1:0,0:0:0:0: +256,192,45004,12,2,45941,1:0:0:0: +332,305,46254,6,0,P|396:289|434:187,2,165,4|0|0,1:0|1:0|1:0,0:0:0:0: +180,305,47504,2,0,P|116:289|78:187,2,165,4|4|0,1:0|1:0|1:0,0:0:0:0: +335,231,48754,6,0,L|145:232,2,165,4|4|8,1:0|1:0|1:0,0:0:0:0: +256,192,50004,12,2,50941,0:0:0:0: +119,198,51254,6,0,L|104:299,1,82.5,4|4,1:0|1:0,0:0:0:0: +230,290,51722,2,0,L|252:120,1,165,4|2,1:0|1:0,0:0:0:0: +373,113,52504,6,0,L|388:214,1,82.5,4|4,1:0|1:0,0:0:0:0: +269,207,52972,2,0,P|240:107|282:67,1,165,4|2,1:0|1:0,0:0:0:0: +424,88,53754,6,0,L|325:81,1,82.5,4|4,1:0|1:0,0:0:0:0: +228,196,54222,2,0,L|408:181,1,165,4|2,1:0|1:0,0:0:0:0: +241,238,55004,6,0,L|340:231,1,82.5,4|4,1:0|1:0,0:0:0:0: +437,346,55472,2,0,L|257:331,1,165,2|10,1:0|1:0,0:0:0:0: +130,320,56254,5,4,1:2:0:0: +260,244,56488,2,0,L|272:143,1,82.5,2|2,1:0|1:0,0:0:0:0: +162,106,56957,2,0,L|64:127,1,82.5,2|2,1:0|1:0,0:0:0:0: +233,322,57504,5,2,1:0:0:0: +363,246,57738,2,0,L|270:233,1,82.5,2|2,1:0|1:0,0:0:0:0: +174,230,58207,2,0,L|76:251,1,82.5,2|2,1:0|1:0,0:0:0:0: +261,143,58754,6,0,L|76:124,2,165,4|4|4,1:2|1:2|1:2,0:0:0:0: +256,192,60004,12,0,60941,1:0:0:0: +258,195,71254,5,0,1:0:0:0: +69,186,71722,2,0,L|59:367,1,165,2|2,0:0|0:0,0:0:0:0: +220,198,72504,1,2,0:0:0:0: +254,195,73754,5,4,0:0:0:0: +443,186,74222,2,0,L|453:367,1,165,2|2,0:0|0:0,0:0:0:0: +292,198,75004,1,2,0:0:0:0: +136,196,76254,5,4,0:0:0:0: +349,161,76722,2,0,L|165:160,1,165,2|2,0:0|0:0,0:0:0:0: +350,161,77504,1,2,0:0:0:0: +376,196,78754,5,4,0:0:0:0: +163,161,79222,2,0,L|347:160,1,165,0|2,0:0|0:0,0:0:0:0: +179,253,80004,1,2,0:0:0:0: +88,255,80316,1,2,0:0:0:0: +88,255,80629,1,2,0:0:0:0: +192,256,80941,1,6,1:2:0:0: +336,252,81254,6,0,L|220:254,1,110,4|10,1:2|1:0,0:0:0:0: +366,149,81879,2,0,L|482:151,1,110,4|10,1:2|1:0,0:0:0:0: +319,41,82504,1,4,1:2:0:0: +224,96,82816,1,8,1:0:0:0: +196,202,83129,1,4,1:2:0:0: +248,298,83441,1,10,1:0:0:0: +390,323,83754,6,0,L|271:324,2,110,4|2|2,1:2|0:0|0:0,0:0:0:0: +104,321,85004,1,4,1:2:0:0: +324,329,86254,5,4,1:0:0:0: +422,281,86566,1,4,1:0:0:0: +446,173,86879,1,4,1:0:0:0: +411,68,87191,1,4,1:0:0:0: +287,49,87504,6,0,L|402:51,1,110,4|6,1:0|1:0,0:0:0:0: +265,155,88129,2,0,L|141:153,1,110,4|6,1:0|1:0,0:0:0:0: +308,153,88754,5,2,1:0:0:0: +408,197,89066,1,2,1:0:0:0: +432,304,89379,1,2,1:0:0:0: +348,374,89691,1,2,1:0:0:0: +256,192,90004,12,4,90941,1:2:0:0: +140,282,91254,6,0,P|71:226|156:145,1,220,4|4,1:2|1:0,0:0:0:0: +268,155,92191,1,8,1:0:0:0: +405,152,92504,2,0,L|274:151,1,110,2|10,1:0|1:0,0:0:0:0: +154,250,93129,2,0,L|295:252,1,110,2|10,1:0|1:0,0:0:0:0: +135,329,93754,6,0,L|380:330,1,220,4|0,1:2|1:0,0:0:0:0: +239,290,94691,1,8,1:0:0:0: +354,223,95004,2,0,L|213:210,1,110,4|8,1:0|1:0,0:0:0:0: +97,240,95629,2,0,L|79:127,1,110,2|10,1:0|1:0,0:0:0:0: +238,55,96254,6,0,P|313:95|229:166,1,220,4|0,1:2|1:0,0:0:0:0: +363,205,97191,1,8,1:0:0:0: +252,247,97504,2,0,L|115:270,2,110,2|10|2,1:0|1:0|1:0,0:0:0:0: +363,287,98441,1,10,1:0:0:0: +223,343,98754,6,0,L|92:326,1,110,4|10,1:2|1:0,0:0:0:0: +293,262,99379,1,2,1:0:0:0: +396,244,99691,1,8,1:0:0:0: +236,219,100004,6,0,P|160:186|103:55,1,220,4|0,1:2|1:0,0:0:0:0: +226,68,100941,1,0,1:0:0:0: +383,69,101254,6,0,P|456:139|387:208,1,220,4|2,1:2|1:0,0:0:0:0: +261,244,102191,1,10,1:0:0:0: +123,311,102504,2,0,L|102:165,1,110,2|10,1:0|1:0,0:0:0:0: +252,178,103129,2,0,L|386:178,1,110,2|10,1:0|1:0,0:0:0:0: +215,263,103754,6,0,P|123:241|79:117,1,220,4|2,1:2|1:0,0:0:0:0: +216,121,104691,1,10,1:0:0:0: +359,106,105004,2,0,L|371:240,1,110,4|8,1:0|1:0,0:0:0:0: +215,312,105629,2,0,L|352:321,1,110,2|10,1:0|1:0,0:0:0:0: +164,359,106254,6,0,L|424:330,1,220,4|2,1:2|1:0,0:0:0:0: +244,297,107191,1,10,1:0:0:0: +390,278,107504,2,0,L|269:255,2,110,2|10|2,1:0|1:0|1:0,0:0:0:0: +244,276,108441,1,10,1:0:0:0: +99,281,108754,6,0,L|87:150,1,110,4|10,1:2|1:0,0:0:0:0: +228,146,109379,2,0,L|364:136,1,110,2|10,1:0|1:0,0:0:0:0: +183,278,110004,6,0,L|424:264,1,220,4|2,1:2|0:0,0:0:0:0: +266,255,110941,1,10,1:0:0:0: +114,283,111254,6,0,L|102:152,1,110,4|10,0:0|1:0,0:0:0:0: +243,148,111879,2,0,L|379:138,1,110,2|10,1:0|1:0,0:0:0:0: +198,280,112504,6,0,L|439:266,1,220,4|2,1:2|1:0,0:0:0:0: +281,257,113441,1,8,1:0:0:0: +137,295,113754,5,4,1:2:0:0: +127,239,113910,1,4,1:2:0:0: +314,108,114379,2,0,L|321:207,1,82.5,4|4,0:0|0:0,0:0:0:0: +211,254,114847,6,0,L|389:266,1,165,6|6,1:0|1:0,0:0:0:0: +265,275,115629,1,4,1:0:0:0: +407,299,115941,1,4,1:2:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2768615-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2768615-expected-conversion.json new file mode 100644 index 0000000000..2ebebdbe7a --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2768615-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":514.0,"Objects":[{"StartTime":514.0,"Position":6.0,"HyperDash":false},{"StartTime":586.0,"Position":0.0,"HyperDash":false},{"StartTime":695.0,"Position":8.064062,"HyperDash":false}]},{"StartTime":877.0,"Objects":[{"StartTime":877.0,"Position":20.0,"HyperDash":false}]},{"StartTime":1059.0,"Objects":[{"StartTime":1059.0,"Position":14.0,"HyperDash":false},{"StartTime":1131.0,"Position":3.79381847,"HyperDash":false},{"StartTime":1240.0,"Position":15.99557,"HyperDash":false}]},{"StartTime":1604.0,"Objects":[{"StartTime":1604.0,"Position":21.0,"HyperDash":false},{"StartTime":1685.0,"Position":19.13056,"HyperDash":false},{"StartTime":1767.0,"Position":38.2750778,"HyperDash":false},{"StartTime":1849.0,"Position":41.4195938,"HyperDash":false},{"StartTime":1967.0,"Position":26.0665855,"HyperDash":false}]},{"StartTime":2332.0,"Objects":[{"StartTime":2332.0,"Position":28.0,"HyperDash":false}]},{"StartTime":2513.0,"Objects":[{"StartTime":2513.0,"Position":27.0,"HyperDash":false},{"StartTime":2576.0,"Position":28.8067379,"HyperDash":false},{"StartTime":2640.0,"Position":27.6262817,"HyperDash":false},{"StartTime":2703.0,"Position":14.43302,"HyperDash":false},{"StartTime":2767.0,"Position":22.2525616,"HyperDash":false},{"StartTime":2831.0,"Position":19.0721054,"HyperDash":false},{"StartTime":2894.0,"Position":27.8788433,"HyperDash":false},{"StartTime":2958.0,"Position":46.6983871,"HyperDash":false},{"StartTime":3058.0,"Position":33.9789238,"HyperDash":false}]},{"StartTime":3423.0,"Objects":[{"StartTime":3423.0,"Position":46.0,"HyperDash":false},{"StartTime":3495.0,"Position":50.821064,"HyperDash":false},{"StartTime":3604.0,"Position":48.064064,"HyperDash":false}]},{"StartTime":3786.0,"Objects":[{"StartTime":3786.0,"Position":60.0,"HyperDash":false}]},{"StartTime":3968.0,"Objects":[{"StartTime":3968.0,"Position":54.0,"HyperDash":false},{"StartTime":4040.0,"Position":45.79382,"HyperDash":false},{"StartTime":4149.0,"Position":55.99557,"HyperDash":false}]},{"StartTime":4513.0,"Objects":[{"StartTime":4513.0,"Position":61.0,"HyperDash":false},{"StartTime":4585.0,"Position":66.82106,"HyperDash":false},{"StartTime":4694.0,"Position":63.064064,"HyperDash":false}]},{"StartTime":4877.0,"Objects":[{"StartTime":4877.0,"Position":65.0,"HyperDash":false},{"StartTime":4967.0,"Position":66.0687,"HyperDash":false},{"StartTime":5058.0,"Position":65.0,"HyperDash":false}]},{"StartTime":5241.0,"Objects":[{"StartTime":5241.0,"Position":68.0,"HyperDash":false},{"StartTime":5313.0,"Position":49.8210678,"HyperDash":false},{"StartTime":5422.0,"Position":70.064064,"HyperDash":false}]},{"StartTime":5604.0,"Objects":[{"StartTime":5604.0,"Position":77.0,"HyperDash":false},{"StartTime":5660.0,"Position":67.75663,"HyperDash":false},{"StartTime":5717.0,"Position":94.50892,"HyperDash":false},{"StartTime":5774.0,"Position":93.26121,"HyperDash":false},{"StartTime":5831.0,"Position":76.0135,"HyperDash":false},{"StartTime":5926.0,"Position":89.42635,"HyperDash":false},{"StartTime":6058.0,"Position":77.0,"HyperDash":false}]},{"StartTime":6332.0,"Objects":[{"StartTime":6332.0,"Position":96.0,"HyperDash":false}]},{"StartTime":6513.0,"Objects":[{"StartTime":6513.0,"Position":80.0,"HyperDash":false}]},{"StartTime":6877.0,"Objects":[{"StartTime":6877.0,"Position":108.0,"HyperDash":false}]},{"StartTime":7059.0,"Objects":[{"StartTime":7059.0,"Position":96.0,"HyperDash":false},{"StartTime":7131.0,"Position":91.4434738,"HyperDash":false},{"StartTime":7240.0,"Position":98.95893,"HyperDash":false}]},{"StartTime":7423.0,"Objects":[{"StartTime":7423.0,"Position":101.0,"HyperDash":false},{"StartTime":7495.0,"Position":79.8501,"HyperDash":false},{"StartTime":7604.0,"Position":96.87991,"HyperDash":false}]},{"StartTime":7786.0,"Objects":[{"StartTime":7786.0,"Position":115.0,"HyperDash":false}]},{"StartTime":7968.0,"Objects":[{"StartTime":7968.0,"Position":95.0,"HyperDash":false}]},{"StartTime":8150.0,"Objects":[{"StartTime":8150.0,"Position":127.0,"HyperDash":false}]},{"StartTime":8332.0,"Objects":[{"StartTime":8332.0,"Position":110.0,"HyperDash":false},{"StartTime":8404.0,"Position":123.196411,"HyperDash":false},{"StartTime":8513.0,"Position":105.877426,"HyperDash":false}]},{"StartTime":8695.0,"Objects":[{"StartTime":8695.0,"Position":110.0,"HyperDash":false},{"StartTime":8767.0,"Position":104.810783,"HyperDash":false},{"StartTime":8876.0,"Position":113.827438,"HyperDash":false}]},{"StartTime":9241.0,"Objects":[{"StartTime":9241.0,"Position":138.0,"HyperDash":false}]},{"StartTime":9423.0,"Objects":[{"StartTime":9423.0,"Position":131.0,"HyperDash":false},{"StartTime":9495.0,"Position":133.615219,"HyperDash":false},{"StartTime":9604.0,"Position":128.964035,"HyperDash":false}]},{"StartTime":9786.0,"Objects":[{"StartTime":9786.0,"Position":143.0,"HyperDash":false}]},{"StartTime":9968.0,"Objects":[{"StartTime":9968.0,"Position":136.0,"HyperDash":false},{"StartTime":10027.0,"Position":122.678848,"HyperDash":false},{"StartTime":10086.0,"Position":134.039719,"HyperDash":false},{"StartTime":10145.0,"Position":136.077042,"HyperDash":false},{"StartTime":10240.0,"Position":132.939224,"HyperDash":false}]},{"StartTime":10332.0,"Objects":[{"StartTime":10332.0,"Position":139.0,"HyperDash":false},{"StartTime":10391.0,"Position":156.013535,"HyperDash":false},{"StartTime":10450.0,"Position":128.715408,"HyperDash":false},{"StartTime":10509.0,"Position":125.074265,"HyperDash":false},{"StartTime":10604.0,"Position":141.969208,"HyperDash":false}]},{"StartTime":10695.0,"Objects":[{"StartTime":10695.0,"Position":150.0,"HyperDash":false}]},{"StartTime":10877.0,"Objects":[{"StartTime":10877.0,"Position":146.0,"HyperDash":false}]},{"StartTime":11059.0,"Objects":[{"StartTime":11059.0,"Position":156.0,"HyperDash":false}]},{"StartTime":11241.0,"Objects":[{"StartTime":11241.0,"Position":150.0,"HyperDash":false}]},{"StartTime":11423.0,"Objects":[{"StartTime":11423.0,"Position":156.0,"HyperDash":false},{"StartTime":11513.0,"Position":158.989288,"HyperDash":false}]},{"StartTime":11604.0,"Objects":[{"StartTime":11604.0,"Position":157.0,"HyperDash":false}]},{"StartTime":11695.0,"Objects":[{"StartTime":11695.0,"Position":163.0,"HyperDash":false}]},{"StartTime":11786.0,"Objects":[{"StartTime":11786.0,"Position":161.0,"HyperDash":false},{"StartTime":11876.0,"Position":161.977341,"HyperDash":false}]},{"StartTime":11968.0,"Objects":[{"StartTime":11968.0,"Position":165.0,"HyperDash":false},{"StartTime":12058.0,"Position":165.977341,"HyperDash":false}]},{"StartTime":12150.0,"Objects":[{"StartTime":12150.0,"Position":166.0,"HyperDash":false},{"StartTime":12222.0,"Position":150.82106,"HyperDash":false},{"StartTime":12331.0,"Position":168.064056,"HyperDash":false}]},{"StartTime":12513.0,"Objects":[{"StartTime":12513.0,"Position":180.0,"HyperDash":false}]},{"StartTime":12695.0,"Objects":[{"StartTime":12695.0,"Position":174.0,"HyperDash":false},{"StartTime":12747.0,"Position":187.463669,"HyperDash":false},{"StartTime":12799.0,"Position":191.927322,"HyperDash":false},{"StartTime":12851.0,"Position":174.390991,"HyperDash":false},{"StartTime":12904.0,"Position":168.863571,"HyperDash":false},{"StartTime":12956.0,"Position":175.32724,"HyperDash":false},{"StartTime":13008.0,"Position":195.7909,"HyperDash":false},{"StartTime":13060.0,"Position":171.254562,"HyperDash":false},{"StartTime":13149.0,"Position":178.048141,"HyperDash":false}]},{"StartTime":13241.0,"Objects":[{"StartTime":13241.0,"Position":183.0,"HyperDash":false},{"StartTime":13313.0,"Position":187.17894,"HyperDash":false},{"StartTime":13422.0,"Position":180.935944,"HyperDash":false}]},{"StartTime":13604.0,"Objects":[{"StartTime":13604.0,"Position":191.0,"HyperDash":false}]},{"StartTime":13786.0,"Objects":[{"StartTime":13786.0,"Position":185.0,"HyperDash":false}]},{"StartTime":13968.0,"Objects":[{"StartTime":13968.0,"Position":197.0,"HyperDash":false}]},{"StartTime":14150.0,"Objects":[{"StartTime":14150.0,"Position":191.0,"HyperDash":false},{"StartTime":14213.0,"Position":204.4671,"HyperDash":false},{"StartTime":14277.0,"Position":208.941635,"HyperDash":false},{"StartTime":14340.0,"Position":209.408737,"HyperDash":false},{"StartTime":14404.0,"Position":207.88327,"HyperDash":false},{"StartTime":14468.0,"Position":210.357788,"HyperDash":false},{"StartTime":14531.0,"Position":202.82489,"HyperDash":false},{"StartTime":14595.0,"Position":177.299423,"HyperDash":false},{"StartTime":14695.0,"Position":195.040863,"HyperDash":false}]},{"StartTime":15059.0,"Objects":[{"StartTime":15059.0,"Position":217.0,"HyperDash":false}]},{"StartTime":15150.0,"Objects":[{"StartTime":15150.0,"Position":197.0,"HyperDash":false}]},{"StartTime":15240.0,"Objects":[{"StartTime":15240.0,"Position":219.0,"HyperDash":false}]},{"StartTime":15422.0,"Objects":[{"StartTime":15422.0,"Position":209.0,"HyperDash":false}]},{"StartTime":15604.0,"Objects":[{"StartTime":15604.0,"Position":214.0,"HyperDash":false},{"StartTime":15656.0,"Position":219.463669,"HyperDash":false},{"StartTime":15708.0,"Position":202.927322,"HyperDash":false},{"StartTime":15760.0,"Position":200.390991,"HyperDash":false},{"StartTime":15813.0,"Position":233.863571,"HyperDash":false},{"StartTime":15865.0,"Position":208.32724,"HyperDash":false},{"StartTime":15917.0,"Position":232.7909,"HyperDash":false},{"StartTime":15969.0,"Position":220.254562,"HyperDash":false},{"StartTime":16058.0,"Position":218.048141,"HyperDash":false}]},{"StartTime":16150.0,"Objects":[{"StartTime":16150.0,"Position":223.0,"HyperDash":false},{"StartTime":16218.0,"Position":212.065735,"HyperDash":false},{"StartTime":16286.0,"Position":221.13147,"HyperDash":false},{"StartTime":16422.0,"Position":223.0,"HyperDash":false}]},{"StartTime":16513.0,"Objects":[{"StartTime":16513.0,"Position":231.0,"HyperDash":false}]},{"StartTime":16695.0,"Objects":[{"StartTime":16695.0,"Position":227.0,"HyperDash":false}]},{"StartTime":16785.0,"Objects":[{"StartTime":16785.0,"Position":233.0,"HyperDash":false}]},{"StartTime":16877.0,"Objects":[{"StartTime":16877.0,"Position":231.0,"HyperDash":false},{"StartTime":16949.0,"Position":232.82106,"HyperDash":false},{"StartTime":17058.0,"Position":233.064056,"HyperDash":false}]},{"StartTime":17241.0,"Objects":[{"StartTime":17241.0,"Position":236.0,"HyperDash":false},{"StartTime":17297.0,"Position":226.466782,"HyperDash":false},{"StartTime":17354.0,"Position":236.9419,"HyperDash":false},{"StartTime":17411.0,"Position":219.417,"HyperDash":false},{"StartTime":17468.0,"Position":237.89212,"HyperDash":false},{"StartTime":17563.0,"Position":221.100266,"HyperDash":false},{"StartTime":17695.0,"Position":236.0,"HyperDash":false}]},{"StartTime":18150.0,"Objects":[{"StartTime":18150.0,"Position":254.0,"HyperDash":false}]},{"StartTime":18331.0,"Objects":[{"StartTime":18331.0,"Position":242.0,"HyperDash":false}]},{"StartTime":18695.0,"Objects":[{"StartTime":18695.0,"Position":264.0,"HyperDash":false}]},{"StartTime":18877.0,"Objects":[{"StartTime":18877.0,"Position":250.0,"HyperDash":false}]},{"StartTime":19059.0,"Objects":[{"StartTime":19059.0,"Position":261.0,"HyperDash":false},{"StartTime":19149.0,"Position":273.538757,"HyperDash":false},{"StartTime":19240.0,"Position":265.1201,"HyperDash":false},{"StartTime":19313.0,"Position":252.953827,"HyperDash":false},{"StartTime":19422.0,"Position":261.0,"HyperDash":false}]},{"StartTime":19604.0,"Objects":[{"StartTime":19604.0,"Position":267.0,"HyperDash":false}]},{"StartTime":19786.0,"Objects":[{"StartTime":19786.0,"Position":271.0,"HyperDash":false}]},{"StartTime":19876.0,"Objects":[{"StartTime":19876.0,"Position":269.0,"HyperDash":false}]},{"StartTime":19968.0,"Objects":[{"StartTime":19968.0,"Position":271.0,"HyperDash":false},{"StartTime":20058.0,"Position":271.71347,"HyperDash":false},{"StartTime":20149.0,"Position":271.0,"HyperDash":false}]},{"StartTime":20331.0,"Objects":[{"StartTime":20331.0,"Position":278.0,"HyperDash":false}]},{"StartTime":20422.0,"Objects":[{"StartTime":20422.0,"Position":276.0,"HyperDash":false},{"StartTime":20494.0,"Position":256.991028,"HyperDash":false},{"StartTime":20603.0,"Position":276.034363,"HyperDash":false}]},{"StartTime":20695.0,"Objects":[{"StartTime":20695.0,"Position":281.0,"HyperDash":false},{"StartTime":20767.0,"Position":276.1846,"HyperDash":false},{"StartTime":20876.0,"Position":282.9045,"HyperDash":false}]},{"StartTime":21059.0,"Objects":[{"StartTime":21059.0,"Position":290.0,"HyperDash":false}]},{"StartTime":21240.0,"Objects":[{"StartTime":21240.0,"Position":291.0,"HyperDash":false},{"StartTime":21312.0,"Position":274.790222,"HyperDash":false},{"StartTime":21421.0,"Position":290.691162,"HyperDash":false}]},{"StartTime":21604.0,"Objects":[{"StartTime":21604.0,"Position":301.0,"HyperDash":false}]},{"StartTime":21786.0,"Objects":[{"StartTime":21786.0,"Position":296.0,"HyperDash":false},{"StartTime":21858.0,"Position":307.443481,"HyperDash":false},{"StartTime":21967.0,"Position":298.958923,"HyperDash":false}]},{"StartTime":22150.0,"Objects":[{"StartTime":22150.0,"Position":301.0,"HyperDash":false},{"StartTime":22222.0,"Position":295.69693,"HyperDash":false},{"StartTime":22331.0,"Position":296.97644,"HyperDash":false}]},{"StartTime":22513.0,"Objects":[{"StartTime":22513.0,"Position":315.0,"HyperDash":false}]},{"StartTime":22695.0,"Objects":[{"StartTime":22695.0,"Position":307.0,"HyperDash":false}]},{"StartTime":22786.0,"Objects":[{"StartTime":22786.0,"Position":315.0,"HyperDash":false}]},{"StartTime":22877.0,"Objects":[{"StartTime":22877.0,"Position":307.0,"HyperDash":false}]},{"StartTime":22968.0,"Objects":[{"StartTime":22968.0,"Position":319.0,"HyperDash":false}]},{"StartTime":23059.0,"Objects":[{"StartTime":23059.0,"Position":309.0,"HyperDash":false}]},{"StartTime":23150.0,"Objects":[{"StartTime":23150.0,"Position":321.0,"HyperDash":false}]},{"StartTime":23240.0,"Objects":[{"StartTime":23240.0,"Position":316.0,"HyperDash":false},{"StartTime":23330.0,"Position":305.998932,"HyperDash":false}]},{"StartTime":23421.0,"Objects":[{"StartTime":23421.0,"Position":332.0,"HyperDash":false}]},{"StartTime":23604.0,"Objects":[{"StartTime":23604.0,"Position":319.0,"HyperDash":false},{"StartTime":23694.0,"Position":319.977325,"HyperDash":false}]},{"StartTime":23786.0,"Objects":[{"StartTime":23786.0,"Position":323.0,"HyperDash":false},{"StartTime":23876.0,"Position":323.977325,"HyperDash":false}]},{"StartTime":23968.0,"Objects":[{"StartTime":23968.0,"Position":332.0,"HyperDash":false}]},{"StartTime":24150.0,"Objects":[{"StartTime":24150.0,"Position":328.0,"HyperDash":false},{"StartTime":24222.0,"Position":344.178925,"HyperDash":false},{"StartTime":24331.0,"Position":325.935944,"HyperDash":false}]},{"StartTime":24513.0,"Objects":[{"StartTime":24513.0,"Position":336.0,"HyperDash":false}]},{"StartTime":24695.0,"Objects":[{"StartTime":24695.0,"Position":340.0,"HyperDash":false}]},{"StartTime":24787.0,"Objects":[{"StartTime":24787.0,"Position":337.0,"HyperDash":false},{"StartTime":24877.0,"Position":336.002228,"HyperDash":false}]},{"StartTime":25059.0,"Objects":[{"StartTime":25059.0,"Position":341.0,"HyperDash":false},{"StartTime":25131.0,"Position":323.178925,"HyperDash":false},{"StartTime":25240.0,"Position":338.935944,"HyperDash":false}]},{"StartTime":25422.0,"Objects":[{"StartTime":25422.0,"Position":363.0,"HyperDash":false}]},{"StartTime":25604.0,"Objects":[{"StartTime":25604.0,"Position":351.0,"HyperDash":false},{"StartTime":25694.0,"Position":351.997772,"HyperDash":false},{"StartTime":25785.0,"Position":351.0,"HyperDash":false}]},{"StartTime":25968.0,"Objects":[{"StartTime":25968.0,"Position":356.0,"HyperDash":false}]},{"StartTime":26059.0,"Objects":[{"StartTime":26059.0,"Position":354.0,"HyperDash":false}]},{"StartTime":26149.0,"Objects":[{"StartTime":26149.0,"Position":356.0,"HyperDash":false},{"StartTime":26239.0,"Position":362.0235,"HyperDash":false},{"StartTime":26330.0,"Position":358.064056,"HyperDash":false},{"StartTime":26403.0,"Position":376.239563,"HyperDash":false},{"StartTime":26512.0,"Position":356.0,"HyperDash":false}]},{"StartTime":26877.0,"Objects":[{"StartTime":26877.0,"Position":374.0,"HyperDash":false}]},{"StartTime":27059.0,"Objects":[{"StartTime":27059.0,"Position":364.0,"HyperDash":false}]},{"StartTime":27149.0,"Objects":[{"StartTime":27149.0,"Position":376.0,"HyperDash":false}]},{"StartTime":27240.0,"Objects":[{"StartTime":27240.0,"Position":371.0,"HyperDash":false},{"StartTime":27330.0,"Position":371.0,"HyperDash":false},{"StartTime":27421.0,"Position":371.0,"HyperDash":false}]},{"StartTime":27604.0,"Objects":[{"StartTime":27604.0,"Position":381.0,"HyperDash":false}]},{"StartTime":27696.0,"Objects":[{"StartTime":27696.0,"Position":377.0,"HyperDash":false},{"StartTime":27786.0,"Position":377.71347,"HyperDash":false}]},{"StartTime":27968.0,"Objects":[{"StartTime":27968.0,"Position":381.0,"HyperDash":false},{"StartTime":28040.0,"Position":390.178925,"HyperDash":false},{"StartTime":28149.0,"Position":378.935944,"HyperDash":false}]},{"StartTime":28331.0,"Objects":[{"StartTime":28331.0,"Position":393.0,"HyperDash":false}]},{"StartTime":28513.0,"Objects":[{"StartTime":28513.0,"Position":385.0,"HyperDash":false}]},{"StartTime":28604.0,"Objects":[{"StartTime":28604.0,"Position":395.0,"HyperDash":false}]},{"StartTime":28695.0,"Objects":[{"StartTime":28695.0,"Position":391.0,"HyperDash":false},{"StartTime":28767.0,"Position":401.821075,"HyperDash":false},{"StartTime":28876.0,"Position":393.064056,"HyperDash":false}]},{"StartTime":29059.0,"Objects":[{"StartTime":29059.0,"Position":398.0,"HyperDash":false},{"StartTime":29115.0,"Position":401.513763,"HyperDash":false},{"StartTime":29172.0,"Position":404.01886,"HyperDash":false},{"StartTime":29229.0,"Position":412.523956,"HyperDash":false},{"StartTime":29286.0,"Position":396.029053,"HyperDash":false},{"StartTime":29381.0,"Position":381.8539,"HyperDash":false},{"StartTime":29513.0,"Position":398.0,"HyperDash":false}]},{"StartTime":29786.0,"Objects":[{"StartTime":29786.0,"Position":416.0,"HyperDash":false}]},{"StartTime":29967.0,"Objects":[{"StartTime":29967.0,"Position":400.0,"HyperDash":false}]},{"StartTime":30331.0,"Objects":[{"StartTime":30331.0,"Position":426.0,"HyperDash":false}]},{"StartTime":30513.0,"Objects":[{"StartTime":30513.0,"Position":408.0,"HyperDash":false}]},{"StartTime":30605.0,"Objects":[{"StartTime":30605.0,"Position":418.0,"HyperDash":false},{"StartTime":30695.0,"Position":418.71347,"HyperDash":false}]},{"StartTime":30877.0,"Objects":[{"StartTime":30877.0,"Position":421.0,"HyperDash":false},{"StartTime":30949.0,"Position":422.1499,"HyperDash":false},{"StartTime":31058.0,"Position":425.1201,"HyperDash":false}]},{"StartTime":31240.0,"Objects":[{"StartTime":31240.0,"Position":427.0,"HyperDash":false}]},{"StartTime":31422.0,"Objects":[{"StartTime":31422.0,"Position":431.0,"HyperDash":false}]},{"StartTime":31513.0,"Objects":[{"StartTime":31513.0,"Position":429.0,"HyperDash":false}]},{"StartTime":31603.0,"Objects":[{"StartTime":31603.0,"Position":433.0,"HyperDash":false}]},{"StartTime":31695.0,"Objects":[{"StartTime":31695.0,"Position":419.0,"HyperDash":false}]},{"StartTime":31786.0,"Objects":[{"StartTime":31786.0,"Position":434.0,"HyperDash":false},{"StartTime":31858.0,"Position":444.725983,"HyperDash":false},{"StartTime":31967.0,"Position":435.019226,"HyperDash":false}]},{"StartTime":32149.0,"Objects":[{"StartTime":32149.0,"Position":442.0,"HyperDash":false},{"StartTime":32221.0,"Position":443.390778,"HyperDash":false},{"StartTime":32330.0,"Position":440.069153,"HyperDash":false}]},{"StartTime":32695.0,"Objects":[{"StartTime":32695.0,"Position":452.0,"HyperDash":false}]},{"StartTime":32877.0,"Objects":[{"StartTime":32877.0,"Position":451.0,"HyperDash":false},{"StartTime":32949.0,"Position":434.066742,"HyperDash":false},{"StartTime":33058.0,"Position":450.7317,"HyperDash":false}]},{"StartTime":33240.0,"Objects":[{"StartTime":33240.0,"Position":461.0,"HyperDash":false}]},{"StartTime":33422.0,"Objects":[{"StartTime":33422.0,"Position":451.0,"HyperDash":false}]},{"StartTime":33513.0,"Objects":[{"StartTime":33513.0,"Position":457.0,"HyperDash":false},{"StartTime":33585.0,"Position":444.147736,"HyperDash":false},{"StartTime":33694.0,"Position":445.292816,"HyperDash":false}]},{"StartTime":33786.0,"Objects":[{"StartTime":33786.0,"Position":479.0,"HyperDash":false}]},{"StartTime":33877.0,"Objects":[{"StartTime":33877.0,"Position":462.0,"HyperDash":false},{"StartTime":33967.0,"Position":462.0,"HyperDash":false}]},{"StartTime":34149.0,"Objects":[{"StartTime":34149.0,"Position":470.0,"HyperDash":false}]},{"StartTime":34331.0,"Objects":[{"StartTime":34331.0,"Position":450.0,"HyperDash":false}]},{"StartTime":34422.0,"Objects":[{"StartTime":34422.0,"Position":450.0,"HyperDash":false}]},{"StartTime":34513.0,"Objects":[{"StartTime":34513.0,"Position":490.0,"HyperDash":false}]},{"StartTime":34604.0,"Objects":[{"StartTime":34604.0,"Position":472.0,"HyperDash":false}]},{"StartTime":34695.0,"Objects":[{"StartTime":34695.0,"Position":489.0,"HyperDash":false}]},{"StartTime":34877.0,"Objects":[{"StartTime":34877.0,"Position":476.0,"HyperDash":false},{"StartTime":34967.0,"Position":474.8665,"HyperDash":false}]},{"StartTime":35059.0,"Objects":[{"StartTime":35059.0,"Position":483.0,"HyperDash":false}]},{"StartTime":35240.0,"Objects":[{"StartTime":35240.0,"Position":479.0,"HyperDash":false},{"StartTime":35330.0,"Position":479.977325,"HyperDash":false}]},{"StartTime":35422.0,"Objects":[{"StartTime":35422.0,"Position":483.0,"HyperDash":false},{"StartTime":35512.0,"Position":483.977325,"HyperDash":false}]},{"StartTime":35604.0,"Objects":[{"StartTime":35604.0,"Position":287.0,"HyperDash":false},{"StartTime":35692.0,"Position":361.0,"HyperDash":false},{"StartTime":35780.0,"Position":479.0,"HyperDash":false},{"StartTime":35868.0,"Position":346.0,"HyperDash":false},{"StartTime":35956.0,"Position":266.0,"HyperDash":false},{"StartTime":36044.0,"Position":400.0,"HyperDash":false},{"StartTime":36132.0,"Position":202.0,"HyperDash":false},{"StartTime":36220.0,"Position":500.0,"HyperDash":false},{"StartTime":36308.0,"Position":80.0,"HyperDash":false},{"StartTime":36396.0,"Position":399.0,"HyperDash":false},{"StartTime":36484.0,"Position":455.0,"HyperDash":false},{"StartTime":36572.0,"Position":105.0,"HyperDash":false},{"StartTime":36660.0,"Position":100.0,"HyperDash":false},{"StartTime":36748.0,"Position":195.0,"HyperDash":false},{"StartTime":36836.0,"Position":106.0,"HyperDash":false},{"StartTime":36924.0,"Position":305.0,"HyperDash":false},{"StartTime":37013.0,"Position":225.0,"HyperDash":false}]},{"StartTime":37059.0,"Objects":[{"StartTime":37059.0,"Position":79.0,"HyperDash":false},{"StartTime":37124.0,"Position":38.0,"HyperDash":false},{"StartTime":37189.0,"Position":99.0,"HyperDash":false},{"StartTime":37254.0,"Position":79.0,"HyperDash":false},{"StartTime":37320.0,"Position":169.0,"HyperDash":false},{"StartTime":37385.0,"Position":238.0,"HyperDash":false},{"StartTime":37450.0,"Position":511.0,"HyperDash":false},{"StartTime":37516.0,"Position":58.0,"HyperDash":false},{"StartTime":37581.0,"Position":368.0,"HyperDash":false},{"StartTime":37646.0,"Position":52.0,"HyperDash":false},{"StartTime":37712.0,"Position":327.0,"HyperDash":false},{"StartTime":37777.0,"Position":226.0,"HyperDash":false},{"StartTime":37842.0,"Position":110.0,"HyperDash":false},{"StartTime":37908.0,"Position":3.0,"HyperDash":false},{"StartTime":37973.0,"Position":26.0,"HyperDash":false},{"StartTime":38038.0,"Position":173.0,"HyperDash":false},{"StartTime":38104.0,"Position":18.0,"HyperDash":false},{"StartTime":38169.0,"Position":310.0,"HyperDash":false},{"StartTime":38234.0,"Position":394.0,"HyperDash":false},{"StartTime":38299.0,"Position":406.0,"HyperDash":false},{"StartTime":38365.0,"Position":262.0,"HyperDash":false},{"StartTime":38430.0,"Position":278.0,"HyperDash":false},{"StartTime":38495.0,"Position":171.0,"HyperDash":false},{"StartTime":38561.0,"Position":22.0,"HyperDash":false},{"StartTime":38626.0,"Position":187.0,"HyperDash":false},{"StartTime":38691.0,"Position":124.0,"HyperDash":false},{"StartTime":38757.0,"Position":454.0,"HyperDash":false},{"StartTime":38822.0,"Position":16.0,"HyperDash":false},{"StartTime":38887.0,"Position":61.0,"HyperDash":false},{"StartTime":38953.0,"Position":161.0,"HyperDash":false},{"StartTime":39018.0,"Position":243.0,"HyperDash":false},{"StartTime":39083.0,"Position":375.0,"HyperDash":false},{"StartTime":39149.0,"Position":247.0,"HyperDash":false}]},{"StartTime":40695.0,"Objects":[{"StartTime":40695.0,"Position":496.0,"HyperDash":false},{"StartTime":40767.0,"Position":489.6517,"HyperDash":false},{"StartTime":40876.0,"Position":495.903229,"HyperDash":false}]},{"StartTime":41059.0,"Objects":[{"StartTime":41059.0,"Position":510.0,"HyperDash":false}]},{"StartTime":41150.0,"Objects":[{"StartTime":41150.0,"Position":498.0,"HyperDash":false}]},{"StartTime":41240.0,"Objects":[{"StartTime":41240.0,"Position":505.0,"HyperDash":false},{"StartTime":41285.0,"Position":505.934265,"HyperDash":false},{"StartTime":41330.0,"Position":505.0,"HyperDash":false},{"StartTime":41376.0,"Position":505.934265,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2768615.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2768615.osu new file mode 100644 index 0000000000..19439172cd --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2768615.osu @@ -0,0 +1,200 @@ +osu file format v14 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +HPDrainRate:4.5 +CircleSize:9 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:1.2 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +514,727.272727272727,4,2,1,50,1,0 +9241,-83.3333333333333,4,2,1,50,0,0 +9968,-100,4,2,1,50,0,0 +10241,-100,4,2,99,5,0,0 +10332,-100,4,2,1,50,0,0 +10604,-100,4,2,99,5,0,0 +10695,-100,4,2,1,50,0,0 +11423,-66.6666666666667,4,2,1,50,0,0 +12150,-100,4,2,1,50,0,0 +13150,-100,4,2,99,5,0,0 +13241,-100,4,2,1,50,0,0 +16059,-100,4,2,99,5,0,0 +16150,-100,4,2,1,50,0,0 +17241,909.090909090909,4,2,1,50,1,0 +17241,-83.3333333333333,4,2,1,50,0,0 +18150,727.272727272727,4,2,1,50,1,0 +20604,-100,4,2,99,5,0,0 +20695,-100,4,2,1,50,0,0 +21059,-83.3333333333333,4,2,1,50,0,0 +21786,-100,4,2,1,50,0,0 +23240,-66.6666666666667,4,2,1,50,0,0 +23968,-100,4,2,1,50,0,0 +32695,-83.3333333333333,4,2,1,50,0,0 +33422,-100,4,2,1,50,0,0 +33695,-100,4,2,99,5,0,0 +33786,-100,4,2,1,50,0,0 +34877,-66.6666666666667,4,2,1,50,0,0 +37013,-100,4,2,1,45,0,0 +39149,-100,4,2,1,40,0,0 +40695,-66.6666666666667,4,2,1,35,0,0 + +[HitObjects] +6,0,514,6,0,L|8:29,1,30,2|0,3:2|0:0,0:0:0:0: +14,66,877,1,0,0:0:0:0: +14,66,1059,2,0,L|16:36,1,30,0|0,0:0|1:0,0:0:0:0: +21,31,1604,6,0,L|26:90,1,60,2|0,0:0|3:0,0:0:0:0: +27,186,2332,1,0,3:0:0:0: +27,186,2513,2,0,L|34:96,1,90,2|0,0:0|0:0,0:0:0:0: +46,269,3423,38,0,L|48:298,1,30,2|0,3:2|0:0,0:0:0:0: +54,335,3786,1,0,0:0:0:0: +54,335,3968,2,0,L|56:305,1,30,0|0,0:0|1:0,0:0:0:0: +61,300,4513,6,0,L|63:271,1,30,2|0,0:0|0:0,0:0:0:0: +65,200,4877,2,0,L|66:186,2,15,0|0|0,3:2|0:0|0:0,0:0:0:0: +68,265,5241,2,0,L|70:236,1,30,2|0,3:2|0:0,0:0:0:0: +77,335,5604,6,0,L|76:373,2,37.5,0|0|0,1:0|3:0|3:0,0:0:0:0: +86,152,6332,21,2,3:2:0:0: +88,199,6513,1,2,0:0:0:0: +94,157,6877,5,2,0:0:0:0: +96,204,7059,2,0,P|100:218|99:233,1,30,2|0,1:2|0:0,0:0:0:0: +101,161,7423,2,0,P|96:146|97:131,1,30,2|0,0:2|0:1,0:0:0:0: +106,85,7786,37,0,3:3:0:0: +105,49,7968,1,0,0:3:0:0: +111,82,8150,1,2,3:3:0:0: +110,45,8332,2,0,P|110:30|106:16,1,30,2|0,0:3|0:0,0:0:0:0: +110,87,8695,2,0,P|110:102|114:117,1,30,2|0,0:3|0:0,0:0:0:0: +126,290,9241,5,4,3:3:0:0: +131,232,9423,2,0,B|134:220|124:211|124:211|129:218|129:223,1,35.9999989013672,8|0,0:3|0:0,0:0:0:0: +136,297,9786,37,4,0:3:0:0: +136,234,9968,2,0,P|138:212|133:190,1,45,2|0,1:2|0:0,0:0:0:0: +139,191,10332,2,0,P|144:212|142:235,1,45,2|0,0:0|0:0,0:0:0:0: +146,264,10695,5,2,3:1:0:0: +148,306,10877,1,2,0:1:0:0: +151,267,11059,1,2,3:1:0:0: +153,309,11241,1,2,0:1:0:0: +156,351,11423,6,0,B|158:362|158:362|159:354|159:350,1,22.5000008583069,2|0,1:0|0:0,0:0:0:0: +158,311,11604,1,2,0:3:0:0: +160,289,11695,1,2,0:3:0:0: +161,267,11786,2,0,L|162:244,1,22.5000008583069,2|0,0:3|0:0,0:0:0:0: +165,268,11968,2,0,L|166:245,1,22.5000008583069,2|0,0:3|0:0,0:0:0:0: +166,187,12150,22,0,L|168:158,1,30,2|0,3:2|0:0,0:0:0:0: +174,121,12513,1,2,0:0:0:0: +174,121,12695,2,0,L|178:195,1,75,2|0,0:0|0:0,0:0:0:0: +183,199,13241,2,0,L|181:170,1,30,2|0,0:0|0:0,0:0:0:0: +186,72,13604,5,0,3:0:0:0: +188,35,13786,1,0,3:0:0:0: +191,0,13968,1,0,3:2:0:0: +191,0,14150,2,0,L|195:89,1,90,2|2,0:0|0:0,0:0:0:0: +206,181,15059,37,2,3:2:0:0: +207,167,15150,1,2,0:0:0:0: +208,152,15240,1,2,0:0:0:0: +214,115,15422,1,2,0:0:0:0: +214,115,15604,2,0,L|218:189,1,75,2|0,0:0|0:0,0:0:0:0: +223,193,16150,6,0,L|221:169,2,22.5,2|2|2,0:0|0:0|0:0,0:0:0:0: +226,228,16513,1,2,3:1:0:0: +229,263,16695,1,2,0:1:0:0: +230,277,16785,1,2,0:1:0:0: +231,292,16877,2,0,L|233:321,1,30,2|0,3:1|0:0,0:0:0:0: +236,218,17241,6,0,L|238:180,2,35.9999989013672,0|0|0,1:0|3:0|3:0,0:0:0:0: +246,362,18150,21,2,3:2:0:0: +248,315,18331,1,2,0:0:0:0: +253,358,18695,5,2,0:0:0:0: +257,310,18877,1,2,1:2:0:0: +261,354,19059,2,0,P|266:369|265:384,2,30,2|0|0,0:0|0:1|0:0,0:0:0:0: +266,305,19604,37,0,3:0:0:0: +269,254,19786,1,2,0:3:0:0: +270,240,19876,1,2,0:3:0:0: +271,225,19968,2,0,L|272:204,2,15,2|2|2,3:1|0:1|0:1,0:0:0:0: +275,301,20331,1,2,1:0:0:0: +276,316,20422,2,0,B|277:328|277:328|275:332|275:332|276:345,1,30,2|0,0:1|0:0,0:0:0:0: +281,349,20695,2,0,B|278:335|287:332|282:316,1,30,2|0,0:1|0:0,0:0:0:0: +286,142,21059,5,4,3:0:0:0: +291,199,21240,2,0,B|291:209|283:214|283:214|281:204|291:199,1,35.9999989013672,8|0,0:3|0:0,0:0:0:0: +296,139,21604,37,4,0:3:0:0: +296,197,21786,2,0,P|300:211|299:226,1,30,2|0,1:1|0:0,0:0:0:0: +301,291,22150,2,0,P|297:276|297:261,1,30,2|0,0:1|0:0,0:0:0:0: +306,136,22513,5,2,3:3:0:0: +311,97,22695,1,2,0:3:0:0: +311,97,22786,1,2,0:3:0:0: +311,97,22877,1,2,3:1:0:0: +313,106,22968,1,2,0:1:0:0: +314,115,23059,1,2,0:1:0:0: +315,124,23150,1,2,0:1:0:0: +316,133,23240,6,0,B|308:125|308:125|306:136,1,22.5000008583069,2|0,1:1|0:0,0:0:0:0: +319,168,23421,5,2,0:1:0:0: +319,201,23604,38,0,L|320:224,1,22.5000008583069,2|0,0:3|0:0,0:0:0:0: +323,200,23786,2,0,L|324:223,1,22.5000008583069,2|0,0:3|0:0,0:0:0:0: +328,297,23968,21,2,3:2:0:0: +328,297,24150,2,0,L|326:268,1,30,2|0,0:0|0:0,0:0:0:0: +331,373,24513,1,2,0:0:0:0: +338,344,24695,1,2,1:0:0:0: +337,329,24787,2,0,L|336:314,1,15,2|0,0:1|0:0,0:0:0:0: +341,239,25059,2,0,L|339:210,1,30,2|0,0:1|0:0,0:0:0:0: +351,122,25422,5,2,3:2:0:0: +351,122,25604,2,0,L|352:107,2,15,2|2|2,0:0|0:0|3:2,0:0:0:0: +354,195,25968,1,2,0:3:0:0: +355,180,26059,1,2,0:3:0:0: +356,165,26149,2,0,L|358:136,2,30,2|0|0,1:3|0:0|0:0,0:0:0:0: +366,79,26877,37,2,3:2:0:0: +369,44,27059,1,2,0:1:0:0: +370,30,27149,1,2,0:1:0:0: +371,15,27240,2,0,L|371:0,2,15,2|2|2,0:1|0:1|0:1,0:0:0:0: +376,101,27604,1,2,1:1:0:0: +377,86,27696,2,0,L|378:65,1,15,2|0,0:1|0:0,0:0:0:0: +381,138,27968,2,0,L|379:167,1,30,2|0,0:1|0:0,0:0:0:0: +386,277,28331,5,2,3:3:0:0: +389,242,28513,1,2,0:3:0:0: +390,227,28604,1,2,0:3:0:0: +391,212,28695,2,0,L|393:183,1,30,2|2,0:3|0:3,0:0:0:0: +398,293,29059,6,0,L|396:331,2,37.5,2|2|2,1:0|3:1|3:1,0:0:0:0: +406,83,29786,21,2,3:2:0:0: +408,130,29967,1,2,0:0:0:0: +413,87,30331,5,2,0:0:0:0: +417,135,30513,1,2,1:0:0:0: +418,150,30605,2,0,L|419:171,1,15,2|0,0:1|0:0,0:0:0:0: +421,91,30877,2,0,P|426:76|425:61,1,30,2|0,0:1|0:0,0:0:0:0: +426,140,31240,37,2,3:2:0:0: +429,193,31422,1,2,0:1:0:0: +430,208,31513,1,2,0:1:0:0: +431,223,31603,1,2,0:1:0:0: +433,237,31695,1,2,0:1:0:0: +434,252,31786,2,0,P|436:237|435:222,1,30,2|0,0:2|1:0,0:0:0:0: +442,296,32149,2,0,P|439:310|440:325,1,30,2|0,0:0|0:0,0:0:0:0: +446,120,32695,5,4,3:0:0:0: +451,63,32877,2,0,B|448:54|448:54|441:49|441:49|443:57|443:57|451:63,1,35.9999989013672,8|0,0:3|0:0,0:0:0:0: +456,123,33240,37,4,0:3:0:0: +456,65,33422,1,2,1:0:0:0: +457,50,33513,2,0,P|451:31|443:20,1,30,2|0,0:1|0:0,0:0:0:0: +461,0,33786,1,2,0:1:0:0: +462,15,33877,2,0,L|462:29,1,15,2|0,0:1|0:0,0:0:0:0: +466,127,34149,5,2,3:3:0:0: +470,180,34331,1,2,0:3:0:0: +470,180,34422,1,2,0:3:0:0: +470,180,34513,1,2,3:1:0:0: +471,171,34604,1,2,0:1:0:0: +472,162,34695,1,2,0:1:0:0: +476,130,34877,6,0,B|486:125|486:125|481:127|475:126,1,22.5000008583069,2|0,1:1|0:0,0:0:0:0: +479,95,35059,5,2,0:1:0:0: +479,62,35240,38,0,L|480:39,1,22.5000008583069,2|0,0:3|0:0,0:0:0:0: +483,63,35422,2,0,L|484:40,1,22.5000008583069,2|0,0:3|0:0,0:0:0:0: +256,192,35604,12,4,37013,0:3:0:0: +256,192,37059,12,4,39149,0:3:0:0: +496,360,40695,6,0,B|498:344|498:344|495:329|495:329|496:314,1,45.0000017166138,2|0,3:1|0:0,0:0:0:0: +503,270,41059,1,2,0:1:0:0: +504,262,41150,1,2,0:1:0:0: +505,254,41240,2,0,L|506:242,3,11.2500004291535,2|0|0|0,0:1|0:0|0:0|0:0,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2781126-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2781126-expected-conversion.json new file mode 100644 index 0000000000..e03f6ae672 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2781126-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":313.0,"Objects":[{"StartTime":313.0,"Position":65.0,"HyperDash":false},{"StartTime":366.0,"Position":482.0,"HyperDash":false},{"StartTime":420.0,"Position":164.0,"HyperDash":false},{"StartTime":474.0,"Position":315.0,"HyperDash":false},{"StartTime":528.0,"Position":145.0,"HyperDash":false},{"StartTime":582.0,"Position":159.0,"HyperDash":false},{"StartTime":636.0,"Position":310.0,"HyperDash":false},{"StartTime":690.0,"Position":441.0,"HyperDash":false},{"StartTime":744.0,"Position":428.0,"HyperDash":false},{"StartTime":797.0,"Position":243.0,"HyperDash":false},{"StartTime":851.0,"Position":422.0,"HyperDash":false},{"StartTime":905.0,"Position":481.0,"HyperDash":false},{"StartTime":959.0,"Position":104.0,"HyperDash":false},{"StartTime":1013.0,"Position":473.0,"HyperDash":false},{"StartTime":1067.0,"Position":135.0,"HyperDash":false},{"StartTime":1121.0,"Position":360.0,"HyperDash":false},{"StartTime":1175.0,"Position":123.0,"HyperDash":false}]},{"StartTime":1348.0,"Objects":[{"StartTime":1348.0,"Position":224.0,"HyperDash":false}]},{"StartTime":1434.0,"Objects":[{"StartTime":1434.0,"Position":177.0,"HyperDash":false}]},{"StartTime":1520.0,"Objects":[{"StartTime":1520.0,"Position":179.0,"HyperDash":false}]},{"StartTime":1606.0,"Objects":[{"StartTime":1606.0,"Position":227.0,"HyperDash":false}]},{"StartTime":1693.0,"Objects":[{"StartTime":1693.0,"Position":292.0,"HyperDash":false},{"StartTime":1779.0,"Position":295.206116,"HyperDash":true}]},{"StartTime":1865.0,"Objects":[{"StartTime":1865.0,"Position":116.0,"HyperDash":false},{"StartTime":1951.0,"Position":99.0,"HyperDash":false},{"StartTime":2037.0,"Position":117.970421,"HyperDash":false},{"StartTime":2105.0,"Position":172.025635,"HyperDash":false},{"StartTime":2209.0,"Position":206.639481,"HyperDash":false}]},{"StartTime":2296.0,"Objects":[{"StartTime":2296.0,"Position":116.0,"HyperDash":false}]},{"StartTime":2382.0,"Objects":[{"StartTime":2382.0,"Position":26.0,"HyperDash":false},{"StartTime":2450.0,"Position":34.6324959,"HyperDash":false},{"StartTime":2554.0,"Position":22.54102,"HyperDash":true}]},{"StartTime":2727.0,"Objects":[{"StartTime":2727.0,"Position":292.0,"HyperDash":false},{"StartTime":2795.0,"Position":337.5814,"HyperDash":false},{"StartTime":2899.0,"Position":382.0,"HyperDash":false}]},{"StartTime":2986.0,"Objects":[{"StartTime":2986.0,"Position":328.0,"HyperDash":false}]},{"StartTime":3072.0,"Objects":[{"StartTime":3072.0,"Position":276.0,"HyperDash":false}]},{"StartTime":3244.0,"Objects":[{"StartTime":3244.0,"Position":448.0,"HyperDash":false}]},{"StartTime":3417.0,"Objects":[{"StartTime":3417.0,"Position":268.0,"HyperDash":false},{"StartTime":3485.0,"Position":236.41861,"HyperDash":false},{"StartTime":3589.0,"Position":178.0,"HyperDash":false}]},{"StartTime":3675.0,"Objects":[{"StartTime":3675.0,"Position":244.0,"HyperDash":false}]},{"StartTime":3762.0,"Objects":[{"StartTime":3762.0,"Position":178.0,"HyperDash":false},{"StartTime":3830.0,"Position":149.41861,"HyperDash":false},{"StartTime":3934.0,"Position":88.0,"HyperDash":true}]},{"StartTime":4106.0,"Objects":[{"StartTime":4106.0,"Position":444.0,"HyperDash":false},{"StartTime":4192.0,"Position":447.737061,"HyperDash":false}]},{"StartTime":4279.0,"Objects":[{"StartTime":4279.0,"Position":376.0,"HyperDash":false},{"StartTime":4365.0,"Position":372.262939,"HyperDash":false}]},{"StartTime":4451.0,"Objects":[{"StartTime":4451.0,"Position":300.0,"HyperDash":false}]},{"StartTime":4624.0,"Objects":[{"StartTime":4624.0,"Position":472.0,"HyperDash":false},{"StartTime":4710.0,"Position":475.451355,"HyperDash":true}]},{"StartTime":4796.0,"Objects":[{"StartTime":4796.0,"Position":296.0,"HyperDash":false},{"StartTime":4864.0,"Position":285.639862,"HyperDash":false},{"StartTime":4968.0,"Position":274.157928,"HyperDash":false}]},{"StartTime":5055.0,"Objects":[{"StartTime":5055.0,"Position":366.0,"HyperDash":false}]},{"StartTime":5141.0,"Objects":[{"StartTime":5141.0,"Position":456.0,"HyperDash":false},{"StartTime":5209.0,"Position":405.4186,"HyperDash":false},{"StartTime":5313.0,"Position":366.0,"HyperDash":true}]},{"StartTime":5486.0,"Objects":[{"StartTime":5486.0,"Position":112.0,"HyperDash":false},{"StartTime":5554.0,"Position":144.58139,"HyperDash":false},{"StartTime":5658.0,"Position":202.0,"HyperDash":false}]},{"StartTime":5744.0,"Objects":[{"StartTime":5744.0,"Position":268.0,"HyperDash":false}]},{"StartTime":5831.0,"Objects":[{"StartTime":5831.0,"Position":202.0,"HyperDash":false}]},{"StartTime":6003.0,"Objects":[{"StartTime":6003.0,"Position":360.0,"HyperDash":false}]},{"StartTime":6175.0,"Objects":[{"StartTime":6175.0,"Position":192.0,"HyperDash":false},{"StartTime":6243.0,"Position":146.41861,"HyperDash":false},{"StartTime":6347.0,"Position":102.0,"HyperDash":false}]},{"StartTime":6434.0,"Objects":[{"StartTime":6434.0,"Position":172.0,"HyperDash":false}]},{"StartTime":6520.0,"Objects":[{"StartTime":6520.0,"Position":102.0,"HyperDash":false},{"StartTime":6588.0,"Position":71.4186,"HyperDash":false},{"StartTime":6692.0,"Position":12.0,"HyperDash":true}]},{"StartTime":6865.0,"Objects":[{"StartTime":6865.0,"Position":288.0,"HyperDash":false}]},{"StartTime":6951.0,"Objects":[{"StartTime":6951.0,"Position":335.0,"HyperDash":false}]},{"StartTime":7037.0,"Objects":[{"StartTime":7037.0,"Position":333.0,"HyperDash":false}]},{"StartTime":7124.0,"Objects":[{"StartTime":7124.0,"Position":285.0,"HyperDash":false}]},{"StartTime":7210.0,"Objects":[{"StartTime":7210.0,"Position":220.0,"HyperDash":false},{"StartTime":7296.0,"Position":216.793884,"HyperDash":false}]},{"StartTime":7382.0,"Objects":[{"StartTime":7382.0,"Position":320.0,"HyperDash":false}]},{"StartTime":7555.0,"Objects":[{"StartTime":7555.0,"Position":204.0,"HyperDash":true}]},{"StartTime":7727.0,"Objects":[{"StartTime":7727.0,"Position":456.0,"HyperDash":false}]},{"StartTime":7813.0,"Objects":[{"StartTime":7813.0,"Position":460.0,"HyperDash":false}]},{"StartTime":7900.0,"Objects":[{"StartTime":7900.0,"Position":464.0,"HyperDash":false},{"StartTime":7968.0,"Position":437.4186,"HyperDash":false},{"StartTime":8072.0,"Position":374.0,"HyperDash":true}]},{"StartTime":8244.0,"Objects":[{"StartTime":8244.0,"Position":120.0,"HyperDash":false},{"StartTime":8312.0,"Position":159.58139,"HyperDash":false},{"StartTime":8416.0,"Position":210.0,"HyperDash":false}]},{"StartTime":8503.0,"Objects":[{"StartTime":8503.0,"Position":280.0,"HyperDash":false}]},{"StartTime":8589.0,"Objects":[{"StartTime":8589.0,"Position":348.0,"HyperDash":false}]},{"StartTime":8762.0,"Objects":[{"StartTime":8762.0,"Position":176.0,"HyperDash":false}]},{"StartTime":8934.0,"Objects":[{"StartTime":8934.0,"Position":354.0,"HyperDash":false},{"StartTime":9002.0,"Position":379.5814,"HyperDash":false},{"StartTime":9106.0,"Position":444.0,"HyperDash":false}]},{"StartTime":9193.0,"Objects":[{"StartTime":9193.0,"Position":374.0,"HyperDash":false}]},{"StartTime":9279.0,"Objects":[{"StartTime":9279.0,"Position":306.0,"HyperDash":false},{"StartTime":9347.0,"Position":331.5814,"HyperDash":false},{"StartTime":9451.0,"Position":396.0,"HyperDash":true}]},{"StartTime":9624.0,"Objects":[{"StartTime":9624.0,"Position":148.0,"HyperDash":false},{"StartTime":9710.0,"Position":104.34359,"HyperDash":false}]},{"StartTime":9796.0,"Objects":[{"StartTime":9796.0,"Position":176.0,"HyperDash":false},{"StartTime":9882.0,"Position":219.6564,"HyperDash":false}]},{"StartTime":9969.0,"Objects":[{"StartTime":9969.0,"Position":148.0,"HyperDash":false}]},{"StartTime":10141.0,"Objects":[{"StartTime":10141.0,"Position":308.0,"HyperDash":false}]},{"StartTime":10313.0,"Objects":[{"StartTime":10313.0,"Position":140.0,"HyperDash":true}]},{"StartTime":10486.0,"Objects":[{"StartTime":10486.0,"Position":396.0,"HyperDash":false},{"StartTime":10572.0,"Position":441.0,"HyperDash":false},{"StartTime":10658.0,"Position":396.0,"HyperDash":false}]},{"StartTime":10831.0,"Objects":[{"StartTime":10831.0,"Position":228.0,"HyperDash":true}]},{"StartTime":11003.0,"Objects":[{"StartTime":11003.0,"Position":460.0,"HyperDash":false},{"StartTime":11089.0,"Position":482.326263,"HyperDash":false}]},{"StartTime":11175.0,"Objects":[{"StartTime":11175.0,"Position":392.0,"HyperDash":false},{"StartTime":11261.0,"Position":414.326263,"HyperDash":false}]},{"StartTime":11348.0,"Objects":[{"StartTime":11348.0,"Position":324.0,"HyperDash":false},{"StartTime":11434.0,"Position":345.614166,"HyperDash":false}]},{"StartTime":11520.0,"Objects":[{"StartTime":11520.0,"Position":260.0,"HyperDash":false},{"StartTime":11606.0,"Position":282.326263,"HyperDash":false}]},{"StartTime":11693.0,"Objects":[{"StartTime":11693.0,"Position":384.0,"HyperDash":false}]},{"StartTime":11865.0,"Objects":[{"StartTime":11865.0,"Position":220.0,"HyperDash":false},{"StartTime":11951.0,"Position":175.0,"HyperDash":true}]},{"StartTime":12037.0,"Objects":[{"StartTime":12037.0,"Position":400.0,"HyperDash":false},{"StartTime":12123.0,"Position":463.25,"HyperDash":false},{"StartTime":12209.0,"Position":488.0,"HyperDash":false},{"StartTime":12295.0,"Position":488.0,"HyperDash":true}]},{"StartTime":12382.0,"Objects":[{"StartTime":12382.0,"Position":284.0,"HyperDash":false},{"StartTime":12450.0,"Position":255.41861,"HyperDash":false},{"StartTime":12554.0,"Position":194.0,"HyperDash":false}]},{"StartTime":12641.0,"Objects":[{"StartTime":12641.0,"Position":264.0,"HyperDash":true}]},{"StartTime":12727.0,"Objects":[{"StartTime":12727.0,"Position":436.0,"HyperDash":false}]},{"StartTime":12900.0,"Objects":[{"StartTime":12900.0,"Position":328.0,"HyperDash":false},{"StartTime":12986.0,"Position":324.793884,"HyperDash":false}]},{"StartTime":13072.0,"Objects":[{"StartTime":13072.0,"Position":424.0,"HyperDash":false},{"StartTime":13140.0,"Position":437.3675,"HyperDash":false},{"StartTime":13244.0,"Position":427.458984,"HyperDash":false}]},{"StartTime":13331.0,"Objects":[{"StartTime":13331.0,"Position":360.0,"HyperDash":true}]},{"StartTime":13417.0,"Objects":[{"StartTime":13417.0,"Position":208.0,"HyperDash":false},{"StartTime":13485.0,"Position":174.41861,"HyperDash":false},{"StartTime":13589.0,"Position":118.0,"HyperDash":false}]},{"StartTime":13762.0,"Objects":[{"StartTime":13762.0,"Position":292.0,"HyperDash":false},{"StartTime":13830.0,"Position":274.545563,"HyperDash":false},{"StartTime":13934.0,"Position":295.909363,"HyperDash":false}]},{"StartTime":14020.0,"Objects":[{"StartTime":14020.0,"Position":228.0,"HyperDash":true}]},{"StartTime":14106.0,"Objects":[{"StartTime":14106.0,"Position":408.0,"HyperDash":false},{"StartTime":14174.0,"Position":426.5814,"HyperDash":false},{"StartTime":14278.0,"Position":498.0,"HyperDash":true}]},{"StartTime":14451.0,"Objects":[{"StartTime":14451.0,"Position":228.0,"HyperDash":false},{"StartTime":14519.0,"Position":266.5814,"HyperDash":false},{"StartTime":14623.0,"Position":318.0,"HyperDash":true}]},{"StartTime":14796.0,"Objects":[{"StartTime":14796.0,"Position":48.0,"HyperDash":false},{"StartTime":14864.0,"Position":91.5814,"HyperDash":false},{"StartTime":14968.0,"Position":138.0,"HyperDash":true}]},{"StartTime":15141.0,"Objects":[{"StartTime":15141.0,"Position":392.0,"HyperDash":false},{"StartTime":15227.0,"Position":394.993347,"HyperDash":false}]},{"StartTime":15313.0,"Objects":[{"StartTime":15313.0,"Position":320.0,"HyperDash":false},{"StartTime":15399.0,"Position":317.006653,"HyperDash":true}]},{"StartTime":15486.0,"Objects":[{"StartTime":15486.0,"Position":488.0,"HyperDash":false}]},{"StartTime":15658.0,"Objects":[{"StartTime":15658.0,"Position":388.0,"HyperDash":false},{"StartTime":15744.0,"Position":343.0,"HyperDash":false}]},{"StartTime":15831.0,"Objects":[{"StartTime":15831.0,"Position":240.0,"HyperDash":false},{"StartTime":15899.0,"Position":231.454437,"HyperDash":false},{"StartTime":16003.0,"Position":236.090652,"HyperDash":false}]},{"StartTime":16089.0,"Objects":[{"StartTime":16089.0,"Position":304.0,"HyperDash":true}]},{"StartTime":16175.0,"Objects":[{"StartTime":16175.0,"Position":132.0,"HyperDash":false},{"StartTime":16243.0,"Position":99.4186,"HyperDash":false},{"StartTime":16347.0,"Position":42.0,"HyperDash":true}]},{"StartTime":16520.0,"Objects":[{"StartTime":16520.0,"Position":312.0,"HyperDash":false},{"StartTime":16588.0,"Position":274.4186,"HyperDash":false},{"StartTime":16692.0,"Position":222.0,"HyperDash":false}]},{"StartTime":16779.0,"Objects":[{"StartTime":16779.0,"Position":152.0,"HyperDash":true}]},{"StartTime":16865.0,"Objects":[{"StartTime":16865.0,"Position":328.0,"HyperDash":false},{"StartTime":16933.0,"Position":309.4186,"HyperDash":false},{"StartTime":17037.0,"Position":238.0,"HyperDash":false}]},{"StartTime":17210.0,"Objects":[{"StartTime":17210.0,"Position":328.0,"HyperDash":false}]},{"StartTime":17382.0,"Objects":[{"StartTime":17382.0,"Position":164.0,"HyperDash":false},{"StartTime":17468.0,"Position":160.54866,"HyperDash":true}]},{"StartTime":17555.0,"Objects":[{"StartTime":17555.0,"Position":336.0,"HyperDash":false},{"StartTime":17623.0,"Position":354.5814,"HyperDash":false},{"StartTime":17727.0,"Position":426.0,"HyperDash":true}]},{"StartTime":17900.0,"Objects":[{"StartTime":17900.0,"Position":152.0,"HyperDash":false}]},{"StartTime":17986.0,"Objects":[{"StartTime":17986.0,"Position":155.0,"HyperDash":false}]},{"StartTime":18072.0,"Objects":[{"StartTime":18072.0,"Position":192.0,"HyperDash":false}]},{"StartTime":18158.0,"Objects":[{"StartTime":18158.0,"Position":252.0,"HyperDash":true}]},{"StartTime":18244.0,"Objects":[{"StartTime":18244.0,"Position":404.0,"HyperDash":false},{"StartTime":18312.0,"Position":416.481262,"HyperDash":false},{"StartTime":18416.0,"Position":407.746735,"HyperDash":true}]},{"StartTime":18589.0,"Objects":[{"StartTime":18589.0,"Position":156.0,"HyperDash":false},{"StartTime":18657.0,"Position":101.4186,"HyperDash":false},{"StartTime":18761.0,"Position":66.0,"HyperDash":false}]},{"StartTime":18848.0,"Objects":[{"StartTime":18848.0,"Position":136.0,"HyperDash":true}]},{"StartTime":18934.0,"Objects":[{"StartTime":18934.0,"Position":304.0,"HyperDash":false},{"StartTime":19002.0,"Position":346.5814,"HyperDash":false},{"StartTime":19106.0,"Position":394.0,"HyperDash":true}]},{"StartTime":19279.0,"Objects":[{"StartTime":19279.0,"Position":120.0,"HyperDash":false},{"StartTime":19347.0,"Position":113.063881,"HyperDash":false},{"StartTime":19451.0,"Position":111.495346,"HyperDash":false}]},{"StartTime":19537.0,"Objects":[{"StartTime":19537.0,"Position":180.0,"HyperDash":true}]},{"StartTime":19624.0,"Objects":[{"StartTime":19624.0,"Position":360.0,"HyperDash":false},{"StartTime":19692.0,"Position":315.4186,"HyperDash":false},{"StartTime":19796.0,"Position":270.0,"HyperDash":true}]},{"StartTime":19969.0,"Objects":[{"StartTime":19969.0,"Position":32.0,"HyperDash":false},{"StartTime":20037.0,"Position":60.581398,"HyperDash":false},{"StartTime":20141.0,"Position":122.0,"HyperDash":false}]},{"StartTime":20227.0,"Objects":[{"StartTime":20227.0,"Position":188.0,"HyperDash":true}]},{"StartTime":20313.0,"Objects":[{"StartTime":20313.0,"Position":16.0,"HyperDash":false},{"StartTime":20381.0,"Position":51.581398,"HyperDash":false},{"StartTime":20485.0,"Position":106.0,"HyperDash":true}]},{"StartTime":20658.0,"Objects":[{"StartTime":20658.0,"Position":368.0,"HyperDash":false},{"StartTime":20744.0,"Position":320.104462,"HyperDash":false},{"StartTime":20830.0,"Position":260.0,"HyperDash":false},{"StartTime":20916.0,"Position":298.686646,"HyperDash":false},{"StartTime":21002.0,"Position":368.0,"HyperDash":false},{"StartTime":21070.0,"Position":333.8027,"HyperDash":false},{"StartTime":21175.0,"Position":260.0,"HyperDash":true}]},{"StartTime":21348.0,"Objects":[{"StartTime":21348.0,"Position":496.0,"HyperDash":false}]},{"StartTime":21520.0,"Objects":[{"StartTime":21520.0,"Position":324.0,"HyperDash":false}]},{"StartTime":21693.0,"Objects":[{"StartTime":21693.0,"Position":496.0,"HyperDash":false}]},{"StartTime":21865.0,"Objects":[{"StartTime":21865.0,"Position":388.0,"HyperDash":false},{"StartTime":21951.0,"Position":343.0,"HyperDash":true}]},{"StartTime":22037.0,"Objects":[{"StartTime":22037.0,"Position":144.0,"HyperDash":false}]},{"StartTime":22210.0,"Objects":[{"StartTime":22210.0,"Position":252.0,"HyperDash":false},{"StartTime":22296.0,"Position":231.875381,"HyperDash":false}]},{"StartTime":22382.0,"Objects":[{"StartTime":22382.0,"Position":312.0,"HyperDash":false},{"StartTime":22468.0,"Position":291.8754,"HyperDash":false}]},{"StartTime":22555.0,"Objects":[{"StartTime":22555.0,"Position":372.0,"HyperDash":false},{"StartTime":22641.0,"Position":351.8754,"HyperDash":true}]},{"StartTime":22727.0,"Objects":[{"StartTime":22727.0,"Position":180.0,"HyperDash":false},{"StartTime":22795.0,"Position":226.58139,"HyperDash":false},{"StartTime":22899.0,"Position":270.0,"HyperDash":false}]},{"StartTime":22986.0,"Objects":[{"StartTime":22986.0,"Position":208.0,"HyperDash":true}]},{"StartTime":23072.0,"Objects":[{"StartTime":23072.0,"Position":436.0,"HyperDash":false},{"StartTime":23158.0,"Position":486.800659,"HyperDash":false},{"StartTime":23244.0,"Position":494.721924,"HyperDash":false},{"StartTime":23330.0,"Position":435.854675,"HyperDash":true}]},{"StartTime":23417.0,"Objects":[{"StartTime":23417.0,"Position":208.0,"HyperDash":false},{"StartTime":23503.0,"Position":163.75,"HyperDash":false},{"StartTime":23589.0,"Position":95.5,"HyperDash":false},{"StartTime":23657.0,"Position":134.976746,"HyperDash":false},{"StartTime":23761.0,"Position":208.0,"HyperDash":false}]},{"StartTime":23934.0,"Objects":[{"StartTime":23934.0,"Position":312.0,"HyperDash":false}]},{"StartTime":24020.0,"Objects":[{"StartTime":24020.0,"Position":220.0,"HyperDash":false}]},{"StartTime":24106.0,"Objects":[{"StartTime":24106.0,"Position":128.0,"HyperDash":false},{"StartTime":24174.0,"Position":164.58139,"HyperDash":false},{"StartTime":24278.0,"Position":218.0,"HyperDash":false}]},{"StartTime":24451.0,"Objects":[{"StartTime":24451.0,"Position":392.0,"HyperDash":false}]},{"StartTime":24537.0,"Objects":[{"StartTime":24537.0,"Position":444.0,"HyperDash":false}]},{"StartTime":24624.0,"Objects":[{"StartTime":24624.0,"Position":444.0,"HyperDash":false}]},{"StartTime":24710.0,"Objects":[{"StartTime":24710.0,"Position":392.0,"HyperDash":true}]},{"StartTime":24796.0,"Objects":[{"StartTime":24796.0,"Position":212.0,"HyperDash":false},{"StartTime":24882.0,"Position":244.0,"HyperDash":false},{"StartTime":24968.0,"Position":302.0,"HyperDash":false},{"StartTime":25036.0,"Position":269.4186,"HyperDash":false},{"StartTime":25140.0,"Position":212.0,"HyperDash":false}]},{"StartTime":25313.0,"Objects":[{"StartTime":25313.0,"Position":320.0,"HyperDash":false}]},{"StartTime":25400.0,"Objects":[{"StartTime":25400.0,"Position":384.0,"HyperDash":false}]},{"StartTime":25486.0,"Objects":[{"StartTime":25486.0,"Position":284.0,"HyperDash":false},{"StartTime":25554.0,"Position":267.4186,"HyperDash":false},{"StartTime":25658.0,"Position":194.0,"HyperDash":true}]},{"StartTime":25831.0,"Objects":[{"StartTime":25831.0,"Position":448.0,"HyperDash":false},{"StartTime":25917.0,"Position":444.548645,"HyperDash":false}]},{"StartTime":26003.0,"Objects":[{"StartTime":26003.0,"Position":344.0,"HyperDash":false},{"StartTime":26089.0,"Position":299.0,"HyperDash":true}]},{"StartTime":26175.0,"Objects":[{"StartTime":26175.0,"Position":128.0,"HyperDash":false},{"StartTime":26261.0,"Position":80.0,"HyperDash":false},{"StartTime":26347.0,"Position":38.0,"HyperDash":false},{"StartTime":26415.0,"Position":73.58139,"HyperDash":false},{"StartTime":26519.0,"Position":128.0,"HyperDash":false}]},{"StartTime":26693.0,"Objects":[{"StartTime":26693.0,"Position":236.0,"HyperDash":false}]},{"StartTime":26779.0,"Objects":[{"StartTime":26779.0,"Position":299.0,"HyperDash":false}]},{"StartTime":26865.0,"Objects":[{"StartTime":26865.0,"Position":362.0,"HyperDash":false}]},{"StartTime":27037.0,"Objects":[{"StartTime":27037.0,"Position":196.0,"HyperDash":false}]},{"StartTime":27210.0,"Objects":[{"StartTime":27210.0,"Position":352.0,"HyperDash":false}]},{"StartTime":27296.0,"Objects":[{"StartTime":27296.0,"Position":352.0,"HyperDash":false}]},{"StartTime":27382.0,"Objects":[{"StartTime":27382.0,"Position":312.0,"HyperDash":false}]},{"StartTime":27469.0,"Objects":[{"StartTime":27469.0,"Position":248.0,"HyperDash":true}]},{"StartTime":27555.0,"Objects":[{"StartTime":27555.0,"Position":412.0,"HyperDash":false},{"StartTime":27641.0,"Position":349.0,"HyperDash":false},{"StartTime":27727.0,"Position":322.0,"HyperDash":false},{"StartTime":27795.0,"Position":343.5814,"HyperDash":false},{"StartTime":27899.0,"Position":412.0,"HyperDash":false}]},{"StartTime":28072.0,"Objects":[{"StartTime":28072.0,"Position":304.0,"HyperDash":false}]},{"StartTime":28158.0,"Objects":[{"StartTime":28158.0,"Position":396.0,"HyperDash":false}]},{"StartTime":28244.0,"Objects":[{"StartTime":28244.0,"Position":488.0,"HyperDash":false},{"StartTime":28312.0,"Position":451.4186,"HyperDash":false},{"StartTime":28416.0,"Position":398.0,"HyperDash":true}]},{"StartTime":28589.0,"Objects":[{"StartTime":28589.0,"Position":88.0,"HyperDash":false}]},{"StartTime":28934.0,"Objects":[{"StartTime":28934.0,"Position":340.0,"HyperDash":false},{"StartTime":29002.0,"Position":358.545563,"HyperDash":false},{"StartTime":29106.0,"Position":343.909363,"HyperDash":false}]},{"StartTime":29279.0,"Objects":[{"StartTime":29279.0,"Position":172.0,"HyperDash":false},{"StartTime":29347.0,"Position":182.577881,"HyperDash":false},{"StartTime":29451.0,"Position":168.402878,"HyperDash":false}]},{"StartTime":29537.0,"Objects":[{"StartTime":29537.0,"Position":268.0,"HyperDash":false}]},{"StartTime":29624.0,"Objects":[{"StartTime":29624.0,"Position":368.0,"HyperDash":false},{"StartTime":29692.0,"Position":343.4186,"HyperDash":false},{"StartTime":29796.0,"Position":278.0,"HyperDash":false}]},{"StartTime":29969.0,"Objects":[{"StartTime":29969.0,"Position":452.0,"HyperDash":false},{"StartTime":30055.0,"Position":459.397949,"HyperDash":false},{"StartTime":30141.0,"Position":452.0,"HyperDash":true}]},{"StartTime":30313.0,"Objects":[{"StartTime":30313.0,"Position":200.0,"HyperDash":false},{"StartTime":30381.0,"Position":196.454437,"HyperDash":false},{"StartTime":30485.0,"Position":196.090652,"HyperDash":false}]},{"StartTime":30658.0,"Objects":[{"StartTime":30658.0,"Position":368.0,"HyperDash":false},{"StartTime":30726.0,"Position":349.4186,"HyperDash":false},{"StartTime":30830.0,"Position":278.0,"HyperDash":false}]},{"StartTime":30917.0,"Objects":[{"StartTime":30917.0,"Position":380.0,"HyperDash":false}]},{"StartTime":31003.0,"Objects":[{"StartTime":31003.0,"Position":480.0,"HyperDash":false},{"StartTime":31071.0,"Position":435.4186,"HyperDash":false},{"StartTime":31175.0,"Position":390.0,"HyperDash":true}]},{"StartTime":31348.0,"Objects":[{"StartTime":31348.0,"Position":128.0,"HyperDash":false},{"StartTime":31434.0,"Position":124.54866,"HyperDash":false}]},{"StartTime":31520.0,"Objects":[{"StartTime":31520.0,"Position":228.0,"HyperDash":false},{"StartTime":31606.0,"Position":273.0,"HyperDash":true}]},{"StartTime":31693.0,"Objects":[{"StartTime":31693.0,"Position":88.0,"HyperDash":false},{"StartTime":31761.0,"Position":101.632492,"HyperDash":false},{"StartTime":31865.0,"Position":84.5410156,"HyperDash":false}]},{"StartTime":32037.0,"Objects":[{"StartTime":32037.0,"Position":256.0,"HyperDash":false},{"StartTime":32105.0,"Position":278.5814,"HyperDash":false},{"StartTime":32209.0,"Position":346.0,"HyperDash":false}]},{"StartTime":32296.0,"Objects":[{"StartTime":32296.0,"Position":246.0,"HyperDash":false}]},{"StartTime":32382.0,"Objects":[{"StartTime":32382.0,"Position":148.0,"HyperDash":false},{"StartTime":32450.0,"Position":101.4186,"HyperDash":false},{"StartTime":32554.0,"Position":58.0,"HyperDash":false}]},{"StartTime":32727.0,"Objects":[{"StartTime":32727.0,"Position":232.0,"HyperDash":false}]},{"StartTime":32813.0,"Objects":[{"StartTime":32813.0,"Position":180.0,"HyperDash":false}]},{"StartTime":32900.0,"Objects":[{"StartTime":32900.0,"Position":124.0,"HyperDash":true}]},{"StartTime":33072.0,"Objects":[{"StartTime":33072.0,"Position":376.0,"HyperDash":false},{"StartTime":33140.0,"Position":415.5814,"HyperDash":false},{"StartTime":33244.0,"Position":466.0,"HyperDash":false}]},{"StartTime":33417.0,"Objects":[{"StartTime":33417.0,"Position":300.0,"HyperDash":false},{"StartTime":33485.0,"Position":323.5814,"HyperDash":false},{"StartTime":33589.0,"Position":390.0,"HyperDash":false}]},{"StartTime":33762.0,"Objects":[{"StartTime":33762.0,"Position":220.0,"HyperDash":false},{"StartTime":33830.0,"Position":200.41861,"HyperDash":false},{"StartTime":33934.0,"Position":130.0,"HyperDash":true}]},{"StartTime":34106.0,"Objects":[{"StartTime":34106.0,"Position":416.0,"HyperDash":false},{"StartTime":34149.0,"Position":438.5,"HyperDash":false},{"StartTime":34192.0,"Position":416.0,"HyperDash":false},{"StartTime":34235.0,"Position":438.5,"HyperDash":false},{"StartTime":34278.0,"Position":416.0,"HyperDash":false},{"StartTime":34321.0,"Position":438.5,"HyperDash":false},{"StartTime":34364.0,"Position":416.0,"HyperDash":false},{"StartTime":34407.0,"Position":438.5,"HyperDash":true}]},{"StartTime":34451.0,"Objects":[{"StartTime":34451.0,"Position":265.0,"HyperDash":false},{"StartTime":34519.0,"Position":199.6279,"HyperDash":false},{"StartTime":34623.0,"Position":130.0,"HyperDash":false}]},{"StartTime":34796.0,"Objects":[{"StartTime":34796.0,"Position":300.0,"HyperDash":false},{"StartTime":34864.0,"Position":300.496857,"HyperDash":false},{"StartTime":34968.0,"Position":303.786133,"HyperDash":false}]},{"StartTime":35141.0,"Objects":[{"StartTime":35141.0,"Position":140.0,"HyperDash":true}]},{"StartTime":35313.0,"Objects":[{"StartTime":35313.0,"Position":376.0,"HyperDash":false}]},{"StartTime":35486.0,"Objects":[{"StartTime":35486.0,"Position":268.0,"HyperDash":false},{"StartTime":35554.0,"Position":253.518738,"HyperDash":false},{"StartTime":35658.0,"Position":264.253265,"HyperDash":true}]},{"StartTime":35831.0,"Objects":[{"StartTime":35831.0,"Position":496.0,"HyperDash":false},{"StartTime":35899.0,"Position":454.4186,"HyperDash":false},{"StartTime":36003.0,"Position":406.0,"HyperDash":false}]},{"StartTime":36175.0,"Objects":[{"StartTime":36175.0,"Position":236.0,"HyperDash":false},{"StartTime":36243.0,"Position":192.41861,"HyperDash":false},{"StartTime":36347.0,"Position":146.0,"HyperDash":true}]},{"StartTime":36520.0,"Objects":[{"StartTime":36520.0,"Position":400.0,"HyperDash":false}]},{"StartTime":36693.0,"Objects":[{"StartTime":36693.0,"Position":236.0,"HyperDash":true}]},{"StartTime":36865.0,"Objects":[{"StartTime":36865.0,"Position":476.0,"HyperDash":false}]},{"StartTime":36951.0,"Objects":[{"StartTime":36951.0,"Position":476.0,"HyperDash":false}]},{"StartTime":37037.0,"Objects":[{"StartTime":37037.0,"Position":434.0,"HyperDash":false}]},{"StartTime":37124.0,"Objects":[{"StartTime":37124.0,"Position":369.0,"HyperDash":true}]},{"StartTime":37210.0,"Objects":[{"StartTime":37210.0,"Position":196.0,"HyperDash":false},{"StartTime":37278.0,"Position":151.41861,"HyperDash":false},{"StartTime":37382.0,"Position":106.0,"HyperDash":false}]},{"StartTime":37555.0,"Objects":[{"StartTime":37555.0,"Position":272.0,"HyperDash":false},{"StartTime":37623.0,"Position":302.5814,"HyperDash":false},{"StartTime":37727.0,"Position":362.0,"HyperDash":false}]},{"StartTime":37900.0,"Objects":[{"StartTime":37900.0,"Position":196.0,"HyperDash":true}]},{"StartTime":38072.0,"Objects":[{"StartTime":38072.0,"Position":432.0,"HyperDash":false}]},{"StartTime":38244.0,"Objects":[{"StartTime":38244.0,"Position":324.0,"HyperDash":false}]},{"StartTime":38331.0,"Objects":[{"StartTime":38331.0,"Position":272.0,"HyperDash":false}]},{"StartTime":38417.0,"Objects":[{"StartTime":38417.0,"Position":224.0,"HyperDash":true}]},{"StartTime":38589.0,"Objects":[{"StartTime":38589.0,"Position":488.0,"HyperDash":false},{"StartTime":38657.0,"Position":483.690765,"HyperDash":false},{"StartTime":38761.0,"Position":489.747253,"HyperDash":false}]},{"StartTime":38934.0,"Objects":[{"StartTime":38934.0,"Position":324.0,"HyperDash":false},{"StartTime":39002.0,"Position":339.316925,"HyperDash":false},{"StartTime":39106.0,"Position":327.331055,"HyperDash":true}]},{"StartTime":39279.0,"Objects":[{"StartTime":39279.0,"Position":88.0,"HyperDash":false}]},{"StartTime":39451.0,"Objects":[{"StartTime":39451.0,"Position":256.0,"HyperDash":true}]},{"StartTime":39624.0,"Objects":[{"StartTime":39624.0,"Position":16.0,"HyperDash":true}]},{"StartTime":39969.0,"Objects":[{"StartTime":39969.0,"Position":428.0,"HyperDash":false},{"StartTime":40055.0,"Position":475.928162,"HyperDash":false},{"StartTime":40141.0,"Position":473.713928,"HyperDash":false},{"StartTime":40227.0,"Position":429.731,"HyperDash":false}]},{"StartTime":40313.0,"Objects":[{"StartTime":40313.0,"Position":328.0,"HyperDash":false},{"StartTime":40399.0,"Position":262.213257,"HyperDash":false},{"StartTime":40485.0,"Position":239.814941,"HyperDash":false},{"StartTime":40571.0,"Position":239.657425,"HyperDash":true}]},{"StartTime":40658.0,"Objects":[{"StartTime":40658.0,"Position":412.0,"HyperDash":false},{"StartTime":40744.0,"Position":464.25,"HyperDash":false},{"StartTime":40830.0,"Position":497.294128,"HyperDash":false},{"StartTime":40916.0,"Position":499.8483,"HyperDash":true}]},{"StartTime":41003.0,"Objects":[{"StartTime":41003.0,"Position":272.0,"HyperDash":false},{"StartTime":41089.0,"Position":253.0,"HyperDash":false},{"StartTime":41175.0,"Position":300.4706,"HyperDash":false},{"StartTime":41261.0,"Position":356.6626,"HyperDash":true}]},{"StartTime":41348.0,"Objects":[{"StartTime":41348.0,"Position":116.0,"HyperDash":false},{"StartTime":41434.0,"Position":72.26963,"HyperDash":false},{"StartTime":41520.0,"Position":61.0594635,"HyperDash":false},{"StartTime":41606.0,"Position":119.336884,"HyperDash":true}]},{"StartTime":41693.0,"Objects":[{"StartTime":41693.0,"Position":340.0,"HyperDash":false},{"StartTime":41779.0,"Position":288.5,"HyperDash":false},{"StartTime":41865.0,"Position":204.999985,"HyperDash":false},{"StartTime":41951.0,"Position":137.5,"HyperDash":true}]},{"StartTime":42037.0,"Objects":[{"StartTime":42037.0,"Position":312.0,"HyperDash":false}]},{"StartTime":42210.0,"Objects":[{"StartTime":42210.0,"Position":164.0,"HyperDash":false}]},{"StartTime":42382.0,"Objects":[{"StartTime":42382.0,"Position":324.0,"HyperDash":false}]},{"StartTime":42555.0,"Objects":[{"StartTime":42555.0,"Position":152.0,"HyperDash":true}]},{"StartTime":42727.0,"Objects":[{"StartTime":42727.0,"Position":404.0,"HyperDash":false}]},{"StartTime":42813.0,"Objects":[{"StartTime":42813.0,"Position":460.0,"HyperDash":false}]},{"StartTime":42900.0,"Objects":[{"StartTime":42900.0,"Position":460.0,"HyperDash":false}]},{"StartTime":42986.0,"Objects":[{"StartTime":42986.0,"Position":404.0,"HyperDash":true}]},{"StartTime":43072.0,"Objects":[{"StartTime":43072.0,"Position":208.0,"HyperDash":false},{"StartTime":43158.0,"Position":204.54866,"HyperDash":false}]},{"StartTime":43244.0,"Objects":[{"StartTime":43244.0,"Position":280.0,"HyperDash":false},{"StartTime":43330.0,"Position":283.451355,"HyperDash":true}]},{"StartTime":43417.0,"Objects":[{"StartTime":43417.0,"Position":104.0,"HyperDash":false},{"StartTime":43503.0,"Position":59.0,"HyperDash":true}]},{"StartTime":43589.0,"Objects":[{"StartTime":43589.0,"Position":240.0,"HyperDash":false},{"StartTime":43675.0,"Position":285.0,"HyperDash":true}]},{"StartTime":43762.0,"Objects":[{"StartTime":43762.0,"Position":80.0,"HyperDash":false},{"StartTime":43848.0,"Position":125.0,"HyperDash":true}]},{"StartTime":43934.0,"Objects":[{"StartTime":43934.0,"Position":372.0,"HyperDash":false},{"StartTime":44020.0,"Position":327.0,"HyperDash":true}]},{"StartTime":44106.0,"Objects":[{"StartTime":44106.0,"Position":124.0,"HyperDash":true}]},{"StartTime":44279.0,"Objects":[{"StartTime":44279.0,"Position":368.0,"HyperDash":true}]},{"StartTime":44451.0,"Objects":[{"StartTime":44451.0,"Position":116.0,"HyperDash":false},{"StartTime":44537.0,"Position":71.0,"HyperDash":false}]},{"StartTime":44624.0,"Objects":[{"StartTime":44624.0,"Position":172.0,"HyperDash":false},{"StartTime":44710.0,"Position":127.0,"HyperDash":true}]},{"StartTime":44796.0,"Objects":[{"StartTime":44796.0,"Position":300.0,"HyperDash":false},{"StartTime":44882.0,"Position":356.5,"HyperDash":false},{"StartTime":44968.0,"Position":435.0,"HyperDash":false},{"StartTime":45011.0,"Position":468.75,"HyperDash":true}]},{"StartTime":45141.0,"Objects":[{"StartTime":45141.0,"Position":260.0,"HyperDash":false},{"StartTime":45227.0,"Position":345.5,"HyperDash":false},{"StartTime":45313.0,"Position":395.0,"HyperDash":false},{"StartTime":45356.0,"Position":428.75,"HyperDash":true}]},{"StartTime":45486.0,"Objects":[{"StartTime":45486.0,"Position":176.0,"HyperDash":false}]},{"StartTime":45507.0,"Objects":[{"StartTime":45507.0,"Position":158.0,"HyperDash":false}]},{"StartTime":45529.0,"Objects":[{"StartTime":45529.0,"Position":143.0,"HyperDash":false}]},{"StartTime":45550.0,"Objects":[{"StartTime":45550.0,"Position":129.0,"HyperDash":false}]},{"StartTime":45572.0,"Objects":[{"StartTime":45572.0,"Position":119.0,"HyperDash":false}]},{"StartTime":45594.0,"Objects":[{"StartTime":45594.0,"Position":111.0,"HyperDash":false}]},{"StartTime":45615.0,"Objects":[{"StartTime":45615.0,"Position":108.0,"HyperDash":false}]},{"StartTime":45637.0,"Objects":[{"StartTime":45637.0,"Position":108.0,"HyperDash":false}]},{"StartTime":45658.0,"Objects":[{"StartTime":45658.0,"Position":112.0,"HyperDash":false}]},{"StartTime":45680.0,"Objects":[{"StartTime":45680.0,"Position":120.0,"HyperDash":false}]},{"StartTime":45701.0,"Objects":[{"StartTime":45701.0,"Position":131.0,"HyperDash":false}]},{"StartTime":45723.0,"Objects":[{"StartTime":45723.0,"Position":145.0,"HyperDash":false}]},{"StartTime":45744.0,"Objects":[{"StartTime":45744.0,"Position":161.0,"HyperDash":false}]},{"StartTime":45766.0,"Objects":[{"StartTime":45766.0,"Position":178.0,"HyperDash":false}]},{"StartTime":45787.0,"Objects":[{"StartTime":45787.0,"Position":196.0,"HyperDash":false}]},{"StartTime":45809.0,"Objects":[{"StartTime":45809.0,"Position":214.0,"HyperDash":false}]},{"StartTime":45831.0,"Objects":[{"StartTime":45831.0,"Position":240.0,"HyperDash":false}]},{"StartTime":45852.0,"Objects":[{"StartTime":45852.0,"Position":257.0,"HyperDash":false}]},{"StartTime":45874.0,"Objects":[{"StartTime":45874.0,"Position":275.0,"HyperDash":false}]},{"StartTime":45895.0,"Objects":[{"StartTime":45895.0,"Position":291.0,"HyperDash":false}]},{"StartTime":45917.0,"Objects":[{"StartTime":45917.0,"Position":304.0,"HyperDash":false}]},{"StartTime":45938.0,"Objects":[{"StartTime":45938.0,"Position":315.0,"HyperDash":false}]},{"StartTime":45960.0,"Objects":[{"StartTime":45960.0,"Position":323.0,"HyperDash":false}]},{"StartTime":45981.0,"Objects":[{"StartTime":45981.0,"Position":327.0,"HyperDash":false}]},{"StartTime":46003.0,"Objects":[{"StartTime":46003.0,"Position":327.0,"HyperDash":false}]},{"StartTime":46025.0,"Objects":[{"StartTime":46025.0,"Position":324.0,"HyperDash":false}]},{"StartTime":46046.0,"Objects":[{"StartTime":46046.0,"Position":317.0,"HyperDash":false}]},{"StartTime":46068.0,"Objects":[{"StartTime":46068.0,"Position":306.0,"HyperDash":false}]},{"StartTime":46089.0,"Objects":[{"StartTime":46089.0,"Position":293.0,"HyperDash":false}]},{"StartTime":46111.0,"Objects":[{"StartTime":46111.0,"Position":277.0,"HyperDash":false}]},{"StartTime":46132.0,"Objects":[{"StartTime":46132.0,"Position":260.0,"HyperDash":true}]},{"StartTime":46175.0,"Objects":[{"StartTime":46175.0,"Position":76.0,"HyperDash":false},{"StartTime":46243.0,"Position":41.627903,"HyperDash":false},{"StartTime":46347.0,"Position":75.00001,"HyperDash":false}]},{"StartTime":46434.0,"Objects":[{"StartTime":46434.0,"Position":120.0,"HyperDash":true}]},{"StartTime":46520.0,"Objects":[{"StartTime":46520.0,"Position":280.0,"HyperDash":false},{"StartTime":46588.0,"Position":298.5814,"HyperDash":false},{"StartTime":46692.0,"Position":370.0,"HyperDash":false}]},{"StartTime":46779.0,"Objects":[{"StartTime":46779.0,"Position":324.0,"HyperDash":true}]},{"StartTime":46865.0,"Objects":[{"StartTime":46865.0,"Position":152.0,"HyperDash":false},{"StartTime":46951.0,"Position":107.0,"HyperDash":false}]},{"StartTime":47037.0,"Objects":[{"StartTime":47037.0,"Position":172.0,"HyperDash":false},{"StartTime":47123.0,"Position":127.0,"HyperDash":true}]},{"StartTime":47210.0,"Objects":[{"StartTime":47210.0,"Position":336.0,"HyperDash":false}]},{"StartTime":47253.0,"Objects":[{"StartTime":47253.0,"Position":363.0,"HyperDash":false}]},{"StartTime":47296.0,"Objects":[{"StartTime":47296.0,"Position":384.0,"HyperDash":false}]},{"StartTime":47339.0,"Objects":[{"StartTime":47339.0,"Position":393.0,"HyperDash":false}]},{"StartTime":47382.0,"Objects":[{"StartTime":47382.0,"Position":389.0,"HyperDash":false}]},{"StartTime":47425.0,"Objects":[{"StartTime":47425.0,"Position":372.0,"HyperDash":false}]},{"StartTime":47469.0,"Objects":[{"StartTime":47469.0,"Position":347.0,"HyperDash":true}]},{"StartTime":47555.0,"Objects":[{"StartTime":47555.0,"Position":168.0,"HyperDash":false},{"StartTime":47623.0,"Position":126.41861,"HyperDash":false},{"StartTime":47727.0,"Position":78.0,"HyperDash":false}]},{"StartTime":47900.0,"Objects":[{"StartTime":47900.0,"Position":244.0,"HyperDash":false},{"StartTime":47968.0,"Position":214.41861,"HyperDash":false},{"StartTime":48072.0,"Position":154.0,"HyperDash":true}]},{"StartTime":48244.0,"Objects":[{"StartTime":48244.0,"Position":400.0,"HyperDash":false},{"StartTime":48330.0,"Position":403.451355,"HyperDash":false}]},{"StartTime":48503.0,"Objects":[{"StartTime":48503.0,"Position":312.0,"HyperDash":true}]},{"StartTime":48589.0,"Objects":[{"StartTime":48589.0,"Position":140.0,"HyperDash":false}]},{"StartTime":48762.0,"Objects":[{"StartTime":48762.0,"Position":248.0,"HyperDash":true}]},{"StartTime":48934.0,"Objects":[{"StartTime":48934.0,"Position":16.0,"HyperDash":false},{"StartTime":49020.0,"Position":61.0,"HyperDash":false}]},{"StartTime":49193.0,"Objects":[{"StartTime":49193.0,"Position":160.0,"HyperDash":true}]},{"StartTime":49279.0,"Objects":[{"StartTime":49279.0,"Position":16.0,"HyperDash":false},{"StartTime":49365.0,"Position":20.0741081,"HyperDash":false}]},{"StartTime":49451.0,"Objects":[{"StartTime":49451.0,"Position":76.0,"HyperDash":false},{"StartTime":49537.0,"Position":121.0,"HyperDash":true}]},{"StartTime":49624.0,"Objects":[{"StartTime":49624.0,"Position":304.0,"HyperDash":false}]},{"StartTime":49667.0,"Objects":[{"StartTime":49667.0,"Position":317.0,"HyperDash":false}]},{"StartTime":49710.0,"Objects":[{"StartTime":49710.0,"Position":326.0,"HyperDash":false}]},{"StartTime":49753.0,"Objects":[{"StartTime":49753.0,"Position":328.0,"HyperDash":false}]},{"StartTime":49796.0,"Objects":[{"StartTime":49796.0,"Position":325.0,"HyperDash":false}]},{"StartTime":49839.0,"Objects":[{"StartTime":49839.0,"Position":316.0,"HyperDash":false}]},{"StartTime":49882.0,"Objects":[{"StartTime":49882.0,"Position":301.0,"HyperDash":true}]},{"StartTime":49969.0,"Objects":[{"StartTime":49969.0,"Position":120.0,"HyperDash":false}]},{"StartTime":50055.0,"Objects":[{"StartTime":50055.0,"Position":52.0,"HyperDash":false}]},{"StartTime":50141.0,"Objects":[{"StartTime":50141.0,"Position":120.0,"HyperDash":false}]},{"StartTime":50313.0,"Objects":[{"StartTime":50313.0,"Position":288.0,"HyperDash":false}]},{"StartTime":50400.0,"Objects":[{"StartTime":50400.0,"Position":332.0,"HyperDash":false}]},{"StartTime":50486.0,"Objects":[{"StartTime":50486.0,"Position":328.0,"HyperDash":false}]},{"StartTime":50572.0,"Objects":[{"StartTime":50572.0,"Position":280.0,"HyperDash":true}]},{"StartTime":50658.0,"Objects":[{"StartTime":50658.0,"Position":104.0,"HyperDash":false},{"StartTime":50744.0,"Position":59.0,"HyperDash":false}]},{"StartTime":50831.0,"Objects":[{"StartTime":50831.0,"Position":104.0,"HyperDash":false},{"StartTime":50917.0,"Position":149.0,"HyperDash":true}]},{"StartTime":51003.0,"Objects":[{"StartTime":51003.0,"Position":328.0,"HyperDash":false},{"StartTime":51071.0,"Position":375.5814,"HyperDash":false},{"StartTime":51175.0,"Position":334.0,"HyperDash":false}]},{"StartTime":51262.0,"Objects":[{"StartTime":51262.0,"Position":280.0,"HyperDash":true}]},{"StartTime":51348.0,"Objects":[{"StartTime":51348.0,"Position":128.0,"HyperDash":true},{"StartTime":51405.0,"Position":204.775,"HyperDash":false},{"StartTime":51498.0,"Position":364.25,"HyperDash":false}]},{"StartTime":51520.0,"Objects":[{"StartTime":51520.0,"Position":364.0,"HyperDash":false},{"StartTime":51588.0,"Position":296.6279,"HyperDash":false},{"StartTime":51692.0,"Position":229.0,"HyperDash":true}]},{"StartTime":51736.0,"Objects":[{"StartTime":51736.0,"Position":368.0,"HyperDash":false},{"StartTime":51865.0,"Position":435.5,"HyperDash":false}]},{"StartTime":51951.0,"Objects":[{"StartTime":51951.0,"Position":380.0,"HyperDash":true}]},{"StartTime":52037.0,"Objects":[{"StartTime":52037.0,"Position":204.0,"HyperDash":false},{"StartTime":52123.0,"Position":136.5,"HyperDash":false}]},{"StartTime":52210.0,"Objects":[{"StartTime":52210.0,"Position":223.0,"HyperDash":false},{"StartTime":52296.0,"Position":155.5,"HyperDash":true}]},{"StartTime":52382.0,"Objects":[{"StartTime":52382.0,"Position":388.0,"HyperDash":false},{"StartTime":52468.0,"Position":455.5,"HyperDash":false}]},{"StartTime":52555.0,"Objects":[{"StartTime":52555.0,"Position":368.0,"HyperDash":false},{"StartTime":52641.0,"Position":435.5,"HyperDash":true}]},{"StartTime":52727.0,"Objects":[{"StartTime":52727.0,"Position":224.0,"HyperDash":false}]},{"StartTime":52770.0,"Objects":[{"StartTime":52770.0,"Position":194.0,"HyperDash":false}]},{"StartTime":52813.0,"Objects":[{"StartTime":52813.0,"Position":169.0,"HyperDash":false}]},{"StartTime":52856.0,"Objects":[{"StartTime":52856.0,"Position":149.0,"HyperDash":false}]},{"StartTime":52900.0,"Objects":[{"StartTime":52900.0,"Position":137.0,"HyperDash":false}]},{"StartTime":52943.0,"Objects":[{"StartTime":52943.0,"Position":134.0,"HyperDash":false}]},{"StartTime":52986.0,"Objects":[{"StartTime":52986.0,"Position":141.0,"HyperDash":true}]},{"StartTime":53072.0,"Objects":[{"StartTime":53072.0,"Position":368.0,"HyperDash":true},{"StartTime":53140.0,"Position":260.0465,"HyperDash":false},{"StartTime":53244.0,"Position":143.0,"HyperDash":true}]},{"StartTime":53417.0,"Objects":[{"StartTime":53417.0,"Position":444.0,"HyperDash":true},{"StartTime":53485.0,"Position":358.0465,"HyperDash":false},{"StartTime":53589.0,"Position":219.0,"HyperDash":true}]},{"StartTime":53762.0,"Objects":[{"StartTime":53762.0,"Position":488.0,"HyperDash":false},{"StartTime":53830.0,"Position":475.545563,"HyperDash":false},{"StartTime":53934.0,"Position":491.909363,"HyperDash":false}]},{"StartTime":54106.0,"Objects":[{"StartTime":54106.0,"Position":336.0,"HyperDash":false}]},{"StartTime":54193.0,"Objects":[{"StartTime":54193.0,"Position":280.0,"HyperDash":false}]},{"StartTime":54279.0,"Objects":[{"StartTime":54279.0,"Position":228.0,"HyperDash":false}]},{"StartTime":54451.0,"Objects":[{"StartTime":54451.0,"Position":392.0,"HyperDash":false},{"StartTime":54494.0,"Position":394.4847,"HyperDash":true}]},{"StartTime":54624.0,"Objects":[{"StartTime":54624.0,"Position":188.0,"HyperDash":false},{"StartTime":54667.0,"Position":185.089874,"HyperDash":true}]},{"StartTime":54796.0,"Objects":[{"StartTime":54796.0,"Position":408.0,"HyperDash":false},{"StartTime":54882.0,"Position":363.0,"HyperDash":true}]},{"StartTime":54969.0,"Objects":[{"StartTime":54969.0,"Position":136.0,"HyperDash":false},{"StartTime":55055.0,"Position":181.0,"HyperDash":true}]},{"StartTime":55141.0,"Objects":[{"StartTime":55141.0,"Position":384.0,"HyperDash":false},{"StartTime":55198.0,"Position":345.1965,"HyperDash":false},{"StartTime":55255.0,"Position":294.0,"HyperDash":false},{"StartTime":55370.0,"Position":384.0,"HyperDash":true}]},{"StartTime":55486.0,"Objects":[{"StartTime":55486.0,"Position":172.0,"HyperDash":false}]},{"StartTime":55601.0,"Objects":[{"StartTime":55601.0,"Position":280.0,"HyperDash":false}]},{"StartTime":55716.0,"Objects":[{"StartTime":55716.0,"Position":388.0,"HyperDash":true}]},{"StartTime":55831.0,"Objects":[{"StartTime":55831.0,"Position":164.0,"HyperDash":false},{"StartTime":55888.0,"Position":147.131012,"HyperDash":false},{"StartTime":55945.0,"Position":104.0,"HyperDash":false},{"StartTime":56060.0,"Position":164.0,"HyperDash":true}]},{"StartTime":56175.0,"Objects":[{"StartTime":56175.0,"Position":340.0,"HyperDash":false}]},{"StartTime":56290.0,"Objects":[{"StartTime":56290.0,"Position":412.0,"HyperDash":false}]},{"StartTime":56405.0,"Objects":[{"StartTime":56405.0,"Position":412.0,"HyperDash":true}]},{"StartTime":56520.0,"Objects":[{"StartTime":56520.0,"Position":212.0,"HyperDash":false},{"StartTime":56606.0,"Position":148.1791,"HyperDash":false},{"StartTime":56692.0,"Position":144.4465,"HyperDash":false},{"StartTime":56778.0,"Position":188.107758,"HyperDash":false},{"StartTime":56864.0,"Position":241.294647,"HyperDash":false},{"StartTime":56950.0,"Position":307.139038,"HyperDash":false},{"StartTime":57037.0,"Position":323.301819,"HyperDash":false},{"StartTime":57123.0,"Position":297.7406,"HyperDash":true}]},{"StartTime":57210.0,"Objects":[{"StartTime":57210.0,"Position":128.0,"HyperDash":false}]},{"StartTime":57231.0,"Objects":[{"StartTime":57231.0,"Position":112.0,"HyperDash":false}]},{"StartTime":57253.0,"Objects":[{"StartTime":57253.0,"Position":97.0,"HyperDash":false}]},{"StartTime":57275.0,"Objects":[{"StartTime":57275.0,"Position":83.0,"HyperDash":false}]},{"StartTime":57296.0,"Objects":[{"StartTime":57296.0,"Position":70.0,"HyperDash":false}]},{"StartTime":57318.0,"Objects":[{"StartTime":57318.0,"Position":57.0,"HyperDash":false}]},{"StartTime":57339.0,"Objects":[{"StartTime":57339.0,"Position":46.0,"HyperDash":false}]},{"StartTime":57361.0,"Objects":[{"StartTime":57361.0,"Position":35.0,"HyperDash":false}]},{"StartTime":57382.0,"Objects":[{"StartTime":57382.0,"Position":26.0,"HyperDash":false}]},{"StartTime":57404.0,"Objects":[{"StartTime":57404.0,"Position":19.0,"HyperDash":false}]},{"StartTime":57425.0,"Objects":[{"StartTime":57425.0,"Position":13.0,"HyperDash":false}]},{"StartTime":57447.0,"Objects":[{"StartTime":57447.0,"Position":8.0,"HyperDash":false}]},{"StartTime":57469.0,"Objects":[{"StartTime":57469.0,"Position":5.0,"HyperDash":false}]},{"StartTime":57490.0,"Objects":[{"StartTime":57490.0,"Position":3.0,"HyperDash":false}]},{"StartTime":57512.0,"Objects":[{"StartTime":57512.0,"Position":3.0,"HyperDash":false}]},{"StartTime":57533.0,"Objects":[{"StartTime":57533.0,"Position":5.0,"HyperDash":false}]},{"StartTime":57555.0,"Objects":[{"StartTime":57555.0,"Position":8.0,"HyperDash":false}]},{"StartTime":57576.0,"Objects":[{"StartTime":57576.0,"Position":12.0,"HyperDash":false}]},{"StartTime":57598.0,"Objects":[{"StartTime":57598.0,"Position":18.0,"HyperDash":false}]},{"StartTime":57619.0,"Objects":[{"StartTime":57619.0,"Position":26.0,"HyperDash":false}]},{"StartTime":57641.0,"Objects":[{"StartTime":57641.0,"Position":35.0,"HyperDash":false}]},{"StartTime":57662.0,"Objects":[{"StartTime":57662.0,"Position":45.0,"HyperDash":false}]},{"StartTime":57684.0,"Objects":[{"StartTime":57684.0,"Position":56.0,"HyperDash":false}]},{"StartTime":57706.0,"Objects":[{"StartTime":57706.0,"Position":69.0,"HyperDash":false}]},{"StartTime":57727.0,"Objects":[{"StartTime":57727.0,"Position":82.0,"HyperDash":false}]},{"StartTime":57749.0,"Objects":[{"StartTime":57749.0,"Position":96.0,"HyperDash":false}]},{"StartTime":57770.0,"Objects":[{"StartTime":57770.0,"Position":111.0,"HyperDash":false}]},{"StartTime":57792.0,"Objects":[{"StartTime":57792.0,"Position":126.0,"HyperDash":false}]},{"StartTime":57813.0,"Objects":[{"StartTime":57813.0,"Position":142.0,"HyperDash":false}]},{"StartTime":57835.0,"Objects":[{"StartTime":57835.0,"Position":158.0,"HyperDash":false}]},{"StartTime":57856.0,"Objects":[{"StartTime":57856.0,"Position":174.0,"HyperDash":true}]},{"StartTime":57900.0,"Objects":[{"StartTime":57900.0,"Position":312.0,"HyperDash":false},{"StartTime":57968.0,"Position":371.3721,"HyperDash":false},{"StartTime":58072.0,"Position":447.0,"HyperDash":false}]},{"StartTime":58158.0,"Objects":[{"StartTime":58158.0,"Position":392.0,"HyperDash":true}]},{"StartTime":58244.0,"Objects":[{"StartTime":58244.0,"Position":216.0,"HyperDash":false},{"StartTime":58330.0,"Position":159.75,"HyperDash":false}]},{"StartTime":58417.0,"Objects":[{"StartTime":58417.0,"Position":232.0,"HyperDash":false},{"StartTime":58503.0,"Position":175.75,"HyperDash":true}]},{"StartTime":58589.0,"Objects":[{"StartTime":58589.0,"Position":20.0,"HyperDash":false},{"StartTime":58657.0,"Position":49.581398,"HyperDash":false},{"StartTime":58761.0,"Position":110.0,"HyperDash":false}]},{"StartTime":58934.0,"Objects":[{"StartTime":58934.0,"Position":276.0,"HyperDash":false},{"StartTime":59002.0,"Position":233.41861,"HyperDash":false},{"StartTime":59106.0,"Position":186.0,"HyperDash":true}]},{"StartTime":59279.0,"Objects":[{"StartTime":59279.0,"Position":440.0,"HyperDash":false}]},{"StartTime":59322.0,"Objects":[{"StartTime":59322.0,"Position":466.0,"HyperDash":false}]},{"StartTime":59365.0,"Objects":[{"StartTime":59365.0,"Position":484.0,"HyperDash":false}]},{"StartTime":59408.0,"Objects":[{"StartTime":59408.0,"Position":491.0,"HyperDash":false}]},{"StartTime":59451.0,"Objects":[{"StartTime":59451.0,"Position":484.0,"HyperDash":false}]},{"StartTime":59537.0,"Objects":[{"StartTime":59537.0,"Position":428.0,"HyperDash":true}]},{"StartTime":59624.0,"Objects":[{"StartTime":59624.0,"Position":260.0,"HyperDash":false},{"StartTime":59710.0,"Position":215.0,"HyperDash":false},{"StartTime":59796.0,"Position":260.0,"HyperDash":true}]},{"StartTime":59969.0,"Objects":[{"StartTime":59969.0,"Position":494.0,"HyperDash":false},{"StartTime":60055.0,"Position":497.451355,"HyperDash":false}]},{"StartTime":60227.0,"Objects":[{"StartTime":60227.0,"Position":392.0,"HyperDash":true}]},{"StartTime":60313.0,"Objects":[{"StartTime":60313.0,"Position":212.0,"HyperDash":false}]},{"StartTime":60486.0,"Objects":[{"StartTime":60486.0,"Position":356.0,"HyperDash":true}]},{"StartTime":60658.0,"Objects":[{"StartTime":60658.0,"Position":104.0,"HyperDash":false},{"StartTime":60744.0,"Position":100.262955,"HyperDash":false}]},{"StartTime":60917.0,"Objects":[{"StartTime":60917.0,"Position":204.0,"HyperDash":true}]},{"StartTime":61003.0,"Objects":[{"StartTime":61003.0,"Position":384.0,"HyperDash":false},{"StartTime":61089.0,"Position":339.0,"HyperDash":true}]},{"StartTime":61175.0,"Objects":[{"StartTime":61175.0,"Position":159.0,"HyperDash":false},{"StartTime":61261.0,"Position":226.5,"HyperDash":true}]},{"StartTime":61348.0,"Objects":[{"StartTime":61348.0,"Position":72.0,"HyperDash":false}]},{"StartTime":61434.0,"Objects":[{"StartTime":61434.0,"Position":9.0,"HyperDash":false}]},{"StartTime":61520.0,"Objects":[{"StartTime":61520.0,"Position":9.0,"HyperDash":false}]},{"StartTime":61606.0,"Objects":[{"StartTime":61606.0,"Position":70.0,"HyperDash":true}]},{"StartTime":61693.0,"Objects":[{"StartTime":61693.0,"Position":250.0,"HyperDash":false},{"StartTime":61761.0,"Position":304.5814,"HyperDash":false},{"StartTime":61865.0,"Position":340.0,"HyperDash":false}]},{"StartTime":62037.0,"Objects":[{"StartTime":62037.0,"Position":184.0,"HyperDash":false},{"StartTime":62105.0,"Position":143.41861,"HyperDash":false},{"StartTime":62209.0,"Position":178.0,"HyperDash":false}]},{"StartTime":62296.0,"Objects":[{"StartTime":62296.0,"Position":232.0,"HyperDash":true}]},{"StartTime":62382.0,"Objects":[{"StartTime":62382.0,"Position":384.0,"HyperDash":true},{"StartTime":62439.0,"Position":294.225,"HyperDash":false},{"StartTime":62532.0,"Position":147.749985,"HyperDash":false}]},{"StartTime":62555.0,"Objects":[{"StartTime":62555.0,"Position":148.0,"HyperDash":false},{"StartTime":62623.0,"Position":216.3721,"HyperDash":false},{"StartTime":62727.0,"Position":283.0,"HyperDash":true}]},{"StartTime":62770.0,"Objects":[{"StartTime":62770.0,"Position":144.0,"HyperDash":false},{"StartTime":62899.0,"Position":76.5,"HyperDash":false}]},{"StartTime":62986.0,"Objects":[{"StartTime":62986.0,"Position":132.0,"HyperDash":true}]},{"StartTime":63072.0,"Objects":[{"StartTime":63072.0,"Position":300.0,"HyperDash":false},{"StartTime":63158.0,"Position":345.0,"HyperDash":true}]},{"StartTime":63244.0,"Objects":[{"StartTime":63244.0,"Position":184.0,"HyperDash":false},{"StartTime":63330.0,"Position":229.0,"HyperDash":true}]},{"StartTime":63417.0,"Objects":[{"StartTime":63417.0,"Position":64.0,"HyperDash":false},{"StartTime":63503.0,"Position":19.0,"HyperDash":true}]},{"StartTime":63589.0,"Objects":[{"StartTime":63589.0,"Position":184.0,"HyperDash":false},{"StartTime":63675.0,"Position":139.0,"HyperDash":true}]},{"StartTime":63762.0,"Objects":[{"StartTime":63762.0,"Position":345.0,"HyperDash":false}]},{"StartTime":63805.0,"Objects":[{"StartTime":63805.0,"Position":375.0,"HyperDash":false}]},{"StartTime":63848.0,"Objects":[{"StartTime":63848.0,"Position":400.0,"HyperDash":false}]},{"StartTime":63891.0,"Objects":[{"StartTime":63891.0,"Position":420.0,"HyperDash":false}]},{"StartTime":63934.0,"Objects":[{"StartTime":63934.0,"Position":432.0,"HyperDash":false}]},{"StartTime":63977.0,"Objects":[{"StartTime":63977.0,"Position":435.0,"HyperDash":false}]},{"StartTime":64020.0,"Objects":[{"StartTime":64020.0,"Position":428.0,"HyperDash":true}]},{"StartTime":64106.0,"Objects":[{"StartTime":64106.0,"Position":224.0,"HyperDash":true},{"StartTime":64174.0,"Position":296.9535,"HyperDash":false},{"StartTime":64278.0,"Position":449.0,"HyperDash":true}]},{"StartTime":64451.0,"Objects":[{"StartTime":64451.0,"Position":148.0,"HyperDash":true},{"StartTime":64519.0,"Position":253.953491,"HyperDash":false},{"StartTime":64623.0,"Position":373.0,"HyperDash":true}]},{"StartTime":64796.0,"Objects":[{"StartTime":64796.0,"Position":120.0,"HyperDash":true}]},{"StartTime":64911.0,"Objects":[{"StartTime":64911.0,"Position":324.0,"HyperDash":true}]},{"StartTime":65026.0,"Objects":[{"StartTime":65026.0,"Position":120.0,"HyperDash":true}]},{"StartTime":65141.0,"Objects":[{"StartTime":65141.0,"Position":336.0,"HyperDash":false}]},{"StartTime":65256.0,"Objects":[{"StartTime":65256.0,"Position":222.0,"HyperDash":false}]},{"StartTime":65371.0,"Objects":[{"StartTime":65371.0,"Position":108.0,"HyperDash":true}]},{"StartTime":65486.0,"Objects":[{"StartTime":65486.0,"Position":336.0,"HyperDash":false}]},{"StartTime":65601.0,"Objects":[{"StartTime":65601.0,"Position":444.0,"HyperDash":false}]},{"StartTime":65716.0,"Objects":[{"StartTime":65716.0,"Position":336.0,"HyperDash":true}]},{"StartTime":65831.0,"Objects":[{"StartTime":65831.0,"Position":144.0,"HyperDash":false}]},{"StartTime":65946.0,"Objects":[{"StartTime":65946.0,"Position":252.0,"HyperDash":false}]},{"StartTime":66060.0,"Objects":[{"StartTime":66060.0,"Position":144.0,"HyperDash":true}]},{"StartTime":66175.0,"Objects":[{"StartTime":66175.0,"Position":360.0,"HyperDash":false},{"StartTime":66243.0,"Position":398.5814,"HyperDash":false},{"StartTime":66347.0,"Position":450.0,"HyperDash":false}]},{"StartTime":66434.0,"Objects":[{"StartTime":66434.0,"Position":396.0,"HyperDash":true}]},{"StartTime":66520.0,"Objects":[{"StartTime":66520.0,"Position":224.0,"HyperDash":false}]},{"StartTime":66693.0,"Objects":[{"StartTime":66693.0,"Position":388.0,"HyperDash":true}]},{"StartTime":66865.0,"Objects":[{"StartTime":66865.0,"Position":124.0,"HyperDash":false},{"StartTime":66951.0,"Position":120.54866,"HyperDash":false}]},{"StartTime":67037.0,"Objects":[{"StartTime":67037.0,"Position":204.0,"HyperDash":false},{"StartTime":67123.0,"Position":200.54866,"HyperDash":true}]},{"StartTime":67210.0,"Objects":[{"StartTime":67210.0,"Position":368.0,"HyperDash":false}]},{"StartTime":67382.0,"Objects":[{"StartTime":67382.0,"Position":204.0,"HyperDash":true}]},{"StartTime":67555.0,"Objects":[{"StartTime":67555.0,"Position":476.0,"HyperDash":false}]},{"StartTime":67900.0,"Objects":[{"StartTime":67900.0,"Position":188.0,"HyperDash":false}]},{"StartTime":68244.0,"Objects":[{"StartTime":68244.0,"Position":488.0,"HyperDash":false}]},{"StartTime":68417.0,"Objects":[{"StartTime":68417.0,"Position":356.0,"HyperDash":false},{"StartTime":68503.0,"Position":423.5,"HyperDash":true}]},{"StartTime":68589.0,"Objects":[{"StartTime":68589.0,"Position":172.0,"HyperDash":false},{"StartTime":68657.0,"Position":166.454437,"HyperDash":false},{"StartTime":68761.0,"Position":168.090652,"HyperDash":true}]},{"StartTime":68934.0,"Objects":[{"StartTime":68934.0,"Position":484.0,"HyperDash":false}]},{"StartTime":69279.0,"Objects":[{"StartTime":69279.0,"Position":368.0,"HyperDash":false},{"StartTime":69343.0,"Position":52.0,"HyperDash":false},{"StartTime":69408.0,"Position":327.0,"HyperDash":false},{"StartTime":69472.0,"Position":226.0,"HyperDash":false},{"StartTime":69537.0,"Position":110.0,"HyperDash":false},{"StartTime":69602.0,"Position":3.0,"HyperDash":false},{"StartTime":69666.0,"Position":26.0,"HyperDash":false},{"StartTime":69731.0,"Position":173.0,"HyperDash":false},{"StartTime":69796.0,"Position":18.0,"HyperDash":false},{"StartTime":69860.0,"Position":310.0,"HyperDash":false},{"StartTime":69925.0,"Position":394.0,"HyperDash":false},{"StartTime":69990.0,"Position":406.0,"HyperDash":false},{"StartTime":70054.0,"Position":262.0,"HyperDash":false},{"StartTime":70119.0,"Position":278.0,"HyperDash":false},{"StartTime":70184.0,"Position":171.0,"HyperDash":false},{"StartTime":70248.0,"Position":22.0,"HyperDash":false},{"StartTime":70313.0,"Position":187.0,"HyperDash":false},{"StartTime":70378.0,"Position":124.0,"HyperDash":false},{"StartTime":70442.0,"Position":454.0,"HyperDash":false},{"StartTime":70507.0,"Position":16.0,"HyperDash":false},{"StartTime":70572.0,"Position":61.0,"HyperDash":false},{"StartTime":70636.0,"Position":161.0,"HyperDash":false},{"StartTime":70701.0,"Position":243.0,"HyperDash":false},{"StartTime":70766.0,"Position":375.0,"HyperDash":false},{"StartTime":70830.0,"Position":247.0,"HyperDash":false},{"StartTime":70895.0,"Position":162.0,"HyperDash":false},{"StartTime":70960.0,"Position":383.0,"HyperDash":false},{"StartTime":71024.0,"Position":127.0,"HyperDash":false},{"StartTime":71089.0,"Position":161.0,"HyperDash":false},{"StartTime":71154.0,"Position":332.0,"HyperDash":false},{"StartTime":71218.0,"Position":356.0,"HyperDash":false},{"StartTime":71283.0,"Position":362.0,"HyperDash":false},{"StartTime":71348.0,"Position":347.0,"HyperDash":false}]},{"StartTime":71693.0,"Objects":[{"StartTime":71693.0,"Position":232.0,"HyperDash":false},{"StartTime":71714.0,"Position":229.7937,"HyperDash":false},{"StartTime":71736.0,"Position":232.0,"HyperDash":false},{"StartTime":71757.0,"Position":229.7937,"HyperDash":false},{"StartTime":71779.0,"Position":232.0,"HyperDash":false},{"StartTime":71800.0,"Position":229.7937,"HyperDash":false},{"StartTime":71822.0,"Position":232.0,"HyperDash":false},{"StartTime":71843.0,"Position":229.7937,"HyperDash":false},{"StartTime":71865.0,"Position":232.0,"HyperDash":false},{"StartTime":71886.0,"Position":229.7937,"HyperDash":false},{"StartTime":71908.0,"Position":232.0,"HyperDash":false},{"StartTime":71930.0,"Position":229.7937,"HyperDash":false},{"StartTime":71951.0,"Position":232.0,"HyperDash":false},{"StartTime":71973.0,"Position":229.7937,"HyperDash":false},{"StartTime":71994.0,"Position":232.0,"HyperDash":false}]},{"StartTime":72037.0,"Objects":[{"StartTime":72037.0,"Position":272.0,"HyperDash":false},{"StartTime":72058.0,"Position":277.46347,"HyperDash":false},{"StartTime":72080.0,"Position":272.0,"HyperDash":false},{"StartTime":72101.0,"Position":277.46347,"HyperDash":false},{"StartTime":72123.0,"Position":272.0,"HyperDash":false},{"StartTime":72144.0,"Position":277.46347,"HyperDash":false},{"StartTime":72166.0,"Position":272.0,"HyperDash":false},{"StartTime":72187.0,"Position":277.46347,"HyperDash":false},{"StartTime":72209.0,"Position":272.0,"HyperDash":false},{"StartTime":72230.0,"Position":277.46347,"HyperDash":false},{"StartTime":72252.0,"Position":272.0,"HyperDash":false},{"StartTime":72274.0,"Position":277.46347,"HyperDash":false},{"StartTime":72295.0,"Position":272.0,"HyperDash":false},{"StartTime":72317.0,"Position":277.46347,"HyperDash":false},{"StartTime":72338.0,"Position":272.0,"HyperDash":false}]},{"StartTime":72382.0,"Objects":[{"StartTime":72382.0,"Position":316.0,"HyperDash":false},{"StartTime":72403.0,"Position":324.5015,"HyperDash":false},{"StartTime":72425.0,"Position":316.0,"HyperDash":false},{"StartTime":72446.0,"Position":324.5015,"HyperDash":false},{"StartTime":72468.0,"Position":316.0,"HyperDash":false},{"StartTime":72489.0,"Position":324.5015,"HyperDash":false},{"StartTime":72511.0,"Position":316.0,"HyperDash":false},{"StartTime":72532.0,"Position":324.5015,"HyperDash":false},{"StartTime":72554.0,"Position":316.0,"HyperDash":false},{"StartTime":72575.0,"Position":324.5015,"HyperDash":false},{"StartTime":72597.0,"Position":316.0,"HyperDash":false},{"StartTime":72619.0,"Position":324.5015,"HyperDash":false},{"StartTime":72640.0,"Position":316.0,"HyperDash":false},{"StartTime":72662.0,"Position":324.5015,"HyperDash":false},{"StartTime":72683.0,"Position":316.0,"HyperDash":false}]},{"StartTime":72727.0,"Objects":[{"StartTime":72727.0,"Position":360.0,"HyperDash":false},{"StartTime":72748.0,"Position":368.5015,"HyperDash":false},{"StartTime":72770.0,"Position":360.0,"HyperDash":false},{"StartTime":72791.0,"Position":368.5015,"HyperDash":false},{"StartTime":72813.0,"Position":360.0,"HyperDash":false},{"StartTime":72834.0,"Position":368.5015,"HyperDash":false},{"StartTime":72856.0,"Position":360.0,"HyperDash":false},{"StartTime":72877.0,"Position":368.5015,"HyperDash":false},{"StartTime":72899.0,"Position":360.0,"HyperDash":false},{"StartTime":72920.0,"Position":368.5015,"HyperDash":false},{"StartTime":72942.0,"Position":360.0,"HyperDash":false},{"StartTime":72964.0,"Position":368.5015,"HyperDash":false},{"StartTime":72985.0,"Position":360.0,"HyperDash":false},{"StartTime":73007.0,"Position":368.5015,"HyperDash":false},{"StartTime":73028.0,"Position":360.0,"HyperDash":true}]},{"StartTime":73072.0,"Objects":[{"StartTime":73072.0,"Position":256.0,"HyperDash":false}]},{"StartTime":73094.0,"Objects":[{"StartTime":73094.0,"Position":244.0,"HyperDash":false}]},{"StartTime":73115.0,"Objects":[{"StartTime":73115.0,"Position":233.0,"HyperDash":false}]},{"StartTime":73137.0,"Objects":[{"StartTime":73137.0,"Position":224.0,"HyperDash":false}]},{"StartTime":73158.0,"Objects":[{"StartTime":73158.0,"Position":215.0,"HyperDash":false}]},{"StartTime":73180.0,"Objects":[{"StartTime":73180.0,"Position":209.0,"HyperDash":false}]},{"StartTime":73201.0,"Objects":[{"StartTime":73201.0,"Position":205.0,"HyperDash":false}]},{"StartTime":73223.0,"Objects":[{"StartTime":73223.0,"Position":202.0,"HyperDash":false}]},{"StartTime":73244.0,"Objects":[{"StartTime":73244.0,"Position":203.0,"HyperDash":false}]},{"StartTime":73266.0,"Objects":[{"StartTime":73266.0,"Position":205.0,"HyperDash":false}]},{"StartTime":73287.0,"Objects":[{"StartTime":73287.0,"Position":210.0,"HyperDash":false}]},{"StartTime":73309.0,"Objects":[{"StartTime":73309.0,"Position":217.0,"HyperDash":false}]},{"StartTime":73331.0,"Objects":[{"StartTime":73331.0,"Position":226.0,"HyperDash":false}]},{"StartTime":73352.0,"Objects":[{"StartTime":73352.0,"Position":236.0,"HyperDash":false}]},{"StartTime":73374.0,"Objects":[{"StartTime":73374.0,"Position":247.0,"HyperDash":false}]},{"StartTime":73395.0,"Objects":[{"StartTime":73395.0,"Position":258.0,"HyperDash":false}]},{"StartTime":73417.0,"Objects":[{"StartTime":73417.0,"Position":270.0,"HyperDash":false}]},{"StartTime":73438.0,"Objects":[{"StartTime":73438.0,"Position":281.0,"HyperDash":false}]},{"StartTime":73460.0,"Objects":[{"StartTime":73460.0,"Position":291.0,"HyperDash":false}]},{"StartTime":73481.0,"Objects":[{"StartTime":73481.0,"Position":300.0,"HyperDash":false}]},{"StartTime":73503.0,"Objects":[{"StartTime":73503.0,"Position":307.0,"HyperDash":false}]},{"StartTime":73525.0,"Objects":[{"StartTime":73525.0,"Position":313.0,"HyperDash":false}]},{"StartTime":73546.0,"Objects":[{"StartTime":73546.0,"Position":316.0,"HyperDash":false}]},{"StartTime":73568.0,"Objects":[{"StartTime":73568.0,"Position":317.0,"HyperDash":false}]},{"StartTime":73589.0,"Objects":[{"StartTime":73589.0,"Position":315.0,"HyperDash":false}]},{"StartTime":73611.0,"Objects":[{"StartTime":73611.0,"Position":311.0,"HyperDash":false}]},{"StartTime":73632.0,"Objects":[{"StartTime":73632.0,"Position":305.0,"HyperDash":false}]},{"StartTime":73654.0,"Objects":[{"StartTime":73654.0,"Position":297.0,"HyperDash":false}]},{"StartTime":73675.0,"Objects":[{"StartTime":73675.0,"Position":288.0,"HyperDash":false}]},{"StartTime":73697.0,"Objects":[{"StartTime":73697.0,"Position":277.0,"HyperDash":false}]},{"StartTime":73719.0,"Objects":[{"StartTime":73719.0,"Position":266.0,"HyperDash":true}]},{"StartTime":73762.0,"Objects":[{"StartTime":73762.0,"Position":164.0,"HyperDash":false}]},{"StartTime":73783.0,"Objects":[{"StartTime":73783.0,"Position":153.0,"HyperDash":false}]},{"StartTime":73805.0,"Objects":[{"StartTime":73805.0,"Position":143.0,"HyperDash":false}]},{"StartTime":73826.0,"Objects":[{"StartTime":73826.0,"Position":133.0,"HyperDash":false}]},{"StartTime":73848.0,"Objects":[{"StartTime":73848.0,"Position":124.0,"HyperDash":false}]},{"StartTime":73869.0,"Objects":[{"StartTime":73869.0,"Position":115.0,"HyperDash":false}]},{"StartTime":73891.0,"Objects":[{"StartTime":73891.0,"Position":108.0,"HyperDash":false}]},{"StartTime":73912.0,"Objects":[{"StartTime":73912.0,"Position":101.0,"HyperDash":false}]},{"StartTime":73934.0,"Objects":[{"StartTime":73934.0,"Position":95.0,"HyperDash":false}]},{"StartTime":73956.0,"Objects":[{"StartTime":73956.0,"Position":90.0,"HyperDash":false}]},{"StartTime":73977.0,"Objects":[{"StartTime":73977.0,"Position":85.0,"HyperDash":false}]},{"StartTime":73999.0,"Objects":[{"StartTime":73999.0,"Position":82.0,"HyperDash":false}]},{"StartTime":74020.0,"Objects":[{"StartTime":74020.0,"Position":80.0,"HyperDash":false}]},{"StartTime":74042.0,"Objects":[{"StartTime":74042.0,"Position":79.0,"HyperDash":false}]},{"StartTime":74063.0,"Objects":[{"StartTime":74063.0,"Position":79.0,"HyperDash":true}]},{"StartTime":74106.0,"Objects":[{"StartTime":74106.0,"Position":180.0,"HyperDash":false}]},{"StartTime":74128.0,"Objects":[{"StartTime":74128.0,"Position":190.0,"HyperDash":false}]},{"StartTime":74150.0,"Objects":[{"StartTime":74150.0,"Position":200.0,"HyperDash":false}]},{"StartTime":74171.0,"Objects":[{"StartTime":74171.0,"Position":210.0,"HyperDash":false}]},{"StartTime":74193.0,"Objects":[{"StartTime":74193.0,"Position":219.0,"HyperDash":false}]},{"StartTime":74214.0,"Objects":[{"StartTime":74214.0,"Position":228.0,"HyperDash":false}]},{"StartTime":74236.0,"Objects":[{"StartTime":74236.0,"Position":235.0,"HyperDash":false}]},{"StartTime":74257.0,"Objects":[{"StartTime":74257.0,"Position":242.0,"HyperDash":false}]},{"StartTime":74279.0,"Objects":[{"StartTime":74279.0,"Position":248.0,"HyperDash":false}]},{"StartTime":74300.0,"Objects":[{"StartTime":74300.0,"Position":253.0,"HyperDash":false}]},{"StartTime":74322.0,"Objects":[{"StartTime":74322.0,"Position":258.0,"HyperDash":false}]},{"StartTime":74344.0,"Objects":[{"StartTime":74344.0,"Position":261.0,"HyperDash":false}]},{"StartTime":74365.0,"Objects":[{"StartTime":74365.0,"Position":263.0,"HyperDash":false}]},{"StartTime":74387.0,"Objects":[{"StartTime":74387.0,"Position":264.0,"HyperDash":false}]},{"StartTime":74408.0,"Objects":[{"StartTime":74408.0,"Position":264.0,"HyperDash":true}]},{"StartTime":74451.0,"Objects":[{"StartTime":74451.0,"Position":148.0,"HyperDash":false},{"StartTime":74519.0,"Position":111.4186,"HyperDash":false},{"StartTime":74623.0,"Position":58.0,"HyperDash":false}]},{"StartTime":74796.0,"Objects":[{"StartTime":74796.0,"Position":196.0,"HyperDash":false},{"StartTime":74864.0,"Position":187.840836,"HyperDash":false},{"StartTime":74968.0,"Position":193.068,"HyperDash":false}]},{"StartTime":75141.0,"Objects":[{"StartTime":75141.0,"Position":328.0,"HyperDash":false},{"StartTime":75209.0,"Position":324.84082,"HyperDash":false},{"StartTime":75313.0,"Position":325.068,"HyperDash":false}]},{"StartTime":75486.0,"Objects":[{"StartTime":75486.0,"Position":228.0,"HyperDash":false}]},{"StartTime":75658.0,"Objects":[{"StartTime":75658.0,"Position":396.0,"HyperDash":true}]},{"StartTime":75831.0,"Objects":[{"StartTime":75831.0,"Position":124.0,"HyperDash":false}]},{"StartTime":76003.0,"Objects":[{"StartTime":76003.0,"Position":36.0,"HyperDash":false}]},{"StartTime":76175.0,"Objects":[{"StartTime":76175.0,"Position":36.0,"HyperDash":false}]},{"StartTime":76348.0,"Objects":[{"StartTime":76348.0,"Position":124.0,"HyperDash":false}]},{"StartTime":76520.0,"Objects":[{"StartTime":76520.0,"Position":292.0,"HyperDash":false},{"StartTime":76588.0,"Position":308.15918,"HyperDash":false},{"StartTime":76692.0,"Position":294.932,"HyperDash":false}]},{"StartTime":76865.0,"Objects":[{"StartTime":76865.0,"Position":192.0,"HyperDash":false},{"StartTime":76933.0,"Position":210.48027,"HyperDash":false},{"StartTime":77037.0,"Position":195.744232,"HyperDash":false}]},{"StartTime":77210.0,"Objects":[{"StartTime":77210.0,"Position":368.0,"HyperDash":false},{"StartTime":77296.0,"Position":391.6784,"HyperDash":false},{"StartTime":77382.0,"Position":424.106964,"HyperDash":false},{"StartTime":77450.0,"Position":426.0137,"HyperDash":false},{"StartTime":77554.0,"Position":368.760162,"HyperDash":false}]},{"StartTime":77727.0,"Objects":[{"StartTime":77727.0,"Position":272.0,"HyperDash":false}]},{"StartTime":77900.0,"Objects":[{"StartTime":77900.0,"Position":176.0,"HyperDash":false},{"StartTime":77968.0,"Position":181.840836,"HyperDash":false},{"StartTime":78072.0,"Position":173.068,"HyperDash":false}]},{"StartTime":78244.0,"Objects":[{"StartTime":78244.0,"Position":272.0,"HyperDash":false}]},{"StartTime":78417.0,"Objects":[{"StartTime":78417.0,"Position":104.0,"HyperDash":true}]},{"StartTime":78589.0,"Objects":[{"StartTime":78589.0,"Position":380.0,"HyperDash":false},{"StartTime":78675.0,"Position":393.75,"HyperDash":false},{"StartTime":78761.0,"Position":447.5,"HyperDash":false},{"StartTime":78829.0,"Position":419.813965,"HyperDash":false},{"StartTime":78933.0,"Position":380.0,"HyperDash":false}]},{"StartTime":79106.0,"Objects":[{"StartTime":79106.0,"Position":284.0,"HyperDash":false}]},{"StartTime":79279.0,"Objects":[{"StartTime":79279.0,"Position":116.0,"HyperDash":false},{"StartTime":79347.0,"Position":99.84083,"HyperDash":false},{"StartTime":79451.0,"Position":113.067986,"HyperDash":false}]},{"StartTime":79624.0,"Objects":[{"StartTime":79624.0,"Position":216.0,"HyperDash":false},{"StartTime":79692.0,"Position":223.68605,"HyperDash":false},{"StartTime":79796.0,"Position":283.5,"HyperDash":false}]},{"StartTime":79882.0,"Objects":[{"StartTime":79882.0,"Position":324.0,"HyperDash":true}]},{"StartTime":79969.0,"Objects":[{"StartTime":79969.0,"Position":152.0,"HyperDash":false},{"StartTime":80055.0,"Position":111.0,"HyperDash":false},{"StartTime":80141.0,"Position":62.0,"HyperDash":false},{"StartTime":80209.0,"Position":99.58139,"HyperDash":false},{"StartTime":80313.0,"Position":152.0,"HyperDash":false}]},{"StartTime":80486.0,"Objects":[{"StartTime":80486.0,"Position":248.0,"HyperDash":false}]},{"StartTime":80658.0,"Objects":[{"StartTime":80658.0,"Position":416.0,"HyperDash":false},{"StartTime":80726.0,"Position":434.4803,"HyperDash":false},{"StartTime":80830.0,"Position":419.744232,"HyperDash":false}]},{"StartTime":81003.0,"Objects":[{"StartTime":81003.0,"Position":324.0,"HyperDash":false},{"StartTime":81071.0,"Position":308.313965,"HyperDash":false},{"StartTime":81175.0,"Position":256.5,"HyperDash":false}]},{"StartTime":81262.0,"Objects":[{"StartTime":81262.0,"Position":208.0,"HyperDash":true}]},{"StartTime":81348.0,"Objects":[{"StartTime":81348.0,"Position":384.0,"HyperDash":false},{"StartTime":81434.0,"Position":431.0,"HyperDash":false},{"StartTime":81520.0,"Position":474.0,"HyperDash":false},{"StartTime":81588.0,"Position":446.4186,"HyperDash":false},{"StartTime":81692.0,"Position":384.0,"HyperDash":false}]},{"StartTime":81865.0,"Objects":[{"StartTime":81865.0,"Position":212.0,"HyperDash":true}]},{"StartTime":82037.0,"Objects":[{"StartTime":82037.0,"Position":444.0,"HyperDash":false},{"StartTime":82105.0,"Position":438.4026,"HyperDash":false},{"StartTime":82209.0,"Position":447.547729,"HyperDash":true}]},{"StartTime":82382.0,"Objects":[{"StartTime":82382.0,"Position":212.0,"HyperDash":false}]},{"StartTime":82469.0,"Objects":[{"StartTime":82469.0,"Position":172.0,"HyperDash":false}]},{"StartTime":82555.0,"Objects":[{"StartTime":82555.0,"Position":132.0,"HyperDash":true}]},{"StartTime":82727.0,"Objects":[{"StartTime":82727.0,"Position":432.0,"HyperDash":false},{"StartTime":82813.0,"Position":480.699646,"HyperDash":false},{"StartTime":82899.0,"Position":500.151184,"HyperDash":false},{"StartTime":82967.0,"Position":468.371918,"HyperDash":false},{"StartTime":83071.0,"Position":432.553162,"HyperDash":false}]},{"StartTime":83244.0,"Objects":[{"StartTime":83244.0,"Position":272.0,"HyperDash":false}]},{"StartTime":83417.0,"Objects":[{"StartTime":83417.0,"Position":440.0,"HyperDash":false},{"StartTime":83485.0,"Position":421.4803,"HyperDash":false},{"StartTime":83589.0,"Position":443.744232,"HyperDash":true}]},{"StartTime":83762.0,"Objects":[{"StartTime":83762.0,"Position":200.0,"HyperDash":false}]},{"StartTime":83934.0,"Objects":[{"StartTime":83934.0,"Position":352.0,"HyperDash":true}]},{"StartTime":84106.0,"Objects":[{"StartTime":84106.0,"Position":104.0,"HyperDash":false},{"StartTime":84192.0,"Position":62.0,"HyperDash":false},{"StartTime":84278.0,"Position":14.0,"HyperDash":false},{"StartTime":84346.0,"Position":49.58139,"HyperDash":false},{"StartTime":84450.0,"Position":104.0,"HyperDash":false}]},{"StartTime":84624.0,"Objects":[{"StartTime":84624.0,"Position":272.0,"HyperDash":false}]},{"StartTime":84796.0,"Objects":[{"StartTime":84796.0,"Position":112.0,"HyperDash":false},{"StartTime":84864.0,"Position":120.667366,"HyperDash":false},{"StartTime":84968.0,"Position":108.629211,"HyperDash":false}]},{"StartTime":85055.0,"Objects":[{"StartTime":85055.0,"Position":164.0,"HyperDash":false}]},{"StartTime":85141.0,"Objects":[{"StartTime":85141.0,"Position":216.0,"HyperDash":false},{"StartTime":85209.0,"Position":224.68605,"HyperDash":false},{"StartTime":85313.0,"Position":283.5,"HyperDash":true}]},{"StartTime":85486.0,"Objects":[{"StartTime":85486.0,"Position":32.0,"HyperDash":false},{"StartTime":85572.0,"Position":0.299288034,"HyperDash":false},{"StartTime":85658.0,"Position":4.98187447,"HyperDash":false},{"StartTime":85744.0,"Position":36.15001,"HyperDash":false}]},{"StartTime":85831.0,"Objects":[{"StartTime":85831.0,"Position":108.0,"HyperDash":false},{"StartTime":85899.0,"Position":145.58139,"HyperDash":false},{"StartTime":86003.0,"Position":198.0,"HyperDash":false}]},{"StartTime":86175.0,"Objects":[{"StartTime":86175.0,"Position":20.0,"HyperDash":false}]},{"StartTime":86348.0,"Objects":[{"StartTime":86348.0,"Position":128.0,"HyperDash":false},{"StartTime":86434.0,"Position":173.0,"HyperDash":true}]},{"StartTime":86520.0,"Objects":[{"StartTime":86520.0,"Position":344.0,"HyperDash":false},{"StartTime":86588.0,"Position":325.4186,"HyperDash":false},{"StartTime":86692.0,"Position":254.0,"HyperDash":false}]},{"StartTime":86865.0,"Objects":[{"StartTime":86865.0,"Position":436.0,"HyperDash":false},{"StartTime":86933.0,"Position":448.3675,"HyperDash":false},{"StartTime":87037.0,"Position":439.458984,"HyperDash":false}]},{"StartTime":87124.0,"Objects":[{"StartTime":87124.0,"Position":375.0,"HyperDash":false}]},{"StartTime":87210.0,"Objects":[{"StartTime":87210.0,"Position":312.0,"HyperDash":false}]},{"StartTime":87382.0,"Objects":[{"StartTime":87382.0,"Position":472.0,"HyperDash":false}]},{"StartTime":87555.0,"Objects":[{"StartTime":87555.0,"Position":300.0,"HyperDash":false},{"StartTime":87623.0,"Position":293.518738,"HyperDash":false},{"StartTime":87727.0,"Position":296.253265,"HyperDash":false}]},{"StartTime":87813.0,"Objects":[{"StartTime":87813.0,"Position":360.0,"HyperDash":true}]},{"StartTime":87900.0,"Objects":[{"StartTime":87900.0,"Position":196.0,"HyperDash":false},{"StartTime":87968.0,"Position":147.41861,"HyperDash":false},{"StartTime":88072.0,"Position":106.0,"HyperDash":false}]},{"StartTime":88244.0,"Objects":[{"StartTime":88244.0,"Position":276.0,"HyperDash":false},{"StartTime":88312.0,"Position":320.5814,"HyperDash":false},{"StartTime":88416.0,"Position":366.0,"HyperDash":false}]},{"StartTime":88503.0,"Objects":[{"StartTime":88503.0,"Position":312.0,"HyperDash":false}]},{"StartTime":88589.0,"Objects":[{"StartTime":88589.0,"Position":260.0,"HyperDash":false}]},{"StartTime":88762.0,"Objects":[{"StartTime":88762.0,"Position":440.0,"HyperDash":true}]},{"StartTime":88934.0,"Objects":[{"StartTime":88934.0,"Position":192.0,"HyperDash":false},{"StartTime":89002.0,"Position":158.41861,"HyperDash":false},{"StartTime":89106.0,"Position":102.0,"HyperDash":false}]},{"StartTime":89193.0,"Objects":[{"StartTime":89193.0,"Position":164.0,"HyperDash":false}]},{"StartTime":89279.0,"Objects":[{"StartTime":89279.0,"Position":228.0,"HyperDash":false},{"StartTime":89347.0,"Position":188.41861,"HyperDash":false},{"StartTime":89451.0,"Position":138.0,"HyperDash":false}]},{"StartTime":89624.0,"Objects":[{"StartTime":89624.0,"Position":306.0,"HyperDash":false},{"StartTime":89692.0,"Position":334.5814,"HyperDash":false},{"StartTime":89796.0,"Position":396.0,"HyperDash":false}]},{"StartTime":89882.0,"Objects":[{"StartTime":89882.0,"Position":450.0,"HyperDash":false}]},{"StartTime":89969.0,"Objects":[{"StartTime":89969.0,"Position":396.0,"HyperDash":false}]},{"StartTime":90141.0,"Objects":[{"StartTime":90141.0,"Position":228.0,"HyperDash":false}]},{"StartTime":90313.0,"Objects":[{"StartTime":90313.0,"Position":396.0,"HyperDash":false},{"StartTime":90381.0,"Position":408.481262,"HyperDash":false},{"StartTime":90485.0,"Position":399.746735,"HyperDash":false}]},{"StartTime":90572.0,"Objects":[{"StartTime":90572.0,"Position":332.0,"HyperDash":false}]},{"StartTime":90658.0,"Objects":[{"StartTime":90658.0,"Position":264.0,"HyperDash":false},{"StartTime":90726.0,"Position":289.5814,"HyperDash":false},{"StartTime":90830.0,"Position":354.0,"HyperDash":false}]},{"StartTime":91003.0,"Objects":[{"StartTime":91003.0,"Position":184.0,"HyperDash":false},{"StartTime":91071.0,"Position":167.41861,"HyperDash":false},{"StartTime":91175.0,"Position":94.0,"HyperDash":false}]},{"StartTime":91262.0,"Objects":[{"StartTime":91262.0,"Position":148.0,"HyperDash":false}]},{"StartTime":91348.0,"Objects":[{"StartTime":91348.0,"Position":200.0,"HyperDash":false}]},{"StartTime":91520.0,"Objects":[{"StartTime":91520.0,"Position":32.0,"HyperDash":true}]},{"StartTime":91693.0,"Objects":[{"StartTime":91693.0,"Position":296.0,"HyperDash":false},{"StartTime":91761.0,"Position":318.5814,"HyperDash":false},{"StartTime":91865.0,"Position":302.0,"HyperDash":false}]},{"StartTime":91951.0,"Objects":[{"StartTime":91951.0,"Position":240.0,"HyperDash":false}]},{"StartTime":92037.0,"Objects":[{"StartTime":92037.0,"Position":136.0,"HyperDash":false},{"StartTime":92123.0,"Position":133.503845,"HyperDash":false}]},{"StartTime":92210.0,"Objects":[{"StartTime":92210.0,"Position":196.0,"HyperDash":false},{"StartTime":92296.0,"Position":199.206116,"HyperDash":true}]},{"StartTime":92382.0,"Objects":[{"StartTime":92382.0,"Position":48.0,"HyperDash":false},{"StartTime":92450.0,"Position":10.418602,"HyperDash":false},{"StartTime":92554.0,"Position":50.0,"HyperDash":false}]},{"StartTime":92641.0,"Objects":[{"StartTime":92641.0,"Position":120.0,"HyperDash":false}]},{"StartTime":92727.0,"Objects":[{"StartTime":92727.0,"Position":188.0,"HyperDash":false}]},{"StartTime":92900.0,"Objects":[{"StartTime":92900.0,"Position":360.0,"HyperDash":true}]},{"StartTime":93072.0,"Objects":[{"StartTime":93072.0,"Position":123.0,"HyperDash":false},{"StartTime":93140.0,"Position":135.518738,"HyperDash":false},{"StartTime":93244.0,"Position":119.25325,"HyperDash":false}]},{"StartTime":93331.0,"Objects":[{"StartTime":93331.0,"Position":188.0,"HyperDash":true}]},{"StartTime":93417.0,"Objects":[{"StartTime":93417.0,"Position":368.0,"HyperDash":false},{"StartTime":93503.0,"Position":413.0,"HyperDash":false},{"StartTime":93589.0,"Position":368.0,"HyperDash":true}]},{"StartTime":93762.0,"Objects":[{"StartTime":93762.0,"Position":96.0,"HyperDash":false}]},{"StartTime":93848.0,"Objects":[{"StartTime":93848.0,"Position":53.0,"HyperDash":false}]},{"StartTime":93934.0,"Objects":[{"StartTime":93934.0,"Position":45.0,"HyperDash":false}]},{"StartTime":94020.0,"Objects":[{"StartTime":94020.0,"Position":75.0,"HyperDash":false}]},{"StartTime":94106.0,"Objects":[{"StartTime":94106.0,"Position":128.0,"HyperDash":false}]},{"StartTime":94279.0,"Objects":[{"StartTime":94279.0,"Position":316.0,"HyperDash":true}]},{"StartTime":94451.0,"Objects":[{"StartTime":94451.0,"Position":48.0,"HyperDash":false},{"StartTime":94519.0,"Position":51.57788,"HyperDash":false},{"StartTime":94623.0,"Position":44.4028778,"HyperDash":false}]},{"StartTime":94710.0,"Objects":[{"StartTime":94710.0,"Position":112.0,"HyperDash":true}]},{"StartTime":94796.0,"Objects":[{"StartTime":94796.0,"Position":300.0,"HyperDash":false}]},{"StartTime":94969.0,"Objects":[{"StartTime":94969.0,"Position":416.0,"HyperDash":false},{"StartTime":95055.0,"Position":371.0,"HyperDash":true}]},{"StartTime":95141.0,"Objects":[{"StartTime":95141.0,"Position":180.0,"HyperDash":false}]},{"StartTime":95227.0,"Objects":[{"StartTime":95227.0,"Position":128.0,"HyperDash":false}]},{"StartTime":95313.0,"Objects":[{"StartTime":95313.0,"Position":76.0,"HyperDash":false}]},{"StartTime":95486.0,"Objects":[{"StartTime":95486.0,"Position":248.0,"HyperDash":false}]},{"StartTime":95658.0,"Objects":[{"StartTime":95658.0,"Position":68.0,"HyperDash":true}]},{"StartTime":95831.0,"Objects":[{"StartTime":95831.0,"Position":348.0,"HyperDash":false},{"StartTime":95899.0,"Position":373.5814,"HyperDash":false},{"StartTime":96003.0,"Position":438.0,"HyperDash":true}]},{"StartTime":96175.0,"Objects":[{"StartTime":96175.0,"Position":176.0,"HyperDash":false},{"StartTime":96261.0,"Position":102.839455,"HyperDash":false},{"StartTime":96347.0,"Position":70.72701,"HyperDash":false},{"StartTime":96433.0,"Position":95.68428,"HyperDash":false},{"StartTime":96519.0,"Position":200.009659,"HyperDash":false},{"StartTime":96605.0,"Position":276.00058,"HyperDash":false},{"StartTime":96692.0,"Position":280.8676,"HyperDash":false},{"StartTime":96821.0,"Position":179.5454,"HyperDash":false}]},{"StartTime":96865.0,"Objects":[{"StartTime":96865.0,"Position":156.0,"HyperDash":false},{"StartTime":96951.0,"Position":90.61737,"HyperDash":false},{"StartTime":97037.0,"Position":78.41168,"HyperDash":false},{"StartTime":97123.0,"Position":117.060234,"HyperDash":false},{"StartTime":97209.0,"Position":173.374588,"HyperDash":false},{"StartTime":97295.0,"Position":216.741837,"HyperDash":false},{"StartTime":97382.0,"Position":244.734863,"HyperDash":false},{"StartTime":97511.0,"Position":177.973221,"HyperDash":false}]},{"StartTime":97555.0,"Objects":[{"StartTime":97555.0,"Position":144.0,"HyperDash":false},{"StartTime":97641.0,"Position":112.369415,"HyperDash":false},{"StartTime":97727.0,"Position":88.02037,"HyperDash":false},{"StartTime":97813.0,"Position":103.779022,"HyperDash":false},{"StartTime":97899.0,"Position":143.185013,"HyperDash":false},{"StartTime":97985.0,"Position":200.21698,"HyperDash":false},{"StartTime":98072.0,"Position":207.316086,"HyperDash":false},{"StartTime":98140.0,"Position":203.132828,"HyperDash":false},{"StartTime":98244.0,"Position":161.018463,"HyperDash":false}]},{"StartTime":99279.0,"Objects":[{"StartTime":99279.0,"Position":164.0,"HyperDash":false}]},{"StartTime":99451.0,"Objects":[{"StartTime":99451.0,"Position":324.0,"HyperDash":false},{"StartTime":99494.0,"Position":333.138123,"HyperDash":false},{"StartTime":99537.0,"Position":324.0,"HyperDash":false},{"StartTime":99580.0,"Position":333.138123,"HyperDash":true}]},{"StartTime":99624.0,"Objects":[{"StartTime":99624.0,"Position":204.0,"HyperDash":false},{"StartTime":99692.0,"Position":148.6279,"HyperDash":false},{"StartTime":99796.0,"Position":69.0,"HyperDash":true}]},{"StartTime":99969.0,"Objects":[{"StartTime":99969.0,"Position":340.0,"HyperDash":false},{"StartTime":100037.0,"Position":303.6279,"HyperDash":false},{"StartTime":100141.0,"Position":205.0,"HyperDash":true}]},{"StartTime":100313.0,"Objects":[{"StartTime":100313.0,"Position":472.0,"HyperDash":true}]},{"StartTime":100658.0,"Objects":[{"StartTime":100658.0,"Position":64.0,"HyperDash":false},{"StartTime":100744.0,"Position":10.0,"HyperDash":false},{"StartTime":100830.0,"Position":64.0,"HyperDash":true}]},{"StartTime":101003.0,"Objects":[{"StartTime":101003.0,"Position":336.0,"HyperDash":false}]},{"StartTime":101175.0,"Objects":[{"StartTime":101175.0,"Position":176.0,"HyperDash":true}]},{"StartTime":101348.0,"Objects":[{"StartTime":101348.0,"Position":448.0,"HyperDash":false},{"StartTime":101416.0,"Position":502.697662,"HyperDash":false},{"StartTime":101520.0,"Position":444.0,"HyperDash":false}]},{"StartTime":101606.0,"Objects":[{"StartTime":101606.0,"Position":384.0,"HyperDash":true}]},{"StartTime":101693.0,"Objects":[{"StartTime":101693.0,"Position":220.0,"HyperDash":false},{"StartTime":101761.0,"Position":270.697662,"HyperDash":false},{"StartTime":101865.0,"Position":328.0,"HyperDash":false}]},{"StartTime":101951.0,"Objects":[{"StartTime":101951.0,"Position":264.0,"HyperDash":true}]},{"StartTime":102037.0,"Objects":[{"StartTime":102037.0,"Position":112.0,"HyperDash":false}]},{"StartTime":102124.0,"Objects":[{"StartTime":102124.0,"Position":56.0,"HyperDash":false}]},{"StartTime":102210.0,"Objects":[{"StartTime":102210.0,"Position":56.0,"HyperDash":true}]},{"StartTime":102382.0,"Objects":[{"StartTime":102382.0,"Position":344.0,"HyperDash":true}]},{"StartTime":102555.0,"Objects":[{"StartTime":102555.0,"Position":56.0,"HyperDash":true}]},{"StartTime":102727.0,"Objects":[{"StartTime":102727.0,"Position":368.0,"HyperDash":false},{"StartTime":102795.0,"Position":407.851135,"HyperDash":false},{"StartTime":102899.0,"Position":390.870453,"HyperDash":false}]},{"StartTime":102986.0,"Objects":[{"StartTime":102986.0,"Position":332.0,"HyperDash":true}]},{"StartTime":103072.0,"Objects":[{"StartTime":103072.0,"Position":168.0,"HyperDash":false},{"StartTime":103140.0,"Position":110.302322,"HyperDash":false},{"StartTime":103244.0,"Position":60.0,"HyperDash":false}]},{"StartTime":103331.0,"Objects":[{"StartTime":103331.0,"Position":120.0,"HyperDash":true}]},{"StartTime":103417.0,"Objects":[{"StartTime":103417.0,"Position":304.0,"HyperDash":false}]},{"StartTime":103503.0,"Objects":[{"StartTime":103503.0,"Position":364.0,"HyperDash":false}]},{"StartTime":103589.0,"Objects":[{"StartTime":103589.0,"Position":424.0,"HyperDash":true}]},{"StartTime":103762.0,"Objects":[{"StartTime":103762.0,"Position":152.0,"HyperDash":false}]},{"StartTime":103934.0,"Objects":[{"StartTime":103934.0,"Position":316.0,"HyperDash":true}]},{"StartTime":104106.0,"Objects":[{"StartTime":104106.0,"Position":56.0,"HyperDash":false},{"StartTime":104174.0,"Position":18.3023262,"HyperDash":false},{"StartTime":104278.0,"Position":59.9999962,"HyperDash":false}]},{"StartTime":104365.0,"Objects":[{"StartTime":104365.0,"Position":116.0,"HyperDash":true}]},{"StartTime":104451.0,"Objects":[{"StartTime":104451.0,"Position":304.0,"HyperDash":false},{"StartTime":104519.0,"Position":357.697662,"HyperDash":false},{"StartTime":104623.0,"Position":412.0,"HyperDash":false}]},{"StartTime":104710.0,"Objects":[{"StartTime":104710.0,"Position":356.0,"HyperDash":true}]},{"StartTime":104796.0,"Objects":[{"StartTime":104796.0,"Position":168.0,"HyperDash":false},{"StartTime":104882.0,"Position":114.0,"HyperDash":false},{"StartTime":104968.0,"Position":168.0,"HyperDash":true}]},{"StartTime":105141.0,"Objects":[{"StartTime":105141.0,"Position":440.0,"HyperDash":true}]},{"StartTime":105313.0,"Objects":[{"StartTime":105313.0,"Position":144.0,"HyperDash":true}]},{"StartTime":105486.0,"Objects":[{"StartTime":105486.0,"Position":468.0,"HyperDash":false},{"StartTime":105554.0,"Position":451.0,"HyperDash":false},{"StartTime":105658.0,"Position":412.0,"HyperDash":false}]},{"StartTime":105744.0,"Objects":[{"StartTime":105744.0,"Position":360.0,"HyperDash":true}]},{"StartTime":105831.0,"Objects":[{"StartTime":105831.0,"Position":164.0,"HyperDash":false},{"StartTime":105899.0,"Position":224.697678,"HyperDash":false},{"StartTime":106003.0,"Position":272.0,"HyperDash":false}]},{"StartTime":106089.0,"Objects":[{"StartTime":106089.0,"Position":212.0,"HyperDash":true}]},{"StartTime":106175.0,"Objects":[{"StartTime":106175.0,"Position":24.0,"HyperDash":false}]},{"StartTime":106262.0,"Objects":[{"StartTime":106262.0,"Position":20.0,"HyperDash":false}]},{"StartTime":106348.0,"Objects":[{"StartTime":106348.0,"Position":16.0,"HyperDash":true}]},{"StartTime":106520.0,"Objects":[{"StartTime":106520.0,"Position":296.0,"HyperDash":false}]},{"StartTime":106693.0,"Objects":[{"StartTime":106693.0,"Position":132.0,"HyperDash":true}]},{"StartTime":106865.0,"Objects":[{"StartTime":106865.0,"Position":400.0,"HyperDash":false},{"StartTime":106933.0,"Position":443.234375,"HyperDash":false},{"StartTime":107037.0,"Position":447.13623,"HyperDash":false}]},{"StartTime":107124.0,"Objects":[{"StartTime":107124.0,"Position":388.0,"HyperDash":true}]},{"StartTime":107210.0,"Objects":[{"StartTime":107210.0,"Position":196.0,"HyperDash":false},{"StartTime":107278.0,"Position":142.302322,"HyperDash":false},{"StartTime":107382.0,"Position":88.0,"HyperDash":false}]},{"StartTime":107469.0,"Objects":[{"StartTime":107469.0,"Position":148.0,"HyperDash":true}]},{"StartTime":107555.0,"Objects":[{"StartTime":107555.0,"Position":304.0,"HyperDash":false}]},{"StartTime":107641.0,"Objects":[{"StartTime":107641.0,"Position":358.0,"HyperDash":false}]},{"StartTime":107727.0,"Objects":[{"StartTime":107727.0,"Position":412.0,"HyperDash":true}]},{"StartTime":107900.0,"Objects":[{"StartTime":107900.0,"Position":136.0,"HyperDash":true}]},{"StartTime":108072.0,"Objects":[{"StartTime":108072.0,"Position":432.0,"HyperDash":true}]},{"StartTime":108244.0,"Objects":[{"StartTime":108244.0,"Position":160.0,"HyperDash":false},{"StartTime":108312.0,"Position":129.302322,"HyperDash":false},{"StartTime":108416.0,"Position":52.0,"HyperDash":false}]},{"StartTime":108503.0,"Objects":[{"StartTime":108503.0,"Position":112.0,"HyperDash":true}]},{"StartTime":108589.0,"Objects":[{"StartTime":108589.0,"Position":300.0,"HyperDash":false},{"StartTime":108657.0,"Position":249.302338,"HyperDash":false},{"StartTime":108761.0,"Position":192.0,"HyperDash":false}]},{"StartTime":108848.0,"Objects":[{"StartTime":108848.0,"Position":248.0,"HyperDash":true}]},{"StartTime":108934.0,"Objects":[{"StartTime":108934.0,"Position":436.0,"HyperDash":false},{"StartTime":109020.0,"Position":490.0,"HyperDash":false},{"StartTime":109106.0,"Position":436.0,"HyperDash":true}]},{"StartTime":109279.0,"Objects":[{"StartTime":109279.0,"Position":164.0,"HyperDash":false}]},{"StartTime":109451.0,"Objects":[{"StartTime":109451.0,"Position":324.0,"HyperDash":true}]},{"StartTime":109624.0,"Objects":[{"StartTime":109624.0,"Position":52.0,"HyperDash":false},{"StartTime":109692.0,"Position":35.4452477,"HyperDash":false},{"StartTime":109796.0,"Position":52.0779152,"HyperDash":false}]},{"StartTime":109882.0,"Objects":[{"StartTime":109882.0,"Position":112.0,"HyperDash":true}]},{"StartTime":109969.0,"Objects":[{"StartTime":109969.0,"Position":316.0,"HyperDash":false},{"StartTime":110037.0,"Position":270.302338,"HyperDash":false},{"StartTime":110141.0,"Position":208.0,"HyperDash":false}]},{"StartTime":110227.0,"Objects":[{"StartTime":110227.0,"Position":268.0,"HyperDash":true}]},{"StartTime":110313.0,"Objects":[{"StartTime":110313.0,"Position":456.0,"HyperDash":false},{"StartTime":110381.0,"Position":440.422455,"HyperDash":false},{"StartTime":110485.0,"Position":459.598,"HyperDash":false}]},{"StartTime":110658.0,"Objects":[{"StartTime":110658.0,"Position":292.0,"HyperDash":false},{"StartTime":110726.0,"Position":273.422455,"HyperDash":false},{"StartTime":110830.0,"Position":295.598,"HyperDash":true}]},{"StartTime":111003.0,"Objects":[{"StartTime":111003.0,"Position":32.0,"HyperDash":false}]},{"StartTime":111118.0,"Objects":[{"StartTime":111118.0,"Position":140.0,"HyperDash":false}]},{"StartTime":111233.0,"Objects":[{"StartTime":111233.0,"Position":248.0,"HyperDash":true}]},{"StartTime":111348.0,"Objects":[{"StartTime":111348.0,"Position":44.0,"HyperDash":false},{"StartTime":111405.0,"Position":62.84279,"HyperDash":false},{"StartTime":111462.0,"Position":116.0,"HyperDash":false},{"StartTime":111577.0,"Position":44.0,"HyperDash":true}]},{"StartTime":111693.0,"Objects":[{"StartTime":111693.0,"Position":320.0,"HyperDash":false}]},{"StartTime":111779.0,"Objects":[{"StartTime":111779.0,"Position":392.0,"HyperDash":false}]},{"StartTime":111865.0,"Objects":[{"StartTime":111865.0,"Position":464.0,"HyperDash":true}]},{"StartTime":112037.0,"Objects":[{"StartTime":112037.0,"Position":196.0,"HyperDash":false}]},{"StartTime":112210.0,"Objects":[{"StartTime":112210.0,"Position":364.0,"HyperDash":true}]},{"StartTime":112382.0,"Objects":[{"StartTime":112382.0,"Position":92.0,"HyperDash":false},{"StartTime":112450.0,"Position":150.697678,"HyperDash":false},{"StartTime":112554.0,"Position":200.0,"HyperDash":false}]},{"StartTime":112641.0,"Objects":[{"StartTime":112641.0,"Position":140.0,"HyperDash":true}]},{"StartTime":112727.0,"Objects":[{"StartTime":112727.0,"Position":356.0,"HyperDash":false},{"StartTime":112795.0,"Position":379.697662,"HyperDash":false},{"StartTime":112899.0,"Position":352.0,"HyperDash":false}]},{"StartTime":112986.0,"Objects":[{"StartTime":112986.0,"Position":292.0,"HyperDash":true}]},{"StartTime":113072.0,"Objects":[{"StartTime":113072.0,"Position":96.0,"HyperDash":false}]},{"StartTime":113158.0,"Objects":[{"StartTime":113158.0,"Position":36.0,"HyperDash":false}]},{"StartTime":113244.0,"Objects":[{"StartTime":113244.0,"Position":96.0,"HyperDash":true}]},{"StartTime":113417.0,"Objects":[{"StartTime":113417.0,"Position":368.0,"HyperDash":true}]},{"StartTime":113589.0,"Objects":[{"StartTime":113589.0,"Position":72.0,"HyperDash":true}]},{"StartTime":113762.0,"Objects":[{"StartTime":113762.0,"Position":364.0,"HyperDash":false},{"StartTime":113830.0,"Position":340.302338,"HyperDash":false},{"StartTime":113934.0,"Position":256.0,"HyperDash":false}]},{"StartTime":114020.0,"Objects":[{"StartTime":114020.0,"Position":316.0,"HyperDash":true}]},{"StartTime":114106.0,"Objects":[{"StartTime":114106.0,"Position":120.0,"HyperDash":false},{"StartTime":114174.0,"Position":143.697678,"HyperDash":false},{"StartTime":114278.0,"Position":228.0,"HyperDash":false}]},{"StartTime":114365.0,"Objects":[{"StartTime":114365.0,"Position":168.0,"HyperDash":true}]},{"StartTime":114451.0,"Objects":[{"StartTime":114451.0,"Position":384.0,"HyperDash":false}]},{"StartTime":114537.0,"Objects":[{"StartTime":114537.0,"Position":444.0,"HyperDash":false}]},{"StartTime":114624.0,"Objects":[{"StartTime":114624.0,"Position":444.0,"HyperDash":true}]},{"StartTime":114796.0,"Objects":[{"StartTime":114796.0,"Position":176.0,"HyperDash":false}]},{"StartTime":114969.0,"Objects":[{"StartTime":114969.0,"Position":344.0,"HyperDash":true}]},{"StartTime":115141.0,"Objects":[{"StartTime":115141.0,"Position":76.0,"HyperDash":false},{"StartTime":115209.0,"Position":33.3023262,"HyperDash":false},{"StartTime":115313.0,"Position":20.0,"HyperDash":false}]},{"StartTime":115400.0,"Objects":[{"StartTime":115400.0,"Position":80.0,"HyperDash":true}]},{"StartTime":115486.0,"Objects":[{"StartTime":115486.0,"Position":284.0,"HyperDash":false},{"StartTime":115554.0,"Position":236.302322,"HyperDash":false},{"StartTime":115658.0,"Position":176.0,"HyperDash":false}]},{"StartTime":115744.0,"Objects":[{"StartTime":115744.0,"Position":236.0,"HyperDash":true}]},{"StartTime":115831.0,"Objects":[{"StartTime":115831.0,"Position":28.0,"HyperDash":false},{"StartTime":115917.0,"Position":82.0,"HyperDash":false},{"StartTime":116003.0,"Position":28.0,"HyperDash":true}]},{"StartTime":116175.0,"Objects":[{"StartTime":116175.0,"Position":300.0,"HyperDash":false}]},{"StartTime":116348.0,"Objects":[{"StartTime":116348.0,"Position":132.0,"HyperDash":true}]},{"StartTime":116520.0,"Objects":[{"StartTime":116520.0,"Position":408.0,"HyperDash":false},{"StartTime":116588.0,"Position":351.302338,"HyperDash":false},{"StartTime":116692.0,"Position":300.0,"HyperDash":false}]},{"StartTime":116779.0,"Objects":[{"StartTime":116779.0,"Position":360.0,"HyperDash":true}]},{"StartTime":116865.0,"Objects":[{"StartTime":116865.0,"Position":156.0,"HyperDash":false},{"StartTime":116933.0,"Position":184.697678,"HyperDash":false},{"StartTime":117037.0,"Position":264.0,"HyperDash":false}]},{"StartTime":117124.0,"Objects":[{"StartTime":117124.0,"Position":204.0,"HyperDash":true}]},{"StartTime":117210.0,"Objects":[{"StartTime":117210.0,"Position":384.0,"HyperDash":false}]},{"StartTime":117296.0,"Objects":[{"StartTime":117296.0,"Position":444.0,"HyperDash":false}]},{"StartTime":117382.0,"Objects":[{"StartTime":117382.0,"Position":504.0,"HyperDash":true}]},{"StartTime":117555.0,"Objects":[{"StartTime":117555.0,"Position":228.0,"HyperDash":false},{"StartTime":117623.0,"Position":287.697662,"HyperDash":false},{"StartTime":117727.0,"Position":336.0,"HyperDash":true}]},{"StartTime":117900.0,"Objects":[{"StartTime":117900.0,"Position":60.0,"HyperDash":false},{"StartTime":117968.0,"Position":86.69768,"HyperDash":false},{"StartTime":118072.0,"Position":168.0,"HyperDash":false}]},{"StartTime":118158.0,"Objects":[{"StartTime":118158.0,"Position":108.0,"HyperDash":true}]},{"StartTime":118244.0,"Objects":[{"StartTime":118244.0,"Position":324.0,"HyperDash":false},{"StartTime":118312.0,"Position":384.697662,"HyperDash":false},{"StartTime":118416.0,"Position":380.0,"HyperDash":false}]},{"StartTime":118503.0,"Objects":[{"StartTime":118503.0,"Position":320.0,"HyperDash":true}]},{"StartTime":118589.0,"Objects":[{"StartTime":118589.0,"Position":132.0,"HyperDash":false}]},{"StartTime":118675.0,"Objects":[{"StartTime":118675.0,"Position":72.0,"HyperDash":false}]},{"StartTime":118762.0,"Objects":[{"StartTime":118762.0,"Position":132.0,"HyperDash":true}]},{"StartTime":118934.0,"Objects":[{"StartTime":118934.0,"Position":428.0,"HyperDash":true}]},{"StartTime":119106.0,"Objects":[{"StartTime":119106.0,"Position":80.0,"HyperDash":true}]},{"StartTime":119279.0,"Objects":[{"StartTime":119279.0,"Position":352.0,"HyperDash":false},{"StartTime":119347.0,"Position":288.6279,"HyperDash":false},{"StartTime":119451.0,"Position":217.0,"HyperDash":false}]},{"StartTime":119537.0,"Objects":[{"StartTime":119537.0,"Position":148.0,"HyperDash":true}]},{"StartTime":119624.0,"Objects":[{"StartTime":119624.0,"Position":388.0,"HyperDash":false},{"StartTime":119692.0,"Position":336.6279,"HyperDash":false},{"StartTime":119796.0,"Position":253.0,"HyperDash":false}]},{"StartTime":119882.0,"Objects":[{"StartTime":119882.0,"Position":320.0,"HyperDash":true}]},{"StartTime":119969.0,"Objects":[{"StartTime":119969.0,"Position":100.0,"HyperDash":false},{"StartTime":120055.0,"Position":46.0,"HyperDash":false},{"StartTime":120141.0,"Position":100.0,"HyperDash":true}]},{"StartTime":120313.0,"Objects":[{"StartTime":120313.0,"Position":384.0,"HyperDash":true}]},{"StartTime":120486.0,"Objects":[{"StartTime":120486.0,"Position":112.0,"HyperDash":true}]},{"StartTime":120658.0,"Objects":[{"StartTime":120658.0,"Position":408.0,"HyperDash":false},{"StartTime":120726.0,"Position":466.697662,"HyperDash":false},{"StartTime":120830.0,"Position":412.0,"HyperDash":false}]},{"StartTime":120917.0,"Objects":[{"StartTime":120917.0,"Position":348.0,"HyperDash":true}]},{"StartTime":121003.0,"Objects":[{"StartTime":121003.0,"Position":132.0,"HyperDash":false},{"StartTime":121071.0,"Position":77.837204,"HyperDash":false},{"StartTime":121175.0,"Position":127.999992,"HyperDash":false}]},{"StartTime":121262.0,"Objects":[{"StartTime":121262.0,"Position":196.0,"HyperDash":true}]},{"StartTime":121348.0,"Objects":[{"StartTime":121348.0,"Position":384.0,"HyperDash":false},{"StartTime":121434.0,"Position":387.368439,"HyperDash":true}]},{"StartTime":121520.0,"Objects":[{"StartTime":121520.0,"Position":188.0,"HyperDash":false},{"StartTime":121606.0,"Position":184.631577,"HyperDash":true}]},{"StartTime":121693.0,"Objects":[{"StartTime":121693.0,"Position":400.0,"HyperDash":false},{"StartTime":121779.0,"Position":346.0,"HyperDash":true}]},{"StartTime":121865.0,"Objects":[{"StartTime":121865.0,"Position":128.0,"HyperDash":false},{"StartTime":121951.0,"Position":124.407974,"HyperDash":true}]},{"StartTime":122037.0,"Objects":[{"StartTime":122037.0,"Position":336.0,"HyperDash":false},{"StartTime":122123.0,"Position":282.0,"HyperDash":true}]},{"StartTime":122210.0,"Objects":[{"StartTime":122210.0,"Position":484.0,"HyperDash":false},{"StartTime":122296.0,"Position":486.696625,"HyperDash":true}]},{"StartTime":122382.0,"Objects":[{"StartTime":122382.0,"Position":272.0,"HyperDash":false},{"StartTime":122468.0,"Position":326.0,"HyperDash":true}]},{"StartTime":122555.0,"Objects":[{"StartTime":122555.0,"Position":108.0,"HyperDash":false},{"StartTime":122641.0,"Position":54.0,"HyperDash":true}]},{"StartTime":122727.0,"Objects":[{"StartTime":122727.0,"Position":280.0,"HyperDash":false}]},{"StartTime":122813.0,"Objects":[{"StartTime":122813.0,"Position":347.0,"HyperDash":false}]},{"StartTime":122900.0,"Objects":[{"StartTime":122900.0,"Position":415.0,"HyperDash":false}]},{"StartTime":123072.0,"Objects":[{"StartTime":123072.0,"Position":256.0,"HyperDash":false}]},{"StartTime":123158.0,"Objects":[{"StartTime":123158.0,"Position":308.0,"HyperDash":false}]},{"StartTime":123244.0,"Objects":[{"StartTime":123244.0,"Position":360.0,"HyperDash":false}]},{"StartTime":123417.0,"Objects":[{"StartTime":123417.0,"Position":228.0,"HyperDash":false}]},{"StartTime":123503.0,"Objects":[{"StartTime":123503.0,"Position":260.0,"HyperDash":false}]},{"StartTime":123589.0,"Objects":[{"StartTime":123589.0,"Position":292.0,"HyperDash":false}]},{"StartTime":123762.0,"Objects":[{"StartTime":123762.0,"Position":188.0,"HyperDash":false}]},{"StartTime":123848.0,"Objects":[{"StartTime":123848.0,"Position":196.0,"HyperDash":false}]},{"StartTime":123934.0,"Objects":[{"StartTime":123934.0,"Position":204.0,"HyperDash":false}]},{"StartTime":124106.0,"Objects":[{"StartTime":124106.0,"Position":311.0,"HyperDash":false},{"StartTime":124170.0,"Position":216.0,"HyperDash":false},{"StartTime":124235.0,"Position":310.0,"HyperDash":false},{"StartTime":124299.0,"Position":397.0,"HyperDash":false},{"StartTime":124364.0,"Position":214.0,"HyperDash":false},{"StartTime":124429.0,"Position":505.0,"HyperDash":false},{"StartTime":124493.0,"Position":173.0,"HyperDash":false},{"StartTime":124558.0,"Position":295.0,"HyperDash":false},{"StartTime":124623.0,"Position":199.0,"HyperDash":false},{"StartTime":124687.0,"Position":494.0,"HyperDash":false},{"StartTime":124752.0,"Position":293.0,"HyperDash":false},{"StartTime":124817.0,"Position":115.0,"HyperDash":false},{"StartTime":124881.0,"Position":412.0,"HyperDash":false},{"StartTime":124946.0,"Position":506.0,"HyperDash":false},{"StartTime":125011.0,"Position":293.0,"HyperDash":false},{"StartTime":125075.0,"Position":346.0,"HyperDash":false},{"StartTime":125140.0,"Position":117.0,"HyperDash":false},{"StartTime":125205.0,"Position":285.0,"HyperDash":false},{"StartTime":125269.0,"Position":17.0,"HyperDash":false},{"StartTime":125334.0,"Position":238.0,"HyperDash":false},{"StartTime":125399.0,"Position":222.0,"HyperDash":false},{"StartTime":125463.0,"Position":450.0,"HyperDash":false},{"StartTime":125528.0,"Position":67.0,"HyperDash":false},{"StartTime":125593.0,"Position":219.0,"HyperDash":false},{"StartTime":125657.0,"Position":307.0,"HyperDash":false},{"StartTime":125722.0,"Position":367.0,"HyperDash":false},{"StartTime":125787.0,"Position":412.0,"HyperDash":false},{"StartTime":125851.0,"Position":413.0,"HyperDash":false},{"StartTime":125916.0,"Position":143.0,"HyperDash":false},{"StartTime":125981.0,"Position":339.0,"HyperDash":false},{"StartTime":126045.0,"Position":342.0,"HyperDash":false},{"StartTime":126110.0,"Position":249.0,"HyperDash":false},{"StartTime":126175.0,"Position":235.0,"HyperDash":false},{"StartTime":126239.0,"Position":323.0,"HyperDash":false},{"StartTime":126304.0,"Position":365.0,"HyperDash":false},{"StartTime":126368.0,"Position":74.0,"HyperDash":false},{"StartTime":126433.0,"Position":281.0,"HyperDash":false},{"StartTime":126498.0,"Position":398.0,"HyperDash":false},{"StartTime":126562.0,"Position":335.0,"HyperDash":false},{"StartTime":126627.0,"Position":388.0,"HyperDash":false},{"StartTime":126692.0,"Position":228.0,"HyperDash":false},{"StartTime":126756.0,"Position":323.0,"HyperDash":false},{"StartTime":126821.0,"Position":441.0,"HyperDash":false},{"StartTime":126886.0,"Position":442.0,"HyperDash":false},{"StartTime":126950.0,"Position":278.0,"HyperDash":false},{"StartTime":127015.0,"Position":90.0,"HyperDash":false},{"StartTime":127080.0,"Position":409.0,"HyperDash":false},{"StartTime":127144.0,"Position":377.0,"HyperDash":false},{"StartTime":127209.0,"Position":457.0,"HyperDash":false},{"StartTime":127274.0,"Position":409.0,"HyperDash":false},{"StartTime":127338.0,"Position":43.0,"HyperDash":false},{"StartTime":127403.0,"Position":162.0,"HyperDash":false},{"StartTime":127468.0,"Position":341.0,"HyperDash":false},{"StartTime":127532.0,"Position":72.0,"HyperDash":false},{"StartTime":127597.0,"Position":135.0,"HyperDash":false},{"StartTime":127662.0,"Position":252.0,"HyperDash":false},{"StartTime":127726.0,"Position":446.0,"HyperDash":false},{"StartTime":127791.0,"Position":284.0,"HyperDash":false},{"StartTime":127856.0,"Position":70.0,"HyperDash":false},{"StartTime":127920.0,"Position":494.0,"HyperDash":false},{"StartTime":127985.0,"Position":463.0,"HyperDash":false},{"StartTime":128050.0,"Position":277.0,"HyperDash":false},{"StartTime":128114.0,"Position":425.0,"HyperDash":false},{"StartTime":128179.0,"Position":281.0,"HyperDash":false},{"StartTime":128244.0,"Position":3.0,"HyperDash":false},{"StartTime":128308.0,"Position":346.0,"HyperDash":false},{"StartTime":128373.0,"Position":350.0,"HyperDash":false},{"StartTime":128437.0,"Position":217.0,"HyperDash":false},{"StartTime":128502.0,"Position":455.0,"HyperDash":false},{"StartTime":128567.0,"Position":229.0,"HyperDash":false},{"StartTime":128631.0,"Position":51.0,"HyperDash":false},{"StartTime":128696.0,"Position":199.0,"HyperDash":false},{"StartTime":128761.0,"Position":208.0,"HyperDash":false},{"StartTime":128825.0,"Position":173.0,"HyperDash":false},{"StartTime":128890.0,"Position":367.0,"HyperDash":false},{"StartTime":128955.0,"Position":193.0,"HyperDash":false},{"StartTime":129019.0,"Position":488.0,"HyperDash":false},{"StartTime":129084.0,"Position":314.0,"HyperDash":false},{"StartTime":129149.0,"Position":135.0,"HyperDash":false},{"StartTime":129213.0,"Position":399.0,"HyperDash":false},{"StartTime":129278.0,"Position":404.0,"HyperDash":false},{"StartTime":129343.0,"Position":152.0,"HyperDash":false},{"StartTime":129407.0,"Position":353.0,"HyperDash":false},{"StartTime":129472.0,"Position":358.0,"HyperDash":false},{"StartTime":129537.0,"Position":447.0,"HyperDash":false},{"StartTime":129601.0,"Position":222.0,"HyperDash":false},{"StartTime":129666.0,"Position":382.0,"HyperDash":false},{"StartTime":129731.0,"Position":433.0,"HyperDash":false},{"StartTime":129795.0,"Position":450.0,"HyperDash":false},{"StartTime":129860.0,"Position":326.0,"HyperDash":false},{"StartTime":129925.0,"Position":414.0,"HyperDash":false},{"StartTime":129989.0,"Position":285.0,"HyperDash":false},{"StartTime":130054.0,"Position":336.0,"HyperDash":false},{"StartTime":130119.0,"Position":509.0,"HyperDash":false},{"StartTime":130183.0,"Position":334.0,"HyperDash":false},{"StartTime":130248.0,"Position":72.0,"HyperDash":false},{"StartTime":130313.0,"Position":425.0,"HyperDash":false},{"StartTime":130377.0,"Position":451.0,"HyperDash":false},{"StartTime":130442.0,"Position":220.0,"HyperDash":false},{"StartTime":130506.0,"Position":25.0,"HyperDash":false},{"StartTime":130571.0,"Position":77.0,"HyperDash":false},{"StartTime":130636.0,"Position":509.0,"HyperDash":false},{"StartTime":130700.0,"Position":90.0,"HyperDash":false},{"StartTime":130765.0,"Position":118.0,"HyperDash":false},{"StartTime":130830.0,"Position":58.0,"HyperDash":false},{"StartTime":130894.0,"Position":12.0,"HyperDash":false},{"StartTime":130959.0,"Position":215.0,"HyperDash":false},{"StartTime":131024.0,"Position":487.0,"HyperDash":false},{"StartTime":131088.0,"Position":446.0,"HyperDash":false},{"StartTime":131153.0,"Position":491.0,"HyperDash":false},{"StartTime":131218.0,"Position":459.0,"HyperDash":false},{"StartTime":131282.0,"Position":37.0,"HyperDash":false},{"StartTime":131347.0,"Position":291.0,"HyperDash":false},{"StartTime":131412.0,"Position":315.0,"HyperDash":false},{"StartTime":131476.0,"Position":35.0,"HyperDash":false},{"StartTime":131541.0,"Position":208.0,"HyperDash":false},{"StartTime":131606.0,"Position":504.0,"HyperDash":false},{"StartTime":131670.0,"Position":296.0,"HyperDash":false},{"StartTime":131735.0,"Position":105.0,"HyperDash":false},{"StartTime":131800.0,"Position":488.0,"HyperDash":false},{"StartTime":131864.0,"Position":230.0,"HyperDash":false},{"StartTime":131929.0,"Position":446.0,"HyperDash":false},{"StartTime":131994.0,"Position":241.0,"HyperDash":false},{"StartTime":132058.0,"Position":413.0,"HyperDash":false},{"StartTime":132123.0,"Position":357.0,"HyperDash":false},{"StartTime":132188.0,"Position":256.0,"HyperDash":false},{"StartTime":132252.0,"Position":192.0,"HyperDash":false},{"StartTime":132317.0,"Position":116.0,"HyperDash":false},{"StartTime":132382.0,"Position":397.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2781126.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2781126.osu new file mode 100644 index 0000000000..af7cd296d7 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/2781126.osu @@ -0,0 +1,908 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 2 + +[Difficulty] +HPDrainRate:6 +CircleSize:4.5 +OverallDifficulty:9.5 +ApproachRate:9.5 +SliderMultiplier:1.8 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +-31,344.827586206897,4,2,1,15,1,0 +486,-100,4,2,1,50,0,0 +658,-100,4,2,1,55,0,0 +831,-100,4,2,1,60,0,0 +1003,-100,4,2,1,65,0,0 +1175,-100,4,2,1,5,0,0 +1348,-100,4,2,1,80,0,0 +11089,-100,4,2,2,40,0,0 +11175,-100,4,2,2,45,0,0 +11262,-100,4,2,2,50,0,0 +11348,-100,4,2,2,55,0,0 +11434,-100,4,2,2,60,0,0 +11520,-100,4,2,2,65,0,0 +11606,-100,4,2,2,70,0,0 +11693,-100,4,2,1,75,0,0 +11865,-100,4,2,1,70,0,0 +12037,-80,4,2,1,75,0,0 +12296,-100,4,2,1,100,0,0 +12382,-100,4,2,1,85,0,0 +20658,-83.3333333333333,4,2,1,85,0,0 +21175,-100,4,2,1,85,0,0 +22037,-100,4,2,1,80,0,0 +22210,-100,4,2,2,50,0,0 +22727,-100,4,2,1,80,0,0 +23072,-66.6666666666667,4,2,1,100,0,0 +23331,-100,4,2,1,100,0,0 +23417,-80,4,2,1,75,0,0 +23762,-100,4,2,1,100,0,0 +34451,-66.6666666666667,4,2,1,80,0,0 +34624,-100,4,2,1,80,0,0 +39969,-80,4,2,1,80,0,0 +40658,-80,4,2,1,100,0,0 +41348,-66.6666666666667,4,2,1,100,0,0 +42037,-100,4,2,1,100,0,0 +44106,-66.6666666666667,4,2,1,70,0,0 +44279,-100,4,2,1,100,0,0 +44796,-66.6666666666667,4,2,1,100,0,0 +46175,-66.6666666666667,4,2,1,90,0,1 +46348,-100,4,2,1,90,0,1 +51348,-33.3333333333333,4,2,1,90,0,1 +51520,-66.6666666666667,4,2,1,90,0,1 +51693,-100,4,2,1,100,0,1 +52037,-66.6666666666667,4,2,1,100,0,1 +52727,-100,4,2,1,100,0,1 +53072,-40,4,2,1,100,0,1 +53762,-100,4,2,1,100,0,1 +55141,-66.6666666666667,4,2,1,100,0,1 +55371,-100,4,2,1,100,0,1 +56520,-66.6666666666667,4,2,1,90,0,0 +56606,-66.6666666666667,4,2,2,47,0,0 +56693,-66.6666666666667,4,2,1,54,0,0 +56779,-66.6666666666667,4,2,2,61,0,0 +56865,-66.6666666666667,4,2,1,68,0,0 +56951,-66.6666666666667,4,2,2,75,0,0 +57037,-66.6666666666667,4,2,1,81,0,0 +57124,-66.6666666666667,4,2,2,88,0,0 +57210,-100,4,2,1,90,0,0 +57900,-66.6666666666667,4,2,1,90,0,1 +58072,-100,4,2,1,90,0,1 +58244,-80,4,2,1,90,0,1 +58589,-100,4,2,1,90,0,1 +61175,-66.6666666666667,4,2,1,90,0,1 +61348,-100,4,2,1,90,0,1 +62382,-33.3333333333333,4,2,1,100,0,1 +62555,-66.6666666666667,4,2,1,100,0,1 +62770,-100,4,2,1,100,0,1 +64106,-40,4,2,1,100,0,1 +64796,-100,4,2,1,100,0,1 +66175,-100,4,2,1,80,0,0 +68417,-66.6666666666667,4,2,1,80,0,0 +68589,-100,4,2,1,80,0,0 +68934,-100,4,2,1,70,0,1 +69020,-100,4,2,1,10,0,0 +71003,-100,4,2,1,15,0,0 +71693,-100,4,2,1,20,0,0 +71865,-100,4,2,1,23,0,0 +72037,-100,4,2,1,26,0,0 +72210,-100,4,2,1,29,0,0 +72382,-100,4,2,1,32,0,0 +72555,-100,4,2,1,35,0,0 +72727,-100,4,2,1,38,0,0 +72900,-100,4,2,1,41,0,0 +73072,-100,4,2,1,44,0,0 +73244,-100,4,2,1,47,0,0 +73417,-100,4,2,1,50,0,0 +73589,-100,4,2,1,53,0,0 +73762,-100,4,2,1,56,0,0 +73934,-100,4,2,1,59,0,0 +74106,-100,4,2,1,62,0,0 +74279,-100,4,2,1,65,0,0 +74451,-100,4,2,1,40,0,0 +74624,-133.333333333333,4,2,1,40,0,0 +77210,-100,4,2,1,40,0,0 +77555,-133.333333333333,4,2,1,40,0,0 +79969,-100,4,2,1,40,0,0 +80313,-133.333333333333,4,2,1,40,0,0 +81348,-100,4,2,1,40,0,0 +81692,-133.333333333333,4,2,1,40,0,0 +82727,-86.9565217391304,4,2,1,40,0,0 +83072,-133.333333333333,4,2,1,40,0,0 +84106,-100,4,2,1,40,0,0 +84450,-133.333333333333,4,2,1,40,0,0 +85486,-100,4,2,2,50,0,0 +96175,-50,4,2,2,60,0,0 +96520,-50,4,2,1,55,0,0 +96822,-50,4,2,1,5,0,0 +96865,-66.6666666666667,4,2,1,50,0,0 +97210,-66.6666666666667,4,2,1,45,0,0 +97512,-66.6666666666667,4,2,1,5,0,0 +97555,-100,4,2,1,40,0,0 +97900,-100,4,2,1,35,0,0 +98244,-100,4,2,1,5,0,0 +99279,-100,4,2,1,75,0,0 +99624,-66.6666666666667,4,2,1,75,0,0 +99796,-66.6666666666667,4,2,1,75,0,0 +100313,-100,4,2,1,75,0,0 +100658,-83.3333333333333,4,2,1,90,0,1 +111578,-83.3333333333333,4,2,1,90,0,0 +111693,-83.3333333333333,4,2,1,90,0,1 +119279,-66.6666666666667,4,2,1,90,0,1 +119969,-83.3333333333333,4,2,1,90,0,1 +121003,-50,4,2,1,90,0,1 +121175,-83.3333333333333,4,2,1,90,0,1 +122727,-100,4,2,1,90,0,0 +123072,-100,4,2,1,50,0,0 +123417,-100,4,2,1,46,0,0 +123762,-100,4,2,1,42,0,0 +124106,-100,4,2,1,38,0,0 +124451,-100,4,2,1,35,0,0 +124796,-100,4,2,1,32,0,0 +125141,-100,4,2,1,29,0,0 +125486,-100,4,2,1,26,0,0 +125831,-100,4,2,1,24,0,0 +126175,-100,4,2,1,22,0,0 +126520,-100,4,2,1,20,0,0 +126865,-100,4,2,1,18,0,0 +127210,-100,4,2,1,16,0,0 +127555,-100,4,2,1,14,0,0 +127900,-100,4,2,1,12,0,0 +128244,-100,4,2,1,10,0,0 +128934,-100,4,2,1,9,0,0 +129624,-100,4,2,1,8,0,0 +130313,-100,4,2,1,7,0,0 +131003,-100,4,2,1,6,0,0 +131693,-100,4,2,1,5,0,0 + +[HitObjects] +256,192,313,12,2,1175,0:0:0:0: +224,192,1348,5,6,3:2:0:0: +177,157,1434,1,0,0:0:0:0: +179,100,1520,1,2,0:2:0:0: +227,68,1606,1,2,0:2:0:0: +292,68,1693,2,0,L|296:12,1,45,2|0,0:0|0:0,0:0:0:0: +116,192,1865,2,0,B|116:280|116:280|208:296,1,180,0|2,3:0|0:1,0:0:0:0: +116,280,2296,1,0,0:0:0:0: +26,264,2382,2,0,L|22:160,1,90,0|2,3:2|0:3,0:0:0:0: +292,192,2727,6,0,L|384:192,1,90,2|2,3:2|0:3,0:0:0:0: +328,192,2986,1,2,0:3:0:0: +276,192,3072,1,2,0:0:0:0: +448,192,3244,1,0,3:0:0:0: +268,96,3417,2,0,B|176:96,1,90,0|2,0:0|0:3,0:0:0:0: +244,96,3675,1,2,0:3:0:0: +178,96,3762,2,0,L|82:96,1,90,0|2,3:0|0:3,0:0:0:0: +444,304,4106,6,0,L|448:256,1,45,2|2,3:2|0:2,0:0:0:0: +376,256,4279,2,0,L|372:208,1,45,2|2,0:2|0:2,0:0:0:0: +300,192,4451,1,2,0:0:0:0: +472,136,4624,2,0,L|476:84,1,45,0|0,3:0|0:0,0:0:0:0: +296,28,4796,2,0,P|264:72|280:108,1,90,0|2,0:0|0:1,0:0:0:0: +366,152,5055,1,0,0:0:0:0: +456,211,5141,2,0,L|352:211,1,90,0|2,3:2|0:3,0:0:0:0: +112,192,5486,6,0,L|208:192,1,90,2|2,3:2|0:3,0:0:0:0: +268,192,5744,1,2,0:3:0:0: +202,192,5831,1,2,0:0:0:0: +360,192,6003,1,0,3:0:0:0: +192,284,6175,2,0,L|100:284,1,90,0|2,0:0|0:3,0:0:0:0: +172,284,6434,1,2,0:3:0:0: +102,284,6520,2,0,L|10:284,1,90,0|2,3:0|0:3,0:0:0:0: +288,284,6865,5,2,3:2:0:0: +335,249,6951,1,2,0:2:0:0: +333,192,7037,1,2,0:2:0:0: +285,160,7124,1,2,0:2:0:0: +220,160,7210,2,0,L|216:104,1,45,2|0,0:0|0:0,0:0:0:0: +320,56,7382,1,0,3:0:0:0: +204,56,7555,1,0,0:0:0:0: +456,52,7727,1,2,0:1:0:0: +460,104,7813,1,0,0:0:0:0: +464,160,7900,2,0,L|372:160,1,90,0|2,3:2|0:3,0:0:0:0: +120,160,8244,6,0,B|212:160,1,90,2|2,3:2|0:3,0:0:0:0: +280,160,8503,1,2,0:3:0:0: +348,160,8589,1,2,0:0:0:0: +176,160,8762,1,0,3:0:0:0: +354,160,8934,2,0,L|446:160,1,90,0|2,0:0|0:3,0:0:0:0: +374,160,9193,1,2,0:3:0:0: +306,160,9279,2,0,L|406:160,1,90,0|2,3:0|0:3,0:0:0:0: +148,56,9624,6,0,L|100:44,1,45,2|2,3:2|0:2,0:0:0:0: +176,120,9796,2,0,L|224:108,1,45,2|2,0:2|0:2,0:0:0:0: +148,56,9969,1,2,0:0:0:0: +308,56,10141,1,0,3:0:0:0: +140,120,10313,1,0,0:0:0:0: +396,192,10486,2,0,L|440:192,2,45,2|0|0,0:1|0:0|3:2,0:0:0:0: +228,192,10831,1,2,0:3:0:0: +460,312,11003,6,0,L|484:270,1,45,0|2,3:3|0:3,0:0:0:0: +392,288,11175,2,0,L|416:246,1,45,2|2,0:3|0:3,0:0:0:0: +324,264,11348,2,0,L|347:222,1,45,2|2,0:3|0:3,0:0:0:0: +260,232,11520,2,0,L|284:190,1,45,2|2,0:3|0:3,0:0:0:0: +384,192,11693,1,0,3:0:0:0: +220,188,11865,2,0,L|156:188,1,45,8|0,0:2|3:0,0:0:0:0: +400,192,12037,2,0,B|488:192|488:192|488:108,1,168.75,8|0,3:2|0:0,0:0:0:0: +284,56,12382,6,0,L|192:56,1,90,6|2,3:2|0:2,0:0:0:0: +264,56,12641,1,2,0:2:0:0: +436,56,12727,1,10,3:2:0:0: +328,56,12900,2,0,L|324:112,1,45,0|0,3:0|0:0,3:3:0:0: +424,112,13072,2,0,L|428:216,1,90,0|2,0:0|0:1,0:0:0:0: +360,200,13331,1,2,0:3:0:0: +208,200,13417,2,0,L|116:200,1,90,8|2,3:2|0:3,0:0:0:0: +292,200,13762,6,0,L|296:292,1,90,2|2,3:2|0:3,0:0:0:0: +228,292,14020,1,2,0:3:0:0: +408,288,14106,2,0,L|508:288,1,90,10|0,3:2|3:0,0:0:0:0: +228,192,14451,2,0,L|324:192,1,90,8|2,3:2|3:3,0:0:0:0: +48,192,14796,2,0,L|140:192,1,90,8|2,3:2|0:3,0:0:0:0: +392,192,15141,6,0,L|396:132,1,45,2|2,3:2|0:2,0:0:0:0: +320,120,15313,2,0,L|316:60,1,45,2|2,0:2|0:2,0:0:0:0: +488,60,15486,1,10,3:2:0:0: +388,60,15658,2,0,L|332:60,1,45,0|0,3:0|0:0,3:3:0:0: +240,60,15831,2,0,L|236:152,1,90,0|2,0:0|0:1,0:0:0:0: +304,152,16089,1,2,0:3:0:0: +132,152,16175,2,0,L|36:152,1,90,8|2,3:2|0:3,0:0:0:0: +312,256,16520,6,0,L|216:256,1,90,10|2,3:2|0:3,0:0:0:0: +152,256,16779,1,2,0:3:0:0: +328,328,16865,2,0,L|236:328,1,90,10|0,3:2|3:0,0:0:0:0: +328,328,17210,1,0,0:0:0:0: +164,328,17382,2,0,L|160:276,1,45,2|2,0:3|0:3,0:0:0:0: +336,240,17555,2,0,L|440:240,1,90,8|2,3:2|0:3,0:0:0:0: +152,56,17900,5,10,3:2:0:0: +155,114,17986,1,2,0:2:0:0: +192,160,18072,1,2,0:2:0:0: +252,168,18158,1,2,0:2:0:0: +404,168,18244,2,0,L|408:72,1,90,10|2,0:2|3:2,0:0:0:0: +156,232,18589,2,0,L|64:232,1,90,8|2,0:0|0:3,0:0:0:0: +136,232,18848,1,2,0:3:0:0: +304,232,18934,2,0,L|396:232,1,90,8|0,3:2|0:3,0:0:0:0: +120,76,19279,6,0,P|100:120|120:168,1,90,8|0,3:2|0:0,0:0:0:0: +180,160,19537,1,0,0:0:0:0: +360,160,19624,2,0,L|268:160,1,90,8|0,0:0|3:0,0:0:0:0: +32,316,19969,2,0,L|132:316,1,90,8|2,3:2|0:3,0:0:0:0: +188,316,20227,1,0,0:0:0:0: +16,232,20313,2,0,L|116:232,1,90,8|2,3:2|0:3,0:0:0:0: +368,232,20658,6,0,L|256:232,3,107.999996704102,10|10|10|8,3:2|0:2|3:2|3:2,0:0:0:0: +496,232,21348,1,8,0:0:0:0: +324,232,21520,1,8,3:2:0:0: +496,232,21693,1,8,3:2:0:0: +388,232,21865,2,0,L|332:232,1,45,8|8,3:2|3:2,0:0:0:0: +144,232,22037,5,2,3:2:0:0: +252,232,22210,2,0,L|232:192,1,45,2|0,3:0|0:0,0:0:0:0: +312,164,22382,2,0,L|292:124,1,45,2|0,3:0|0:0,0:0:0:0: +372,96,22555,2,0,L|352:56,1,45,2|0,3:0|0:0,0:0:0:0: +180,56,22727,2,0,L|276:56,1,90,2|8,3:2|0:0,0:0:0:0: +208,56,22986,1,0,3:0:0:0: +436,56,23072,2,0,P|504:104|436:168,1,202.500007724762,8|0,3:2|0:0,0:0:0:0: +208,192,23417,6,0,L|92:192,2,112.5,6|2|2,3:2|0:0|0:0,0:0:0:0: +312,192,23934,1,2,0:0:0:0: +220,192,24020,1,2,0:0:0:0: +128,192,24106,2,0,L|220:192,1,90,2|2,0:0|0:1,0:0:0:0: +392,192,24451,1,0,3:0:0:0: +444,176,24537,1,2,0:0:0:0: +444,120,24624,1,2,0:0:0:0: +392,100,24710,1,2,0:0:0:0: +212,276,24796,6,0,L|308:276,2,90,2|2|2,0:2|0:2|0:2,0:0:0:0: +320,276,25313,1,2,0:2:0:0: +384,276,25400,1,2,0:2:0:0: +284,352,25486,2,0,L|192:352,1,90,2|2,0:2|0:0,0:0:0:0: +448,276,25831,2,0,L|444:224,1,45,2|2,3:2|0:2,0:0:0:0: +344,192,26003,2,0,L|300:192,1,45,2|2,0:2|0:2,0:0:0:0: +128,192,26175,6,0,L|28:192,2,90,2|2|2,3:2|0:0|0:0,0:0:0:0: +236,192,26693,1,2,0:0:0:0: +299,192,26779,1,2,0:0:0:0: +362,192,26865,1,2,0:0:0:0: +196,192,27037,1,2,0:1:0:0: +352,192,27210,1,2,3:2:0:0: +352,128,27296,1,2,0:2:0:0: +312,80,27382,1,2,0:2:0:0: +248,80,27469,1,2,0:2:0:0: +412,80,27555,6,0,L|320:80,2,90,2|2|2,0:2|0:0|0:0,0:0:0:0: +304,80,28072,1,2,0:0:0:0: +396,80,28158,1,2,0:0:0:0: +488,80,28244,2,0,L|396:80,1,90,2|2,0:0|0:0,0:0:0:0: +88,80,28589,1,8,3:2:0:0: +340,80,28934,6,0,L|344:172,1,90,2|2,3:2|0:2,0:0:0:0: +172,192,29279,2,0,L|168:292,1,90,2|2,3:2|0:2,0:0:0:0: +268,284,29537,1,2,0:2:0:0: +368,284,29624,2,0,L|268:284,1,90,2|2,3:2|0:1,0:0:0:0: +452,284,29969,2,0,L|460:236,2,45,0|0|2,3:2|0:0|0:2,0:0:0:0: +200,372,30313,6,0,L|196:280,1,90,2|2,3:2|0:2,0:0:0:0: +368,160,30658,2,0,L|264:160,1,90,2|2,3:2|0:2,0:0:0:0: +380,160,30917,1,2,0:2:0:0: +480,160,31003,2,0,L|374:160,1,90,2|2,3:2|0:2,0:0:0:0: +128,192,31348,2,0,L|124:140,1,45,2|2,3:2|0:2,0:0:0:0: +228,104,31520,2,0,L|292:104,1,45,2|2,0:2|0:2,0:0:0:0: +88,148,31693,6,0,L|84:252,1,90,2|2,3:2|0:2,0:0:0:0: +256,236,32037,2,0,L|352:236,1,90,2|2,3:2|0:2,0:0:0:0: +246,236,32296,1,2,0:2:0:0: +148,236,32382,2,0,L|48:236,1,90,2|2,3:2|0:2,0:0:0:0: +232,68,32727,1,0,3:2:0:0: +180,68,32813,1,0,0:0:0:0: +124,68,32900,1,2,0:2:0:0: +376,68,33072,6,0,L|476:68,1,90,2|2,3:2|0:2,0:0:0:0: +300,192,33417,2,0,L|396:192,1,90,2|2,3:2|0:2,0:0:0:0: +220,192,33762,2,0,L|128:192,1,90,2|2,3:2|0:2,0:0:0:0: +416,192,34106,2,0,L|448:192,7,22.5,0|0|0|0|0|0|0|0,3:0|3:0|3:0|3:0|3:0|3:0|3:0|0:0,0:0:0:0: +265,192,34451,6,0,L|129:192,1,135.000005149842,10|2,3:2|0:2,0:0:0:0: +300,192,34796,2,0,L|304:97,1,90,2|2,3:2|0:2,0:0:0:0: +140,100,35141,1,10,3:2:0:0: +376,100,35313,1,2,0:1:0:0: +268,100,35486,2,0,L|264:196,1,90,0|2,3:2|0:2,0:0:0:0: +496,192,35831,6,0,L|404:192,1,90,10|2,3:2|0:2,0:0:0:0: +236,192,36175,2,0,L|140:192,1,90,2|2,3:2|0:2,0:0:0:0: +400,256,36520,1,10,3:2:0:0: +236,256,36693,1,2,0:2:0:0: +476,256,36865,1,2,3:2:0:0: +476,322,36951,1,0,0:0:0:0: +434,372,37037,1,2,0:2:0:0: +369,383,37124,1,0,0:0:0:0: +196,384,37210,6,0,L|104:384,1,90,10|2,3:2|0:2,0:0:0:0: +272,384,37555,2,0,L|368:384,1,90,2|2,3:2|0:2,0:0:0:0: +196,384,37900,1,10,3:2:0:0: +432,384,38072,1,2,0:1:0:0: +324,384,38244,1,0,3:2:0:0: +272,384,38331,1,0,0:0:0:0: +224,384,38417,1,2,0:2:0:0: +488,384,38589,6,0,L|490:281,1,90,10|2,3:2|0:2,0:0:0:0: +324,296,38934,2,0,L|328:188,1,90,2|2,3:2|0:2,0:0:0:0: +88,204,39279,1,10,3:2:0:0: +256,204,39451,1,2,0:2:0:0: +16,204,39624,1,10,3:2:0:0: +428,208,39969,6,0,P|480:152|428:92,1,168.75,8|2,3:2|0:0,0:0:0:0: +328,92,40313,2,0,P|256:120|240:204,1,168.75,10|0,3:2|0:0,0:0:0:0: +412,208,40658,2,0,B|496:208|496:208|500:296,1,168.75,8|0,3:2|0:0,0:0:0:0: +272,376,41003,2,0,B|272:292|272:292|360:288,1,168.75,8|0,3:2|0:0,0:0:0:0: +116,296,41348,6,0,P|52:224|120:176,1,202.500007724762,8|0,3:2|0:0,0:0:0:0: +340,176,41693,2,0,L|132:176,1,202.500007724762,8|0,3:2|0:0,0:0:0:0: +312,96,42037,1,8,3:2:0:0: +164,96,42210,1,8,3:2:0:0: +324,96,42382,1,8,3:2:0:0: +152,96,42555,1,8,3:2:0:0: +404,96,42727,5,8,3:2:0:0: +460,128,42813,1,8,3:2:0:0: +460,192,42900,1,8,3:2:0:0: +404,224,42986,1,8,3:2:0:0: +208,192,43072,2,0,L|204:244,1,45,8|8,3:2|3:2,0:0:0:0: +280,240,43244,2,0,L|284:292,1,45,8|8,3:2|3:2,0:0:0:0: +104,328,43417,6,8,L|48:328,1,45,8|8,3:2|3:2,0:0:0:0: +240,364,43589,2,8,L|300:364,1,45,8|8,3:2|3:2,0:0:0:0: +80,192,43762,2,0,L|136:192,1,45,8|8,3:2|3:2,0:0:0:0: +372,224,43934,2,0,L|316:224,1,45,8|8,3:2|3:2,0:0:0:0: +124,44,44106,5,8,3:2:0:0: +368,44,44279,1,2,0:0:0:0: +116,44,44451,2,0,L|64:44,1,45,2|2,0:0|0:0,0:0:0:0: +172,116,44624,2,0,L|112:116,1,45,2|2,0:0|0:0,0:0:0:0: +300,116,44796,2,0,L|476:116,1,168.750006437302,2|2,0:0|0:0,0:0:0:0: +260,192,45141,2,0,L|428:192,1,168.750006437302,2|2,0:0|0:0,0:0:0:0: +176,328,45486,5,2,3:2:0:0: +158,322,45507,1,0,0:0:0:0: +143,313,45529,1,0,0:0:0:0: +129,301,45550,1,0,0:0:0:0: +119,287,45572,1,0,0:0:0:0: +111,270,45594,1,0,0:0:0:0: +108,253,45615,1,0,0:0:0:0: +108,235,45637,1,0,0:0:0:0: +112,217,45658,1,0,0:0:0:0: +120,201,45680,1,0,0:0:0:0: +131,187,45701,1,0,0:0:0:0: +145,175,45723,1,0,0:0:0:0: +161,167,45744,1,0,0:0:0:0: +178,162,45766,1,0,0:0:0:0: +196,161,45787,1,0,0:0:0:0: +214,164,45809,1,0,0:0:0:0: +240,168,45831,1,0,0:0:0:0: +257,167,45852,1,0,0:0:0:0: +275,162,45874,1,0,0:0:0:0: +291,153,45895,1,0,0:0:0:0: +304,142,45917,1,0,0:0:0:0: +315,128,45938,1,0,0:0:0:0: +323,111,45960,1,0,0:0:0:0: +327,94,45981,1,0,0:0:0:0: +327,76,46003,1,0,0:0:0:0: +324,58,46025,1,0,0:0:0:0: +317,42,46046,1,0,0:0:0:0: +306,27,46068,1,0,0:0:0:0: +293,16,46089,1,0,0:0:0:0: +277,7,46111,1,0,0:0:0:0: +260,1,46132,1,0,0:0:0:0: +76,52,46175,6,0,B|8:52|8:52|80:52,1,135.000005149842,4|2,3:2|0:0,0:0:0:0: +120,52,46434,1,2,0:0:0:0: +280,52,46520,2,0,L|376:52,1,90,8|2,3:2|0:0,0:0:0:0: +324,52,46779,1,2,0:0:0:0: +152,136,46865,2,0,L|96:136,1,45,2|2,3:2|0:0,0:0:0:0: +172,208,47037,2,0,L|112:208,1,45,2|2,0:0|0:0,0:0:0:0: +336,192,47210,1,8,3:2:0:0: +363,202,47253,1,0,0:0:0:0: +384,224,47296,1,0,0:0:0:0: +393,252,47339,1,0,0:0:0:0: +389,282,47382,1,0,0:0:0:0: +372,306,47425,1,0,0:0:0:0: +347,322,47469,1,0,0:0:0:0: +168,324,47555,6,0,L|76:324,1,90,2|0,3:2|0:0,0:0:0:0: +244,208,47900,2,0,L|152:208,1,90,10|2,3:2|0:3,0:0:0:0: +400,208,48244,2,0,L|404:156,1,45,2|2,3:2|0:2,0:0:0:0: +312,76,48503,1,2,0:2:0:0: +140,76,48589,1,10,0:2:0:0: +248,76,48762,1,2,0:2:0:0: +16,76,48934,6,0,L|60:76,1,45,2|2,3:2|0:0,0:0:0:0: +160,76,49193,1,2,3:2:0:0: +16,76,49279,2,0,L|20:120,1,45,10|2,3:2|0:2,0:0:0:0: +76,164,49451,2,0,L|140:164,1,45,2|2,0:2|0:2,0:0:0:0: +304,192,49624,5,0,3:0:0:0: +317,209,49667,1,0,0:0:0:0: +326,230,49710,1,0,0:0:0:0: +328,252,49753,1,0,0:0:0:0: +325,274,49796,1,2,0:0:0:0: +316,295,49839,1,0,0:0:0:0: +301,312,49882,1,0,0:0:0:0: +120,312,49969,1,8,3:2:0:0: +52,312,50055,1,2,0:0:0:0: +120,312,50141,1,2,0:0:0:0: +288,312,50313,5,2,3:2:0:0: +332,273,50400,1,2,3:2:0:0: +328,215,50486,1,2,3:2:0:0: +280,184,50572,1,2,3:2:0:0: +104,92,50658,2,0,L|60:92,1,45,10|2,3:2|0:3,0:0:0:0: +104,184,50831,2,0,L|148:184,1,45,2|0,0:3|0:0,0:0:0:0: +328,215,51003,6,0,B|376:215|376:215|324:215,1,90,4|2,3:2|0:0,0:0:0:0: +280,215,51262,1,2,0:0:0:0: +128,296,51348,2,0,L|364:296,1,236.250009012223,8|0,3:2|0:0,0:0:0:0: +364,296,51520,2,0,L|224:296,1,135.000005149842,2|2,0:0|0:0,0:0:0:0: +368,144,51736,2,0,L|440:144,1,67.5,2|2,0:0|3:2,0:0:0:0: +380,144,51951,1,2,0:0:0:0: +204,64,52037,2,0,L|128:64,1,67.5000025749208,10|0,0:0|0:0,0:0:0:0: +223,64,52210,2,0,L|148:64,1,67.5000025749208,2|2,0:2|0:2,0:0:0:0: +388,240,52382,6,0,L|464:240,1,67.5000025749208,2|2,3:2|0:2,0:0:0:0: +368,144,52555,2,0,L|436:144,1,67.5000025749208,2|2,0:2|0:2,0:0:0:0: +224,144,52727,1,10,3:2:0:0: +194,150,52770,1,0,0:0:0:0: +169,165,52813,1,0,0:0:0:0: +149,188,52856,1,0,0:0:0:0: +137,215,52900,1,2,0:3:0:0: +134,245,52943,1,0,0:0:0:0: +141,274,52986,1,0,0:0:0:0: +368,348,53072,2,0,B|144:348,1,225,10|0,3:2|0:0,0:0:0:0: +444,272,53417,2,0,B|220:272,1,225,10|2,3:2|0:3,0:0:0:0: +488,184,53762,6,0,L|492:276,1,90,2|2,3:2|0:3,0:0:0:0: +336,184,54106,1,8,3:2:0:0: +280,184,54193,1,2,0:3:0:0: +228,184,54279,1,2,0:3:0:0: +392,276,54451,2,0,L|396:312,1,22.5,2|0,3:2|0:0,0:0:0:0: +188,328,54624,2,0,L|185:305,1,22.5,2|0,0:3|0:0,0:0:0:0: +408,108,54796,2,0,L|356:108,1,45,10|2,3:2|0:3,0:0:0:0: +136,176,54969,2,0,L|188:176,1,45,2|0,0:0|0:0,0:0:0:0: +384,192,55141,6,0,L|292:192,2,90.0000034332277,2|2|2,3:2|0:2|0:2,0:0:0:0: +172,272,55486,1,2,3:2:0:0: +280,272,55601,1,2,0:2:0:0: +388,272,55716,1,2,0:2:0:0: +164,192,55831,2,0,L|96:192,2,60,2|2|2,3:2|0:2|0:2,0:0:0:0: +340,192,56175,1,2,3:2:0:0: +412,192,56290,1,2,0:2:0:0: +412,120,56405,1,2,0:2:0:0: +212,120,56520,6,0,P|160:260|288:136,1,472.500018024445,0|0,3:2|0:0,0:0:0:0: +128,40,57210,5,2,3:2:0:0: +112,44,57231,1,0,0:0:0:0: +97,50,57253,1,0,0:0:0:0: +83,58,57275,1,0,0:0:0:0: +70,67,57296,1,0,0:0:0:0: +57,77,57318,1,0,0:0:0:0: +46,89,57339,1,0,0:0:0:0: +35,101,57361,1,0,0:0:0:0: +26,114,57382,1,0,0:0:0:0: +19,129,57404,1,0,0:0:0:0: +13,143,57425,1,0,0:0:0:0: +8,159,57447,1,0,0:0:0:0: +5,175,57469,1,0,0:0:0:0: +3,191,57490,1,0,0:0:0:0: +3,207,57512,1,0,0:0:0:0: +5,223,57533,1,0,0:0:0:0: +8,239,57555,1,0,0:0:0:0: +12,254,57576,1,0,0:0:0:0: +18,269,57598,1,0,0:0:0:0: +26,283,57619,1,0,0:0:0:0: +35,297,57641,1,0,0:0:0:0: +45,309,57662,1,0,0:0:0:0: +56,321,57684,1,0,0:0:0:0: +69,331,57706,1,0,0:0:0:0: +82,340,57727,1,0,0:0:0:0: +96,348,57749,1,0,0:0:0:0: +111,354,57770,1,0,0:0:0:0: +126,359,57792,1,0,0:0:0:0: +142,362,57813,1,0,0:0:0:0: +158,364,57835,1,0,0:0:0:0: +174,364,57856,1,0,0:0:0:0: +312,364,57900,6,0,L|448:364,1,135.000005149842,12|2,3:2|0:0,0:0:0:0: +392,364,58158,1,2,0:0:0:0: +216,192,58244,2,0,L|160:192,1,56.25,10|0,3:2|0:0,0:0:0:0: +232,124,58417,2,0,L|176:124,1,56.25,2|2,0:0|0:0,0:0:0:0: +20,192,58589,2,0,L|112:192,1,90,2|2,3:2|0:0,0:0:0:0: +276,264,58934,2,0,L|180:264,1,90,8|2,3:2|0:3,0:0:0:0: +440,264,59279,5,0,3:0:0:0: +466,250,59322,1,0,0:0:0:0: +484,226,59365,1,0,0:0:0:0: +491,198,59408,1,0,0:0:0:0: +484,168,59451,1,2,0:3:0:0: +428,128,59537,1,0,0:0:0:0: +260,128,59624,2,0,L|216:128,2,45,8|2|2,3:2|0:0|0:0,0:0:0:0: +494,129,59969,2,0,L|498:181,1,45,2|2,3:2|0:2,0:0:0:0: +392,260,60227,1,2,0:2:0:0: +212,260,60313,1,10,3:2:0:0: +356,260,60486,1,2,0:2:0:0: +104,64,60658,6,0,L|100:112,1,45,2|2,3:2|0:2,0:0:0:0: +204,192,60917,1,2,0:2:0:0: +384,128,61003,2,0,L|340:128,1,45,10|2,3:2|0:2,0:0:0:0: +159,192,61175,2,0,L|240:192,1,67.5000025749208,2|2,0:2|0:2,0:0:0:0: +72,192,61348,5,2,3:2:0:0: +9,228,61434,1,2,3:2:0:0: +9,300,61520,1,2,3:2:0:0: +70,336,61606,1,2,3:2:0:0: +250,272,61693,2,0,L|350:272,1,90,10|0,3:2|0:0,0:0:0:0: +184,215,62037,6,0,B|136:215|136:215|188:215,1,90,6|2,3:2|0:3,0:0:0:0: +232,215,62296,1,2,0:3:0:0: +384,296,62382,2,0,L|148:296,1,236.250009012223,8|0,0:0|0:0,0:0:0:0: +148,296,62555,2,0,L|288:296,1,135.000005149842,2|2,0:0|3:2,0:0:0:0: +144,144,62770,2,0,L|72:144,1,67.5,2|2,0:0|0:0,0:0:0:0: +132,144,62986,1,2,0:0:0:0: +300,64,63072,2,0,L|344:64,1,45,8|0,3:2|0:0,0:0:0:0: +184,192,63244,2,0,L|232:192,1,45,2|2,0:0|0:0,0:0:0:0: +64,64,63417,6,0,L|20:64,1,45,2|2,3:2|0:0,0:0:0:0: +184,192,63589,2,0,L|140:192,1,45,2|2,0:0|0:0,0:0:0:0: +345,64,63762,1,10,0:0:0:0: +375,70,63805,1,0,0:0:0:0: +400,85,63848,1,0,0:0:0:0: +420,108,63891,1,0,0:0:0:0: +432,135,63934,1,0,0:0:0:0: +435,165,63977,1,0,0:0:0:0: +428,194,64020,1,0,0:0:0:0: +224,344,64106,2,0,B|448:344,1,225,8|2,3:2|3:2,0:0:0:0: +148,268,64451,2,0,B|372:268,1,225,8|0,3:2|0:0,0:0:0:0: +120,344,64796,5,6,3:2:0:0: +324,344,64911,1,2,0:0:0:0: +120,344,65026,1,2,0:0:0:0: +336,168,65141,1,10,3:2:0:0: +222,168,65256,1,2,0:0:0:0: +108,168,65371,1,2,0:0:0:0: +336,92,65486,1,2,3:2:0:0: +444,92,65601,1,2,0:0:0:0: +336,92,65716,1,2,0:0:0:0: +144,92,65831,1,10,3:2:0:0: +252,92,65946,1,2,0:0:0:0: +144,92,66060,1,2,0:0:0:0: +360,288,66175,6,0,L|468:288,1,90,2|2,0:0|0:0,0:0:0:0: +396,288,66434,1,2,0:0:0:0: +224,192,66520,1,2,0:0:0:0: +388,192,66693,1,2,0:0:0:0: +124,316,66865,2,0,L|120:264,1,45,2|2,0:0|0:0,0:0:0:0: +204,352,67037,2,0,L|200:300,1,45,2|2,0:0|0:0,0:0:0:0: +368,192,67210,1,2,0:0:0:0: +204,192,67382,1,2,0:0:0:0: +476,192,67555,5,6,3:2:0:0: +188,192,67900,1,6,3:2:0:0: +488,192,68244,1,0,3:0:0:0: +356,192,68417,2,0,L|424:192,1,67.5000025749208,0|0,3:0|3:0,0:0:0:0: +172,192,68589,2,0,L|168:100,1,90,8|2,0:0|0:0,0:0:0:0: +484,60,68934,5,4,3:2:0:0: +256,192,69279,12,0,71348,0:0:0:0: +232,196,71693,6,0,L|228:176,14,11.25,2|0|0|0|0|0|0|0|0|0|0|0|0|0|0,0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: +272,164,72037,2,0,L|282:146,14,11.25,2|0|0|0|0|0|0|0|0|0|0|0|0|0|0,0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: +316,216,72382,6,0,L|331:203,14,11.25,2|0|0|0|0|0|0|0|0|0|0|0|0|0|0,0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: +360,140,72727,2,0,L|375:127,14,11.25,2|0|0|0|0|0|0|0|0|0|0|0|0|0|0,0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0,0:0:0:0: +256,76,73072,5,2,0:0:0:0: +244,78,73094,1,0,0:0:0:0: +233,82,73115,1,0,0:0:0:0: +224,88,73137,1,0,0:0:0:0: +215,96,73158,1,0,0:0:0:0: +209,106,73180,1,0,0:0:0:0: +205,117,73201,1,0,0:0:0:0: +202,128,73223,1,0,0:0:0:0: +203,140,73244,1,0,0:0:0:0: +205,151,73266,1,0,0:0:0:0: +210,162,73287,1,0,0:0:0:0: +217,171,73309,1,0,0:0:0:0: +226,179,73331,1,0,0:0:0:0: +236,184,73352,1,0,0:0:0:0: +247,188,73374,1,0,0:0:0:0: +258,190,73395,1,0,0:0:0:0: +270,189,73417,1,0,0:0:0:0: +281,185,73438,1,0,0:0:0:0: +291,180,73460,1,0,0:0:0:0: +300,173,73481,1,0,0:0:0:0: +307,164,73503,1,0,0:0:0:0: +313,153,73525,1,0,0:0:0:0: +316,142,73546,1,0,0:0:0:0: +317,131,73568,1,0,0:0:0:0: +315,119,73589,1,0,0:0:0:0: +311,108,73611,1,0,0:0:0:0: +305,98,73632,1,0,0:0:0:0: +297,90,73654,1,0,0:0:0:0: +288,83,73675,1,0,0:0:0:0: +277,78,73697,1,0,0:0:0:0: +266,76,73719,1,0,0:0:0:0: +164,20,73762,5,2,0:0:0:0: +153,23,73783,1,0,0:0:0:0: +143,28,73805,1,0,0:0:0:0: +133,34,73826,1,0,0:0:0:0: +124,40,73848,1,0,0:0:0:0: +115,48,73869,1,0,0:0:0:0: +108,56,73891,1,0,0:0:0:0: +101,65,73912,1,0,0:0:0:0: +95,74,73934,1,0,0:0:0:0: +90,84,73956,1,0,0:0:0:0: +85,95,73977,1,0,0:0:0:0: +82,105,73999,1,0,0:0:0:0: +80,116,74020,1,0,0:0:0:0: +79,128,74042,1,0,0:0:0:0: +79,139,74063,1,0,0:0:0:0: +180,148,74106,1,2,0:0:0:0: +190,151,74128,1,0,0:0:0:0: +200,156,74150,1,0,0:0:0:0: +210,162,74171,1,0,0:0:0:0: +219,168,74193,1,0,0:0:0:0: +228,176,74214,1,0,0:0:0:0: +235,184,74236,1,0,0:0:0:0: +242,193,74257,1,0,0:0:0:0: +248,202,74279,1,0,0:0:0:0: +253,212,74300,1,0,0:0:0:0: +258,223,74322,1,0,0:0:0:0: +261,233,74344,1,0,0:0:0:0: +263,244,74365,1,0,0:0:0:0: +264,256,74387,1,0,0:0:0:0: +264,267,74408,1,0,0:0:0:0: +148,236,74451,6,0,L|52:236,1,90,6|0,0:1|0:0,0:0:0:0: +196,124,74796,2,0,L|192:32,1,67.5000025749208 +328,208,75141,2,0,L|324:116,1,67.5000025749208,0|0,0:0|0:0,0:0:0:0: +228,168,75486,1,2,0:0:0:0: +396,208,75658,1,2,0:0:0:0: +124,168,75831,5,2,0:0:0:0: +36,168,76003,1,0,0:0:0:0: +36,80,76175,1,0,0:0:0:0: +124,80,76348,1,0,0:0:0:0: +292,80,76520,2,0,L|296:172,1,67.5000025749208,2|0,0:0|0:0,0:0:0:0: +192,224,76865,2,0,L|196:152,1,67.5000025749208,0|0,0:0|0:0,0:0:0:0: +368,148,77210,6,0,P|424:204|368:268,1,180,2|0,0:0|0:0,0:0:0:0: +272,268,77727,1,0,0:0:0:0: +176,268,77900,2,0,L|172:360,1,67.5000025749208,0|0,0:0|0:0,0:0:0:0: +272,268,78244,1,2,0:0:0:0: +104,336,78417,1,2,0:0:0:0: +380,268,78589,6,0,L|456:268,2,67.5000025749208,2|0|0,0:0|0:0|0:0,0:0:0:0: +284,268,79106,1,0,0:0:0:0: +116,268,79279,2,0,L|112:176,1,67.5000025749208,2|0,0:0|0:0,0:0:0:0: +216,192,79624,2,0,L|312:192,1,67.5000025749208,0|0,0:0|0:0,0:0:0:0: +324,192,79882,1,0,0:0:0:0: +152,96,79969,6,0,B|56:96,2,90,2|0|0,0:0|0:0|0:0,0:0:0:0: +248,96,80486,1,0,0:0:0:0: +416,96,80658,2,0,L|420:168,1,67.5000025749208,2|0,0:0|0:0,0:0:0:0: +324,192,81003,2,0,L|252:192,1,67.5000025749208,0|0,0:0|0:0,0:0:0:0: +208,192,81262,1,0,0:0:0:0: +384,256,81348,6,0,B|480:256,2,90,2|0|0,0:0|0:0|0:0,0:0:0:0: +212,296,81865,1,2,0:0:0:0: +444,360,82037,2,0,L|448:284,1,67.5000025749208,2|0,0:0|0:0,0:0:0:0: +212,296,82382,1,2,0:0:0:0: +172,296,82469,1,0,0:0:0:0: +132,296,82555,1,0,0:0:0:0: +432,24,82727,6,0,P|500:80|432:148,1,207.000003948212,2|0,0:0|0:0,0:0:0:0: +272,148,83244,1,0,0:0:0:0: +440,148,83417,2,0,L|444:220,1,67.5000025749208,0|0,0:0|0:0,0:0:0:0: +200,148,83762,1,2,0:0:0:0: +352,148,83934,1,2,0:0:0:0: +104,148,84106,6,0,B|8:148,2,90,2|0|0,0:0|0:0|0:0,0:0:0:0: +272,196,84624,1,0,0:0:0:0: +112,148,84796,2,0,L|108:228,1,67.5000025749208,2|2,0:0|0:0,0:0:0:0: +164,216,85055,1,2,0:0:0:0: +216,216,85141,2,0,L|292:216,1,67.5000025749208,2|2,0:0|0:0,0:0:0:0: +32,216,85486,6,0,P|0:264|36:324,1,135,6|0,3:1|0:0,0:0:0:0: +108,324,85831,2,0,L|216:324,1,90,0|2,3:2|0:2,0:0:0:0: +20,324,86175,1,2,3:2:0:0: +128,324,86348,2,0,L|180:324,1,45,0|0,0:2|0:0,0:0:0:0: +344,192,86520,2,0,L|248:192,1,90,2|2,3:2|0:2,0:0:0:0: +436,312,86865,6,0,L|440:208,1,90,2|2,3:2|0:2,0:0:0:0: +375,208,87124,1,2,0:2:0:0: +312,192,87210,1,2,3:2:0:0: +472,192,87382,1,2,0:2:0:0: +300,192,87555,2,0,L|296:96,1,90,2|2,3:2|0:2,0:0:0:0: +360,100,87813,1,2,0:2:0:0: +196,100,87900,2,0,L|104:100,1,90,2|2,3:2|0:2,0:0:0:0: +276,16,88244,6,0,L|368:16,1,90,2|0,3:2|0:0,0:0:0:0: +312,16,88503,1,0,0:0:0:0: +260,16,88589,1,0,3:2:0:0: +440,16,88762,1,2,0:2:0:0: +192,16,88934,2,0,L|100:16,1,90,2|0,3:2|0:2,0:0:0:0: +164,16,89193,1,0,0:0:0:0: +228,16,89279,2,0,L|136:16,1,90,2|2,3:2|0:2,0:0:0:0: +306,112,89624,6,0,L|414:112,1,90,2|2,3:2|0:2,0:0:0:0: +450,112,89882,1,2,0:2:0:0: +396,112,89969,1,2,3:2:0:0: +228,112,90141,1,2,0:2:0:0: +396,112,90313,2,0,L|400:208,1,90,2|0,3:2|0:2,0:0:0:0: +332,204,90572,1,0,0:0:0:0: +264,204,90658,2,0,L|360:204,1,90,2|0,3:2|0:2,0:0:0:0: +184,204,91003,6,0,L|80:204,1,90,2|2,3:2|0:2,0:0:0:0: +148,204,91262,1,0,0:0:0:0: +200,204,91348,1,2,3:2:0:0: +32,204,91520,1,2,0:2:0:0: +296,204,91693,2,0,B|344:204|344:204|296:204,1,90,2|2,3:2|0:2,0:0:0:0: +240,204,91951,1,0,0:0:0:0: +136,204,92037,2,0,L|132:132,1,45,0|0,3:2|0:0,0:0:0:0: +196,112,92210,2,0,L|200:168,1,45,0|0,0:2|0:0,0:0:0:0: +48,204,92382,6,0,B|4:204|4:204|52:204,1,90,2|2,3:2|0:2,0:0:0:0: +120,204,92641,1,0,0:0:0:0: +188,204,92727,1,0,3:2:0:0: +360,204,92900,1,2,0:2:0:0: +123,293,93072,2,0,L|119:197,1,90,2|2,3:2|0:2,0:0:0:0: +188,204,93331,1,2,0:2:0:0: +368,204,93417,2,0,L|424:204,2,45,2|2|2,3:2|0:2|0:2,0:0:0:0: +96,204,93762,5,2,3:2:0:0: +53,169,93848,1,0,0:0:0:0: +45,114,93934,1,2,0:2:0:0: +75,69,94020,1,0,0:0:0:0: +128,55,94106,1,2,3:2:0:0: +316,56,94279,1,2,0:2:0:0: +48,52,94451,2,0,L|44:152,1,90,2|0,3:2|0:2,0:0:0:0: +112,160,94710,1,0,0:0:0:0: +300,160,94796,1,2,3:2:0:0: +416,160,94969,2,0,L|352:160,1,45,2|0,0:2|0:0,0:0:0:0: +180,232,95141,5,2,3:2:0:0: +128,232,95227,1,0,0:0:0:0: +76,232,95313,1,2,0:2:0:0: +248,232,95486,1,2,3:2:0:0: +68,232,95658,1,2,0:2:0:0: +348,232,95831,2,0,L|440:232,1,90,2|2,3:2|0:2,0:0:0:0: +176,232,96175,6,0,P|176:16|180:232,1,675,2|0,3:2|0:0,0:0:0:0: +156,232,96865,2,0,P|160:64|168:232,1,506.250019311906,0|0,0:0|0:0,0:0:0:0: +144,232,97555,2,0,P|148:112|152:232,1,360,0|0,0:0|0:0,0:0:0:0: +164,320,99279,5,0,3:0:0:0: +324,320,99451,2,0,L|340:284,3,22.5,8|8|8|8,0:2|0:2|0:2|0:2,0:0:0:0: +204,320,99624,2,0,L|64:320,1,135.000005149842,8|0,3:2|3:0,0:0:0:0: +340,228,99969,2,0,L|200:228,1,135.000005149842,8|0,3:2|3:0,0:0:0:0: +472,228,100313,1,8,3:2:0:0: +64,172,100658,6,0,L|8:172,2,53.9999983520508,4|2|2,3:2|0:0|0:0,0:0:0:0: +336,228,101003,1,10,3:2:0:0: +176,228,101175,1,2,0:0:0:0: +448,228,101348,2,0,B|500:228|500:228|444:228,1,107.999996704102,2|2,3:2|3:2,0:0:0:0: +384,228,101606,1,2,0:0:0:0: +220,128,101693,2,0,L|328:128,1,107.999996704102,8|2,3:2|0:0,0:0:0:0: +264,128,101951,1,2,0:0:0:0: +112,128,102037,5,2,3:2:0:0: +56,128,102124,1,2,0:0:0:0: +56,180,102210,1,2,0:0:0:0: +344,252,102382,1,8,3:2:0:0: +56,180,102555,1,2,0:0:0:0: +368,252,102727,2,0,P|400:304|388:352,1,107.999996704102,0|2,3:0|3:2,0:0:0:0: +332,348,102986,1,2,0:2:0:0: +168,348,103072,2,0,L|56:348,1,107.999996704102,10|2,3:2|0:0,0:0:0:0: +120,348,103331,1,0,0:0:0:0: +304,192,103417,5,0,3:2:0:0: +364,192,103503,1,2,0:0:0:0: +424,192,103589,1,2,0:0:0:0: +152,192,103762,1,10,3:2:0:0: +316,192,103934,1,2,0:0:0:0: +56,192,104106,2,0,B|4:192|4:192|60:192,1,107.999996704102,2|2,3:2|3:2,0:0:0:0: +116,192,104365,1,2,0:0:0:0: +304,192,104451,2,0,L|416:192,1,107.999996704102,8|2,3:2|0:0,0:0:0:0: +356,192,104710,1,2,0:0:0:0: +168,112,104796,6,0,L|112:112,2,53.9999983520508,2|2|2,3:2|0:0|0:0,0:0:0:0: +440,112,105141,1,8,3:2:0:0: +144,112,105313,1,2,0:0:0:0: +468,112,105486,2,0,B|468:60|468:60|412:60,1,107.999996704102,0|2,3:0|3:2,0:0:0:0: +360,60,105744,1,2,0:2:0:0: +164,192,105831,2,0,L|276:192,1,107.999996704102,10|2,3:2|0:0,0:0:0:0: +212,192,106089,1,2,0:0:0:0: +24,192,106175,5,2,3:2:0:0: +20,132,106262,1,2,0:0:0:0: +16,72,106348,1,2,0:0:0:0: +296,72,106520,1,8,3:2:0:0: +132,72,106693,1,2,0:0:0:0: +400,72,106865,2,0,P|448:108|440:164,1,107.999996704102,0|2,3:0|3:2,0:0:0:0: +388,192,107124,1,2,0:2:0:0: +196,192,107210,2,0,L|88:192,1,107.999996704102,10|2,3:2|0:0,0:0:0:0: +148,192,107469,1,2,0:0:0:0: +304,12,107555,5,2,3:2:0:0: +358,12,107641,1,2,0:0:0:0: +412,12,107727,1,2,0:0:0:0: +136,12,107900,1,8,3:2:0:0: +432,12,108072,1,2,0:0:0:0: +160,116,108244,2,0,L|52:116,1,107.999996704102,0|2,3:0|3:2,0:0:0:0: +112,116,108503,1,2,0:2:0:0: +300,192,108589,2,0,L|188:192,1,107.999996704102,10|2,3:2|0:0,0:0:0:0: +248,192,108848,1,2,0:0:0:0: +436,192,108934,6,0,L|496:192,2,53.9999983520508,2|2|2,3:2|0:0|0:0,0:0:0:0: +164,192,109279,1,8,3:2:0:0: +324,192,109451,1,2,0:0:0:0: +52,192,109624,2,0,P|24:235|60:280,1,107.999996704102,0|2,3:0|3:2,0:0:0:0: +112,276,109882,1,2,0:2:0:0: +316,276,109969,2,0,L|204:276,1,107.999996704102,10|2,3:2|0:0,0:0:0:0: +268,276,110227,1,2,0:0:0:0: +456,272,110313,6,0,L|460:152,1,107.999996704102,2|2,3:2|0:0,0:0:0:0: +292,276,110658,2,0,L|296:156,1,107.999996704102,8|2,3:2|0:0,0:0:0:0: +32,168,111003,1,8,3:2:0:0: +140,168,111118,1,2,0:0:0:0: +248,168,111233,1,2,0:0:0:0: +44,168,111348,2,0,L|124:168,2,71.9999978027344,10|2|2,3:2|0:2|0:2,0:0:0:0: +320,168,111693,5,4,3:2:0:0: +392,168,111779,1,2,0:0:0:0: +464,168,111865,1,2,0:0:0:0: +196,168,112037,1,10,3:2:0:0: +364,168,112210,1,2,0:0:0:0: +92,80,112382,2,0,L|204:80,1,107.999996704102,2|2,3:2|3:2,0:0:0:0: +140,80,112641,1,2,0:0:0:0: +356,80,112727,2,0,B|408:80|408:80|352:80,1,107.999996704102,8|2,3:2|0:0,0:0:0:0: +292,80,112986,1,2,0:0:0:0: +96,168,113072,5,2,3:2:0:0: +36,168,113158,1,2,0:0:0:0: +96,168,113244,1,2,0:0:0:0: +368,168,113417,1,8,3:2:0:0: +72,168,113589,1,2,0:0:0:0: +364,264,113762,2,0,L|252:264,1,107.999996704102,0|2,3:0|3:2,0:0:0:0: +316,264,114020,1,2,0:2:0:0: +120,344,114106,2,0,L|228:344,1,107.999996704102,10|2,3:2|0:0,0:0:0:0: +168,344,114365,1,2,0:0:0:0: +384,264,114451,5,2,3:2:0:0: +444,264,114537,1,2,0:0:0:0: +444,324,114624,1,2,0:0:0:0: +176,344,114796,1,8,3:2:0:0: +344,344,114969,1,2,0:0:0:0: +76,292,115141,2,0,B|20:292|20:292|20:344,1,107.999996704102,0|2,3:0|3:2,0:0:0:0: +80,344,115400,1,2,0:2:0:0: +284,192,115486,2,0,L|176:192,1,107.999996704102,10|2,3:2|0:0,0:0:0:0: +236,192,115744,1,2,0:0:0:0: +28,192,115831,6,0,L|84:192,2,53.9999983520508,2|2|2,3:2|0:0|0:0,0:0:0:0: +300,192,116175,1,8,3:2:0:0: +132,192,116348,1,2,0:0:0:0: +408,192,116520,2,0,L|300:192,1,107.999996704102,0|2,3:0|3:2,0:0:0:0: +360,192,116779,1,2,0:2:0:0: +156,84,116865,2,0,L|268:84,1,107.999996704102,10|2,3:2|0:0,0:0:0:0: +204,84,117124,1,2,0:0:0:0: +384,84,117210,5,10,3:2:0:0: +444,84,117296,1,2,0:0:0:0: +504,84,117382,1,2,0:0:0:0: +228,284,117555,2,0,L|344:284,1,107.999996704102,8|2,3:2|0:0,0:0:0:0: +60,192,117900,2,0,L|169:192,1,107.999996704102,8|2,3:2|3:2,0:0:0:0: +108,192,118158,1,2,0:2:0:0: +324,192,118244,2,0,B|380:192|380:192|380:140,1,107.999996704102,10|2,3:2|0:0,0:0:0:0: +320,112,118503,1,2,0:0:0:0: +132,112,118589,5,10,3:2:0:0: +72,112,118675,1,2,0:0:0:0: +132,112,118762,1,2,0:0:0:0: +428,140,118934,1,8,3:2:0:0: +80,112,119106,1,2,0:0:0:0: +352,192,119279,2,0,L|216:192,1,135.000005149842,8|2,3:2|3:2,0:0:0:0: +148,192,119537,1,2,0:2:0:0: +388,264,119624,2,0,L|252:264,1,135.000005149842,10|2,3:2|0:0,0:0:0:0: +320,264,119882,1,2,0:0:0:0: +100,264,119969,6,0,L|40:264,2,53.9999983520508,10|2|10,3:2|0:0|0:2,0:0:0:0: +384,192,120313,1,8,3:2:0:0: +112,192,120486,1,10,0:2:0:0: +408,192,120658,2,0,B|464:192|464:192|404:192,1,107.999996704102,8|10,3:2|3:2,0:0:0:0: +348,192,120917,1,2,0:2:0:0: +132,96,121003,2,0,B|40:96|40:96|134:96,1,180,10|10,3:2|0:2,0:0:0:0: +196,96,121262,1,2,0:0:0:0: +384,96,121348,6,0,L|388:160,1,53.9999983520508,10|0,3:2|0:0,0:0:0:0: +188,192,121520,2,0,L|184:256,1,53.9999983520508,10|0,0:2|0:0,0:0:0:0: +400,248,121693,2,0,L|336:248,1,53.9999983520508,8|0,3:2|0:0,0:0:0:0: +128,192,121865,2,0,L|124:252,1,53.9999983520508,10|0,0:2|0:0,0:0:0:0: +336,96,122037,6,0,L|276:96,1,53.9999983520508,8|0,3:2|0:0,0:0:0:0: +484,96,122210,2,0,L|488:176,1,53.9999983520508,10|2,3:2|0:2,0:0:0:0: +272,192,122382,2,0,L|328:192,1,53.9999983520508,10|0,3:2|0:0,0:0:0:0: +108,192,122555,2,0,L|52:192,1,53.9999983520508,8|0,0:2|0:0,0:0:0:0: +280,272,122727,5,8,3:2:0:0: +347,272,122813,1,0,0:0:0:0: +415,272,122900,1,0,0:0:0:0: +256,192,123072,1,2,0:0:0:0: +308,192,123158,1,0,0:0:0:0: +360,192,123244,1,0,0:0:0:0: +228,112,123417,5,2,0:0:0:0: +260,112,123503,1,0,0:0:0:0: +292,112,123589,1,0,0:0:0:0: +188,28,123762,1,2,0:0:0:0: +196,28,123848,1,0,0:0:0:0: +204,28,123934,1,0,0:0:0:0: +256,192,124106,12,0,132382,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3152510-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3152510-expected-conversion.json new file mode 100644 index 0000000000..990550408d --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3152510-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":512.0,"Objects":[{"StartTime":512.0,"Position":368.0,"HyperDash":false},{"StartTime":573.0,"Position":353.0,"HyperDash":false},{"StartTime":670.0,"Position":368.0,"HyperDash":true}]},{"StartTime":829.0,"Objects":[{"StartTime":829.0,"Position":136.0,"HyperDash":false}]},{"StartTime":988.0,"Objects":[{"StartTime":988.0,"Position":272.0,"HyperDash":false}]},{"StartTime":1146.0,"Objects":[{"StartTime":1146.0,"Position":136.0,"HyperDash":false},{"StartTime":1207.0,"Position":125.0,"HyperDash":false},{"StartTime":1304.0,"Position":136.0,"HyperDash":true}]},{"StartTime":1464.0,"Objects":[{"StartTime":1464.0,"Position":368.0,"HyperDash":true}]},{"StartTime":1623.0,"Objects":[{"StartTime":1623.0,"Position":136.0,"HyperDash":false},{"StartTime":1702.0,"Position":87.4122238,"HyperDash":false},{"StartTime":1781.0,"Position":64.73344,"HyperDash":false},{"StartTime":1842.0,"Position":87.7600861,"HyperDash":false},{"StartTime":1940.0,"Position":119.014381,"HyperDash":false}]},{"StartTime":2019.0,"Objects":[{"StartTime":2019.0,"Position":176.0,"HyperDash":true}]},{"StartTime":2099.0,"Objects":[{"StartTime":2099.0,"Position":368.0,"HyperDash":false}]},{"StartTime":2258.0,"Objects":[{"StartTime":2258.0,"Position":232.0,"HyperDash":false}]},{"StartTime":2416.0,"Objects":[{"StartTime":2416.0,"Position":368.0,"HyperDash":false},{"StartTime":2477.0,"Position":369.0,"HyperDash":false},{"StartTime":2574.0,"Position":368.0,"HyperDash":true}]},{"StartTime":2734.0,"Objects":[{"StartTime":2734.0,"Position":136.0,"HyperDash":false},{"StartTime":2795.0,"Position":98.3227844,"HyperDash":false},{"StartTime":2892.0,"Position":41.0,"HyperDash":true}]},{"StartTime":3051.0,"Objects":[{"StartTime":3051.0,"Position":280.0,"HyperDash":false},{"StartTime":3112.0,"Position":301.677216,"HyperDash":false},{"StartTime":3209.0,"Position":375.0,"HyperDash":true}]},{"StartTime":3369.0,"Objects":[{"StartTime":3369.0,"Position":136.0,"HyperDash":false}]},{"StartTime":3527.0,"Objects":[{"StartTime":3527.0,"Position":272.0,"HyperDash":false}]},{"StartTime":3686.0,"Objects":[{"StartTime":3686.0,"Position":136.0,"HyperDash":false},{"StartTime":3747.0,"Position":128.0,"HyperDash":false},{"StartTime":3844.0,"Position":136.0,"HyperDash":true}]},{"StartTime":4004.0,"Objects":[{"StartTime":4004.0,"Position":384.0,"HyperDash":true}]},{"StartTime":4162.0,"Objects":[{"StartTime":4162.0,"Position":136.0,"HyperDash":false},{"StartTime":4241.0,"Position":171.350159,"HyperDash":false},{"StartTime":4320.0,"Position":230.700317,"HyperDash":false},{"StartTime":4381.0,"Position":281.261841,"HyperDash":false},{"StartTime":4479.0,"Position":326.0,"HyperDash":false}]},{"StartTime":4559.0,"Objects":[{"StartTime":4559.0,"Position":272.0,"HyperDash":true}]},{"StartTime":4638.0,"Objects":[{"StartTime":4638.0,"Position":80.0,"HyperDash":false}]},{"StartTime":4797.0,"Objects":[{"StartTime":4797.0,"Position":216.0,"HyperDash":false}]},{"StartTime":4956.0,"Objects":[{"StartTime":4956.0,"Position":80.0,"HyperDash":false},{"StartTime":5017.0,"Position":84.0,"HyperDash":false},{"StartTime":5114.0,"Position":80.0,"HyperDash":true}]},{"StartTime":5273.0,"Objects":[{"StartTime":5273.0,"Position":312.0,"HyperDash":false},{"StartTime":5334.0,"Position":266.322784,"HyperDash":false},{"StartTime":5431.0,"Position":217.0,"HyperDash":true}]},{"StartTime":5591.0,"Objects":[{"StartTime":5591.0,"Position":456.0,"HyperDash":false},{"StartTime":5652.0,"Position":461.0,"HyperDash":false},{"StartTime":5749.0,"Position":456.0,"HyperDash":true}]},{"StartTime":5908.0,"Objects":[{"StartTime":5908.0,"Position":216.0,"HyperDash":false}]},{"StartTime":6067.0,"Objects":[{"StartTime":6067.0,"Position":352.0,"HyperDash":false}]},{"StartTime":6226.0,"Objects":[{"StartTime":6226.0,"Position":216.0,"HyperDash":false},{"StartTime":6287.0,"Position":197.0,"HyperDash":false},{"StartTime":6384.0,"Position":216.0,"HyperDash":true}]},{"StartTime":6543.0,"Objects":[{"StartTime":6543.0,"Position":456.0,"HyperDash":true}]},{"StartTime":6702.0,"Objects":[{"StartTime":6702.0,"Position":216.0,"HyperDash":false},{"StartTime":6781.0,"Position":163.345444,"HyperDash":false},{"StartTime":6860.0,"Position":152.1179,"HyperDash":false},{"StartTime":6921.0,"Position":177.651291,"HyperDash":false},{"StartTime":7019.0,"Position":209.232849,"HyperDash":false}]},{"StartTime":7099.0,"Objects":[{"StartTime":7099.0,"Position":264.0,"HyperDash":true}]},{"StartTime":7178.0,"Objects":[{"StartTime":7178.0,"Position":456.0,"HyperDash":false}]},{"StartTime":7337.0,"Objects":[{"StartTime":7337.0,"Position":320.0,"HyperDash":false}]},{"StartTime":7496.0,"Objects":[{"StartTime":7496.0,"Position":456.0,"HyperDash":false},{"StartTime":7557.0,"Position":469.0,"HyperDash":false},{"StartTime":7654.0,"Position":456.0,"HyperDash":true}]},{"StartTime":7813.0,"Objects":[{"StartTime":7813.0,"Position":216.0,"HyperDash":false},{"StartTime":7874.0,"Position":171.322784,"HyperDash":false},{"StartTime":7971.0,"Position":121.0,"HyperDash":true}]},{"StartTime":8131.0,"Objects":[{"StartTime":8131.0,"Position":368.0,"HyperDash":false},{"StartTime":8192.0,"Position":351.0,"HyperDash":false},{"StartTime":8289.0,"Position":368.0,"HyperDash":true}]},{"StartTime":8448.0,"Objects":[{"StartTime":8448.0,"Position":128.0,"HyperDash":false}]},{"StartTime":8607.0,"Objects":[{"StartTime":8607.0,"Position":264.0,"HyperDash":false}]},{"StartTime":8765.0,"Objects":[{"StartTime":8765.0,"Position":128.0,"HyperDash":false},{"StartTime":8826.0,"Position":141.0,"HyperDash":false},{"StartTime":8923.0,"Position":128.0,"HyperDash":true}]},{"StartTime":9083.0,"Objects":[{"StartTime":9083.0,"Position":368.0,"HyperDash":true}]},{"StartTime":9242.0,"Objects":[{"StartTime":9242.0,"Position":128.0,"HyperDash":false},{"StartTime":9321.0,"Position":170.350159,"HyperDash":false},{"StartTime":9400.0,"Position":222.700317,"HyperDash":false},{"StartTime":9461.0,"Position":244.261841,"HyperDash":false},{"StartTime":9559.0,"Position":318.0,"HyperDash":false}]},{"StartTime":9638.0,"Objects":[{"StartTime":9638.0,"Position":264.0,"HyperDash":true}]},{"StartTime":9718.0,"Objects":[{"StartTime":9718.0,"Position":72.0,"HyperDash":false}]},{"StartTime":9877.0,"Objects":[{"StartTime":9877.0,"Position":208.0,"HyperDash":false}]},{"StartTime":10035.0,"Objects":[{"StartTime":10035.0,"Position":72.0,"HyperDash":false},{"StartTime":10096.0,"Position":68.0,"HyperDash":false},{"StartTime":10193.0,"Position":72.0,"HyperDash":true}]},{"StartTime":10353.0,"Objects":[{"StartTime":10353.0,"Position":312.0,"HyperDash":false},{"StartTime":10414.0,"Position":274.322784,"HyperDash":false},{"StartTime":10511.0,"Position":217.0,"HyperDash":true}]},{"StartTime":10670.0,"Objects":[{"StartTime":10670.0,"Position":464.0,"HyperDash":false},{"StartTime":10731.0,"Position":478.0,"HyperDash":false},{"StartTime":10828.0,"Position":464.0,"HyperDash":true}]},{"StartTime":10988.0,"Objects":[{"StartTime":10988.0,"Position":224.0,"HyperDash":false},{"StartTime":11049.0,"Position":209.0,"HyperDash":false},{"StartTime":11146.0,"Position":224.0,"HyperDash":false}]},{"StartTime":11305.0,"Objects":[{"StartTime":11305.0,"Position":360.0,"HyperDash":false}]},{"StartTime":11464.0,"Objects":[{"StartTime":11464.0,"Position":224.0,"HyperDash":true}]},{"StartTime":11623.0,"Objects":[{"StartTime":11623.0,"Position":464.0,"HyperDash":false}]},{"StartTime":11781.0,"Objects":[{"StartTime":11781.0,"Position":328.0,"HyperDash":false},{"StartTime":11842.0,"Position":309.0,"HyperDash":false},{"StartTime":11939.0,"Position":328.0,"HyperDash":false}]},{"StartTime":12099.0,"Objects":[{"StartTime":12099.0,"Position":464.0,"HyperDash":false},{"StartTime":12160.0,"Position":448.0,"HyperDash":false},{"StartTime":12257.0,"Position":464.0,"HyperDash":false}]},{"StartTime":12416.0,"Objects":[{"StartTime":12416.0,"Position":328.0,"HyperDash":false},{"StartTime":12477.0,"Position":377.677216,"HyperDash":false},{"StartTime":12574.0,"Position":423.0,"HyperDash":false}]},{"StartTime":12734.0,"Objects":[{"StartTime":12734.0,"Position":288.0,"HyperDash":false}]},{"StartTime":12892.0,"Objects":[{"StartTime":12892.0,"Position":424.0,"HyperDash":false},{"StartTime":12953.0,"Position":441.0,"HyperDash":false},{"StartTime":13050.0,"Position":424.0,"HyperDash":true}]},{"StartTime":13210.0,"Objects":[{"StartTime":13210.0,"Position":192.0,"HyperDash":false},{"StartTime":13271.0,"Position":191.0,"HyperDash":false},{"StartTime":13368.0,"Position":192.0,"HyperDash":true}]},{"StartTime":13527.0,"Objects":[{"StartTime":13527.0,"Position":424.0,"HyperDash":false},{"StartTime":13588.0,"Position":417.0,"HyperDash":false},{"StartTime":13685.0,"Position":424.0,"HyperDash":false}]},{"StartTime":13845.0,"Objects":[{"StartTime":13845.0,"Position":288.0,"HyperDash":false}]},{"StartTime":14004.0,"Objects":[{"StartTime":14004.0,"Position":424.0,"HyperDash":true}]},{"StartTime":14162.0,"Objects":[{"StartTime":14162.0,"Position":184.0,"HyperDash":false}]},{"StartTime":14321.0,"Objects":[{"StartTime":14321.0,"Position":320.0,"HyperDash":false},{"StartTime":14382.0,"Position":319.0,"HyperDash":false},{"StartTime":14479.0,"Position":320.0,"HyperDash":true}]},{"StartTime":14638.0,"Objects":[{"StartTime":14638.0,"Position":88.0,"HyperDash":false},{"StartTime":14699.0,"Position":107.0,"HyperDash":false},{"StartTime":14796.0,"Position":88.0,"HyperDash":false}]},{"StartTime":14956.0,"Objects":[{"StartTime":14956.0,"Position":224.0,"HyperDash":false}]},{"StartTime":15115.0,"Objects":[{"StartTime":15115.0,"Position":88.0,"HyperDash":false},{"StartTime":15176.0,"Position":82.0,"HyperDash":false},{"StartTime":15273.0,"Position":88.0,"HyperDash":true}]},{"StartTime":15432.0,"Objects":[{"StartTime":15432.0,"Position":328.0,"HyperDash":false},{"StartTime":15493.0,"Position":369.677216,"HyperDash":false},{"StartTime":15590.0,"Position":423.0,"HyperDash":true}]},{"StartTime":15750.0,"Objects":[{"StartTime":15750.0,"Position":192.0,"HyperDash":false}]},{"StartTime":15908.0,"Objects":[{"StartTime":15908.0,"Position":328.0,"HyperDash":false}]},{"StartTime":16067.0,"Objects":[{"StartTime":16067.0,"Position":192.0,"HyperDash":false},{"StartTime":16128.0,"Position":168.322784,"HyperDash":false},{"StartTime":16225.0,"Position":97.0,"HyperDash":false}]},{"StartTime":16385.0,"Objects":[{"StartTime":16385.0,"Position":232.0,"HyperDash":false}]},{"StartTime":16543.0,"Objects":[{"StartTime":16543.0,"Position":96.0,"HyperDash":true}]},{"StartTime":16702.0,"Objects":[{"StartTime":16702.0,"Position":336.0,"HyperDash":false}]},{"StartTime":16861.0,"Objects":[{"StartTime":16861.0,"Position":200.0,"HyperDash":false},{"StartTime":16922.0,"Position":217.0,"HyperDash":false},{"StartTime":17019.0,"Position":200.0,"HyperDash":true}]},{"StartTime":17178.0,"Objects":[{"StartTime":17178.0,"Position":440.0,"HyperDash":false}]},{"StartTime":17337.0,"Objects":[{"StartTime":17337.0,"Position":304.0,"HyperDash":false}]},{"StartTime":17496.0,"Objects":[{"StartTime":17496.0,"Position":408.0,"HyperDash":false},{"StartTime":17557.0,"Position":461.677216,"HyperDash":false},{"StartTime":17654.0,"Position":503.0,"HyperDash":false}]},{"StartTime":17813.0,"Objects":[{"StartTime":17813.0,"Position":360.0,"HyperDash":false}]},{"StartTime":17972.0,"Objects":[{"StartTime":17972.0,"Position":496.0,"HyperDash":false},{"StartTime":18033.0,"Position":511.0,"HyperDash":false},{"StartTime":18130.0,"Position":496.0,"HyperDash":true}]},{"StartTime":18289.0,"Objects":[{"StartTime":18289.0,"Position":256.0,"HyperDash":false},{"StartTime":18350.0,"Position":236.322784,"HyperDash":false},{"StartTime":18447.0,"Position":161.0,"HyperDash":true}]},{"StartTime":18607.0,"Objects":[{"StartTime":18607.0,"Position":392.0,"HyperDash":false},{"StartTime":18668.0,"Position":401.0,"HyperDash":false},{"StartTime":18765.0,"Position":392.0,"HyperDash":false}]},{"StartTime":18924.0,"Objects":[{"StartTime":18924.0,"Position":256.0,"HyperDash":false}]},{"StartTime":19083.0,"Objects":[{"StartTime":19083.0,"Position":392.0,"HyperDash":true}]},{"StartTime":19242.0,"Objects":[{"StartTime":19242.0,"Position":152.0,"HyperDash":false}]},{"StartTime":19400.0,"Objects":[{"StartTime":19400.0,"Position":288.0,"HyperDash":false},{"StartTime":19461.0,"Position":271.0,"HyperDash":false},{"StartTime":19558.0,"Position":288.0,"HyperDash":true}]},{"StartTime":19718.0,"Objects":[{"StartTime":19718.0,"Position":48.0,"HyperDash":false},{"StartTime":19779.0,"Position":53.0,"HyperDash":false},{"StartTime":19876.0,"Position":48.0,"HyperDash":false}]},{"StartTime":20035.0,"Objects":[{"StartTime":20035.0,"Position":168.0,"HyperDash":false}]},{"StartTime":20194.0,"Objects":[{"StartTime":20194.0,"Position":48.0,"HyperDash":false},{"StartTime":20273.0,"Position":83.30042,"HyperDash":false},{"StartTime":20352.0,"Position":142.600845,"HyperDash":false},{"StartTime":20431.0,"Position":207.90126,"HyperDash":false},{"StartTime":20511.0,"Position":237.800415,"HyperDash":false},{"StartTime":20572.0,"Position":290.323517,"HyperDash":false},{"StartTime":20670.0,"Position":333.0,"HyperDash":true}]},{"StartTime":20829.0,"Objects":[{"StartTime":20829.0,"Position":88.0,"HyperDash":false},{"StartTime":20890.0,"Position":91.0,"HyperDash":false},{"StartTime":20987.0,"Position":88.0,"HyperDash":false}]},{"StartTime":21146.0,"Objects":[{"StartTime":21146.0,"Position":232.0,"HyperDash":false},{"StartTime":21207.0,"Position":222.0,"HyperDash":false},{"StartTime":21304.0,"Position":232.0,"HyperDash":false}]},{"StartTime":21464.0,"Objects":[{"StartTime":21464.0,"Position":88.0,"HyperDash":false},{"StartTime":21525.0,"Position":125.677216,"HyperDash":false},{"StartTime":21622.0,"Position":183.0,"HyperDash":false}]},{"StartTime":21781.0,"Objects":[{"StartTime":21781.0,"Position":320.0,"HyperDash":false}]},{"StartTime":21940.0,"Objects":[{"StartTime":21940.0,"Position":184.0,"HyperDash":false},{"StartTime":22001.0,"Position":174.0,"HyperDash":false},{"StartTime":22098.0,"Position":184.0,"HyperDash":false}]},{"StartTime":22258.0,"Objects":[{"StartTime":22258.0,"Position":320.0,"HyperDash":false},{"StartTime":22319.0,"Position":320.0,"HyperDash":false},{"StartTime":22416.0,"Position":320.0,"HyperDash":false}]},{"StartTime":22575.0,"Objects":[{"StartTime":22575.0,"Position":184.0,"HyperDash":false},{"StartTime":22636.0,"Position":166.0,"HyperDash":false},{"StartTime":22733.0,"Position":184.0,"HyperDash":false}]},{"StartTime":22892.0,"Objects":[{"StartTime":22892.0,"Position":320.0,"HyperDash":false}]},{"StartTime":23051.0,"Objects":[{"StartTime":23051.0,"Position":184.0,"HyperDash":false},{"StartTime":23112.0,"Position":131.322784,"HyperDash":false},{"StartTime":23209.0,"Position":89.0,"HyperDash":true}]},{"StartTime":23369.0,"Objects":[{"StartTime":23369.0,"Position":328.0,"HyperDash":false},{"StartTime":23448.0,"Position":383.3004,"HyperDash":false},{"StartTime":23527.0,"Position":422.60083,"HyperDash":false},{"StartTime":23607.0,"Position":470.5,"HyperDash":false}]},{"StartTime":23686.0,"Objects":[{"StartTime":23686.0,"Position":416.0,"HyperDash":false}]},{"StartTime":23845.0,"Objects":[{"StartTime":23845.0,"Position":280.0,"HyperDash":false},{"StartTime":23924.0,"Position":212.649841,"HyperDash":false},{"StartTime":24003.0,"Position":185.0,"HyperDash":false},{"StartTime":24064.0,"Position":216.261841,"HyperDash":false},{"StartTime":24162.0,"Position":280.0,"HyperDash":false}]},{"StartTime":24321.0,"Objects":[{"StartTime":24321.0,"Position":424.0,"HyperDash":false}]},{"StartTime":24480.0,"Objects":[{"StartTime":24480.0,"Position":288.0,"HyperDash":false},{"StartTime":24559.0,"Position":324.350159,"HyperDash":false},{"StartTime":24638.0,"Position":382.700317,"HyperDash":false},{"StartTime":24699.0,"Position":417.261841,"HyperDash":false},{"StartTime":24797.0,"Position":478.0,"HyperDash":false}]},{"StartTime":24956.0,"Objects":[{"StartTime":24956.0,"Position":360.0,"HyperDash":false}]},{"StartTime":25115.0,"Objects":[{"StartTime":25115.0,"Position":224.0,"HyperDash":false}]},{"StartTime":25273.0,"Objects":[{"StartTime":25273.0,"Position":360.0,"HyperDash":false},{"StartTime":25352.0,"Position":360.0,"HyperDash":false}]},{"StartTime":25432.0,"Objects":[{"StartTime":25432.0,"Position":288.0,"HyperDash":false},{"StartTime":25511.0,"Position":288.0,"HyperDash":true}]},{"StartTime":25591.0,"Objects":[{"StartTime":25591.0,"Position":448.0,"HyperDash":false},{"StartTime":25652.0,"Position":465.0,"HyperDash":false},{"StartTime":25749.0,"Position":448.0,"HyperDash":true}]},{"StartTime":25908.0,"Objects":[{"StartTime":25908.0,"Position":208.0,"HyperDash":false},{"StartTime":25969.0,"Position":154.322784,"HyperDash":false},{"StartTime":26066.0,"Position":113.0,"HyperDash":false}]},{"StartTime":26226.0,"Objects":[{"StartTime":26226.0,"Position":248.0,"HyperDash":false},{"StartTime":26287.0,"Position":289.677216,"HyperDash":false},{"StartTime":26384.0,"Position":343.0,"HyperDash":false}]},{"StartTime":26543.0,"Objects":[{"StartTime":26543.0,"Position":208.0,"HyperDash":false},{"StartTime":26604.0,"Position":227.0,"HyperDash":false},{"StartTime":26701.0,"Position":208.0,"HyperDash":false}]},{"StartTime":26861.0,"Objects":[{"StartTime":26861.0,"Position":344.0,"HyperDash":false}]},{"StartTime":27019.0,"Objects":[{"StartTime":27019.0,"Position":208.0,"HyperDash":false},{"StartTime":27080.0,"Position":181.322784,"HyperDash":false},{"StartTime":27177.0,"Position":113.0,"HyperDash":false}]},{"StartTime":27337.0,"Objects":[{"StartTime":27337.0,"Position":248.0,"HyperDash":false},{"StartTime":27398.0,"Position":294.677216,"HyperDash":false},{"StartTime":27495.0,"Position":343.0,"HyperDash":false}]},{"StartTime":27654.0,"Objects":[{"StartTime":27654.0,"Position":208.0,"HyperDash":false}]},{"StartTime":27813.0,"Objects":[{"StartTime":27813.0,"Position":344.0,"HyperDash":false}]},{"StartTime":27972.0,"Objects":[{"StartTime":27972.0,"Position":208.0,"HyperDash":false}]},{"StartTime":28131.0,"Objects":[{"StartTime":28131.0,"Position":344.0,"HyperDash":false},{"StartTime":28192.0,"Position":384.677216,"HyperDash":false},{"StartTime":28289.0,"Position":439.0,"HyperDash":true}]},{"StartTime":28448.0,"Objects":[{"StartTime":28448.0,"Position":208.0,"HyperDash":false},{"StartTime":28527.0,"Position":167.699585,"HyperDash":false},{"StartTime":28606.0,"Position":113.399155,"HyperDash":false},{"StartTime":28686.0,"Position":65.5,"HyperDash":false}]},{"StartTime":28765.0,"Objects":[{"StartTime":28765.0,"Position":120.0,"HyperDash":false}]},{"StartTime":28924.0,"Objects":[{"StartTime":28924.0,"Position":256.0,"HyperDash":false},{"StartTime":29003.0,"Position":288.350159,"HyperDash":false},{"StartTime":29082.0,"Position":351.0,"HyperDash":false},{"StartTime":29143.0,"Position":311.738159,"HyperDash":false},{"StartTime":29241.0,"Position":256.0,"HyperDash":false}]},{"StartTime":29400.0,"Objects":[{"StartTime":29400.0,"Position":112.0,"HyperDash":false}]},{"StartTime":29559.0,"Objects":[{"StartTime":29559.0,"Position":248.0,"HyperDash":false},{"StartTime":29638.0,"Position":190.649841,"HyperDash":false},{"StartTime":29717.0,"Position":153.299683,"HyperDash":false},{"StartTime":29778.0,"Position":125.738174,"HyperDash":false},{"StartTime":29876.0,"Position":58.0,"HyperDash":false}]},{"StartTime":30035.0,"Objects":[{"StartTime":30035.0,"Position":192.0,"HyperDash":false}]},{"StartTime":30194.0,"Objects":[{"StartTime":30194.0,"Position":328.0,"HyperDash":false}]},{"StartTime":30353.0,"Objects":[{"StartTime":30353.0,"Position":192.0,"HyperDash":false},{"StartTime":30414.0,"Position":196.0,"HyperDash":false},{"StartTime":30511.0,"Position":192.0,"HyperDash":true}]},{"StartTime":30670.0,"Objects":[{"StartTime":30670.0,"Position":432.0,"HyperDash":false},{"StartTime":30749.0,"Position":384.5,"HyperDash":true}]},{"StartTime":30829.0,"Objects":[{"StartTime":30829.0,"Position":192.0,"HyperDash":false},{"StartTime":30908.0,"Position":144.5,"HyperDash":true}]},{"StartTime":30988.0,"Objects":[{"StartTime":30988.0,"Position":336.0,"HyperDash":false},{"StartTime":31049.0,"Position":326.0,"HyperDash":false},{"StartTime":31146.0,"Position":336.0,"HyperDash":false}]},{"StartTime":31305.0,"Objects":[{"StartTime":31305.0,"Position":208.0,"HyperDash":false},{"StartTime":31366.0,"Position":198.0,"HyperDash":false},{"StartTime":31463.0,"Position":208.0,"HyperDash":false}]},{"StartTime":31623.0,"Objects":[{"StartTime":31623.0,"Position":80.0,"HyperDash":false},{"StartTime":31684.0,"Position":87.0,"HyperDash":false},{"StartTime":31781.0,"Position":80.0,"HyperDash":false}]},{"StartTime":31940.0,"Objects":[{"StartTime":31940.0,"Position":208.0,"HyperDash":false}]},{"StartTime":32099.0,"Objects":[{"StartTime":32099.0,"Position":80.0,"HyperDash":false},{"StartTime":32160.0,"Position":130.677216,"HyperDash":false},{"StartTime":32257.0,"Position":175.0,"HyperDash":false}]},{"StartTime":32416.0,"Objects":[{"StartTime":32416.0,"Position":296.0,"HyperDash":false},{"StartTime":32477.0,"Position":303.0,"HyperDash":false},{"StartTime":32574.0,"Position":296.0,"HyperDash":false}]},{"StartTime":32734.0,"Objects":[{"StartTime":32734.0,"Position":176.0,"HyperDash":false},{"StartTime":32795.0,"Position":188.0,"HyperDash":false},{"StartTime":32892.0,"Position":176.0,"HyperDash":false}]},{"StartTime":33051.0,"Objects":[{"StartTime":33051.0,"Position":296.0,"HyperDash":false},{"StartTime":33130.0,"Position":250.649841,"HyperDash":false},{"StartTime":33209.0,"Position":201.0,"HyperDash":false},{"StartTime":33270.0,"Position":218.261841,"HyperDash":false},{"StartTime":33368.0,"Position":296.0,"HyperDash":true}]},{"StartTime":33527.0,"Objects":[{"StartTime":33527.0,"Position":48.0,"HyperDash":false}]},{"StartTime":33686.0,"Objects":[{"StartTime":33686.0,"Position":160.0,"HyperDash":false}]},{"StartTime":33845.0,"Objects":[{"StartTime":33845.0,"Position":272.0,"HyperDash":false}]},{"StartTime":34004.0,"Objects":[{"StartTime":34004.0,"Position":160.0,"HyperDash":false}]},{"StartTime":34162.0,"Objects":[{"StartTime":34162.0,"Position":304.0,"HyperDash":false},{"StartTime":34241.0,"Position":332.587769,"HyperDash":false},{"StartTime":34320.0,"Position":375.266571,"HyperDash":false},{"StartTime":34381.0,"Position":377.239929,"HyperDash":false},{"StartTime":34479.0,"Position":320.985657,"HyperDash":false}]},{"StartTime":34638.0,"Objects":[{"StartTime":34638.0,"Position":184.0,"HyperDash":false},{"StartTime":34717.0,"Position":224.350159,"HyperDash":false},{"StartTime":34796.0,"Position":278.700317,"HyperDash":false},{"StartTime":34857.0,"Position":313.261841,"HyperDash":false},{"StartTime":34955.0,"Position":374.0,"HyperDash":false}]},{"StartTime":35035.0,"Objects":[{"StartTime":35035.0,"Position":440.0,"HyperDash":false}]},{"StartTime":35115.0,"Objects":[{"StartTime":35115.0,"Position":376.0,"HyperDash":false}]},{"StartTime":35273.0,"Objects":[{"StartTime":35273.0,"Position":224.0,"HyperDash":false}]},{"StartTime":35432.0,"Objects":[{"StartTime":35432.0,"Position":368.0,"HyperDash":false},{"StartTime":35511.0,"Position":430.6414,"HyperDash":false},{"StartTime":35590.0,"Position":439.319336,"HyperDash":false},{"StartTime":35651.0,"Position":442.987732,"HyperDash":false},{"StartTime":35749.0,"Position":381.729523,"HyperDash":false}]},{"StartTime":35908.0,"Objects":[{"StartTime":35908.0,"Position":288.0,"HyperDash":true}]},{"StartTime":36067.0,"Objects":[{"StartTime":36067.0,"Position":72.0,"HyperDash":false}]},{"StartTime":36146.0,"Objects":[{"StartTime":36146.0,"Position":16.0,"HyperDash":false}]},{"StartTime":36226.0,"Objects":[{"StartTime":36226.0,"Position":16.0,"HyperDash":false}]},{"StartTime":36305.0,"Objects":[{"StartTime":36305.0,"Position":72.0,"HyperDash":true}]},{"StartTime":36385.0,"Objects":[{"StartTime":36385.0,"Position":264.0,"HyperDash":false}]},{"StartTime":36464.0,"Objects":[{"StartTime":36464.0,"Position":328.0,"HyperDash":false}]},{"StartTime":36543.0,"Objects":[{"StartTime":36543.0,"Position":264.0,"HyperDash":false}]},{"StartTime":36623.0,"Objects":[{"StartTime":36623.0,"Position":200.0,"HyperDash":true}]},{"StartTime":36702.0,"Objects":[{"StartTime":36702.0,"Position":392.0,"HyperDash":false},{"StartTime":36781.0,"Position":439.5,"HyperDash":true}]},{"StartTime":36861.0,"Objects":[{"StartTime":36861.0,"Position":232.0,"HyperDash":false},{"StartTime":36940.0,"Position":226.108353,"HyperDash":false}]},{"StartTime":37019.0,"Objects":[{"StartTime":37019.0,"Position":304.0,"HyperDash":false},{"StartTime":37098.0,"Position":315.520447,"HyperDash":true}]},{"StartTime":37178.0,"Objects":[{"StartTime":37178.0,"Position":104.0,"HyperDash":false},{"StartTime":37257.0,"Position":56.5,"HyperDash":true}]},{"StartTime":37337.0,"Objects":[{"StartTime":37337.0,"Position":264.0,"HyperDash":false},{"StartTime":37416.0,"Position":279.0208,"HyperDash":false}]},{"StartTime":37496.0,"Objects":[{"StartTime":37496.0,"Position":208.0,"HyperDash":false},{"StartTime":37575.0,"Position":201.282486,"HyperDash":true}]},{"StartTime":37654.0,"Objects":[{"StartTime":37654.0,"Position":392.0,"HyperDash":false}]},{"StartTime":37734.0,"Objects":[{"StartTime":37734.0,"Position":448.0,"HyperDash":false}]},{"StartTime":37813.0,"Objects":[{"StartTime":37813.0,"Position":448.0,"HyperDash":false}]},{"StartTime":37892.0,"Objects":[{"StartTime":37892.0,"Position":392.0,"HyperDash":true}]},{"StartTime":37972.0,"Objects":[{"StartTime":37972.0,"Position":192.0,"HyperDash":false},{"StartTime":38051.0,"Position":239.5,"HyperDash":true}]},{"StartTime":38131.0,"Objects":[{"StartTime":38131.0,"Position":410.0,"HyperDash":false},{"StartTime":38210.0,"Position":457.5,"HyperDash":true}]},{"StartTime":38289.0,"Objects":[{"StartTime":38289.0,"Position":264.0,"HyperDash":false},{"StartTime":38368.0,"Position":216.5,"HyperDash":true}]},{"StartTime":38448.0,"Objects":[{"StartTime":38448.0,"Position":448.0,"HyperDash":false},{"StartTime":38527.0,"Position":495.5,"HyperDash":true}]},{"StartTime":38607.0,"Objects":[{"StartTime":38607.0,"Position":296.0,"HyperDash":false}]},{"StartTime":38924.0,"Objects":[{"StartTime":38924.0,"Position":440.0,"HyperDash":false}]},{"StartTime":39242.0,"Objects":[{"StartTime":39242.0,"Position":296.0,"HyperDash":false}]},{"StartTime":39559.0,"Objects":[{"StartTime":39559.0,"Position":152.0,"HyperDash":false}]},{"StartTime":39877.0,"Objects":[{"StartTime":39877.0,"Position":352.0,"HyperDash":false},{"StartTime":39956.0,"Position":285.84314,"HyperDash":false},{"StartTime":40035.0,"Position":257.328156,"HyperDash":false},{"StartTime":40114.0,"Position":311.126862,"HyperDash":false},{"StartTime":40194.0,"Position":352.0,"HyperDash":false},{"StartTime":40273.0,"Position":315.962524,"HyperDash":false},{"StartTime":40353.0,"Position":257.328156,"HyperDash":false},{"StartTime":40432.0,"Position":295.60437,"HyperDash":false},{"StartTime":40511.0,"Position":352.0,"HyperDash":false},{"StartTime":40572.0,"Position":308.8265,"HyperDash":false},{"StartTime":40670.0,"Position":257.328156,"HyperDash":false}]},{"StartTime":40829.0,"Objects":[{"StartTime":40829.0,"Position":432.0,"HyperDash":true},{"StartTime":40890.0,"Position":349.476257,"HyperDash":false},{"StartTime":40987.0,"Position":218.25,"HyperDash":true}]},{"StartTime":41146.0,"Objects":[{"StartTime":41146.0,"Position":440.0,"HyperDash":false},{"StartTime":41225.0,"Position":485.905121,"HyperDash":false},{"StartTime":41304.0,"Position":484.021484,"HyperDash":false},{"StartTime":41384.0,"Position":448.07428,"HyperDash":true}]},{"StartTime":41464.0,"Objects":[{"StartTime":41464.0,"Position":256.0,"HyperDash":false}]},{"StartTime":41623.0,"Objects":[{"StartTime":41623.0,"Position":400.0,"HyperDash":true}]},{"StartTime":41781.0,"Objects":[{"StartTime":41781.0,"Position":168.0,"HyperDash":false},{"StartTime":41842.0,"Position":176.0,"HyperDash":false},{"StartTime":41939.0,"Position":168.0,"HyperDash":true}]},{"StartTime":42099.0,"Objects":[{"StartTime":42099.0,"Position":400.0,"HyperDash":false}]},{"StartTime":42258.0,"Objects":[{"StartTime":42258.0,"Position":256.0,"HyperDash":false},{"StartTime":42319.0,"Position":267.0,"HyperDash":false},{"StartTime":42416.0,"Position":256.0,"HyperDash":false}]},{"StartTime":42575.0,"Objects":[{"StartTime":42575.0,"Position":400.0,"HyperDash":false}]},{"StartTime":42734.0,"Objects":[{"StartTime":42734.0,"Position":256.0,"HyperDash":true}]},{"StartTime":42892.0,"Objects":[{"StartTime":42892.0,"Position":488.0,"HyperDash":false},{"StartTime":42953.0,"Position":480.0,"HyperDash":false},{"StartTime":43050.0,"Position":488.0,"HyperDash":true}]},{"StartTime":43210.0,"Objects":[{"StartTime":43210.0,"Position":256.0,"HyperDash":false}]},{"StartTime":43369.0,"Objects":[{"StartTime":43369.0,"Position":368.0,"HyperDash":false}]},{"StartTime":43527.0,"Objects":[{"StartTime":43527.0,"Position":480.0,"HyperDash":true}]},{"StartTime":43686.0,"Objects":[{"StartTime":43686.0,"Position":256.0,"HyperDash":false},{"StartTime":43747.0,"Position":223.322784,"HyperDash":false},{"StartTime":43844.0,"Position":161.0,"HyperDash":true}]},{"StartTime":44004.0,"Objects":[{"StartTime":44004.0,"Position":392.0,"HyperDash":false}]},{"StartTime":44162.0,"Objects":[{"StartTime":44162.0,"Position":248.0,"HyperDash":false},{"StartTime":44223.0,"Position":260.0,"HyperDash":false},{"StartTime":44320.0,"Position":248.0,"HyperDash":true}]},{"StartTime":44480.0,"Objects":[{"StartTime":44480.0,"Position":480.0,"HyperDash":false},{"StartTime":44541.0,"Position":475.0,"HyperDash":false},{"StartTime":44638.0,"Position":480.0,"HyperDash":true}]},{"StartTime":44797.0,"Objects":[{"StartTime":44797.0,"Position":248.0,"HyperDash":false},{"StartTime":44858.0,"Position":285.677216,"HyperDash":false},{"StartTime":44955.0,"Position":343.0,"HyperDash":true}]},{"StartTime":45115.0,"Objects":[{"StartTime":45115.0,"Position":104.0,"HyperDash":false},{"StartTime":45194.0,"Position":104.0,"HyperDash":true}]},{"StartTime":45273.0,"Objects":[{"StartTime":45273.0,"Position":296.0,"HyperDash":false}]},{"StartTime":45432.0,"Objects":[{"StartTime":45432.0,"Position":160.0,"HyperDash":true}]},{"StartTime":45591.0,"Objects":[{"StartTime":45591.0,"Position":392.0,"HyperDash":false},{"StartTime":45652.0,"Position":379.0,"HyperDash":false},{"StartTime":45749.0,"Position":392.0,"HyperDash":true}]},{"StartTime":45908.0,"Objects":[{"StartTime":45908.0,"Position":160.0,"HyperDash":true},{"StartTime":45969.0,"Position":245.523743,"HyperDash":false},{"StartTime":46066.0,"Position":373.75,"HyperDash":true}]},{"StartTime":46226.0,"Objects":[{"StartTime":46226.0,"Position":136.0,"HyperDash":false},{"StartTime":46305.0,"Position":111.869118,"HyperDash":false},{"StartTime":46384.0,"Position":80.52514,"HyperDash":false},{"StartTime":46464.0,"Position":110.225082,"HyperDash":true}]},{"StartTime":46543.0,"Objects":[{"StartTime":46543.0,"Position":304.0,"HyperDash":false}]},{"StartTime":46702.0,"Objects":[{"StartTime":46702.0,"Position":160.0,"HyperDash":true}]},{"StartTime":46861.0,"Objects":[{"StartTime":46861.0,"Position":400.0,"HyperDash":false},{"StartTime":46922.0,"Position":400.0,"HyperDash":false},{"StartTime":47019.0,"Position":400.0,"HyperDash":true}]},{"StartTime":47178.0,"Objects":[{"StartTime":47178.0,"Position":160.0,"HyperDash":false}]},{"StartTime":47337.0,"Objects":[{"StartTime":47337.0,"Position":296.0,"HyperDash":false},{"StartTime":47398.0,"Position":314.677216,"HyperDash":false},{"StartTime":47495.0,"Position":391.0,"HyperDash":false}]},{"StartTime":47654.0,"Objects":[{"StartTime":47654.0,"Position":248.0,"HyperDash":false}]},{"StartTime":47734.0,"Objects":[{"StartTime":47734.0,"Position":304.0,"HyperDash":false}]},{"StartTime":47813.0,"Objects":[{"StartTime":47813.0,"Position":360.0,"HyperDash":true}]},{"StartTime":47972.0,"Objects":[{"StartTime":47972.0,"Position":136.0,"HyperDash":false},{"StartTime":48033.0,"Position":122.0,"HyperDash":false},{"StartTime":48130.0,"Position":136.0,"HyperDash":true}]},{"StartTime":48289.0,"Objects":[{"StartTime":48289.0,"Position":376.0,"HyperDash":false}]},{"StartTime":48448.0,"Objects":[{"StartTime":48448.0,"Position":264.0,"HyperDash":false}]},{"StartTime":48607.0,"Objects":[{"StartTime":48607.0,"Position":152.0,"HyperDash":true}]},{"StartTime":48765.0,"Objects":[{"StartTime":48765.0,"Position":392.0,"HyperDash":false},{"StartTime":48826.0,"Position":391.0,"HyperDash":false},{"StartTime":48923.0,"Position":392.0,"HyperDash":true}]},{"StartTime":49083.0,"Objects":[{"StartTime":49083.0,"Position":160.0,"HyperDash":false}]},{"StartTime":49241.0,"Objects":[{"StartTime":49241.0,"Position":304.0,"HyperDash":false},{"StartTime":49302.0,"Position":321.0,"HyperDash":false},{"StartTime":49399.0,"Position":304.0,"HyperDash":true}]},{"StartTime":49559.0,"Objects":[{"StartTime":49559.0,"Position":64.0,"HyperDash":false},{"StartTime":49620.0,"Position":76.0,"HyperDash":false},{"StartTime":49717.0,"Position":64.0,"HyperDash":true}]},{"StartTime":49877.0,"Objects":[{"StartTime":49877.0,"Position":304.0,"HyperDash":false},{"StartTime":49938.0,"Position":278.322784,"HyperDash":false},{"StartTime":50035.0,"Position":209.0,"HyperDash":true}]},{"StartTime":50194.0,"Objects":[{"StartTime":50194.0,"Position":448.0,"HyperDash":false},{"StartTime":50255.0,"Position":446.0,"HyperDash":false},{"StartTime":50352.0,"Position":448.0,"HyperDash":true}]},{"StartTime":50511.0,"Objects":[{"StartTime":50511.0,"Position":208.0,"HyperDash":false},{"StartTime":50590.0,"Position":160.5,"HyperDash":true}]},{"StartTime":50670.0,"Objects":[{"StartTime":50670.0,"Position":352.0,"HyperDash":false},{"StartTime":50731.0,"Position":369.0,"HyperDash":false},{"StartTime":50828.0,"Position":352.0,"HyperDash":true}]},{"StartTime":50988.0,"Objects":[{"StartTime":50988.0,"Position":128.0,"HyperDash":true},{"StartTime":51049.0,"Position":201.523743,"HyperDash":false},{"StartTime":51146.0,"Position":341.75,"HyperDash":true}]},{"StartTime":51305.0,"Objects":[{"StartTime":51305.0,"Position":104.0,"HyperDash":false},{"StartTime":51384.0,"Position":76.12657,"HyperDash":false},{"StartTime":51463.0,"Position":49.38173,"HyperDash":false},{"StartTime":51543.0,"Position":79.5740662,"HyperDash":true}]},{"StartTime":51623.0,"Objects":[{"StartTime":51623.0,"Position":272.0,"HyperDash":false}]},{"StartTime":51781.0,"Objects":[{"StartTime":51781.0,"Position":128.0,"HyperDash":true}]},{"StartTime":51940.0,"Objects":[{"StartTime":51940.0,"Position":368.0,"HyperDash":false},{"StartTime":52001.0,"Position":357.0,"HyperDash":false},{"StartTime":52098.0,"Position":368.0,"HyperDash":true}]},{"StartTime":52258.0,"Objects":[{"StartTime":52258.0,"Position":128.0,"HyperDash":false}]},{"StartTime":52416.0,"Objects":[{"StartTime":52416.0,"Position":272.0,"HyperDash":false},{"StartTime":52477.0,"Position":276.0,"HyperDash":false},{"StartTime":52574.0,"Position":272.0,"HyperDash":false}]},{"StartTime":52734.0,"Objects":[{"StartTime":52734.0,"Position":128.0,"HyperDash":false}]},{"StartTime":52813.0,"Objects":[{"StartTime":52813.0,"Position":184.0,"HyperDash":false}]},{"StartTime":52892.0,"Objects":[{"StartTime":52892.0,"Position":240.0,"HyperDash":true}]},{"StartTime":53051.0,"Objects":[{"StartTime":53051.0,"Position":16.0,"HyperDash":false},{"StartTime":53112.0,"Position":4.0,"HyperDash":false},{"StartTime":53209.0,"Position":16.0,"HyperDash":true}]},{"StartTime":53369.0,"Objects":[{"StartTime":53369.0,"Position":264.0,"HyperDash":false}]},{"StartTime":53527.0,"Objects":[{"StartTime":53527.0,"Position":152.0,"HyperDash":false}]},{"StartTime":53686.0,"Objects":[{"StartTime":53686.0,"Position":40.0,"HyperDash":true}]},{"StartTime":53845.0,"Objects":[{"StartTime":53845.0,"Position":280.0,"HyperDash":false},{"StartTime":53906.0,"Position":296.0,"HyperDash":false},{"StartTime":54003.0,"Position":280.0,"HyperDash":true}]},{"StartTime":54162.0,"Objects":[{"StartTime":54162.0,"Position":56.0,"HyperDash":false}]},{"StartTime":54321.0,"Objects":[{"StartTime":54321.0,"Position":184.0,"HyperDash":false},{"StartTime":54382.0,"Position":208.677216,"HyperDash":false},{"StartTime":54479.0,"Position":279.0,"HyperDash":true}]},{"StartTime":54638.0,"Objects":[{"StartTime":54638.0,"Position":32.0,"HyperDash":false},{"StartTime":54699.0,"Position":31.0,"HyperDash":false},{"StartTime":54796.0,"Position":32.0,"HyperDash":true}]},{"StartTime":54956.0,"Objects":[{"StartTime":54956.0,"Position":264.0,"HyperDash":false},{"StartTime":55017.0,"Position":287.677216,"HyperDash":false},{"StartTime":55114.0,"Position":359.0,"HyperDash":true}]},{"StartTime":55274.0,"Objects":[{"StartTime":55274.0,"Position":120.0,"HyperDash":false},{"StartTime":55353.0,"Position":120.0,"HyperDash":true}]},{"StartTime":55432.0,"Objects":[{"StartTime":55432.0,"Position":312.0,"HyperDash":false}]},{"StartTime":55591.0,"Objects":[{"StartTime":55591.0,"Position":176.0,"HyperDash":true}]},{"StartTime":55750.0,"Objects":[{"StartTime":55750.0,"Position":408.0,"HyperDash":false},{"StartTime":55811.0,"Position":402.0,"HyperDash":false},{"StartTime":55908.0,"Position":408.0,"HyperDash":true}]},{"StartTime":56067.0,"Objects":[{"StartTime":56067.0,"Position":136.0,"HyperDash":true},{"StartTime":56128.0,"Position":210.523743,"HyperDash":false},{"StartTime":56225.0,"Position":349.75,"HyperDash":true}]},{"StartTime":56385.0,"Objects":[{"StartTime":56385.0,"Position":112.0,"HyperDash":false},{"StartTime":56464.0,"Position":63.0948868,"HyperDash":false},{"StartTime":56543.0,"Position":67.97851,"HyperDash":false},{"StartTime":56623.0,"Position":103.92572,"HyperDash":true}]},{"StartTime":56702.0,"Objects":[{"StartTime":56702.0,"Position":296.0,"HyperDash":false}]},{"StartTime":56861.0,"Objects":[{"StartTime":56861.0,"Position":152.0,"HyperDash":true}]},{"StartTime":57019.0,"Objects":[{"StartTime":57019.0,"Position":392.0,"HyperDash":false},{"StartTime":57080.0,"Position":387.0,"HyperDash":false},{"StartTime":57177.0,"Position":392.0,"HyperDash":true}]},{"StartTime":57337.0,"Objects":[{"StartTime":57337.0,"Position":152.0,"HyperDash":false}]},{"StartTime":57496.0,"Objects":[{"StartTime":57496.0,"Position":296.0,"HyperDash":false},{"StartTime":57557.0,"Position":346.677216,"HyperDash":false},{"StartTime":57654.0,"Position":391.0,"HyperDash":false}]},{"StartTime":57813.0,"Objects":[{"StartTime":57813.0,"Position":248.0,"HyperDash":false}]},{"StartTime":57972.0,"Objects":[{"StartTime":57972.0,"Position":392.0,"HyperDash":true}]},{"StartTime":58131.0,"Objects":[{"StartTime":58131.0,"Position":152.0,"HyperDash":false},{"StartTime":58192.0,"Position":155.0,"HyperDash":false},{"StartTime":58289.0,"Position":152.0,"HyperDash":true}]},{"StartTime":58448.0,"Objects":[{"StartTime":58448.0,"Position":392.0,"HyperDash":false}]},{"StartTime":58607.0,"Objects":[{"StartTime":58607.0,"Position":280.0,"HyperDash":false}]},{"StartTime":58765.0,"Objects":[{"StartTime":58765.0,"Position":168.0,"HyperDash":true}]},{"StartTime":58924.0,"Objects":[{"StartTime":58924.0,"Position":392.0,"HyperDash":false}]},{"StartTime":59083.0,"Objects":[{"StartTime":59083.0,"Position":248.0,"HyperDash":false},{"StartTime":59144.0,"Position":236.0,"HyperDash":false},{"StartTime":59241.0,"Position":248.0,"HyperDash":true}]},{"StartTime":59400.0,"Objects":[{"StartTime":59400.0,"Position":488.0,"HyperDash":false},{"StartTime":59461.0,"Position":476.0,"HyperDash":false},{"StartTime":59558.0,"Position":488.0,"HyperDash":true}]},{"StartTime":59718.0,"Objects":[{"StartTime":59718.0,"Position":248.0,"HyperDash":false},{"StartTime":59779.0,"Position":233.0,"HyperDash":false},{"StartTime":59876.0,"Position":248.0,"HyperDash":true}]},{"StartTime":60035.0,"Objects":[{"StartTime":60035.0,"Position":488.0,"HyperDash":false},{"StartTime":60114.0,"Position":436.649841,"HyperDash":false},{"StartTime":60193.0,"Position":393.299683,"HyperDash":false},{"StartTime":60254.0,"Position":337.738159,"HyperDash":false},{"StartTime":60352.0,"Position":298.0,"HyperDash":false}]},{"StartTime":60511.0,"Objects":[{"StartTime":60511.0,"Position":448.0,"HyperDash":false},{"StartTime":60572.0,"Position":448.0,"HyperDash":false},{"StartTime":60669.0,"Position":448.0,"HyperDash":true}]},{"StartTime":60829.0,"Objects":[{"StartTime":60829.0,"Position":200.0,"HyperDash":true}]},{"StartTime":60988.0,"Objects":[{"StartTime":60988.0,"Position":448.0,"HyperDash":false},{"StartTime":61067.0,"Position":495.5,"HyperDash":true}]},{"StartTime":61146.0,"Objects":[{"StartTime":61146.0,"Position":304.0,"HyperDash":false},{"StartTime":61225.0,"Position":256.5,"HyperDash":true}]},{"StartTime":61305.0,"Objects":[{"StartTime":61305.0,"Position":448.0,"HyperDash":false},{"StartTime":61384.0,"Position":495.5,"HyperDash":true}]},{"StartTime":61464.0,"Objects":[{"StartTime":61464.0,"Position":304.0,"HyperDash":false},{"StartTime":61543.0,"Position":273.8691,"HyperDash":false},{"StartTime":61622.0,"Position":248.525131,"HyperDash":false},{"StartTime":61702.0,"Position":278.225067,"HyperDash":true}]},{"StartTime":61781.0,"Objects":[{"StartTime":61781.0,"Position":448.0,"HyperDash":false},{"StartTime":61860.0,"Position":503.905121,"HyperDash":false},{"StartTime":61939.0,"Position":492.021484,"HyperDash":false},{"StartTime":62019.0,"Position":456.07428,"HyperDash":true}]},{"StartTime":62099.0,"Objects":[{"StartTime":62099.0,"Position":272.0,"HyperDash":false}]},{"StartTime":62258.0,"Objects":[{"StartTime":62258.0,"Position":408.0,"HyperDash":true}]},{"StartTime":62416.0,"Objects":[{"StartTime":62416.0,"Position":168.0,"HyperDash":false}]},{"StartTime":62575.0,"Objects":[{"StartTime":62575.0,"Position":312.0,"HyperDash":false},{"StartTime":62636.0,"Position":301.0,"HyperDash":false},{"StartTime":62733.0,"Position":312.0,"HyperDash":true}]},{"StartTime":62892.0,"Objects":[{"StartTime":62892.0,"Position":72.0,"HyperDash":false},{"StartTime":62953.0,"Position":58.0,"HyperDash":false},{"StartTime":63050.0,"Position":72.0,"HyperDash":true}]},{"StartTime":63210.0,"Objects":[{"StartTime":63210.0,"Position":312.0,"HyperDash":false}]},{"StartTime":63369.0,"Objects":[{"StartTime":63369.0,"Position":176.0,"HyperDash":false},{"StartTime":63430.0,"Position":194.0,"HyperDash":false},{"StartTime":63527.0,"Position":176.0,"HyperDash":false}]},{"StartTime":63686.0,"Objects":[{"StartTime":63686.0,"Position":312.0,"HyperDash":false}]},{"StartTime":63845.0,"Objects":[{"StartTime":63845.0,"Position":176.0,"HyperDash":true}]},{"StartTime":64004.0,"Objects":[{"StartTime":64004.0,"Position":408.0,"HyperDash":false},{"StartTime":64083.0,"Position":460.8734,"HyperDash":false},{"StartTime":64162.0,"Position":462.618256,"HyperDash":false},{"StartTime":64242.0,"Position":432.4259,"HyperDash":true}]},{"StartTime":64321.0,"Objects":[{"StartTime":64321.0,"Position":240.0,"HyperDash":false}]},{"StartTime":64480.0,"Objects":[{"StartTime":64480.0,"Position":376.0,"HyperDash":true}]},{"StartTime":64638.0,"Objects":[{"StartTime":64638.0,"Position":136.0,"HyperDash":false},{"StartTime":64699.0,"Position":119.0,"HyperDash":false},{"StartTime":64796.0,"Position":136.0,"HyperDash":false}]},{"StartTime":64956.0,"Objects":[{"StartTime":64956.0,"Position":272.0,"HyperDash":true}]},{"StartTime":65115.0,"Objects":[{"StartTime":65115.0,"Position":32.0,"HyperDash":false},{"StartTime":65176.0,"Position":26.0,"HyperDash":false},{"StartTime":65273.0,"Position":32.0,"HyperDash":true}]},{"StartTime":65432.0,"Objects":[{"StartTime":65432.0,"Position":272.0,"HyperDash":false},{"StartTime":65493.0,"Position":314.677216,"HyperDash":false},{"StartTime":65590.0,"Position":367.0,"HyperDash":true}]},{"StartTime":65750.0,"Objects":[{"StartTime":65750.0,"Position":128.0,"HyperDash":false}]},{"StartTime":65908.0,"Objects":[{"StartTime":65908.0,"Position":264.0,"HyperDash":false}]},{"StartTime":66067.0,"Objects":[{"StartTime":66067.0,"Position":128.0,"HyperDash":false},{"StartTime":66128.0,"Position":140.0,"HyperDash":false},{"StartTime":66225.0,"Position":128.0,"HyperDash":false}]},{"StartTime":66385.0,"Objects":[{"StartTime":66385.0,"Position":264.0,"HyperDash":true}]},{"StartTime":66543.0,"Objects":[{"StartTime":66543.0,"Position":32.0,"HyperDash":false},{"StartTime":66604.0,"Position":19.0,"HyperDash":false},{"StartTime":66701.0,"Position":32.0,"HyperDash":true}]},{"StartTime":66861.0,"Objects":[{"StartTime":66861.0,"Position":280.0,"HyperDash":false}]},{"StartTime":67019.0,"Objects":[{"StartTime":67019.0,"Position":144.0,"HyperDash":false}]},{"StartTime":67178.0,"Objects":[{"StartTime":67178.0,"Position":280.0,"HyperDash":false},{"StartTime":67239.0,"Position":302.677216,"HyperDash":false},{"StartTime":67336.0,"Position":375.0,"HyperDash":true}]},{"StartTime":67496.0,"Objects":[{"StartTime":67496.0,"Position":136.0,"HyperDash":false}]},{"StartTime":67654.0,"Objects":[{"StartTime":67654.0,"Position":272.0,"HyperDash":false},{"StartTime":67733.0,"Position":292.355682,"HyperDash":false},{"StartTime":67812.0,"Position":317.325684,"HyperDash":false},{"StartTime":67892.0,"Position":284.836639,"HyperDash":true}]},{"StartTime":67972.0,"Objects":[{"StartTime":67972.0,"Position":96.0,"HyperDash":false},{"StartTime":68033.0,"Position":82.0,"HyperDash":false},{"StartTime":68130.0,"Position":96.0,"HyperDash":true}]},{"StartTime":68289.0,"Objects":[{"StartTime":68289.0,"Position":328.0,"HyperDash":false}]},{"StartTime":68448.0,"Objects":[{"StartTime":68448.0,"Position":192.0,"HyperDash":false}]},{"StartTime":68607.0,"Objects":[{"StartTime":68607.0,"Position":328.0,"HyperDash":true}]},{"StartTime":68765.0,"Objects":[{"StartTime":68765.0,"Position":96.0,"HyperDash":false}]},{"StartTime":68924.0,"Objects":[{"StartTime":68924.0,"Position":232.0,"HyperDash":true}]},{"StartTime":69083.0,"Objects":[{"StartTime":69083.0,"Position":472.0,"HyperDash":false},{"StartTime":69144.0,"Position":478.0,"HyperDash":false},{"StartTime":69241.0,"Position":472.0,"HyperDash":false}]},{"StartTime":69400.0,"Objects":[{"StartTime":69400.0,"Position":368.0,"HyperDash":true}]},{"StartTime":69559.0,"Objects":[{"StartTime":69559.0,"Position":152.0,"HyperDash":false}]},{"StartTime":69718.0,"Objects":[{"StartTime":69718.0,"Position":288.0,"HyperDash":false}]},{"StartTime":69877.0,"Objects":[{"StartTime":69877.0,"Position":152.0,"HyperDash":true}]},{"StartTime":70035.0,"Objects":[{"StartTime":70035.0,"Position":384.0,"HyperDash":false}]},{"StartTime":70194.0,"Objects":[{"StartTime":70194.0,"Position":248.0,"HyperDash":false},{"StartTime":70255.0,"Position":261.0,"HyperDash":false},{"StartTime":70352.0,"Position":248.0,"HyperDash":false}]},{"StartTime":70511.0,"Objects":[{"StartTime":70511.0,"Position":384.0,"HyperDash":false}]},{"StartTime":70670.0,"Objects":[{"StartTime":70670.0,"Position":248.0,"HyperDash":false},{"StartTime":70749.0,"Position":194.869125,"HyperDash":false},{"StartTime":70828.0,"Position":192.525131,"HyperDash":false},{"StartTime":70908.0,"Position":222.225082,"HyperDash":true}]},{"StartTime":70988.0,"Objects":[{"StartTime":70988.0,"Position":416.0,"HyperDash":false},{"StartTime":71049.0,"Position":426.0,"HyperDash":false},{"StartTime":71146.0,"Position":416.0,"HyperDash":false}]},{"StartTime":71226.0,"Objects":[{"StartTime":71226.0,"Position":352.0,"HyperDash":true}]},{"StartTime":71305.0,"Objects":[{"StartTime":71305.0,"Position":168.0,"HyperDash":false},{"StartTime":71384.0,"Position":120.5,"HyperDash":true}]},{"StartTime":71464.0,"Objects":[{"StartTime":71464.0,"Position":312.0,"HyperDash":false},{"StartTime":71543.0,"Position":359.5,"HyperDash":true}]},{"StartTime":71623.0,"Objects":[{"StartTime":71623.0,"Position":168.0,"HyperDash":false},{"StartTime":71684.0,"Position":140.322784,"HyperDash":false},{"StartTime":71781.0,"Position":73.0,"HyperDash":true}]},{"StartTime":71940.0,"Objects":[{"StartTime":71940.0,"Position":312.0,"HyperDash":false}]},{"StartTime":72099.0,"Objects":[{"StartTime":72099.0,"Position":168.0,"HyperDash":false},{"StartTime":72160.0,"Position":138.322784,"HyperDash":false},{"StartTime":72257.0,"Position":73.0,"HyperDash":true}]},{"StartTime":72416.0,"Objects":[{"StartTime":72416.0,"Position":312.0,"HyperDash":false},{"StartTime":72477.0,"Position":310.0,"HyperDash":false},{"StartTime":72574.0,"Position":312.0,"HyperDash":false}]},{"StartTime":72734.0,"Objects":[{"StartTime":72734.0,"Position":176.0,"HyperDash":true}]},{"StartTime":72892.0,"Objects":[{"StartTime":72892.0,"Position":416.0,"HyperDash":false}]},{"StartTime":73051.0,"Objects":[{"StartTime":73051.0,"Position":280.0,"HyperDash":false},{"StartTime":73112.0,"Position":286.0,"HyperDash":false},{"StartTime":73209.0,"Position":280.0,"HyperDash":false}]},{"StartTime":73369.0,"Objects":[{"StartTime":73369.0,"Position":416.0,"HyperDash":true}]},{"StartTime":73527.0,"Objects":[{"StartTime":73527.0,"Position":176.0,"HyperDash":false},{"StartTime":73606.0,"Position":130.644318,"HyperDash":false},{"StartTime":73685.0,"Position":130.674316,"HyperDash":false},{"StartTime":73765.0,"Position":163.163345,"HyperDash":true}]},{"StartTime":73845.0,"Objects":[{"StartTime":73845.0,"Position":352.0,"HyperDash":false},{"StartTime":73906.0,"Position":371.0,"HyperDash":false},{"StartTime":74003.0,"Position":352.0,"HyperDash":true}]},{"StartTime":74162.0,"Objects":[{"StartTime":74162.0,"Position":104.0,"HyperDash":false}]},{"StartTime":74321.0,"Objects":[{"StartTime":74321.0,"Position":240.0,"HyperDash":false},{"StartTime":74382.0,"Position":235.0,"HyperDash":false},{"StartTime":74479.0,"Position":240.0,"HyperDash":false}]},{"StartTime":74638.0,"Objects":[{"StartTime":74638.0,"Position":104.0,"HyperDash":true}]},{"StartTime":74797.0,"Objects":[{"StartTime":74797.0,"Position":344.0,"HyperDash":false}]},{"StartTime":74956.0,"Objects":[{"StartTime":74956.0,"Position":208.0,"HyperDash":false}]},{"StartTime":75115.0,"Objects":[{"StartTime":75115.0,"Position":344.0,"HyperDash":true}]},{"StartTime":75273.0,"Objects":[{"StartTime":75273.0,"Position":104.0,"HyperDash":false},{"StartTime":75334.0,"Position":104.0,"HyperDash":false},{"StartTime":75431.0,"Position":104.0,"HyperDash":false}]},{"StartTime":75591.0,"Objects":[{"StartTime":75591.0,"Position":240.0,"HyperDash":true}]},{"StartTime":75750.0,"Objects":[{"StartTime":75750.0,"Position":16.0,"HyperDash":false}]},{"StartTime":75908.0,"Objects":[{"StartTime":75908.0,"Position":152.0,"HyperDash":false}]},{"StartTime":76067.0,"Objects":[{"StartTime":76067.0,"Position":16.0,"HyperDash":false},{"StartTime":76128.0,"Position":31.0,"HyperDash":false},{"StartTime":76225.0,"Position":16.0,"HyperDash":true}]},{"StartTime":76385.0,"Objects":[{"StartTime":76385.0,"Position":256.0,"HyperDash":false},{"StartTime":76446.0,"Position":276.677216,"HyperDash":false},{"StartTime":76543.0,"Position":351.0,"HyperDash":true}]},{"StartTime":76702.0,"Objects":[{"StartTime":76702.0,"Position":112.0,"HyperDash":false}]},{"StartTime":76861.0,"Objects":[{"StartTime":76861.0,"Position":248.0,"HyperDash":false}]},{"StartTime":77019.0,"Objects":[{"StartTime":77019.0,"Position":112.0,"HyperDash":false},{"StartTime":77080.0,"Position":129.0,"HyperDash":false},{"StartTime":77177.0,"Position":112.0,"HyperDash":false}]},{"StartTime":77258.0,"Objects":[{"StartTime":77258.0,"Position":176.0,"HyperDash":true}]},{"StartTime":77337.0,"Objects":[{"StartTime":77337.0,"Position":368.0,"HyperDash":false},{"StartTime":77398.0,"Position":371.0,"HyperDash":false},{"StartTime":77495.0,"Position":368.0,"HyperDash":false}]},{"StartTime":77654.0,"Objects":[{"StartTime":77654.0,"Position":232.0,"HyperDash":false}]},{"StartTime":77813.0,"Objects":[{"StartTime":77813.0,"Position":368.0,"HyperDash":true}]},{"StartTime":77972.0,"Objects":[{"StartTime":77972.0,"Position":80.0,"HyperDash":false}]},{"StartTime":79242.0,"Objects":[{"StartTime":79242.0,"Position":64.0,"HyperDash":false},{"StartTime":79303.0,"Position":60.0,"HyperDash":false},{"StartTime":79400.0,"Position":64.0,"HyperDash":true}]},{"StartTime":79559.0,"Objects":[{"StartTime":79559.0,"Position":296.0,"HyperDash":false}]},{"StartTime":79718.0,"Objects":[{"StartTime":79718.0,"Position":160.0,"HyperDash":false}]},{"StartTime":79876.0,"Objects":[{"StartTime":79876.0,"Position":296.0,"HyperDash":false},{"StartTime":79937.0,"Position":304.0,"HyperDash":false},{"StartTime":80034.0,"Position":296.0,"HyperDash":true}]},{"StartTime":80194.0,"Objects":[{"StartTime":80194.0,"Position":64.0,"HyperDash":true}]},{"StartTime":80353.0,"Objects":[{"StartTime":80353.0,"Position":296.0,"HyperDash":false},{"StartTime":80432.0,"Position":340.5878,"HyperDash":false},{"StartTime":80511.0,"Position":367.266571,"HyperDash":false},{"StartTime":80572.0,"Position":349.239929,"HyperDash":false},{"StartTime":80670.0,"Position":312.985657,"HyperDash":false}]},{"StartTime":80749.0,"Objects":[{"StartTime":80749.0,"Position":256.0,"HyperDash":true}]},{"StartTime":80829.0,"Objects":[{"StartTime":80829.0,"Position":64.0,"HyperDash":false}]},{"StartTime":80988.0,"Objects":[{"StartTime":80988.0,"Position":200.0,"HyperDash":false}]},{"StartTime":81146.0,"Objects":[{"StartTime":81146.0,"Position":64.0,"HyperDash":false},{"StartTime":81207.0,"Position":48.0,"HyperDash":false},{"StartTime":81304.0,"Position":64.0,"HyperDash":true}]},{"StartTime":81464.0,"Objects":[{"StartTime":81464.0,"Position":296.0,"HyperDash":false},{"StartTime":81525.0,"Position":325.677216,"HyperDash":false},{"StartTime":81622.0,"Position":391.0,"HyperDash":true}]},{"StartTime":81781.0,"Objects":[{"StartTime":81781.0,"Position":152.0,"HyperDash":false},{"StartTime":81842.0,"Position":97.3227844,"HyperDash":false},{"StartTime":81939.0,"Position":57.0,"HyperDash":true}]},{"StartTime":82099.0,"Objects":[{"StartTime":82099.0,"Position":296.0,"HyperDash":false}]},{"StartTime":82257.0,"Objects":[{"StartTime":82257.0,"Position":160.0,"HyperDash":false}]},{"StartTime":82416.0,"Objects":[{"StartTime":82416.0,"Position":296.0,"HyperDash":false},{"StartTime":82477.0,"Position":292.0,"HyperDash":false},{"StartTime":82574.0,"Position":296.0,"HyperDash":true}]},{"StartTime":82734.0,"Objects":[{"StartTime":82734.0,"Position":48.0,"HyperDash":true}]},{"StartTime":82892.0,"Objects":[{"StartTime":82892.0,"Position":296.0,"HyperDash":false},{"StartTime":82971.0,"Position":253.649841,"HyperDash":false},{"StartTime":83050.0,"Position":201.299683,"HyperDash":false},{"StartTime":83111.0,"Position":162.738174,"HyperDash":false},{"StartTime":83209.0,"Position":106.0,"HyperDash":false}]},{"StartTime":83289.0,"Objects":[{"StartTime":83289.0,"Position":160.0,"HyperDash":true}]},{"StartTime":83368.0,"Objects":[{"StartTime":83368.0,"Position":352.0,"HyperDash":false}]},{"StartTime":83527.0,"Objects":[{"StartTime":83527.0,"Position":216.0,"HyperDash":false}]},{"StartTime":83686.0,"Objects":[{"StartTime":83686.0,"Position":352.0,"HyperDash":false},{"StartTime":83747.0,"Position":368.0,"HyperDash":false},{"StartTime":83844.0,"Position":352.0,"HyperDash":true}]},{"StartTime":84003.0,"Objects":[{"StartTime":84003.0,"Position":120.0,"HyperDash":false},{"StartTime":84064.0,"Position":80.3227844,"HyperDash":false},{"StartTime":84161.0,"Position":25.0,"HyperDash":true}]},{"StartTime":84321.0,"Objects":[{"StartTime":84321.0,"Position":264.0,"HyperDash":false}]},{"StartTime":84480.0,"Objects":[{"StartTime":84480.0,"Position":128.0,"HyperDash":true}]},{"StartTime":84638.0,"Objects":[{"StartTime":84638.0,"Position":368.0,"HyperDash":false}]},{"StartTime":84797.0,"Objects":[{"StartTime":84797.0,"Position":464.0,"HyperDash":false}]},{"StartTime":84956.0,"Objects":[{"StartTime":84956.0,"Position":464.0,"HyperDash":false}]},{"StartTime":85115.0,"Objects":[{"StartTime":85115.0,"Position":368.0,"HyperDash":false}]},{"StartTime":85273.0,"Objects":[{"StartTime":85273.0,"Position":232.0,"HyperDash":true}]},{"StartTime":85432.0,"Objects":[{"StartTime":85432.0,"Position":472.0,"HyperDash":false},{"StartTime":85493.0,"Position":486.0,"HyperDash":false},{"StartTime":85590.0,"Position":472.0,"HyperDash":true}]},{"StartTime":85750.0,"Objects":[{"StartTime":85750.0,"Position":232.0,"HyperDash":false},{"StartTime":85811.0,"Position":219.0,"HyperDash":false},{"StartTime":85908.0,"Position":232.0,"HyperDash":false}]},{"StartTime":86067.0,"Objects":[{"StartTime":86067.0,"Position":368.0,"HyperDash":false}]},{"StartTime":86226.0,"Objects":[{"StartTime":86226.0,"Position":232.0,"HyperDash":false},{"StartTime":86287.0,"Position":194.322784,"HyperDash":false},{"StartTime":86384.0,"Position":137.0,"HyperDash":false}]},{"StartTime":86543.0,"Objects":[{"StartTime":86543.0,"Position":272.0,"HyperDash":false},{"StartTime":86604.0,"Position":296.677216,"HyperDash":false},{"StartTime":86701.0,"Position":367.0,"HyperDash":true}]},{"StartTime":86861.0,"Objects":[{"StartTime":86861.0,"Position":128.0,"HyperDash":false}]},{"StartTime":87019.0,"Objects":[{"StartTime":87019.0,"Position":264.0,"HyperDash":true}]},{"StartTime":87178.0,"Objects":[{"StartTime":87178.0,"Position":24.0,"HyperDash":false}]},{"StartTime":87337.0,"Objects":[{"StartTime":87337.0,"Position":24.0,"HyperDash":false}]},{"StartTime":87496.0,"Objects":[{"StartTime":87496.0,"Position":160.0,"HyperDash":false}]},{"StartTime":87654.0,"Objects":[{"StartTime":87654.0,"Position":24.0,"HyperDash":true}]},{"StartTime":87813.0,"Objects":[{"StartTime":87813.0,"Position":272.0,"HyperDash":true}]},{"StartTime":87972.0,"Objects":[{"StartTime":87972.0,"Position":24.0,"HyperDash":false}]},{"StartTime":88131.0,"Objects":[{"StartTime":88131.0,"Position":295.0,"HyperDash":false},{"StartTime":88210.0,"Position":311.0,"HyperDash":false},{"StartTime":88289.0,"Position":17.0,"HyperDash":false},{"StartTime":88368.0,"Position":467.0,"HyperDash":false},{"StartTime":88448.0,"Position":30.0,"HyperDash":false},{"StartTime":88527.0,"Position":218.0,"HyperDash":false},{"StartTime":88606.0,"Position":26.0,"HyperDash":false},{"StartTime":88686.0,"Position":16.0,"HyperDash":false},{"StartTime":88765.0,"Position":248.0,"HyperDash":false},{"StartTime":88844.0,"Position":100.0,"HyperDash":false},{"StartTime":88924.0,"Position":24.0,"HyperDash":false},{"StartTime":89003.0,"Position":66.0,"HyperDash":false},{"StartTime":89082.0,"Position":97.0,"HyperDash":false},{"StartTime":89162.0,"Position":267.0,"HyperDash":false},{"StartTime":89241.0,"Position":116.0,"HyperDash":false},{"StartTime":89320.0,"Position":451.0,"HyperDash":false},{"StartTime":89400.0,"Position":414.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3152510.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3152510.osu new file mode 100644 index 0000000000..9d65d5cc19 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3152510.osu @@ -0,0 +1,468 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 2 + +[Difficulty] +HPDrainRate:6 +CircleSize:4.2 +OverallDifficulty:9.2 +ApproachRate:9.2 +SliderMultiplier:1.9 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +512,317.460317460317,4,2,1,70,1,0 +2972,-100,4,2,1,5,0,0 +3051,-100,4,2,1,70,0,0 +8051,-100,4,2,1,5,0,0 +8131,-100,4,2,1,70,0,0 +10591,-100,4,2,1,5,0,0 +10670,-100,4,2,1,60,0,0 +12019,-100,4,2,1,5,0,0 +12099,-100,4,2,1,60,0,0 +12654,-100,4,2,1,5,0,0 +12734,-100,4,2,1,60,0,0 +14559,-100,4,2,1,5,0,0 +14638,-100,4,2,1,60,0,0 +17734,-100,4,2,1,5,0,0 +17813,-100,4,2,1,60,0,0 +21385,-100,4,2,1,5,0,0 +21464,-100,4,2,1,60,0,0 +22178,-100,4,2,1,5,0,0 +22257,-100,4,2,1,60,0,0 +30988,-100,4,2,1,50,0,0 +40829,-44.4444444444445,4,2,1,80,0,0 +41067,-44.4444444444445,4,2,1,5,0,0 +41146,-100,4,2,1,80,0,1 +41385,-100,4,2,1,5,0,1 +41464,-100,4,2,1,80,0,1 +45908,-44.4444444444445,4,2,1,80,0,1 +46146,-44.4444444444445,4,2,1,5,0,1 +46226,-100,4,2,1,80,0,1 +46464,-100,4,2,1,5,0,1 +46543,-100,4,2,1,80,0,1 +50988,-44.4444444444445,4,2,1,80,0,1 +51226,-44.4444444444445,4,2,1,5,0,1 +51305,-100,4,2,1,80,0,1 +51543,-100,4,2,1,5,0,1 +51622,-100,4,2,1,80,0,1 +56067,-44.4444444444445,4,2,1,80,0,1 +56305,-44.4444444444445,4,2,1,5,0,1 +56385,-100,4,2,1,80,0,1 +56623,-100,4,2,1,5,0,1 +56702,-100,4,2,1,80,0,1 +61464,-100,4,2,1,70,0,0 +63607,-100,4,2,1,5,0,0 +63686,-100,4,2,1,80,0,0 +66305,-100,4,2,1,5,0,0 +66384,-100,4,2,1,80,0,0 +77972,-100,4,2,1,60,0,0 +79242,-100,4,2,1,70,0,0 +84321,-100,4,2,1,60,0,0 +85670,-100,4,2,1,5,0,0 +85750,-100,4,2,1,60,0,0 +85988,-100,4,2,1,5,0,0 +86068,-100,4,2,1,60,0,0 +88131,-100,4,2,1,50,0,0 +88289,-100,4,2,1,45,0,0 +88448,-100,4,2,1,40,0,0 +88607,-100,4,2,1,35,0,0 +88765,-100,4,2,1,30,0,0 +88924,-100,4,2,1,25,0,0 +89083,-100,4,2,1,20,0,0 +89242,-100,4,2,1,15,0,0 +89400,-100,4,2,1,10,0,0 + +[HitObjects] +368,312,512,6,0,L|368:200,1,95,6|0,3:2|0:2,0:0:0:0: +136,152,829,1,8,0:2:0:0: +272,152,988,1,8,0:2:0:0: +136,192,1146,2,0,L|136:304,1,95,0|0,3:2|3:2,0:2:0:0: +368,96,1464,1,8,0:2:0:0: +136,256,1623,6,0,P|64:208|136:152,1,190,2|0,3:2|3:2,0:2:0:0: +176,144,2019,1,0,3:2:0:0: +368,160,2099,1,8,0:2:0:0: +232,112,2258,1,8,0:2:0:0: +368,224,2416,2,0,L|368:344,1,95,0|0,3:2|3:2,0:0:0:0: +136,152,2734,2,0,L|32:152,1,95,8|0,0:2|0:0,0:2:0:0: +280,176,3051,6,0,L|384:176,1,95,2|0,3:2|3:2,0:2:0:0: +136,96,3369,1,8,0:2:0:0: +272,96,3527,1,8,0:2:0:0: +136,160,3686,2,0,L|136:280,1,95,0|0,3:2|3:2,0:0:0:0: +384,56,4004,1,8,0:2:0:0: +136,216,4162,6,0,L|344:216,1,190,2|0,3:2|3:2,0:2:0:0: +272,168,4559,1,0,3:2:0:0: +80,136,4638,1,8,0:2:0:0: +216,96,4797,1,8,0:2:0:0: +80,192,4956,2,0,L|80:304,1,95,0|0,3:2|3:2,0:0:0:0: +312,144,5273,2,0,L|192:144,1,95,8|0,0:2|0:2,0:2:0:0: +456,184,5591,6,0,L|456:80,1,95,2|0,3:2|3:2,0:0:0:0: +216,264,5908,1,8,0:2:0:0: +352,264,6067,1,8,0:2:0:0: +216,264,6226,2,0,L|216:168,1,95,0|0,3:2|3:2,0:2:0:0: +456,144,6543,1,8,0:2:0:0: +216,184,6702,6,0,P|152:128|216:64,1,190,2|0,3:2|3:2,0:2:0:0: +264,56,7099,1,0,3:2:0:0: +456,184,7178,1,8,0:2:0:0: +320,152,7337,1,8,0:2:0:0: +456,224,7496,2,0,L|456:320,1,95,0|0,3:2|3:2,0:2:0:0: +216,192,7813,2,0,L|112:192,1,95,8|0,0:2|0:0,0:2:0:0: +368,184,8131,6,0,L|368:80,1,95,2|0,3:2|3:2,0:0:0:0: +128,272,8448,1,8,0:2:0:0: +264,264,8607,1,8,0:2:0:0: +128,216,8765,2,0,L|128:120,1,95,0|0,3:2|3:2,0:0:0:0: +368,136,9083,1,8,0:2:0:0: +128,272,9242,6,0,L|344:272,1,190,2|0,3:2|3:2,0:2:0:0: +264,224,9638,1,0,3:2:0:0: +72,144,9718,1,8,0:2:0:0: +208,128,9877,1,8,0:2:0:0: +72,200,10035,2,0,L|72:312,1,95,0|0,3:2|3:2,0:2:0:0: +312,288,10353,2,0,L|208:288,1,95,8|0,0:2|0:0,0:2:0:0: +464,192,10670,6,0,L|464:88,1,95,2|0,3:2|0:0,0:2:0:0: +224,192,10988,2,0,L|224:80,1,95,8|0,0:2|0:0,0:2:0:0: +360,200,11305,1,0,0:2:0:0: +224,192,11464,1,0,0:2:0:0: +464,320,11623,1,8,0:2:0:0: +328,264,11781,6,0,L|328:168,1,95,2|0,3:2|0:0,0:2:0:0: +464,232,12099,2,0,L|464:128,1,95,0|8,3:2|0:2,0:2:0:0: +328,184,12416,2,0,L|432:184,1,95,2|0,0:2|0:0,0:2:0:0: +288,120,12734,1,0,0:2:0:0: +424,128,12892,2,0,L|424:16,1,95,8|0,0:2|0:2,0:0:0:0: +192,192,13210,6,0,L|192:88,1,95,2|0,3:2|0:0,0:2:0:0: +424,200,13527,2,0,L|424:88,1,95,8|0,0:2|0:2,0:2:0:0: +288,176,13845,1,0,0:2:0:0: +424,176,14004,1,0,0:2:0:0: +184,288,14162,1,8,0:2:0:0: +320,248,14321,6,0,L|320:136,1,95,2|0,3:2|0:0,0:2:0:0: +88,176,14638,2,0,L|88:72,1,95,0|8,3:2|0:2,0:0:0:0: +224,176,14956,1,0,0:2:0:0: +88,224,15115,2,0,L|88:128,1,95,2|0,0:2|0:0,0:0:0:0: +328,224,15432,2,0,L|424:224,1,95,8|0,0:2|0:0,0:0:0:0: +192,184,15750,5,2,3:2:0:0: +328,168,15908,1,0,0:0:0:0: +192,240,16067,2,0,L|80:240,1,95,8|0,0:2|0:2,0:2:0:0: +232,168,16385,1,2,0:2:0:0: +96,144,16543,1,0,0:2:0:0: +336,288,16702,1,8,0:2:0:0: +200,256,16861,6,0,L|200:152,1,95,2|0,3:2|0:2,0:2:0:0: +440,168,17178,1,0,3:2:0:0: +304,160,17337,1,8,0:2:0:0: +408,160,17496,2,0,L|504:160,1,95 +360,192,17813,1,0,0:2:0:0: +496,144,17972,2,0,L|496:40,1,95,8|0,0:2|0:2,0:0:0:0: +256,288,18289,6,0,L|128:288,1,95,2|0,3:2|0:0,0:2:0:0: +392,256,18607,2,0,L|392:152,1,95,8|0,0:2|0:2,0:2:0:0: +256,224,18924,1,0,0:2:0:0: +392,224,19083,1,0,0:2:0:0: +152,288,19242,1,8,0:2:0:0: +288,224,19400,6,0,L|288:120,1,95,2|0,3:2|0:0,0:2:0:0: +48,192,19718,2,0,L|48:96,1,95 +168,168,20035,1,0,0:0:0:0: +48,248,20194,2,0,L|344:248,1,285 +88,320,20829,6,0,L|88:224,1,95,6|0,3:2|0:0,0:2:0:0: +232,176,21146,2,0,L|232:80,1,95,8|0,0:2|0:0,0:2:0:0: +88,176,21464,2,0,L|200:176,1,95,0|0,0:2|3:2,0:0:0:0: +320,168,21781,1,8,0:2:0:0: +184,312,21940,6,0,L|184:200,1,95,2|0,3:2|0:0,0:2:0:0: +320,224,22258,2,0,L|320:128,1,95,0|8,3:2|0:2,0:2:0:0: +184,336,22575,2,0,L|184:208,1,95,2|0,3:2|0:0,0:2:0:0: +320,280,22892,1,0,3:2:0:0: +184,264,23051,2,0,L|80:264,1,95,8|0,0:2|0:2,0:2:0:0: +328,216,23369,6,0,L|488:216,1,142.5,6|2,3:2|3:2,0:0:0:0: +416,160,23686,1,0,0:0:0:0: +280,120,23845,2,0,L|184:120,2,95,2|2|0,3:2|0:2|3:2,0:0:0:0: +424,232,24321,1,8,0:2:0:0: +288,176,24480,6,0,L|480:176,1,190,2|2,3:2|3:2,0:2:0:0: +360,120,24956,1,8,0:2:0:0: +224,280,25115,1,0,3:2:0:0: +360,224,25273,2,0,L|360:176,1,47.5,0|0,3:2|3:0,3:0:0:0: +288,152,25432,2,0,L|288:88,1,47.5,0|0,3:0|3:0,3:0:0:0: +448,176,25591,2,0,L|448:56,1,95,8|0,0:2|0:0,0:0:0:0: +208,312,25908,6,0,L|96:312,1,95,2|0,3:2|3:2,0:2:0:0: +248,240,26226,2,0,L|352:240,1,95,8|0,0:2|3:2,0:2:0:0: +208,184,26543,2,0,L|208:80,1,95,0|0,0:2|3:2,0:0:0:0: +344,80,26861,1,8,0:2:0:0: +208,240,27019,6,0,L|104:240,1,95,2|0,3:2|0:2,0:2:0:0: +248,176,27337,2,0,L|352:176,1,95,0|8,3:2|0:2,0:2:0:0: +208,80,27654,1,0,0:2:0:0: +344,248,27813,1,0,3:2:0:0: +208,152,27972,1,0,3:2:0:0: +344,152,28131,2,0,L|456:152,1,95,8|0,0:2|0:2,0:2:0:0: +208,216,28448,6,0,L|48:216,1,142.5,6|2,3:2|3:2,0:2:0:0: +120,160,28765,1,0,0:0:0:0: +256,120,28924,2,0,L|352:120,2,95,2|0|0,3:2|0:2|3:2,0:0:0:0: +112,232,29400,1,8,0:2:0:0: +248,176,29559,6,0,L|56:176,1,190,2|2,3:2|3:2,0:2:0:0: +192,128,30035,1,0,0:0:0:0: +328,184,30194,1,2,3:2:0:0: +192,200,30353,2,0,L|192:104,1,95,8|0,0:2|3:2,0:0:0:0: +432,184,30670,2,0,L|368:184,1,47.5,8|8,0:2|0:2,0:0:0:0: +192,256,30829,2,0,L|136:256,1,47.5,8|8,0:2|0:2,0:0:0:0: +336,304,30988,6,0,L|336:192,1,95,2|0,3:2|0:2,0:2:0:0: +208,176,31305,2,0,L|208:80,1,95,0|0,3:2|0:2,0:2:0:0: +80,192,31623,2,0,L|80:288,1,95,0|0,3:2|0:2,0:2:0:0: +208,224,31940,1,0,3:2:0:0: +80,192,32099,6,0,L|184:192,1,95,0|2,0:2|3:2,0:0:0:0: +296,176,32416,2,0,L|296:56,1,95,0|0,0:2|3:2,0:0:0:0: +176,128,32734,2,0,L|176:24,1,95,0|0,0:2|3:2,0:0:0:0: +296,224,33051,2,0,L|184:224,2,95,0|0|0,0:2|3:2|0:2,0:0:0:0: +48,144,33527,5,0,3:2:0:0: +160,144,33686,1,2,0:2:0:0: +272,144,33845,1,0,3:2:0:0: +160,144,34004,1,2,0:2:0:0: +304,272,34162,2,0,P|376:216|304:168,1,190,0|0,3:2|3:2,0:0:0:0: +184,160,34638,6,0,L|408:160,1,190,2|2,0:2|0:2,0:2:0:0: +440,160,35035,1,0,0:0:0:0: +376,120,35115,1,0,3:2:0:0: +224,248,35273,1,0,0:2:0:0: +368,184,35432,2,0,P|440:136|368:88,1,190,0|0,3:2|3:2,0:2:0:0: +288,80,35908,1,0,0:2:0:0: +72,328,36067,5,4,3:2:0:0: +16,296,36146,1,0,3:0:0:0: +16,240,36226,1,0,3:0:0:0: +72,208,36305,1,0,3:0:0:0: +264,168,36385,1,8,3:0:0:0: +328,168,36464,1,0,3:0:0:0: +264,168,36543,1,0,3:0:0:0: +200,168,36623,1,0,3:0:0:0: +392,272,36702,6,0,L|440:272,1,47.5,8|0,3:0|3:0,0:0:0:0: +232,280,36861,2,0,L|224:216,1,47.5,0|0,3:0|3:0,0:0:0:0: +304,208,37019,2,0,L|320:144,1,47.5,0|0,3:0|3:0,0:0:0:0: +104,96,37178,2,0,L|40:96,1,47.5,0|0,3:0|3:0,0:0:0:0: +264,344,37337,6,0,L|280:296,1,47.5,8|0,3:0|3:0,0:0:0:0: +208,264,37496,2,0,L|200:208,1,47.5,0|0,3:0|3:0,0:0:0:0: +392,192,37654,1,8,3:0:0:0: +448,152,37734,1,0,3:0:0:0: +448,96,37813,1,0,3:0:0:0: +392,64,37892,1,0,3:0:0:0: +192,192,37972,6,0,L|272:192,1,47.5,8|0,3:0|3:0,0:0:0:0: +410,263,38131,2,0,L|458:263,1,47.5,0|0,3:0|3:0,0:0:0:0: +264,160,38289,2,0,L|208:160,1,47.5,8|0,0:0|3:0,0:0:0:0: +448,208,38448,2,0,L|496:208,1,47.5,0|0,3:0|3:0,0:0:0:0: +296,224,38607,5,0,3:2:0:0: +440,152,38924,1,0,3:2:0:0: +296,160,39242,1,0,3:2:0:0: +152,128,39559,1,0,3:2:0:0: +352,264,39877,6,0,L|256:256,5,95 +432,192,40829,6,0,L|184:192,1,213.750008153916,2|0,0:2|0:0,0:2:0:0: +440,264,41146,6,0,P|488:216|440:168,1,142.5,6|0,3:2|0:0,0:2:0:0: +256,168,41464,1,8,0:2:0:0: +400,128,41623,1,8,0:2:0:0: +168,248,41781,2,0,L|168:152,1,95,0|0,3:2|3:2,0:0:0:0: +400,192,42099,1,8,0:2:0:0: +256,136,42258,6,0,L|256:32,1,95,2|0,3:2|0:2,0:2:0:0: +400,248,42575,1,0,3:2:0:0: +256,200,42734,1,8,0:2:0:0: +488,192,42892,2,0,L|488:96,1,95,8|0,0:2|3:2,0:2:0:0: +256,136,43210,1,0,3:2:0:0: +368,136,43369,1,8,0:2:0:0: +480,136,43527,1,0,0:2:0:0: +256,136,43686,6,0,L|136:136,1,95,2|0,3:2|3:2,0:0:0:0: +392,296,44004,1,8,0:2:0:0: +248,248,44162,2,0,L|248:136,1,95,8|0,0:2|3:2,0:0:0:0: +480,184,44480,2,0,L|480:80,1,95,0|8,3:2|0:2,0:0:0:0: +248,248,44797,6,0,L|352:248,1,95,2|0,3:2|0:0,0:2:0:0: +104,176,45115,2,0,L|104:104,1,47.5,0|0,3:2|3:2,0:0:0:0: +296,176,45273,1,8,0:2:0:0: +160,112,45432,1,8,0:2:0:0: +392,200,45591,2,0,L|392:88,1,95,0|0,3:2|3:2,0:0:0:0: +160,176,45908,2,0,L|376:176,1,213.750008153916,10|0,0:2|0:0,0:2:0:0: +136,288,46226,6,0,P|80:232|136:192,1,142.5,6|0,3:2|0:0,0:2:0:0: +304,192,46543,1,8,0:2:0:0: +160,128,46702,1,8,0:2:0:0: +400,296,46861,2,0,L|400:192,1,95,0|0,3:2|3:2,0:0:0:0: +160,72,47178,1,8,0:2:0:0: +296,72,47337,6,0,L|408:72,1,95,2|0,3:2|0:2,0:2:0:0: +248,168,47654,1,0,3:2:0:0: +304,152,47734,1,0,3:2:0:0: +360,128,47813,1,8,0:2:0:0: +136,224,47972,2,0,L|136:128,1,95,8|0,0:2|3:2,0:0:0:0: +376,64,48289,1,0,3:2:0:0: +264,64,48448,1,8,0:2:0:0: +152,64,48607,1,0,0:2:0:0: +392,168,48765,6,0,L|392:72,1,95,2|0,3:2|3:2,0:2:0:0: +160,320,49083,1,8,0:2:0:0: +304,272,49241,2,0,L|304:160,1,95,8|0,0:2|3:2,0:2:0:0: +64,208,49559,2,0,L|64:112,1,95,0|8,3:2|0:2,0:0:0:0: +304,272,49877,6,0,L|200:272,1,95,2|0,3:2|0:0,0:2:0:0: +448,192,50194,2,0,L|448:80,1,95,2|8,3:2|0:2,0:2:0:0: +208,96,50511,2,0,L|144:96,1,47.5,8|0,0:2|0:0,0:0:0:0: +352,96,50670,2,0,L|352:200,1,95,0|0,3:2|3:2,0:2:0:0: +128,160,50988,2,0,L|360:160,1,213.750008153916,2|0,3:2|0:0,0:2:0:0: +104,288,51305,6,0,P|48:240|104:192,1,142.5,6|0,0:2|0:0,0:2:0:0: +272,176,51623,1,8,0:2:0:0: +128,120,51781,1,8,0:2:0:0: +368,280,51940,2,0,L|368:176,1,95,0|0,3:2|3:2,0:0:0:0: +128,184,52258,1,8,0:2:0:0: +272,184,52416,6,0,L|272:80,1,95,2|0,3:2|0:2,0:2:0:0: +128,120,52734,1,0,3:2:0:0: +184,112,52813,1,0,3:2:0:0: +240,96,52892,1,8,0:2:0:0: +16,312,53051,2,0,L|16:208,1,95,8|0,0:2|3:2,0:0:0:0: +264,168,53369,1,0,3:2:0:0: +152,168,53527,1,8,0:2:0:0: +40,168,53686,1,0,0:2:0:0: +280,256,53845,6,0,L|280:136,1,95,2|0,3:2|3:2,0:0:0:0: +56,240,54162,1,8,0:2:0:0: +184,232,54321,2,0,L|304:232,1,95,8|0,0:2|3:2,0:0:0:0: +32,320,54638,2,0,L|32:224,1,95,0|8,3:2|0:2,0:0:0:0: +264,248,54956,6,0,L|368:248,1,95,2|0,3:2|0:0,0:2:0:0: +120,176,55274,2,0,L|120:104,1,47.5,2|0,3:2|0:0,0:0:0:0: +312,176,55432,1,8,0:2:0:0: +176,112,55591,1,8,0:2:0:0: +408,200,55750,2,0,L|408:88,1,95,0|0,0:0|3:2,0:0:0:0: +136,288,56067,2,0,L|352:288,1,213.750008153916,10|0,0:2|0:0,0:2:0:0: +112,224,56385,6,0,P|64:176|112:128,1,142.5,6|0,3:2|0:0,0:2:0:0: +296,192,56702,1,8,0:2:0:0: +152,128,56861,1,8,0:2:0:0: +392,296,57019,2,0,L|392:192,1,95,0|0,3:2|3:2,0:2:0:0: +152,184,57337,1,8,0:2:0:0: +296,192,57496,6,0,L|416:192,1,95,2|0,3:2|0:2,0:2:0:0: +248,120,57813,1,0,3:2:0:0: +392,80,57972,1,8,0:2:0:0: +152,288,58131,2,0,L|152:192,1,95,8|0,0:2|3:2,0:2:0:0: +392,184,58448,1,0,3:2:0:0: +280,192,58607,1,8,0:2:0:0: +168,192,58765,1,0,0:2:0:0: +392,272,58924,5,2,3:2:0:0: +248,224,59083,2,0,L|248:120,1,95,0|8,3:2|0:2,0:0:0:0: +488,192,59400,2,0,L|488:96,1,95,10|0,0:2|3:2,0:2:0:0: +248,160,59718,2,0,L|248:56,1,95,2|8,3:2|0:2,0:2:0:0: +488,256,60035,6,0,L|280:256,1,190,2|2,3:2|3:2,0:0:0:0: +448,336,60511,2,0,L|448:232,1,95,2|0,0:2|3:2,0:0:0:0: +200,200,60829,1,8,0:2:0:0: +448,336,60988,2,0,L|504:336,1,47.5,8|8,0:2|0:2,0:0:0:0: +304,208,61146,2,0,L|224:208,1,47.5,8|8,0:2|0:2,0:0:0:0: +448,280,61305,2,0,L|496:280,1,47.5,8|8,0:2|0:2,0:0:0:0: +304,288,61464,6,0,P|248:232|304:192,1,142.5,6|0,3:2|0:0,0:2:0:0: +448,224,61781,2,0,P|496:176|448:128,1,142.5,8|0,0:2|0:0,0:2:0:0: +272,184,62099,1,0,3:2:0:0: +408,128,62258,1,0,3:2:0:0: +168,200,62416,1,8,0:2:0:0: +312,152,62575,6,0,L|312:48,1,95,2|0,3:2|3:2,0:2:0:0: +72,144,62892,2,0,L|72:32,1,95,0|8,0:2|0:2,0:2:0:0: +312,304,63210,1,0,0:2:0:0: +176,232,63369,2,0,L|176:128,1,95,0|0,3:2|0:0,0:2:0:0: +312,232,63686,1,8,0:2:0:0: +176,232,63845,1,0,0:2:0:0: +408,232,64004,6,0,P|464:184|408:136,1,142.5,2|0,3:2|0:0,0:2:0:0: +240,120,64321,1,8,0:2:0:0: +376,64,64480,1,0,0:2:0:0: +136,272,64638,2,0,L|136:168,1,95,0|0,3:2|0:2,0:0:0:0: +272,288,64956,1,8,0:2:0:0: +32,192,65115,6,0,L|32:88,1,95,2|0,3:2|3:2,0:2:0:0: +272,136,65432,2,0,L|368:136,1,95,0|8,0:2|0:2,0:2:0:0: +128,240,65750,1,0,0:2:0:0: +264,192,65908,1,0,3:2:0:0: +128,184,66067,2,0,L|128:80,1,95,8|0,0:2|0:0,0:2:0:0: +264,128,66385,1,10,0:2:0:0: +32,144,66543,6,0,L|32:40,1,95,0|0,0:0|0:2,0:0:0:0: +280,240,66861,1,8,0:2:0:0: +144,240,67019,1,0,0:2:0:0: +280,240,67178,2,0,L|384:240,1,95,0|0,3:2|0:2,0:0:0:0: +136,104,67496,1,8,0:2:0:0: +272,136,67654,6,0,P|320:80|272:32,1,142.5,2|0,3:2|0:0,0:2:0:0: +96,80,67972,2,0,L|96:176,1,95,0|8,0:2|0:2,0:2:0:0: +328,232,68289,1,0,0:2:0:0: +192,224,68448,1,0,3:2:0:0: +328,232,68607,1,0,0:2:0:0: +96,152,68765,1,8,0:2:0:0: +232,136,68924,1,2,3:2:0:0: +472,296,69083,6,0,L|472:176,1,95,0|0,3:2|0:2,0:0:0:0: +368,168,69400,1,8,0:2:0:0: +152,192,69559,1,0,0:2:0:0: +288,160,69718,1,0,3:2:0:0: +152,144,69877,1,0,0:2:0:0: +384,184,70035,1,8,0:2:0:0: +248,168,70194,6,0,L|248:56,1,95,2|0,3:2|0:0,0:2:0:0: +384,120,70511,1,0,0:2:0:0: +248,112,70670,2,0,P|192:56|248:16,1,142.5,8|0,0:2|0:0,0:2:0:0: +416,128,70988,2,0,L|416:24,1,95,0|8,3:2|0:2,0:0:0:0: +352,128,71226,1,8,0:2:0:0: +168,192,71305,2,0,L|96:192,1,47.5,8|8,0:2|0:2,0:0:0:0: +312,208,71464,2,0,L|384:208,1,47.5,8|8,0:2|0:2,0:0:0:0: +168,272,71623,6,0,L|64:272,1,95,6|0,3:2|0:2,0:2:0:0: +312,312,71940,1,8,0:2:0:0: +168,248,72099,2,0,L|56:248,1,95,0|0,3:2|3:2,0:2:0:0: +312,200,72416,2,0,L|312:88,1,95,0|8,0:2|0:2,0:2:0:0: +176,80,72734,1,2,3:2:0:0: +416,264,72892,5,0,3:2:0:0: +280,192,73051,2,0,L|280:80,1,95,0|8,0:2|0:2,0:0:0:0: +416,200,73369,1,0,0:2:0:0: +176,184,73527,2,0,P|128:128|176:80,1,142.5,0|0,3:2|0:0,0:2:0:0: +352,192,73845,2,0,L|352:88,1,95,8|2,0:2|3:2,0:0:0:0: +104,192,74162,5,0,3:2:0:0: +240,144,74321,2,0,L|240:48,1,95,0|8,0:2|0:2,0:2:0:0: +104,104,74638,1,0,0:2:0:0: +344,304,74797,1,0,3:2:0:0: +208,256,74956,1,0,0:2:0:0: +344,240,75115,1,8,0:2:0:0: +104,184,75273,6,0,L|104:80,1,95,2|0,3:2|3:2,0:2:0:0: +240,72,75591,1,0,0:2:0:0: +16,312,75750,1,8,0:2:0:0: +152,320,75908,1,0,0:2:0:0: +16,264,76067,2,0,L|16:168,1,95,0|0,3:2|0:2,0:0:0:0: +256,192,76385,2,0,L|376:192,1,95,10|0,0:2|0:0,0:2:0:0: +112,128,76702,5,0,3:2:0:0: +248,120,76861,1,8,0:2:0:0: +112,176,77019,2,0,L|112:280,1,95,0|0,0:2|3:2,0:0:0:0: +176,176,77258,1,0,3:2:0:0: +368,176,77337,2,0,L|368:80,1,95,8|0,0:2|0:2,0:2:0:0: +232,152,77654,1,0,0:2:0:0: +368,160,77813,1,0,0:2:0:0: +80,96,77972,5,10,0:2:0:0: +64,296,79242,6,0,L|64:184,1,95,6|0,3:2|3:2,0:0:0:0: +296,136,79559,1,8,0:2:0:0: +160,136,79718,1,8,0:2:0:0: +296,176,79876,2,0,L|296:288,1,95,0|0,3:2|3:2,0:2:0:0: +64,80,80194,1,8,0:2:0:0: +296,240,80353,6,0,P|368:192|296:136,1,190,2|0,3:2|3:2,0:2:0:0: +256,128,80749,1,0,3:2:0:0: +64,144,80829,1,8,0:2:0:0: +200,96,80988,1,8,0:2:0:0: +64,208,81146,2,0,L|64:328,1,95,0|0,3:2|3:2,0:2:0:0: +296,136,81464,2,0,L|400:136,1,95,8|0,0:2|0:2,0:2:0:0: +152,160,81781,6,0,L|48:160,1,95,2|0,3:2|3:2,0:2:0:0: +296,80,82099,1,8,0:2:0:0: +160,80,82257,1,8,0:2:0:0: +296,144,82416,2,0,L|296:264,1,95,0|0,3:2|3:2,0:0:0:0: +48,40,82734,1,8,0:2:0:0: +296,200,82892,6,0,L|88:200,1,190,2|0,3:2|3:2,0:2:0:0: +160,152,83289,1,0,3:2:0:0: +352,120,83368,1,8,0:2:0:0: +216,80,83527,1,8,0:2:0:0: +352,176,83686,2,0,L|352:288,1,95,0|0,3:2|3:2,0:2:0:0: +120,128,84003,2,0,L|16:128,1,95,8|0,0:2|0:2,0:0:0:0: +264,232,84321,5,10,0:2:0:0: +128,152,84480,1,8,0:2:0:0: +368,320,84638,1,0,3:2:0:0: +464,272,84797,1,8,0:2:0:0: +464,184,84956,1,0,3:2:0:0: +368,136,85115,1,0,0:2:0:0: +232,104,85273,1,8,0:2:0:0: +472,344,85432,6,0,L|472:240,1,95,10|0,0:2|0:0,0:2:0:0: +232,160,85750,2,0,L|232:40,1,95,10|0,0:2|0:0,0:2:0:0: +368,144,86067,1,8,3:2:0:0: +232,208,86226,2,0,L|136:208,1,95,0|0,3:2|0:2,0:2:0:0: +272,64,86543,2,0,L|400:64,1,95,0|0,3:2|0:2,0:2:0:0: +128,320,86861,5,10,0:0:0:0: +264,272,87019,1,8,0:0:0:0: +24,224,87178,1,2,3:0:0:0: +24,128,87337,1,2,0:0:0:0: +160,104,87496,1,8,3:0:0:0: +24,104,87654,1,0,3:0:0:0: +272,144,87813,1,8,0:0:0:0: +24,56,87972,5,8,0:0:0:0: +256,192,88131,12,0,89400,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3227428-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3227428-expected-conversion.json new file mode 100644 index 0000000000..bd1c6d658f --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3227428-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":22.0,"Objects":[{"StartTime":22.0,"Position":206.0,"HyperDash":false}]},{"StartTime":362.0,"Objects":[{"StartTime":362.0,"Position":137.0,"HyperDash":false},{"StartTime":447.0,"Position":104.571175,"HyperDash":false},{"StartTime":532.0,"Position":91.14235,"HyperDash":false},{"StartTime":617.0,"Position":81.71353,"HyperDash":false},{"StartTime":702.0,"Position":67.18218,"HyperDash":false},{"StartTime":778.0,"Position":97.66308,"HyperDash":false},{"StartTime":854.0,"Position":115.24649,"HyperDash":false},{"StartTime":930.0,"Position":114.82991,"HyperDash":false},{"StartTime":1043.0,"Position":137.0,"HyperDash":false}]},{"StartTime":1385.0,"Objects":[{"StartTime":1385.0,"Position":220.0,"HyperDash":false},{"StartTime":1465.0,"Position":233.326752,"HyperDash":false},{"StartTime":1546.0,"Position":231.079025,"HyperDash":false},{"StartTime":1626.0,"Position":246.1162,"HyperDash":false},{"StartTime":1707.0,"Position":246.012085,"HyperDash":false},{"StartTime":1788.0,"Position":253.358887,"HyperDash":false},{"StartTime":1868.0,"Position":266.303955,"HyperDash":false},{"StartTime":1949.0,"Position":247.094482,"HyperDash":false},{"StartTime":2066.0,"Position":224.02179,"HyperDash":false}]},{"StartTime":2408.0,"Objects":[{"StartTime":2408.0,"Position":160.0,"HyperDash":false},{"StartTime":2493.0,"Position":133.573441,"HyperDash":false},{"StartTime":2578.0,"Position":130.146881,"HyperDash":false},{"StartTime":2663.0,"Position":88.72033,"HyperDash":false},{"StartTime":2748.0,"Position":90.19126,"HyperDash":false},{"StartTime":2824.0,"Position":96.67014,"HyperDash":false},{"StartTime":2900.0,"Position":139.251526,"HyperDash":false},{"StartTime":2976.0,"Position":153.832932,"HyperDash":false},{"StartTime":3089.0,"Position":160.0,"HyperDash":false}]},{"StartTime":3772.0,"Objects":[{"StartTime":3772.0,"Position":340.0,"HyperDash":false}]},{"StartTime":4112.0,"Objects":[{"StartTime":4112.0,"Position":401.0,"HyperDash":false},{"StartTime":4192.0,"Position":414.4298,"HyperDash":false},{"StartTime":4273.0,"Position":393.865021,"HyperDash":false},{"StartTime":4353.0,"Position":385.29483,"HyperDash":false},{"StartTime":4434.0,"Position":415.730042,"HyperDash":false},{"StartTime":4515.0,"Position":398.165222,"HyperDash":false},{"StartTime":4595.0,"Position":407.595062,"HyperDash":false},{"StartTime":4676.0,"Position":389.030243,"HyperDash":false},{"StartTime":4793.0,"Position":404.658875,"HyperDash":false}]},{"StartTime":5135.0,"Objects":[{"StartTime":5135.0,"Position":343.0,"HyperDash":false},{"StartTime":5211.0,"Position":324.279724,"HyperDash":false},{"StartTime":5287.0,"Position":312.955536,"HyperDash":false},{"StartTime":5363.0,"Position":314.093536,"HyperDash":false},{"StartTime":5475.0,"Position":280.640778,"HyperDash":false}]},{"StartTime":5817.0,"Objects":[{"StartTime":5817.0,"Position":189.0,"HyperDash":false},{"StartTime":5902.0,"Position":156.58606,"HyperDash":false},{"StartTime":5987.0,"Position":135.172119,"HyperDash":false},{"StartTime":6072.0,"Position":120.758179,"HyperDash":false},{"StartTime":6157.0,"Position":119.241791,"HyperDash":false},{"StartTime":6233.0,"Position":147.709473,"HyperDash":false},{"StartTime":6309.0,"Position":167.279587,"HyperDash":false},{"StartTime":6385.0,"Position":164.8497,"HyperDash":false},{"StartTime":6498.0,"Position":189.0,"HyperDash":false}]},{"StartTime":6840.0,"Objects":[{"StartTime":6840.0,"Position":208.0,"HyperDash":false},{"StartTime":6920.0,"Position":217.418747,"HyperDash":false},{"StartTime":7001.0,"Position":240.042725,"HyperDash":false},{"StartTime":7081.0,"Position":276.4615,"HyperDash":false},{"StartTime":7162.0,"Position":268.085449,"HyperDash":false},{"StartTime":7243.0,"Position":295.709442,"HyperDash":false},{"StartTime":7323.0,"Position":320.128174,"HyperDash":false},{"StartTime":7404.0,"Position":340.752167,"HyperDash":false},{"StartTime":7521.0,"Position":347.7646,"HyperDash":false}]},{"StartTime":7862.0,"Objects":[{"StartTime":7862.0,"Position":416.0,"HyperDash":false},{"StartTime":7947.0,"Position":441.4566,"HyperDash":false},{"StartTime":8032.0,"Position":446.637848,"HyperDash":false},{"StartTime":8117.0,"Position":454.495941,"HyperDash":false},{"StartTime":8202.0,"Position":442.012817,"HyperDash":false},{"StartTime":8278.0,"Position":447.07373,"HyperDash":false},{"StartTime":8354.0,"Position":416.0334,"HyperDash":false},{"StartTime":8430.0,"Position":431.9387,"HyperDash":false},{"StartTime":8543.0,"Position":416.0,"HyperDash":false}]},{"StartTime":9226.0,"Objects":[{"StartTime":9226.0,"Position":275.0,"HyperDash":false}]},{"StartTime":9567.0,"Objects":[{"StartTime":9567.0,"Position":208.0,"HyperDash":false},{"StartTime":9652.0,"Position":187.257431,"HyperDash":false},{"StartTime":9737.0,"Position":176.772141,"HyperDash":false},{"StartTime":9822.0,"Position":203.593216,"HyperDash":false},{"StartTime":9907.0,"Position":172.761276,"HyperDash":false},{"StartTime":9992.0,"Position":193.308182,"HyperDash":false},{"StartTime":10077.0,"Position":178.273483,"HyperDash":false},{"StartTime":10162.0,"Position":164.69072,"HyperDash":false},{"StartTime":10248.0,"Position":175.555847,"HyperDash":false},{"StartTime":10328.0,"Position":175.714554,"HyperDash":false},{"StartTime":10409.0,"Position":165.140945,"HyperDash":false},{"StartTime":10490.0,"Position":176.860687,"HyperDash":false},{"StartTime":10571.0,"Position":161.863419,"HyperDash":false},{"StartTime":10651.0,"Position":168.073318,"HyperDash":false},{"StartTime":10732.0,"Position":197.565277,"HyperDash":false},{"StartTime":10813.0,"Position":183.264725,"HyperDash":false},{"StartTime":10930.0,"Position":208.0,"HyperDash":false}]},{"StartTime":11272.0,"Objects":[{"StartTime":11272.0,"Position":272.0,"HyperDash":false},{"StartTime":11348.0,"Position":267.478119,"HyperDash":false},{"StartTime":11424.0,"Position":297.956238,"HyperDash":false},{"StartTime":11500.0,"Position":307.4344,"HyperDash":false},{"StartTime":11612.0,"Position":341.244232,"HyperDash":false}]},{"StartTime":11953.0,"Objects":[{"StartTime":11953.0,"Position":397.0,"HyperDash":false},{"StartTime":12038.0,"Position":422.321472,"HyperDash":false},{"StartTime":12123.0,"Position":429.5693,"HyperDash":false},{"StartTime":12208.0,"Position":465.729126,"HyperDash":false},{"StartTime":12293.0,"Position":465.889526,"HyperDash":false},{"StartTime":12369.0,"Position":433.739624,"HyperDash":false},{"StartTime":12445.0,"Position":440.41095,"HyperDash":false},{"StartTime":12521.0,"Position":439.010223,"HyperDash":false},{"StartTime":12634.0,"Position":397.0,"HyperDash":false}]},{"StartTime":12976.0,"Objects":[{"StartTime":12976.0,"Position":309.0,"HyperDash":false},{"StartTime":13052.0,"Position":315.9078,"HyperDash":false},{"StartTime":13128.0,"Position":313.544037,"HyperDash":false},{"StartTime":13204.0,"Position":305.913849,"HyperDash":false},{"StartTime":13316.0,"Position":300.853455,"HyperDash":false}]},{"StartTime":13658.0,"Objects":[{"StartTime":13658.0,"Position":226.0,"HyperDash":false},{"StartTime":13738.0,"Position":220.477478,"HyperDash":false},{"StartTime":13819.0,"Position":214.759888,"HyperDash":false},{"StartTime":13899.0,"Position":167.911179,"HyperDash":false},{"StartTime":13980.0,"Position":163.303925,"HyperDash":false},{"StartTime":14061.0,"Position":139.940048,"HyperDash":false},{"StartTime":14141.0,"Position":139.7893,"HyperDash":false},{"StartTime":14222.0,"Position":130.218536,"HyperDash":false},{"StartTime":14339.0,"Position":106.02227,"HyperDash":false}]},{"StartTime":14681.0,"Objects":[{"StartTime":14681.0,"Position":71.0,"HyperDash":false}]},{"StartTime":15022.0,"Objects":[{"StartTime":15022.0,"Position":109.0,"HyperDash":false},{"StartTime":15102.0,"Position":125.26535,"HyperDash":false},{"StartTime":15183.0,"Position":125.837524,"HyperDash":false},{"StartTime":15263.0,"Position":141.6249,"HyperDash":false},{"StartTime":15344.0,"Position":175.222168,"HyperDash":false},{"StartTime":15425.0,"Position":198.591125,"HyperDash":false},{"StartTime":15505.0,"Position":206.724686,"HyperDash":false},{"StartTime":15586.0,"Position":225.222351,"HyperDash":false},{"StartTime":15703.0,"Position":228.196915,"HyperDash":false}]},{"StartTime":16044.0,"Objects":[{"StartTime":16044.0,"Position":305.0,"HyperDash":false},{"StartTime":16120.0,"Position":322.564636,"HyperDash":false},{"StartTime":16196.0,"Position":317.036682,"HyperDash":false},{"StartTime":16272.0,"Position":334.3818,"HyperDash":false},{"StartTime":16384.0,"Position":373.692017,"HyperDash":false}]},{"StartTime":16726.0,"Objects":[{"StartTime":16726.0,"Position":416.0,"HyperDash":false},{"StartTime":16811.0,"Position":436.3275,"HyperDash":false},{"StartTime":16896.0,"Position":458.65506,"HyperDash":false},{"StartTime":16981.0,"Position":460.982574,"HyperDash":false},{"StartTime":17066.0,"Position":485.412048,"HyperDash":false},{"StartTime":17142.0,"Position":473.021118,"HyperDash":false},{"StartTime":17218.0,"Position":452.528259,"HyperDash":false},{"StartTime":17294.0,"Position":456.035431,"HyperDash":false},{"StartTime":17407.0,"Position":416.0,"HyperDash":false}]},{"StartTime":17749.0,"Objects":[{"StartTime":17749.0,"Position":338.0,"HyperDash":false},{"StartTime":17829.0,"Position":314.194427,"HyperDash":false},{"StartTime":17910.0,"Position":335.3038,"HyperDash":false},{"StartTime":17990.0,"Position":298.49823,"HyperDash":false},{"StartTime":18071.0,"Position":319.4706,"HyperDash":false},{"StartTime":18152.0,"Position":321.945831,"HyperDash":false},{"StartTime":18232.0,"Position":300.43985,"HyperDash":false},{"StartTime":18313.0,"Position":300.91507,"HyperDash":false},{"StartTime":18430.0,"Position":305.712616,"HyperDash":false}]},{"StartTime":18772.0,"Objects":[{"StartTime":18772.0,"Position":293.0,"HyperDash":false}]},{"StartTime":19112.0,"Objects":[{"StartTime":19112.0,"Position":201.0,"HyperDash":false},{"StartTime":19192.0,"Position":184.726288,"HyperDash":false},{"StartTime":19273.0,"Position":174.249146,"HyperDash":false},{"StartTime":19353.0,"Position":136.975433,"HyperDash":false},{"StartTime":19434.0,"Position":143.498291,"HyperDash":false},{"StartTime":19515.0,"Position":130.021179,"HyperDash":false},{"StartTime":19595.0,"Position":94.91766,"HyperDash":false},{"StartTime":19676.0,"Position":90.74364,"HyperDash":false},{"StartTime":19793.0,"Position":63.3811569,"HyperDash":false}]},{"StartTime":20476.0,"Objects":[{"StartTime":20476.0,"Position":129.0,"HyperDash":false},{"StartTime":20556.0,"Position":157.2956,"HyperDash":false},{"StartTime":20637.0,"Position":156.794876,"HyperDash":false},{"StartTime":20717.0,"Position":179.090469,"HyperDash":false},{"StartTime":20798.0,"Position":181.589752,"HyperDash":false},{"StartTime":20879.0,"Position":214.089035,"HyperDash":false},{"StartTime":20959.0,"Position":246.179916,"HyperDash":false},{"StartTime":21040.0,"Position":240.353943,"HyperDash":false},{"StartTime":21157.0,"Position":266.716431,"HyperDash":false}]},{"StartTime":21499.0,"Objects":[{"StartTime":21499.0,"Position":352.0,"HyperDash":false},{"StartTime":21584.0,"Position":366.69278,"HyperDash":false},{"StartTime":21669.0,"Position":363.83432,"HyperDash":false},{"StartTime":21754.0,"Position":383.387665,"HyperDash":false},{"StartTime":21839.0,"Position":413.4099,"HyperDash":false},{"StartTime":21915.0,"Position":398.248138,"HyperDash":false},{"StartTime":21991.0,"Position":402.284668,"HyperDash":false},{"StartTime":22067.0,"Position":383.640961,"HyperDash":false},{"StartTime":22180.0,"Position":352.0,"HyperDash":false}]},{"StartTime":22522.0,"Objects":[{"StartTime":22522.0,"Position":337.0,"HyperDash":false}]},{"StartTime":22862.0,"Objects":[{"StartTime":22862.0,"Position":412.0,"HyperDash":false},{"StartTime":22938.0,"Position":422.0278,"HyperDash":false},{"StartTime":23014.0,"Position":407.799652,"HyperDash":false},{"StartTime":23090.0,"Position":425.315735,"HyperDash":false},{"StartTime":23202.0,"Position":405.6633,"HyperDash":false}]},{"StartTime":23885.0,"Objects":[{"StartTime":23885.0,"Position":214.0,"HyperDash":false},{"StartTime":23970.0,"Position":199.7902,"HyperDash":false},{"StartTime":24055.0,"Position":219.068054,"HyperDash":false},{"StartTime":24140.0,"Position":186.837479,"HyperDash":false},{"StartTime":24225.0,"Position":196.081757,"HyperDash":false},{"StartTime":24301.0,"Position":188.375519,"HyperDash":false},{"StartTime":24377.0,"Position":207.0842,"HyperDash":false},{"StartTime":24453.0,"Position":195.185028,"HyperDash":false},{"StartTime":24566.0,"Position":214.0,"HyperDash":false}]},{"StartTime":24908.0,"Objects":[{"StartTime":24908.0,"Position":301.0,"HyperDash":false},{"StartTime":24988.0,"Position":317.5747,"HyperDash":false},{"StartTime":25069.0,"Position":290.1566,"HyperDash":false},{"StartTime":25149.0,"Position":301.7313,"HyperDash":false},{"StartTime":25230.0,"Position":290.313171,"HyperDash":false},{"StartTime":25311.0,"Position":297.89505,"HyperDash":false},{"StartTime":25391.0,"Position":296.469727,"HyperDash":false},{"StartTime":25472.0,"Position":296.051636,"HyperDash":false},{"StartTime":25589.0,"Position":305.89212,"HyperDash":false}]},{"StartTime":25931.0,"Objects":[{"StartTime":25931.0,"Position":302.0,"HyperDash":false}]},{"StartTime":26612.0,"Objects":[{"StartTime":26612.0,"Position":131.0,"HyperDash":false}]},{"StartTime":26953.0,"Objects":[{"StartTime":26953.0,"Position":67.0,"HyperDash":false},{"StartTime":27029.0,"Position":61.19864,"HyperDash":false},{"StartTime":27105.0,"Position":60.3972778,"HyperDash":false},{"StartTime":27181.0,"Position":78.59592,"HyperDash":false},{"StartTime":27293.0,"Position":63.4149666,"HyperDash":false}]},{"StartTime":27635.0,"Objects":[{"StartTime":27635.0,"Position":96.0,"HyperDash":false},{"StartTime":27720.0,"Position":101.143433,"HyperDash":false},{"StartTime":27805.0,"Position":88.28687,"HyperDash":false},{"StartTime":27890.0,"Position":90.4303055,"HyperDash":false},{"StartTime":27975.0,"Position":104.586349,"HyperDash":false},{"StartTime":28051.0,"Position":87.68248,"HyperDash":false},{"StartTime":28127.0,"Position":96.76599,"HyperDash":false},{"StartTime":28203.0,"Position":101.84951,"HyperDash":false},{"StartTime":28316.0,"Position":96.0,"HyperDash":false}]},{"StartTime":28658.0,"Objects":[{"StartTime":28658.0,"Position":165.0,"HyperDash":false},{"StartTime":28738.0,"Position":161.813614,"HyperDash":false},{"StartTime":28819.0,"Position":196.82489,"HyperDash":false},{"StartTime":28899.0,"Position":225.6385,"HyperDash":false},{"StartTime":28980.0,"Position":225.64978,"HyperDash":false},{"StartTime":29061.0,"Position":260.60907,"HyperDash":false},{"StartTime":29141.0,"Position":251.337646,"HyperDash":false},{"StartTime":29222.0,"Position":265.2628,"HyperDash":false},{"StartTime":29339.0,"Position":299.265778,"HyperDash":false}]},{"StartTime":29681.0,"Objects":[{"StartTime":29681.0,"Position":385.0,"HyperDash":false},{"StartTime":29766.0,"Position":388.458282,"HyperDash":false},{"StartTime":29851.0,"Position":437.916565,"HyperDash":false},{"StartTime":29936.0,"Position":447.374817,"HyperDash":false},{"StartTime":30021.0,"Position":454.9358,"HyperDash":false},{"StartTime":30097.0,"Position":458.428741,"HyperDash":false},{"StartTime":30173.0,"Position":406.819,"HyperDash":false},{"StartTime":30249.0,"Position":402.209229,"HyperDash":false},{"StartTime":30362.0,"Position":385.0,"HyperDash":false}]},{"StartTime":31044.0,"Objects":[{"StartTime":31044.0,"Position":202.0,"HyperDash":false}]},{"StartTime":31385.0,"Objects":[{"StartTime":31385.0,"Position":197.0,"HyperDash":false},{"StartTime":31470.0,"Position":185.578781,"HyperDash":false},{"StartTime":31555.0,"Position":174.157562,"HyperDash":false},{"StartTime":31640.0,"Position":131.736343,"HyperDash":false},{"StartTime":31725.0,"Position":113.315117,"HyperDash":false},{"StartTime":31810.0,"Position":90.8939,"HyperDash":false},{"StartTime":31895.0,"Position":95.47269,"HyperDash":false},{"StartTime":31980.0,"Position":61.0514679,"HyperDash":false},{"StartTime":32066.0,"Position":57.3228149,"HyperDash":false},{"StartTime":32146.0,"Position":79.6167755,"HyperDash":false},{"StartTime":32227.0,"Position":103.21817,"HyperDash":false},{"StartTime":32308.0,"Position":96.81957,"HyperDash":false},{"StartTime":32389.0,"Position":116.420967,"HyperDash":false},{"StartTime":32469.0,"Position":149.817413,"HyperDash":false},{"StartTime":32550.0,"Position":165.418808,"HyperDash":false},{"StartTime":32631.0,"Position":180.0202,"HyperDash":false},{"StartTime":32748.0,"Position":197.0,"HyperDash":false}]},{"StartTime":33090.0,"Objects":[{"StartTime":33090.0,"Position":285.0,"HyperDash":false},{"StartTime":33175.0,"Position":283.775879,"HyperDash":false},{"StartTime":33260.0,"Position":292.551727,"HyperDash":false},{"StartTime":33345.0,"Position":281.3276,"HyperDash":false},{"StartTime":33430.0,"Position":288.108032,"HyperDash":false},{"StartTime":33506.0,"Position":280.418884,"HyperDash":false},{"StartTime":33582.0,"Position":305.725159,"HyperDash":false},{"StartTime":33658.0,"Position":281.031433,"HyperDash":false},{"StartTime":33771.0,"Position":285.0,"HyperDash":false}]},{"StartTime":34112.0,"Objects":[{"StartTime":34112.0,"Position":286.0,"HyperDash":false},{"StartTime":34188.0,"Position":286.694733,"HyperDash":false},{"StartTime":34264.0,"Position":302.389465,"HyperDash":false},{"StartTime":34340.0,"Position":272.0842,"HyperDash":false},{"StartTime":34452.0,"Position":289.108032,"HyperDash":false}]},{"StartTime":34794.0,"Objects":[{"StartTime":34794.0,"Position":373.0,"HyperDash":false},{"StartTime":34870.0,"Position":405.631622,"HyperDash":false},{"StartTime":34946.0,"Position":407.263245,"HyperDash":false},{"StartTime":35022.0,"Position":415.8949,"HyperDash":false},{"StartTime":35134.0,"Position":442.930969,"HyperDash":false}]},{"StartTime":35476.0,"Objects":[{"StartTime":35476.0,"Position":453.0,"HyperDash":false},{"StartTime":35556.0,"Position":463.4278,"HyperDash":false},{"StartTime":35637.0,"Position":456.885925,"HyperDash":false},{"StartTime":35717.0,"Position":475.313721,"HyperDash":false},{"StartTime":35798.0,"Position":450.771881,"HyperDash":false},{"StartTime":35879.0,"Position":441.79422,"HyperDash":false},{"StartTime":35959.0,"Position":445.1267,"HyperDash":false},{"StartTime":36040.0,"Position":428.388367,"HyperDash":false},{"StartTime":36157.0,"Position":438.09967,"HyperDash":false}]},{"StartTime":36499.0,"Objects":[{"StartTime":36499.0,"Position":362.0,"HyperDash":false}]},{"StartTime":36840.0,"Objects":[{"StartTime":36840.0,"Position":304.0,"HyperDash":false},{"StartTime":36920.0,"Position":297.5722,"HyperDash":false},{"StartTime":37001.0,"Position":304.114075,"HyperDash":false},{"StartTime":37081.0,"Position":297.686279,"HyperDash":false},{"StartTime":37162.0,"Position":292.228119,"HyperDash":false},{"StartTime":37243.0,"Position":315.20578,"HyperDash":false},{"StartTime":37323.0,"Position":301.8733,"HyperDash":false},{"StartTime":37404.0,"Position":324.611633,"HyperDash":false},{"StartTime":37521.0,"Position":318.90033,"HyperDash":false}]},{"StartTime":38203.0,"Objects":[{"StartTime":38203.0,"Position":160.0,"HyperDash":false},{"StartTime":38279.0,"Position":131.357956,"HyperDash":false},{"StartTime":38355.0,"Position":127.715912,"HyperDash":false},{"StartTime":38431.0,"Position":101.07386,"HyperDash":false},{"StartTime":38543.0,"Position":90.02242,"HyperDash":false}]},{"StartTime":38885.0,"Objects":[{"StartTime":38885.0,"Position":48.0,"HyperDash":false},{"StartTime":38970.0,"Position":51.7274666,"HyperDash":false},{"StartTime":39055.0,"Position":59.45493,"HyperDash":false},{"StartTime":39140.0,"Position":46.1823959,"HyperDash":false},{"StartTime":39225.0,"Position":50.91414,"HyperDash":false},{"StartTime":39301.0,"Position":30.2679787,"HyperDash":false},{"StartTime":39377.0,"Position":53.61754,"HyperDash":false},{"StartTime":39453.0,"Position":53.9671021,"HyperDash":false},{"StartTime":39566.0,"Position":48.0,"HyperDash":false}]},{"StartTime":40249.0,"Objects":[{"StartTime":40249.0,"Position":219.0,"HyperDash":false},{"StartTime":40325.0,"Position":234.6352,"HyperDash":false},{"StartTime":40401.0,"Position":232.270386,"HyperDash":false},{"StartTime":40477.0,"Position":246.905579,"HyperDash":false},{"StartTime":40589.0,"Position":288.94693,"HyperDash":false}]},{"StartTime":40931.0,"Objects":[{"StartTime":40931.0,"Position":379.0,"HyperDash":false},{"StartTime":41016.0,"Position":385.054565,"HyperDash":false},{"StartTime":41101.0,"Position":420.911255,"HyperDash":false},{"StartTime":41186.0,"Position":418.812378,"HyperDash":false},{"StartTime":41271.0,"Position":453.0934,"HyperDash":false},{"StartTime":41356.0,"Position":459.2142,"HyperDash":false},{"StartTime":41441.0,"Position":442.7846,"HyperDash":false},{"StartTime":41526.0,"Position":431.531372,"HyperDash":false},{"StartTime":41612.0,"Position":447.3299,"HyperDash":false},{"StartTime":41692.0,"Position":455.0114,"HyperDash":false},{"StartTime":41773.0,"Position":432.572144,"HyperDash":false},{"StartTime":41854.0,"Position":423.492432,"HyperDash":false},{"StartTime":41935.0,"Position":402.304352,"HyperDash":false},{"StartTime":42015.0,"Position":376.832733,"HyperDash":false},{"StartTime":42096.0,"Position":372.3723,"HyperDash":false},{"StartTime":42177.0,"Position":366.8342,"HyperDash":false},{"StartTime":42294.0,"Position":334.281647,"HyperDash":false}]},{"StartTime":42976.0,"Objects":[{"StartTime":42976.0,"Position":172.0,"HyperDash":false},{"StartTime":43056.0,"Position":148.8692,"HyperDash":false},{"StartTime":43137.0,"Position":157.674286,"HyperDash":false},{"StartTime":43217.0,"Position":139.543488,"HyperDash":false},{"StartTime":43298.0,"Position":162.348572,"HyperDash":false},{"StartTime":43379.0,"Position":134.066162,"HyperDash":false},{"StartTime":43459.0,"Position":174.156235,"HyperDash":false},{"StartTime":43540.0,"Position":170.297409,"HyperDash":false},{"StartTime":43657.0,"Position":167.279129,"HyperDash":false}]},{"StartTime":43999.0,"Objects":[{"StartTime":43999.0,"Position":255.0,"HyperDash":false},{"StartTime":44084.0,"Position":272.431122,"HyperDash":false},{"StartTime":44169.0,"Position":288.862274,"HyperDash":false},{"StartTime":44254.0,"Position":319.2934,"HyperDash":false},{"StartTime":44339.0,"Position":324.827057,"HyperDash":false},{"StartTime":44415.0,"Position":311.344116,"HyperDash":false},{"StartTime":44491.0,"Position":294.758636,"HyperDash":false},{"StartTime":44567.0,"Position":265.173157,"HyperDash":false},{"StartTime":44680.0,"Position":255.0,"HyperDash":false}]},{"StartTime":45022.0,"Objects":[{"StartTime":45022.0,"Position":163.0,"HyperDash":false},{"StartTime":45098.0,"Position":150.362976,"HyperDash":false},{"StartTime":45174.0,"Position":119.747879,"HyperDash":false},{"StartTime":45250.0,"Position":109.167084,"HyperDash":false},{"StartTime":45362.0,"Position":93.2945,"HyperDash":false}]},{"StartTime":45703.0,"Objects":[{"StartTime":45703.0,"Position":81.0,"HyperDash":false},{"StartTime":45779.0,"Position":67.62006,"HyperDash":false},{"StartTime":45855.0,"Position":93.24382,"HyperDash":false},{"StartTime":45931.0,"Position":89.85092,"HyperDash":false},{"StartTime":46043.0,"Position":98.2657852,"HyperDash":false}]},{"StartTime":46385.0,"Objects":[{"StartTime":46385.0,"Position":123.0,"HyperDash":false},{"StartTime":46465.0,"Position":115.7731,"HyperDash":false},{"StartTime":46546.0,"Position":133.424759,"HyperDash":false},{"StartTime":46626.0,"Position":169.264114,"HyperDash":false},{"StartTime":46707.0,"Position":177.250015,"HyperDash":false},{"StartTime":46788.0,"Position":189.772232,"HyperDash":false},{"StartTime":46868.0,"Position":199.175552,"HyperDash":false},{"StartTime":46949.0,"Position":219.42218,"HyperDash":false},{"StartTime":47066.0,"Position":250.349442,"HyperDash":false}]},{"StartTime":47408.0,"Objects":[{"StartTime":47408.0,"Position":339.0,"HyperDash":false},{"StartTime":47484.0,"Position":348.605347,"HyperDash":false},{"StartTime":47560.0,"Position":359.2107,"HyperDash":false},{"StartTime":47636.0,"Position":385.816,"HyperDash":false},{"StartTime":47748.0,"Position":408.813354,"HyperDash":false}]},{"StartTime":48431.0,"Objects":[{"StartTime":48431.0,"Position":436.0,"HyperDash":false},{"StartTime":48511.0,"Position":410.2269,"HyperDash":false},{"StartTime":48592.0,"Position":411.575226,"HyperDash":false},{"StartTime":48672.0,"Position":410.73587,"HyperDash":false},{"StartTime":48753.0,"Position":368.749969,"HyperDash":false},{"StartTime":48834.0,"Position":357.227753,"HyperDash":false},{"StartTime":48914.0,"Position":363.824432,"HyperDash":false},{"StartTime":48995.0,"Position":311.57782,"HyperDash":false},{"StartTime":49112.0,"Position":308.650574,"HyperDash":false}]},{"StartTime":49453.0,"Objects":[{"StartTime":49453.0,"Position":217.0,"HyperDash":false},{"StartTime":49538.0,"Position":226.735519,"HyperDash":false},{"StartTime":49623.0,"Position":224.662048,"HyperDash":false},{"StartTime":49708.0,"Position":184.780319,"HyperDash":false},{"StartTime":49793.0,"Position":197.063889,"HyperDash":false},{"StartTime":49869.0,"Position":185.218567,"HyperDash":false},{"StartTime":49945.0,"Position":217.554169,"HyperDash":false},{"StartTime":50021.0,"Position":222.04332,"HyperDash":false},{"StartTime":50134.0,"Position":217.0,"HyperDash":false}]},{"StartTime":50476.0,"Objects":[{"StartTime":50476.0,"Position":153.0,"HyperDash":false},{"StartTime":50552.0,"Position":152.801224,"HyperDash":false},{"StartTime":50628.0,"Position":114.521843,"HyperDash":false},{"StartTime":50704.0,"Position":93.1696854,"HyperDash":false},{"StartTime":50816.0,"Position":84.42975,"HyperDash":false}]},{"StartTime":51158.0,"Objects":[{"StartTime":51158.0,"Position":115.0,"HyperDash":false},{"StartTime":51238.0,"Position":126.432373,"HyperDash":false},{"StartTime":51319.0,"Position":159.057648,"HyperDash":false},{"StartTime":51399.0,"Position":160.49,"HyperDash":false},{"StartTime":51480.0,"Position":177.372131,"HyperDash":false},{"StartTime":51561.0,"Position":186.782,"HyperDash":false},{"StartTime":51641.0,"Position":225.989288,"HyperDash":false},{"StartTime":51722.0,"Position":235.399139,"HyperDash":false},{"StartTime":51839.0,"Position":250.10228,"HyperDash":false}]},{"StartTime":52181.0,"Objects":[{"StartTime":52181.0,"Position":339.0,"HyperDash":false}]},{"StartTime":52862.0,"Objects":[{"StartTime":52862.0,"Position":347.0,"HyperDash":false}]},{"StartTime":53203.0,"Objects":[{"StartTime":53203.0,"Position":253.0,"HyperDash":false},{"StartTime":53288.0,"Position":228.749466,"HyperDash":false},{"StartTime":53373.0,"Position":234.498917,"HyperDash":false},{"StartTime":53458.0,"Position":185.284058,"HyperDash":false},{"StartTime":53543.0,"Position":183.852417,"HyperDash":false},{"StartTime":53628.0,"Position":151.420776,"HyperDash":false},{"StartTime":53713.0,"Position":139.989136,"HyperDash":false},{"StartTime":53798.0,"Position":123.557495,"HyperDash":false},{"StartTime":53884.0,"Position":118.835892,"HyperDash":false},{"StartTime":53964.0,"Position":126.204315,"HyperDash":false},{"StartTime":54045.0,"Position":151.862686,"HyperDash":false},{"StartTime":54126.0,"Position":154.521072,"HyperDash":false},{"StartTime":54207.0,"Position":198.179474,"HyperDash":false},{"StartTime":54287.0,"Position":205.644547,"HyperDash":false},{"StartTime":54368.0,"Position":202.816391,"HyperDash":false},{"StartTime":54449.0,"Position":244.255142,"HyperDash":false},{"StartTime":54566.0,"Position":253.0,"HyperDash":false}]},{"StartTime":54908.0,"Objects":[{"StartTime":54908.0,"Position":343.0,"HyperDash":false}]},{"StartTime":55249.0,"Objects":[{"StartTime":55249.0,"Position":418.0,"HyperDash":false},{"StartTime":55325.0,"Position":411.540649,"HyperDash":false},{"StartTime":55401.0,"Position":412.081329,"HyperDash":false},{"StartTime":55477.0,"Position":412.621979,"HyperDash":false},{"StartTime":55589.0,"Position":429.366119,"HyperDash":false}]},{"StartTime":55931.0,"Objects":[{"StartTime":55931.0,"Position":415.0,"HyperDash":false},{"StartTime":56011.0,"Position":388.6705,"HyperDash":false},{"StartTime":56092.0,"Position":384.1857,"HyperDash":false},{"StartTime":56172.0,"Position":357.960541,"HyperDash":false},{"StartTime":56253.0,"Position":367.598083,"HyperDash":false},{"StartTime":56334.0,"Position":339.3097,"HyperDash":false},{"StartTime":56414.0,"Position":332.302979,"HyperDash":false},{"StartTime":56495.0,"Position":306.186432,"HyperDash":false},{"StartTime":56612.0,"Position":278.082428,"HyperDash":false}]},{"StartTime":56953.0,"Objects":[{"StartTime":56953.0,"Position":187.0,"HyperDash":false}]},{"StartTime":57294.0,"Objects":[{"StartTime":57294.0,"Position":96.0,"HyperDash":false},{"StartTime":57374.0,"Position":78.94491,"HyperDash":false},{"StartTime":57455.0,"Position":76.87663,"HyperDash":false},{"StartTime":57535.0,"Position":104.821541,"HyperDash":false},{"StartTime":57616.0,"Position":98.75326,"HyperDash":false},{"StartTime":57697.0,"Position":72.68498,"HyperDash":false},{"StartTime":57777.0,"Position":93.62989,"HyperDash":false},{"StartTime":57858.0,"Position":89.56161,"HyperDash":false},{"StartTime":57975.0,"Position":87.01854,"HyperDash":false}]},{"StartTime":58317.0,"Objects":[{"StartTime":58317.0,"Position":149.0,"HyperDash":false}]},{"StartTime":58658.0,"Objects":[{"StartTime":58658.0,"Position":239.0,"HyperDash":false},{"StartTime":58738.0,"Position":248.055084,"HyperDash":false},{"StartTime":58819.0,"Position":226.123367,"HyperDash":false},{"StartTime":58899.0,"Position":238.178467,"HyperDash":false},{"StartTime":58980.0,"Position":242.246735,"HyperDash":false},{"StartTime":59061.0,"Position":251.315018,"HyperDash":false},{"StartTime":59141.0,"Position":233.370117,"HyperDash":false},{"StartTime":59222.0,"Position":253.438385,"HyperDash":false},{"StartTime":59339.0,"Position":247.981461,"HyperDash":false}]},{"StartTime":60022.0,"Objects":[{"StartTime":60022.0,"Position":365.0,"HyperDash":false},{"StartTime":60098.0,"Position":378.011719,"HyperDash":false},{"StartTime":60174.0,"Position":393.785217,"HyperDash":false},{"StartTime":60250.0,"Position":402.2842,"HyperDash":false},{"StartTime":60362.0,"Position":430.07663,"HyperDash":false}]},{"StartTime":60703.0,"Objects":[{"StartTime":60703.0,"Position":436.0,"HyperDash":false},{"StartTime":60779.0,"Position":419.315674,"HyperDash":false},{"StartTime":60855.0,"Position":421.518646,"HyperDash":false},{"StartTime":60931.0,"Position":408.615723,"HyperDash":false},{"StartTime":61043.0,"Position":369.475067,"HyperDash":false}]},{"StartTime":61385.0,"Objects":[{"StartTime":61385.0,"Position":294.0,"HyperDash":false},{"StartTime":61465.0,"Position":267.3266,"HyperDash":false},{"StartTime":61546.0,"Position":256.9999,"HyperDash":false},{"StartTime":61626.0,"Position":281.482758,"HyperDash":false},{"StartTime":61707.0,"Position":273.83606,"HyperDash":false},{"StartTime":61788.0,"Position":247.226837,"HyperDash":false},{"StartTime":61868.0,"Position":269.576416,"HyperDash":false},{"StartTime":61949.0,"Position":261.865753,"HyperDash":false},{"StartTime":62066.0,"Position":290.626923,"HyperDash":false}]},{"StartTime":62408.0,"Objects":[{"StartTime":62408.0,"Position":368.0,"HyperDash":false}]},{"StartTime":62749.0,"Objects":[{"StartTime":62749.0,"Position":451.0,"HyperDash":false},{"StartTime":62829.0,"Position":437.402985,"HyperDash":false},{"StartTime":62910.0,"Position":465.8735,"HyperDash":false},{"StartTime":62990.0,"Position":468.8882,"HyperDash":false},{"StartTime":63071.0,"Position":481.67627,"HyperDash":false},{"StartTime":63152.0,"Position":473.464355,"HyperDash":false},{"StartTime":63232.0,"Position":462.279724,"HyperDash":false},{"StartTime":63313.0,"Position":466.06778,"HyperDash":false},{"StartTime":63430.0,"Position":454.872772,"HyperDash":false}]},{"StartTime":64112.0,"Objects":[{"StartTime":64112.0,"Position":288.0,"HyperDash":false},{"StartTime":64192.0,"Position":265.466431,"HyperDash":false},{"StartTime":64273.0,"Position":257.738678,"HyperDash":false},{"StartTime":64353.0,"Position":225.165848,"HyperDash":false},{"StartTime":64434.0,"Position":205.661438,"HyperDash":false},{"StartTime":64515.0,"Position":188.157013,"HyperDash":false},{"StartTime":64595.0,"Position":194.856354,"HyperDash":false},{"StartTime":64676.0,"Position":175.351929,"HyperDash":false},{"StartTime":64793.0,"Position":151.512222,"HyperDash":false}]},{"StartTime":65135.0,"Objects":[{"StartTime":65135.0,"Position":124.0,"HyperDash":false},{"StartTime":65220.0,"Position":116.567741,"HyperDash":false},{"StartTime":65305.0,"Position":71.23689,"HyperDash":false},{"StartTime":65390.0,"Position":69.06891,"HyperDash":false},{"StartTime":65475.0,"Position":55.0260773,"HyperDash":false},{"StartTime":65551.0,"Position":64.0663147,"HyperDash":false},{"StartTime":65627.0,"Position":87.38679,"HyperDash":false},{"StartTime":65703.0,"Position":117.8468,"HyperDash":false},{"StartTime":65816.0,"Position":124.0,"HyperDash":false}]},{"StartTime":66158.0,"Objects":[{"StartTime":66158.0,"Position":212.0,"HyperDash":false}]},{"StartTime":66499.0,"Objects":[{"StartTime":66499.0,"Position":190.0,"HyperDash":false},{"StartTime":66575.0,"Position":206.978012,"HyperDash":false},{"StartTime":66651.0,"Position":197.713776,"HyperDash":false},{"StartTime":66727.0,"Position":197.188354,"HyperDash":false},{"StartTime":66839.0,"Position":222.507156,"HyperDash":false}]},{"StartTime":67522.0,"Objects":[{"StartTime":67522.0,"Position":400.0,"HyperDash":false},{"StartTime":67598.0,"Position":417.733978,"HyperDash":false},{"StartTime":67674.0,"Position":418.6181,"HyperDash":false},{"StartTime":67750.0,"Position":419.6206,"HyperDash":false},{"StartTime":67862.0,"Position":432.309723,"HyperDash":false}]},{"StartTime":68203.0,"Objects":[{"StartTime":68203.0,"Position":441.0,"HyperDash":false},{"StartTime":68283.0,"Position":421.1902,"HyperDash":false},{"StartTime":68364.0,"Position":424.512329,"HyperDash":false},{"StartTime":68444.0,"Position":387.838,"HyperDash":false},{"StartTime":68525.0,"Position":400.321259,"HyperDash":false},{"StartTime":68606.0,"Position":351.733856,"HyperDash":false},{"StartTime":68686.0,"Position":346.847382,"HyperDash":false},{"StartTime":68767.0,"Position":347.826874,"HyperDash":false},{"StartTime":68884.0,"Position":315.044037,"HyperDash":false}]},{"StartTime":69226.0,"Objects":[{"StartTime":69226.0,"Position":271.0,"HyperDash":false},{"StartTime":69302.0,"Position":260.591034,"HyperDash":false},{"StartTime":69378.0,"Position":230.182068,"HyperDash":false},{"StartTime":69454.0,"Position":230.7731,"HyperDash":false},{"StartTime":69566.0,"Position":202.065155,"HyperDash":false}]},{"StartTime":70249.0,"Objects":[{"StartTime":70249.0,"Position":71.0,"HyperDash":false},{"StartTime":70329.0,"Position":88.54798,"HyperDash":false},{"StartTime":70410.0,"Position":83.29032,"HyperDash":false},{"StartTime":70490.0,"Position":120.8383,"HyperDash":false},{"StartTime":70571.0,"Position":138.779388,"HyperDash":false},{"StartTime":70652.0,"Position":166.2048,"HyperDash":false},{"StartTime":70732.0,"Position":159.427444,"HyperDash":false},{"StartTime":70813.0,"Position":172.852844,"HyperDash":false},{"StartTime":70930.0,"Position":206.578461,"HyperDash":false}]},{"StartTime":71272.0,"Objects":[{"StartTime":71272.0,"Position":285.0,"HyperDash":false},{"StartTime":71357.0,"Position":295.671265,"HyperDash":false},{"StartTime":71442.0,"Position":272.7821,"HyperDash":false},{"StartTime":71527.0,"Position":278.3155,"HyperDash":false},{"StartTime":71612.0,"Position":290.256958,"HyperDash":false},{"StartTime":71688.0,"Position":299.285034,"HyperDash":false},{"StartTime":71764.0,"Position":299.052734,"HyperDash":false},{"StartTime":71840.0,"Position":298.55127,"HyperDash":false},{"StartTime":71953.0,"Position":285.0,"HyperDash":false}]},{"StartTime":72294.0,"Objects":[{"StartTime":72294.0,"Position":257.0,"HyperDash":false},{"StartTime":72370.0,"Position":270.334442,"HyperDash":false},{"StartTime":72446.0,"Position":266.123962,"HyperDash":false},{"StartTime":72522.0,"Position":313.321533,"HyperDash":false},{"StartTime":72634.0,"Position":319.88623,"HyperDash":false}]},{"StartTime":72976.0,"Objects":[{"StartTime":72976.0,"Position":367.0,"HyperDash":false},{"StartTime":73056.0,"Position":386.222748,"HyperDash":false},{"StartTime":73137.0,"Position":399.63,"HyperDash":false},{"StartTime":73217.0,"Position":410.031372,"HyperDash":false},{"StartTime":73298.0,"Position":440.0546,"HyperDash":false},{"StartTime":73379.0,"Position":442.924225,"HyperDash":false},{"StartTime":73459.0,"Position":443.179749,"HyperDash":false},{"StartTime":73540.0,"Position":448.717773,"HyperDash":false},{"StartTime":73657.0,"Position":429.740936,"HyperDash":false}]},{"StartTime":73999.0,"Objects":[{"StartTime":73999.0,"Position":368.0,"HyperDash":false}]},{"StartTime":74681.0,"Objects":[{"StartTime":74681.0,"Position":2.0,"HyperDash":false}]},{"StartTime":75022.0,"Objects":[{"StartTime":75022.0,"Position":108.0,"HyperDash":false},{"StartTime":75107.0,"Position":87.6573639,"HyperDash":false},{"StartTime":75192.0,"Position":106.552719,"HyperDash":false},{"StartTime":75277.0,"Position":115.749847,"HyperDash":false},{"StartTime":75362.0,"Position":95.05077,"HyperDash":false},{"StartTime":75447.0,"Position":141.007874,"HyperDash":false},{"StartTime":75532.0,"Position":142.951065,"HyperDash":false},{"StartTime":75617.0,"Position":142.0284,"HyperDash":false},{"StartTime":75703.0,"Position":170.5651,"HyperDash":false},{"StartTime":75783.0,"Position":166.401367,"HyperDash":false},{"StartTime":75864.0,"Position":130.8922,"HyperDash":false},{"StartTime":75945.0,"Position":135.213562,"HyperDash":false},{"StartTime":76026.0,"Position":111.1313,"HyperDash":false},{"StartTime":76106.0,"Position":89.331665,"HyperDash":false},{"StartTime":76187.0,"Position":82.0756454,"HyperDash":false},{"StartTime":76268.0,"Position":84.71052,"HyperDash":false},{"StartTime":76385.0,"Position":108.0,"HyperDash":false}]},{"StartTime":76726.0,"Objects":[{"StartTime":76726.0,"Position":185.0,"HyperDash":false}]},{"StartTime":77067.0,"Objects":[{"StartTime":77067.0,"Position":134.0,"HyperDash":false},{"StartTime":77152.0,"Position":132.526932,"HyperDash":false},{"StartTime":77237.0,"Position":80.05387,"HyperDash":false},{"StartTime":77322.0,"Position":100.580811,"HyperDash":false},{"StartTime":77407.0,"Position":64.00496,"HyperDash":false},{"StartTime":77483.0,"Position":60.5251465,"HyperDash":false},{"StartTime":77559.0,"Position":95.1481247,"HyperDash":false},{"StartTime":77635.0,"Position":105.7711,"HyperDash":false},{"StartTime":77748.0,"Position":134.0,"HyperDash":false}]},{"StartTime":78090.0,"Objects":[{"StartTime":78090.0,"Position":225.0,"HyperDash":false},{"StartTime":78166.0,"Position":226.044952,"HyperDash":false},{"StartTime":78242.0,"Position":241.340271,"HyperDash":false},{"StartTime":78318.0,"Position":287.819031,"HyperDash":false},{"StartTime":78430.0,"Position":293.820984,"HyperDash":false}]},{"StartTime":78772.0,"Objects":[{"StartTime":78772.0,"Position":461.0,"HyperDash":false}]},{"StartTime":79112.0,"Objects":[{"StartTime":79112.0,"Position":429.0,"HyperDash":false},{"StartTime":79192.0,"Position":416.857025,"HyperDash":false},{"StartTime":79273.0,"Position":453.389954,"HyperDash":false},{"StartTime":79353.0,"Position":426.5255,"HyperDash":false},{"StartTime":79434.0,"Position":438.278229,"HyperDash":false},{"StartTime":79515.0,"Position":450.6304,"HyperDash":false},{"StartTime":79595.0,"Position":448.640656,"HyperDash":false},{"StartTime":79676.0,"Position":431.2529,"HyperDash":false},{"StartTime":79793.0,"Position":418.566681,"HyperDash":false}]},{"StartTime":80135.0,"Objects":[{"StartTime":80135.0,"Position":330.0,"HyperDash":false}]},{"StartTime":80476.0,"Objects":[{"StartTime":80476.0,"Position":239.0,"HyperDash":false},{"StartTime":80556.0,"Position":231.143,"HyperDash":false},{"StartTime":80637.0,"Position":222.610062,"HyperDash":false},{"StartTime":80717.0,"Position":240.474518,"HyperDash":false},{"StartTime":80798.0,"Position":227.721756,"HyperDash":false},{"StartTime":80879.0,"Position":221.3696,"HyperDash":false},{"StartTime":80959.0,"Position":225.35936,"HyperDash":false},{"StartTime":81040.0,"Position":255.747116,"HyperDash":false},{"StartTime":81157.0,"Position":249.43335,"HyperDash":false}]},{"StartTime":81840.0,"Objects":[{"StartTime":81840.0,"Position":372.0,"HyperDash":false},{"StartTime":81916.0,"Position":360.517242,"HyperDash":false},{"StartTime":81992.0,"Position":348.0345,"HyperDash":false},{"StartTime":82068.0,"Position":338.5517,"HyperDash":false},{"StartTime":82180.0,"Position":302.735016,"HyperDash":false}]},{"StartTime":82522.0,"Objects":[{"StartTime":82522.0,"Position":222.0,"HyperDash":false},{"StartTime":82607.0,"Position":195.592834,"HyperDash":false},{"StartTime":82692.0,"Position":198.185669,"HyperDash":false},{"StartTime":82777.0,"Position":161.7785,"HyperDash":false},{"StartTime":82862.0,"Position":152.268951,"HyperDash":false},{"StartTime":82938.0,"Position":160.730591,"HyperDash":false},{"StartTime":83014.0,"Position":191.294647,"HyperDash":false},{"StartTime":83090.0,"Position":194.8587,"HyperDash":false},{"StartTime":83203.0,"Position":222.0,"HyperDash":false}]},{"StartTime":83885.0,"Objects":[{"StartTime":83885.0,"Position":374.0,"HyperDash":false},{"StartTime":83961.0,"Position":382.5809,"HyperDash":false},{"StartTime":84037.0,"Position":345.4707,"HyperDash":false},{"StartTime":84113.0,"Position":351.689972,"HyperDash":false},{"StartTime":84225.0,"Position":335.561218,"HyperDash":false}]},{"StartTime":84567.0,"Objects":[{"StartTime":84567.0,"Position":246.0,"HyperDash":false},{"StartTime":84652.0,"Position":250.610764,"HyperDash":false},{"StartTime":84737.0,"Position":219.0491,"HyperDash":false},{"StartTime":84822.0,"Position":214.657715,"HyperDash":false},{"StartTime":84907.0,"Position":183.720428,"HyperDash":false},{"StartTime":84992.0,"Position":188.454575,"HyperDash":false},{"StartTime":85077.0,"Position":201.005173,"HyperDash":false},{"StartTime":85162.0,"Position":173.53,"HyperDash":false},{"StartTime":85248.0,"Position":196.9969,"HyperDash":false},{"StartTime":85333.0,"Position":206.210022,"HyperDash":false},{"StartTime":85418.0,"Position":224.027588,"HyperDash":false},{"StartTime":85503.0,"Position":233.208817,"HyperDash":false},{"StartTime":85589.0,"Position":242.6152,"HyperDash":false},{"StartTime":85674.0,"Position":246.61525,"HyperDash":false},{"StartTime":85759.0,"Position":258.8727,"HyperDash":false},{"StartTime":85844.0,"Position":298.942719,"HyperDash":false},{"StartTime":85930.0,"Position":302.666138,"HyperDash":false},{"StartTime":86015.0,"Position":288.350464,"HyperDash":false},{"StartTime":86100.0,"Position":286.2681,"HyperDash":false},{"StartTime":86185.0,"Position":256.988831,"HyperDash":false},{"StartTime":86271.0,"Position":229.786652,"HyperDash":false},{"StartTime":86356.0,"Position":240.490692,"HyperDash":false},{"StartTime":86441.0,"Position":214.260208,"HyperDash":false},{"StartTime":86526.0,"Position":191.387848,"HyperDash":false},{"StartTime":86612.0,"Position":197.0563,"HyperDash":false},{"StartTime":86692.0,"Position":212.729446,"HyperDash":false},{"StartTime":86773.0,"Position":181.9589,"HyperDash":false},{"StartTime":86854.0,"Position":206.823822,"HyperDash":false},{"StartTime":86935.0,"Position":185.277771,"HyperDash":false},{"StartTime":87015.0,"Position":222.114685,"HyperDash":false},{"StartTime":87096.0,"Position":227.322708,"HyperDash":false},{"StartTime":87177.0,"Position":214.614655,"HyperDash":false},{"StartTime":87294.0,"Position":246.0,"HyperDash":false}]},{"StartTime":87465.0,"Objects":[{"StartTime":87465.0,"Position":408.0,"HyperDash":false},{"StartTime":87547.0,"Position":243.0,"HyperDash":false},{"StartTime":87630.0,"Position":78.0,"HyperDash":false},{"StartTime":87712.0,"Position":172.0,"HyperDash":false},{"StartTime":87795.0,"Position":450.0,"HyperDash":false},{"StartTime":87877.0,"Position":231.0,"HyperDash":false},{"StartTime":87960.0,"Position":118.0,"HyperDash":false},{"StartTime":88042.0,"Position":511.0,"HyperDash":false},{"StartTime":88125.0,"Position":333.0,"HyperDash":false},{"StartTime":88208.0,"Position":234.0,"HyperDash":false},{"StartTime":88290.0,"Position":228.0,"HyperDash":false},{"StartTime":88373.0,"Position":302.0,"HyperDash":false},{"StartTime":88455.0,"Position":390.0,"HyperDash":false},{"StartTime":88538.0,"Position":75.0,"HyperDash":false},{"StartTime":88620.0,"Position":506.0,"HyperDash":false},{"StartTime":88703.0,"Position":3.0,"HyperDash":false},{"StartTime":88786.0,"Position":289.0,"HyperDash":false},{"StartTime":88868.0,"Position":217.0,"HyperDash":false},{"StartTime":88951.0,"Position":447.0,"HyperDash":false},{"StartTime":89033.0,"Position":324.0,"HyperDash":false},{"StartTime":89116.0,"Position":183.0,"HyperDash":false},{"StartTime":89198.0,"Position":279.0,"HyperDash":false},{"StartTime":89281.0,"Position":157.0,"HyperDash":false},{"StartTime":89363.0,"Position":501.0,"HyperDash":false},{"StartTime":89446.0,"Position":215.0,"HyperDash":false},{"StartTime":89529.0,"Position":79.0,"HyperDash":false},{"StartTime":89611.0,"Position":337.0,"HyperDash":false},{"StartTime":89694.0,"Position":380.0,"HyperDash":false},{"StartTime":89776.0,"Position":348.0,"HyperDash":false},{"StartTime":89859.0,"Position":225.0,"HyperDash":false},{"StartTime":89941.0,"Position":363.0,"HyperDash":false},{"StartTime":90024.0,"Position":96.0,"HyperDash":false},{"StartTime":90107.0,"Position":104.0,"HyperDash":false},{"StartTime":90189.0,"Position":173.0,"HyperDash":false},{"StartTime":90272.0,"Position":373.0,"HyperDash":false},{"StartTime":90354.0,"Position":424.0,"HyperDash":false},{"StartTime":90437.0,"Position":268.0,"HyperDash":false},{"StartTime":90519.0,"Position":373.0,"HyperDash":false},{"StartTime":90602.0,"Position":436.0,"HyperDash":false},{"StartTime":90684.0,"Position":190.0,"HyperDash":false},{"StartTime":90767.0,"Position":419.0,"HyperDash":false},{"StartTime":90850.0,"Position":158.0,"HyperDash":false},{"StartTime":90932.0,"Position":143.0,"HyperDash":false},{"StartTime":91015.0,"Position":266.0,"HyperDash":false},{"StartTime":91097.0,"Position":166.0,"HyperDash":false},{"StartTime":91180.0,"Position":297.0,"HyperDash":false},{"StartTime":91262.0,"Position":198.0,"HyperDash":false},{"StartTime":91345.0,"Position":241.0,"HyperDash":false},{"StartTime":91428.0,"Position":477.0,"HyperDash":false},{"StartTime":91510.0,"Position":371.0,"HyperDash":false},{"StartTime":91593.0,"Position":152.0,"HyperDash":false},{"StartTime":91675.0,"Position":321.0,"HyperDash":false},{"StartTime":91758.0,"Position":303.0,"HyperDash":false},{"StartTime":91840.0,"Position":259.0,"HyperDash":false},{"StartTime":91923.0,"Position":186.0,"HyperDash":false},{"StartTime":92005.0,"Position":140.0,"HyperDash":false},{"StartTime":92088.0,"Position":207.0,"HyperDash":false},{"StartTime":92171.0,"Position":278.0,"HyperDash":false},{"StartTime":92253.0,"Position":223.0,"HyperDash":false},{"StartTime":92336.0,"Position":389.0,"HyperDash":false},{"StartTime":92418.0,"Position":245.0,"HyperDash":false},{"StartTime":92501.0,"Position":400.0,"HyperDash":false},{"StartTime":92583.0,"Position":445.0,"HyperDash":false},{"StartTime":92666.0,"Position":443.0,"HyperDash":false},{"StartTime":92749.0,"Position":245.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3227428.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3227428.osu new file mode 100644 index 0000000000..7f9cdb97cc --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3227428.osu @@ -0,0 +1,142 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:4 +CircleSize:3.5 +OverallDifficulty:4 +ApproachRate:4 +SliderMultiplier:1.4 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +22,681.818181818182,4,2,1,60,1,0 +9908,-100,4,2,1,40,0,0 +10931,-100,4,2,1,67,0,0 +31726,-100,4,2,1,40,0,0 +33090,-100,4,2,1,67,0,0 +43658,-100,4,2,1,74,0,0 +53544,-100,4,2,1,50,0,0 +54908,-100,4,2,1,74,0,0 +75362,-100,4,2,1,50,0,0 +76726,-100,4,2,1,74,0,0 +86612,-100,4,2,1,67,0,0 +87294,-100,4,2,1,40,0,0 +87465,-100,4,2,1,67,0,0 +90022,-100,4,2,1,57,0,0 +91385,-100,4,2,1,37,0,0 +92067,-100,4,2,1,17,0,0 +92749,-100,4,2,1,5,0,0 + +[HitObjects] +206,12,22,5,0,2:0:0:0: +137,71,362,2,0,L|54:77,2,70,2|0|0,2:0|2:0|2:0,0:0:0:0: +220,108,1385,2,0,P|258:171|211:223,1,140,2|0,0:0|0:0,0:0:0:0: +160,283,2408,2,0,L|79:277,2,70,0|2|2,0:0|0:0|0:1,0:0:0:0: +340,303,3772,1,0,0:0:0:0: +401,235,4112,2,0,L|405:82,1,140,2|0,0:0|0:0,0:0:0:0: +343,27,5135,2,0,P|309:41|263:72,1,70,0|2,0:0|0:1,0:0:0:0: +189,63,5817,6,0,L|93:55,2,70,2|0|0,0:0|0:0|0:0,0:0:0:0: +208,151,6840,2,0,B|363:142,1,140,2|0,0:0|0:0,0:0:0:0: +416,202,7862,2,0,P|436:245|446:291,2,70,0|2|2,0:0|0:0|0:1,0:0:0:0: +275,86,9226,1,0,0:0:0:0: +208,151,9567,2,0,P|187:194|177:297,2,140,6|0|2,0:0|0:0|0:1,0:0:0:0: +272,87,11272,6,0,L|353:99,1,70,2|0,0:0|0:0,0:0:0:0: +397,169,11953,2,0,P|431:164|465:157,2,70,0|2|0,0:0|0:1|0:0,0:0:0:0: +309,196,12976,2,0,P|302:241|301:280,1,70 +226,317,13658,2,0,P|162:340|106:303,1,140,2|0,0:0|0:0,0:0:0:0: +71,218,14681,1,0,0:0:0:0: +109,135,15022,2,0,P|172:111|228:148,1,140,2|0,0:0|0:0,0:0:0:0: +305,192,16044,2,0,P|342:187|384:176,1,70,0|2,0:0|0:1,0:0:0:0: +416,99,16726,6,0,L|508:111,2,70,2|0|0,0:0|0:0|0:0,0:0:0:0: +338,58,17749,2,0,B|313:113|313:113|305:200,1,140,2|0,0:0|0:0,0:0:0:0: +293,287,18772,1,0,0:0:0:0: +201,278,19112,2,0,B|112:265|112:265|63:277,1,140,2|0,0:0|0:0,0:0:0:0: +129,107,20476,2,0,B|217:119|217:119|266:107,1,140,2|0,0:0|0:0,0:0:0:0: +352,75,21499,6,0,P|393:51|436:33,2,70,0|2|2,0:0|0:1|0:0,0:0:0:0: +337,165,22522,1,2,0:0:0:0: +412,214,22862,2,0,P|409:254|403:303,1,70,0|2,0:0|0:0,0:0:0:0: +214,306,23885,2,0,P|205:276|195:233,2,70,0|0|2,0:0|0:0|0:0,0:0:0:0: +301,331,24908,2,0,L|306:188,1,140,2|0,0:1|0:0,0:0:0:0: +302,99,25931,1,2,0:0:0:0: +131,34,26612,1,0,0:0:0:0: +67,99,26953,2,0,L|63:177,1,70,0|2,0:0|0:1,0:0:0:0: +96,254,27635,6,0,L|107:343,2,70,2|0|0,0:0|0:0|0:0,0:0:0:0: +165,194,28658,2,0,B|235:174|235:174|307:196,1,140,2|0,0:1|0:0,0:0:0:0: +385,223,29681,2,0,L|455:220,2,70,0|2|2,0:0|0:0|0:0,0:0:0:0: +202,223,31044,1,0,0:0:0:0: +197,132,31385,2,0,L|50:122,2,140,6|0|0,0:0|0:0|0:0,0:0:0:0: +285,111,33090,6,0,L|289:21,2,70,2|0|0,0:0|0:0|0:0,0:0:0:0: +286,202,34112,2,0,L|290:292,1,70,2|0,0:0|0:0,0:0:0:0: +373,306,34794,2,0,L|463:302,1,70 +453,212,35476,2,0,B|463:145|463:145|434:66,1,140,2|0,0:0|0:0,0:0:0:0: +362,25,36499,1,2,0:0:0:0: +304,95,36840,2,0,B|294:162|294:162|323:241,1,140,2|0,0:0|0:0,0:0:0:0: +160,319,38203,6,0,L|81:317,1,70,2|0,0:0|0:0,0:0:0:0: +48,235,38885,2,0,L|51:163,2,70,0|0|2,0:0|0:0|0:0,0:0:0:0: +219,295,40249,2,0,L|296:292,1,70,2|0,0:0|0:0,0:0:0:0: +379,284,40931,2,2,P|450:216|324:142,1,280,2|0,0:0|0:0,0:0:0:0: +172,210,42976,6,0,B|150:143|150:143|169:69,1,140,2|6,0:0|0:0,0:0:0:0: +255,54,43999,2,0,L|326:59,2,70,2|2|0,0:0|0:0|0:0,0:0:0:0: +163,56,45022,2,0,P|126:58|80:64,1,70,2|0,0:0|0:0,0:0:0:0: +81,153,45703,2,0,P|97:210|99:230,1,70 +123,308,46385,2,0,P|154:284|260:294,1,140,2|0,0:0|0:0,0:0:0:0: +339,307,47408,2,0,L|421:313,1,70,0|2,0:0|0:0,0:0:0:0: +436,132,48431,2,0,P|405:108|299:118,1,140,0|2,0:0|0:1,0:0:0:0: +217,111,49453,6,0,P|205:72|196:40,2,70,2|2|0,0:0|0:0|0:0,0:0:0:0: +153,175,50476,2,0,P|123:182|77:190,1,70,2|0,0:0|0:0,0:0:0:0: +115,274,51158,2,0,B|172:253|172:253|259:268,1,140,0|2,0:0|0:0,0:0:0:0: +339,247,52181,1,0,0:0:0:0: +343,65,52862,1,0,0:0:0:0: +253,81,53203,2,0,B|202:89|202:89|113:57,2,140,6|0|2,0:0|0:0|0:1,0:0:0:0: +343,65,54908,5,2,0:0:0:0: +418,116,55249,2,0,L|431:195,1,70 +415,279,55931,2,0,P|350:269|263:246,1,140,2|0,0:0|0:0,0:0:0:0: +187,254,56953,1,0,0:0:0:0: +96,242,57294,2,0,L|87:102,1,140,2|0,0:0|0:0,0:0:0:0: +149,35,58317,1,2,0:0:0:0: +239,29,58658,2,0,L|248:169,1,140,2|0,0:0|0:0,0:0:0:0: +365,304,60022,6,0,P|406:290|435:276,1,70,2|2,0:1|0:0,0:0:0:0: +436,187,60703,2,0,P|405:176|357:162,1,70 +294,217,61385,2,0,P|268:168|295:86,1,140,2|0,0:0|0:0,0:0:0:0: +368,43,62408,1,0,0:0:0:0: +451,79,62749,2,0,B|467:125|467:125|454:222,1,140,2|0,0:0|0:0,0:0:0:0: +288,290,64112,2,0,B|242:306|242:306|145:293,1,140 +124,206,65135,6,0,P|80:211|48:219,2,70,0|2|2,0:0|0:1|0:0,0:0:0:0: +212,184,66158,1,2,0:0:0:0: +190,95,66499,2,0,P|205:62|224:31,1,70,2|2,0:0|0:0,0:0:0:0: +400,67,67522,2,0,P|418:96|432:128,1,70 +441,219,68203,2,0,P|398:242|305:204,1,140,2|0,0:0|0:0,0:0:0:0: +271,136,69226,2,0,L|186:151,1,70,0|2,0:0|0:0,0:0:0:0: +71,275,70249,2,0,B|129:295|129:295|225:279,1,140,0|2,0:0|0:1,0:0:0:0: +285,236,71272,6,0,P|291:273|290:308,2,70,2|2|0,0:0|0:0|0:0,0:0:0:0: +257,150,72294,2,0,P|287:133|322:119,1,70,2|0,0:0|0:0,0:0:0:0: +367,42,72976,2,0,P|415:63|420:159,1,140,0|2,0:0|0:0,0:0:0:0: +368,210,73999,1,0,0:0:0:0: +185,209,74681,1,0,0:0:0:0: +108,159,75022,2,0,P|112:92|171:59,2,140,6|0|2,0:0|0:0|0:1,0:0:0:0: +185,209,76726,5,2,0:0:0:0: +134,284,77067,2,0,L|50:283,2,70,0|0|2,0:0|0:0|0:0,0:0:0:0: +225,289,78090,2,0,P|264:280|309:278,1,70 +385,274,78772,1,0,0:0:0:0: +429,194,79112,2,0,P|436:124|409:39,1,140,2|0,0:0|0:0,0:0:0:0: +330,33,80135,1,2,0:0:0:0: +239,38,80476,2,0,P|232:108|259:193,1,140,2|0,0:0|0:0,0:0:0:0: +372,316,81840,6,0,L|283:303,1,70,2|0,0:0|0:0,0:0:0:0: +222,262,82522,2,0,L|131:270,2,70,0|0|2,0:0|0:0|0:0,0:0:0:0: +374,161,83885,2,0,P|356:130|335:102,1,70 +246,110,84567,2,0,P|214:138|321:303,2,280,2|0|2,0:0|0:0|0:1,0:0:0:0: +256,192,87465,12,0,92749,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3524302-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3524302-expected-conversion.json new file mode 100644 index 0000000000..b4ccc8da8f --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3524302-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":14259.0,"Objects":[{"StartTime":14259.0,"Position":65.0,"HyperDash":false},{"StartTime":14354.0,"Position":482.0,"HyperDash":false},{"StartTime":14450.0,"Position":164.0,"HyperDash":false},{"StartTime":14546.0,"Position":315.0,"HyperDash":false},{"StartTime":14642.0,"Position":145.0,"HyperDash":false},{"StartTime":14738.0,"Position":159.0,"HyperDash":false},{"StartTime":14833.0,"Position":310.0,"HyperDash":false},{"StartTime":14929.0,"Position":441.0,"HyperDash":false},{"StartTime":15025.0,"Position":428.0,"HyperDash":false},{"StartTime":15121.0,"Position":243.0,"HyperDash":false},{"StartTime":15217.0,"Position":422.0,"HyperDash":false},{"StartTime":15312.0,"Position":481.0,"HyperDash":false},{"StartTime":15408.0,"Position":104.0,"HyperDash":false},{"StartTime":15504.0,"Position":473.0,"HyperDash":false},{"StartTime":15600.0,"Position":135.0,"HyperDash":false},{"StartTime":15696.0,"Position":360.0,"HyperDash":false},{"StartTime":15792.0,"Position":123.0,"HyperDash":false},{"StartTime":15887.0,"Position":42.0,"HyperDash":false},{"StartTime":15983.0,"Position":393.0,"HyperDash":false},{"StartTime":16079.0,"Position":75.0,"HyperDash":false},{"StartTime":16175.0,"Position":377.0,"HyperDash":false},{"StartTime":16271.0,"Position":354.0,"HyperDash":false},{"StartTime":16366.0,"Position":287.0,"HyperDash":false},{"StartTime":16462.0,"Position":361.0,"HyperDash":false},{"StartTime":16558.0,"Position":479.0,"HyperDash":false},{"StartTime":16654.0,"Position":346.0,"HyperDash":false},{"StartTime":16750.0,"Position":266.0,"HyperDash":false},{"StartTime":16845.0,"Position":400.0,"HyperDash":false},{"StartTime":16941.0,"Position":202.0,"HyperDash":false},{"StartTime":17037.0,"Position":500.0,"HyperDash":false},{"StartTime":17133.0,"Position":80.0,"HyperDash":false},{"StartTime":17229.0,"Position":399.0,"HyperDash":false},{"StartTime":17325.0,"Position":455.0,"HyperDash":false}]},{"StartTime":17763.0,"Objects":[{"StartTime":17763.0,"Position":166.0,"HyperDash":false},{"StartTime":17854.0,"Position":153.171234,"HyperDash":false},{"StartTime":17981.0,"Position":164.014587,"HyperDash":false}]},{"StartTime":18201.0,"Objects":[{"StartTime":18201.0,"Position":358.0,"HyperDash":false},{"StartTime":18292.0,"Position":374.828766,"HyperDash":false},{"StartTime":18419.0,"Position":359.9854,"HyperDash":false}]},{"StartTime":18639.0,"Objects":[{"StartTime":18639.0,"Position":165.0,"HyperDash":false},{"StartTime":18730.0,"Position":95.399826,"HyperDash":false},{"StartTime":18857.0,"Position":27.0127716,"HyperDash":false}]},{"StartTime":18967.0,"Objects":[{"StartTime":18967.0,"Position":137.0,"HyperDash":false},{"StartTime":19076.0,"Position":205.993164,"HyperDash":false}]},{"StartTime":19296.0,"Objects":[{"StartTime":19296.0,"Position":25.0,"HyperDash":false}]},{"StartTime":19515.0,"Objects":[{"StartTime":19515.0,"Position":314.0,"HyperDash":false}]},{"StartTime":19624.0,"Objects":[{"StartTime":19624.0,"Position":350.0,"HyperDash":false}]},{"StartTime":19734.0,"Objects":[{"StartTime":19734.0,"Position":312.0,"HyperDash":false}]},{"StartTime":19953.0,"Objects":[{"StartTime":19953.0,"Position":118.0,"HyperDash":false},{"StartTime":20044.0,"Position":174.604065,"HyperDash":false},{"StartTime":20171.0,"Position":255.996536,"HyperDash":false}]},{"StartTime":20390.0,"Objects":[{"StartTime":20390.0,"Position":449.0,"HyperDash":false},{"StartTime":20481.0,"Position":437.183441,"HyperDash":false},{"StartTime":20608.0,"Position":451.835022,"HyperDash":false}]},{"StartTime":20828.0,"Objects":[{"StartTime":20828.0,"Position":271.0,"HyperDash":false}]},{"StartTime":21047.0,"Objects":[{"StartTime":21047.0,"Position":451.0,"HyperDash":true}]},{"StartTime":21266.0,"Objects":[{"StartTime":21266.0,"Position":133.0,"HyperDash":false}]},{"StartTime":21376.0,"Objects":[{"StartTime":21376.0,"Position":97.0,"HyperDash":false}]},{"StartTime":21485.0,"Objects":[{"StartTime":21485.0,"Position":136.0,"HyperDash":false}]},{"StartTime":21704.0,"Objects":[{"StartTime":21704.0,"Position":329.0,"HyperDash":false},{"StartTime":21795.0,"Position":323.8056,"HyperDash":false},{"StartTime":21922.0,"Position":330.929871,"HyperDash":false}]},{"StartTime":22142.0,"Objects":[{"StartTime":22142.0,"Position":136.0,"HyperDash":false},{"StartTime":22233.0,"Position":185.6055,"HyperDash":false},{"StartTime":22360.0,"Position":274.0,"HyperDash":false}]},{"StartTime":22471.0,"Objects":[{"StartTime":22471.0,"Position":385.0,"HyperDash":false},{"StartTime":22580.0,"Position":316.0,"HyperDash":false}]},{"StartTime":22799.0,"Objects":[{"StartTime":22799.0,"Position":136.0,"HyperDash":false}]},{"StartTime":23018.0,"Objects":[{"StartTime":23018.0,"Position":425.0,"HyperDash":false}]},{"StartTime":23128.0,"Objects":[{"StartTime":23128.0,"Position":461.0,"HyperDash":false}]},{"StartTime":23237.0,"Objects":[{"StartTime":23237.0,"Position":421.0,"HyperDash":false}]},{"StartTime":23456.0,"Objects":[{"StartTime":23456.0,"Position":227.0,"HyperDash":false},{"StartTime":23547.0,"Position":216.765884,"HyperDash":false},{"StartTime":23674.0,"Position":224.043533,"HyperDash":false}]},{"StartTime":23894.0,"Objects":[{"StartTime":23894.0,"Position":404.0,"HyperDash":false}]},{"StartTime":24113.0,"Objects":[{"StartTime":24113.0,"Position":224.0,"HyperDash":false}]},{"StartTime":24332.0,"Objects":[{"StartTime":24332.0,"Position":417.0,"HyperDash":false},{"StartTime":24423.0,"Position":412.811279,"HyperDash":false},{"StartTime":24550.0,"Position":418.943481,"HyperDash":false}]},{"StartTime":24661.0,"Objects":[{"StartTime":24661.0,"Position":341.0,"HyperDash":true}]},{"StartTime":24770.0,"Objects":[{"StartTime":24770.0,"Position":107.0,"HyperDash":false}]},{"StartTime":24880.0,"Objects":[{"StartTime":24880.0,"Position":69.0,"HyperDash":false}]},{"StartTime":24989.0,"Objects":[{"StartTime":24989.0,"Position":111.0,"HyperDash":false}]},{"StartTime":25208.0,"Objects":[{"StartTime":25208.0,"Position":304.0,"HyperDash":false},{"StartTime":25299.0,"Position":299.828766,"HyperDash":false},{"StartTime":25426.0,"Position":305.9854,"HyperDash":false}]},{"StartTime":25646.0,"Objects":[{"StartTime":25646.0,"Position":111.0,"HyperDash":false},{"StartTime":25737.0,"Position":124.585579,"HyperDash":false},{"StartTime":25864.0,"Position":110.007217,"HyperDash":false}]},{"StartTime":25974.0,"Objects":[{"StartTime":25974.0,"Position":220.0,"HyperDash":false},{"StartTime":26083.0,"Position":289.0,"HyperDash":false}]},{"StartTime":26303.0,"Objects":[{"StartTime":26303.0,"Position":108.0,"HyperDash":false}]},{"StartTime":26522.0,"Objects":[{"StartTime":26522.0,"Position":397.0,"HyperDash":false}]},{"StartTime":26631.0,"Objects":[{"StartTime":26631.0,"Position":432.0,"HyperDash":false}]},{"StartTime":26741.0,"Objects":[{"StartTime":26741.0,"Position":395.0,"HyperDash":false}]},{"StartTime":26960.0,"Objects":[{"StartTime":26960.0,"Position":215.0,"HyperDash":false}]},{"StartTime":27179.0,"Objects":[{"StartTime":27179.0,"Position":395.0,"HyperDash":false}]},{"StartTime":27398.0,"Objects":[{"StartTime":27398.0,"Position":201.0,"HyperDash":false},{"StartTime":27489.0,"Position":203.591461,"HyperDash":false},{"StartTime":27616.0,"Position":200.0213,"HyperDash":false}]},{"StartTime":27836.0,"Objects":[{"StartTime":27836.0,"Position":380.0,"HyperDash":false}]},{"StartTime":28055.0,"Objects":[{"StartTime":28055.0,"Position":200.0,"HyperDash":false}]},{"StartTime":28164.0,"Objects":[{"StartTime":28164.0,"Position":131.0,"HyperDash":true}]},{"StartTime":28274.0,"Objects":[{"StartTime":28274.0,"Position":365.0,"HyperDash":false},{"StartTime":28328.0,"Position":386.782776,"HyperDash":false},{"StartTime":28383.0,"Position":416.967926,"HyperDash":false},{"StartTime":28437.0,"Position":433.199036,"HyperDash":false},{"StartTime":28492.0,"Position":453.031036,"HyperDash":false},{"StartTime":28583.0,"Position":429.885376,"HyperDash":false},{"StartTime":28711.0,"Position":350.852478,"HyperDash":false}]},{"StartTime":28931.0,"Objects":[{"StartTime":28931.0,"Position":170.0,"HyperDash":false}]},{"StartTime":29150.0,"Objects":[{"StartTime":29150.0,"Position":349.0,"HyperDash":false},{"StartTime":29204.0,"Position":363.954376,"HyperDash":false},{"StartTime":29259.0,"Position":416.5623,"HyperDash":false},{"StartTime":29313.0,"Position":458.929749,"HyperDash":false},{"StartTime":29368.0,"Position":469.713531,"HyperDash":false},{"StartTime":29459.0,"Position":506.401428,"HyperDash":false},{"StartTime":29587.0,"Position":474.2096,"HyperDash":false}]},{"StartTime":30026.0,"Objects":[{"StartTime":30026.0,"Position":114.0,"HyperDash":false}]},{"StartTime":30244.0,"Objects":[{"StartTime":30244.0,"Position":292.0,"HyperDash":false}]},{"StartTime":30463.0,"Objects":[{"StartTime":30463.0,"Position":114.0,"HyperDash":false},{"StartTime":30554.0,"Position":104.591461,"HyperDash":false},{"StartTime":30681.0,"Position":113.0213,"HyperDash":false}]},{"StartTime":30901.0,"Objects":[{"StartTime":30901.0,"Position":307.0,"HyperDash":false},{"StartTime":30992.0,"Position":296.817017,"HyperDash":false},{"StartTime":31119.0,"Position":308.957245,"HyperDash":false}]},{"StartTime":31230.0,"Objects":[{"StartTime":31230.0,"Position":197.0,"HyperDash":false},{"StartTime":31339.0,"Position":128.007462,"HyperDash":false}]},{"StartTime":31558.0,"Objects":[{"StartTime":31558.0,"Position":417.0,"HyperDash":false},{"StartTime":31667.0,"Position":417.932343,"HyperDash":true}]},{"StartTime":31777.0,"Objects":[{"StartTime":31777.0,"Position":148.0,"HyperDash":false}]},{"StartTime":31887.0,"Objects":[{"StartTime":31887.0,"Position":78.0,"HyperDash":false}]},{"StartTime":31996.0,"Objects":[{"StartTime":31996.0,"Position":148.0,"HyperDash":false}]},{"StartTime":32215.0,"Objects":[{"StartTime":32215.0,"Position":341.0,"HyperDash":false},{"StartTime":32306.0,"Position":339.731537,"HyperDash":false},{"StartTime":32433.0,"Position":342.362183,"HyperDash":false}]},{"StartTime":32544.0,"Objects":[{"StartTime":32544.0,"Position":265.0,"HyperDash":false}]},{"StartTime":32653.0,"Objects":[{"StartTime":32653.0,"Position":155.0,"HyperDash":false},{"StartTime":32744.0,"Position":115.395584,"HyperDash":false},{"StartTime":32871.0,"Position":17.0026245,"HyperDash":false}]},{"StartTime":32982.0,"Objects":[{"StartTime":32982.0,"Position":93.0,"HyperDash":true}]},{"StartTime":33091.0,"Objects":[{"StartTime":33091.0,"Position":292.0,"HyperDash":false}]},{"StartTime":33310.0,"Objects":[{"StartTime":33310.0,"Position":112.0,"HyperDash":false},{"StartTime":33419.0,"Position":110.057106,"HyperDash":true}]},{"StartTime":33529.0,"Objects":[{"StartTime":33529.0,"Position":327.0,"HyperDash":false}]},{"StartTime":33639.0,"Objects":[{"StartTime":33639.0,"Position":396.0,"HyperDash":false}]},{"StartTime":33748.0,"Objects":[{"StartTime":33748.0,"Position":327.0,"HyperDash":false}]},{"StartTime":33967.0,"Objects":[{"StartTime":33967.0,"Position":133.0,"HyperDash":false},{"StartTime":34058.0,"Position":142.165222,"HyperDash":false},{"StartTime":34185.0,"Position":131.000214,"HyperDash":false}]},{"StartTime":34296.0,"Objects":[{"StartTime":34296.0,"Position":207.0,"HyperDash":false}]},{"StartTime":34405.0,"Objects":[{"StartTime":34405.0,"Position":316.0,"HyperDash":false},{"StartTime":34496.0,"Position":277.3945,"HyperDash":false},{"StartTime":34623.0,"Position":178.0,"HyperDash":false}]},{"StartTime":34734.0,"Objects":[{"StartTime":34734.0,"Position":254.0,"HyperDash":true}]},{"StartTime":34843.0,"Objects":[{"StartTime":34843.0,"Position":453.0,"HyperDash":false},{"StartTime":34934.0,"Position":448.526672,"HyperDash":false},{"StartTime":35061.0,"Position":455.260864,"HyperDash":false}]},{"StartTime":35172.0,"Objects":[{"StartTime":35172.0,"Position":378.0,"HyperDash":true}]},{"StartTime":35281.0,"Objects":[{"StartTime":35281.0,"Position":145.0,"HyperDash":false}]},{"StartTime":35390.0,"Objects":[{"StartTime":35390.0,"Position":76.0,"HyperDash":false}]},{"StartTime":35500.0,"Objects":[{"StartTime":35500.0,"Position":145.0,"HyperDash":false}]},{"StartTime":35719.0,"Objects":[{"StartTime":35719.0,"Position":338.0,"HyperDash":false},{"StartTime":35810.0,"Position":332.840851,"HyperDash":false},{"StartTime":35937.0,"Position":340.014374,"HyperDash":false}]},{"StartTime":36047.0,"Objects":[{"StartTime":36047.0,"Position":263.0,"HyperDash":false}]},{"StartTime":36157.0,"Objects":[{"StartTime":36157.0,"Position":165.0,"HyperDash":false}]},{"StartTime":36266.0,"Objects":[{"StartTime":36266.0,"Position":263.0,"HyperDash":false}]},{"StartTime":36376.0,"Objects":[{"StartTime":36376.0,"Position":339.0,"HyperDash":false}]},{"StartTime":36485.0,"Objects":[{"StartTime":36485.0,"Position":263.0,"HyperDash":true}]},{"StartTime":36595.0,"Objects":[{"StartTime":36595.0,"Position":61.0,"HyperDash":false},{"StartTime":36686.0,"Position":51.31877,"HyperDash":false},{"StartTime":36813.0,"Position":59.2572021,"HyperDash":false}]},{"StartTime":36923.0,"Objects":[{"StartTime":36923.0,"Position":135.0,"HyperDash":true}]},{"StartTime":37033.0,"Objects":[{"StartTime":37033.0,"Position":371.0,"HyperDash":false}]},{"StartTime":37142.0,"Objects":[{"StartTime":37142.0,"Position":439.0,"HyperDash":false}]},{"StartTime":37252.0,"Objects":[{"StartTime":37252.0,"Position":371.0,"HyperDash":false}]},{"StartTime":37471.0,"Objects":[{"StartTime":37471.0,"Position":177.0,"HyperDash":false},{"StartTime":37562.0,"Position":246.6055,"HyperDash":false},{"StartTime":37689.0,"Position":315.0,"HyperDash":false}]},{"StartTime":37799.0,"Objects":[{"StartTime":37799.0,"Position":238.0,"HyperDash":false}]},{"StartTime":37909.0,"Objects":[{"StartTime":37909.0,"Position":127.0,"HyperDash":false},{"StartTime":38000.0,"Position":113.171227,"HyperDash":false},{"StartTime":38127.0,"Position":125.014595,"HyperDash":false}]},{"StartTime":38237.0,"Objects":[{"StartTime":38237.0,"Position":201.0,"HyperDash":true}]},{"StartTime":38347.0,"Objects":[{"StartTime":38347.0,"Position":402.0,"HyperDash":false},{"StartTime":38438.0,"Position":395.763641,"HyperDash":false},{"StartTime":38565.0,"Position":404.707062,"HyperDash":false}]},{"StartTime":38675.0,"Objects":[{"StartTime":38675.0,"Position":328.0,"HyperDash":true}]},{"StartTime":38785.0,"Objects":[{"StartTime":38785.0,"Position":92.0,"HyperDash":false}]},{"StartTime":38894.0,"Objects":[{"StartTime":38894.0,"Position":23.0,"HyperDash":false}]},{"StartTime":39004.0,"Objects":[{"StartTime":39004.0,"Position":92.0,"HyperDash":false}]},{"StartTime":39223.0,"Objects":[{"StartTime":39223.0,"Position":285.0,"HyperDash":false},{"StartTime":39314.0,"Position":323.6055,"HyperDash":false},{"StartTime":39441.0,"Position":423.0,"HyperDash":false}]},{"StartTime":39551.0,"Objects":[{"StartTime":39551.0,"Position":346.0,"HyperDash":false}]},{"StartTime":39661.0,"Objects":[{"StartTime":39661.0,"Position":235.0,"HyperDash":false},{"StartTime":39770.0,"Position":234.054886,"HyperDash":false}]},{"StartTime":39880.0,"Objects":[{"StartTime":39880.0,"Position":344.0,"HyperDash":false},{"StartTime":39989.0,"Position":345.9429,"HyperDash":true}]},{"StartTime":40099.0,"Objects":[{"StartTime":40099.0,"Position":144.0,"HyperDash":false},{"StartTime":40190.0,"Position":89.39449,"HyperDash":false},{"StartTime":40317.0,"Position":6.0,"HyperDash":false}]},{"StartTime":40427.0,"Objects":[{"StartTime":40427.0,"Position":82.0,"HyperDash":true}]},{"StartTime":40536.0,"Objects":[{"StartTime":40536.0,"Position":315.0,"HyperDash":false}]},{"StartTime":40646.0,"Objects":[{"StartTime":40646.0,"Position":384.0,"HyperDash":false}]},{"StartTime":40755.0,"Objects":[{"StartTime":40755.0,"Position":315.0,"HyperDash":false}]},{"StartTime":40974.0,"Objects":[{"StartTime":40974.0,"Position":121.0,"HyperDash":false},{"StartTime":41065.0,"Position":106.171227,"HyperDash":false},{"StartTime":41192.0,"Position":119.014595,"HyperDash":false}]},{"StartTime":41303.0,"Objects":[{"StartTime":41303.0,"Position":195.0,"HyperDash":true}]},{"StartTime":41412.0,"Objects":[{"StartTime":41412.0,"Position":394.0,"HyperDash":false}]},{"StartTime":41631.0,"Objects":[{"StartTime":41631.0,"Position":214.0,"HyperDash":false}]},{"StartTime":41741.0,"Objects":[{"StartTime":41741.0,"Position":144.0,"HyperDash":false}]},{"StartTime":41850.0,"Objects":[{"StartTime":41850.0,"Position":214.0,"HyperDash":false}]},{"StartTime":42069.0,"Objects":[{"StartTime":42069.0,"Position":407.0,"HyperDash":false},{"StartTime":42178.0,"Position":476.0,"HyperDash":true}]},{"StartTime":42288.0,"Objects":[{"StartTime":42288.0,"Position":240.0,"HyperDash":false}]},{"StartTime":42398.0,"Objects":[{"StartTime":42398.0,"Position":170.0,"HyperDash":false}]},{"StartTime":42507.0,"Objects":[{"StartTime":42507.0,"Position":240.0,"HyperDash":false}]},{"StartTime":42726.0,"Objects":[{"StartTime":42726.0,"Position":419.0,"HyperDash":false}]},{"StartTime":42945.0,"Objects":[{"StartTime":42945.0,"Position":129.0,"HyperDash":false},{"StartTime":43054.0,"Position":128.028259,"HyperDash":false}]},{"StartTime":43164.0,"Objects":[{"StartTime":43164.0,"Position":238.0,"HyperDash":false},{"StartTime":43255.0,"Position":301.604065,"HyperDash":false},{"StartTime":43382.0,"Position":375.996582,"HyperDash":false}]},{"StartTime":43493.0,"Objects":[{"StartTime":43493.0,"Position":299.0,"HyperDash":false}]},{"StartTime":43602.0,"Objects":[{"StartTime":43602.0,"Position":195.0,"HyperDash":false}]},{"StartTime":43821.0,"Objects":[{"StartTime":43821.0,"Position":374.0,"HyperDash":false}]},{"StartTime":43931.0,"Objects":[{"StartTime":43931.0,"Position":376.0,"HyperDash":true}]},{"StartTime":44040.0,"Objects":[{"StartTime":44040.0,"Position":108.0,"HyperDash":false}]},{"StartTime":44150.0,"Objects":[{"StartTime":44150.0,"Position":106.0,"HyperDash":false}]},{"StartTime":44259.0,"Objects":[{"StartTime":44259.0,"Position":209.0,"HyperDash":false}]},{"StartTime":44478.0,"Objects":[{"StartTime":44478.0,"Position":388.0,"HyperDash":false}]},{"StartTime":44697.0,"Objects":[{"StartTime":44697.0,"Position":195.0,"HyperDash":false}]},{"StartTime":44916.0,"Objects":[{"StartTime":44916.0,"Position":484.0,"HyperDash":false}]},{"StartTime":45026.0,"Objects":[{"StartTime":45026.0,"Position":407.0,"HyperDash":false}]},{"StartTime":45244.0,"Objects":[{"StartTime":45244.0,"Position":213.0,"HyperDash":false}]},{"StartTime":45354.0,"Objects":[{"StartTime":45354.0,"Position":316.0,"HyperDash":false},{"StartTime":45445.0,"Position":386.604126,"HyperDash":false},{"StartTime":45572.0,"Position":453.996674,"HyperDash":true}]},{"StartTime":45792.0,"Objects":[{"StartTime":45792.0,"Position":103.0,"HyperDash":false},{"StartTime":45846.0,"Position":59.25476,"HyperDash":false},{"StartTime":45901.0,"Position":32.3233032,"HyperDash":false},{"StartTime":45955.0,"Position":30.0666771,"HyperDash":false},{"StartTime":46010.0,"Position":14.6513605,"HyperDash":false},{"StartTime":46101.0,"Position":45.94827,"HyperDash":false},{"StartTime":46229.0,"Position":114.217232,"HyperDash":false}]},{"StartTime":46449.0,"Objects":[{"StartTime":46449.0,"Position":294.0,"HyperDash":false},{"StartTime":46503.0,"Position":260.4281,"HyperDash":false},{"StartTime":46558.0,"Position":236.271561,"HyperDash":false},{"StartTime":46612.0,"Position":192.699677,"HyperDash":false},{"StartTime":46667.0,"Position":166.543121,"HyperDash":false},{"StartTime":46758.0,"Position":132.338623,"HyperDash":false},{"StartTime":46886.0,"Position":38.5015564,"HyperDash":false}]},{"StartTime":47106.0,"Objects":[{"StartTime":47106.0,"Position":204.0,"HyperDash":false}]},{"StartTime":47325.0,"Objects":[{"StartTime":47325.0,"Position":38.0,"HyperDash":true}]},{"StartTime":47544.0,"Objects":[{"StartTime":47544.0,"Position":355.0,"HyperDash":false},{"StartTime":47598.0,"Position":383.787079,"HyperDash":false},{"StartTime":47653.0,"Position":419.23172,"HyperDash":false},{"StartTime":47707.0,"Position":453.6615,"HyperDash":false},{"StartTime":47762.0,"Position":443.4226,"HyperDash":false},{"StartTime":47853.0,"Position":433.484253,"HyperDash":false},{"StartTime":47981.0,"Position":340.2439,"HyperDash":false}]},{"StartTime":48201.0,"Objects":[{"StartTime":48201.0,"Position":173.0,"HyperDash":false}]},{"StartTime":48420.0,"Objects":[{"StartTime":48420.0,"Position":338.0,"HyperDash":false},{"StartTime":48474.0,"Position":355.147217,"HyperDash":false},{"StartTime":48529.0,"Position":351.588867,"HyperDash":false},{"StartTime":48583.0,"Position":355.8642,"HyperDash":false},{"StartTime":48638.0,"Position":329.530029,"HyperDash":false},{"StartTime":48729.0,"Position":296.7339,"HyperDash":false},{"StartTime":48857.0,"Position":203.29097,"HyperDash":false}]},{"StartTime":49077.0,"Objects":[{"StartTime":49077.0,"Position":369.0,"HyperDash":true}]},{"StartTime":49296.0,"Objects":[{"StartTime":49296.0,"Position":51.0,"HyperDash":false},{"StartTime":49387.0,"Position":38.1829834,"HyperDash":false},{"StartTime":49514.0,"Position":49.04275,"HyperDash":false}]},{"StartTime":49734.0,"Objects":[{"StartTime":49734.0,"Position":229.0,"HyperDash":false},{"StartTime":49825.0,"Position":270.604065,"HyperDash":false},{"StartTime":49952.0,"Position":366.996582,"HyperDash":false}]},{"StartTime":50172.0,"Objects":[{"StartTime":50172.0,"Position":186.0,"HyperDash":false},{"StartTime":50263.0,"Position":121.395981,"HyperDash":false},{"StartTime":50390.0,"Position":48.00357,"HyperDash":false}]},{"StartTime":50609.0,"Objects":[{"StartTime":50609.0,"Position":227.0,"HyperDash":false}]},{"StartTime":50828.0,"Objects":[{"StartTime":50828.0,"Position":47.0,"HyperDash":true}]},{"StartTime":51047.0,"Objects":[{"StartTime":51047.0,"Position":347.0,"HyperDash":false},{"StartTime":51101.0,"Position":362.642029,"HyperDash":false},{"StartTime":51156.0,"Position":410.800537,"HyperDash":false},{"StartTime":51210.0,"Position":450.264282,"HyperDash":false},{"StartTime":51265.0,"Position":470.407257,"HyperDash":false},{"StartTime":51356.0,"Position":491.032837,"HyperDash":false},{"StartTime":51484.0,"Position":477.784576,"HyperDash":false}]},{"StartTime":51923.0,"Objects":[{"StartTime":51923.0,"Position":118.0,"HyperDash":false},{"StartTime":52014.0,"Position":119.348648,"HyperDash":false},{"StartTime":52141.0,"Position":119.0904,"HyperDash":false}]},{"StartTime":52361.0,"Objects":[{"StartTime":52361.0,"Position":313.0,"HyperDash":false}]},{"StartTime":52580.0,"Objects":[{"StartTime":52580.0,"Position":119.0,"HyperDash":true}]},{"StartTime":52799.0,"Objects":[{"StartTime":52799.0,"Position":436.0,"HyperDash":false},{"StartTime":52853.0,"Position":399.0876,"HyperDash":false},{"StartTime":52908.0,"Position":381.54715,"HyperDash":false},{"StartTime":52962.0,"Position":320.634735,"HyperDash":false},{"StartTime":53017.0,"Position":299.0943,"HyperDash":false},{"StartTime":53108.0,"Position":229.9456,"HyperDash":false},{"StartTime":53236.0,"Position":161.560608,"HyperDash":false}]},{"StartTime":53456.0,"Objects":[{"StartTime":53456.0,"Position":452.0,"HyperDash":false}]},{"StartTime":53566.0,"Objects":[{"StartTime":53566.0,"Position":489.0,"HyperDash":false}]},{"StartTime":53675.0,"Objects":[{"StartTime":53675.0,"Position":454.0,"HyperDash":false}]},{"StartTime":53894.0,"Objects":[{"StartTime":53894.0,"Position":274.0,"HyperDash":false}]},{"StartTime":54113.0,"Objects":[{"StartTime":54113.0,"Position":454.0,"HyperDash":false},{"StartTime":54204.0,"Position":399.395721,"HyperDash":false},{"StartTime":54331.0,"Position":316.00293,"HyperDash":true}]},{"StartTime":54551.0,"Objects":[{"StartTime":54551.0,"Position":24.0,"HyperDash":false},{"StartTime":54605.0,"Position":67.96123,"HyperDash":false},{"StartTime":54660.0,"Position":88.55135,"HyperDash":false},{"StartTime":54714.0,"Position":106.512581,"HyperDash":false},{"StartTime":54769.0,"Position":161.1027,"HyperDash":false},{"StartTime":54860.0,"Position":223.333679,"HyperDash":false},{"StartTime":54988.0,"Position":298.834351,"HyperDash":false}]},{"StartTime":55208.0,"Objects":[{"StartTime":55208.0,"Position":104.0,"HyperDash":false}]},{"StartTime":55317.0,"Objects":[{"StartTime":55317.0,"Position":62.0,"HyperDash":false}]},{"StartTime":55427.0,"Objects":[{"StartTime":55427.0,"Position":104.0,"HyperDash":false}]},{"StartTime":55646.0,"Objects":[{"StartTime":55646.0,"Position":393.0,"HyperDash":false},{"StartTime":55737.0,"Position":340.600342,"HyperDash":false},{"StartTime":55864.0,"Position":267.4712,"HyperDash":false}]},{"StartTime":56084.0,"Objects":[{"StartTime":56084.0,"Position":87.0,"HyperDash":true}]},{"StartTime":56303.0,"Objects":[{"StartTime":56303.0,"Position":432.0,"HyperDash":false},{"StartTime":56357.0,"Position":388.775055,"HyperDash":false},{"StartTime":56412.0,"Position":359.8976,"HyperDash":false},{"StartTime":56466.0,"Position":338.6523,"HyperDash":false},{"StartTime":56521.0,"Position":318.247742,"HyperDash":false},{"StartTime":56612.0,"Position":256.532,"HyperDash":false},{"StartTime":56740.0,"Position":183.343277,"HyperDash":false}]},{"StartTime":56960.0,"Objects":[{"StartTime":56960.0,"Position":365.0,"HyperDash":false}]},{"StartTime":57179.0,"Objects":[{"StartTime":57179.0,"Position":75.0,"HyperDash":false},{"StartTime":57270.0,"Position":148.586823,"HyperDash":false},{"StartTime":57397.0,"Position":212.955231,"HyperDash":false}]},{"StartTime":57617.0,"Objects":[{"StartTime":57617.0,"Position":407.0,"HyperDash":false},{"StartTime":57708.0,"Position":422.1916,"HyperDash":false},{"StartTime":57835.0,"Position":409.854553,"HyperDash":true}]},{"StartTime":58055.0,"Objects":[{"StartTime":58055.0,"Position":118.0,"HyperDash":false},{"StartTime":58109.0,"Position":145.079269,"HyperDash":false},{"StartTime":58164.0,"Position":167.789642,"HyperDash":false},{"StartTime":58218.0,"Position":202.8689,"HyperDash":false},{"StartTime":58273.0,"Position":255.579269,"HyperDash":false},{"StartTime":58328.0,"Position":291.2896,"HyperDash":false},{"StartTime":58383.0,"Position":325.0,"HyperDash":false},{"StartTime":58437.0,"Position":294.920746,"HyperDash":false},{"StartTime":58492.0,"Position":256.210358,"HyperDash":false},{"StartTime":58583.0,"Position":185.780487,"HyperDash":false},{"StartTime":58711.0,"Position":118.0,"HyperDash":false}]},{"StartTime":58931.0,"Objects":[{"StartTime":58931.0,"Position":312.0,"HyperDash":false},{"StartTime":58985.0,"Position":312.3719,"HyperDash":false},{"StartTime":59040.0,"Position":318.289,"HyperDash":false},{"StartTime":59094.0,"Position":279.119,"HyperDash":false},{"StartTime":59149.0,"Position":279.84906,"HyperDash":false},{"StartTime":59203.0,"Position":240.6435,"HyperDash":false},{"StartTime":59258.0,"Position":241.519592,"HyperDash":false},{"StartTime":59313.0,"Position":180.829834,"HyperDash":false},{"StartTime":59368.0,"Position":166.661133,"HyperDash":false},{"StartTime":59459.0,"Position":118.512878,"HyperDash":false},{"StartTime":59587.0,"Position":33.8594971,"HyperDash":true}]},{"StartTime":59807.0,"Objects":[{"StartTime":59807.0,"Position":380.0,"HyperDash":false},{"StartTime":59898.0,"Position":403.4091,"HyperDash":false},{"StartTime":60025.0,"Position":380.555023,"HyperDash":false}]},{"StartTime":60135.0,"Objects":[{"StartTime":60135.0,"Position":290.0,"HyperDash":false}]},{"StartTime":60244.0,"Objects":[{"StartTime":60244.0,"Position":380.0,"HyperDash":false},{"StartTime":60353.0,"Position":381.815155,"HyperDash":true}]},{"StartTime":60463.0,"Objects":[{"StartTime":60463.0,"Position":180.0,"HyperDash":false},{"StartTime":60572.0,"Position":111.0,"HyperDash":true}]},{"StartTime":60682.0,"Objects":[{"StartTime":60682.0,"Position":346.0,"HyperDash":false},{"StartTime":60791.0,"Position":346.0,"HyperDash":true}]},{"StartTime":60901.0,"Objects":[{"StartTime":60901.0,"Position":144.0,"HyperDash":true}]},{"StartTime":61011.0,"Objects":[{"StartTime":61011.0,"Position":345.0,"HyperDash":false}]},{"StartTime":61120.0,"Objects":[{"StartTime":61120.0,"Position":441.0,"HyperDash":false},{"StartTime":61211.0,"Position":474.310272,"HyperDash":false},{"StartTime":61338.0,"Position":439.1717,"HyperDash":false}]},{"StartTime":61449.0,"Objects":[{"StartTime":61449.0,"Position":355.0,"HyperDash":true}]},{"StartTime":61558.0,"Objects":[{"StartTime":61558.0,"Position":121.0,"HyperDash":false},{"StartTime":61667.0,"Position":120.041756,"HyperDash":true}]},{"StartTime":61777.0,"Objects":[{"StartTime":61777.0,"Position":321.0,"HyperDash":true}]},{"StartTime":61887.0,"Objects":[{"StartTime":61887.0,"Position":120.0,"HyperDash":false}]},{"StartTime":61996.0,"Objects":[{"StartTime":61996.0,"Position":23.0,"HyperDash":false},{"StartTime":62087.0,"Position":92.6042938,"HyperDash":false},{"StartTime":62214.0,"Position":160.997086,"HyperDash":false}]},{"StartTime":62325.0,"Objects":[{"StartTime":62325.0,"Position":63.0,"HyperDash":true}]},{"StartTime":62434.0,"Objects":[{"StartTime":62434.0,"Position":296.0,"HyperDash":false},{"StartTime":62543.0,"Position":296.971741,"HyperDash":false}]},{"StartTime":62653.0,"Objects":[{"StartTime":62653.0,"Position":199.0,"HyperDash":true}]},{"StartTime":62763.0,"Objects":[{"StartTime":62763.0,"Position":400.0,"HyperDash":false}]},{"StartTime":62872.0,"Objects":[{"StartTime":62872.0,"Position":303.0,"HyperDash":false},{"StartTime":62963.0,"Position":294.5297,"HyperDash":false},{"StartTime":63090.0,"Position":354.147156,"HyperDash":false}]},{"StartTime":63201.0,"Objects":[{"StartTime":63201.0,"Position":438.0,"HyperDash":true}]},{"StartTime":63310.0,"Objects":[{"StartTime":63310.0,"Position":204.0,"HyperDash":false},{"StartTime":63401.0,"Position":148.549332,"HyperDash":false},{"StartTime":63528.0,"Position":93.9026642,"HyperDash":false}]},{"StartTime":63639.0,"Objects":[{"StartTime":63639.0,"Position":184.0,"HyperDash":false}]},{"StartTime":63748.0,"Objects":[{"StartTime":63748.0,"Position":93.0,"HyperDash":false},{"StartTime":63857.0,"Position":92.17863,"HyperDash":true}]},{"StartTime":63967.0,"Objects":[{"StartTime":63967.0,"Position":293.0,"HyperDash":false},{"StartTime":64076.0,"Position":293.919922,"HyperDash":true}]},{"StartTime":64186.0,"Objects":[{"StartTime":64186.0,"Position":93.0,"HyperDash":true}]},{"StartTime":64296.0,"Objects":[{"StartTime":64296.0,"Position":293.0,"HyperDash":false},{"StartTime":64405.0,"Position":362.0,"HyperDash":true}]},{"StartTime":64515.0,"Objects":[{"StartTime":64515.0,"Position":160.0,"HyperDash":false}]},{"StartTime":64624.0,"Objects":[{"StartTime":64624.0,"Position":63.0,"HyperDash":false},{"StartTime":64715.0,"Position":16.49675,"HyperDash":false},{"StartTime":64842.0,"Position":70.69653,"HyperDash":false}]},{"StartTime":64953.0,"Objects":[{"StartTime":64953.0,"Position":154.0,"HyperDash":true}]},{"StartTime":65062.0,"Objects":[{"StartTime":65062.0,"Position":387.0,"HyperDash":false},{"StartTime":65171.0,"Position":318.007446,"HyperDash":true}]},{"StartTime":65281.0,"Objects":[{"StartTime":65281.0,"Position":116.0,"HyperDash":true}]},{"StartTime":65390.0,"Objects":[{"StartTime":65390.0,"Position":318.0,"HyperDash":false}]},{"StartTime":65500.0,"Objects":[{"StartTime":65500.0,"Position":415.0,"HyperDash":false},{"StartTime":65591.0,"Position":455.432068,"HyperDash":false},{"StartTime":65718.0,"Position":412.315582,"HyperDash":false}]},{"StartTime":65828.0,"Objects":[{"StartTime":65828.0,"Position":315.0,"HyperDash":true}]},{"StartTime":65938.0,"Objects":[{"StartTime":65938.0,"Position":79.0,"HyperDash":false},{"StartTime":66047.0,"Position":78.01439,"HyperDash":false}]},{"StartTime":66157.0,"Objects":[{"StartTime":66157.0,"Position":175.0,"HyperDash":true}]},{"StartTime":66266.0,"Objects":[{"StartTime":66266.0,"Position":374.0,"HyperDash":false}]},{"StartTime":66376.0,"Objects":[{"StartTime":66376.0,"Position":276.0,"HyperDash":false},{"StartTime":66467.0,"Position":321.6042,"HyperDash":false},{"StartTime":66594.0,"Position":413.996857,"HyperDash":false}]},{"StartTime":66704.0,"Objects":[{"StartTime":66704.0,"Position":331.0,"HyperDash":true}]},{"StartTime":66814.0,"Objects":[{"StartTime":66814.0,"Position":60.0,"HyperDash":false},{"StartTime":66905.0,"Position":21.5649185,"HyperDash":false},{"StartTime":67032.0,"Position":61.75552,"HyperDash":false}]},{"StartTime":67142.0,"Objects":[{"StartTime":67142.0,"Position":151.0,"HyperDash":false}]},{"StartTime":67252.0,"Objects":[{"StartTime":67252.0,"Position":61.0,"HyperDash":true}]},{"StartTime":67471.0,"Objects":[{"StartTime":67471.0,"Position":378.0,"HyperDash":false}]},{"StartTime":67580.0,"Objects":[{"StartTime":67580.0,"Position":422.0,"HyperDash":false}]},{"StartTime":67690.0,"Objects":[{"StartTime":67690.0,"Position":381.0,"HyperDash":false}]},{"StartTime":67799.0,"Objects":[{"StartTime":67799.0,"Position":305.0,"HyperDash":false}]},{"StartTime":67909.0,"Objects":[{"StartTime":67909.0,"Position":194.0,"HyperDash":false},{"StartTime":68018.0,"Position":193.103973,"HyperDash":true}]},{"StartTime":68128.0,"Objects":[{"StartTime":68128.0,"Position":428.0,"HyperDash":false},{"StartTime":68219.0,"Position":351.3945,"HyperDash":false},{"StartTime":68346.0,"Position":290.0,"HyperDash":false}]},{"StartTime":68456.0,"Objects":[{"StartTime":68456.0,"Position":373.0,"HyperDash":true}]},{"StartTime":68566.0,"Objects":[{"StartTime":68566.0,"Position":137.0,"HyperDash":false},{"StartTime":68675.0,"Position":135.057114,"HyperDash":false}]},{"StartTime":68785.0,"Objects":[{"StartTime":68785.0,"Position":245.0,"HyperDash":false},{"StartTime":68894.0,"Position":245.896027,"HyperDash":true}]},{"StartTime":69004.0,"Objects":[{"StartTime":69004.0,"Position":44.0,"HyperDash":false},{"StartTime":69095.0,"Position":103.604172,"HyperDash":false},{"StartTime":69222.0,"Position":181.9968,"HyperDash":false}]},{"StartTime":69332.0,"Objects":[{"StartTime":69332.0,"Position":98.0,"HyperDash":true}]},{"StartTime":69442.0,"Objects":[{"StartTime":69442.0,"Position":333.0,"HyperDash":false},{"StartTime":69551.0,"Position":334.768646,"HyperDash":true}]},{"StartTime":69661.0,"Objects":[{"StartTime":69661.0,"Position":133.0,"HyperDash":false}]},{"StartTime":69880.0,"Objects":[{"StartTime":69880.0,"Position":326.0,"HyperDash":false}]},{"StartTime":70099.0,"Objects":[{"StartTime":70099.0,"Position":133.0,"HyperDash":false},{"StartTime":70208.0,"Position":131.084076,"HyperDash":true}]},{"StartTime":70317.0,"Objects":[{"StartTime":70317.0,"Position":398.0,"HyperDash":false},{"StartTime":70371.0,"Position":358.896545,"HyperDash":false},{"StartTime":70426.0,"Position":310.16153,"HyperDash":false},{"StartTime":70480.0,"Position":280.058075,"HyperDash":false},{"StartTime":70535.0,"Position":260.323059,"HyperDash":false},{"StartTime":70626.0,"Position":200.8524,"HyperDash":false},{"StartTime":70754.0,"Position":122.014557,"HyperDash":true}]},{"StartTime":70974.0,"Objects":[{"StartTime":70974.0,"Position":468.0,"HyperDash":false},{"StartTime":71028.0,"Position":427.894928,"HyperDash":false},{"StartTime":71083.0,"Position":386.1583,"HyperDash":false},{"StartTime":71137.0,"Position":356.053223,"HyperDash":false},{"StartTime":71192.0,"Position":330.3166,"HyperDash":false},{"StartTime":71283.0,"Position":261.843262,"HyperDash":false},{"StartTime":71411.0,"Position":192.001617,"HyperDash":false}]},{"StartTime":71631.0,"Objects":[{"StartTime":71631.0,"Position":483.0,"HyperDash":false},{"StartTime":71722.0,"Position":425.3945,"HyperDash":false},{"StartTime":71849.0,"Position":345.0,"HyperDash":true}]},{"StartTime":72069.0,"Objects":[{"StartTime":72069.0,"Position":26.0,"HyperDash":false},{"StartTime":72123.0,"Position":46.07927,"HyperDash":false},{"StartTime":72178.0,"Position":95.7896347,"HyperDash":false},{"StartTime":72232.0,"Position":143.8689,"HyperDash":false},{"StartTime":72287.0,"Position":163.579269,"HyperDash":false},{"StartTime":72342.0,"Position":192.289627,"HyperDash":false},{"StartTime":72397.0,"Position":233.0,"HyperDash":false},{"StartTime":72451.0,"Position":215.920746,"HyperDash":false},{"StartTime":72506.0,"Position":164.210358,"HyperDash":false},{"StartTime":72597.0,"Position":121.780487,"HyperDash":false},{"StartTime":72725.0,"Position":26.0,"HyperDash":true}]},{"StartTime":72945.0,"Objects":[{"StartTime":72945.0,"Position":344.0,"HyperDash":false},{"StartTime":72999.0,"Position":395.749939,"HyperDash":false},{"StartTime":73054.0,"Position":392.115662,"HyperDash":false},{"StartTime":73108.0,"Position":414.29538,"HyperDash":false},{"StartTime":73163.0,"Position":437.262848,"HyperDash":false},{"StartTime":73254.0,"Position":425.923279,"HyperDash":false},{"StartTime":73382.0,"Position":338.6331,"HyperDash":false}]},{"StartTime":73493.0,"Objects":[{"StartTime":73493.0,"Position":247.0,"HyperDash":false}]},{"StartTime":73602.0,"Objects":[{"StartTime":73602.0,"Position":338.0,"HyperDash":true}]},{"StartTime":73712.0,"Objects":[{"StartTime":73712.0,"Position":102.0,"HyperDash":true}]},{"StartTime":73821.0,"Objects":[{"StartTime":73821.0,"Position":338.0,"HyperDash":false},{"StartTime":73912.0,"Position":386.8002,"HyperDash":false},{"StartTime":74039.0,"Position":335.152557,"HyperDash":false}]},{"StartTime":74150.0,"Objects":[{"StartTime":74150.0,"Position":244.0,"HyperDash":false}]},{"StartTime":74259.0,"Objects":[{"StartTime":74259.0,"Position":334.0,"HyperDash":false},{"StartTime":74368.0,"Position":334.958252,"HyperDash":true}]},{"StartTime":74478.0,"Objects":[{"StartTime":74478.0,"Position":133.0,"HyperDash":false},{"StartTime":74587.0,"Position":131.253723,"HyperDash":true}]},{"StartTime":74697.0,"Objects":[{"StartTime":74697.0,"Position":366.0,"HyperDash":false},{"StartTime":74806.0,"Position":366.896027,"HyperDash":true}]},{"StartTime":74916.0,"Objects":[{"StartTime":74916.0,"Position":165.0,"HyperDash":true}]},{"StartTime":75026.0,"Objects":[{"StartTime":75026.0,"Position":366.0,"HyperDash":false}]},{"StartTime":75135.0,"Objects":[{"StartTime":75135.0,"Position":462.0,"HyperDash":false},{"StartTime":75226.0,"Position":396.3945,"HyperDash":false},{"StartTime":75353.0,"Position":324.0,"HyperDash":false}]},{"StartTime":75463.0,"Objects":[{"StartTime":75463.0,"Position":407.0,"HyperDash":true}]},{"StartTime":75573.0,"Objects":[{"StartTime":75573.0,"Position":171.0,"HyperDash":false},{"StartTime":75682.0,"Position":169.3576,"HyperDash":true}]},{"StartTime":75792.0,"Objects":[{"StartTime":75792.0,"Position":370.0,"HyperDash":true}]},{"StartTime":75901.0,"Objects":[{"StartTime":75901.0,"Position":170.0,"HyperDash":false}]},{"StartTime":76011.0,"Objects":[{"StartTime":76011.0,"Position":72.0,"HyperDash":false},{"StartTime":76102.0,"Position":31.1678276,"HyperDash":false},{"StartTime":76229.0,"Position":82.8498,"HyperDash":false}]},{"StartTime":76339.0,"Objects":[{"StartTime":76339.0,"Position":179.0,"HyperDash":true}]},{"StartTime":76449.0,"Objects":[{"StartTime":76449.0,"Position":414.0,"HyperDash":false},{"StartTime":76558.0,"Position":483.0,"HyperDash":false}]},{"StartTime":76668.0,"Objects":[{"StartTime":76668.0,"Position":385.0,"HyperDash":true}]},{"StartTime":76777.0,"Objects":[{"StartTime":76777.0,"Position":185.0,"HyperDash":false}]},{"StartTime":76887.0,"Objects":[{"StartTime":76887.0,"Position":282.0,"HyperDash":false},{"StartTime":76978.0,"Position":335.60437,"HyperDash":false},{"StartTime":77105.0,"Position":419.9973,"HyperDash":false}]},{"StartTime":77215.0,"Objects":[{"StartTime":77215.0,"Position":336.0,"HyperDash":true}]},{"StartTime":77325.0,"Objects":[{"StartTime":77325.0,"Position":100.0,"HyperDash":false},{"StartTime":77416.0,"Position":88.294014,"HyperDash":false},{"StartTime":77543.0,"Position":102.248474,"HyperDash":false}]},{"StartTime":77653.0,"Objects":[{"StartTime":77653.0,"Position":192.0,"HyperDash":false}]},{"StartTime":77763.0,"Objects":[{"StartTime":77763.0,"Position":102.0,"HyperDash":false},{"StartTime":77872.0,"Position":100.2084,"HyperDash":true}]},{"StartTime":77982.0,"Objects":[{"StartTime":77982.0,"Position":301.0,"HyperDash":false},{"StartTime":78091.0,"Position":370.0,"HyperDash":true}]},{"StartTime":78201.0,"Objects":[{"StartTime":78201.0,"Position":134.0,"HyperDash":false},{"StartTime":78310.0,"Position":133.028259,"HyperDash":true}]},{"StartTime":78420.0,"Objects":[{"StartTime":78420.0,"Position":334.0,"HyperDash":true}]},{"StartTime":78529.0,"Objects":[{"StartTime":78529.0,"Position":135.0,"HyperDash":false}]},{"StartTime":78639.0,"Objects":[{"StartTime":78639.0,"Position":37.0,"HyperDash":false},{"StartTime":78730.0,"Position":18.6601868,"HyperDash":false},{"StartTime":78857.0,"Position":64.53217,"HyperDash":false}]},{"StartTime":78967.0,"Objects":[{"StartTime":78967.0,"Position":147.0,"HyperDash":true}]},{"StartTime":79077.0,"Objects":[{"StartTime":79077.0,"Position":382.0,"HyperDash":false},{"StartTime":79186.0,"Position":384.028534,"HyperDash":false}]},{"StartTime":79296.0,"Objects":[{"StartTime":79296.0,"Position":273.0,"HyperDash":false},{"StartTime":79405.0,"Position":270.971466,"HyperDash":true}]},{"StartTime":79515.0,"Objects":[{"StartTime":79515.0,"Position":472.0,"HyperDash":false},{"StartTime":79624.0,"Position":473.915924,"HyperDash":true}]},{"StartTime":79734.0,"Objects":[{"StartTime":79734.0,"Position":203.0,"HyperDash":false},{"StartTime":79843.0,"Position":134.006836,"HyperDash":false}]},{"StartTime":79953.0,"Objects":[{"StartTime":79953.0,"Position":244.0,"HyperDash":false},{"StartTime":80062.0,"Position":313.0,"HyperDash":true}]},{"StartTime":80172.0,"Objects":[{"StartTime":80172.0,"Position":111.0,"HyperDash":false},{"StartTime":80281.0,"Position":108.002831,"HyperDash":true}]},{"StartTime":80390.0,"Objects":[{"StartTime":80390.0,"Position":307.0,"HyperDash":false},{"StartTime":80499.0,"Position":376.0,"HyperDash":true}]},{"StartTime":80609.0,"Objects":[{"StartTime":80609.0,"Position":140.0,"HyperDash":false},{"StartTime":80718.0,"Position":71.0,"HyperDash":true}]},{"StartTime":80828.0,"Objects":[{"StartTime":80828.0,"Position":341.0,"HyperDash":false},{"StartTime":80919.0,"Position":398.6055,"HyperDash":false},{"StartTime":81046.0,"Position":479.0,"HyperDash":false}]},{"StartTime":81157.0,"Objects":[{"StartTime":81157.0,"Position":388.0,"HyperDash":false}]},{"StartTime":81266.0,"Objects":[{"StartTime":81266.0,"Position":476.0,"HyperDash":true}]},{"StartTime":81485.0,"Objects":[{"StartTime":81485.0,"Position":161.0,"HyperDash":false}]},{"StartTime":81595.0,"Objects":[{"StartTime":81595.0,"Position":124.0,"HyperDash":false}]},{"StartTime":81704.0,"Objects":[{"StartTime":81704.0,"Position":166.0,"HyperDash":false}]},{"StartTime":81814.0,"Objects":[{"StartTime":81814.0,"Position":242.0,"HyperDash":false}]},{"StartTime":81923.0,"Objects":[{"StartTime":81923.0,"Position":351.0,"HyperDash":false},{"StartTime":82032.0,"Position":351.9999,"HyperDash":true}]},{"StartTime":82142.0,"Objects":[{"StartTime":82142.0,"Position":150.0,"HyperDash":false}]},{"StartTime":82252.0,"Objects":[{"StartTime":82252.0,"Position":74.0,"HyperDash":false}]},{"StartTime":82361.0,"Objects":[{"StartTime":82361.0,"Position":84.0,"HyperDash":false}]},{"StartTime":82471.0,"Objects":[{"StartTime":82471.0,"Position":166.0,"HyperDash":true}]},{"StartTime":82580.0,"Objects":[{"StartTime":82580.0,"Position":399.0,"HyperDash":false}]},{"StartTime":82690.0,"Objects":[{"StartTime":82690.0,"Position":442.0,"HyperDash":false}]},{"StartTime":82799.0,"Objects":[{"StartTime":82799.0,"Position":399.0,"HyperDash":false}]},{"StartTime":82909.0,"Objects":[{"StartTime":82909.0,"Position":316.0,"HyperDash":false}]},{"StartTime":83018.0,"Objects":[{"StartTime":83018.0,"Position":206.0,"HyperDash":false},{"StartTime":83127.0,"Position":204.184845,"HyperDash":false}]},{"StartTime":83237.0,"Objects":[{"StartTime":83237.0,"Position":315.0,"HyperDash":false},{"StartTime":83346.0,"Position":315.971741,"HyperDash":true}]},{"StartTime":83456.0,"Objects":[{"StartTime":83456.0,"Position":80.0,"HyperDash":false},{"StartTime":83565.0,"Position":78.18484,"HyperDash":false}]},{"StartTime":83675.0,"Objects":[{"StartTime":83675.0,"Position":182.0,"HyperDash":false}]},{"StartTime":83894.0,"Objects":[{"StartTime":83894.0,"Position":375.0,"HyperDash":true}]},{"StartTime":84113.0,"Objects":[{"StartTime":84113.0,"Position":57.0,"HyperDash":false}]},{"StartTime":84223.0,"Objects":[{"StartTime":84223.0,"Position":133.0,"HyperDash":true}]},{"StartTime":84332.0,"Objects":[{"StartTime":84332.0,"Position":366.0,"HyperDash":false}]},{"StartTime":84442.0,"Objects":[{"StartTime":84442.0,"Position":405.0,"HyperDash":false}]},{"StartTime":84551.0,"Objects":[{"StartTime":84551.0,"Position":361.0,"HyperDash":false}]},{"StartTime":84661.0,"Objects":[{"StartTime":84661.0,"Position":284.0,"HyperDash":false}]},{"StartTime":84770.0,"Objects":[{"StartTime":84770.0,"Position":174.0,"HyperDash":false},{"StartTime":84879.0,"Position":172.184845,"HyperDash":true}]},{"StartTime":84989.0,"Objects":[{"StartTime":84989.0,"Position":442.0,"HyperDash":false}]},{"StartTime":85099.0,"Objects":[{"StartTime":85099.0,"Position":358.0,"HyperDash":false}]},{"StartTime":85208.0,"Objects":[{"StartTime":85208.0,"Position":321.0,"HyperDash":false}]},{"StartTime":85317.0,"Objects":[{"StartTime":85317.0,"Position":365.0,"HyperDash":false}]},{"StartTime":85427.0,"Objects":[{"StartTime":85427.0,"Position":475.0,"HyperDash":false},{"StartTime":85536.0,"Position":475.919922,"HyperDash":true}]},{"StartTime":85646.0,"Objects":[{"StartTime":85646.0,"Position":274.0,"HyperDash":false},{"StartTime":85755.0,"Position":273.103973,"HyperDash":false}]},{"StartTime":85865.0,"Objects":[{"StartTime":85865.0,"Position":363.0,"HyperDash":false}]},{"StartTime":85974.0,"Objects":[{"StartTime":85974.0,"Position":273.0,"HyperDash":true}]},{"StartTime":86084.0,"Objects":[{"StartTime":86084.0,"Position":71.0,"HyperDash":false},{"StartTime":86193.0,"Position":70.21596,"HyperDash":true}]},{"StartTime":86303.0,"Objects":[{"StartTime":86303.0,"Position":305.0,"HyperDash":false},{"StartTime":86412.0,"Position":305.0,"HyperDash":true}]},{"StartTime":86522.0,"Objects":[{"StartTime":86522.0,"Position":103.0,"HyperDash":true}]},{"StartTime":86631.0,"Objects":[{"StartTime":86631.0,"Position":305.0,"HyperDash":false},{"StartTime":86740.0,"Position":373.995,"HyperDash":true}]},{"StartTime":86960.0,"Objects":[{"StartTime":86960.0,"Position":55.0,"HyperDash":false},{"StartTime":87014.0,"Position":76.89231,"HyperDash":false},{"StartTime":87069.0,"Position":136.4433,"HyperDash":false},{"StartTime":87123.0,"Position":166.220535,"HyperDash":false},{"StartTime":87178.0,"Position":189.010239,"HyperDash":false},{"StartTime":87232.0,"Position":225.342209,"HyperDash":false},{"StartTime":87287.0,"Position":199.378647,"HyperDash":false},{"StartTime":87342.0,"Position":204.410217,"HyperDash":false},{"StartTime":87397.0,"Position":181.37085,"HyperDash":false},{"StartTime":87488.0,"Position":110.687065,"HyperDash":false},{"StartTime":87616.0,"Position":48.63235,"HyperDash":true}]},{"StartTime":87836.0,"Objects":[{"StartTime":87836.0,"Position":398.0,"HyperDash":false}]},{"StartTime":101412.0,"Objects":[{"StartTime":101412.0,"Position":77.0,"HyperDash":false}]},{"StartTime":101850.0,"Objects":[{"StartTime":101850.0,"Position":435.0,"HyperDash":false},{"StartTime":101941.0,"Position":437.939,"HyperDash":false},{"StartTime":102068.0,"Position":434.39502,"HyperDash":false}]},{"StartTime":102288.0,"Objects":[{"StartTime":102288.0,"Position":240.0,"HyperDash":false},{"StartTime":102379.0,"Position":174.395935,"HyperDash":false},{"StartTime":102506.0,"Position":102.003464,"HyperDash":false}]},{"StartTime":102726.0,"Objects":[{"StartTime":102726.0,"Position":296.0,"HyperDash":false},{"StartTime":102817.0,"Position":355.604065,"HyperDash":false},{"StartTime":102944.0,"Position":433.996521,"HyperDash":false}]},{"StartTime":103055.0,"Objects":[{"StartTime":103055.0,"Position":322.0,"HyperDash":false},{"StartTime":103164.0,"Position":253.0,"HyperDash":false}]},{"StartTime":103383.0,"Objects":[{"StartTime":103383.0,"Position":433.0,"HyperDash":false}]},{"StartTime":103602.0,"Objects":[{"StartTime":103602.0,"Position":145.0,"HyperDash":false}]},{"StartTime":103712.0,"Objects":[{"StartTime":103712.0,"Position":228.0,"HyperDash":false}]},{"StartTime":103821.0,"Objects":[{"StartTime":103821.0,"Position":283.0,"HyperDash":false}]},{"StartTime":104040.0,"Objects":[{"StartTime":104040.0,"Position":89.0,"HyperDash":false},{"StartTime":104131.0,"Position":77.58258,"HyperDash":false},{"StartTime":104258.0,"Position":88.00002,"HyperDash":false}]},{"StartTime":104478.0,"Objects":[{"StartTime":104478.0,"Position":268.0,"HyperDash":false}]},{"StartTime":104697.0,"Objects":[{"StartTime":104697.0,"Position":88.0,"HyperDash":false}]},{"StartTime":104916.0,"Objects":[{"StartTime":104916.0,"Position":281.0,"HyperDash":false},{"StartTime":105007.0,"Position":355.604126,"HyperDash":false},{"StartTime":105134.0,"Position":418.9967,"HyperDash":false}]},{"StartTime":105354.0,"Objects":[{"StartTime":105354.0,"Position":129.0,"HyperDash":false}]},{"StartTime":105463.0,"Objects":[{"StartTime":105463.0,"Position":211.0,"HyperDash":false}]},{"StartTime":105573.0,"Objects":[{"StartTime":105573.0,"Position":266.0,"HyperDash":false}]},{"StartTime":105792.0,"Objects":[{"StartTime":105792.0,"Position":72.0,"HyperDash":false},{"StartTime":105883.0,"Position":80.618515,"HyperDash":false},{"StartTime":106010.0,"Position":71.08611,"HyperDash":false}]},{"StartTime":106230.0,"Objects":[{"StartTime":106230.0,"Position":265.0,"HyperDash":false},{"StartTime":106321.0,"Position":197.395813,"HyperDash":false},{"StartTime":106448.0,"Position":127.003143,"HyperDash":false}]},{"StartTime":106558.0,"Objects":[{"StartTime":106558.0,"Position":237.0,"HyperDash":false},{"StartTime":106667.0,"Position":306.0,"HyperDash":false}]},{"StartTime":106887.0,"Objects":[{"StartTime":106887.0,"Position":126.0,"HyperDash":false}]},{"StartTime":107106.0,"Objects":[{"StartTime":107106.0,"Position":415.0,"HyperDash":false}]},{"StartTime":107215.0,"Objects":[{"StartTime":107215.0,"Position":332.0,"HyperDash":false}]},{"StartTime":107325.0,"Objects":[{"StartTime":107325.0,"Position":276.0,"HyperDash":false}]},{"StartTime":107544.0,"Objects":[{"StartTime":107544.0,"Position":469.0,"HyperDash":false},{"StartTime":107635.0,"Position":484.411469,"HyperDash":false},{"StartTime":107762.0,"Position":469.9857,"HyperDash":false}]},{"StartTime":107982.0,"Objects":[{"StartTime":107982.0,"Position":289.0,"HyperDash":false}]},{"StartTime":108201.0,"Objects":[{"StartTime":108201.0,"Position":469.0,"HyperDash":false}]},{"StartTime":108420.0,"Objects":[{"StartTime":108420.0,"Position":275.0,"HyperDash":false},{"StartTime":108511.0,"Position":208.3945,"HyperDash":false},{"StartTime":108638.0,"Position":137.0,"HyperDash":false}]},{"StartTime":108858.0,"Objects":[{"StartTime":108858.0,"Position":428.0,"HyperDash":false}]},{"StartTime":108967.0,"Objects":[{"StartTime":108967.0,"Position":345.0,"HyperDash":false}]},{"StartTime":109077.0,"Objects":[{"StartTime":109077.0,"Position":289.0,"HyperDash":false}]},{"StartTime":109296.0,"Objects":[{"StartTime":109296.0,"Position":482.0,"HyperDash":false},{"StartTime":109387.0,"Position":471.822845,"HyperDash":false},{"StartTime":109514.0,"Position":483.971222,"HyperDash":false}]},{"StartTime":109734.0,"Objects":[{"StartTime":109734.0,"Position":291.0,"HyperDash":false},{"StartTime":109825.0,"Position":335.604,"HyperDash":false},{"StartTime":109952.0,"Position":428.9964,"HyperDash":false}]},{"StartTime":110062.0,"Objects":[{"StartTime":110062.0,"Position":318.0,"HyperDash":false},{"StartTime":110171.0,"Position":249.005829,"HyperDash":false}]},{"StartTime":110390.0,"Objects":[{"StartTime":110390.0,"Position":428.0,"HyperDash":false}]},{"StartTime":110609.0,"Objects":[{"StartTime":110609.0,"Position":138.0,"HyperDash":false}]},{"StartTime":110719.0,"Objects":[{"StartTime":110719.0,"Position":215.0,"HyperDash":false}]},{"StartTime":110828.0,"Objects":[{"StartTime":110828.0,"Position":277.0,"HyperDash":false}]},{"StartTime":111047.0,"Objects":[{"StartTime":111047.0,"Position":83.0,"HyperDash":false},{"StartTime":111138.0,"Position":130.6055,"HyperDash":false},{"StartTime":111265.0,"Position":221.0,"HyperDash":false}]},{"StartTime":111485.0,"Objects":[{"StartTime":111485.0,"Position":26.0,"HyperDash":false},{"StartTime":111576.0,"Position":27.5795326,"HyperDash":false},{"StartTime":111703.0,"Position":24.9927273,"HyperDash":false}]},{"StartTime":111923.0,"Objects":[{"StartTime":111923.0,"Position":205.0,"HyperDash":false}]},{"StartTime":112142.0,"Objects":[{"StartTime":112142.0,"Position":25.0,"HyperDash":false}]},{"StartTime":112361.0,"Objects":[{"StartTime":112361.0,"Position":314.0,"HyperDash":false}]},{"StartTime":112471.0,"Objects":[{"StartTime":112471.0,"Position":230.0,"HyperDash":false}]},{"StartTime":112580.0,"Objects":[{"StartTime":112580.0,"Position":314.0,"HyperDash":false},{"StartTime":112634.0,"Position":339.679535,"HyperDash":false},{"StartTime":112689.0,"Position":397.080627,"HyperDash":false},{"StartTime":112743.0,"Position":405.857666,"HyperDash":false},{"StartTime":112798.0,"Position":405.816559,"HyperDash":false},{"StartTime":112889.0,"Position":384.664124,"HyperDash":false},{"StartTime":113017.0,"Position":303.560425,"HyperDash":false}]},{"StartTime":113237.0,"Objects":[{"StartTime":113237.0,"Position":109.0,"HyperDash":false},{"StartTime":113291.0,"Position":59.27102,"HyperDash":false},{"StartTime":113346.0,"Position":27.2394676,"HyperDash":false},{"StartTime":113400.0,"Position":36.17946,"HyperDash":false},{"StartTime":113455.0,"Position":19.1602039,"HyperDash":false},{"StartTime":113546.0,"Position":28.68512,"HyperDash":false},{"StartTime":113674.0,"Position":122.962029,"HyperDash":false}]},{"StartTime":114113.0,"Objects":[{"StartTime":114113.0,"Position":482.0,"HyperDash":false}]},{"StartTime":114332.0,"Objects":[{"StartTime":114332.0,"Position":288.0,"HyperDash":false}]},{"StartTime":114551.0,"Objects":[{"StartTime":114551.0,"Position":482.0,"HyperDash":false},{"StartTime":114642.0,"Position":428.3945,"HyperDash":false},{"StartTime":114769.0,"Position":344.0,"HyperDash":false}]},{"StartTime":114989.0,"Objects":[{"StartTime":114989.0,"Position":149.0,"HyperDash":false},{"StartTime":115080.0,"Position":207.6055,"HyperDash":false},{"StartTime":115207.0,"Position":287.0,"HyperDash":false}]},{"StartTime":115317.0,"Objects":[{"StartTime":115317.0,"Position":397.0,"HyperDash":false},{"StartTime":115426.0,"Position":328.004547,"HyperDash":false}]},{"StartTime":115646.0,"Objects":[{"StartTime":115646.0,"Position":133.0,"HyperDash":false},{"StartTime":115755.0,"Position":132.092178,"HyperDash":true}]},{"StartTime":115865.0,"Objects":[{"StartTime":115865.0,"Position":367.0,"HyperDash":false}]},{"StartTime":115974.0,"Objects":[{"StartTime":115974.0,"Position":284.0,"HyperDash":false}]},{"StartTime":116084.0,"Objects":[{"StartTime":116084.0,"Position":228.0,"HyperDash":false}]},{"StartTime":116303.0,"Objects":[{"StartTime":116303.0,"Position":421.0,"HyperDash":false},{"StartTime":116394.0,"Position":429.822845,"HyperDash":false},{"StartTime":116521.0,"Position":422.971222,"HyperDash":false}]},{"StartTime":116631.0,"Objects":[{"StartTime":116631.0,"Position":346.0,"HyperDash":false}]},{"StartTime":116741.0,"Objects":[{"StartTime":116741.0,"Position":235.0,"HyperDash":false},{"StartTime":116832.0,"Position":277.6042,"HyperDash":false},{"StartTime":116959.0,"Position":372.996857,"HyperDash":false}]},{"StartTime":117069.0,"Objects":[{"StartTime":117069.0,"Position":296.0,"HyperDash":true}]},{"StartTime":117179.0,"Objects":[{"StartTime":117179.0,"Position":94.0,"HyperDash":false}]},{"StartTime":117398.0,"Objects":[{"StartTime":117398.0,"Position":273.0,"HyperDash":false},{"StartTime":117507.0,"Position":341.99353,"HyperDash":true}]},{"StartTime":117617.0,"Objects":[{"StartTime":117617.0,"Position":129.0,"HyperDash":false}]},{"StartTime":117726.0,"Objects":[{"StartTime":117726.0,"Position":60.0,"HyperDash":false}]},{"StartTime":117836.0,"Objects":[{"StartTime":117836.0,"Position":131.0,"HyperDash":false}]},{"StartTime":118055.0,"Objects":[{"StartTime":118055.0,"Position":324.0,"HyperDash":false},{"StartTime":118146.0,"Position":262.3945,"HyperDash":false},{"StartTime":118273.0,"Position":186.0,"HyperDash":false}]},{"StartTime":118383.0,"Objects":[{"StartTime":118383.0,"Position":262.0,"HyperDash":false}]},{"StartTime":118493.0,"Objects":[{"StartTime":118493.0,"Position":372.0,"HyperDash":false},{"StartTime":118584.0,"Position":427.036163,"HyperDash":false},{"StartTime":118711.0,"Position":476.603577,"HyperDash":false}]},{"StartTime":118821.0,"Objects":[{"StartTime":118821.0,"Position":400.0,"HyperDash":true}]},{"StartTime":118931.0,"Objects":[{"StartTime":118931.0,"Position":198.0,"HyperDash":false}]},{"StartTime":119150.0,"Objects":[{"StartTime":119150.0,"Position":391.0,"HyperDash":false},{"StartTime":119259.0,"Position":391.8414,"HyperDash":true}]},{"StartTime":119369.0,"Objects":[{"StartTime":119369.0,"Position":156.0,"HyperDash":false}]},{"StartTime":119478.0,"Objects":[{"StartTime":119478.0,"Position":238.0,"HyperDash":false}]},{"StartTime":119588.0,"Objects":[{"StartTime":119588.0,"Position":293.0,"HyperDash":false}]},{"StartTime":119807.0,"Objects":[{"StartTime":119807.0,"Position":99.0,"HyperDash":false},{"StartTime":119898.0,"Position":105.171227,"HyperDash":false},{"StartTime":120025.0,"Position":97.014595,"HyperDash":false}]},{"StartTime":120135.0,"Objects":[{"StartTime":120135.0,"Position":174.0,"HyperDash":false}]},{"StartTime":120244.0,"Objects":[{"StartTime":120244.0,"Position":283.0,"HyperDash":false}]},{"StartTime":120354.0,"Objects":[{"StartTime":120354.0,"Position":333.0,"HyperDash":false}]},{"StartTime":120463.0,"Objects":[{"StartTime":120463.0,"Position":283.0,"HyperDash":false}]},{"StartTime":120573.0,"Objects":[{"StartTime":120573.0,"Position":185.0,"HyperDash":true}]},{"StartTime":120682.0,"Objects":[{"StartTime":120682.0,"Position":384.0,"HyperDash":false},{"StartTime":120773.0,"Position":427.280121,"HyperDash":false},{"StartTime":120900.0,"Position":482.186859,"HyperDash":false}]},{"StartTime":121011.0,"Objects":[{"StartTime":121011.0,"Position":412.0,"HyperDash":true}]},{"StartTime":121120.0,"Objects":[{"StartTime":121120.0,"Position":178.0,"HyperDash":false}]},{"StartTime":121230.0,"Objects":[{"StartTime":121230.0,"Position":108.0,"HyperDash":false}]},{"StartTime":121339.0,"Objects":[{"StartTime":121339.0,"Position":178.0,"HyperDash":false}]},{"StartTime":121558.0,"Objects":[{"StartTime":121558.0,"Position":371.0,"HyperDash":false},{"StartTime":121649.0,"Position":320.3945,"HyperDash":false},{"StartTime":121776.0,"Position":233.0,"HyperDash":false}]},{"StartTime":121887.0,"Objects":[{"StartTime":121887.0,"Position":309.0,"HyperDash":false}]},{"StartTime":121996.0,"Objects":[{"StartTime":121996.0,"Position":418.0,"HyperDash":false},{"StartTime":122087.0,"Position":443.873138,"HyperDash":false},{"StartTime":122214.0,"Position":414.947174,"HyperDash":false}]},{"StartTime":122325.0,"Objects":[{"StartTime":122325.0,"Position":337.0,"HyperDash":true}]},{"StartTime":122434.0,"Objects":[{"StartTime":122434.0,"Position":137.0,"HyperDash":false},{"StartTime":122525.0,"Position":79.57886,"HyperDash":false},{"StartTime":122652.0,"Position":25.39234,"HyperDash":false}]},{"StartTime":122763.0,"Objects":[{"StartTime":122763.0,"Position":102.0,"HyperDash":true}]},{"StartTime":122872.0,"Objects":[{"StartTime":122872.0,"Position":335.0,"HyperDash":false}]},{"StartTime":122982.0,"Objects":[{"StartTime":122982.0,"Position":251.0,"HyperDash":false}]},{"StartTime":123091.0,"Objects":[{"StartTime":123091.0,"Position":196.0,"HyperDash":false}]},{"StartTime":123310.0,"Objects":[{"StartTime":123310.0,"Position":389.0,"HyperDash":false},{"StartTime":123401.0,"Position":399.5055,"HyperDash":false},{"StartTime":123528.0,"Position":387.780823,"HyperDash":false}]},{"StartTime":123639.0,"Objects":[{"StartTime":123639.0,"Position":312.0,"HyperDash":false}]},{"StartTime":123748.0,"Objects":[{"StartTime":123748.0,"Position":202.0,"HyperDash":false},{"StartTime":123839.0,"Position":146.4552,"HyperDash":false},{"StartTime":123966.0,"Position":122.737045,"HyperDash":false}]},{"StartTime":124077.0,"Objects":[{"StartTime":124077.0,"Position":200.0,"HyperDash":true}]},{"StartTime":124186.0,"Objects":[{"StartTime":124186.0,"Position":399.0,"HyperDash":false}]},{"StartTime":124405.0,"Objects":[{"StartTime":124405.0,"Position":219.0,"HyperDash":false},{"StartTime":124514.0,"Position":150.0,"HyperDash":true}]},{"StartTime":124624.0,"Objects":[{"StartTime":124624.0,"Position":386.0,"HyperDash":false}]},{"StartTime":124734.0,"Objects":[{"StartTime":124734.0,"Position":455.0,"HyperDash":false}]},{"StartTime":124843.0,"Objects":[{"StartTime":124843.0,"Position":386.0,"HyperDash":false}]},{"StartTime":125062.0,"Objects":[{"StartTime":125062.0,"Position":192.0,"HyperDash":false},{"StartTime":125153.0,"Position":149.893311,"HyperDash":false},{"StartTime":125280.0,"Position":68.0014954,"HyperDash":false}]},{"StartTime":125390.0,"Objects":[{"StartTime":125390.0,"Position":144.0,"HyperDash":true}]},{"StartTime":125500.0,"Objects":[{"StartTime":125500.0,"Position":345.0,"HyperDash":false},{"StartTime":125591.0,"Position":419.1067,"HyperDash":false},{"StartTime":125718.0,"Position":468.9985,"HyperDash":false}]},{"StartTime":125828.0,"Objects":[{"StartTime":125828.0,"Position":393.0,"HyperDash":false}]},{"StartTime":125938.0,"Objects":[{"StartTime":125938.0,"Position":282.0,"HyperDash":false}]},{"StartTime":126157.0,"Objects":[{"StartTime":126157.0,"Position":475.0,"HyperDash":false},{"StartTime":126266.0,"Position":475.9078,"HyperDash":true}]},{"StartTime":126376.0,"Objects":[{"StartTime":126376.0,"Position":240.0,"HyperDash":false}]},{"StartTime":126485.0,"Objects":[{"StartTime":126485.0,"Position":322.0,"HyperDash":false}]},{"StartTime":126595.0,"Objects":[{"StartTime":126595.0,"Position":377.0,"HyperDash":false}]},{"StartTime":126814.0,"Objects":[{"StartTime":126814.0,"Position":183.0,"HyperDash":false}]},{"StartTime":127033.0,"Objects":[{"StartTime":127033.0,"Position":472.0,"HyperDash":false}]},{"StartTime":127142.0,"Objects":[{"StartTime":127142.0,"Position":389.0,"HyperDash":false}]},{"StartTime":127252.0,"Objects":[{"StartTime":127252.0,"Position":333.0,"HyperDash":false}]},{"StartTime":127471.0,"Objects":[{"StartTime":127471.0,"Position":153.0,"HyperDash":false},{"StartTime":127580.0,"Position":152.067657,"HyperDash":false}]},{"StartTime":127690.0,"Objects":[{"StartTime":127690.0,"Position":256.0,"HyperDash":false}]},{"StartTime":127909.0,"Objects":[{"StartTime":127909.0,"Position":76.0,"HyperDash":true}]},{"StartTime":128128.0,"Objects":[{"StartTime":128128.0,"Position":421.0,"HyperDash":false}]},{"StartTime":128237.0,"Objects":[{"StartTime":128237.0,"Position":423.0,"HyperDash":false}]},{"StartTime":128347.0,"Objects":[{"StartTime":128347.0,"Position":319.0,"HyperDash":false}]},{"StartTime":128566.0,"Objects":[{"StartTime":128566.0,"Position":139.0,"HyperDash":false}]},{"StartTime":128785.0,"Objects":[{"StartTime":128785.0,"Position":332.0,"HyperDash":false}]},{"StartTime":129004.0,"Objects":[{"StartTime":129004.0,"Position":42.0,"HyperDash":false}]},{"StartTime":129113.0,"Objects":[{"StartTime":129113.0,"Position":111.0,"HyperDash":false}]},{"StartTime":129332.0,"Objects":[{"StartTime":129332.0,"Position":304.0,"HyperDash":false},{"StartTime":129386.0,"Position":253.920715,"HyperDash":false},{"StartTime":129441.0,"Position":217.210358,"HyperDash":false},{"StartTime":129495.0,"Position":213.1311,"HyperDash":false},{"StartTime":129550.0,"Position":166.420731,"HyperDash":false},{"StartTime":129660.0,"Position":97.0,"HyperDash":true}]},{"StartTime":129880.0,"Objects":[{"StartTime":129880.0,"Position":408.0,"HyperDash":false},{"StartTime":129934.0,"Position":421.643433,"HyperDash":false},{"StartTime":129989.0,"Position":469.5894,"HyperDash":false},{"StartTime":130043.0,"Position":472.515442,"HyperDash":false},{"StartTime":130098.0,"Position":489.2183,"HyperDash":false},{"StartTime":130189.0,"Position":462.087952,"HyperDash":false},{"StartTime":130317.0,"Position":381.479523,"HyperDash":false}]},{"StartTime":130536.0,"Objects":[{"StartTime":130536.0,"Position":188.0,"HyperDash":false},{"StartTime":130590.0,"Position":224.105255,"HyperDash":false},{"StartTime":130645.0,"Position":273.8421,"HyperDash":false},{"StartTime":130699.0,"Position":301.947357,"HyperDash":false},{"StartTime":130754.0,"Position":325.6842,"HyperDash":false},{"StartTime":130845.0,"Position":391.1579,"HyperDash":false},{"StartTime":130973.0,"Position":464.0,"HyperDash":false}]},{"StartTime":131193.0,"Objects":[{"StartTime":131193.0,"Position":283.0,"HyperDash":false}]},{"StartTime":131412.0,"Objects":[{"StartTime":131412.0,"Position":463.0,"HyperDash":true}]},{"StartTime":131631.0,"Objects":[{"StartTime":131631.0,"Position":145.0,"HyperDash":false},{"StartTime":131685.0,"Position":104.253967,"HyperDash":false},{"StartTime":131740.0,"Position":82.46871,"HyperDash":false},{"StartTime":131794.0,"Position":46.8594933,"HyperDash":false},{"StartTime":131849.0,"Position":58.7363319,"HyperDash":false},{"StartTime":131940.0,"Position":65.76372,"HyperDash":false},{"StartTime":132068.0,"Position":161.933884,"HyperDash":false}]},{"StartTime":132288.0,"Objects":[{"StartTime":132288.0,"Position":342.0,"HyperDash":false}]},{"StartTime":132507.0,"Objects":[{"StartTime":132507.0,"Position":148.0,"HyperDash":false},{"StartTime":132598.0,"Position":150.628357,"HyperDash":false},{"StartTime":132725.0,"Position":147.1097,"HyperDash":false}]},{"StartTime":132945.0,"Objects":[{"StartTime":132945.0,"Position":327.0,"HyperDash":false}]},{"StartTime":133164.0,"Objects":[{"StartTime":133164.0,"Position":147.0,"HyperDash":true}]},{"StartTime":133383.0,"Objects":[{"StartTime":133383.0,"Position":464.0,"HyperDash":false},{"StartTime":133437.0,"Position":470.84375,"HyperDash":false},{"StartTime":133492.0,"Position":474.752625,"HyperDash":false},{"StartTime":133546.0,"Position":428.8388,"HyperDash":false},{"StartTime":133601.0,"Position":419.0386,"HyperDash":false},{"StartTime":133711.0,"Position":351.443878,"HyperDash":false}]},{"StartTime":133821.0,"Objects":[{"StartTime":133821.0,"Position":240.0,"HyperDash":false},{"StartTime":133875.0,"Position":265.918579,"HyperDash":false},{"StartTime":133930.0,"Position":308.4777,"HyperDash":false},{"StartTime":133984.0,"Position":356.101166,"HyperDash":false},{"StartTime":134039.0,"Position":367.7393,"HyperDash":false},{"StartTime":134130.0,"Position":392.480377,"HyperDash":false},{"StartTime":134258.0,"Position":390.924835,"HyperDash":false}]},{"StartTime":134478.0,"Objects":[{"StartTime":134478.0,"Position":196.0,"HyperDash":false},{"StartTime":134569.0,"Position":183.414413,"HyperDash":false},{"StartTime":134696.0,"Position":196.992783,"HyperDash":false}]},{"StartTime":134916.0,"Objects":[{"StartTime":134916.0,"Position":391.0,"HyperDash":true}]},{"StartTime":135135.0,"Objects":[{"StartTime":135135.0,"Position":73.0,"HyperDash":false},{"StartTime":135189.0,"Position":110.225349,"HyperDash":false},{"StartTime":135244.0,"Position":131.973389,"HyperDash":false},{"StartTime":135298.0,"Position":154.19873,"HyperDash":false},{"StartTime":135353.0,"Position":186.946777,"HyperDash":false},{"StartTime":135444.0,"Position":152.597885,"HyperDash":false},{"StartTime":135572.0,"Position":74.8510361,"HyperDash":false}]},{"StartTime":136011.0,"Objects":[{"StartTime":136011.0,"Position":434.0,"HyperDash":false},{"StartTime":136102.0,"Position":424.411469,"HyperDash":false},{"StartTime":136229.0,"Position":434.9857,"HyperDash":false}]},{"StartTime":136449.0,"Objects":[{"StartTime":136449.0,"Position":227.0,"HyperDash":false}]},{"StartTime":136668.0,"Objects":[{"StartTime":136668.0,"Position":434.0,"HyperDash":true}]},{"StartTime":136887.0,"Objects":[{"StartTime":136887.0,"Position":116.0,"HyperDash":false},{"StartTime":136941.0,"Position":169.105255,"HyperDash":false},{"StartTime":136996.0,"Position":171.8421,"HyperDash":false},{"StartTime":137050.0,"Position":216.947357,"HyperDash":false},{"StartTime":137105.0,"Position":253.6842,"HyperDash":false},{"StartTime":137196.0,"Position":316.1579,"HyperDash":false},{"StartTime":137324.0,"Position":392.0,"HyperDash":true}]},{"StartTime":137544.0,"Objects":[{"StartTime":137544.0,"Position":100.0,"HyperDash":false}]},{"StartTime":137653.0,"Objects":[{"StartTime":137653.0,"Position":182.0,"HyperDash":false}]},{"StartTime":137763.0,"Objects":[{"StartTime":137763.0,"Position":242.0,"HyperDash":false}]},{"StartTime":137982.0,"Objects":[{"StartTime":137982.0,"Position":62.0,"HyperDash":false}]},{"StartTime":138201.0,"Objects":[{"StartTime":138201.0,"Position":241.0,"HyperDash":false},{"StartTime":138292.0,"Position":173.399414,"HyperDash":false},{"StartTime":138419.0,"Position":103.011795,"HyperDash":true}]},{"StartTime":138639.0,"Objects":[{"StartTime":138639.0,"Position":421.0,"HyperDash":false},{"StartTime":138693.0,"Position":392.894928,"HyperDash":false},{"StartTime":138748.0,"Position":354.1583,"HyperDash":false},{"StartTime":138802.0,"Position":299.053223,"HyperDash":false},{"StartTime":138857.0,"Position":283.3166,"HyperDash":false},{"StartTime":138948.0,"Position":230.843246,"HyperDash":false},{"StartTime":139076.0,"Position":145.001617,"HyperDash":false}]},{"StartTime":139296.0,"Objects":[{"StartTime":139296.0,"Position":339.0,"HyperDash":false},{"StartTime":139405.0,"Position":339.884552,"HyperDash":false}]},{"StartTime":139515.0,"Objects":[{"StartTime":139515.0,"Position":235.0,"HyperDash":false}]},{"StartTime":139734.0,"Objects":[{"StartTime":139734.0,"Position":55.0,"HyperDash":false}]},{"StartTime":139953.0,"Objects":[{"StartTime":139953.0,"Position":344.0,"HyperDash":false},{"StartTime":140044.0,"Position":417.604126,"HyperDash":false},{"StartTime":140171.0,"Position":481.9967,"HyperDash":true}]},{"StartTime":140390.0,"Objects":[{"StartTime":140390.0,"Position":136.0,"HyperDash":false},{"StartTime":140481.0,"Position":128.599976,"HyperDash":false},{"StartTime":140608.0,"Position":135.041687,"HyperDash":false}]},{"StartTime":140828.0,"Objects":[{"StartTime":140828.0,"Position":328.0,"HyperDash":false}]},{"StartTime":141047.0,"Objects":[{"StartTime":141047.0,"Position":135.0,"HyperDash":false}]},{"StartTime":141266.0,"Objects":[{"StartTime":141266.0,"Position":342.0,"HyperDash":false}]},{"StartTime":141485.0,"Objects":[{"StartTime":141485.0,"Position":493.0,"HyperDash":false}]},{"StartTime":141704.0,"Objects":[{"StartTime":141704.0,"Position":299.0,"HyperDash":false}]},{"StartTime":141923.0,"Objects":[{"StartTime":141923.0,"Position":91.0,"HyperDash":false}]},{"StartTime":142142.0,"Objects":[{"StartTime":142142.0,"Position":380.0,"HyperDash":false},{"StartTime":142196.0,"Position":335.923767,"HyperDash":false},{"StartTime":142251.0,"Position":318.2165,"HyperDash":false},{"StartTime":142305.0,"Position":259.140259,"HyperDash":false},{"StartTime":142360.0,"Position":242.432953,"HyperDash":false},{"StartTime":142415.0,"Position":215.7257,"HyperDash":false},{"StartTime":142470.0,"Position":173.0184,"HyperDash":false},{"StartTime":142524.0,"Position":215.09462,"HyperDash":false},{"StartTime":142579.0,"Position":241.801926,"HyperDash":false},{"StartTime":142670.0,"Position":299.226685,"HyperDash":false},{"StartTime":142798.0,"Position":380.0,"HyperDash":false}]},{"StartTime":143018.0,"Objects":[{"StartTime":143018.0,"Position":185.0,"HyperDash":false},{"StartTime":143072.0,"Position":198.796173,"HyperDash":false},{"StartTime":143127.0,"Position":265.955566,"HyperDash":false},{"StartTime":143181.0,"Position":287.965,"HyperDash":false},{"StartTime":143236.0,"Position":318.749146,"HyperDash":false},{"StartTime":143290.0,"Position":347.800385,"HyperDash":false},{"StartTime":143345.0,"Position":395.071777,"HyperDash":false},{"StartTime":143400.0,"Position":413.8512,"HyperDash":false},{"StartTime":143455.0,"Position":420.164917,"HyperDash":false},{"StartTime":143546.0,"Position":449.768,"HyperDash":false},{"StartTime":143674.0,"Position":428.7935,"HyperDash":true}]},{"StartTime":143894.0,"Objects":[{"StartTime":143894.0,"Position":82.0,"HyperDash":false},{"StartTime":143985.0,"Position":35.4371643,"HyperDash":false},{"StartTime":144112.0,"Position":83.57783,"HyperDash":false}]},{"StartTime":144223.0,"Objects":[{"StartTime":144223.0,"Position":174.0,"HyperDash":false}]},{"StartTime":144332.0,"Objects":[{"StartTime":144332.0,"Position":84.0,"HyperDash":false},{"StartTime":144441.0,"Position":83.06765,"HyperDash":true}]},{"StartTime":144551.0,"Objects":[{"StartTime":144551.0,"Position":284.0,"HyperDash":false},{"StartTime":144660.0,"Position":353.0,"HyperDash":true}]},{"StartTime":144770.0,"Objects":[{"StartTime":144770.0,"Position":117.0,"HyperDash":false},{"StartTime":144879.0,"Position":48.0,"HyperDash":true}]},{"StartTime":144989.0,"Objects":[{"StartTime":144989.0,"Position":249.0,"HyperDash":true}]},{"StartTime":145099.0,"Objects":[{"StartTime":145099.0,"Position":48.0,"HyperDash":false}]},{"StartTime":145208.0,"Objects":[{"StartTime":145208.0,"Position":144.0,"HyperDash":false},{"StartTime":145299.0,"Position":184.508865,"HyperDash":false},{"StartTime":145426.0,"Position":138.552429,"HyperDash":false}]},{"StartTime":145536.0,"Objects":[{"StartTime":145536.0,"Position":55.0,"HyperDash":true}]},{"StartTime":145646.0,"Objects":[{"StartTime":145646.0,"Position":290.0,"HyperDash":false},{"StartTime":145755.0,"Position":358.994629,"HyperDash":true}]},{"StartTime":145865.0,"Objects":[{"StartTime":145865.0,"Position":157.0,"HyperDash":true}]},{"StartTime":145974.0,"Objects":[{"StartTime":145974.0,"Position":356.0,"HyperDash":false}]},{"StartTime":146084.0,"Objects":[{"StartTime":146084.0,"Position":453.0,"HyperDash":false},{"StartTime":146175.0,"Position":406.3945,"HyperDash":false},{"StartTime":146302.0,"Position":315.0,"HyperDash":false}]},{"StartTime":146412.0,"Objects":[{"StartTime":146412.0,"Position":412.0,"HyperDash":true}]},{"StartTime":146522.0,"Objects":[{"StartTime":146522.0,"Position":176.0,"HyperDash":false}]},{"StartTime":146631.0,"Objects":[{"StartTime":146631.0,"Position":272.0,"HyperDash":false},{"StartTime":146740.0,"Position":272.9078,"HyperDash":true}]},{"StartTime":146850.0,"Objects":[{"StartTime":146850.0,"Position":71.0,"HyperDash":false}]},{"StartTime":146960.0,"Objects":[{"StartTime":146960.0,"Position":168.0,"HyperDash":false},{"StartTime":147051.0,"Position":93.39449,"HyperDash":false},{"StartTime":147178.0,"Position":30.0000153,"HyperDash":false}]},{"StartTime":147288.0,"Objects":[{"StartTime":147288.0,"Position":113.0,"HyperDash":true}]},{"StartTime":147398.0,"Objects":[{"StartTime":147398.0,"Position":348.0,"HyperDash":false},{"StartTime":147489.0,"Position":401.9966,"HyperDash":false},{"StartTime":147616.0,"Position":345.685974,"HyperDash":false}]},{"StartTime":147726.0,"Objects":[{"StartTime":147726.0,"Position":255.0,"HyperDash":false}]},{"StartTime":147836.0,"Objects":[{"StartTime":147836.0,"Position":345.0,"HyperDash":false},{"StartTime":147945.0,"Position":347.028534,"HyperDash":true}]},{"StartTime":148055.0,"Objects":[{"StartTime":148055.0,"Position":145.0,"HyperDash":false}]},{"StartTime":148164.0,"Objects":[{"StartTime":148164.0,"Position":76.0,"HyperDash":true}]},{"StartTime":148274.0,"Objects":[{"StartTime":148274.0,"Position":280.0,"HyperDash":false},{"StartTime":148383.0,"Position":349.0,"HyperDash":true}]},{"StartTime":148493.0,"Objects":[{"StartTime":148493.0,"Position":147.0,"HyperDash":true}]},{"StartTime":148602.0,"Objects":[{"StartTime":148602.0,"Position":346.0,"HyperDash":false}]},{"StartTime":148712.0,"Objects":[{"StartTime":148712.0,"Position":248.0,"HyperDash":false},{"StartTime":148803.0,"Position":196.3945,"HyperDash":false},{"StartTime":148930.0,"Position":110.0,"HyperDash":false}]},{"StartTime":149040.0,"Objects":[{"StartTime":149040.0,"Position":193.0,"HyperDash":true}]},{"StartTime":149150.0,"Objects":[{"StartTime":149150.0,"Position":428.0,"HyperDash":false},{"StartTime":149241.0,"Position":448.54718,"HyperDash":false},{"StartTime":149368.0,"Position":427.29248,"HyperDash":true}]},{"StartTime":149478.0,"Objects":[{"StartTime":149478.0,"Position":226.0,"HyperDash":false}]},{"StartTime":149588.0,"Objects":[{"StartTime":149588.0,"Position":323.0,"HyperDash":false},{"StartTime":149679.0,"Position":392.6055,"HyperDash":false},{"StartTime":149806.0,"Position":461.0,"HyperDash":false}]},{"StartTime":149916.0,"Objects":[{"StartTime":149916.0,"Position":377.0,"HyperDash":true}]},{"StartTime":150026.0,"Objects":[{"StartTime":150026.0,"Position":141.0,"HyperDash":false}]},{"StartTime":150135.0,"Objects":[{"StartTime":150135.0,"Position":237.0,"HyperDash":false},{"StartTime":150244.0,"Position":238.915924,"HyperDash":true}]},{"StartTime":150354.0,"Objects":[{"StartTime":150354.0,"Position":37.0,"HyperDash":false}]},{"StartTime":150463.0,"Objects":[{"StartTime":150463.0,"Position":133.0,"HyperDash":false},{"StartTime":150554.0,"Position":160.2725,"HyperDash":false},{"StartTime":150681.0,"Position":126.154518,"HyperDash":false}]},{"StartTime":150792.0,"Objects":[{"StartTime":150792.0,"Position":42.0,"HyperDash":true}]},{"StartTime":150901.0,"Objects":[{"StartTime":150901.0,"Position":309.0,"HyperDash":false},{"StartTime":150992.0,"Position":376.6055,"HyperDash":false},{"StartTime":151119.0,"Position":447.0,"HyperDash":false}]},{"StartTime":151230.0,"Objects":[{"StartTime":151230.0,"Position":356.0,"HyperDash":false}]},{"StartTime":151339.0,"Objects":[{"StartTime":151339.0,"Position":445.0,"HyperDash":true}]},{"StartTime":151558.0,"Objects":[{"StartTime":151558.0,"Position":127.0,"HyperDash":false}]},{"StartTime":151668.0,"Objects":[{"StartTime":151668.0,"Position":203.0,"HyperDash":false}]},{"StartTime":151777.0,"Objects":[{"StartTime":151777.0,"Position":239.0,"HyperDash":false}]},{"StartTime":151887.0,"Objects":[{"StartTime":151887.0,"Position":196.0,"HyperDash":false}]},{"StartTime":151996.0,"Objects":[{"StartTime":151996.0,"Position":86.0,"HyperDash":false},{"StartTime":152105.0,"Position":84.23135,"HyperDash":true}]},{"StartTime":152215.0,"Objects":[{"StartTime":152215.0,"Position":285.0,"HyperDash":false},{"StartTime":152306.0,"Position":224.395935,"HyperDash":false},{"StartTime":152433.0,"Position":147.003464,"HyperDash":false}]},{"StartTime":152544.0,"Objects":[{"StartTime":152544.0,"Position":230.0,"HyperDash":true}]},{"StartTime":152653.0,"Objects":[{"StartTime":152653.0,"Position":463.0,"HyperDash":false},{"StartTime":152762.0,"Position":394.006836,"HyperDash":false}]},{"StartTime":152872.0,"Objects":[{"StartTime":152872.0,"Position":284.0,"HyperDash":false},{"StartTime":152981.0,"Position":282.231354,"HyperDash":true}]},{"StartTime":153091.0,"Objects":[{"StartTime":153091.0,"Position":483.0,"HyperDash":false},{"StartTime":153182.0,"Position":408.3958,"HyperDash":false},{"StartTime":153309.0,"Position":345.0032,"HyperDash":false}]},{"StartTime":153420.0,"Objects":[{"StartTime":153420.0,"Position":428.0,"HyperDash":true}]},{"StartTime":153529.0,"Objects":[{"StartTime":153529.0,"Position":227.0,"HyperDash":false},{"StartTime":153638.0,"Position":226.115463,"HyperDash":false}]},{"StartTime":153748.0,"Objects":[{"StartTime":153748.0,"Position":323.0,"HyperDash":false}]},{"StartTime":153967.0,"Objects":[{"StartTime":153967.0,"Position":33.0,"HyperDash":false},{"StartTime":154058.0,"Position":11.8165741,"HyperDash":false},{"StartTime":154185.0,"Position":30.1649818,"HyperDash":false}]},{"StartTime":154296.0,"Objects":[{"StartTime":154296.0,"Position":114.0,"HyperDash":true}]},{"StartTime":154405.0,"Objects":[{"StartTime":154405.0,"Position":381.0,"HyperDash":false},{"StartTime":154459.0,"Position":329.8956,"HyperDash":false},{"StartTime":154514.0,"Position":328.159637,"HyperDash":false},{"StartTime":154568.0,"Position":259.055237,"HyperDash":false},{"StartTime":154623.0,"Position":243.31926,"HyperDash":false},{"StartTime":154714.0,"Position":166.847,"HyperDash":false},{"StartTime":154842.0,"Position":105.006927,"HyperDash":true}]},{"StartTime":155062.0,"Objects":[{"StartTime":155062.0,"Position":451.0,"HyperDash":false},{"StartTime":155116.0,"Position":474.1808,"HyperDash":false},{"StartTime":155171.0,"Position":482.115234,"HyperDash":false},{"StartTime":155225.0,"Position":473.658417,"HyperDash":false},{"StartTime":155280.0,"Position":475.76123,"HyperDash":false},{"StartTime":155371.0,"Position":450.3246,"HyperDash":false},{"StartTime":155499.0,"Position":354.987061,"HyperDash":true}]},{"StartTime":155719.0,"Objects":[{"StartTime":155719.0,"Position":22.0,"HyperDash":false},{"StartTime":155810.0,"Position":63.60431,"HyperDash":false},{"StartTime":155937.0,"Position":159.997131,"HyperDash":true}]},{"StartTime":156157.0,"Objects":[{"StartTime":156157.0,"Position":478.0,"HyperDash":false},{"StartTime":156211.0,"Position":461.9211,"HyperDash":false},{"StartTime":156266.0,"Position":399.211151,"HyperDash":false},{"StartTime":156320.0,"Position":377.132263,"HyperDash":false},{"StartTime":156375.0,"Position":340.4223,"HyperDash":false},{"StartTime":156430.0,"Position":322.712341,"HyperDash":false},{"StartTime":156485.0,"Position":271.00235,"HyperDash":false},{"StartTime":156539.0,"Position":309.0812,"HyperDash":false},{"StartTime":156594.0,"Position":339.7912,"HyperDash":false},{"StartTime":156685.0,"Position":387.220428,"HyperDash":false},{"StartTime":156813.0,"Position":478.0,"HyperDash":true}]},{"StartTime":157033.0,"Objects":[{"StartTime":157033.0,"Position":159.0,"HyperDash":false},{"StartTime":157087.0,"Position":134.242828,"HyperDash":false},{"StartTime":157142.0,"Position":89.84937,"HyperDash":false},{"StartTime":157196.0,"Position":60.5968933,"HyperDash":false},{"StartTime":157251.0,"Position":65.38586,"HyperDash":false},{"StartTime":157342.0,"Position":103.223328,"HyperDash":false},{"StartTime":157470.0,"Position":163.359787,"HyperDash":false}]},{"StartTime":157580.0,"Objects":[{"StartTime":157580.0,"Position":254.0,"HyperDash":false}]},{"StartTime":157690.0,"Objects":[{"StartTime":157690.0,"Position":163.0,"HyperDash":true}]},{"StartTime":157799.0,"Objects":[{"StartTime":157799.0,"Position":396.0,"HyperDash":true}]},{"StartTime":157909.0,"Objects":[{"StartTime":157909.0,"Position":163.0,"HyperDash":false},{"StartTime":158000.0,"Position":136.677887,"HyperDash":false},{"StartTime":158127.0,"Position":164.098557,"HyperDash":false}]},{"StartTime":158237.0,"Objects":[{"StartTime":158237.0,"Position":255.0,"HyperDash":false}]},{"StartTime":158347.0,"Objects":[{"StartTime":158347.0,"Position":164.0,"HyperDash":false},{"StartTime":158456.0,"Position":162.135818,"HyperDash":true}]},{"StartTime":158566.0,"Objects":[{"StartTime":158566.0,"Position":363.0,"HyperDash":false},{"StartTime":158675.0,"Position":363.919922,"HyperDash":true}]},{"StartTime":158785.0,"Objects":[{"StartTime":158785.0,"Position":128.0,"HyperDash":false},{"StartTime":158894.0,"Position":196.994614,"HyperDash":true}]},{"StartTime":159004.0,"Objects":[{"StartTime":159004.0,"Position":398.0,"HyperDash":true}]},{"StartTime":159113.0,"Objects":[{"StartTime":159113.0,"Position":198.0,"HyperDash":false}]},{"StartTime":159223.0,"Objects":[{"StartTime":159223.0,"Position":100.0,"HyperDash":false},{"StartTime":159314.0,"Position":80.50117,"HyperDash":false},{"StartTime":159441.0,"Position":104.636375,"HyperDash":false}]},{"StartTime":159551.0,"Objects":[{"StartTime":159551.0,"Position":187.0,"HyperDash":true}]},{"StartTime":159661.0,"Objects":[{"StartTime":159661.0,"Position":422.0,"HyperDash":false},{"StartTime":159770.0,"Position":353.00705,"HyperDash":true}]},{"StartTime":159880.0,"Objects":[{"StartTime":159880.0,"Position":151.0,"HyperDash":true}]},{"StartTime":159989.0,"Objects":[{"StartTime":159989.0,"Position":350.0,"HyperDash":false}]},{"StartTime":160099.0,"Objects":[{"StartTime":160099.0,"Position":254.0,"HyperDash":false},{"StartTime":160190.0,"Position":324.6055,"HyperDash":false},{"StartTime":160317.0,"Position":392.0,"HyperDash":false}]},{"StartTime":160427.0,"Objects":[{"StartTime":160427.0,"Position":296.0,"HyperDash":true}]},{"StartTime":160536.0,"Objects":[{"StartTime":160536.0,"Position":62.0,"HyperDash":false},{"StartTime":160645.0,"Position":61.054882,"HyperDash":false}]},{"StartTime":160755.0,"Objects":[{"StartTime":160755.0,"Position":171.0,"HyperDash":false},{"StartTime":160864.0,"Position":240.0,"HyperDash":true}]},{"StartTime":160974.0,"Objects":[{"StartTime":160974.0,"Position":441.0,"HyperDash":false},{"StartTime":161065.0,"Position":460.246124,"HyperDash":false},{"StartTime":161192.0,"Position":438.9324,"HyperDash":false}]},{"StartTime":161303.0,"Objects":[{"StartTime":161303.0,"Position":354.0,"HyperDash":true}]},{"StartTime":161412.0,"Objects":[{"StartTime":161412.0,"Position":120.0,"HyperDash":false},{"StartTime":161503.0,"Position":188.6055,"HyperDash":false},{"StartTime":161630.0,"Position":258.0,"HyperDash":false}]},{"StartTime":161741.0,"Objects":[{"StartTime":161741.0,"Position":167.0,"HyperDash":false}]},{"StartTime":161850.0,"Objects":[{"StartTime":161850.0,"Position":256.0,"HyperDash":false},{"StartTime":161959.0,"Position":256.873352,"HyperDash":true}]},{"StartTime":162069.0,"Objects":[{"StartTime":162069.0,"Position":55.0,"HyperDash":false},{"StartTime":162178.0,"Position":53.2083969,"HyperDash":true}]},{"StartTime":162288.0,"Objects":[{"StartTime":162288.0,"Position":288.0,"HyperDash":false},{"StartTime":162397.0,"Position":357.0,"HyperDash":true}]},{"StartTime":162507.0,"Objects":[{"StartTime":162507.0,"Position":155.0,"HyperDash":true}]},{"StartTime":162617.0,"Objects":[{"StartTime":162617.0,"Position":356.0,"HyperDash":false}]},{"StartTime":162726.0,"Objects":[{"StartTime":162726.0,"Position":452.0,"HyperDash":false},{"StartTime":162817.0,"Position":467.2106,"HyperDash":false},{"StartTime":162944.0,"Position":448.8102,"HyperDash":false}]},{"StartTime":163055.0,"Objects":[{"StartTime":163055.0,"Position":364.0,"HyperDash":true}]},{"StartTime":163164.0,"Objects":[{"StartTime":163164.0,"Position":130.0,"HyperDash":false},{"StartTime":163273.0,"Position":128.231354,"HyperDash":false}]},{"StartTime":163383.0,"Objects":[{"StartTime":163383.0,"Position":239.0,"HyperDash":false},{"StartTime":163492.0,"Position":240.915924,"HyperDash":true}]},{"StartTime":163602.0,"Objects":[{"StartTime":163602.0,"Position":39.0,"HyperDash":false},{"StartTime":163711.0,"Position":108.0,"HyperDash":true}]},{"StartTime":163821.0,"Objects":[{"StartTime":163821.0,"Position":378.0,"HyperDash":false},{"StartTime":163930.0,"Position":379.0146,"HyperDash":false}]},{"StartTime":164040.0,"Objects":[{"StartTime":164040.0,"Position":268.0,"HyperDash":false},{"StartTime":164149.0,"Position":199.0,"HyperDash":true}]},{"StartTime":164259.0,"Objects":[{"StartTime":164259.0,"Position":400.0,"HyperDash":false},{"StartTime":164368.0,"Position":401.8897,"HyperDash":true}]},{"StartTime":164478.0,"Objects":[{"StartTime":164478.0,"Position":200.0,"HyperDash":false},{"StartTime":164587.0,"Position":131.0,"HyperDash":true}]},{"StartTime":164697.0,"Objects":[{"StartTime":164697.0,"Position":366.0,"HyperDash":false},{"StartTime":164806.0,"Position":434.995453,"HyperDash":true}]},{"StartTime":164916.0,"Objects":[{"StartTime":164916.0,"Position":164.0,"HyperDash":false},{"StartTime":165007.0,"Position":99.39598,"HyperDash":false},{"StartTime":165134.0,"Position":26.00357,"HyperDash":false}]},{"StartTime":165244.0,"Objects":[{"StartTime":165244.0,"Position":116.0,"HyperDash":false}]},{"StartTime":165354.0,"Objects":[{"StartTime":165354.0,"Position":27.0,"HyperDash":true}]},{"StartTime":165573.0,"Objects":[{"StartTime":165573.0,"Position":344.0,"HyperDash":false}]},{"StartTime":165682.0,"Objects":[{"StartTime":165682.0,"Position":381.0,"HyperDash":false}]},{"StartTime":165792.0,"Objects":[{"StartTime":165792.0,"Position":339.0,"HyperDash":false}]},{"StartTime":165901.0,"Objects":[{"StartTime":165901.0,"Position":263.0,"HyperDash":false}]},{"StartTime":166011.0,"Objects":[{"StartTime":166011.0,"Position":152.0,"HyperDash":false},{"StartTime":166120.0,"Position":151.092178,"HyperDash":true}]},{"StartTime":166230.0,"Objects":[{"StartTime":166230.0,"Position":352.0,"HyperDash":false}]},{"StartTime":166339.0,"Objects":[{"StartTime":166339.0,"Position":427.0,"HyperDash":false}]},{"StartTime":166449.0,"Objects":[{"StartTime":166449.0,"Position":464.0,"HyperDash":false}]},{"StartTime":166558.0,"Objects":[{"StartTime":166558.0,"Position":425.0,"HyperDash":true}]},{"StartTime":166668.0,"Objects":[{"StartTime":166668.0,"Position":189.0,"HyperDash":false}]},{"StartTime":166777.0,"Objects":[{"StartTime":166777.0,"Position":116.0,"HyperDash":false}]},{"StartTime":166887.0,"Objects":[{"StartTime":166887.0,"Position":125.0,"HyperDash":false}]},{"StartTime":166996.0,"Objects":[{"StartTime":166996.0,"Position":199.0,"HyperDash":false}]},{"StartTime":167106.0,"Objects":[{"StartTime":167106.0,"Position":309.0,"HyperDash":false},{"StartTime":167215.0,"Position":310.768646,"HyperDash":false}]},{"StartTime":167325.0,"Objects":[{"StartTime":167325.0,"Position":199.0,"HyperDash":false},{"StartTime":167434.0,"Position":197.084076,"HyperDash":true}]},{"StartTime":167544.0,"Objects":[{"StartTime":167544.0,"Position":398.0,"HyperDash":false},{"StartTime":167653.0,"Position":467.0,"HyperDash":false}]},{"StartTime":167763.0,"Objects":[{"StartTime":167763.0,"Position":356.0,"HyperDash":false},{"StartTime":167872.0,"Position":287.00647,"HyperDash":true}]},{"StartTime":167982.0,"Objects":[{"StartTime":167982.0,"Position":85.0,"HyperDash":false},{"StartTime":168091.0,"Position":16.0,"HyperDash":false}]},{"StartTime":168201.0,"Objects":[{"StartTime":168201.0,"Position":126.0,"HyperDash":false},{"StartTime":168310.0,"Position":195.0,"HyperDash":true}]},{"StartTime":168420.0,"Objects":[{"StartTime":168420.0,"Position":430.0,"HyperDash":false},{"StartTime":168474.0,"Position":467.7612,"HyperDash":false},{"StartTime":168529.0,"Position":476.801575,"HyperDash":false},{"StartTime":168583.0,"Position":504.865875,"HyperDash":false},{"StartTime":168638.0,"Position":482.8523,"HyperDash":false},{"StartTime":168729.0,"Position":447.068665,"HyperDash":false},{"StartTime":168857.0,"Position":367.438934,"HyperDash":false}]},{"StartTime":169077.0,"Objects":[{"StartTime":169077.0,"Position":174.0,"HyperDash":false}]},{"StartTime":169186.0,"Objects":[{"StartTime":169186.0,"Position":99.0,"HyperDash":false}]},{"StartTime":169296.0,"Objects":[{"StartTime":169296.0,"Position":67.0,"HyperDash":false}]},{"StartTime":169405.0,"Objects":[{"StartTime":169405.0,"Position":101.0,"HyperDash":false}]},{"StartTime":169515.0,"Objects":[{"StartTime":169515.0,"Position":176.0,"HyperDash":false}]},{"StartTime":169734.0,"Objects":[{"StartTime":169734.0,"Position":465.0,"HyperDash":false},{"StartTime":169825.0,"Position":484.828766,"HyperDash":false},{"StartTime":169952.0,"Position":466.9854,"HyperDash":false}]},{"StartTime":170062.0,"Objects":[{"StartTime":170062.0,"Position":390.0,"HyperDash":true}]},{"StartTime":170172.0,"Objects":[{"StartTime":170172.0,"Position":154.0,"HyperDash":false},{"StartTime":170226.0,"Position":188.078888,"HyperDash":false},{"StartTime":170281.0,"Position":228.788879,"HyperDash":false},{"StartTime":170335.0,"Position":239.867767,"HyperDash":false},{"StartTime":170390.0,"Position":291.577759,"HyperDash":false},{"StartTime":170500.0,"Position":360.997742,"HyperDash":true}]},{"StartTime":170609.0,"Objects":[{"StartTime":170609.0,"Position":127.0,"HyperDash":false},{"StartTime":170700.0,"Position":112.127007,"HyperDash":false},{"StartTime":170827.0,"Position":125.797905,"HyperDash":false}]},{"StartTime":170938.0,"Objects":[{"StartTime":170938.0,"Position":202.0,"HyperDash":true}]},{"StartTime":171047.0,"Objects":[{"StartTime":171047.0,"Position":401.0,"HyperDash":false},{"StartTime":171101.0,"Position":350.353882,"HyperDash":false},{"StartTime":171156.0,"Position":321.8849,"HyperDash":false},{"StartTime":171210.0,"Position":305.955536,"HyperDash":false},{"StartTime":171265.0,"Position":268.51535,"HyperDash":false},{"StartTime":171319.0,"Position":246.017654,"HyperDash":false},{"StartTime":171374.0,"Position":211.42424,"HyperDash":false},{"StartTime":171429.0,"Position":173.4286,"HyperDash":false},{"StartTime":171484.0,"Position":155.9888,"HyperDash":false},{"StartTime":171575.0,"Position":145.032578,"HyperDash":false},{"StartTime":171703.0,"Position":125.051888,"HyperDash":false}]},{"StartTime":171923.0,"Objects":[{"StartTime":171923.0,"Position":416.0,"HyperDash":false}]},{"StartTime":178712.0,"Objects":[{"StartTime":178712.0,"Position":85.0,"HyperDash":true}]},{"StartTime":178931.0,"Objects":[{"StartTime":178931.0,"Position":402.0,"HyperDash":false},{"StartTime":179022.0,"Position":430.926239,"HyperDash":false},{"StartTime":179149.0,"Position":400.1261,"HyperDash":false}]},{"StartTime":179259.0,"Objects":[{"StartTime":179259.0,"Position":323.0,"HyperDash":false}]},{"StartTime":179369.0,"Objects":[{"StartTime":179369.0,"Position":212.0,"HyperDash":false},{"StartTime":179460.0,"Position":173.1731,"HyperDash":false},{"StartTime":179587.0,"Position":94.04442,"HyperDash":false}]},{"StartTime":179697.0,"Objects":[{"StartTime":179697.0,"Position":170.0,"HyperDash":false}]},{"StartTime":179807.0,"Objects":[{"StartTime":179807.0,"Position":280.0,"HyperDash":false},{"StartTime":179898.0,"Position":342.6055,"HyperDash":false},{"StartTime":180025.0,"Position":418.0,"HyperDash":false}]},{"StartTime":180135.0,"Objects":[{"StartTime":180135.0,"Position":307.0,"HyperDash":false}]},{"StartTime":180244.0,"Objects":[{"StartTime":180244.0,"Position":238.0,"HyperDash":false}]},{"StartTime":180354.0,"Objects":[{"StartTime":180354.0,"Position":307.0,"HyperDash":false}]},{"StartTime":180463.0,"Objects":[{"StartTime":180463.0,"Position":417.0,"HyperDash":false},{"StartTime":180572.0,"Position":417.896027,"HyperDash":true}]},{"StartTime":180682.0,"Objects":[{"StartTime":180682.0,"Position":216.0,"HyperDash":false}]},{"StartTime":180792.0,"Objects":[{"StartTime":180792.0,"Position":313.0,"HyperDash":false}]},{"StartTime":180901.0,"Objects":[{"StartTime":180901.0,"Position":381.0,"HyperDash":false}]},{"StartTime":181011.0,"Objects":[{"StartTime":181011.0,"Position":313.0,"HyperDash":false}]},{"StartTime":181120.0,"Objects":[{"StartTime":181120.0,"Position":203.0,"HyperDash":false}]},{"StartTime":181230.0,"Objects":[{"StartTime":181230.0,"Position":133.0,"HyperDash":false}]},{"StartTime":181339.0,"Objects":[{"StartTime":181339.0,"Position":203.0,"HyperDash":false}]},{"StartTime":181558.0,"Objects":[{"StartTime":181558.0,"Position":396.0,"HyperDash":false},{"StartTime":181649.0,"Position":414.144623,"HyperDash":false},{"StartTime":181776.0,"Position":397.136444,"HyperDash":false}]},{"StartTime":181887.0,"Objects":[{"StartTime":181887.0,"Position":320.0,"HyperDash":false}]},{"StartTime":181996.0,"Objects":[{"StartTime":181996.0,"Position":210.0,"HyperDash":false},{"StartTime":182087.0,"Position":169.395859,"HyperDash":false},{"StartTime":182214.0,"Position":72.00328,"HyperDash":false}]},{"StartTime":182325.0,"Objects":[{"StartTime":182325.0,"Position":148.0,"HyperDash":true}]},{"StartTime":182434.0,"Objects":[{"StartTime":182434.0,"Position":347.0,"HyperDash":false}]},{"StartTime":182544.0,"Objects":[{"StartTime":182544.0,"Position":416.0,"HyperDash":false}]},{"StartTime":182653.0,"Objects":[{"StartTime":182653.0,"Position":347.0,"HyperDash":false}]},{"StartTime":182872.0,"Objects":[{"StartTime":182872.0,"Position":154.0,"HyperDash":false}]},{"StartTime":182982.0,"Objects":[{"StartTime":182982.0,"Position":85.0,"HyperDash":false}]},{"StartTime":183091.0,"Objects":[{"StartTime":183091.0,"Position":154.0,"HyperDash":false}]},{"StartTime":183310.0,"Objects":[{"StartTime":183310.0,"Position":347.0,"HyperDash":false},{"StartTime":183401.0,"Position":374.666382,"HyperDash":false},{"StartTime":183528.0,"Position":343.605865,"HyperDash":false}]},{"StartTime":183639.0,"Objects":[{"StartTime":183639.0,"Position":231.0,"HyperDash":false}]},{"StartTime":183748.0,"Objects":[{"StartTime":183748.0,"Position":162.0,"HyperDash":false}]},{"StartTime":183858.0,"Objects":[{"StartTime":183858.0,"Position":231.0,"HyperDash":false}]},{"StartTime":183967.0,"Objects":[{"StartTime":183967.0,"Position":343.0,"HyperDash":false},{"StartTime":184076.0,"Position":344.8897,"HyperDash":true}]},{"StartTime":184186.0,"Objects":[{"StartTime":184186.0,"Position":143.0,"HyperDash":false}]},{"StartTime":184405.0,"Objects":[{"StartTime":184405.0,"Position":323.0,"HyperDash":false}]},{"StartTime":184624.0,"Objects":[{"StartTime":184624.0,"Position":143.0,"HyperDash":false},{"StartTime":184715.0,"Position":105.191986,"HyperDash":false},{"StartTime":184842.0,"Position":143.952225,"HyperDash":false}]},{"StartTime":184953.0,"Objects":[{"StartTime":184953.0,"Position":221.0,"HyperDash":true}]},{"StartTime":185062.0,"Objects":[{"StartTime":185062.0,"Position":421.0,"HyperDash":false},{"StartTime":185116.0,"Position":402.9211,"HyperDash":false},{"StartTime":185171.0,"Position":371.211121,"HyperDash":false},{"StartTime":185225.0,"Position":307.1322,"HyperDash":false},{"StartTime":185280.0,"Position":283.422241,"HyperDash":false},{"StartTime":185335.0,"Position":234.712234,"HyperDash":false},{"StartTime":185390.0,"Position":214.002228,"HyperDash":false},{"StartTime":185444.0,"Position":264.081116,"HyperDash":false},{"StartTime":185499.0,"Position":282.791138,"HyperDash":false},{"StartTime":185590.0,"Position":328.2204,"HyperDash":false},{"StartTime":185718.0,"Position":421.0,"HyperDash":true}]},{"StartTime":185938.0,"Objects":[{"StartTime":185938.0,"Position":102.0,"HyperDash":false},{"StartTime":186029.0,"Position":81.6439056,"HyperDash":false},{"StartTime":186156.0,"Position":105.267693,"HyperDash":false}]},{"StartTime":186266.0,"Objects":[{"StartTime":186266.0,"Position":181.0,"HyperDash":false}]},{"StartTime":186376.0,"Objects":[{"StartTime":186376.0,"Position":291.0,"HyperDash":false},{"StartTime":186467.0,"Position":364.6055,"HyperDash":false},{"StartTime":186594.0,"Position":429.0,"HyperDash":false}]},{"StartTime":186704.0,"Objects":[{"StartTime":186704.0,"Position":352.0,"HyperDash":true}]},{"StartTime":186814.0,"Objects":[{"StartTime":186814.0,"Position":150.0,"HyperDash":false},{"StartTime":186905.0,"Position":147.9285,"HyperDash":false},{"StartTime":187032.0,"Position":146.246689,"HyperDash":false}]},{"StartTime":187142.0,"Objects":[{"StartTime":187142.0,"Position":257.0,"HyperDash":false}]},{"StartTime":187252.0,"Objects":[{"StartTime":187252.0,"Position":325.0,"HyperDash":false}]},{"StartTime":187361.0,"Objects":[{"StartTime":187361.0,"Position":253.0,"HyperDash":false}]},{"StartTime":187471.0,"Objects":[{"StartTime":187471.0,"Position":141.0,"HyperDash":false},{"StartTime":187580.0,"Position":72.0,"HyperDash":true}]},{"StartTime":187690.0,"Objects":[{"StartTime":187690.0,"Position":307.0,"HyperDash":false},{"StartTime":187781.0,"Position":334.582428,"HyperDash":false},{"StartTime":187908.0,"Position":308.8075,"HyperDash":false}]},{"StartTime":188128.0,"Objects":[{"StartTime":188128.0,"Position":113.0,"HyperDash":false},{"StartTime":188219.0,"Position":99.06281,"HyperDash":false},{"StartTime":188346.0,"Position":114.246552,"HyperDash":false}]},{"StartTime":188456.0,"Objects":[{"StartTime":188456.0,"Position":190.0,"HyperDash":true}]},{"StartTime":188566.0,"Objects":[{"StartTime":188566.0,"Position":391.0,"HyperDash":false}]},{"StartTime":188785.0,"Objects":[{"StartTime":188785.0,"Position":211.0,"HyperDash":false}]},{"StartTime":189004.0,"Objects":[{"StartTime":189004.0,"Position":390.0,"HyperDash":false},{"StartTime":189095.0,"Position":373.8,"HyperDash":false},{"StartTime":189222.0,"Position":391.916473,"HyperDash":true}]},{"StartTime":189442.0,"Objects":[{"StartTime":189442.0,"Position":73.0,"HyperDash":false}]},{"StartTime":189551.0,"Objects":[{"StartTime":189551.0,"Position":39.0,"HyperDash":false}]},{"StartTime":189661.0,"Objects":[{"StartTime":189661.0,"Position":76.0,"HyperDash":false}]},{"StartTime":189770.0,"Objects":[{"StartTime":189770.0,"Position":158.0,"HyperDash":false}]},{"StartTime":189880.0,"Objects":[{"StartTime":189880.0,"Position":268.0,"HyperDash":false},{"StartTime":189971.0,"Position":212.3957,"HyperDash":false},{"StartTime":190098.0,"Position":130.002914,"HyperDash":false}]},{"StartTime":190208.0,"Objects":[{"StartTime":190208.0,"Position":213.0,"HyperDash":true}]},{"StartTime":190317.0,"Objects":[{"StartTime":190317.0,"Position":412.0,"HyperDash":false},{"StartTime":190408.0,"Position":424.883728,"HyperDash":false},{"StartTime":190535.0,"Position":410.9749,"HyperDash":false}]},{"StartTime":190646.0,"Objects":[{"StartTime":190646.0,"Position":320.0,"HyperDash":false}]},{"StartTime":190755.0,"Objects":[{"StartTime":190755.0,"Position":230.0,"HyperDash":false}]},{"StartTime":190974.0,"Objects":[{"StartTime":190974.0,"Position":409.0,"HyperDash":true}]},{"StartTime":191193.0,"Objects":[{"StartTime":191193.0,"Position":91.0,"HyperDash":false},{"StartTime":191247.0,"Position":44.74952,"HyperDash":false},{"StartTime":191302.0,"Position":25.7194824,"HyperDash":false},{"StartTime":191356.0,"Position":28.6760178,"HyperDash":false},{"StartTime":191411.0,"Position":24.610136,"HyperDash":false},{"StartTime":191502.0,"Position":53.48176,"HyperDash":false},{"StartTime":191630.0,"Position":137.592667,"HyperDash":false}]},{"StartTime":191850.0,"Objects":[{"StartTime":191850.0,"Position":344.0,"HyperDash":false}]},{"StartTime":191960.0,"Objects":[{"StartTime":191960.0,"Position":427.0,"HyperDash":false}]},{"StartTime":192069.0,"Objects":[{"StartTime":192069.0,"Position":344.0,"HyperDash":false}]},{"StartTime":192288.0,"Objects":[{"StartTime":192288.0,"Position":138.0,"HyperDash":false}]},{"StartTime":192507.0,"Objects":[{"StartTime":192507.0,"Position":427.0,"HyperDash":false},{"StartTime":192598.0,"Position":442.391876,"HyperDash":false},{"StartTime":192725.0,"Position":427.938751,"HyperDash":true}]},{"StartTime":192945.0,"Objects":[{"StartTime":192945.0,"Position":81.0,"HyperDash":false},{"StartTime":193036.0,"Position":144.887146,"HyperDash":false},{"StartTime":193163.0,"Position":260.4,"HyperDash":false}]},{"StartTime":193383.0,"Objects":[{"StartTime":193383.0,"Position":81.0,"HyperDash":true},{"StartTime":193474.0,"Position":189.970917,"HyperDash":false},{"StartTime":193601.0,"Position":370.798462,"HyperDash":false}]},{"StartTime":193821.0,"Objects":[{"StartTime":193821.0,"Position":190.0,"HyperDash":false},{"StartTime":193912.0,"Position":279.887146,"HyperDash":false},{"StartTime":194039.0,"Position":369.4,"HyperDash":false}]},{"StartTime":194259.0,"Objects":[{"StartTime":194259.0,"Position":78.0,"HyperDash":true},{"StartTime":194350.0,"Position":207.970978,"HyperDash":false},{"StartTime":194477.0,"Position":367.798584,"HyperDash":true}]},{"StartTime":194697.0,"Objects":[{"StartTime":194697.0,"Position":76.0,"HyperDash":false},{"StartTime":194788.0,"Position":77.1591339,"HyperDash":false},{"StartTime":194915.0,"Position":73.98562,"HyperDash":false}]},{"StartTime":195135.0,"Objects":[{"StartTime":195135.0,"Position":365.0,"HyperDash":true},{"StartTime":195226.0,"Position":253.0291,"HyperDash":false},{"StartTime":195353.0,"Position":75.2016,"HyperDash":true}]},{"StartTime":195573.0,"Objects":[{"StartTime":195573.0,"Position":394.0,"HyperDash":false},{"StartTime":195664.0,"Position":392.411469,"HyperDash":false},{"StartTime":195791.0,"Position":394.9857,"HyperDash":false}]},{"StartTime":196011.0,"Objects":[{"StartTime":196011.0,"Position":105.0,"HyperDash":true},{"StartTime":196102.0,"Position":210.9709,"HyperDash":false},{"StartTime":196229.0,"Position":394.7984,"HyperDash":true}]},{"StartTime":196449.0,"Objects":[{"StartTime":196449.0,"Position":75.0,"HyperDash":true}]},{"StartTime":196668.0,"Objects":[{"StartTime":196668.0,"Position":422.0,"HyperDash":true},{"StartTime":196722.0,"Position":331.3793,"HyperDash":false},{"StartTime":196777.0,"Position":264.4323,"HyperDash":false},{"StartTime":196831.0,"Position":194.811615,"HyperDash":false},{"StartTime":196886.0,"Position":132.201477,"HyperDash":false},{"StartTime":196977.0,"Position":246.232452,"HyperDash":false},{"StartTime":197105.0,"Position":422.0,"HyperDash":true}]},{"StartTime":197325.0,"Objects":[{"StartTime":197325.0,"Position":75.0,"HyperDash":true},{"StartTime":197379.0,"Position":144.6207,"HyperDash":false},{"StartTime":197434.0,"Position":211.567688,"HyperDash":false},{"StartTime":197488.0,"Position":310.1884,"HyperDash":false},{"StartTime":197543.0,"Position":364.798523,"HyperDash":false},{"StartTime":197634.0,"Position":238.767548,"HyperDash":false},{"StartTime":197762.0,"Position":75.0,"HyperDash":true}]},{"StartTime":197982.0,"Objects":[{"StartTime":197982.0,"Position":395.0,"HyperDash":true}]},{"StartTime":198201.0,"Objects":[{"StartTime":198201.0,"Position":47.0,"HyperDash":true},{"StartTime":198292.0,"Position":164.970886,"HyperDash":false},{"StartTime":198419.0,"Position":336.7984,"HyperDash":false}]},{"StartTime":198639.0,"Objects":[{"StartTime":198639.0,"Position":142.0,"HyperDash":false},{"StartTime":198730.0,"Position":237.6467,"HyperDash":false},{"StartTime":198857.0,"Position":335.197571,"HyperDash":true}]},{"StartTime":199077.0,"Objects":[{"StartTime":199077.0,"Position":26.0,"HyperDash":true}]},{"StartTime":199296.0,"Objects":[{"StartTime":199296.0,"Position":371.0,"HyperDash":false},{"StartTime":199350.0,"Position":333.0469,"HyperDash":false},{"StartTime":199405.0,"Position":303.045837,"HyperDash":false},{"StartTime":199459.0,"Position":275.5022,"HyperDash":false},{"StartTime":199514.0,"Position":251.71991,"HyperDash":false},{"StartTime":199605.0,"Position":278.949951,"HyperDash":false},{"StartTime":199733.0,"Position":378.108917,"HyperDash":true}]},{"StartTime":199953.0,"Objects":[{"StartTime":199953.0,"Position":56.0,"HyperDash":false},{"StartTime":200007.0,"Position":103.078979,"HyperDash":false},{"StartTime":200062.0,"Position":109.78904,"HyperDash":false},{"StartTime":200116.0,"Position":145.868011,"HyperDash":false},{"StartTime":200171.0,"Position":193.578079,"HyperDash":false},{"StartTime":200226.0,"Position":229.288147,"HyperDash":false},{"StartTime":200281.0,"Position":262.99823,"HyperDash":false},{"StartTime":200335.0,"Position":225.91925,"HyperDash":false},{"StartTime":200390.0,"Position":194.209167,"HyperDash":false},{"StartTime":200481.0,"Position":139.779785,"HyperDash":false},{"StartTime":200609.0,"Position":56.0,"HyperDash":false}]},{"StartTime":200828.0,"Objects":[{"StartTime":200828.0,"Position":249.0,"HyperDash":false},{"StartTime":200937.0,"Position":250.56778,"HyperDash":false}]},{"StartTime":201047.0,"Objects":[{"StartTime":201047.0,"Position":160.0,"HyperDash":false}]},{"StartTime":201157.0,"Objects":[{"StartTime":201157.0,"Position":250.0,"HyperDash":true}]},{"StartTime":201266.0,"Objects":[{"StartTime":201266.0,"Position":50.0,"HyperDash":false}]},{"StartTime":201376.0,"Objects":[{"StartTime":201376.0,"Position":139.0,"HyperDash":false}]},{"StartTime":201485.0,"Objects":[{"StartTime":201485.0,"Position":50.0,"HyperDash":true}]},{"StartTime":201595.0,"Objects":[{"StartTime":201595.0,"Position":285.0,"HyperDash":true}]},{"StartTime":201704.0,"Objects":[{"StartTime":201704.0,"Position":50.0,"HyperDash":false},{"StartTime":201813.0,"Position":48.2537231,"HyperDash":true}]},{"StartTime":201923.0,"Objects":[{"StartTime":201923.0,"Position":249.0,"HyperDash":true}]},{"StartTime":202033.0,"Objects":[{"StartTime":202033.0,"Position":48.0,"HyperDash":false}]},{"StartTime":202142.0,"Objects":[{"StartTime":202142.0,"Position":141.0,"HyperDash":false},{"StartTime":202233.0,"Position":181.263123,"HyperDash":false},{"StartTime":202360.0,"Position":140.921326,"HyperDash":false}]},{"StartTime":202471.0,"Objects":[{"StartTime":202471.0,"Position":45.0,"HyperDash":true}]},{"StartTime":202580.0,"Objects":[{"StartTime":202580.0,"Position":278.0,"HyperDash":false}]},{"StartTime":202690.0,"Objects":[{"StartTime":202690.0,"Position":180.0,"HyperDash":false},{"StartTime":202799.0,"Position":179.028259,"HyperDash":true}]},{"StartTime":202909.0,"Objects":[{"StartTime":202909.0,"Position":380.0,"HyperDash":false}]},{"StartTime":203018.0,"Objects":[{"StartTime":203018.0,"Position":283.0,"HyperDash":false},{"StartTime":203109.0,"Position":343.604553,"HyperDash":false},{"StartTime":203236.0,"Position":420.997742,"HyperDash":false}]},{"StartTime":203347.0,"Objects":[{"StartTime":203347.0,"Position":337.0,"HyperDash":true}]},{"StartTime":203456.0,"Objects":[{"StartTime":203456.0,"Position":103.0,"HyperDash":false},{"StartTime":203547.0,"Position":60.25659,"HyperDash":false},{"StartTime":203674.0,"Position":111.501694,"HyperDash":false}]},{"StartTime":203785.0,"Objects":[{"StartTime":203785.0,"Position":202.0,"HyperDash":false}]},{"StartTime":203894.0,"Objects":[{"StartTime":203894.0,"Position":111.0,"HyperDash":false},{"StartTime":204003.0,"Position":109.296814,"HyperDash":true}]},{"StartTime":204113.0,"Objects":[{"StartTime":204113.0,"Position":310.0,"HyperDash":false},{"StartTime":204222.0,"Position":378.995667,"HyperDash":true}]},{"StartTime":204332.0,"Objects":[{"StartTime":204332.0,"Position":177.0,"HyperDash":true}]},{"StartTime":204442.0,"Objects":[{"StartTime":204442.0,"Position":378.0,"HyperDash":false},{"StartTime":204551.0,"Position":378.932343,"HyperDash":true}]},{"StartTime":204661.0,"Objects":[{"StartTime":204661.0,"Position":177.0,"HyperDash":false}]},{"StartTime":204770.0,"Objects":[{"StartTime":204770.0,"Position":80.0,"HyperDash":false},{"StartTime":204861.0,"Position":65.8601456,"HyperDash":false},{"StartTime":204988.0,"Position":78.31786,"HyperDash":false}]},{"StartTime":205099.0,"Objects":[{"StartTime":205099.0,"Position":162.0,"HyperDash":true}]},{"StartTime":205208.0,"Objects":[{"StartTime":205208.0,"Position":395.0,"HyperDash":false},{"StartTime":205317.0,"Position":326.0,"HyperDash":true}]},{"StartTime":205427.0,"Objects":[{"StartTime":205427.0,"Position":124.0,"HyperDash":true}]},{"StartTime":205536.0,"Objects":[{"StartTime":205536.0,"Position":323.0,"HyperDash":false}]},{"StartTime":205646.0,"Objects":[{"StartTime":205646.0,"Position":420.0,"HyperDash":false},{"StartTime":205737.0,"Position":379.3955,"HyperDash":false},{"StartTime":205864.0,"Position":282.002441,"HyperDash":false}]},{"StartTime":205974.0,"Objects":[{"StartTime":205974.0,"Position":379.0,"HyperDash":true}]},{"StartTime":206084.0,"Objects":[{"StartTime":206084.0,"Position":143.0,"HyperDash":false},{"StartTime":206193.0,"Position":74.02588,"HyperDash":false}]},{"StartTime":206303.0,"Objects":[{"StartTime":206303.0,"Position":171.0,"HyperDash":true}]},{"StartTime":206412.0,"Objects":[{"StartTime":206412.0,"Position":370.0,"HyperDash":false}]},{"StartTime":206522.0,"Objects":[{"StartTime":206522.0,"Position":467.0,"HyperDash":false},{"StartTime":206613.0,"Position":501.909729,"HyperDash":false},{"StartTime":206740.0,"Position":463.333649,"HyperDash":false}]},{"StartTime":206850.0,"Objects":[{"StartTime":206850.0,"Position":380.0,"HyperDash":true}]},{"StartTime":206960.0,"Objects":[{"StartTime":206960.0,"Position":109.0,"HyperDash":false},{"StartTime":207051.0,"Position":184.6055,"HyperDash":false},{"StartTime":207178.0,"Position":247.0,"HyperDash":false}]},{"StartTime":207288.0,"Objects":[{"StartTime":207288.0,"Position":156.0,"HyperDash":false}]},{"StartTime":207398.0,"Objects":[{"StartTime":207398.0,"Position":65.0,"HyperDash":true}]},{"StartTime":207617.0,"Objects":[{"StartTime":207617.0,"Position":382.0,"HyperDash":false}]},{"StartTime":207726.0,"Objects":[{"StartTime":207726.0,"Position":420.0,"HyperDash":false}]},{"StartTime":207836.0,"Objects":[{"StartTime":207836.0,"Position":378.0,"HyperDash":false}]},{"StartTime":207945.0,"Objects":[{"StartTime":207945.0,"Position":302.0,"HyperDash":false}]},{"StartTime":208055.0,"Objects":[{"StartTime":208055.0,"Position":191.0,"HyperDash":false},{"StartTime":208164.0,"Position":190.092178,"HyperDash":true}]},{"StartTime":208274.0,"Objects":[{"StartTime":208274.0,"Position":391.0,"HyperDash":false},{"StartTime":208365.0,"Position":402.309845,"HyperDash":false},{"StartTime":208492.0,"Position":381.4403,"HyperDash":false}]},{"StartTime":208602.0,"Objects":[{"StartTime":208602.0,"Position":298.0,"HyperDash":true}]},{"StartTime":208712.0,"Objects":[{"StartTime":208712.0,"Position":62.0,"HyperDash":false},{"StartTime":208821.0,"Position":61.1154556,"HyperDash":false}]},{"StartTime":208931.0,"Objects":[{"StartTime":208931.0,"Position":172.0,"HyperDash":false},{"StartTime":209040.0,"Position":240.99353,"HyperDash":true}]},{"StartTime":209150.0,"Objects":[{"StartTime":209150.0,"Position":442.0,"HyperDash":false},{"StartTime":209241.0,"Position":460.81012,"HyperDash":false},{"StartTime":209368.0,"Position":438.616364,"HyperDash":false}]},{"StartTime":209478.0,"Objects":[{"StartTime":209478.0,"Position":355.0,"HyperDash":true}]},{"StartTime":209588.0,"Objects":[{"StartTime":209588.0,"Position":119.0,"HyperDash":false},{"StartTime":209697.0,"Position":116.205,"HyperDash":false}]},{"StartTime":209807.0,"Objects":[{"StartTime":209807.0,"Position":220.0,"HyperDash":false}]},{"StartTime":210026.0,"Objects":[{"StartTime":210026.0,"Position":413.0,"HyperDash":false}]},{"StartTime":210244.0,"Objects":[{"StartTime":210244.0,"Position":124.0,"HyperDash":false},{"StartTime":210353.0,"Position":55.0,"HyperDash":true}]},{"StartTime":210463.0,"Objects":[{"StartTime":210463.0,"Position":325.0,"HyperDash":false},{"StartTime":210517.0,"Position":370.597351,"HyperDash":false},{"StartTime":210572.0,"Position":383.999176,"HyperDash":false},{"StartTime":210626.0,"Position":443.559265,"HyperDash":false},{"StartTime":210681.0,"Position":452.158966,"HyperDash":false},{"StartTime":210772.0,"Position":494.5323,"HyperDash":false},{"StartTime":210900.0,"Position":484.299774,"HyperDash":true}]},{"StartTime":211120.0,"Objects":[{"StartTime":211120.0,"Position":165.0,"HyperDash":false},{"StartTime":211174.0,"Position":212.105072,"HyperDash":false},{"StartTime":211229.0,"Position":213.841736,"HyperDash":false},{"StartTime":211283.0,"Position":247.946808,"HyperDash":false},{"StartTime":211338.0,"Position":302.683472,"HyperDash":false},{"StartTime":211429.0,"Position":349.15686,"HyperDash":false},{"StartTime":211557.0,"Position":440.9985,"HyperDash":true}]},{"StartTime":211777.0,"Objects":[{"StartTime":211777.0,"Position":149.0,"HyperDash":false},{"StartTime":211868.0,"Position":93.3959351,"HyperDash":false},{"StartTime":211995.0,"Position":11.0034637,"HyperDash":true}]},{"StartTime":212215.0,"Objects":[{"StartTime":212215.0,"Position":357.0,"HyperDash":false},{"StartTime":212269.0,"Position":341.920715,"HyperDash":false},{"StartTime":212324.0,"Position":294.210358,"HyperDash":false},{"StartTime":212378.0,"Position":264.1311,"HyperDash":false},{"StartTime":212433.0,"Position":219.420731,"HyperDash":false},{"StartTime":212488.0,"Position":202.710373,"HyperDash":false},{"StartTime":212543.0,"Position":150.0,"HyperDash":false},{"StartTime":212597.0,"Position":190.079254,"HyperDash":false},{"StartTime":212652.0,"Position":218.789642,"HyperDash":false},{"StartTime":212743.0,"Position":290.2195,"HyperDash":false},{"StartTime":212871.0,"Position":357.0,"HyperDash":true}]},{"StartTime":213091.0,"Objects":[{"StartTime":213091.0,"Position":65.0,"HyperDash":false},{"StartTime":213145.0,"Position":117.105263,"HyperDash":false},{"StartTime":213200.0,"Position":132.8421,"HyperDash":false},{"StartTime":213254.0,"Position":151.947357,"HyperDash":false},{"StartTime":213309.0,"Position":202.6842,"HyperDash":false},{"StartTime":213400.0,"Position":256.1579,"HyperDash":false},{"StartTime":213528.0,"Position":341.0,"HyperDash":false}]},{"StartTime":213639.0,"Objects":[{"StartTime":213639.0,"Position":250.0,"HyperDash":false}]},{"StartTime":213748.0,"Objects":[{"StartTime":213748.0,"Position":339.0,"HyperDash":true}]},{"StartTime":213858.0,"Objects":[{"StartTime":213858.0,"Position":103.0,"HyperDash":true}]},{"StartTime":213967.0,"Objects":[{"StartTime":213967.0,"Position":339.0,"HyperDash":false},{"StartTime":214058.0,"Position":364.006348,"HyperDash":false},{"StartTime":214185.0,"Position":336.10022,"HyperDash":false}]},{"StartTime":214296.0,"Objects":[{"StartTime":214296.0,"Position":245.0,"HyperDash":false}]},{"StartTime":214405.0,"Objects":[{"StartTime":214405.0,"Position":334.0,"HyperDash":false},{"StartTime":214514.0,"Position":335.746277,"HyperDash":true}]},{"StartTime":214624.0,"Objects":[{"StartTime":214624.0,"Position":134.0,"HyperDash":false},{"StartTime":214733.0,"Position":65.0045547,"HyperDash":true}]},{"StartTime":214843.0,"Objects":[{"StartTime":214843.0,"Position":300.0,"HyperDash":false},{"StartTime":214952.0,"Position":300.896027,"HyperDash":true}]},{"StartTime":215062.0,"Objects":[{"StartTime":215062.0,"Position":99.0,"HyperDash":true}]},{"StartTime":215172.0,"Objects":[{"StartTime":215172.0,"Position":300.0,"HyperDash":false}]},{"StartTime":215281.0,"Objects":[{"StartTime":215281.0,"Position":203.0,"HyperDash":false},{"StartTime":215372.0,"Position":151.402954,"HyperDash":false},{"StartTime":215499.0,"Position":65.02028,"HyperDash":false}]},{"StartTime":215609.0,"Objects":[{"StartTime":215609.0,"Position":148.0,"HyperDash":true}]},{"StartTime":215719.0,"Objects":[{"StartTime":215719.0,"Position":383.0,"HyperDash":false},{"StartTime":215828.0,"Position":314.0,"HyperDash":true}]},{"StartTime":215938.0,"Objects":[{"StartTime":215938.0,"Position":112.0,"HyperDash":true}]},{"StartTime":216047.0,"Objects":[{"StartTime":216047.0,"Position":311.0,"HyperDash":false}]},{"StartTime":216157.0,"Objects":[{"StartTime":216157.0,"Position":408.0,"HyperDash":false},{"StartTime":216248.0,"Position":431.067078,"HyperDash":false},{"StartTime":216375.0,"Position":402.494934,"HyperDash":false}]},{"StartTime":216485.0,"Objects":[{"StartTime":216485.0,"Position":305.0,"HyperDash":true}]},{"StartTime":216595.0,"Objects":[{"StartTime":216595.0,"Position":69.0,"HyperDash":false},{"StartTime":216704.0,"Position":68.16873,"HyperDash":false}]},{"StartTime":216814.0,"Objects":[{"StartTime":216814.0,"Position":179.0,"HyperDash":false},{"StartTime":216923.0,"Position":247.995117,"HyperDash":true}]},{"StartTime":217033.0,"Objects":[{"StartTime":217033.0,"Position":449.0,"HyperDash":false},{"StartTime":217142.0,"Position":380.0034,"HyperDash":true}]},{"StartTime":217252.0,"Objects":[{"StartTime":217252.0,"Position":178.0,"HyperDash":false},{"StartTime":217361.0,"Position":109.0,"HyperDash":true}]},{"StartTime":217471.0,"Objects":[{"StartTime":217471.0,"Position":344.0,"HyperDash":false},{"StartTime":217562.0,"Position":286.3945,"HyperDash":false},{"StartTime":217689.0,"Position":206.0,"HyperDash":false}]},{"StartTime":217799.0,"Objects":[{"StartTime":217799.0,"Position":289.0,"HyperDash":false}]},{"StartTime":217909.0,"Objects":[{"StartTime":217909.0,"Position":206.0,"HyperDash":false},{"StartTime":218018.0,"Position":205.092178,"HyperDash":true}]},{"StartTime":218128.0,"Objects":[{"StartTime":218128.0,"Position":406.0,"HyperDash":false},{"StartTime":218237.0,"Position":474.99353,"HyperDash":true}]},{"StartTime":218347.0,"Objects":[{"StartTime":218347.0,"Position":239.0,"HyperDash":false},{"StartTime":218456.0,"Position":170.005249,"HyperDash":true}]},{"StartTime":218566.0,"Objects":[{"StartTime":218566.0,"Position":371.0,"HyperDash":true}]},{"StartTime":218675.0,"Objects":[{"StartTime":218675.0,"Position":170.0,"HyperDash":false}]},{"StartTime":218785.0,"Objects":[{"StartTime":218785.0,"Position":267.0,"HyperDash":false},{"StartTime":218876.0,"Position":329.6045,"HyperDash":false},{"StartTime":219003.0,"Position":404.997559,"HyperDash":false}]},{"StartTime":219113.0,"Objects":[{"StartTime":219113.0,"Position":321.0,"HyperDash":true}]},{"StartTime":219223.0,"Objects":[{"StartTime":219223.0,"Position":85.0,"HyperDash":false},{"StartTime":219332.0,"Position":85.0,"HyperDash":true}]},{"StartTime":219442.0,"Objects":[{"StartTime":219442.0,"Position":286.0,"HyperDash":false},{"StartTime":219551.0,"Position":354.996,"HyperDash":true}]},{"StartTime":219661.0,"Objects":[{"StartTime":219661.0,"Position":119.0,"HyperDash":false},{"StartTime":219770.0,"Position":50.0000076,"HyperDash":true}]},{"StartTime":219880.0,"Objects":[{"StartTime":219880.0,"Position":320.0,"HyperDash":false}]},{"StartTime":219989.0,"Objects":[{"StartTime":219989.0,"Position":399.0,"HyperDash":false}]},{"StartTime":220099.0,"Objects":[{"StartTime":220099.0,"Position":402.0,"HyperDash":false}]},{"StartTime":220208.0,"Objects":[{"StartTime":220208.0,"Position":327.0,"HyperDash":true}]},{"StartTime":220317.0,"Objects":[{"StartTime":220317.0,"Position":129.0,"HyperDash":false},{"StartTime":220426.0,"Position":129.0,"HyperDash":true}]},{"StartTime":220536.0,"Objects":[{"StartTime":220536.0,"Position":330.0,"HyperDash":false},{"StartTime":220645.0,"Position":398.953857,"HyperDash":true}]},{"StartTime":220755.0,"Objects":[{"StartTime":220755.0,"Position":163.0,"HyperDash":false},{"StartTime":220864.0,"Position":94.00001,"HyperDash":true}]},{"StartTime":220974.0,"Objects":[{"StartTime":220974.0,"Position":364.0,"HyperDash":false}]},{"StartTime":221084.0,"Objects":[{"StartTime":221084.0,"Position":439.0,"HyperDash":false}]},{"StartTime":221193.0,"Objects":[{"StartTime":221193.0,"Position":426.0,"HyperDash":false}]},{"StartTime":221303.0,"Objects":[{"StartTime":221303.0,"Position":350.0,"HyperDash":false}]},{"StartTime":221412.0,"Objects":[{"StartTime":221412.0,"Position":240.0,"HyperDash":false},{"StartTime":221521.0,"Position":239.148209,"HyperDash":true}]},{"StartTime":221631.0,"Objects":[{"StartTime":221631.0,"Position":440.0,"HyperDash":false}]},{"StartTime":221741.0,"Objects":[{"StartTime":221741.0,"Position":472.0,"HyperDash":false}]},{"StartTime":221850.0,"Objects":[{"StartTime":221850.0,"Position":434.0,"HyperDash":false}]},{"StartTime":221960.0,"Objects":[{"StartTime":221960.0,"Position":357.0,"HyperDash":true}]},{"StartTime":222069.0,"Objects":[{"StartTime":222069.0,"Position":157.0,"HyperDash":false},{"StartTime":222178.0,"Position":88.06657,"HyperDash":true}]},{"StartTime":222288.0,"Objects":[{"StartTime":222288.0,"Position":289.0,"HyperDash":false},{"StartTime":222379.0,"Position":364.60202,"HyperDash":false},{"StartTime":222506.0,"Position":426.991669,"HyperDash":false}]},{"StartTime":222617.0,"Objects":[{"StartTime":222617.0,"Position":343.0,"HyperDash":true}]},{"StartTime":222726.0,"Objects":[{"StartTime":222726.0,"Position":109.0,"HyperDash":false},{"StartTime":222817.0,"Position":84.0503159,"HyperDash":false},{"StartTime":222944.0,"Position":116.766586,"HyperDash":false}]},{"StartTime":223055.0,"Objects":[{"StartTime":223055.0,"Position":207.0,"HyperDash":false}]},{"StartTime":223164.0,"Objects":[{"StartTime":223164.0,"Position":117.0,"HyperDash":false},{"StartTime":223273.0,"Position":114.6221,"HyperDash":true}]},{"StartTime":223383.0,"Objects":[{"StartTime":223383.0,"Position":315.0,"HyperDash":false},{"StartTime":223492.0,"Position":383.995117,"HyperDash":true}]},{"StartTime":223602.0,"Objects":[{"StartTime":223602.0,"Position":148.0,"HyperDash":false},{"StartTime":223711.0,"Position":145.971466,"HyperDash":false}]},{"StartTime":223821.0,"Objects":[{"StartTime":223821.0,"Position":256.0,"HyperDash":false},{"StartTime":223930.0,"Position":325.0,"HyperDash":true}]},{"StartTime":224040.0,"Objects":[{"StartTime":224040.0,"Position":123.0,"HyperDash":false},{"StartTime":224149.0,"Position":192.0,"HyperDash":true}]},{"StartTime":224259.0,"Objects":[{"StartTime":224259.0,"Position":393.0,"HyperDash":false},{"StartTime":224368.0,"Position":393.896027,"HyperDash":true}]},{"StartTime":224478.0,"Objects":[{"StartTime":224478.0,"Position":158.0,"HyperDash":false}]},{"StartTime":224588.0,"Objects":[{"StartTime":224588.0,"Position":82.0,"HyperDash":false}]},{"StartTime":224697.0,"Objects":[{"StartTime":224697.0,"Position":44.0,"HyperDash":false}]},{"StartTime":224807.0,"Objects":[{"StartTime":224807.0,"Position":86.0,"HyperDash":true}]},{"StartTime":224916.0,"Objects":[{"StartTime":224916.0,"Position":285.0,"HyperDash":false},{"StartTime":225025.0,"Position":353.996,"HyperDash":true}]},{"StartTime":225135.0,"Objects":[{"StartTime":225135.0,"Position":83.0,"HyperDash":false}]},{"StartTime":225244.0,"Objects":[{"StartTime":225244.0,"Position":41.0,"HyperDash":false}]},{"StartTime":225354.0,"Objects":[{"StartTime":225354.0,"Position":82.0,"HyperDash":false}]},{"StartTime":225463.0,"Objects":[{"StartTime":225463.0,"Position":157.0,"HyperDash":false}]},{"StartTime":225573.0,"Objects":[{"StartTime":225573.0,"Position":267.0,"HyperDash":false},{"StartTime":225682.0,"Position":267.0,"HyperDash":true}]},{"StartTime":225792.0,"Objects":[{"StartTime":225792.0,"Position":65.0,"HyperDash":false},{"StartTime":225901.0,"Position":64.19773,"HyperDash":false}]},{"StartTime":226011.0,"Objects":[{"StartTime":226011.0,"Position":154.0,"HyperDash":false}]},{"StartTime":226120.0,"Objects":[{"StartTime":226120.0,"Position":64.0,"HyperDash":true}]},{"StartTime":226230.0,"Objects":[{"StartTime":226230.0,"Position":299.0,"HyperDash":false}]},{"StartTime":226449.0,"Objects":[{"StartTime":226449.0,"Position":105.0,"HyperDash":false},{"StartTime":226558.0,"Position":104.115456,"HyperDash":true}]},{"StartTime":226668.0,"Objects":[{"StartTime":226668.0,"Position":305.0,"HyperDash":true}]},{"StartTime":226777.0,"Objects":[{"StartTime":226777.0,"Position":104.0,"HyperDash":false},{"StartTime":226886.0,"Position":35.0059738,"HyperDash":true}]},{"StartTime":227106.0,"Objects":[{"StartTime":227106.0,"Position":383.0,"HyperDash":false},{"StartTime":227160.0,"Position":350.499268,"HyperDash":false},{"StartTime":227215.0,"Position":324.281738,"HyperDash":false},{"StartTime":227269.0,"Position":266.25296,"HyperDash":false},{"StartTime":227324.0,"Position":247.835876,"HyperDash":false},{"StartTime":227378.0,"Position":218.959808,"HyperDash":false},{"StartTime":227433.0,"Position":168.075058,"HyperDash":false},{"StartTime":227488.0,"Position":136.432785,"HyperDash":false},{"StartTime":227543.0,"Position":126.625404,"HyperDash":false},{"StartTime":227597.0,"Position":101.627563,"HyperDash":false},{"StartTime":227652.0,"Position":86.03102,"HyperDash":false},{"StartTime":227707.0,"Position":60.6709824,"HyperDash":false},{"StartTime":227762.0,"Position":57.8545761,"HyperDash":false},{"StartTime":227853.0,"Position":59.5702324,"HyperDash":false},{"StartTime":227981.0,"Position":63.0289955,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3524302.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3524302.osu new file mode 100644 index 0000000000..36f52c4ae2 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3524302.osu @@ -0,0 +1,889 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 2 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:9.2 +ApproachRate:9.2 +SliderMultiplier:2.76 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +2,88036,100842 +2,172123,178142 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +245,437.956204379562,4,2,1,30,1,0 +17763,-100,4,2,1,65,0,0 +31777,-100,4,2,1,70,0,0 +45792,-100,4,2,1,75,0,0 +52799,-100,4,2,1,80,0,0 +59807,-100,4,2,1,85,0,1 +86960,-90.9090909090909,4,2,1,80,0,1 +87836,-100,4,2,1,75,0,0 +101850,-100,4,2,1,65,0,0 +115865,-100,4,2,1,70,0,0 +129880,-100,4,2,1,75,0,0 +136887,-100,4,2,1,80,0,0 +140828,-100,4,2,1,60,0,0 +141485,-100,4,2,1,65,0,0 +141704,-100,4,2,1,70,0,0 +141923,-100,4,2,1,75,0,0 +142142,-100,4,2,1,80,0,0 +143894,-100,4,2,1,85,0,1 +171923,-100,4,2,1,75,0,0 +178931,-100,4,2,1,75,0,0 +192945,-76.9230769230769,4,2,1,85,0,1 +193383,-47.6190476190476,4,2,1,85,0,1 +193821,-76.9230769230769,4,2,1,85,0,1 +194259,-47.6190476190476,4,2,1,85,0,1 +194697,-100,4,2,1,85,0,1 +195135,-47.6190476190476,4,2,1,85,0,1 +195573,-100,4,2,1,85,0,1 +196011,-47.6190476190476,4,2,1,85,0,1 +196449,-100,4,2,1,85,0,1 +196668,-47.6190476190476,4,2,1,85,0,1 +198639,-71.4285714285714,4,2,2,85,0,1 +199077,-100,4,2,2,85,0,1 +199296,-76.9230769230769,4,2,1,85,0,1 +199953,-100,4,2,1,80,0,0 +201704,-100,4,2,1,85,0,1 +227982,-100,4,2,1,30,0,0 + +[HitObjects] +256,192,14259,12,0,17325,0:0:0:0: +166,339,17763,6,0,L|164:200,1,138,2|0,1:2|0:0,0:0:0:0: +358,201,18201,2,0,L|360:62,1,138,0|0,1:2|0:0,0:0:0:0: +165,63,18639,2,0,L|18:65,1,138,2|2,1:2|0:0,0:0:0:0: +137,64,18967,2,0,L|208:65,1,69,2|0,0:0|1:2,0:0:0:0: +25,64,19296,1,2,0:0:0:0: +314,64,19515,5,2,1:2:0:0: +350,130,19624,1,0,0:0:0:0: +312,196,19734,1,2,0:0:0:0: +118,196,19953,2,0,L|259:197,1,138,2|2,1:2|0:0,0:0:0:0: +449,196,20390,2,0,L|452:342,1,138,2|2,1:2|0:0,0:0:0:0: +271,333,20828,1,2,1:2:0:0: +451,333,21047,1,2,0:0:0:0: +133,333,21266,5,2,1:2:0:0: +97,265,21376,1,0,0:0:0:0: +136,200,21485,1,0,0:0:0:0: +329,200,21704,2,0,L|331:57,1,138,0|0,1:2|0:0,0:0:0:0: +136,62,22142,2,0,L|297:62,1,138,2|2,1:2|0:0,0:0:0:0: +385,62,22471,2,0,L|294:62,1,69,2|0,0:0|1:2,0:0:0:0: +136,62,22799,1,2,0:0:0:0: +425,62,23018,5,2,1:2:0:0: +461,128,23128,1,0,0:0:0:0: +421,192,23237,1,2,0:0:0:0: +227,192,23456,2,0,L|224:332,1,138,2|2,1:2|0:0,0:0:0:0: +404,329,23894,1,2,1:2:0:0: +224,329,24113,1,2,0:0:0:0: +417,329,24332,2,0,L|419:187,1,138,2|2,1:2|0:0,0:0:0:0: +341,191,24661,1,2,0:0:0:0: +107,191,24770,5,2,1:2:0:0: +69,124,24880,1,0,0:0:0:0: +111,61,24989,1,0,0:0:0:0: +304,61,25208,2,0,L|306:200,1,138,0|0,1:2|0:0,0:0:0:0: +111,198,25646,2,0,L|110:337,1,138,2|0,1:2|0:0,0:0:0:0: +220,335,25974,2,0,L|292:335,1,69,2|0,0:0|1:2,0:0:0:0: +108,335,26303,1,2,0:0:0:0: +397,335,26522,5,2,1:2:0:0: +432,268,26631,1,0,0:0:0:0: +395,200,26741,1,2,0:0:0:0: +215,200,26960,1,2,1:2:0:0: +395,200,27179,1,2,0:0:0:0: +201,200,27398,2,0,L|200:59,1,138,2|0,1:2|0:0,0:0:0:0: +380,62,27836,1,0,1:2:0:0: +200,62,28055,1,2,0:0:0:0: +131,62,28164,1,2,0:0:0:0: +365,62,28274,6,0,P|452:120|350:202,1,276,2|0,1:2|0:0,0:0:0:0: +170,202,28931,1,2,0:0:0:0: +349,202,29150,2,0,P|415:208|474:382,1,276,2|0,0:0|0:0,0:0:0:0: +114,381,30026,5,0,1:2:0:0: +292,381,30244,1,8,0:3:0:0: +114,381,30463,2,0,L|113:240,1,138,8|0,0:3|0:0,0:0:0:0: +307,243,30901,2,0,L|309:102,1,138,4|0,0:3|0:0,0:0:0:0: +197,105,31230,2,0,L|129:106,1,69,4|0,0:3|1:2,0:0:0:0: +417,106,31558,2,0,L|418:180,1,69,0|0,3:2|0:0,0:0:0:0: +148,174,31777,5,2,1:2:0:0: +78,174,31887,1,0,0:0:0:0: +148,174,31996,1,0,0:0:0:0: +341,174,32215,2,0,P|354:234|340:315,1,138,0|0,3:2|0:0,0:0:0:0: +265,311,32544,1,0,1:2:0:0: +155,311,32653,2,0,L|-7:310,1,138,2|2,0:0|1:2,0:0:0:0: +93,310,32982,1,2,0:0:0:0: +292,310,33091,1,0,3:2:0:0: +112,310,33310,2,0,L|110:239,1,69,2|0,0:0|0:0,0:0:0:0: +327,242,33529,5,2,1:2:0:0: +396,242,33639,1,0,0:0:0:0: +327,242,33748,1,0,0:0:0:0: +133,242,33967,2,0,L|131:104,1,138,2|2,3:2|0:0,0:0:0:0: +207,104,34296,1,0,1:2:0:0: +316,104,34405,2,0,L|170:104,1,138,2|2,0:0|1:2,0:0:0:0: +254,104,34734,1,0,0:0:0:0: +453,104,34843,2,0,P|466:169|455:240,1,138,2|2,3:2|0:0,0:0:0:0: +378,239,35172,1,2,0:0:0:0: +145,239,35281,5,2,1:2:0:0: +76,239,35390,1,0,0:0:0:0: +145,239,35500,1,0,0:0:0:0: +338,239,35719,2,0,L|340:102,1,138,0|0,3:2|0:0,0:0:0:0: +263,101,36047,1,0,1:2:0:0: +165,101,36157,1,2,0:0:0:0: +263,101,36266,1,2,0:0:0:0: +339,101,36376,1,2,1:2:0:0: +263,101,36485,1,2,0:0:0:0: +61,101,36595,2,0,P|45:160|61:238,1,138,0|2,3:2|0:0,0:0:0:0: +135,234,36923,1,0,0:0:0:0: +371,233,37033,5,2,1:2:0:0: +439,233,37142,1,0,0:0:0:0: +371,233,37252,1,0,0:0:0:0: +177,233,37471,2,0,L|318:233,1,138,2|0,3:2|0:0,0:0:0:0: +238,233,37799,1,0,1:2:0:0: +127,233,37909,2,0,L|125:94,1,138,2|2,0:0|1:2,0:0:0:0: +201,95,38237,1,0,0:0:0:0: +402,95,38347,2,0,P|410:157|404:236,1,138,2|2,3:2|0:0,0:0:0:0: +328,232,38675,1,0,0:0:0:0: +92,233,38785,5,2,1:2:0:0: +23,233,38894,1,0,0:0:0:0: +92,233,39004,1,0,0:0:0:0: +285,233,39223,2,0,L|430:233,1,138,0|0,3:2|0:0,0:0:0:0: +346,233,39551,1,0,1:2:0:0: +235,233,39661,2,0,L|234:160,1,69,2|2,0:0|0:0,0:0:0:0: +344,164,39880,2,0,L|346:93,1,69,2|2,1:2|0:0,0:0:0:0: +144,95,40099,2,0,L|5:95,1,138,0|2,3:2|0:0,0:0:0:0: +82,95,40427,1,0,0:0:0:0: +315,95,40536,5,2,1:2:0:0: +384,95,40646,1,0,0:0:0:0: +315,95,40755,1,2,0:0:0:0: +121,95,40974,2,0,L|119:234,1,138,2|2,3:2|0:0,0:0:0:0: +195,232,41303,1,0,1:2:0:0: +394,232,41412,1,2,0:0:0:0: +214,232,41631,1,0,1:2:0:0: +144,232,41741,1,0,0:0:0:0: +214,232,41850,1,0,3:2:0:0: +407,232,42069,2,0,L|492:232,1,69,2|2,0:0|0:0,0:0:0:0: +240,232,42288,5,2,1:2:0:0: +170,232,42398,1,0,0:0:0:0: +240,232,42507,1,0,0:0:0:0: +419,232,42726,1,2,3:2:0:0: +129,232,42945,2,0,L|128:161,1,69,2|0,0:0|1:2,0:0:0:0: +238,163,43164,2,0,L|380:164,1,138,2|2,0:0|1:2,0:0:0:0: +299,163,43493,1,0,0:0:0:0: +195,163,43602,1,2,3:2:0:0: +374,163,43821,1,2,0:0:0:0: +376,93,43931,1,0,0:0:0:0: +108,163,44040,5,6,1:2:0:0: +106,93,44150,1,2,0:0:0:0: +209,93,44259,1,0,3:2:0:0: +388,93,44478,1,0,3:2:0:0: +195,93,44697,1,2,1:2:0:0: +484,93,44916,1,8,0:3:0:0: +407,93,45026,1,8,0:3:0:0: +213,93,45244,1,8,0:3:0:0: +316,93,45354,2,0,L|460:94,1,138,2|4,0:0|0:3,0:0:0:0: +103,93,45792,6,0,P|17:149|121:239,1,276,6|0,1:2|0:0,0:0:0:0: +294,241,46449,2,0,L|37:136,1,276,2|2,0:0|0:0,0:0:0:0: +204,136,47106,1,2,0:0:0:0: +38,136,47325,1,2,0:0:0:0: +355,136,47544,6,0,P|438:178|341:272,1,276,6|0,1:2|0:0,0:0:0:0: +173,271,48201,1,0,0:0:0:0: +338,271,48420,2,0,P|355:199|200:122,1,276,2|2,0:0|0:0,0:0:0:0: +369,120,49077,1,2,0:0:0:0: +51,120,49296,6,0,L|49:261,1,138,6|2,1:2|0:0,0:0:0:0: +229,257,49734,2,0,L|371:256,1,138,2|2,0:0|0:0,0:0:0:0: +186,256,50172,2,0,L|47:255,1,138,2|2,0:0|0:0,0:0:0:0: +227,255,50609,1,2,0:0:0:0: +47,255,50828,1,2,0:0:0:0: +347,254,51047,6,0,P|438:243|478:85,1,276,6|0,1:2|0:0,0:0:0:0: +118,84,51923,2,0,P|103:147|121:221,1,138,2|2,3:2|3:2,0:0:0:0: +313,217,52361,1,8,0:3:0:0: +119,217,52580,1,8,0:3:0:0: +436,217,52799,6,0,L|127:184,1,276,2|2,1:2|3:2,0:0:0:0: +452,187,53456,1,2,0:0:0:0: +489,128,53566,1,0,1:2:0:0: +454,68,53675,1,0,0:0:0:0: +274,68,53894,1,2,1:2:0:0: +454,68,54113,2,0,L|301:69,1,138,2|2,3:2|0:0,0:0:0:0: +24,68,54551,6,0,L|306:94,1,276,0|0,1:2|3:2,0:0:0:0: +104,93,55208,1,0,0:0:0:0: +62,93,55317,1,0,1:2:0:0: +104,93,55427,1,2,0:0:0:0: +393,93,55646,2,0,L|266:151,1,138,2|0,1:2|3:2,0:0:0:0: +87,150,56084,1,2,0:0:0:0: +432,116,56303,6,0,P|308:196|181:218,1,276,6|2,1:2|3:2,0:0:0:0: +365,218,56960,1,2,1:2:0:0: +75,218,57179,2,0,L|232:214,1,138,2|2,3:2|1:2,0:0:0:0: +407,214,57617,2,0,L|410:69,1,138,2|2,3:2|0:0,0:0:0:0: +118,76,58055,6,0,L|335:76,2,207,2|2|2,1:2|0:0|0:0,0:0:0:0: +312,76,58931,2,0,P|275:213|34:256,1,414,2|0,0:0|0:0,0:0:0:0: +380,255,59807,6,0,P|404:186|380:128,1,138,6|0,1:2|0:0,0:0:0:0: +290,128,60135,1,2,0:0:0:0: +380,128,60244,2,0,L|382:52,1,69,0|0,3:2|0:0,0:0:0:0: +180,59,60463,2,0,L|96:59,1,69,2|0,0:0|1:2,0:0:0:0: +346,59,60682,6,0,L|346:144,1,69,2|2,0:0|0:0,0:0:0:0: +144,128,60901,1,2,1:2:0:0: +345,128,61011,1,2,0:0:0:0: +441,128,61120,2,0,P|475:194|424:240,1,138,0|2,3:2|0:0,0:0:0:0: +355,236,61449,1,0,0:0:0:0: +121,236,61558,6,0,L|120:164,1,69,2|2,1:2|0:0,0:0:0:0: +321,167,61777,1,2,0:0:0:0: +120,167,61887,1,2,0:0:0:0: +23,167,61996,2,0,L|177:166,1,138,0|2,3:2|0:0,0:0:0:0: +63,166,62325,1,0,1:2:0:0: +296,166,62434,6,0,L|297:95,1,69,2|2,0:0|0:0,0:0:0:0: +199,97,62653,1,0,1:2:0:0: +400,97,62763,1,2,0:0:0:0: +303,97,62872,2,0,P|293:153|354:193,1,138,0|2,3:2|0:0,0:0:0:0: +438,192,63201,1,0,0:0:0:0: +204,192,63310,6,0,P|133:187|94:138,1,138,2|0,1:2|0:0,0:0:0:0: +184,137,63639,1,2,0:0:0:0: +93,137,63748,2,0,L|92:53,1,69,0|0,3:2|0:0,0:0:0:0: +293,68,63967,2,0,L|294:143,1,69,2|0,0:0|1:2,0:0:0:0: +93,137,64186,5,2,0:0:0:0: +293,136,64296,2,0,L|361:136,1,69,2|0,0:0|1:2,0:0:0:0: +160,136,64515,1,2,0:0:0:0: +63,136,64624,2,0,P|29:83|79:30,1,138,0|2,3:2|0:0,0:0:0:0: +154,31,64953,1,0,0:0:0:0: +387,31,65062,6,0,L|319:30,1,69,2|2,1:2|0:0,0:0:0:0: +116,29,65281,1,2,0:0:0:0: +318,29,65390,1,2,0:0:0:0: +415,29,65500,2,0,P|452:91|413:129,1,138,0|2,3:2|0:0,0:0:0:0: +315,129,65828,1,0,1:2:0:0: +79,129,65938,6,0,L|78:59,1,69,2|2,0:0|0:0,0:0:0:0: +175,60,66157,1,0,1:2:0:0: +374,60,66266,1,2,0:0:0:0: +276,60,66376,2,0,L|424:61,1,138,0|2,3:2|0:0,0:0:0:0: +331,60,66704,1,0,0:0:0:0: +60,60,66814,6,0,P|28:123|66:176,1,138,6|0,1:2|0:0,0:0:0:0: +151,173,67142,1,2,0:0:0:0: +61,173,67252,1,0,3:2:0:0: +378,173,67471,5,2,1:2:0:0: +422,111,67580,1,0,0:0:0:0: +381,46,67690,1,0,0:0:0:0: +305,44,67799,1,0,0:0:0:0: +194,44,67909,2,0,L|193:121,1,69,0|0,1:2|0:0,0:0:0:0: +428,112,68128,2,0,L|288:112,1,138,2|2,3:2|0:0,0:0:0:0: +373,112,68456,1,0,0:0:0:0: +137,112,68566,6,0,L|135:183,1,69,2|0,1:2|0:0,0:0:0:0: +245,181,68785,2,0,L|246:258,1,69,2|0,0:0|0:0,0:0:0:0: +44,249,69004,2,0,L|191:248,1,138,2|2,3:2|1:2,0:0:0:0: +98,248,69332,1,0,0:0:0:0: +333,248,69442,6,0,L|335:170,1,69,2|0,1:2|0:0,0:0:0:0: +133,179,69661,1,2,1:2:0:0: +326,179,69880,1,2,3:2:0:0: +133,179,70099,2,0,L|131:251,1,69,2|0,0:0|0:0,0:0:0:0: +398,247,70317,6,0,L|106:250,1,276,6|2,1:2|3:2,0:0:0:0: +468,249,70974,2,0,L|177:250,1,276,6|0,1:2|1:2,0:0:0:0: +483,249,71631,2,0,L|334:249,1,138,2|2,3:2|0:0,0:0:0:0: +26,249,72069,6,0,L|243:249,2,207,6|8|8,1:2|0:3|0:3,0:0:0:0: +344,249,72945,2,0,P|434:201|334:113,1,276,6|0,1:2|3:2,0:0:0:0: +247,111,73493,1,0,3:2:0:0: +338,111,73602,1,0,3:2:0:0: +102,111,73712,1,0,3:2:0:0: +338,111,73821,6,0,P|372:156|334:220,1,138,6|0,1:2|0:0,0:0:0:0: +244,219,74150,1,2,0:0:0:0: +334,219,74259,2,0,L|335:147,1,69,0|0,3:2|0:0,0:0:0:0: +133,150,74478,2,0,L|131:71,1,69,2|0,0:0|1:2,0:0:0:0: +366,81,74697,6,0,L|367:158,1,69,2|2,0:0|0:0,0:0:0:0: +165,149,74916,1,2,1:2:0:0: +366,149,75026,1,2,0:0:0:0: +462,149,75135,2,0,L|296:149,1,138,0|2,3:2|0:0,0:0:0:0: +407,149,75463,1,0,0:0:0:0: +171,149,75573,6,0,L|169:233,1,69,2|2,1:2|0:0,0:0:0:0: +370,217,75792,1,2,0:0:0:0: +170,217,75901,1,2,0:0:0:0: +72,217,76011,2,0,P|46:151|98:97,1,138,0|2,3:2|0:0,0:0:0:0: +179,102,76339,1,0,1:2:0:0: +414,102,76449,6,0,L|491:102,1,69,2|2,0:0|0:0,0:0:0:0: +385,102,76668,1,0,1:2:0:0: +185,102,76777,1,2,0:0:0:0: +282,102,76887,2,0,L|442:101,1,138,0|2,3:2|0:0,0:0:0:0: +336,101,77215,1,0,0:0:0:0: +100,101,77325,6,0,P|75:169|105:227,1,138,2|0,1:2|0:0,0:0:0:0: +192,224,77653,1,2,0:0:0:0: +102,224,77763,2,0,L|100:301,1,69,0|0,3:2|0:0,0:0:0:0: +301,292,77982,2,0,L|394:292,1,69,2|0,0:0|1:2,0:0:0:0: +134,292,78201,6,0,L|133:221,1,69,2|2,0:0|0:0,0:0:0:0: +334,223,78420,1,2,1:2:0:0: +135,223,78529,1,2,0:0:0:0: +37,223,78639,2,0,P|21:160|69:106,1,138,0|2,3:2|0:0,0:0:0:0: +147,107,78967,1,0,0:0:0:0: +382,107,79077,6,0,L|384:175,1,69,2|0,1:2|0:0,0:0:0:0: +273,175,79296,2,0,L|271:243,1,69,2|0,1:2|0:0,0:0:0:0: +472,243,79515,2,0,L|474:315,1,69,2|0,3:2|0:0,0:0:0:0: +203,311,79734,6,0,L|132:312,1,69,6|0,1:2|0:0,0:0:0:0: +244,311,79953,2,0,L|317:311,1,69,2|0,0:0|0:0,0:0:0:0: +111,311,80172,2,0,L|108:242,1,69,8|0,2:3|0:0,0:0:0:0: +307,242,80390,2,0,L|385:242,1,69,8|0,2:3|0:0,0:0:0:0: +140,242,80609,2,0,L|69:242,1,69,4|0,2:3|0:0,0:0:0:0: +341,242,80828,6,0,L|495:242,1,138,6|0,1:2|0:0,0:0:0:0: +388,242,81157,1,2,0:0:0:0: +476,242,81266,1,0,3:2:0:0: +161,242,81485,5,2,1:2:0:0: +124,175,81595,1,0,0:0:0:0: +166,112,81704,1,0,0:0:0:0: +242,106,81814,1,0,0:0:0:0: +351,106,81923,2,0,L|352:37,1,69,0|0,1:2|0:0,0:0:0:0: +150,37,82142,1,2,3:2:0:0: +74,50,82252,1,0,0:0:0:0: +84,124,82361,1,0,0:0:0:0: +166,131,82471,1,0,0:0:0:0: +399,131,82580,5,2,1:2:0:0: +442,193,82690,1,0,0:0:0:0: +399,255,82799,1,0,0:0:0:0: +316,261,82909,1,2,0:0:0:0: +206,261,83018,2,0,L|204:185,1,69,0|0,3:2|0:0,0:0:0:0: +315,192,83237,2,0,L|316:121,1,69,2|0,1:2|0:0,0:0:0:0: +80,123,83456,6,0,L|78:47,1,69,2|0,1:2|0:0,0:0:0:0: +182,54,83675,1,2,1:2:0:0: +375,54,83894,1,2,3:2:0:0: +57,54,84113,1,2,0:0:0:0: +133,54,84223,1,0,0:0:0:0: +366,54,84332,5,2,1:2:0:0: +405,119,84442,1,0,0:0:0:0: +361,180,84551,1,0,0:0:0:0: +284,180,84661,1,0,0:0:0:0: +174,180,84770,2,0,L|172:256,1,69,0|0,3:2|0:0,0:0:0:0: +442,248,84989,5,6,1:2:0:0: +358,248,85099,1,0,0:0:0:0: +321,183,85208,1,0,0:0:0:0: +365,123,85317,1,0,0:0:0:0: +475,123,85427,2,0,L|476:48,1,69,0|0,1:2|0:0,0:0:0:0: +274,54,85646,2,0,L|273:131,1,69,0|0,3:2|0:0,0:0:0:0: +363,122,85865,1,0,0:0:0:0: +273,122,85974,1,0,0:0:0:0: +71,122,86084,6,0,L|70:210,1,69,0|0,1:2|0:0,0:0:0:0: +305,190,86303,2,0,L|305:270,1,69,8|0,0:3|0:0,0:0:0:0: +103,259,86522,1,0,3:2:0:0: +305,259,86631,2,0,L|388:258,1,69,8|2,0:3|0:0,0:0:0:0: +55,258,86960,2,0,P|215:211|49:153,1,455.400013897705,2|0,1:2|0:0,0:0:0:0: +398,117,87836,5,6,1:2:0:0: +77,106,101412,5,0,3:2:0:0: +435,106,101850,6,0,P|450:162|434:240,1,138,2|0,1:2|0:0,0:0:0:0: +240,239,102288,2,0,L|99:240,1,138,0|0,1:2|0:0,0:0:0:0: +296,239,102726,2,0,L|437:238,1,138,2|0,1:2|0:0,0:0:0:0: +322,238,103055,2,0,L|243:238,1,69,2|0,0:0|1:2,0:0:0:0: +433,238,103383,1,2,0:0:0:0: +145,242,103602,5,2,1:2:0:0: +228,242,103712,1,0,0:0:0:0: +283,242,103821,1,2,0:0:0:0: +89,242,104040,2,0,L|88:104,1,138,2|2,1:2|0:0,0:0:0:0: +268,104,104478,1,2,1:2:0:0: +88,104,104697,1,2,0:0:0:0: +281,104,104916,2,0,L|426:105,1,138,2|0,1:2|0:0,0:0:0:0: +129,104,105354,5,2,1:2:0:0: +211,104,105463,1,0,0:0:0:0: +266,104,105573,1,0,0:0:0:0: +72,104,105792,2,0,L|71:255,1,138,0|0,1:2|0:0,0:0:0:0: +265,241,106230,2,0,L|117:242,1,138,2|2,1:2|0:0,0:0:0:0: +237,241,106558,2,0,L|307:241,1,69,2|0,0:0|1:2,0:0:0:0: +126,240,106887,1,2,0:0:0:0: +415,240,107106,5,2,1:2:0:0: +332,240,107215,1,0,0:0:0:0: +276,240,107325,1,2,0:0:0:0: +469,240,107544,2,0,L|470:100,1,138,2|2,1:2|0:0,0:0:0:0: +289,102,107982,1,2,1:2:0:0: +469,102,108201,1,2,0:0:0:0: +275,102,108420,2,0,L|138:102,1,138,2|0,1:2|0:0,0:0:0:0: +428,102,108858,5,2,1:2:0:0: +345,102,108967,1,0,0:0:0:0: +289,102,109077,1,0,0:0:0:0: +482,102,109296,2,0,L|484:242,1,138,0|0,1:2|0:0,0:0:0:0: +291,239,109734,2,0,L|429:240,1,138,2|0,1:2|0:0,0:0:0:0: +318,239,110062,2,0,L|241:238,1,69,2|0,0:0|1:2,0:0:0:0: +428,239,110390,1,2,0:0:0:0: +138,239,110609,5,2,1:2:0:0: +215,239,110719,1,0,0:0:0:0: +277,239,110828,1,2,0:0:0:0: +83,239,111047,2,0,L|229:239,1,138,2|2,1:2|0:0,0:0:0:0: +26,239,111485,2,0,L|25:102,1,138,2|0,1:2|0:0,0:0:0:0: +205,101,111923,1,0,1:2:0:0: +25,101,112142,1,2,0:0:0:0: +314,101,112361,5,2,1:2:0:0: +230,101,112471,1,2,0:0:0:0: +314,101,112580,2,0,P|399:137|304:230,1,276,2|2,0:0|0:0,0:0:0:0: +109,229,113237,2,0,P|23:186|123:101,1,276,2|0,0:0|0:0,0:0:0:0: +482,100,114113,5,0,1:2:0:0: +288,100,114332,1,8,0:3:0:0: +482,100,114551,2,0,L|324:100,1,138,8|0,0:3|0:0,0:0:0:0: +149,100,114989,2,0,L|292:100,1,138,4|0,0:3|0:0,0:0:0:0: +397,100,115317,2,0,L|310:101,1,69,4|0,0:3|1:2,0:0:0:0: +133,100,115646,2,0,L|132:176,1,69,0|0,3:2|0:0,0:0:0:0: +367,168,115865,5,2,1:2:0:0: +284,168,115974,1,0,0:0:0:0: +228,168,116084,1,0,0:0:0:0: +421,168,116303,2,0,L|423:308,1,138,0|0,3:2|0:0,0:0:0:0: +346,305,116631,1,0,1:2:0:0: +235,305,116741,2,0,L|383:306,1,138,2|2,0:0|1:2,0:0:0:0: +296,305,117069,1,2,0:0:0:0: +94,305,117179,1,0,3:2:0:0: +273,305,117398,2,0,L|346:306,1,69,2|0,0:0|0:0,0:0:0:0: +129,304,117617,5,2,1:2:0:0: +60,304,117726,1,0,0:0:0:0: +131,304,117836,1,0,0:0:0:0: +324,304,118055,2,0,L|177:304,1,138,2|2,3:2|0:0,0:0:0:0: +262,304,118383,1,0,1:2:0:0: +372,304,118493,2,0,P|443:286|477:233,1,138,2|2,0:0|1:2,0:0:0:0: +400,234,118821,1,0,0:0:0:0: +198,234,118931,1,2,3:2:0:0: +391,234,119150,2,0,L|392:152,1,69,0|0,0:0|0:0,0:0:0:0: +156,165,119369,5,2,1:2:0:0: +238,165,119478,1,0,0:0:0:0: +293,165,119588,1,0,0:0:0:0: +99,165,119807,2,0,L|97:26,1,138,0|0,3:2|0:0,0:0:0:0: +174,27,120135,1,0,1:2:0:0: +283,27,120244,1,2,0:0:0:0: +333,79,120354,1,2,0:0:0:0: +283,27,120463,1,2,1:2:0:0: +185,27,120573,1,2,0:0:0:0: +384,27,120682,2,0,P|442:41|483:113,1,138,0|2,3:2|0:0,0:0:0:0: +412,104,121011,1,0,0:0:0:0: +178,104,121120,5,2,1:2:0:0: +108,104,121230,1,0,0:0:0:0: +178,104,121339,1,0,0:0:0:0: +371,104,121558,2,0,L|224:104,1,138,2|0,3:2|0:0,0:0:0:0: +309,104,121887,1,0,1:2:0:0: +418,104,121996,2,0,P|446:171|408:227,1,138,2|2,0:0|1:2,0:0:0:0: +337,222,122325,1,0,0:0:0:0: +137,222,122434,2,0,P|64:206|23:153,1,138,2|0,3:2|0:0,0:0:0:0: +102,159,122763,1,0,0:0:0:0: +335,159,122872,5,2,1:2:0:0: +251,159,122982,1,0,0:0:0:0: +196,159,123091,1,0,0:0:0:0: +389,159,123310,2,0,P|406:239|386:293,1,138,0|0,3:2|0:0,0:0:0:0: +312,290,123639,1,0,1:2:0:0: +202,290,123748,2,0,P|128:246|123:199,1,138,2|2,0:0|1:2,0:0:0:0: +200,162,124077,1,2,0:0:0:0: +399,161,124186,1,0,3:2:0:0: +219,92,124405,2,0,L|148:92,1,69,2|0,0:0|0:0,0:0:0:0: +386,227,124624,5,2,1:2:0:0: +455,227,124734,1,0,0:0:0:0: +386,227,124843,1,2,0:0:0:0: +192,227,125062,2,0,P|106:213|67:181,1,138,2|2,3:2|0:0,0:0:0:0: +144,182,125390,1,0,1:2:0:0: +345,182,125500,2,0,P|431:168|470:136,1,138,2|0,0:0|1:2,0:0:0:0: +393,137,125828,1,0,0:0:0:0: +282,137,125938,1,0,3:2:0:0: +475,137,126157,2,0,L|476:213,1,69,2|0,0:0|0:0,0:0:0:0: +240,205,126376,5,2,1:2:0:0: +322,205,126485,1,0,0:0:0:0: +377,205,126595,1,0,0:0:0:0: +183,205,126814,1,2,3:2:0:0: +472,205,127033,1,2,0:0:0:0: +389,205,127142,1,0,1:2:0:0: +333,205,127252,1,0,0:0:0:0: +153,205,127471,2,0,L|152:131,1,69,2|0,1:2|0:0,0:0:0:0: +256,136,127690,1,2,3:2:0:0: +76,136,127909,1,2,0:0:0:0: +421,136,128128,5,6,1:2:0:0: +423,67,128237,1,2,0:0:0:0: +319,67,128347,1,2,3:2:0:0: +139,67,128566,1,2,3:2:0:0: +332,67,128785,1,2,1:2:0:0: +42,67,129004,1,8,0:3:0:0: +111,67,129113,1,8,0:3:0:0: +304,67,129332,2,0,L|72:67,1,207,8|4,0:3|0:3,0:0:0:0: +408,67,129880,6,0,P|490:129|379:199,1,276,6|0,1:2|0:0,0:0:0:0: +188,200,130536,2,0,L|483:200,1,276,2|2,0:0|0:0,0:0:0:0: +283,200,131193,1,2,0:0:0:0: +463,200,131412,1,2,0:0:0:0: +145,200,131631,6,0,P|59:138|164:60,1,276,6|0,1:2|0:0,0:0:0:0: +342,59,132288,1,0,0:0:0:0: +148,59,132507,2,0,L|147:214,1,138,2|2,0:0|0:0,0:0:0:0: +327,196,132945,1,2,0:0:0:0: +147,196,133164,1,2,0:0:0:0: +464,196,133383,6,0,P|469:249|351:316,1,207,6|0,1:2|0:0,0:0:0:0: +240,316,133821,2,0,P|354:311|391:173,1,276,2|2,0:0|0:0,0:0:0:0: +196,172,134478,2,0,L|197:33,1,138,2|2,0:0|0:0,0:0:0:0: +391,34,134916,1,2,0:0:0:0: +73,34,135135,6,0,B|188:112|188:112|68:30,1,276,6|0,1:2|0:0,0:0:0:0: +434,34,136011,2,0,L|435:174,1,138,2|2,3:2|3:2,0:0:0:0: +227,171,136449,1,8,0:3:0:0: +434,171,136668,1,8,0:3:0:0: +116,171,136887,6,0,L|412:171,1,276,2|2,1:2|3:2,0:0:0:0: +100,171,137544,1,2,0:0:0:0: +182,171,137653,1,0,1:2:0:0: +242,171,137763,1,0,0:0:0:0: +62,171,137982,1,2,1:2:0:0: +241,171,138201,2,0,L|88:169,1,138,2|2,3:2|0:0,0:0:0:0: +421,169,138639,6,0,L|128:168,1,276,0|0,1:2|3:2,0:0:0:0: +339,168,139296,2,0,L|340:90,1,69,0|0,0:0|1:2,0:0:0:0: +235,99,139515,1,2,0:0:0:0: +55,99,139734,1,2,1:2:0:0: +344,99,139953,2,0,L|489:98,1,138,0|2,3:2|0:0,0:0:0:0: +136,98,140390,6,0,L|135:242,1,138,6|2,1:2|0:0,0:0:0:0: +328,235,140828,1,2,3:2:0:0: +135,235,141047,1,2,3:2:0:0: +342,235,141266,1,2,3:2:0:0: +493,235,141485,1,2,3:2:0:0: +299,235,141704,1,2,3:2:0:0: +91,235,141923,1,2,3:2:0:0: +380,235,142142,6,0,L|155:232,2,207,2|2|2,1:2|0:0|0:0,0:0:0:0: +185,235,143018,2,0,P|347:232|428:19,1,414,2|0,0:0|0:0,0:0:0:0: +82,21,143894,6,0,P|50:85|84:135,1,138,6|0,1:2|0:0,0:0:0:0: +174,134,144223,1,2,0:0:0:0: +84,134,144332,2,0,L|83:208,1,69,0|0,3:2|0:0,0:0:0:0: +284,202,144551,2,0,L|368:202,1,69,2|0,0:0|1:2,0:0:0:0: +117,202,144770,6,0,L|46:202,1,69,2|2,0:0|0:0,0:0:0:0: +249,202,144989,1,2,1:2:0:0: +48,202,145099,1,2,0:0:0:0: +144,202,145208,2,0,P|180:157|139:100,1,138,0|2,3:2|0:0,0:0:0:0: +55,99,145536,1,0,0:0:0:0: +290,99,145646,6,0,L|370:98,1,69,2|2,1:2|0:0,0:0:0:0: +157,98,145865,1,2,0:0:0:0: +356,98,145974,1,2,0:0:0:0: +453,98,146084,2,0,L|277:98,1,138,0|2,3:2|0:0,0:0:0:0: +412,98,146412,1,0,1:2:0:0: +176,98,146522,5,2,0:0:0:0: +272,98,146631,2,0,L|273:174,1,69,2|0,0:0|1:2,0:0:0:0: +71,166,146850,1,2,0:0:0:0: +168,166,146960,2,0,L|27:166,1,138,0|2,3:2|0:0,0:0:0:0: +113,166,147288,1,0,0:0:0:0: +348,166,147398,6,0,P|385:115|346:62,1,138,2|0,1:2|0:0,0:0:0:0: +255,61,147726,1,2,0:0:0:0: +345,61,147836,2,0,L|347:129,1,69,0|0,3:2|0:0,0:0:0:0: +145,129,148055,1,2,0:0:0:0: +76,129,148164,1,0,1:2:0:0: +280,97,148274,6,0,L|360:97,1,69,2|2,0:0|0:0,0:0:0:0: +147,97,148493,1,2,1:2:0:0: +346,97,148602,1,2,0:0:0:0: +248,97,148712,2,0,L|103:97,1,138,0|2,3:2|0:0,0:0:0:0: +193,97,149040,1,0,0:0:0:0: +428,97,149150,6,0,P|459:168|420:215,1,138,2|0,1:2|0:0,0:0:0:0: +226,211,149478,1,2,0:0:0:0: +323,211,149588,2,0,L|466:211,1,138,0|2,3:2|0:0,0:0:0:0: +377,211,149916,1,0,1:2:0:0: +141,211,150026,5,2,0:0:0:0: +237,211,150135,2,0,L|239:139,1,69,2|0,0:0|1:2,0:0:0:0: +37,142,150354,1,2,0:0:0:0: +133,142,150463,2,0,P|166:75|119:40,1,138,0|2,3:2|0:0,0:0:0:0: +42,40,150792,1,0,0:0:0:0: +309,40,150901,6,0,L|465:40,1,138,6|0,1:2|0:0,0:0:0:0: +356,40,151230,1,2,0:0:0:0: +445,40,151339,1,0,3:2:0:0: +127,40,151558,5,2,1:2:0:0: +203,45,151668,1,0,0:0:0:0: +239,111,151777,1,0,0:0:0:0: +196,174,151887,1,0,0:0:0:0: +86,174,151996,2,0,L|84:252,1,69,0|0,1:2|0:0,0:0:0:0: +285,242,152215,2,0,L|144:241,1,138,2|2,3:2|0:0,0:0:0:0: +230,241,152544,1,0,0:0:0:0: +463,241,152653,6,0,L|392:240,1,69,0|0,1:2|0:0,0:0:0:0: +284,242,152872,2,0,L|282:164,1,69,2|0,0:0|0:0,0:0:0:0: +483,173,153091,2,0,L|336:172,1,138,2|2,3:2|1:2,0:0:0:0: +428,172,153420,1,0,0:0:0:0: +227,171,153529,6,0,L|226:93,1,69,2|0,1:2|0:0,0:0:0:0: +323,102,153748,1,2,1:2:0:0: +33,102,153967,2,0,L|30:248,1,138,2|2,3:2|0:0,0:0:0:0: +114,239,154296,1,0,0:0:0:0: +381,239,154405,6,0,L|99:237,1,276,6|2,1:2|3:2,0:0:0:0: +451,237,155062,2,0,P|488:148|355:78,1,276,6|0,1:2|1:2,0:0:0:0: +22,80,155719,2,0,L|177:81,1,138,2|2,3:2|0:0,0:0:0:0: +478,80,156157,6,0,L|268:81,2,207,6|8|8,1:2|0:3|0:3,0:0:0:0: +159,80,157033,2,0,P|66:140|166:218,1,276,6|0,1:2|3:2,0:0:0:0: +254,218,157580,1,0,3:2:0:0: +163,218,157690,1,0,3:2:0:0: +396,218,157799,1,0,3:2:0:0: +163,218,157909,6,0,P|132:155|167:100,1,138,6|0,1:2|0:0,0:0:0:0: +255,100,158237,1,2,0:0:0:0: +164,100,158347,2,0,L|162:174,1,69,0|0,3:2|0:0,0:0:0:0: +363,168,158566,2,0,L|364:243,1,69,2|0,0:0|1:2,0:0:0:0: +128,236,158785,6,0,L|208:237,1,69,2|2,0:0|0:0,0:0:0:0: +398,236,159004,1,2,1:2:0:0: +198,236,159113,1,2,0:0:0:0: +100,236,159223,2,0,P|73:178|105:116,1,138,0|2,3:2|0:0,0:0:0:0: +187,116,159551,1,0,0:0:0:0: +422,116,159661,6,0,L|352:115,1,69,2|0,1:2|0:0,0:0:0:0: +151,115,159880,1,2,0:0:0:0: +350,115,159989,1,2,0:0:0:0: +254,115,160099,2,0,L|426:115,1,138,0|2,3:2|0:0,0:0:0:0: +296,115,160427,1,0,1:2:0:0: +62,115,160536,6,0,L|61:188,1,69,2|0,0:0|0:0,0:0:0:0: +171,183,160755,2,0,L|250:183,1,69,2|0,1:2|0:0,0:0:0:0: +441,183,160974,2,0,P|470:243|434:305,1,138,2|2,3:2|0:0,0:0:0:0: +354,301,161303,1,0,0:0:0:0: +120,301,161412,6,0,L|271:301,1,138,2|0,1:2|0:0,0:0:0:0: +167,301,161741,1,2,0:0:0:0: +256,301,161850,2,0,L|257:222,1,69,0|0,3:2|0:0,0:0:0:0: +55,232,162069,2,0,L|53:155,1,69,2|0,0:0|1:2,0:0:0:0: +288,163,162288,6,0,L|363:163,1,69,2|0,0:0|0:0,0:0:0:0: +155,163,162507,1,2,1:2:0:0: +356,163,162617,1,2,0:0:0:0: +452,163,162726,2,0,P|475:235|443:293,1,138,0|2,3:2|0:0,0:0:0:0: +364,287,163055,1,0,0:0:0:0: +130,287,163164,6,0,L|128:209,1,69,2|0,1:2|0:0,0:0:0:0: +239,218,163383,2,0,L|241:146,1,69,2|0,1:2|0:0,0:0:0:0: +39,149,163602,2,0,L|120:149,1,69,2|0,3:2|0:0,0:0:0:0: +378,149,163821,6,0,L|379:81,1,69,6|0,1:2|0:0,0:0:0:0: +268,80,164040,2,0,L|172:80,1,69,2|0,0:0|0:0,0:0:0:0: +400,80,164259,2,0,L|402:153,1,69,8|0,2:3|0:0,0:0:0:0: +200,148,164478,2,0,L|112:148,1,69,8|0,2:3|0:0,0:0:0:0: +366,148,164697,2,0,L|453:149,1,69,4|0,2:3|0:0,0:0:0:0: +164,148,164916,6,0,L|25:149,1,138,6|0,1:2|0:0,0:0:0:0: +116,148,165244,1,2,0:0:0:0: +27,148,165354,1,0,3:2:0:0: +344,148,165573,5,2,1:2:0:0: +381,213,165682,1,0,0:0:0:0: +339,277,165792,1,0,0:0:0:0: +263,277,165901,1,0,0:0:0:0: +152,277,166011,2,0,L|151:353,1,69,0|0,1:2|0:0,0:0:0:0: +352,345,166230,1,2,3:2:0:0: +427,345,166339,1,0,0:0:0:0: +464,278,166449,1,0,0:0:0:0: +425,212,166558,1,0,0:0:0:0: +189,212,166668,5,2,1:2:0:0: +116,189,166777,1,0,0:0:0:0: +125,113,166887,1,0,0:0:0:0: +199,102,166996,1,2,0:0:0:0: +309,102,167106,2,0,L|311:180,1,69,0|0,3:2|0:0,0:0:0:0: +199,170,167325,2,0,L|197:242,1,69,2|0,1:2|0:0,0:0:0:0: +398,238,167544,6,0,L|483:238,1,69,2|0,1:2|0:0,0:0:0:0: +356,238,167763,2,0,L|283:237,1,69,2|0,1:2|0:0,0:0:0:0: +85,237,167982,2,0,L|11:237,1,69,2|0,3:2|0:0,0:0:0:0: +126,237,168201,2,0,L|206:237,1,69,2|0,0:0|0:0,0:0:0:0: +430,237,168420,6,0,P|487:176|366:86,1,276,2|0,1:2|3:2,0:0:0:0: +174,89,169077,1,2,1:2:0:0: +99,98,169186,1,0,0:0:0:0: +67,167,169296,1,0,0:0:0:0: +101,234,169405,1,0,0:0:0:0: +176,243,169515,1,0,1:2:0:0: +465,243,169734,2,0,L|467:104,1,138,0|0,3:2|1:2,0:0:0:0: +390,105,170062,1,0,0:0:0:0: +154,105,170172,6,0,L|367:106,1,207,2|2,1:2|0:0,0:0:0:0: +127,105,170609,2,0,P|104:181|130:237,1,138,0|2,3:2|0:0,0:0:0:0: +202,232,170938,1,2,0:0:0:0: +401,232,171047,2,0,P|176:204|125:49,1,414,2|0,1:2|0:0,0:0:0:0: +416,48,171923,5,2,1:2:0:0: +85,274,178712,5,0,3:2:0:0: +402,274,178931,6,0,P|428:204|398:150,1,138,2|2,1:2|0:0,0:0:0:0: +323,151,179259,1,2,0:0:0:0: +212,151,179369,2,0,P|134:143|92:99,1,138,2|2,1:2|0:0,0:0:0:0: +170,102,179697,1,2,0:0:0:0: +280,102,179807,2,0,L|429:102,1,138,2|2,1:2|0:0,0:0:0:0: +307,102,180135,1,2,0:0:0:0: +238,102,180244,1,0,1:2:0:0: +307,102,180354,1,2,0:0:0:0: +417,102,180463,2,0,L|418:179,1,69,2|0,0:0|0:0,0:0:0:0: +216,159,180682,5,2,1:2:0:0: +313,159,180792,1,2,0:0:0:0: +381,159,180901,1,2,0:0:0:0: +313,159,181011,1,2,0:0:0:0: +203,159,181120,1,2,1:2:0:0: +133,159,181230,1,2,0:0:0:0: +203,159,181339,1,2,0:0:0:0: +396,159,181558,2,0,P|422:224|388:292,1,138,2|2,1:2|0:0,0:0:0:0: +320,283,181887,1,2,0:0:0:0: +210,283,181996,2,0,L|65:282,1,138,0|0,1:2|0:0,0:0:0:0: +148,282,182325,1,0,0:0:0:0: +347,282,182434,5,2,1:2:0:0: +416,282,182544,1,2,0:0:0:0: +347,282,182653,1,2,0:0:0:0: +154,282,182872,1,2,1:2:0:0: +85,282,182982,1,2,0:0:0:0: +154,282,183091,1,2,0:0:0:0: +347,282,183310,2,0,P|373:217|342:159,1,138,2|2,1:2|0:0,0:0:0:0: +231,160,183639,1,2,0:0:0:0: +162,160,183748,1,0,1:2:0:0: +231,160,183858,1,2,0:0:0:0: +343,160,183967,2,0,L|345:87,1,69,2|0,0:0|0:0,0:0:0:0: +143,91,184186,5,2,1:2:0:0: +323,91,184405,1,8,0:3:0:0: +143,91,184624,2,0,P|118:168|149:218,1,138,8|2,0:3|0:0,0:0:0:0: +221,213,184953,1,2,0:0:0:0: +421,270,185062,2,0,L|206:271,2,207,4|4|0,0:3|0:3|3:2,0:0:0:0: +102,270,185938,6,0,P|72:198|110:155,1,138,2|2,1:2|0:0,0:0:0:0: +181,157,186266,1,2,0:0:0:0: +291,157,186376,2,0,L|432:157,1,138,2|2,3:2|0:0,0:0:0:0: +352,157,186704,1,2,1:2:0:0: +150,157,186814,2,0,P|128:221|149:291,1,138,2|2,0:0|1:2,0:0:0:0: +257,286,187142,1,2,0:0:0:0: +325,227,187252,1,0,3:2:0:0: +253,155,187361,1,2,0:0:0:0: +141,155,187471,2,0,L|52:155,1,69,2|0,0:0|0:0,0:0:0:0: +307,155,187690,6,0,P|325:214|306:292,1,138,0|0,1:2|0:0,0:0:0:0: +113,292,188128,2,0,P|100:235|115:156,1,138,0|0,3:2|0:0,0:0:0:0: +190,157,188456,1,0,1:2:0:0: +391,157,188566,1,0,0:0:0:0: +211,157,188785,1,0,1:2:0:0: +390,157,189004,2,0,L|392:13,1,138,0|0,3:2|0:0,0:0:0:0: +73,19,189442,5,2,1:2:0:0: +39,86,189551,1,2,0:0:0:0: +76,152,189661,1,2,0:0:0:0: +158,152,189770,1,2,0:0:0:0: +268,152,189880,2,0,L|114:153,1,138,2|2,3:2|0:0,0:0:0:0: +213,152,190208,1,2,1:2:0:0: +412,152,190317,2,0,P|430:226|409:286,1,138,2|2,0:0|1:2,0:0:0:0: +320,282,190646,1,2,0:0:0:0: +230,282,190755,1,0,3:2:0:0: +409,282,190974,1,2,0:0:0:0: +91,282,191193,6,0,P|23:224|137:141,1,276,0|0,1:2|3:2,0:0:0:0: +344,141,191850,1,0,1:2:0:0: +427,141,191960,1,0,1:2:0:0: +344,141,192069,1,2,3:2:0:0: +138,141,192288,1,0,1:2:0:0: +427,141,192507,2,0,L|428:288,1,138,2|0,3:2|0:0,0:0:0:0: +81,278,192945,6,0,L|266:278,1,179.39999178772,6|2,1:2|1:1,0:0:0:0: +81,278,193383,2,0,L|388:279,1,289.799991156006,2|2,1:1|1:1,0:0:0:0: +190,278,193821,2,0,L|381:278,1,179.39999178772,2|2,1:2|1:1,0:0:0:0: +78,278,194259,2,0,L|401:277,1,289.799991156006,2|2,1:1|1:1,0:0:0:0: +76,277,194697,6,0,L|74:140,1,138,2|2,1:2|1:1,0:0:0:0: +365,139,195135,2,0,L|59:138,1,289.799991156006,2|2,1:1|1:1,0:0:0:0: +394,138,195573,2,0,L|395:278,1,138,2|2,1:2|1:1,0:0:0:0: +105,276,196011,2,0,L|411:277,1,289.799991156006,2|2,1:1|1:1,0:0:0:0: +75,276,196449,5,2,3:2:0:0: +422,276,196668,2,0,L|108:275,2,289.799991156006,6|6|6,1:1|1:1|1:1,0:0:0:0: +75,276,197325,2,0,L|389:275,2,289.799991156006,6|6|6,1:1|1:1|1:1,0:0:0:0: +395,276,197982,1,6,1:1:0:0: +47,276,198201,6,0,L|349:277,1,289.799991156006,6|6,1:1|1:1,0:0:0:0: +142,276,198639,2,0,L|342:277,1,193.199994104004,14|14,2:3|2:3,0:0:0:0: +26,277,199077,1,14,2:3:0:0: +371,277,199296,2,0,P|254:202|378:86,1,358.79998357544,6|0,1:2|0:0,0:0:0:0: +56,81,199953,6,0,L|297:80,2,207,6|2|2,1:2|0:0|0:0,0:0:0:0: +249,81,200828,2,0,L|251:169,1,69,2|2,0:0|0:0,0:0:0:0: +160,149,201047,1,2,0:0:0:0: +250,149,201157,1,2,0:0:0:0: +50,149,201266,1,0,3:2:0:0: +139,149,201376,1,0,3:2:0:0: +50,149,201485,1,2,3:2:0:0: +285,149,201595,1,0,3:2:0:0: +50,149,201704,6,0,L|48:228,1,69,6|2,1:2|0:0,0:0:0:0: +249,217,201923,1,2,0:0:0:0: +48,217,202033,1,2,0:0:0:0: +141,217,202142,2,0,P|172:281|134:338,1,138,0|2,3:2|0:0,0:0:0:0: +45,333,202471,1,0,1:2:0:0: +278,333,202580,5,2,0:0:0:0: +180,333,202690,2,0,L|179:262,1,69,2|0,0:0|1:2,0:0:0:0: +380,264,202909,1,2,0:0:0:0: +283,264,203018,2,0,L|457:265,1,138,0|2,3:2|0:0,0:0:0:0: +337,264,203347,1,0,0:0:0:0: +103,264,203456,6,0,P|72:200|117:155,1,138,0|0,1:2|0:0,0:0:0:0: +202,156,203785,1,2,0:0:0:0: +111,156,203894,2,0,L|109:75,1,69,0|0,3:2|0:0,0:0:0:0: +310,87,204113,2,0,L|399:86,1,69,2|0,0:0|1:2,0:0:0:0: +177,86,204332,5,2,0:0:0:0: +378,86,204442,2,0,L|379:160,1,69,2|0,0:0|1:2,0:0:0:0: +177,154,204661,1,2,0:0:0:0: +80,154,204770,2,0,P|55:217|80:282,1,138,0|2,3:2|0:0,0:0:0:0: +162,280,205099,1,0,0:0:0:0: +395,280,205208,6,0,L|312:280,1,69,2|2,1:2|0:0,0:0:0:0: +124,280,205427,1,2,0:0:0:0: +323,280,205536,1,2,0:0:0:0: +420,280,205646,2,0,L|252:279,1,138,0|2,3:2|0:0,0:0:0:0: +379,279,205974,1,0,1:2:0:0: +143,279,206084,6,0,L|70:281,1,69,2|2,0:0|0:0,0:0:0:0: +171,280,206303,1,0,1:2:0:0: +370,280,206412,1,2,0:0:0:0: +467,280,206522,2,0,P|494:213|463:160,1,138,0|2,3:2|0:0,0:0:0:0: +380,160,206850,1,0,0:0:0:0: +109,160,206960,6,0,L|259:160,1,138,6|0,1:2|0:0,0:0:0:0: +156,160,207288,1,2,0:0:0:0: +65,160,207398,1,0,3:2:0:0: +382,160,207617,5,2,1:2:0:0: +420,224,207726,1,0,0:0:0:0: +378,288,207836,1,0,0:0:0:0: +302,288,207945,1,0,0:0:0:0: +191,288,208055,2,0,L|190:212,1,69,0|0,1:2|0:0,0:0:0:0: +391,219,208274,2,0,P|417:155|379:101,1,138,2|2,3:2|0:0,0:0:0:0: +298,102,208602,1,0,0:0:0:0: +62,102,208712,6,0,L|61:180,1,69,0|0,1:2|0:0,0:0:0:0: +172,170,208931,2,0,L|245:169,1,69,2|0,0:0|0:0,0:0:0:0: +442,169,209150,2,0,P|466:237|434:297,1,138,2|2,3:2|1:2,0:0:0:0: +355,292,209478,1,0,0:0:0:0: +119,292,209588,6,0,L|116:218,1,69,2|0,0:0|0:0,0:0:0:0: +220,223,209807,1,2,1:2:0:0: +413,223,210026,1,2,3:2:0:0: +124,223,210244,2,0,L|48:223,1,69,2|2,0:0|0:0,0:0:0:0: +325,223,210463,6,0,P|407:220|484:62,1,276,6|2,1:2|3:2,0:0:0:0: +165,63,211120,2,0,L|469:62,1,276,6|0,1:2|1:2,0:0:0:0: +149,62,211777,2,0,L|8:61,1,138,2|2,3:2|0:0,0:0:0:0: +357,61,212215,6,0,L|142:61,2,207,6|8|8,1:2|0:3|0:3,0:0:0:0: +65,61,213091,2,0,L|375:61,1,276,6|0,1:2|3:2,0:0:0:0: +250,61,213639,1,0,3:2:0:0: +339,61,213748,1,0,3:2:0:0: +103,61,213858,1,0,3:2:0:0: +339,61,213967,6,0,P|366:130|332:184,1,138,6|0,1:2|0:0,0:0:0:0: +245,180,214296,1,2,0:0:0:0: +334,180,214405,2,0,L|336:259,1,69,0|0,3:2|0:0,0:0:0:0: +134,248,214624,2,0,L|47:249,1,69,2|0,0:0|1:2,0:0:0:0: +300,248,214843,6,0,L|301:171,1,69,2|0,0:0|0:0,0:0:0:0: +99,179,215062,1,2,1:2:0:0: +300,179,215172,1,2,0:0:0:0: +203,179,215281,2,0,L|28:176,1,138,0|2,3:2|0:0,0:0:0:0: +148,176,215609,1,0,0:0:0:0: +383,176,215719,6,0,L|290:176,1,69,2|2,1:2|0:0,0:0:0:0: +112,176,215938,1,2,0:0:0:0: +311,176,216047,1,2,0:0:0:0: +408,176,216157,2,0,P|437:111|399:59,1,138,0|2,3:2|0:0,0:0:0:0: +305,60,216485,1,0,1:2:0:0: +69,60,216595,6,0,L|68:143,1,69,2|0,0:0|0:0,0:0:0:0: +179,128,216814,2,0,L|263:129,1,69,2|0,1:2|0:0,0:0:0:0: +449,128,217033,2,0,L|348:129,1,69,2|0,3:2|0:0,0:0:0:0: +178,128,217252,2,0,L|86:128,1,69,2|0,0:0|0:0,0:0:0:0: +344,128,217471,6,0,L|197:128,1,138,2|0,1:2|0:0,0:0:0:0: +289,128,217799,1,2,0:0:0:0: +206,128,217909,2,0,L|205:204,1,69,0|0,3:2|0:0,0:0:0:0: +406,196,218128,2,0,L|479:195,1,69,2|0,0:0|1:2,0:0:0:0: +239,195,218347,6,0,L|158:196,1,69,2|0,0:0|0:0,0:0:0:0: +371,195,218566,1,2,1:2:0:0: +170,195,218675,1,2,0:0:0:0: +267,195,218785,2,0,L|435:196,1,138,0|2,3:2|0:0,0:0:0:0: +321,195,219113,1,0,0:0:0:0: +85,195,219223,6,0,L|85:273,1,69,2|0,1:2|0:0,0:0:0:0: +286,264,219442,2,0,L|379:265,1,69,2|0,1:2|0:0,0:0:0:0: +119,264,219661,2,0,L|37:264,1,69,2|0,3:2|0:0,0:0:0:0: +320,264,219880,5,6,1:2:0:0: +399,257,219989,1,0,0:0:0:0: +402,180,220099,1,0,0:0:0:0: +327,170,220208,1,0,0:0:0:0: +129,120,220317,2,0,L|129:48,1,69,8|0,0:3|0:0,0:0:0:0: +330,51,220536,2,0,L|412:48,1,69,8|0,0:3|0:0,0:0:0:0: +163,48,220755,2,0,L|80:48,1,69,4|0,0:3|0:0,0:0:0:0: +364,52,220974,5,6,1:2:0:0: +439,64,221084,1,0,0:0:0:0: +426,139,221193,1,0,0:0:0:0: +350,146,221303,1,2,0:0:0:0: +240,146,221412,2,0,L|239:227,1,69,0|0,3:2|0:0,0:0:0:0: +440,214,221631,5,2,1:2:0:0: +472,282,221741,1,0,0:0:0:0: +434,346,221850,1,0,0:0:0:0: +357,352,221960,1,0,0:0:0:0: +157,352,222069,2,0,L|66:348,1,69,2|0,1:2|0:0,0:0:0:0: +289,348,222288,2,0,L|471:346,1,138,2|2,3:2|0:0,0:0:0:0: +343,346,222617,1,0,0:0:0:0: +109,346,222726,6,0,P|83:283|123:224,1,138,2|0,1:2|0:0,0:0:0:0: +207,227,223055,1,2,0:0:0:0: +117,227,223164,2,0,L|114:140,1,69,0|0,3:2|0:0,0:0:0:0: +315,158,223383,2,0,L|399:159,1,69,2|0,1:2|0:0,0:0:0:0: +148,158,223602,6,0,L|146:226,1,69,2|0,0:0|0:0,0:0:0:0: +256,226,223821,2,0,L|346:226,1,69,2|0,1:2|0:0,0:0:0:0: +123,226,224040,2,0,L|209:226,1,69,2|0,3:2|0:0,0:0:0:0: +393,226,224259,2,0,L|394:149,1,69,2|0,0:0|0:0,0:0:0:0: +158,157,224478,5,2,1:2:0:0: +82,163,224588,1,0,0:0:0:0: +44,228,224697,1,0,0:0:0:0: +86,291,224807,1,0,0:0:0:0: +285,291,224916,2,0,L|378:292,1,69,0|0,3:2|0:0,0:0:0:0: +83,291,225135,5,6,1:2:0:0: +41,227,225244,1,0,0:0:0:0: +82,163,225354,1,0,0:0:0:0: +157,156,225463,1,0,0:0:0:0: +267,156,225573,2,0,L|267:86,1,69,0|0,1:2|0:0,0:0:0:0: +65,87,225792,2,0,L|64:173,1,69,2|0,3:2|0:0,0:0:0:0: +154,155,226011,1,2,0:0:0:0: +64,155,226120,1,2,0:0:0:0: +299,155,226230,5,2,1:2:0:0: +105,155,226449,2,0,L|104:233,1,69,8|0,0:3|0:0,0:0:0:0: +305,223,226668,1,0,3:2:0:0: +104,223,226777,2,0,L|28:224,1,69,8|2,0:3|0:0,0:0:0:0: +383,353,227106,6,0,P|161:330|63:49,1,552,6|2,1:2|0:0,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3644427-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3644427-expected-conversion.json new file mode 100644 index 0000000000..9d4210c71e --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3644427-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":22.0,"Objects":[{"StartTime":22.0,"Position":28.0,"HyperDash":false},{"StartTime":90.0,"Position":34.2658119,"HyperDash":false},{"StartTime":158.0,"Position":28.0,"HyperDash":false},{"StartTime":226.0,"Position":34.2658119,"HyperDash":false},{"StartTime":294.0,"Position":28.0,"HyperDash":false},{"StartTime":362.0,"Position":34.2658119,"HyperDash":false}]},{"StartTime":431.0,"Objects":[{"StartTime":431.0,"Position":106.0,"HyperDash":false},{"StartTime":499.0,"Position":114.369232,"HyperDash":false},{"StartTime":567.0,"Position":106.0,"HyperDash":false},{"StartTime":635.0,"Position":114.369232,"HyperDash":false},{"StartTime":703.0,"Position":106.0,"HyperDash":false},{"StartTime":771.0,"Position":114.369232,"HyperDash":false}]},{"StartTime":840.0,"Objects":[{"StartTime":840.0,"Position":207.0,"HyperDash":false},{"StartTime":891.0,"Position":229.046875,"HyperDash":false},{"StartTime":942.0,"Position":270.0929,"HyperDash":false},{"StartTime":993.0,"Position":315.1283,"HyperDash":false},{"StartTime":1044.0,"Position":355.531,"HyperDash":false},{"StartTime":1128.0,"Position":309.8878,"HyperDash":false},{"StartTime":1249.0,"Position":207.0,"HyperDash":false}]},{"StartTime":1385.0,"Objects":[{"StartTime":1385.0,"Position":313.0,"HyperDash":false},{"StartTime":1453.0,"Position":346.417664,"HyperDash":false},{"StartTime":1521.0,"Position":313.0,"HyperDash":false},{"StartTime":1589.0,"Position":346.417664,"HyperDash":false},{"StartTime":1657.0,"Position":313.0,"HyperDash":false},{"StartTime":1725.0,"Position":346.417664,"HyperDash":false}]},{"StartTime":1794.0,"Objects":[{"StartTime":1794.0,"Position":347.0,"HyperDash":false},{"StartTime":1862.0,"Position":379.8631,"HyperDash":false},{"StartTime":1930.0,"Position":347.0,"HyperDash":false},{"StartTime":1998.0,"Position":379.8631,"HyperDash":false},{"StartTime":2066.0,"Position":347.0,"HyperDash":false},{"StartTime":2134.0,"Position":379.8631,"HyperDash":false}]},{"StartTime":2203.0,"Objects":[{"StartTime":2203.0,"Position":415.0,"HyperDash":false},{"StartTime":2254.0,"Position":441.134857,"HyperDash":false},{"StartTime":2305.0,"Position":433.726776,"HyperDash":false},{"StartTime":2356.0,"Position":437.454437,"HyperDash":false},{"StartTime":2407.0,"Position":447.7087,"HyperDash":false},{"StartTime":2491.0,"Position":417.7042,"HyperDash":false},{"StartTime":2612.0,"Position":415.0,"HyperDash":false}]},{"StartTime":2749.0,"Objects":[{"StartTime":2749.0,"Position":235.0,"HyperDash":false},{"StartTime":2817.0,"Position":201.582336,"HyperDash":false},{"StartTime":2885.0,"Position":235.0,"HyperDash":false},{"StartTime":2953.0,"Position":201.582336,"HyperDash":false},{"StartTime":3021.0,"Position":235.0,"HyperDash":false},{"StartTime":3089.0,"Position":201.582336,"HyperDash":false}]},{"StartTime":3158.0,"Objects":[{"StartTime":3158.0,"Position":219.0,"HyperDash":false},{"StartTime":3226.0,"Position":229.565125,"HyperDash":false},{"StartTime":3294.0,"Position":219.0,"HyperDash":false},{"StartTime":3362.0,"Position":229.565125,"HyperDash":false},{"StartTime":3430.0,"Position":219.0,"HyperDash":false},{"StartTime":3498.0,"Position":229.565125,"HyperDash":false}]},{"StartTime":3567.0,"Objects":[{"StartTime":3567.0,"Position":299.0,"HyperDash":false},{"StartTime":3618.0,"Position":253.857819,"HyperDash":false},{"StartTime":3669.0,"Position":212.7368,"HyperDash":false},{"StartTime":3720.0,"Position":183.691315,"HyperDash":false},{"StartTime":3771.0,"Position":150.281219,"HyperDash":false},{"StartTime":3855.0,"Position":224.9364,"HyperDash":false},{"StartTime":3976.0,"Position":299.0,"HyperDash":false}]},{"StartTime":4112.0,"Objects":[{"StartTime":4112.0,"Position":234.0,"HyperDash":false},{"StartTime":4180.0,"Position":201.015152,"HyperDash":false},{"StartTime":4248.0,"Position":234.0,"HyperDash":false},{"StartTime":4316.0,"Position":201.015152,"HyperDash":false},{"StartTime":4384.0,"Position":234.0,"HyperDash":false},{"StartTime":4452.0,"Position":201.015152,"HyperDash":false}]},{"StartTime":4522.0,"Objects":[{"StartTime":4522.0,"Position":135.0,"HyperDash":false},{"StartTime":4590.0,"Position":102.015152,"HyperDash":false},{"StartTime":4658.0,"Position":135.0,"HyperDash":false},{"StartTime":4726.0,"Position":102.015152,"HyperDash":false},{"StartTime":4794.0,"Position":135.0,"HyperDash":false},{"StartTime":4862.0,"Position":102.015152,"HyperDash":false}]},{"StartTime":4931.0,"Objects":[{"StartTime":4931.0,"Position":35.0,"HyperDash":false},{"StartTime":4982.0,"Position":48.13485,"HyperDash":false},{"StartTime":5033.0,"Position":43.7267761,"HyperDash":false},{"StartTime":5084.0,"Position":63.454422,"HyperDash":false},{"StartTime":5135.0,"Position":67.7087,"HyperDash":false},{"StartTime":5219.0,"Position":33.7041931,"HyperDash":false},{"StartTime":5340.0,"Position":35.0,"HyperDash":false}]},{"StartTime":5476.0,"Objects":[{"StartTime":5476.0,"Position":22.0,"HyperDash":false},{"StartTime":5544.0,"Position":18.9217854,"HyperDash":false},{"StartTime":5612.0,"Position":22.0,"HyperDash":false},{"StartTime":5680.0,"Position":18.9217854,"HyperDash":false},{"StartTime":5748.0,"Position":22.0,"HyperDash":false},{"StartTime":5816.0,"Position":18.9217854,"HyperDash":false}]},{"StartTime":5885.0,"Objects":[{"StartTime":5885.0,"Position":120.0,"HyperDash":false},{"StartTime":5953.0,"Position":152.061676,"HyperDash":false},{"StartTime":6021.0,"Position":120.0,"HyperDash":false},{"StartTime":6089.0,"Position":152.061676,"HyperDash":false},{"StartTime":6157.0,"Position":120.0,"HyperDash":false},{"StartTime":6225.0,"Position":152.061676,"HyperDash":false}]},{"StartTime":6294.0,"Objects":[{"StartTime":6294.0,"Position":187.0,"HyperDash":false},{"StartTime":6345.0,"Position":140.953125,"HyperDash":false},{"StartTime":6396.0,"Position":130.9071,"HyperDash":false},{"StartTime":6447.0,"Position":92.8717041,"HyperDash":false},{"StartTime":6498.0,"Position":38.469,"HyperDash":false},{"StartTime":6582.0,"Position":112.112221,"HyperDash":false},{"StartTime":6703.0,"Position":187.0,"HyperDash":false}]},{"StartTime":6840.0,"Objects":[{"StartTime":6840.0,"Position":363.0,"HyperDash":false},{"StartTime":6908.0,"Position":359.921783,"HyperDash":false},{"StartTime":6976.0,"Position":363.0,"HyperDash":false},{"StartTime":7044.0,"Position":359.921783,"HyperDash":false},{"StartTime":7112.0,"Position":363.0,"HyperDash":false},{"StartTime":7180.0,"Position":359.921783,"HyperDash":false}]},{"StartTime":7249.0,"Objects":[{"StartTime":7249.0,"Position":411.0,"HyperDash":false},{"StartTime":7317.0,"Position":443.061676,"HyperDash":false},{"StartTime":7385.0,"Position":411.0,"HyperDash":false},{"StartTime":7453.0,"Position":443.061676,"HyperDash":false},{"StartTime":7521.0,"Position":411.0,"HyperDash":false},{"StartTime":7589.0,"Position":443.061676,"HyperDash":false}]},{"StartTime":7658.0,"Objects":[{"StartTime":7658.0,"Position":355.0,"HyperDash":false},{"StartTime":7709.0,"Position":356.134857,"HyperDash":false},{"StartTime":7760.0,"Position":355.726776,"HyperDash":false},{"StartTime":7811.0,"Position":391.454437,"HyperDash":false},{"StartTime":7862.0,"Position":387.7087,"HyperDash":false},{"StartTime":7946.0,"Position":367.7042,"HyperDash":false},{"StartTime":8067.0,"Position":355.0,"HyperDash":false}]},{"StartTime":8203.0,"Objects":[{"StartTime":8203.0,"Position":502.0,"HyperDash":false},{"StartTime":8271.0,"Position":508.2658,"HyperDash":false},{"StartTime":8339.0,"Position":502.0,"HyperDash":false},{"StartTime":8407.0,"Position":508.2658,"HyperDash":false},{"StartTime":8475.0,"Position":502.0,"HyperDash":false},{"StartTime":8543.0,"Position":508.2658,"HyperDash":false}]},{"StartTime":8612.0,"Objects":[{"StartTime":8612.0,"Position":419.0,"HyperDash":false},{"StartTime":8680.0,"Position":429.565125,"HyperDash":false},{"StartTime":8748.0,"Position":419.0,"HyperDash":false},{"StartTime":8816.0,"Position":429.565125,"HyperDash":false},{"StartTime":8884.0,"Position":419.0,"HyperDash":false},{"StartTime":8952.0,"Position":429.565125,"HyperDash":false}]},{"StartTime":9022.0,"Objects":[{"StartTime":9022.0,"Position":364.0,"HyperDash":false},{"StartTime":9073.0,"Position":405.046875,"HyperDash":false},{"StartTime":9124.0,"Position":423.0929,"HyperDash":false},{"StartTime":9175.0,"Position":471.1283,"HyperDash":false},{"StartTime":9226.0,"Position":512.0,"HyperDash":false},{"StartTime":9310.0,"Position":450.8878,"HyperDash":false},{"StartTime":9431.0,"Position":364.0,"HyperDash":false}]},{"StartTime":9567.0,"Objects":[{"StartTime":9567.0,"Position":233.0,"HyperDash":false},{"StartTime":9635.0,"Position":226.21344,"HyperDash":false},{"StartTime":9703.0,"Position":233.0,"HyperDash":false},{"StartTime":9771.0,"Position":226.21344,"HyperDash":false},{"StartTime":9839.0,"Position":233.0,"HyperDash":false},{"StartTime":9907.0,"Position":226.21344,"HyperDash":false}]},{"StartTime":9976.0,"Objects":[{"StartTime":9976.0,"Position":284.0,"HyperDash":false},{"StartTime":10044.0,"Position":302.4323,"HyperDash":false},{"StartTime":10112.0,"Position":284.0,"HyperDash":false},{"StartTime":10180.0,"Position":302.4323,"HyperDash":false},{"StartTime":10248.0,"Position":284.0,"HyperDash":false},{"StartTime":10316.0,"Position":302.4323,"HyperDash":false}]},{"StartTime":10385.0,"Objects":[{"StartTime":10385.0,"Position":245.0,"HyperDash":false},{"StartTime":10436.0,"Position":221.437592,"HyperDash":false},{"StartTime":10487.0,"Position":156.41156,"HyperDash":false},{"StartTime":10538.0,"Position":124.576111,"HyperDash":false},{"StartTime":10589.0,"Position":129.145676,"HyperDash":false},{"StartTime":10673.0,"Position":143.747086,"HyperDash":false},{"StartTime":10794.0,"Position":245.0,"HyperDash":false}]},{"StartTime":12021.0,"Objects":[{"StartTime":12021.0,"Position":407.0,"HyperDash":false},{"StartTime":12157.0,"Position":430.1819,"HyperDash":false}]},{"StartTime":12225.0,"Objects":[{"StartTime":12225.0,"Position":484.0,"HyperDash":false}]},{"StartTime":12293.0,"Objects":[{"StartTime":12293.0,"Position":484.0,"HyperDash":false},{"StartTime":12429.0,"Position":405.168243,"HyperDash":false}]},{"StartTime":12566.0,"Objects":[{"StartTime":12566.0,"Position":387.0,"HyperDash":false},{"StartTime":12617.0,"Position":436.7446,"HyperDash":false},{"StartTime":12668.0,"Position":476.301575,"HyperDash":false},{"StartTime":12719.0,"Position":481.9031,"HyperDash":false},{"StartTime":12770.0,"Position":487.317719,"HyperDash":false},{"StartTime":12854.0,"Position":463.006,"HyperDash":false},{"StartTime":12975.0,"Position":387.0,"HyperDash":false}]},{"StartTime":13111.0,"Objects":[{"StartTime":13111.0,"Position":274.0,"HyperDash":false},{"StartTime":13247.0,"Position":173.621216,"HyperDash":false}]},{"StartTime":13316.0,"Objects":[{"StartTime":13316.0,"Position":124.0,"HyperDash":false}]},{"StartTime":13384.0,"Objects":[{"StartTime":13384.0,"Position":124.0,"HyperDash":false},{"StartTime":13520.0,"Position":23.6840134,"HyperDash":false}]},{"StartTime":13657.0,"Objects":[{"StartTime":13657.0,"Position":24.0,"HyperDash":false},{"StartTime":13741.0,"Position":80.13487,"HyperDash":false},{"StartTime":13861.0,"Position":108.188271,"HyperDash":false}]},{"StartTime":14066.0,"Objects":[{"StartTime":14066.0,"Position":229.0,"HyperDash":false}]},{"StartTime":14202.0,"Objects":[{"StartTime":14202.0,"Position":328.0,"HyperDash":false},{"StartTime":14338.0,"Position":300.487976,"HyperDash":false}]},{"StartTime":14407.0,"Objects":[{"StartTime":14407.0,"Position":256.0,"HyperDash":false}]},{"StartTime":14475.0,"Objects":[{"StartTime":14475.0,"Position":256.0,"HyperDash":false},{"StartTime":14611.0,"Position":333.4403,"HyperDash":false}]},{"StartTime":14748.0,"Objects":[{"StartTime":14748.0,"Position":378.0,"HyperDash":false},{"StartTime":14799.0,"Position":434.77832,"HyperDash":false},{"StartTime":14850.0,"Position":445.8225,"HyperDash":false},{"StartTime":14901.0,"Position":485.582428,"HyperDash":false},{"StartTime":14952.0,"Position":497.584961,"HyperDash":false},{"StartTime":15036.0,"Position":476.6938,"HyperDash":false},{"StartTime":15157.0,"Position":378.0,"HyperDash":false}]},{"StartTime":15293.0,"Objects":[{"StartTime":15293.0,"Position":277.0,"HyperDash":false},{"StartTime":15429.0,"Position":252.447769,"HyperDash":false}]},{"StartTime":15498.0,"Objects":[{"StartTime":15498.0,"Position":212.0,"HyperDash":false}]},{"StartTime":15566.0,"Objects":[{"StartTime":15566.0,"Position":212.0,"HyperDash":false},{"StartTime":15702.0,"Position":236.552231,"HyperDash":false}]},{"StartTime":15839.0,"Objects":[{"StartTime":15839.0,"Position":256.0,"HyperDash":false},{"StartTime":15923.0,"Position":331.136047,"HyperDash":false},{"StartTime":16043.0,"Position":396.792,"HyperDash":false}]},{"StartTime":16248.0,"Objects":[{"StartTime":16248.0,"Position":473.0,"HyperDash":false}]},{"StartTime":16384.0,"Objects":[{"StartTime":16384.0,"Position":486.0,"HyperDash":false},{"StartTime":16520.0,"Position":397.151581,"HyperDash":false}]},{"StartTime":16589.0,"Objects":[{"StartTime":16589.0,"Position":382.0,"HyperDash":false}]},{"StartTime":16657.0,"Objects":[{"StartTime":16657.0,"Position":382.0,"HyperDash":false},{"StartTime":16793.0,"Position":297.671143,"HyperDash":false}]},{"StartTime":16930.0,"Objects":[{"StartTime":16930.0,"Position":201.0,"HyperDash":false},{"StartTime":16981.0,"Position":215.6601,"HyperDash":false},{"StartTime":17032.0,"Position":193.010483,"HyperDash":false},{"StartTime":17083.0,"Position":161.445236,"HyperDash":false},{"StartTime":17134.0,"Position":106.410675,"HyperDash":false},{"StartTime":17218.0,"Position":175.619278,"HyperDash":false},{"StartTime":17339.0,"Position":201.0,"HyperDash":false}]},{"StartTime":17475.0,"Objects":[{"StartTime":17475.0,"Position":40.0,"HyperDash":false},{"StartTime":17611.0,"Position":56.7687,"HyperDash":false}]},{"StartTime":17680.0,"Objects":[{"StartTime":17680.0,"Position":97.0,"HyperDash":false}]},{"StartTime":17748.0,"Objects":[{"StartTime":17748.0,"Position":97.0,"HyperDash":false},{"StartTime":17884.0,"Position":197.612183,"HyperDash":false}]},{"StartTime":18021.0,"Objects":[{"StartTime":18021.0,"Position":275.0,"HyperDash":false},{"StartTime":18105.0,"Position":227.99115,"HyperDash":false},{"StartTime":18225.0,"Position":263.429932,"HyperDash":false}]},{"StartTime":18430.0,"Objects":[{"StartTime":18430.0,"Position":415.0,"HyperDash":false}]},{"StartTime":18566.0,"Objects":[{"StartTime":18566.0,"Position":355.0,"HyperDash":false},{"StartTime":18702.0,"Position":450.052368,"HyperDash":false}]},{"StartTime":18771.0,"Objects":[{"StartTime":18771.0,"Position":486.0,"HyperDash":false}]},{"StartTime":18839.0,"Objects":[{"StartTime":18839.0,"Position":486.0,"HyperDash":false},{"StartTime":18975.0,"Position":451.9095,"HyperDash":false}]},{"StartTime":19111.0,"Objects":[{"StartTime":19111.0,"Position":476.0,"HyperDash":false},{"StartTime":19162.0,"Position":467.7854,"HyperDash":false},{"StartTime":19213.0,"Position":421.959442,"HyperDash":false},{"StartTime":19264.0,"Position":381.8976,"HyperDash":false},{"StartTime":19315.0,"Position":360.902435,"HyperDash":false},{"StartTime":19399.0,"Position":438.64032,"HyperDash":false},{"StartTime":19520.0,"Position":476.0,"HyperDash":false}]},{"StartTime":19657.0,"Objects":[{"StartTime":19657.0,"Position":306.0,"HyperDash":false},{"StartTime":19793.0,"Position":210.46254,"HyperDash":false}]},{"StartTime":19861.0,"Objects":[{"StartTime":19861.0,"Position":161.0,"HyperDash":false}]},{"StartTime":19930.0,"Objects":[{"StartTime":19930.0,"Position":161.0,"HyperDash":false},{"StartTime":20066.0,"Position":196.729462,"HyperDash":false}]},{"StartTime":20202.0,"Objects":[{"StartTime":20202.0,"Position":127.0,"HyperDash":false},{"StartTime":20338.0,"Position":32.14918,"HyperDash":false}]},{"StartTime":20475.0,"Objects":[{"StartTime":20475.0,"Position":41.0,"HyperDash":false}]},{"StartTime":20543.0,"Objects":[{"StartTime":20543.0,"Position":48.0,"HyperDash":false}]},{"StartTime":20611.0,"Objects":[{"StartTime":20611.0,"Position":64.0,"HyperDash":false}]},{"StartTime":20679.0,"Objects":[{"StartTime":20679.0,"Position":86.0,"HyperDash":false}]},{"StartTime":20748.0,"Objects":[{"StartTime":20748.0,"Position":111.0,"HyperDash":false},{"StartTime":20884.0,"Position":197.677109,"HyperDash":false}]},{"StartTime":20952.0,"Objects":[{"StartTime":20952.0,"Position":249.0,"HyperDash":false}]},{"StartTime":21021.0,"Objects":[{"StartTime":21021.0,"Position":249.0,"HyperDash":false},{"StartTime":21157.0,"Position":350.174561,"HyperDash":false}]},{"StartTime":21293.0,"Objects":[{"StartTime":21293.0,"Position":451.0,"HyperDash":false},{"StartTime":21377.0,"Position":450.080383,"HyperDash":false},{"StartTime":21497.0,"Position":406.784882,"HyperDash":false}]},{"StartTime":21702.0,"Objects":[{"StartTime":21702.0,"Position":398.0,"HyperDash":false}]},{"StartTime":21839.0,"Objects":[{"StartTime":21839.0,"Position":337.0,"HyperDash":false},{"StartTime":21975.0,"Position":245.5466,"HyperDash":false}]},{"StartTime":22043.0,"Objects":[{"StartTime":22043.0,"Position":202.0,"HyperDash":false}]},{"StartTime":22111.0,"Objects":[{"StartTime":22111.0,"Position":202.0,"HyperDash":false},{"StartTime":22247.0,"Position":175.162018,"HyperDash":false}]},{"StartTime":22384.0,"Objects":[{"StartTime":22384.0,"Position":7.0,"HyperDash":false}]},{"StartTime":22589.0,"Objects":[{"StartTime":22589.0,"Position":7.0,"HyperDash":false}]},{"StartTime":22793.0,"Objects":[{"StartTime":22793.0,"Position":7.0,"HyperDash":false}]},{"StartTime":22930.0,"Objects":[{"StartTime":22930.0,"Position":61.0,"HyperDash":false},{"StartTime":23066.0,"Position":50.69364,"HyperDash":false}]},{"StartTime":23134.0,"Objects":[{"StartTime":23134.0,"Position":92.0,"HyperDash":false}]},{"StartTime":23202.0,"Objects":[{"StartTime":23202.0,"Position":92.0,"HyperDash":false},{"StartTime":23338.0,"Position":179.7528,"HyperDash":false}]},{"StartTime":23475.0,"Objects":[{"StartTime":23475.0,"Position":262.0,"HyperDash":false},{"StartTime":23559.0,"Position":335.717,"HyperDash":false},{"StartTime":23679.0,"Position":354.8034,"HyperDash":false}]},{"StartTime":23884.0,"Objects":[{"StartTime":23884.0,"Position":467.0,"HyperDash":false}]},{"StartTime":24021.0,"Objects":[{"StartTime":24021.0,"Position":430.0,"HyperDash":false},{"StartTime":24157.0,"Position":329.387817,"HyperDash":false}]},{"StartTime":24225.0,"Objects":[{"StartTime":24225.0,"Position":284.0,"HyperDash":false}]},{"StartTime":24293.0,"Objects":[{"StartTime":24293.0,"Position":284.0,"HyperDash":false},{"StartTime":24429.0,"Position":261.101257,"HyperDash":false}]},{"StartTime":24566.0,"Objects":[{"StartTime":24566.0,"Position":386.0,"HyperDash":false}]},{"StartTime":24771.0,"Objects":[{"StartTime":24771.0,"Position":386.0,"HyperDash":false}]},{"StartTime":24975.0,"Objects":[{"StartTime":24975.0,"Position":386.0,"HyperDash":false}]},{"StartTime":25111.0,"Objects":[{"StartTime":25111.0,"Position":432.0,"HyperDash":false},{"StartTime":25247.0,"Position":447.35553,"HyperDash":false}]},{"StartTime":25316.0,"Objects":[{"StartTime":25316.0,"Position":416.0,"HyperDash":false}]},{"StartTime":25384.0,"Objects":[{"StartTime":25384.0,"Position":416.0,"HyperDash":false},{"StartTime":25520.0,"Position":316.536438,"HyperDash":false}]},{"StartTime":25657.0,"Objects":[{"StartTime":25657.0,"Position":219.0,"HyperDash":false},{"StartTime":25741.0,"Position":178.386673,"HyperDash":false},{"StartTime":25861.0,"Position":167.07811,"HyperDash":false}]},{"StartTime":26066.0,"Objects":[{"StartTime":26066.0,"Position":40.0,"HyperDash":false}]},{"StartTime":26202.0,"Objects":[{"StartTime":26202.0,"Position":28.0,"HyperDash":false},{"StartTime":26338.0,"Position":101.876366,"HyperDash":false}]},{"StartTime":26407.0,"Objects":[{"StartTime":26407.0,"Position":125.0,"HyperDash":false}]},{"StartTime":26475.0,"Objects":[{"StartTime":26475.0,"Position":125.0,"HyperDash":false},{"StartTime":26611.0,"Position":142.871735,"HyperDash":false}]},{"StartTime":26748.0,"Objects":[{"StartTime":26748.0,"Position":221.0,"HyperDash":false}]},{"StartTime":26953.0,"Objects":[{"StartTime":26953.0,"Position":221.0,"HyperDash":false}]},{"StartTime":27157.0,"Objects":[{"StartTime":27157.0,"Position":221.0,"HyperDash":false}]},{"StartTime":27293.0,"Objects":[{"StartTime":27293.0,"Position":379.0,"HyperDash":false},{"StartTime":27429.0,"Position":479.272156,"HyperDash":false}]},{"StartTime":27498.0,"Objects":[{"StartTime":27498.0,"Position":510.0,"HyperDash":false}]},{"StartTime":27566.0,"Objects":[{"StartTime":27566.0,"Position":510.0,"HyperDash":false},{"StartTime":27702.0,"Position":491.238281,"HyperDash":false}]},{"StartTime":27839.0,"Objects":[{"StartTime":27839.0,"Position":503.0,"HyperDash":false},{"StartTime":27923.0,"Position":457.324524,"HyperDash":false},{"StartTime":28043.0,"Position":381.6685,"HyperDash":false}]},{"StartTime":28248.0,"Objects":[{"StartTime":28248.0,"Position":256.0,"HyperDash":false}]},{"StartTime":28384.0,"Objects":[{"StartTime":28384.0,"Position":190.0,"HyperDash":false}]},{"StartTime":28521.0,"Objects":[{"StartTime":28521.0,"Position":269.0,"HyperDash":false}]},{"StartTime":28589.0,"Objects":[{"StartTime":28589.0,"Position":272.0,"HyperDash":false}]},{"StartTime":28657.0,"Objects":[{"StartTime":28657.0,"Position":275.0,"HyperDash":false},{"StartTime":28793.0,"Position":264.133636,"HyperDash":false}]},{"StartTime":28930.0,"Objects":[{"StartTime":28930.0,"Position":179.0,"HyperDash":false}]},{"StartTime":28998.0,"Objects":[{"StartTime":28998.0,"Position":154.0,"HyperDash":false}]},{"StartTime":29066.0,"Objects":[{"StartTime":29066.0,"Position":135.0,"HyperDash":false}]},{"StartTime":29134.0,"Objects":[{"StartTime":29134.0,"Position":122.0,"HyperDash":false}]},{"StartTime":29202.0,"Objects":[{"StartTime":29202.0,"Position":118.0,"HyperDash":false},{"StartTime":29270.0,"Position":107.667114,"HyperDash":false},{"StartTime":29338.0,"Position":118.0,"HyperDash":false},{"StartTime":29406.0,"Position":107.667114,"HyperDash":false}]},{"StartTime":29475.0,"Objects":[{"StartTime":29475.0,"Position":45.0,"HyperDash":false},{"StartTime":29543.0,"Position":4.39538574,"HyperDash":false},{"StartTime":29611.0,"Position":45.0,"HyperDash":false},{"StartTime":29679.0,"Position":4.39538574,"HyperDash":false}]},{"StartTime":29748.0,"Objects":[{"StartTime":29748.0,"Position":102.0,"HyperDash":false},{"StartTime":29816.0,"Position":142.604614,"HyperDash":false},{"StartTime":29884.0,"Position":102.0,"HyperDash":false},{"StartTime":29952.0,"Position":142.604614,"HyperDash":false}]},{"StartTime":30021.0,"Objects":[{"StartTime":30021.0,"Position":193.0,"HyperDash":false},{"StartTime":30089.0,"Position":205.21228,"HyperDash":false},{"StartTime":30157.0,"Position":193.0,"HyperDash":false},{"StartTime":30225.0,"Position":205.21228,"HyperDash":false}]},{"StartTime":30293.0,"Objects":[{"StartTime":30293.0,"Position":291.0,"HyperDash":false},{"StartTime":30361.0,"Position":302.9382,"HyperDash":false},{"StartTime":30429.0,"Position":291.0,"HyperDash":false},{"StartTime":30497.0,"Position":302.9382,"HyperDash":false}]},{"StartTime":30566.0,"Objects":[{"StartTime":30566.0,"Position":391.0,"HyperDash":false}]},{"StartTime":30634.0,"Objects":[{"StartTime":30634.0,"Position":400.0,"HyperDash":false}]},{"StartTime":30702.0,"Objects":[{"StartTime":30702.0,"Position":409.0,"HyperDash":false}]},{"StartTime":30839.0,"Objects":[{"StartTime":30839.0,"Position":434.0,"HyperDash":false}]},{"StartTime":30907.0,"Objects":[{"StartTime":30907.0,"Position":425.0,"HyperDash":false}]},{"StartTime":30975.0,"Objects":[{"StartTime":30975.0,"Position":416.0,"HyperDash":false}]},{"StartTime":31111.0,"Objects":[{"StartTime":31111.0,"Position":512.0,"HyperDash":false},{"StartTime":31179.0,"Position":499.154633,"HyperDash":false},{"StartTime":31247.0,"Position":512.0,"HyperDash":false}]},{"StartTime":31384.0,"Objects":[{"StartTime":31384.0,"Position":435.0,"HyperDash":false},{"StartTime":31452.0,"Position":446.9382,"HyperDash":false},{"StartTime":31520.0,"Position":435.0,"HyperDash":false}]},{"StartTime":31657.0,"Objects":[{"StartTime":31657.0,"Position":381.0,"HyperDash":false},{"StartTime":31725.0,"Position":340.211151,"HyperDash":false},{"StartTime":31793.0,"Position":381.0,"HyperDash":false},{"StartTime":31861.0,"Position":340.211151,"HyperDash":false}]},{"StartTime":31930.0,"Objects":[{"StartTime":31930.0,"Position":251.0,"HyperDash":false},{"StartTime":31998.0,"Position":210.395386,"HyperDash":false},{"StartTime":32066.0,"Position":251.0,"HyperDash":false},{"StartTime":32134.0,"Position":210.395386,"HyperDash":false}]},{"StartTime":32202.0,"Objects":[{"StartTime":32202.0,"Position":146.0,"HyperDash":false},{"StartTime":32270.0,"Position":158.21228,"HyperDash":false},{"StartTime":32338.0,"Position":146.0,"HyperDash":false},{"StartTime":32406.0,"Position":158.21228,"HyperDash":false}]},{"StartTime":32475.0,"Objects":[{"StartTime":32475.0,"Position":56.0,"HyperDash":false},{"StartTime":32543.0,"Position":68.21229,"HyperDash":false},{"StartTime":32611.0,"Position":56.0,"HyperDash":false},{"StartTime":32679.0,"Position":68.21229,"HyperDash":false}]},{"StartTime":32748.0,"Objects":[{"StartTime":32748.0,"Position":22.0,"HyperDash":false}]},{"StartTime":32816.0,"Objects":[{"StartTime":32816.0,"Position":25.0,"HyperDash":false}]},{"StartTime":32884.0,"Objects":[{"StartTime":32884.0,"Position":28.0,"HyperDash":false}]},{"StartTime":33021.0,"Objects":[{"StartTime":33021.0,"Position":93.0,"HyperDash":false}]},{"StartTime":33089.0,"Objects":[{"StartTime":33089.0,"Position":90.0,"HyperDash":false}]},{"StartTime":33157.0,"Objects":[{"StartTime":33157.0,"Position":87.0,"HyperDash":false}]},{"StartTime":33293.0,"Objects":[{"StartTime":33293.0,"Position":168.0,"HyperDash":false}]},{"StartTime":33361.0,"Objects":[{"StartTime":33361.0,"Position":176.0,"HyperDash":false}]},{"StartTime":33430.0,"Objects":[{"StartTime":33430.0,"Position":184.0,"HyperDash":false},{"StartTime":33566.0,"Position":268.439758,"HyperDash":false}]},{"StartTime":33839.0,"Objects":[{"StartTime":33839.0,"Position":274.0,"HyperDash":false},{"StartTime":33907.0,"Position":261.78772,"HyperDash":false},{"StartTime":33975.0,"Position":274.0,"HyperDash":false},{"StartTime":34043.0,"Position":261.78772,"HyperDash":false}]},{"StartTime":34112.0,"Objects":[{"StartTime":34112.0,"Position":330.0,"HyperDash":false},{"StartTime":34180.0,"Position":342.21228,"HyperDash":false},{"StartTime":34248.0,"Position":330.0,"HyperDash":false},{"StartTime":34316.0,"Position":342.21228,"HyperDash":false}]},{"StartTime":34384.0,"Objects":[{"StartTime":34384.0,"Position":422.0,"HyperDash":false},{"StartTime":34452.0,"Position":462.788849,"HyperDash":false},{"StartTime":34520.0,"Position":422.0,"HyperDash":false},{"StartTime":34588.0,"Position":462.788849,"HyperDash":false}]},{"StartTime":34657.0,"Objects":[{"StartTime":34657.0,"Position":461.0,"HyperDash":false},{"StartTime":34725.0,"Position":501.6046,"HyperDash":false},{"StartTime":34793.0,"Position":461.0,"HyperDash":false},{"StartTime":34861.0,"Position":501.6046,"HyperDash":false}]},{"StartTime":34930.0,"Objects":[{"StartTime":34930.0,"Position":448.0,"HyperDash":false}]},{"StartTime":34998.0,"Objects":[{"StartTime":34998.0,"Position":439.0,"HyperDash":false}]},{"StartTime":35066.0,"Objects":[{"StartTime":35066.0,"Position":430.0,"HyperDash":false}]},{"StartTime":35202.0,"Objects":[{"StartTime":35202.0,"Position":321.0,"HyperDash":false}]},{"StartTime":35270.0,"Objects":[{"StartTime":35270.0,"Position":312.0,"HyperDash":false}]},{"StartTime":35338.0,"Objects":[{"StartTime":35338.0,"Position":303.0,"HyperDash":false}]},{"StartTime":35475.0,"Objects":[{"StartTime":35475.0,"Position":269.0,"HyperDash":false},{"StartTime":35543.0,"Position":228.395386,"HyperDash":false},{"StartTime":35611.0,"Position":269.0,"HyperDash":false}]},{"StartTime":35748.0,"Objects":[{"StartTime":35748.0,"Position":162.0,"HyperDash":false},{"StartTime":35816.0,"Position":202.788834,"HyperDash":false},{"StartTime":35884.0,"Position":162.0,"HyperDash":false}]},{"StartTime":36021.0,"Objects":[{"StartTime":36021.0,"Position":87.0,"HyperDash":false},{"StartTime":36089.0,"Position":99.21229,"HyperDash":false},{"StartTime":36157.0,"Position":87.0,"HyperDash":false},{"StartTime":36225.0,"Position":99.21229,"HyperDash":false}]},{"StartTime":36294.0,"Objects":[{"StartTime":36294.0,"Position":31.0,"HyperDash":false},{"StartTime":36362.0,"Position":18.7877159,"HyperDash":false},{"StartTime":36430.0,"Position":31.0,"HyperDash":false},{"StartTime":36498.0,"Position":18.7877159,"HyperDash":false}]},{"StartTime":36566.0,"Objects":[{"StartTime":36566.0,"Position":101.0,"HyperDash":false},{"StartTime":36634.0,"Position":141.788834,"HyperDash":false},{"StartTime":36702.0,"Position":101.0,"HyperDash":false},{"StartTime":36770.0,"Position":141.788834,"HyperDash":false}]},{"StartTime":36839.0,"Objects":[{"StartTime":36839.0,"Position":184.0,"HyperDash":false},{"StartTime":36907.0,"Position":224.604614,"HyperDash":false},{"StartTime":36975.0,"Position":184.0,"HyperDash":false},{"StartTime":37043.0,"Position":224.604614,"HyperDash":false}]},{"StartTime":37111.0,"Objects":[{"StartTime":37111.0,"Position":304.0,"HyperDash":false}]},{"StartTime":37179.0,"Objects":[{"StartTime":37179.0,"Position":307.0,"HyperDash":false}]},{"StartTime":37247.0,"Objects":[{"StartTime":37247.0,"Position":310.0,"HyperDash":false}]},{"StartTime":37384.0,"Objects":[{"StartTime":37384.0,"Position":392.0,"HyperDash":false}]},{"StartTime":37452.0,"Objects":[{"StartTime":37452.0,"Position":395.0,"HyperDash":false}]},{"StartTime":37520.0,"Objects":[{"StartTime":37520.0,"Position":398.0,"HyperDash":false}]},{"StartTime":37657.0,"Objects":[{"StartTime":37657.0,"Position":341.0,"HyperDash":false},{"StartTime":37725.0,"Position":356.784119,"HyperDash":false},{"StartTime":37793.0,"Position":341.0,"HyperDash":false},{"StartTime":37861.0,"Position":356.784119,"HyperDash":false}]},{"StartTime":37930.0,"Objects":[{"StartTime":37930.0,"Position":352.0,"HyperDash":false},{"StartTime":37998.0,"Position":367.784119,"HyperDash":false},{"StartTime":38066.0,"Position":352.0,"HyperDash":false},{"StartTime":38134.0,"Position":367.784119,"HyperDash":false}]},{"StartTime":38202.0,"Objects":[{"StartTime":38202.0,"Position":449.0,"HyperDash":false},{"StartTime":38261.0,"Position":474.06,"HyperDash":false},{"StartTime":38320.0,"Position":479.854156,"HyperDash":false},{"StartTime":38379.0,"Position":497.1734,"HyperDash":false},{"StartTime":38474.0,"Position":487.267334,"HyperDash":false}]},{"StartTime":38748.0,"Objects":[{"StartTime":38748.0,"Position":487.0,"HyperDash":false},{"StartTime":38807.0,"Position":440.148621,"HyperDash":false},{"StartTime":38866.0,"Position":413.297272,"HyperDash":false},{"StartTime":38925.0,"Position":408.4459,"HyperDash":false},{"StartTime":39020.0,"Position":353.9903,"HyperDash":false}]},{"StartTime":39293.0,"Objects":[{"StartTime":39293.0,"Position":403.0,"HyperDash":false},{"StartTime":39352.0,"Position":361.224365,"HyperDash":false},{"StartTime":39411.0,"Position":329.028229,"HyperDash":false},{"StartTime":39470.0,"Position":319.025146,"HyperDash":false},{"StartTime":39565.0,"Position":277.9407,"HyperDash":false}]},{"StartTime":39702.0,"Objects":[{"StartTime":39702.0,"Position":277.0,"HyperDash":false}]},{"StartTime":39839.0,"Objects":[{"StartTime":39839.0,"Position":155.0,"HyperDash":false},{"StartTime":39975.0,"Position":184.883255,"HyperDash":false}]},{"StartTime":40111.0,"Objects":[{"StartTime":40111.0,"Position":65.0,"HyperDash":false}]},{"StartTime":40384.0,"Objects":[{"StartTime":40384.0,"Position":65.0,"HyperDash":false},{"StartTime":40520.0,"Position":148.5545,"HyperDash":false}]},{"StartTime":40657.0,"Objects":[{"StartTime":40657.0,"Position":90.0,"HyperDash":false},{"StartTime":40793.0,"Position":6.445488,"HyperDash":false}]},{"StartTime":40930.0,"Objects":[{"StartTime":40930.0,"Position":180.0,"HyperDash":false}]},{"StartTime":41066.0,"Objects":[{"StartTime":41066.0,"Position":280.0,"HyperDash":false}]},{"StartTime":41134.0,"Objects":[{"StartTime":41134.0,"Position":280.0,"HyperDash":false}]},{"StartTime":41202.0,"Objects":[{"StartTime":41202.0,"Position":280.0,"HyperDash":false},{"StartTime":41338.0,"Position":363.5545,"HyperDash":false}]},{"StartTime":41475.0,"Objects":[{"StartTime":41475.0,"Position":208.0,"HyperDash":false}]},{"StartTime":41611.0,"Objects":[{"StartTime":41611.0,"Position":208.0,"HyperDash":false}]},{"StartTime":41748.0,"Objects":[{"StartTime":41748.0,"Position":372.0,"HyperDash":false},{"StartTime":41884.0,"Position":288.4455,"HyperDash":false}]},{"StartTime":42021.0,"Objects":[{"StartTime":42021.0,"Position":170.0,"HyperDash":false},{"StartTime":42157.0,"Position":187.164719,"HyperDash":false}]},{"StartTime":42293.0,"Objects":[{"StartTime":42293.0,"Position":64.0,"HyperDash":false},{"StartTime":42361.0,"Position":71.60263,"HyperDash":false},{"StartTime":42429.0,"Position":64.0,"HyperDash":false},{"StartTime":42497.0,"Position":71.60263,"HyperDash":false}]},{"StartTime":42566.0,"Objects":[{"StartTime":42566.0,"Position":25.0,"HyperDash":false},{"StartTime":42625.0,"Position":29.524353,"HyperDash":false},{"StartTime":42684.0,"Position":56.72582,"HyperDash":false},{"StartTime":42743.0,"Position":46.7086868,"HyperDash":false},{"StartTime":42838.0,"Position":32.0564842,"HyperDash":false}]},{"StartTime":43111.0,"Objects":[{"StartTime":43111.0,"Position":32.0,"HyperDash":false},{"StartTime":43170.0,"Position":77.73514,"HyperDash":false},{"StartTime":43229.0,"Position":72.4702759,"HyperDash":false},{"StartTime":43288.0,"Position":123.205421,"HyperDash":false},{"StartTime":43383.0,"Position":164.473862,"HyperDash":false}]},{"StartTime":43657.0,"Objects":[{"StartTime":43657.0,"Position":420.0,"HyperDash":false},{"StartTime":43716.0,"Position":410.224365,"HyperDash":false},{"StartTime":43775.0,"Position":376.028229,"HyperDash":false},{"StartTime":43834.0,"Position":351.025146,"HyperDash":false},{"StartTime":43929.0,"Position":294.9407,"HyperDash":false}]},{"StartTime":44066.0,"Objects":[{"StartTime":44066.0,"Position":294.0,"HyperDash":false}]},{"StartTime":44202.0,"Objects":[{"StartTime":44202.0,"Position":204.0,"HyperDash":false},{"StartTime":44338.0,"Position":217.130188,"HyperDash":false}]},{"StartTime":44475.0,"Objects":[{"StartTime":44475.0,"Position":381.0,"HyperDash":false}]},{"StartTime":44748.0,"Objects":[{"StartTime":44748.0,"Position":381.0,"HyperDash":false},{"StartTime":44884.0,"Position":392.2908,"HyperDash":false}]},{"StartTime":45021.0,"Objects":[{"StartTime":45021.0,"Position":500.0,"HyperDash":false},{"StartTime":45157.0,"Position":488.7092,"HyperDash":false}]},{"StartTime":45293.0,"Objects":[{"StartTime":45293.0,"Position":285.0,"HyperDash":false}]},{"StartTime":45430.0,"Objects":[{"StartTime":45430.0,"Position":397.0,"HyperDash":false}]},{"StartTime":45498.0,"Objects":[{"StartTime":45498.0,"Position":397.0,"HyperDash":false}]},{"StartTime":45566.0,"Objects":[{"StartTime":45566.0,"Position":397.0,"HyperDash":false},{"StartTime":45702.0,"Position":385.7092,"HyperDash":false}]},{"StartTime":45839.0,"Objects":[{"StartTime":45839.0,"Position":208.0,"HyperDash":false}]},{"StartTime":45907.0,"Objects":[{"StartTime":45907.0,"Position":208.0,"HyperDash":false}]},{"StartTime":45975.0,"Objects":[{"StartTime":45975.0,"Position":208.0,"HyperDash":false},{"StartTime":46111.0,"Position":131.34523,"HyperDash":false}]},{"StartTime":46248.0,"Objects":[{"StartTime":46248.0,"Position":47.0,"HyperDash":false}]},{"StartTime":46316.0,"Objects":[{"StartTime":46316.0,"Position":54.0,"HyperDash":false}]},{"StartTime":46384.0,"Objects":[{"StartTime":46384.0,"Position":61.0,"HyperDash":false}]},{"StartTime":46521.0,"Objects":[{"StartTime":46521.0,"Position":118.0,"HyperDash":false},{"StartTime":46589.0,"Position":111.337379,"HyperDash":false},{"StartTime":46657.0,"Position":118.0,"HyperDash":false},{"StartTime":46725.0,"Position":111.337379,"HyperDash":false},{"StartTime":46793.0,"Position":118.0,"HyperDash":false},{"StartTime":46861.0,"Position":111.337379,"HyperDash":false}]},{"StartTime":46930.0,"Objects":[{"StartTime":46930.0,"Position":186.0,"HyperDash":false},{"StartTime":47066.0,"Position":274.623718,"HyperDash":false}]},{"StartTime":47202.0,"Objects":[{"StartTime":47202.0,"Position":446.0,"HyperDash":false},{"StartTime":47338.0,"Position":357.889038,"HyperDash":false}]},{"StartTime":47475.0,"Objects":[{"StartTime":47475.0,"Position":367.0,"HyperDash":false},{"StartTime":47611.0,"Position":390.840118,"HyperDash":false}]},{"StartTime":47748.0,"Objects":[{"StartTime":47748.0,"Position":297.0,"HyperDash":false},{"StartTime":47884.0,"Position":319.863068,"HyperDash":false}]},{"StartTime":48021.0,"Objects":[{"StartTime":48021.0,"Position":243.0,"HyperDash":false},{"StartTime":48157.0,"Position":143.595367,"HyperDash":false}]},{"StartTime":48293.0,"Objects":[{"StartTime":48293.0,"Position":188.0,"HyperDash":false}]},{"StartTime":48430.0,"Objects":[{"StartTime":48430.0,"Position":188.0,"HyperDash":false}]},{"StartTime":48566.0,"Objects":[{"StartTime":48566.0,"Position":59.0,"HyperDash":false},{"StartTime":48702.0,"Position":43.64902,"HyperDash":false}]},{"StartTime":48839.0,"Objects":[{"StartTime":48839.0,"Position":174.0,"HyperDash":false},{"StartTime":48975.0,"Position":273.404633,"HyperDash":false}]},{"StartTime":49111.0,"Objects":[{"StartTime":49111.0,"Position":423.0,"HyperDash":false},{"StartTime":49247.0,"Position":415.1793,"HyperDash":false}]},{"StartTime":49384.0,"Objects":[{"StartTime":49384.0,"Position":346.0,"HyperDash":false},{"StartTime":49520.0,"Position":433.371735,"HyperDash":true}]},{"StartTime":49657.0,"Objects":[{"StartTime":49657.0,"Position":217.0,"HyperDash":false}]},{"StartTime":49793.0,"Objects":[{"StartTime":49793.0,"Position":208.0,"HyperDash":false}]},{"StartTime":49861.0,"Objects":[{"StartTime":49861.0,"Position":208.0,"HyperDash":false}]},{"StartTime":49930.0,"Objects":[{"StartTime":49930.0,"Position":208.0,"HyperDash":false},{"StartTime":50066.0,"Position":107.101242,"HyperDash":false}]},{"StartTime":50202.0,"Objects":[{"StartTime":50202.0,"Position":45.0,"HyperDash":false}]},{"StartTime":50338.0,"Objects":[{"StartTime":50338.0,"Position":108.0,"HyperDash":false}]},{"StartTime":50475.0,"Objects":[{"StartTime":50475.0,"Position":107.0,"HyperDash":false}]},{"StartTime":50611.0,"Objects":[{"StartTime":50611.0,"Position":44.0,"HyperDash":false}]},{"StartTime":50748.0,"Objects":[{"StartTime":50748.0,"Position":70.0,"HyperDash":false},{"StartTime":50807.0,"Position":117.635452,"HyperDash":false},{"StartTime":50866.0,"Position":164.2709,"HyperDash":false},{"StartTime":50925.0,"Position":211.774979,"HyperDash":false},{"StartTime":51020.0,"Position":266.492462,"HyperDash":false}]},{"StartTime":51157.0,"Objects":[{"StartTime":51157.0,"Position":441.0,"HyperDash":false}]},{"StartTime":51225.0,"Objects":[{"StartTime":51225.0,"Position":434.0,"HyperDash":false}]},{"StartTime":51293.0,"Objects":[{"StartTime":51293.0,"Position":427.0,"HyperDash":false},{"StartTime":51429.0,"Position":405.05188,"HyperDash":false}]},{"StartTime":51566.0,"Objects":[{"StartTime":51566.0,"Position":482.0,"HyperDash":false},{"StartTime":51702.0,"Position":460.05188,"HyperDash":false}]},{"StartTime":51839.0,"Objects":[{"StartTime":51839.0,"Position":357.0,"HyperDash":false},{"StartTime":51975.0,"Position":265.6038,"HyperDash":false}]},{"StartTime":52111.0,"Objects":[{"StartTime":52111.0,"Position":119.0,"HyperDash":false},{"StartTime":52247.0,"Position":210.2502,"HyperDash":false}]},{"StartTime":52384.0,"Objects":[{"StartTime":52384.0,"Position":164.0,"HyperDash":false},{"StartTime":52520.0,"Position":74.00247,"HyperDash":false}]},{"StartTime":52657.0,"Objects":[{"StartTime":52657.0,"Position":0.0,"HyperDash":false}]},{"StartTime":52793.0,"Objects":[{"StartTime":52793.0,"Position":0.0,"HyperDash":false}]},{"StartTime":52930.0,"Objects":[{"StartTime":52930.0,"Position":124.0,"HyperDash":false},{"StartTime":53066.0,"Position":225.212341,"HyperDash":false}]},{"StartTime":53202.0,"Objects":[{"StartTime":53202.0,"Position":316.0,"HyperDash":false},{"StartTime":53338.0,"Position":303.34845,"HyperDash":false}]},{"StartTime":53475.0,"Objects":[{"StartTime":53475.0,"Position":332.0,"HyperDash":false},{"StartTime":53611.0,"Position":415.923523,"HyperDash":false}]},{"StartTime":53748.0,"Objects":[{"StartTime":53748.0,"Position":512.0,"HyperDash":false},{"StartTime":53884.0,"Position":428.076477,"HyperDash":false}]},{"StartTime":54021.0,"Objects":[{"StartTime":54021.0,"Position":512.0,"HyperDash":false}]},{"StartTime":54157.0,"Objects":[{"StartTime":54157.0,"Position":363.0,"HyperDash":false}]},{"StartTime":54225.0,"Objects":[{"StartTime":54225.0,"Position":363.0,"HyperDash":false}]},{"StartTime":54293.0,"Objects":[{"StartTime":54293.0,"Position":363.0,"HyperDash":false},{"StartTime":54429.0,"Position":262.3189,"HyperDash":false}]},{"StartTime":54566.0,"Objects":[{"StartTime":54566.0,"Position":308.0,"HyperDash":false}]},{"StartTime":54634.0,"Objects":[{"StartTime":54634.0,"Position":269.0,"HyperDash":false}]},{"StartTime":54702.0,"Objects":[{"StartTime":54702.0,"Position":227.0,"HyperDash":false}]},{"StartTime":54770.0,"Objects":[{"StartTime":54770.0,"Position":193.0,"HyperDash":false}]},{"StartTime":54838.0,"Objects":[{"StartTime":54838.0,"Position":175.0,"HyperDash":false}]},{"StartTime":54975.0,"Objects":[{"StartTime":54975.0,"Position":81.0,"HyperDash":false}]},{"StartTime":55043.0,"Objects":[{"StartTime":55043.0,"Position":74.0,"HyperDash":false}]},{"StartTime":55111.0,"Objects":[{"StartTime":55111.0,"Position":67.0,"HyperDash":false}]},{"StartTime":55248.0,"Objects":[{"StartTime":55248.0,"Position":18.0,"HyperDash":false},{"StartTime":55316.0,"Position":25.9951439,"HyperDash":false},{"StartTime":55384.0,"Position":18.0,"HyperDash":false},{"StartTime":55452.0,"Position":25.9951439,"HyperDash":false},{"StartTime":55520.0,"Position":18.0,"HyperDash":false},{"StartTime":55588.0,"Position":25.9951439,"HyperDash":false}]},{"StartTime":55657.0,"Objects":[{"StartTime":55657.0,"Position":87.0,"HyperDash":false},{"StartTime":55725.0,"Position":127.788834,"HyperDash":false},{"StartTime":55793.0,"Position":87.0,"HyperDash":false},{"StartTime":55861.0,"Position":127.788834,"HyperDash":false}]},{"StartTime":55929.0,"Objects":[{"StartTime":55929.0,"Position":175.0,"HyperDash":false},{"StartTime":55997.0,"Position":215.604614,"HyperDash":false},{"StartTime":56065.0,"Position":175.0,"HyperDash":false},{"StartTime":56133.0,"Position":215.604614,"HyperDash":false}]},{"StartTime":56202.0,"Objects":[{"StartTime":56202.0,"Position":295.0,"HyperDash":false},{"StartTime":56270.0,"Position":307.21228,"HyperDash":false},{"StartTime":56338.0,"Position":295.0,"HyperDash":false},{"StartTime":56406.0,"Position":307.21228,"HyperDash":false}]},{"StartTime":56475.0,"Objects":[{"StartTime":56475.0,"Position":265.0,"HyperDash":false},{"StartTime":56543.0,"Position":252.78772,"HyperDash":false},{"StartTime":56611.0,"Position":265.0,"HyperDash":false},{"StartTime":56679.0,"Position":252.78772,"HyperDash":false}]},{"StartTime":56748.0,"Objects":[{"StartTime":56748.0,"Position":327.0,"HyperDash":false}]},{"StartTime":56816.0,"Objects":[{"StartTime":56816.0,"Position":336.0,"HyperDash":false}]},{"StartTime":56884.0,"Objects":[{"StartTime":56884.0,"Position":345.0,"HyperDash":false}]},{"StartTime":57021.0,"Objects":[{"StartTime":57021.0,"Position":414.0,"HyperDash":false}]},{"StartTime":57089.0,"Objects":[{"StartTime":57089.0,"Position":423.0,"HyperDash":false}]},{"StartTime":57157.0,"Objects":[{"StartTime":57157.0,"Position":432.0,"HyperDash":false}]},{"StartTime":57293.0,"Objects":[{"StartTime":57293.0,"Position":502.0,"HyperDash":false},{"StartTime":57361.0,"Position":489.78772,"HyperDash":false},{"StartTime":57429.0,"Position":502.0,"HyperDash":false}]},{"StartTime":57566.0,"Objects":[{"StartTime":57566.0,"Position":431.0,"HyperDash":false},{"StartTime":57634.0,"Position":443.21228,"HyperDash":false},{"StartTime":57702.0,"Position":431.0,"HyperDash":false}]},{"StartTime":57839.0,"Objects":[{"StartTime":57839.0,"Position":356.0,"HyperDash":false},{"StartTime":57907.0,"Position":343.78772,"HyperDash":false},{"StartTime":57975.0,"Position":356.0,"HyperDash":false},{"StartTime":58043.0,"Position":343.78772,"HyperDash":false}]},{"StartTime":58112.0,"Objects":[{"StartTime":58112.0,"Position":294.0,"HyperDash":false},{"StartTime":58180.0,"Position":334.7076,"HyperDash":false},{"StartTime":58248.0,"Position":294.0,"HyperDash":false},{"StartTime":58316.0,"Position":334.7076,"HyperDash":false}]},{"StartTime":58384.0,"Objects":[{"StartTime":58384.0,"Position":205.0,"HyperDash":false},{"StartTime":58452.0,"Position":192.78772,"HyperDash":false},{"StartTime":58520.0,"Position":205.0,"HyperDash":false},{"StartTime":58588.0,"Position":192.78772,"HyperDash":false}]},{"StartTime":58657.0,"Objects":[{"StartTime":58657.0,"Position":151.0,"HyperDash":false},{"StartTime":58725.0,"Position":110.292381,"HyperDash":false},{"StartTime":58793.0,"Position":151.0,"HyperDash":false},{"StartTime":58861.0,"Position":110.292381,"HyperDash":false}]},{"StartTime":58930.0,"Objects":[{"StartTime":58930.0,"Position":21.0,"HyperDash":false}]},{"StartTime":58998.0,"Objects":[{"StartTime":58998.0,"Position":18.0,"HyperDash":false}]},{"StartTime":59066.0,"Objects":[{"StartTime":59066.0,"Position":15.0,"HyperDash":false}]},{"StartTime":59202.0,"Objects":[{"StartTime":59202.0,"Position":96.0,"HyperDash":false}]},{"StartTime":59270.0,"Objects":[{"StartTime":59270.0,"Position":93.0,"HyperDash":false}]},{"StartTime":59338.0,"Objects":[{"StartTime":59338.0,"Position":90.0,"HyperDash":false}]},{"StartTime":59475.0,"Objects":[{"StartTime":59475.0,"Position":38.0,"HyperDash":false}]},{"StartTime":59543.0,"Objects":[{"StartTime":59543.0,"Position":41.0,"HyperDash":false}]},{"StartTime":59611.0,"Objects":[{"StartTime":59611.0,"Position":44.0,"HyperDash":false},{"StartTime":59747.0,"Position":35.8773422,"HyperDash":false}]},{"StartTime":60021.0,"Objects":[{"StartTime":60021.0,"Position":227.0,"HyperDash":false},{"StartTime":60089.0,"Position":214.78772,"HyperDash":false},{"StartTime":60157.0,"Position":227.0,"HyperDash":false},{"StartTime":60225.0,"Position":214.78772,"HyperDash":false}]},{"StartTime":60294.0,"Objects":[{"StartTime":60294.0,"Position":257.0,"HyperDash":false},{"StartTime":60362.0,"Position":269.21228,"HyperDash":false},{"StartTime":60430.0,"Position":257.0,"HyperDash":false},{"StartTime":60498.0,"Position":269.21228,"HyperDash":false}]},{"StartTime":60566.0,"Objects":[{"StartTime":60566.0,"Position":357.0,"HyperDash":false},{"StartTime":60634.0,"Position":397.788849,"HyperDash":false},{"StartTime":60702.0,"Position":357.0,"HyperDash":false},{"StartTime":60770.0,"Position":397.788849,"HyperDash":false}]},{"StartTime":60838.0,"Objects":[{"StartTime":60838.0,"Position":445.0,"HyperDash":false},{"StartTime":60906.0,"Position":485.6046,"HyperDash":false},{"StartTime":60974.0,"Position":445.0,"HyperDash":false},{"StartTime":61042.0,"Position":485.6046,"HyperDash":false}]},{"StartTime":61111.0,"Objects":[{"StartTime":61111.0,"Position":496.0,"HyperDash":false}]},{"StartTime":61179.0,"Objects":[{"StartTime":61179.0,"Position":493.0,"HyperDash":false}]},{"StartTime":61247.0,"Objects":[{"StartTime":61247.0,"Position":490.0,"HyperDash":false}]},{"StartTime":61384.0,"Objects":[{"StartTime":61384.0,"Position":420.0,"HyperDash":false}]},{"StartTime":61452.0,"Objects":[{"StartTime":61452.0,"Position":417.0,"HyperDash":false}]},{"StartTime":61521.0,"Objects":[{"StartTime":61521.0,"Position":414.0,"HyperDash":false}]},{"StartTime":61657.0,"Objects":[{"StartTime":61657.0,"Position":389.0,"HyperDash":false},{"StartTime":61725.0,"Position":348.2924,"HyperDash":false},{"StartTime":61793.0,"Position":389.0,"HyperDash":false}]},{"StartTime":61930.0,"Objects":[{"StartTime":61930.0,"Position":277.0,"HyperDash":false},{"StartTime":61998.0,"Position":236.292389,"HyperDash":false},{"StartTime":62066.0,"Position":277.0,"HyperDash":false}]},{"StartTime":62202.0,"Objects":[{"StartTime":62202.0,"Position":161.0,"HyperDash":false},{"StartTime":62270.0,"Position":148.78772,"HyperDash":false},{"StartTime":62338.0,"Position":161.0,"HyperDash":false},{"StartTime":62406.0,"Position":148.78772,"HyperDash":false}]},{"StartTime":62475.0,"Objects":[{"StartTime":62475.0,"Position":142.0,"HyperDash":false},{"StartTime":62543.0,"Position":101.292381,"HyperDash":false},{"StartTime":62611.0,"Position":142.0,"HyperDash":false},{"StartTime":62679.0,"Position":101.292381,"HyperDash":false}]},{"StartTime":62748.0,"Objects":[{"StartTime":62748.0,"Position":2.0,"HyperDash":false},{"StartTime":62816.0,"Position":14.212285,"HyperDash":false},{"StartTime":62884.0,"Position":2.0,"HyperDash":false},{"StartTime":62952.0,"Position":14.212285,"HyperDash":false}]},{"StartTime":63021.0,"Objects":[{"StartTime":63021.0,"Position":0.0,"HyperDash":false},{"StartTime":63089.0,"Position":40.70762,"HyperDash":false},{"StartTime":63157.0,"Position":0.0,"HyperDash":false},{"StartTime":63225.0,"Position":40.70762,"HyperDash":false}]},{"StartTime":63293.0,"Objects":[{"StartTime":63293.0,"Position":95.0,"HyperDash":false}]},{"StartTime":63361.0,"Objects":[{"StartTime":63361.0,"Position":104.0,"HyperDash":false}]},{"StartTime":63429.0,"Objects":[{"StartTime":63429.0,"Position":113.0,"HyperDash":false}]},{"StartTime":63566.0,"Objects":[{"StartTime":63566.0,"Position":189.0,"HyperDash":false}]},{"StartTime":63634.0,"Objects":[{"StartTime":63634.0,"Position":198.0,"HyperDash":false}]},{"StartTime":63702.0,"Objects":[{"StartTime":63702.0,"Position":207.0,"HyperDash":false}]},{"StartTime":63839.0,"Objects":[{"StartTime":63839.0,"Position":281.0,"HyperDash":false},{"StartTime":63907.0,"Position":322.273315,"HyperDash":false},{"StartTime":63975.0,"Position":281.0,"HyperDash":false},{"StartTime":64043.0,"Position":322.273315,"HyperDash":false}]},{"StartTime":64111.0,"Objects":[{"StartTime":64111.0,"Position":362.0,"HyperDash":false},{"StartTime":64179.0,"Position":403.273315,"HyperDash":false},{"StartTime":64247.0,"Position":362.0,"HyperDash":false},{"StartTime":64315.0,"Position":403.273315,"HyperDash":false}]},{"StartTime":64384.0,"Objects":[{"StartTime":64384.0,"Position":478.0,"HyperDash":false},{"StartTime":64443.0,"Position":442.243439,"HyperDash":false},{"StartTime":64502.0,"Position":440.1484,"HyperDash":false},{"StartTime":64561.0,"Position":427.0997,"HyperDash":false},{"StartTime":64656.0,"Position":444.9422,"HyperDash":false}]},{"StartTime":64930.0,"Objects":[{"StartTime":64930.0,"Position":485.0,"HyperDash":false},{"StartTime":64989.0,"Position":461.072876,"HyperDash":false},{"StartTime":65048.0,"Position":436.145752,"HyperDash":false},{"StartTime":65107.0,"Position":402.2186,"HyperDash":false},{"StartTime":65202.0,"Position":351.641022,"HyperDash":false}]},{"StartTime":65475.0,"Objects":[{"StartTime":65475.0,"Position":222.0,"HyperDash":false},{"StartTime":65534.0,"Position":184.205688,"HyperDash":false},{"StartTime":65593.0,"Position":161.582535,"HyperDash":false},{"StartTime":65652.0,"Position":155.982361,"HyperDash":false},{"StartTime":65747.0,"Position":104.778061,"HyperDash":false}]},{"StartTime":65884.0,"Objects":[{"StartTime":65884.0,"Position":104.0,"HyperDash":false}]},{"StartTime":66021.0,"Objects":[{"StartTime":66021.0,"Position":16.0,"HyperDash":false},{"StartTime":66157.0,"Position":28.7026157,"HyperDash":false}]},{"StartTime":66225.0,"Objects":[{"StartTime":66225.0,"Position":28.0,"HyperDash":false}]},{"StartTime":66293.0,"Objects":[{"StartTime":66293.0,"Position":28.0,"HyperDash":false}]},{"StartTime":66566.0,"Objects":[{"StartTime":66566.0,"Position":90.0,"HyperDash":false},{"StartTime":66702.0,"Position":76.934906,"HyperDash":false}]},{"StartTime":66839.0,"Objects":[{"StartTime":66839.0,"Position":256.0,"HyperDash":false},{"StartTime":66975.0,"Position":242.9349,"HyperDash":false}]},{"StartTime":67111.0,"Objects":[{"StartTime":67111.0,"Position":186.0,"HyperDash":false}]},{"StartTime":67248.0,"Objects":[{"StartTime":67248.0,"Position":273.0,"HyperDash":false}]},{"StartTime":67316.0,"Objects":[{"StartTime":67316.0,"Position":273.0,"HyperDash":false}]},{"StartTime":67384.0,"Objects":[{"StartTime":67384.0,"Position":273.0,"HyperDash":false},{"StartTime":67520.0,"Position":357.364716,"HyperDash":false}]},{"StartTime":67657.0,"Objects":[{"StartTime":67657.0,"Position":471.0,"HyperDash":false}]},{"StartTime":67793.0,"Objects":[{"StartTime":67793.0,"Position":471.0,"HyperDash":false}]},{"StartTime":67930.0,"Objects":[{"StartTime":67930.0,"Position":392.0,"HyperDash":false},{"StartTime":68066.0,"Position":307.582184,"HyperDash":false}]},{"StartTime":68202.0,"Objects":[{"StartTime":68202.0,"Position":165.0,"HyperDash":false},{"StartTime":68338.0,"Position":178.0651,"HyperDash":false}]},{"StartTime":68475.0,"Objects":[{"StartTime":68475.0,"Position":266.0,"HyperDash":false},{"StartTime":68543.0,"Position":307.8938,"HyperDash":false},{"StartTime":68611.0,"Position":266.0,"HyperDash":false},{"StartTime":68679.0,"Position":307.8938,"HyperDash":false}]},{"StartTime":68748.0,"Objects":[{"StartTime":68748.0,"Position":358.0,"HyperDash":false},{"StartTime":68807.0,"Position":396.968262,"HyperDash":false},{"StartTime":68866.0,"Position":418.199738,"HyperDash":false},{"StartTime":68925.0,"Position":452.599548,"HyperDash":false},{"StartTime":69020.0,"Position":484.638855,"HyperDash":false}]},{"StartTime":69293.0,"Objects":[{"StartTime":69293.0,"Position":447.0,"HyperDash":false},{"StartTime":69352.0,"Position":453.674744,"HyperDash":false},{"StartTime":69411.0,"Position":437.3495,"HyperDash":false},{"StartTime":69470.0,"Position":444.024231,"HyperDash":false},{"StartTime":69565.0,"Position":468.551361,"HyperDash":false}]},{"StartTime":69839.0,"Objects":[{"StartTime":69839.0,"Position":343.0,"HyperDash":false},{"StartTime":69898.0,"Position":329.563446,"HyperDash":false},{"StartTime":69957.0,"Position":311.8805,"HyperDash":false},{"StartTime":70016.0,"Position":271.0514,"HyperDash":false},{"StartTime":70111.0,"Position":243.183487,"HyperDash":false}]},{"StartTime":70248.0,"Objects":[{"StartTime":70248.0,"Position":216.0,"HyperDash":false}]},{"StartTime":70316.0,"Objects":[{"StartTime":70316.0,"Position":216.0,"HyperDash":false}]},{"StartTime":70384.0,"Objects":[{"StartTime":70384.0,"Position":216.0,"HyperDash":false},{"StartTime":70520.0,"Position":154.538864,"HyperDash":false}]},{"StartTime":70657.0,"Objects":[{"StartTime":70657.0,"Position":58.0,"HyperDash":false}]},{"StartTime":70930.0,"Objects":[{"StartTime":70930.0,"Position":58.0,"HyperDash":false},{"StartTime":71066.0,"Position":48.7692032,"HyperDash":false}]},{"StartTime":71202.0,"Objects":[{"StartTime":71202.0,"Position":129.0,"HyperDash":false},{"StartTime":71338.0,"Position":138.2308,"HyperDash":false}]},{"StartTime":71475.0,"Objects":[{"StartTime":71475.0,"Position":132.0,"HyperDash":false}]},{"StartTime":71611.0,"Objects":[{"StartTime":71611.0,"Position":228.0,"HyperDash":false}]},{"StartTime":71680.0,"Objects":[{"StartTime":71680.0,"Position":228.0,"HyperDash":false}]},{"StartTime":71748.0,"Objects":[{"StartTime":71748.0,"Position":228.0,"HyperDash":false},{"StartTime":71884.0,"Position":312.5163,"HyperDash":false}]},{"StartTime":72021.0,"Objects":[{"StartTime":72021.0,"Position":382.0,"HyperDash":false}]},{"StartTime":72089.0,"Objects":[{"StartTime":72089.0,"Position":414.0,"HyperDash":false}]},{"StartTime":72157.0,"Objects":[{"StartTime":72157.0,"Position":448.0,"HyperDash":false}]},{"StartTime":72225.0,"Objects":[{"StartTime":72225.0,"Position":478.0,"HyperDash":false}]},{"StartTime":72293.0,"Objects":[{"StartTime":72293.0,"Position":500.0,"HyperDash":false}]},{"StartTime":72430.0,"Objects":[{"StartTime":72430.0,"Position":453.0,"HyperDash":false}]},{"StartTime":72498.0,"Objects":[{"StartTime":72498.0,"Position":449.0,"HyperDash":false}]},{"StartTime":72566.0,"Objects":[{"StartTime":72566.0,"Position":445.0,"HyperDash":false},{"StartTime":72634.0,"Position":427.8085,"HyperDash":false},{"StartTime":72702.0,"Position":445.0,"HyperDash":false}]},{"StartTime":72839.0,"Objects":[{"StartTime":72839.0,"Position":486.0,"HyperDash":false},{"StartTime":72907.0,"Position":502.9824,"HyperDash":false}]},{"StartTime":72975.0,"Objects":[{"StartTime":72975.0,"Position":414.0,"HyperDash":false},{"StartTime":73043.0,"Position":430.9824,"HyperDash":false}]},{"StartTime":73111.0,"Objects":[{"StartTime":73111.0,"Position":344.0,"HyperDash":false}]},{"StartTime":75293.0,"Objects":[{"StartTime":75293.0,"Position":62.0,"HyperDash":false}]},{"StartTime":76930.0,"Objects":[{"StartTime":76930.0,"Position":403.0,"HyperDash":false},{"StartTime":77020.0,"Position":467.2785,"HyperDash":false},{"StartTime":77111.0,"Position":403.0,"HyperDash":false},{"StartTime":77202.0,"Position":467.2785,"HyperDash":false},{"StartTime":77293.0,"Position":403.0,"HyperDash":false},{"StartTime":77384.0,"Position":467.2785,"HyperDash":false}]},{"StartTime":77475.0,"Objects":[{"StartTime":77475.0,"Position":412.0,"HyperDash":false},{"StartTime":77565.0,"Position":439.85,"HyperDash":false},{"StartTime":77656.0,"Position":412.0,"HyperDash":false}]},{"StartTime":77748.0,"Objects":[{"StartTime":77748.0,"Position":320.0,"HyperDash":false},{"StartTime":77838.0,"Position":313.270081,"HyperDash":false},{"StartTime":77929.0,"Position":320.0,"HyperDash":false}]},{"StartTime":78021.0,"Objects":[{"StartTime":78021.0,"Position":248.0,"HyperDash":false},{"StartTime":78111.0,"Position":275.85,"HyperDash":false},{"StartTime":78202.0,"Position":248.0,"HyperDash":false}]},{"StartTime":78294.0,"Objects":[{"StartTime":78294.0,"Position":156.0,"HyperDash":false},{"StartTime":78384.0,"Position":149.56723,"HyperDash":false},{"StartTime":78475.0,"Position":156.0,"HyperDash":false}]},{"StartTime":78566.0,"Objects":[{"StartTime":78566.0,"Position":97.0,"HyperDash":false}]},{"StartTime":78657.0,"Objects":[{"StartTime":78657.0,"Position":89.0,"HyperDash":false},{"StartTime":78747.0,"Position":22.422142,"HyperDash":false}]},{"StartTime":78839.0,"Objects":[{"StartTime":78839.0,"Position":10.0,"HyperDash":false}]},{"StartTime":78930.0,"Objects":[{"StartTime":78930.0,"Position":52.0,"HyperDash":false}]},{"StartTime":79021.0,"Objects":[{"StartTime":79021.0,"Position":106.0,"HyperDash":false}]},{"StartTime":79111.0,"Objects":[{"StartTime":79111.0,"Position":154.0,"HyperDash":false},{"StartTime":79170.0,"Position":200.598,"HyperDash":false},{"StartTime":79229.0,"Position":235.269073,"HyperDash":false},{"StartTime":79288.0,"Position":279.5065,"HyperDash":false},{"StartTime":79383.0,"Position":258.247284,"HyperDash":false}]},{"StartTime":79657.0,"Objects":[{"StartTime":79657.0,"Position":258.0,"HyperDash":false},{"StartTime":79747.0,"Position":190.279266,"HyperDash":false},{"StartTime":79838.0,"Position":258.0,"HyperDash":false}]},{"StartTime":79930.0,"Objects":[{"StartTime":79930.0,"Position":226.0,"HyperDash":false},{"StartTime":80020.0,"Position":158.966843,"HyperDash":false},{"StartTime":80111.0,"Position":226.0,"HyperDash":false}]},{"StartTime":80202.0,"Objects":[{"StartTime":80202.0,"Position":287.0,"HyperDash":false},{"StartTime":80292.0,"Position":354.0113,"HyperDash":false},{"StartTime":80383.0,"Position":287.0,"HyperDash":false}]},{"StartTime":80475.0,"Objects":[{"StartTime":80475.0,"Position":293.0,"HyperDash":false},{"StartTime":80565.0,"Position":354.718628,"HyperDash":false},{"StartTime":80656.0,"Position":293.0,"HyperDash":false}]},{"StartTime":80748.0,"Objects":[{"StartTime":80748.0,"Position":218.0,"HyperDash":false}]},{"StartTime":80839.0,"Objects":[{"StartTime":80839.0,"Position":209.0,"HyperDash":false},{"StartTime":80929.0,"Position":195.476837,"HyperDash":false}]},{"StartTime":81021.0,"Objects":[{"StartTime":81021.0,"Position":256.0,"HyperDash":false}]},{"StartTime":81111.0,"Objects":[{"StartTime":81111.0,"Position":299.0,"HyperDash":false}]},{"StartTime":81202.0,"Objects":[{"StartTime":81202.0,"Position":352.0,"HyperDash":false}]},{"StartTime":81293.0,"Objects":[{"StartTime":81293.0,"Position":398.0,"HyperDash":false},{"StartTime":81352.0,"Position":388.6871,"HyperDash":false},{"StartTime":81411.0,"Position":437.698456,"HyperDash":false},{"StartTime":81470.0,"Position":430.344421,"HyperDash":false},{"StartTime":81565.0,"Position":462.164764,"HyperDash":false}]},{"StartTime":81839.0,"Objects":[{"StartTime":81839.0,"Position":462.0,"HyperDash":false},{"StartTime":81929.0,"Position":398.4922,"HyperDash":false},{"StartTime":82020.0,"Position":462.0,"HyperDash":false}]},{"StartTime":82111.0,"Objects":[{"StartTime":82111.0,"Position":347.0,"HyperDash":false},{"StartTime":82201.0,"Position":301.8704,"HyperDash":false},{"StartTime":82292.0,"Position":347.0,"HyperDash":false}]},{"StartTime":82384.0,"Objects":[{"StartTime":82384.0,"Position":368.0,"HyperDash":false},{"StartTime":82474.0,"Position":323.2633,"HyperDash":false},{"StartTime":82565.0,"Position":368.0,"HyperDash":false}]},{"StartTime":82657.0,"Objects":[{"StartTime":82657.0,"Position":238.0,"HyperDash":false},{"StartTime":82747.0,"Position":223.616516,"HyperDash":false},{"StartTime":82838.0,"Position":238.0,"HyperDash":false}]},{"StartTime":82930.0,"Objects":[{"StartTime":82930.0,"Position":135.0,"HyperDash":false}]},{"StartTime":83021.0,"Objects":[{"StartTime":83021.0,"Position":139.0,"HyperDash":false},{"StartTime":83111.0,"Position":190.412811,"HyperDash":false}]},{"StartTime":83202.0,"Objects":[{"StartTime":83202.0,"Position":41.0,"HyperDash":false}]},{"StartTime":83293.0,"Objects":[{"StartTime":83293.0,"Position":83.0,"HyperDash":false}]},{"StartTime":83384.0,"Objects":[{"StartTime":83384.0,"Position":103.0,"HyperDash":false}]},{"StartTime":83475.0,"Objects":[{"StartTime":83475.0,"Position":99.0,"HyperDash":false},{"StartTime":83534.0,"Position":103.780617,"HyperDash":false},{"StartTime":83593.0,"Position":126.401306,"HyperDash":false},{"StartTime":83652.0,"Position":141.544952,"HyperDash":false},{"StartTime":83747.0,"Position":219.928558,"HyperDash":false}]},{"StartTime":84021.0,"Objects":[{"StartTime":84021.0,"Position":219.0,"HyperDash":false},{"StartTime":84111.0,"Position":155.1237,"HyperDash":false},{"StartTime":84202.0,"Position":219.0,"HyperDash":false}]},{"StartTime":84293.0,"Objects":[{"StartTime":84293.0,"Position":237.0,"HyperDash":false},{"StartTime":84383.0,"Position":181.530167,"HyperDash":false},{"StartTime":84474.0,"Position":237.0,"HyperDash":false}]},{"StartTime":84566.0,"Objects":[{"StartTime":84566.0,"Position":291.0,"HyperDash":false},{"StartTime":84656.0,"Position":354.876282,"HyperDash":false},{"StartTime":84747.0,"Position":291.0,"HyperDash":false}]},{"StartTime":84839.0,"Objects":[{"StartTime":84839.0,"Position":273.0,"HyperDash":false},{"StartTime":84929.0,"Position":328.1262,"HyperDash":false},{"StartTime":85020.0,"Position":273.0,"HyperDash":false}]},{"StartTime":85111.0,"Objects":[{"StartTime":85111.0,"Position":210.0,"HyperDash":false}]},{"StartTime":85202.0,"Objects":[{"StartTime":85202.0,"Position":199.0,"HyperDash":false},{"StartTime":85292.0,"Position":175.375092,"HyperDash":false}]},{"StartTime":85384.0,"Objects":[{"StartTime":85384.0,"Position":227.0,"HyperDash":false}]},{"StartTime":85475.0,"Objects":[{"StartTime":85475.0,"Position":280.0,"HyperDash":false}]},{"StartTime":85566.0,"Objects":[{"StartTime":85566.0,"Position":326.0,"HyperDash":false}]},{"StartTime":85657.0,"Objects":[{"StartTime":85657.0,"Position":380.0,"HyperDash":false},{"StartTime":85708.0,"Position":410.039581,"HyperDash":false},{"StartTime":85759.0,"Position":454.079163,"HyperDash":false},{"StartTime":85810.0,"Position":496.148,"HyperDash":false},{"StartTime":85861.0,"Position":512.0,"HyperDash":false},{"StartTime":85945.0,"Position":452.8782,"HyperDash":false},{"StartTime":86066.0,"Position":380.0,"HyperDash":false}]},{"StartTime":86202.0,"Objects":[{"StartTime":86202.0,"Position":414.0,"HyperDash":false},{"StartTime":86270.0,"Position":406.751984,"HyperDash":false},{"StartTime":86338.0,"Position":414.0,"HyperDash":false},{"StartTime":86406.0,"Position":406.751984,"HyperDash":false}]},{"StartTime":86475.0,"Objects":[{"StartTime":86475.0,"Position":313.0,"HyperDash":false},{"StartTime":86543.0,"Position":320.248016,"HyperDash":false},{"StartTime":86611.0,"Position":313.0,"HyperDash":false},{"StartTime":86679.0,"Position":320.248016,"HyperDash":false}]},{"StartTime":86748.0,"Objects":[{"StartTime":86748.0,"Position":229.0,"HyperDash":false},{"StartTime":86816.0,"Position":236.248016,"HyperDash":false}]},{"StartTime":86884.0,"Objects":[{"StartTime":86884.0,"Position":140.0,"HyperDash":false},{"StartTime":86952.0,"Position":147.248016,"HyperDash":false}]},{"StartTime":87021.0,"Objects":[{"StartTime":87021.0,"Position":51.0,"HyperDash":false},{"StartTime":87089.0,"Position":58.2480125,"HyperDash":false},{"StartTime":87157.0,"Position":51.0,"HyperDash":false},{"StartTime":87225.0,"Position":58.2480125,"HyperDash":false}]},{"StartTime":87293.0,"Objects":[{"StartTime":87293.0,"Position":41.0,"HyperDash":false},{"StartTime":87361.0,"Position":0.0,"HyperDash":false},{"StartTime":87429.0,"Position":41.0,"HyperDash":false}]},{"StartTime":87566.0,"Objects":[{"StartTime":87566.0,"Position":111.0,"HyperDash":false}]},{"StartTime":87634.0,"Objects":[{"StartTime":87634.0,"Position":119.0,"HyperDash":false}]},{"StartTime":87702.0,"Objects":[{"StartTime":87702.0,"Position":127.0,"HyperDash":false}]},{"StartTime":87839.0,"Objects":[{"StartTime":87839.0,"Position":152.0,"HyperDash":false},{"StartTime":87907.0,"Position":110.122604,"HyperDash":false},{"StartTime":87975.0,"Position":152.0,"HyperDash":false}]},{"StartTime":88112.0,"Objects":[{"StartTime":88112.0,"Position":222.0,"HyperDash":false}]},{"StartTime":88180.0,"Objects":[{"StartTime":88180.0,"Position":230.0,"HyperDash":false}]},{"StartTime":88248.0,"Objects":[{"StartTime":88248.0,"Position":238.0,"HyperDash":false}]},{"StartTime":88384.0,"Objects":[{"StartTime":88384.0,"Position":295.0,"HyperDash":false},{"StartTime":88452.0,"Position":336.8774,"HyperDash":false},{"StartTime":88520.0,"Position":295.0,"HyperDash":false},{"StartTime":88588.0,"Position":336.8774,"HyperDash":false}]},{"StartTime":88657.0,"Objects":[{"StartTime":88657.0,"Position":334.0,"HyperDash":false},{"StartTime":88725.0,"Position":375.8774,"HyperDash":false},{"StartTime":88793.0,"Position":334.0,"HyperDash":false},{"StartTime":88861.0,"Position":375.8774,"HyperDash":false}]},{"StartTime":88930.0,"Objects":[{"StartTime":88930.0,"Position":464.0,"HyperDash":false},{"StartTime":88998.0,"Position":471.248016,"HyperDash":false}]},{"StartTime":89066.0,"Objects":[{"StartTime":89066.0,"Position":449.0,"HyperDash":false},{"StartTime":89134.0,"Position":456.248016,"HyperDash":false}]},{"StartTime":89202.0,"Objects":[{"StartTime":89202.0,"Position":434.0,"HyperDash":false},{"StartTime":89270.0,"Position":441.248016,"HyperDash":false},{"StartTime":89338.0,"Position":434.0,"HyperDash":false},{"StartTime":89406.0,"Position":441.248016,"HyperDash":false}]},{"StartTime":89475.0,"Objects":[{"StartTime":89475.0,"Position":362.0,"HyperDash":false}]},{"StartTime":89543.0,"Objects":[{"StartTime":89543.0,"Position":360.0,"HyperDash":false}]},{"StartTime":89611.0,"Objects":[{"StartTime":89611.0,"Position":358.0,"HyperDash":false}]},{"StartTime":89748.0,"Objects":[{"StartTime":89748.0,"Position":288.0,"HyperDash":false}]},{"StartTime":89816.0,"Objects":[{"StartTime":89816.0,"Position":286.0,"HyperDash":false}]},{"StartTime":89884.0,"Objects":[{"StartTime":89884.0,"Position":284.0,"HyperDash":false}]},{"StartTime":90021.0,"Objects":[{"StartTime":90021.0,"Position":201.0,"HyperDash":false}]},{"StartTime":90089.0,"Objects":[{"StartTime":90089.0,"Position":193.0,"HyperDash":false}]},{"StartTime":90158.0,"Objects":[{"StartTime":90158.0,"Position":185.0,"HyperDash":false},{"StartTime":90294.0,"Position":100.560234,"HyperDash":false}]},{"StartTime":90566.0,"Objects":[{"StartTime":90566.0,"Position":67.0,"HyperDash":false},{"StartTime":90634.0,"Position":25.1226,"HyperDash":false},{"StartTime":90702.0,"Position":67.0,"HyperDash":false},{"StartTime":90770.0,"Position":25.1226,"HyperDash":false}]},{"StartTime":90839.0,"Objects":[{"StartTime":90839.0,"Position":50.0,"HyperDash":false},{"StartTime":90907.0,"Position":8.122601,"HyperDash":false},{"StartTime":90975.0,"Position":50.0,"HyperDash":false},{"StartTime":91043.0,"Position":8.122601,"HyperDash":false}]},{"StartTime":91111.0,"Objects":[{"StartTime":91111.0,"Position":147.0,"HyperDash":false},{"StartTime":91179.0,"Position":139.751984,"HyperDash":false}]},{"StartTime":91247.0,"Objects":[{"StartTime":91247.0,"Position":236.0,"HyperDash":false},{"StartTime":91315.0,"Position":228.751984,"HyperDash":false}]},{"StartTime":91384.0,"Objects":[{"StartTime":91384.0,"Position":325.0,"HyperDash":false},{"StartTime":91452.0,"Position":317.751984,"HyperDash":false},{"StartTime":91520.0,"Position":325.0,"HyperDash":false},{"StartTime":91588.0,"Position":317.751984,"HyperDash":false}]},{"StartTime":91657.0,"Objects":[{"StartTime":91657.0,"Position":257.0,"HyperDash":false},{"StartTime":91725.0,"Position":249.751984,"HyperDash":false},{"StartTime":91793.0,"Position":257.0,"HyperDash":false}]},{"StartTime":91930.0,"Objects":[{"StartTime":91930.0,"Position":154.0,"HyperDash":false}]},{"StartTime":91998.0,"Objects":[{"StartTime":91998.0,"Position":156.0,"HyperDash":false}]},{"StartTime":92066.0,"Objects":[{"StartTime":92066.0,"Position":158.0,"HyperDash":false}]},{"StartTime":92203.0,"Objects":[{"StartTime":92203.0,"Position":231.0,"HyperDash":false},{"StartTime":92271.0,"Position":238.248016,"HyperDash":false},{"StartTime":92339.0,"Position":231.0,"HyperDash":false}]},{"StartTime":92476.0,"Objects":[{"StartTime":92476.0,"Position":327.0,"HyperDash":false}]},{"StartTime":92544.0,"Objects":[{"StartTime":92544.0,"Position":329.0,"HyperDash":false}]},{"StartTime":92612.0,"Objects":[{"StartTime":92612.0,"Position":331.0,"HyperDash":false}]},{"StartTime":92748.0,"Objects":[{"StartTime":92748.0,"Position":431.0,"HyperDash":false},{"StartTime":92816.0,"Position":423.751984,"HyperDash":false},{"StartTime":92884.0,"Position":431.0,"HyperDash":false},{"StartTime":92952.0,"Position":423.751984,"HyperDash":false}]},{"StartTime":93021.0,"Objects":[{"StartTime":93021.0,"Position":503.0,"HyperDash":false},{"StartTime":93089.0,"Position":495.047729,"HyperDash":false},{"StartTime":93157.0,"Position":503.0,"HyperDash":false},{"StartTime":93225.0,"Position":495.047729,"HyperDash":false}]},{"StartTime":93293.0,"Objects":[{"StartTime":93293.0,"Position":457.0,"HyperDash":false},{"StartTime":93361.0,"Position":498.8774,"HyperDash":false}]},{"StartTime":93429.0,"Objects":[{"StartTime":93429.0,"Position":371.0,"HyperDash":false},{"StartTime":93497.0,"Position":412.8774,"HyperDash":false}]},{"StartTime":93566.0,"Objects":[{"StartTime":93566.0,"Position":286.0,"HyperDash":false},{"StartTime":93634.0,"Position":327.8774,"HyperDash":false},{"StartTime":93702.0,"Position":286.0,"HyperDash":false},{"StartTime":93770.0,"Position":327.8774,"HyperDash":false}]},{"StartTime":93839.0,"Objects":[{"StartTime":93839.0,"Position":195.0,"HyperDash":false}]},{"StartTime":93907.0,"Objects":[{"StartTime":93907.0,"Position":193.0,"HyperDash":false}]},{"StartTime":93975.0,"Objects":[{"StartTime":93975.0,"Position":191.0,"HyperDash":false}]},{"StartTime":94112.0,"Objects":[{"StartTime":94112.0,"Position":118.0,"HyperDash":false}]},{"StartTime":94180.0,"Objects":[{"StartTime":94180.0,"Position":120.0,"HyperDash":false}]},{"StartTime":94248.0,"Objects":[{"StartTime":94248.0,"Position":122.0,"HyperDash":false}]},{"StartTime":94385.0,"Objects":[{"StartTime":94385.0,"Position":145.0,"HyperDash":false}]},{"StartTime":94453.0,"Objects":[{"StartTime":94453.0,"Position":143.0,"HyperDash":false}]},{"StartTime":94522.0,"Objects":[{"StartTime":94522.0,"Position":141.0,"HyperDash":false},{"StartTime":94658.0,"Position":150.743042,"HyperDash":false}]},{"StartTime":94930.0,"Objects":[{"StartTime":94930.0,"Position":48.0,"HyperDash":false}]},{"StartTime":94998.0,"Objects":[{"StartTime":94998.0,"Position":41.0,"HyperDash":false}]},{"StartTime":95066.0,"Objects":[{"StartTime":95066.0,"Position":34.0,"HyperDash":false},{"StartTime":95134.0,"Position":75.8533,"HyperDash":false},{"StartTime":95202.0,"Position":34.0,"HyperDash":false},{"StartTime":95270.0,"Position":75.8533,"HyperDash":false}]},{"StartTime":95339.0,"Objects":[{"StartTime":95339.0,"Position":77.0,"HyperDash":false},{"StartTime":95407.0,"Position":118.8533,"HyperDash":false}]},{"StartTime":95475.0,"Objects":[{"StartTime":95475.0,"Position":37.0,"HyperDash":false},{"StartTime":95543.0,"Position":78.8533,"HyperDash":false},{"StartTime":95611.0,"Position":37.0,"HyperDash":false},{"StartTime":95679.0,"Position":78.8533,"HyperDash":false},{"StartTime":95747.0,"Position":37.0,"HyperDash":false},{"StartTime":95815.0,"Position":78.8533,"HyperDash":false},{"StartTime":95884.0,"Position":37.0,"HyperDash":false},{"StartTime":95952.0,"Position":78.8533,"HyperDash":false},{"StartTime":96020.0,"Position":37.0,"HyperDash":false}]},{"StartTime":104748.0,"Objects":[{"StartTime":104748.0,"Position":285.0,"HyperDash":false},{"StartTime":104884.0,"Position":196.3763,"HyperDash":false}]},{"StartTime":105020.0,"Objects":[{"StartTime":105020.0,"Position":372.0,"HyperDash":false},{"StartTime":105156.0,"Position":460.110962,"HyperDash":false}]},{"StartTime":105293.0,"Objects":[{"StartTime":105293.0,"Position":483.0,"HyperDash":false},{"StartTime":105429.0,"Position":506.840118,"HyperDash":false}]},{"StartTime":105566.0,"Objects":[{"StartTime":105566.0,"Position":381.0,"HyperDash":false},{"StartTime":105702.0,"Position":403.863068,"HyperDash":false}]},{"StartTime":105839.0,"Objects":[{"StartTime":105839.0,"Position":336.0,"HyperDash":false},{"StartTime":105975.0,"Position":236.595367,"HyperDash":false}]},{"StartTime":106111.0,"Objects":[{"StartTime":106111.0,"Position":190.0,"HyperDash":false}]},{"StartTime":106248.0,"Objects":[{"StartTime":106248.0,"Position":190.0,"HyperDash":false}]},{"StartTime":106384.0,"Objects":[{"StartTime":106384.0,"Position":66.0,"HyperDash":false},{"StartTime":106520.0,"Position":50.64902,"HyperDash":false}]},{"StartTime":106657.0,"Objects":[{"StartTime":106657.0,"Position":160.0,"HyperDash":false},{"StartTime":106793.0,"Position":256.028931,"HyperDash":false}]},{"StartTime":106929.0,"Objects":[{"StartTime":106929.0,"Position":419.0,"HyperDash":false},{"StartTime":107065.0,"Position":411.1793,"HyperDash":false}]},{"StartTime":107202.0,"Objects":[{"StartTime":107202.0,"Position":350.0,"HyperDash":false},{"StartTime":107338.0,"Position":437.371735,"HyperDash":false}]},{"StartTime":107475.0,"Objects":[{"StartTime":107475.0,"Position":500.0,"HyperDash":false}]},{"StartTime":107611.0,"Objects":[{"StartTime":107611.0,"Position":387.0,"HyperDash":false}]},{"StartTime":107679.0,"Objects":[{"StartTime":107679.0,"Position":387.0,"HyperDash":false}]},{"StartTime":107748.0,"Objects":[{"StartTime":107748.0,"Position":387.0,"HyperDash":false},{"StartTime":107884.0,"Position":286.101257,"HyperDash":false}]},{"StartTime":108020.0,"Objects":[{"StartTime":108020.0,"Position":126.0,"HyperDash":false}]},{"StartTime":108156.0,"Objects":[{"StartTime":108156.0,"Position":139.0,"HyperDash":false}]},{"StartTime":108293.0,"Objects":[{"StartTime":108293.0,"Position":213.0,"HyperDash":false}]},{"StartTime":108429.0,"Objects":[{"StartTime":108429.0,"Position":301.0,"HyperDash":false}]},{"StartTime":108566.0,"Objects":[{"StartTime":108566.0,"Position":267.0,"HyperDash":false},{"StartTime":108625.0,"Position":232.172058,"HyperDash":false},{"StartTime":108684.0,"Position":191.248871,"HyperDash":false},{"StartTime":108743.0,"Position":129.18779,"HyperDash":false},{"StartTime":108838.0,"Position":67.07219,"HyperDash":false}]},{"StartTime":108975.0,"Objects":[{"StartTime":108975.0,"Position":55.0,"HyperDash":false}]},{"StartTime":109043.0,"Objects":[{"StartTime":109043.0,"Position":44.0,"HyperDash":false}]},{"StartTime":109111.0,"Objects":[{"StartTime":109111.0,"Position":35.0,"HyperDash":false},{"StartTime":109247.0,"Position":134.610657,"HyperDash":false}]},{"StartTime":109384.0,"Objects":[{"StartTime":109384.0,"Position":279.0,"HyperDash":false},{"StartTime":109520.0,"Position":378.779877,"HyperDash":false}]},{"StartTime":109657.0,"Objects":[{"StartTime":109657.0,"Position":474.0,"HyperDash":false},{"StartTime":109793.0,"Position":414.009949,"HyperDash":false}]},{"StartTime":109929.0,"Objects":[{"StartTime":109929.0,"Position":357.0,"HyperDash":false},{"StartTime":110065.0,"Position":448.250183,"HyperDash":false}]},{"StartTime":110202.0,"Objects":[{"StartTime":110202.0,"Position":499.0,"HyperDash":false},{"StartTime":110338.0,"Position":409.002472,"HyperDash":false}]},{"StartTime":110475.0,"Objects":[{"StartTime":110475.0,"Position":280.0,"HyperDash":false}]},{"StartTime":110611.0,"Objects":[{"StartTime":110611.0,"Position":280.0,"HyperDash":false}]},{"StartTime":110748.0,"Objects":[{"StartTime":110748.0,"Position":357.0,"HyperDash":false},{"StartTime":110884.0,"Position":344.34845,"HyperDash":false}]},{"StartTime":111020.0,"Objects":[{"StartTime":111020.0,"Position":209.0,"HyperDash":false},{"StartTime":111156.0,"Position":196.34845,"HyperDash":false}]},{"StartTime":111293.0,"Objects":[{"StartTime":111293.0,"Position":65.0,"HyperDash":false},{"StartTime":111429.0,"Position":148.923523,"HyperDash":false}]},{"StartTime":111566.0,"Objects":[{"StartTime":111566.0,"Position":80.0,"HyperDash":false},{"StartTime":111702.0,"Position":78.81489,"HyperDash":false}]},{"StartTime":111839.0,"Objects":[{"StartTime":111839.0,"Position":148.0,"HyperDash":false}]},{"StartTime":111975.0,"Objects":[{"StartTime":111975.0,"Position":269.0,"HyperDash":false}]},{"StartTime":112043.0,"Objects":[{"StartTime":112043.0,"Position":269.0,"HyperDash":false}]},{"StartTime":112111.0,"Objects":[{"StartTime":112111.0,"Position":269.0,"HyperDash":false},{"StartTime":112247.0,"Position":369.6811,"HyperDash":false}]},{"StartTime":112384.0,"Objects":[{"StartTime":112384.0,"Position":369.0,"HyperDash":false}]},{"StartTime":112452.0,"Objects":[{"StartTime":112452.0,"Position":410.0,"HyperDash":false}]},{"StartTime":112520.0,"Objects":[{"StartTime":112520.0,"Position":450.0,"HyperDash":false}]},{"StartTime":112588.0,"Objects":[{"StartTime":112588.0,"Position":478.0,"HyperDash":false}]},{"StartTime":112656.0,"Objects":[{"StartTime":112656.0,"Position":487.0,"HyperDash":false}]},{"StartTime":112793.0,"Objects":[{"StartTime":112793.0,"Position":413.0,"HyperDash":false}]},{"StartTime":112861.0,"Objects":[{"StartTime":112861.0,"Position":371.0,"HyperDash":false}]},{"StartTime":112929.0,"Objects":[{"StartTime":112929.0,"Position":329.0,"HyperDash":false}]},{"StartTime":113066.0,"Objects":[{"StartTime":113066.0,"Position":259.0,"HyperDash":false},{"StartTime":113134.0,"Position":208.630585,"HyperDash":false},{"StartTime":113202.0,"Position":259.0,"HyperDash":false},{"StartTime":113270.0,"Position":208.630585,"HyperDash":false},{"StartTime":113338.0,"Position":259.0,"HyperDash":false},{"StartTime":113406.0,"Position":208.630585,"HyperDash":false},{"StartTime":113475.0,"Position":259.0,"HyperDash":false}]},{"StartTime":117839.0,"Objects":[{"StartTime":117839.0,"Position":352.0,"HyperDash":false},{"StartTime":117907.0,"Position":367.7046,"HyperDash":false},{"StartTime":117975.0,"Position":377.8776,"HyperDash":false},{"StartTime":118043.0,"Position":353.339722,"HyperDash":false},{"StartTime":118111.0,"Position":341.5588,"HyperDash":false},{"StartTime":118170.0,"Position":357.394043,"HyperDash":false},{"StartTime":118229.0,"Position":351.709229,"HyperDash":false},{"StartTime":118288.0,"Position":368.7251,"HyperDash":false},{"StartTime":118384.0,"Position":352.0,"HyperDash":false}]},{"StartTime":118521.0,"Objects":[{"StartTime":118521.0,"Position":435.0,"HyperDash":false}]},{"StartTime":118657.0,"Objects":[{"StartTime":118657.0,"Position":435.0,"HyperDash":false},{"StartTime":118716.0,"Position":424.944855,"HyperDash":false},{"StartTime":118775.0,"Position":373.775269,"HyperDash":false},{"StartTime":118834.0,"Position":349.8368,"HyperDash":false},{"StartTime":118929.0,"Position":316.293427,"HyperDash":false}]},{"StartTime":119203.0,"Objects":[{"StartTime":119203.0,"Position":353.0,"HyperDash":false}]},{"StartTime":119339.0,"Objects":[{"StartTime":119339.0,"Position":353.0,"HyperDash":false},{"StartTime":119398.0,"Position":364.062378,"HyperDash":false},{"StartTime":119457.0,"Position":397.124756,"HyperDash":false},{"StartTime":119516.0,"Position":439.1871,"HyperDash":false},{"StartTime":119611.0,"Position":486.982452,"HyperDash":true}]},{"StartTime":119748.0,"Objects":[{"StartTime":119748.0,"Position":273.0,"HyperDash":false}]},{"StartTime":120021.0,"Objects":[{"StartTime":120021.0,"Position":90.0,"HyperDash":false},{"StartTime":120089.0,"Position":108.62011,"HyperDash":false},{"StartTime":120157.0,"Position":95.3407,"HyperDash":false},{"StartTime":120225.0,"Position":76.87965,"HyperDash":false},{"StartTime":120293.0,"Position":40.5374374,"HyperDash":false},{"StartTime":120352.0,"Position":60.58837,"HyperDash":false},{"StartTime":120411.0,"Position":96.3111343,"HyperDash":false},{"StartTime":120470.0,"Position":80.33538,"HyperDash":false},{"StartTime":120566.0,"Position":90.0,"HyperDash":false}]},{"StartTime":120703.0,"Objects":[{"StartTime":120703.0,"Position":128.0,"HyperDash":false}]},{"StartTime":120839.0,"Objects":[{"StartTime":120839.0,"Position":128.0,"HyperDash":false},{"StartTime":120975.0,"Position":68.21395,"HyperDash":false}]},{"StartTime":121112.0,"Objects":[{"StartTime":121112.0,"Position":14.0,"HyperDash":false},{"StartTime":121180.0,"Position":34.0660934,"HyperDash":false},{"StartTime":121248.0,"Position":24.13219,"HyperDash":false},{"StartTime":121384.0,"Position":14.0,"HyperDash":false}]},{"StartTime":121521.0,"Objects":[{"StartTime":121521.0,"Position":68.0,"HyperDash":false},{"StartTime":121580.0,"Position":75.36682,"HyperDash":false},{"StartTime":121639.0,"Position":102.431969,"HyperDash":false},{"StartTime":121698.0,"Position":145.603821,"HyperDash":false},{"StartTime":121793.0,"Position":188.698318,"HyperDash":false}]},{"StartTime":121930.0,"Objects":[{"StartTime":121930.0,"Position":267.0,"HyperDash":false}]},{"StartTime":122202.0,"Objects":[{"StartTime":122202.0,"Position":267.0,"HyperDash":false},{"StartTime":122261.0,"Position":230.862274,"HyperDash":false},{"StartTime":122320.0,"Position":245.4149,"HyperDash":false},{"StartTime":122379.0,"Position":216.465454,"HyperDash":false},{"StartTime":122474.0,"Position":252.568588,"HyperDash":false}]},{"StartTime":122611.0,"Objects":[{"StartTime":122611.0,"Position":252.0,"HyperDash":false},{"StartTime":122670.0,"Position":237.2295,"HyperDash":false},{"StartTime":122729.0,"Position":198.886948,"HyperDash":false},{"StartTime":122788.0,"Position":171.432022,"HyperDash":false},{"StartTime":122883.0,"Position":120.432274,"HyperDash":false}]},{"StartTime":123021.0,"Objects":[{"StartTime":123021.0,"Position":58.0,"HyperDash":false},{"StartTime":123157.0,"Position":78.36528,"HyperDash":false}]},{"StartTime":123293.0,"Objects":[{"StartTime":123293.0,"Position":6.0,"HyperDash":false},{"StartTime":123429.0,"Position":88.6607361,"HyperDash":false}]},{"StartTime":123566.0,"Objects":[{"StartTime":123566.0,"Position":156.0,"HyperDash":false},{"StartTime":123702.0,"Position":224.188141,"HyperDash":false}]},{"StartTime":123839.0,"Objects":[{"StartTime":123839.0,"Position":349.0,"HyperDash":false}]},{"StartTime":123975.0,"Objects":[{"StartTime":123975.0,"Position":375.0,"HyperDash":false}]},{"StartTime":124111.0,"Objects":[{"StartTime":124111.0,"Position":456.0,"HyperDash":false},{"StartTime":124195.0,"Position":453.9612,"HyperDash":false},{"StartTime":124315.0,"Position":470.4772,"HyperDash":false}]},{"StartTime":124384.0,"Objects":[{"StartTime":124384.0,"Position":498.0,"HyperDash":false},{"StartTime":124443.0,"Position":452.638641,"HyperDash":false},{"StartTime":124502.0,"Position":424.858124,"HyperDash":false},{"StartTime":124561.0,"Position":402.805267,"HyperDash":false},{"StartTime":124656.0,"Position":400.806458,"HyperDash":false}]},{"StartTime":124793.0,"Objects":[{"StartTime":124793.0,"Position":400.0,"HyperDash":false}]},{"StartTime":124930.0,"Objects":[{"StartTime":124930.0,"Position":320.0,"HyperDash":false},{"StartTime":125020.0,"Position":265.6432,"HyperDash":false},{"StartTime":125111.0,"Position":320.0,"HyperDash":false}]},{"StartTime":125202.0,"Objects":[{"StartTime":125202.0,"Position":226.0,"HyperDash":false},{"StartTime":125292.0,"Position":184.534943,"HyperDash":false},{"StartTime":125383.0,"Position":226.0,"HyperDash":false}]},{"StartTime":125475.0,"Objects":[{"StartTime":125475.0,"Position":165.0,"HyperDash":false},{"StartTime":125565.0,"Position":148.008514,"HyperDash":false},{"StartTime":125656.0,"Position":165.0,"HyperDash":false}]},{"StartTime":125748.0,"Objects":[{"StartTime":125748.0,"Position":64.0,"HyperDash":false},{"StartTime":125838.0,"Position":76.2514648,"HyperDash":false},{"StartTime":125929.0,"Position":64.0,"HyperDash":false}]},{"StartTime":126021.0,"Objects":[{"StartTime":126021.0,"Position":98.0,"HyperDash":false},{"StartTime":126111.0,"Position":42.3349533,"HyperDash":false},{"StartTime":126202.0,"Position":98.0,"HyperDash":false}]},{"StartTime":126293.0,"Objects":[{"StartTime":126293.0,"Position":168.0,"HyperDash":false}]},{"StartTime":126384.0,"Objects":[{"StartTime":126384.0,"Position":176.0,"HyperDash":false},{"StartTime":126474.0,"Position":231.724014,"HyperDash":false}]},{"StartTime":126566.0,"Objects":[{"StartTime":126566.0,"Position":294.0,"HyperDash":false},{"StartTime":126625.0,"Position":304.065277,"HyperDash":false},{"StartTime":126684.0,"Position":289.130554,"HyperDash":false},{"StartTime":126743.0,"Position":270.195831,"HyperDash":false},{"StartTime":126838.0,"Position":275.86026,"HyperDash":false}]},{"StartTime":126975.0,"Objects":[{"StartTime":126975.0,"Position":269.0,"HyperDash":false},{"StartTime":127034.0,"Position":238.030014,"HyperDash":false},{"StartTime":127093.0,"Position":206.798035,"HyperDash":false},{"StartTime":127152.0,"Position":183.373825,"HyperDash":false},{"StartTime":127247.0,"Position":128.954315,"HyperDash":false}]},{"StartTime":127384.0,"Objects":[{"StartTime":127384.0,"Position":128.0,"HyperDash":false},{"StartTime":127443.0,"Position":104.877335,"HyperDash":false},{"StartTime":127502.0,"Position":66.8338852,"HyperDash":false},{"StartTime":127561.0,"Position":81.92623,"HyperDash":false},{"StartTime":127656.0,"Position":101.414925,"HyperDash":false}]},{"StartTime":127930.0,"Objects":[{"StartTime":127930.0,"Position":102.0,"HyperDash":false},{"StartTime":128066.0,"Position":185.98468,"HyperDash":false}]},{"StartTime":128202.0,"Objects":[{"StartTime":128202.0,"Position":268.0,"HyperDash":false},{"StartTime":128338.0,"Position":276.750061,"HyperDash":false}]},{"StartTime":128475.0,"Objects":[{"StartTime":128475.0,"Position":220.0,"HyperDash":false}]},{"StartTime":128611.0,"Objects":[{"StartTime":128611.0,"Position":246.0,"HyperDash":false}]},{"StartTime":128748.0,"Objects":[{"StartTime":128748.0,"Position":272.0,"HyperDash":false},{"StartTime":128838.0,"Position":259.471741,"HyperDash":false},{"StartTime":128929.0,"Position":272.0,"HyperDash":false}]},{"StartTime":129021.0,"Objects":[{"StartTime":129021.0,"Position":341.0,"HyperDash":false},{"StartTime":129111.0,"Position":356.802368,"HyperDash":false},{"StartTime":129202.0,"Position":341.0,"HyperDash":false}]},{"StartTime":129293.0,"Objects":[{"StartTime":129293.0,"Position":374.0,"HyperDash":false},{"StartTime":129383.0,"Position":414.349274,"HyperDash":false},{"StartTime":129474.0,"Position":374.0,"HyperDash":false}]},{"StartTime":129566.0,"Objects":[{"StartTime":129566.0,"Position":363.0,"HyperDash":false},{"StartTime":129656.0,"Position":417.3568,"HyperDash":false},{"StartTime":129747.0,"Position":363.0,"HyperDash":false}]},{"StartTime":129839.0,"Objects":[{"StartTime":129839.0,"Position":399.0,"HyperDash":false}]},{"StartTime":129930.0,"Objects":[{"StartTime":129930.0,"Position":363.0,"HyperDash":false}]},{"StartTime":130021.0,"Objects":[{"StartTime":130021.0,"Position":319.0,"HyperDash":false}]},{"StartTime":130111.0,"Objects":[{"StartTime":130111.0,"Position":274.0,"HyperDash":false}]},{"StartTime":130202.0,"Objects":[{"StartTime":130202.0,"Position":233.0,"HyperDash":false}]},{"StartTime":130293.0,"Objects":[{"StartTime":130293.0,"Position":188.0,"HyperDash":false}]},{"StartTime":130384.0,"Objects":[{"StartTime":130384.0,"Position":144.0,"HyperDash":false},{"StartTime":130443.0,"Position":136.688782,"HyperDash":false},{"StartTime":130502.0,"Position":118.278656,"HyperDash":false},{"StartTime":130561.0,"Position":153.723068,"HyperDash":false},{"StartTime":130656.0,"Position":190.433411,"HyperDash":false}]},{"StartTime":130793.0,"Objects":[{"StartTime":130793.0,"Position":282.0,"HyperDash":false}]},{"StartTime":130861.0,"Objects":[{"StartTime":130861.0,"Position":282.0,"HyperDash":false}]},{"StartTime":130930.0,"Objects":[{"StartTime":130930.0,"Position":282.0,"HyperDash":false},{"StartTime":130989.0,"Position":284.273651,"HyperDash":false},{"StartTime":131048.0,"Position":293.547333,"HyperDash":false},{"StartTime":131107.0,"Position":301.820984,"HyperDash":false},{"StartTime":131202.0,"Position":264.598328,"HyperDash":false}]},{"StartTime":131339.0,"Objects":[{"StartTime":131339.0,"Position":264.0,"HyperDash":false},{"StartTime":131398.0,"Position":248.803833,"HyperDash":false},{"StartTime":131457.0,"Position":204.483932,"HyperDash":false},{"StartTime":131516.0,"Position":177.141281,"HyperDash":false},{"StartTime":131611.0,"Position":107.439949,"HyperDash":false}]},{"StartTime":131748.0,"Objects":[{"StartTime":131748.0,"Position":107.0,"HyperDash":false},{"StartTime":131884.0,"Position":136.185135,"HyperDash":false}]},{"StartTime":132021.0,"Objects":[{"StartTime":132021.0,"Position":88.0,"HyperDash":false},{"StartTime":132080.0,"Position":51.873764,"HyperDash":false},{"StartTime":132139.0,"Position":55.46241,"HyperDash":false},{"StartTime":132198.0,"Position":72.92975,"HyperDash":false},{"StartTime":132293.0,"Position":100.14119,"HyperDash":false}]},{"StartTime":132430.0,"Objects":[{"StartTime":132430.0,"Position":100.0,"HyperDash":false},{"StartTime":132489.0,"Position":75.71915,"HyperDash":false},{"StartTime":132548.0,"Position":18.4710484,"HyperDash":false},{"StartTime":132607.0,"Position":27.815239,"HyperDash":false},{"StartTime":132702.0,"Position":100.250526,"HyperDash":false}]},{"StartTime":132839.0,"Objects":[{"StartTime":132839.0,"Position":100.0,"HyperDash":false},{"StartTime":132975.0,"Position":179.952286,"HyperDash":false}]},{"StartTime":133111.0,"Objects":[{"StartTime":133111.0,"Position":246.0,"HyperDash":false},{"StartTime":133247.0,"Position":327.362976,"HyperDash":false}]},{"StartTime":133384.0,"Objects":[{"StartTime":133384.0,"Position":390.0,"HyperDash":false}]},{"StartTime":133521.0,"Objects":[{"StartTime":133521.0,"Position":472.0,"HyperDash":false}]},{"StartTime":133657.0,"Objects":[{"StartTime":133657.0,"Position":491.0,"HyperDash":false}]},{"StartTime":133793.0,"Objects":[{"StartTime":133793.0,"Position":439.0,"HyperDash":false}]},{"StartTime":133930.0,"Objects":[{"StartTime":133930.0,"Position":420.0,"HyperDash":false}]},{"StartTime":134066.0,"Objects":[{"StartTime":134066.0,"Position":461.0,"HyperDash":false}]},{"StartTime":134202.0,"Objects":[{"StartTime":134202.0,"Position":448.0,"HyperDash":false}]},{"StartTime":134339.0,"Objects":[{"StartTime":134339.0,"Position":381.0,"HyperDash":false}]},{"StartTime":134475.0,"Objects":[{"StartTime":134475.0,"Position":296.0,"HyperDash":false}]},{"StartTime":134611.0,"Objects":[{"StartTime":134611.0,"Position":214.0,"HyperDash":false}]},{"StartTime":134748.0,"Objects":[{"StartTime":134748.0,"Position":164.0,"HyperDash":false},{"StartTime":134884.0,"Position":83.35544,"HyperDash":false}]},{"StartTime":135021.0,"Objects":[{"StartTime":135021.0,"Position":19.0,"HyperDash":false},{"StartTime":135157.0,"Position":99.57382,"HyperDash":false}]},{"StartTime":135293.0,"Objects":[{"StartTime":135293.0,"Position":25.0,"HyperDash":false},{"StartTime":135352.0,"Position":41.8271523,"HyperDash":false},{"StartTime":135411.0,"Position":95.72167,"HyperDash":false},{"StartTime":135470.0,"Position":108.490532,"HyperDash":false},{"StartTime":135565.0,"Position":179.471237,"HyperDash":false}]},{"StartTime":135702.0,"Objects":[{"StartTime":135702.0,"Position":252.0,"HyperDash":false}]},{"StartTime":135839.0,"Objects":[{"StartTime":135839.0,"Position":252.0,"HyperDash":false},{"StartTime":135975.0,"Position":241.337753,"HyperDash":false}]},{"StartTime":136111.0,"Objects":[{"StartTime":136111.0,"Position":175.0,"HyperDash":false},{"StartTime":136247.0,"Position":185.662247,"HyperDash":false}]},{"StartTime":136384.0,"Objects":[{"StartTime":136384.0,"Position":138.0,"HyperDash":false}]},{"StartTime":136521.0,"Objects":[{"StartTime":136521.0,"Position":194.0,"HyperDash":false}]},{"StartTime":136657.0,"Objects":[{"StartTime":136657.0,"Position":278.0,"HyperDash":false}]},{"StartTime":136793.0,"Objects":[{"StartTime":136793.0,"Position":360.0,"HyperDash":false}]},{"StartTime":136930.0,"Objects":[{"StartTime":136930.0,"Position":407.0,"HyperDash":false}]},{"StartTime":137066.0,"Objects":[{"StartTime":137066.0,"Position":447.0,"HyperDash":false}]},{"StartTime":137202.0,"Objects":[{"StartTime":137202.0,"Position":367.0,"HyperDash":false}]},{"StartTime":137338.0,"Objects":[{"StartTime":137338.0,"Position":407.0,"HyperDash":false}]},{"StartTime":137475.0,"Objects":[{"StartTime":137475.0,"Position":280.0,"HyperDash":false}]},{"StartTime":137611.0,"Objects":[{"StartTime":137611.0,"Position":194.0,"HyperDash":false}]},{"StartTime":137748.0,"Objects":[{"StartTime":137748.0,"Position":207.0,"HyperDash":false}]},{"StartTime":137884.0,"Objects":[{"StartTime":137884.0,"Position":293.0,"HyperDash":false}]},{"StartTime":138021.0,"Objects":[{"StartTime":138021.0,"Position":198.0,"HyperDash":false},{"StartTime":138080.0,"Position":186.6536,"HyperDash":false},{"StartTime":138139.0,"Position":165.980225,"HyperDash":false},{"StartTime":138198.0,"Position":108.5129,"HyperDash":false},{"StartTime":138293.0,"Position":60.4876747,"HyperDash":false}]},{"StartTime":138566.0,"Objects":[{"StartTime":138566.0,"Position":20.0,"HyperDash":false}]},{"StartTime":138657.0,"Objects":[{"StartTime":138657.0,"Position":67.0,"HyperDash":false}]},{"StartTime":138748.0,"Objects":[{"StartTime":138748.0,"Position":122.0,"HyperDash":false}]},{"StartTime":138839.0,"Objects":[{"StartTime":138839.0,"Position":178.0,"HyperDash":false}]},{"StartTime":138930.0,"Objects":[{"StartTime":138930.0,"Position":221.0,"HyperDash":false}]},{"StartTime":139021.0,"Objects":[{"StartTime":139021.0,"Position":244.0,"HyperDash":false}]},{"StartTime":139111.0,"Objects":[{"StartTime":139111.0,"Position":248.0,"HyperDash":false},{"StartTime":139201.0,"Position":233.246613,"HyperDash":false},{"StartTime":139292.0,"Position":248.0,"HyperDash":false}]},{"StartTime":139384.0,"Objects":[{"StartTime":139384.0,"Position":327.0,"HyperDash":false},{"StartTime":139468.0,"Position":372.042328,"HyperDash":false},{"StartTime":139588.0,"Position":453.388519,"HyperDash":false}]},{"StartTime":139657.0,"Objects":[{"StartTime":139657.0,"Position":489.0,"HyperDash":false},{"StartTime":139716.0,"Position":500.969269,"HyperDash":false},{"StartTime":139775.0,"Position":484.081482,"HyperDash":false},{"StartTime":139834.0,"Position":452.1301,"HyperDash":false},{"StartTime":139929.0,"Position":387.50766,"HyperDash":false}]},{"StartTime":140066.0,"Objects":[{"StartTime":140066.0,"Position":311.0,"HyperDash":false},{"StartTime":140125.0,"Position":300.9206,"HyperDash":false},{"StartTime":140184.0,"Position":285.442963,"HyperDash":false},{"StartTime":140243.0,"Position":239.63205,"HyperDash":false},{"StartTime":140338.0,"Position":189.411591,"HyperDash":false}]},{"StartTime":140475.0,"Objects":[{"StartTime":140475.0,"Position":118.0,"HyperDash":false},{"StartTime":140611.0,"Position":39.25152,"HyperDash":false}]},{"StartTime":140748.0,"Objects":[{"StartTime":140748.0,"Position":13.0,"HyperDash":false}]},{"StartTime":140884.0,"Objects":[{"StartTime":140884.0,"Position":93.0,"HyperDash":false}]},{"StartTime":141021.0,"Objects":[{"StartTime":141021.0,"Position":30.0,"HyperDash":false}]},{"StartTime":141157.0,"Objects":[{"StartTime":141157.0,"Position":91.0,"HyperDash":false},{"StartTime":141216.0,"Position":120.467026,"HyperDash":false},{"StartTime":141275.0,"Position":182.934052,"HyperDash":false},{"StartTime":141334.0,"Position":189.6184,"HyperDash":false},{"StartTime":141429.0,"Position":253.543488,"HyperDash":false}]},{"StartTime":141566.0,"Objects":[{"StartTime":141566.0,"Position":253.0,"HyperDash":false},{"StartTime":141702.0,"Position":252.173325,"HyperDash":false}]},{"StartTime":141839.0,"Objects":[{"StartTime":141839.0,"Position":302.0,"HyperDash":false},{"StartTime":141898.0,"Position":271.164337,"HyperDash":false},{"StartTime":141957.0,"Position":265.036316,"HyperDash":false},{"StartTime":142016.0,"Position":225.854385,"HyperDash":false},{"StartTime":142111.0,"Position":258.0618,"HyperDash":false}]},{"StartTime":142248.0,"Objects":[{"StartTime":142248.0,"Position":329.0,"HyperDash":false}]},{"StartTime":142384.0,"Objects":[{"StartTime":142384.0,"Position":401.0,"HyperDash":false},{"StartTime":142474.0,"Position":456.101929,"HyperDash":false},{"StartTime":142565.0,"Position":401.0,"HyperDash":false}]},{"StartTime":142657.0,"Objects":[{"StartTime":142657.0,"Position":430.0,"HyperDash":false},{"StartTime":142747.0,"Position":485.101929,"HyperDash":false},{"StartTime":142838.0,"Position":430.0,"HyperDash":false}]},{"StartTime":142930.0,"Objects":[{"StartTime":142930.0,"Position":474.0,"HyperDash":false}]},{"StartTime":143020.0,"Objects":[{"StartTime":143020.0,"Position":433.0,"HyperDash":false}]},{"StartTime":143111.0,"Objects":[{"StartTime":143111.0,"Position":389.0,"HyperDash":false}]},{"StartTime":143202.0,"Objects":[{"StartTime":143202.0,"Position":356.0,"HyperDash":false}]},{"StartTime":143293.0,"Objects":[{"StartTime":143293.0,"Position":347.0,"HyperDash":false}]},{"StartTime":143384.0,"Objects":[{"StartTime":143384.0,"Position":363.0,"HyperDash":false}]},{"StartTime":143475.0,"Objects":[{"StartTime":143475.0,"Position":403.0,"HyperDash":false},{"StartTime":143565.0,"Position":458.0956,"HyperDash":false}]},{"StartTime":143657.0,"Objects":[{"StartTime":143657.0,"Position":315.0,"HyperDash":false}]},{"StartTime":143748.0,"Objects":[{"StartTime":143748.0,"Position":303.0,"HyperDash":false},{"StartTime":143838.0,"Position":247.904388,"HyperDash":false}]},{"StartTime":143930.0,"Objects":[{"StartTime":143930.0,"Position":152.0,"HyperDash":false}]},{"StartTime":144021.0,"Objects":[{"StartTime":144021.0,"Position":140.0,"HyperDash":false},{"StartTime":144080.0,"Position":123.703255,"HyperDash":false},{"StartTime":144139.0,"Position":84.1756058,"HyperDash":false},{"StartTime":144198.0,"Position":55.1062,"HyperDash":false},{"StartTime":144293.0,"Position":34.28666,"HyperDash":false}]},{"StartTime":144430.0,"Objects":[{"StartTime":144430.0,"Position":34.0,"HyperDash":false},{"StartTime":144489.0,"Position":39.2494774,"HyperDash":false},{"StartTime":144548.0,"Position":42.4028931,"HyperDash":false},{"StartTime":144607.0,"Position":83.5909,"HyperDash":false},{"StartTime":144702.0,"Position":124.747566,"HyperDash":false}]},{"StartTime":144839.0,"Objects":[{"StartTime":144839.0,"Position":151.0,"HyperDash":false}]},{"StartTime":144975.0,"Objects":[{"StartTime":144975.0,"Position":151.0,"HyperDash":false}]},{"StartTime":145111.0,"Objects":[{"StartTime":145111.0,"Position":91.0,"HyperDash":false},{"StartTime":145247.0,"Position":6.988411,"HyperDash":false}]},{"StartTime":145384.0,"Objects":[{"StartTime":145384.0,"Position":124.0,"HyperDash":false},{"StartTime":145520.0,"Position":208.0116,"HyperDash":false}]},{"StartTime":145657.0,"Objects":[{"StartTime":145657.0,"Position":284.0,"HyperDash":false}]},{"StartTime":145793.0,"Objects":[{"StartTime":145793.0,"Position":330.0,"HyperDash":false}]},{"StartTime":145930.0,"Objects":[{"StartTime":145930.0,"Position":412.0,"HyperDash":false}]},{"StartTime":146066.0,"Objects":[{"StartTime":146066.0,"Position":494.0,"HyperDash":false}]},{"StartTime":146202.0,"Objects":[{"StartTime":146202.0,"Position":422.0,"HyperDash":false},{"StartTime":146261.0,"Position":374.5958,"HyperDash":false},{"StartTime":146320.0,"Position":340.97818,"HyperDash":false},{"StartTime":146379.0,"Position":321.774567,"HyperDash":false},{"StartTime":146474.0,"Position":273.590363,"HyperDash":false}]},{"StartTime":146611.0,"Objects":[{"StartTime":146611.0,"Position":273.0,"HyperDash":false}]},{"StartTime":146748.0,"Objects":[{"StartTime":146748.0,"Position":242.0,"HyperDash":false},{"StartTime":146884.0,"Position":179.186676,"HyperDash":false}]},{"StartTime":147021.0,"Objects":[{"StartTime":147021.0,"Position":33.0,"HyperDash":false},{"StartTime":147157.0,"Position":95.18677,"HyperDash":false}]},{"StartTime":147293.0,"Objects":[{"StartTime":147293.0,"Position":120.0,"HyperDash":false},{"StartTime":147383.0,"Position":174.276825,"HyperDash":false},{"StartTime":147474.0,"Position":120.0,"HyperDash":false}]},{"StartTime":147566.0,"Objects":[{"StartTime":147566.0,"Position":83.0,"HyperDash":false},{"StartTime":147702.0,"Position":0.0,"HyperDash":false}]},{"StartTime":147839.0,"Objects":[{"StartTime":147839.0,"Position":175.0,"HyperDash":false}]},{"StartTime":147975.0,"Objects":[{"StartTime":147975.0,"Position":256.0,"HyperDash":false}]},{"StartTime":148111.0,"Objects":[{"StartTime":148111.0,"Position":195.0,"HyperDash":false}]},{"StartTime":148248.0,"Objects":[{"StartTime":148248.0,"Position":300.0,"HyperDash":false}]},{"StartTime":148316.0,"Objects":[{"StartTime":148316.0,"Position":300.0,"HyperDash":false}]},{"StartTime":148384.0,"Objects":[{"StartTime":148384.0,"Position":300.0,"HyperDash":false},{"StartTime":148452.0,"Position":241.037445,"HyperDash":false},{"StartTime":148520.0,"Position":208.872025,"HyperDash":false},{"StartTime":148588.0,"Position":167.7589,"HyperDash":false},{"StartTime":148656.0,"Position":112.687119,"HyperDash":false},{"StartTime":148724.0,"Position":64.53349,"HyperDash":false},{"StartTime":148792.0,"Position":45.59254,"HyperDash":false},{"StartTime":148860.0,"Position":57.11486,"HyperDash":false},{"StartTime":148929.0,"Position":103.807991,"HyperDash":false},{"StartTime":148997.0,"Position":140.8881,"HyperDash":false},{"StartTime":149065.0,"Position":203.1637,"HyperDash":false},{"StartTime":149133.0,"Position":175.605743,"HyperDash":false},{"StartTime":149202.0,"Position":157.292023,"HyperDash":false},{"StartTime":149261.0,"Position":179.837387,"HyperDash":false},{"StartTime":149320.0,"Position":205.033386,"HyperDash":false},{"StartTime":149379.0,"Position":230.499939,"HyperDash":false},{"StartTime":149474.0,"Position":318.759552,"HyperDash":false}]},{"StartTime":149611.0,"Objects":[{"StartTime":149611.0,"Position":416.0,"HyperDash":false},{"StartTime":149679.0,"Position":459.112885,"HyperDash":false},{"StartTime":149747.0,"Position":483.116028,"HyperDash":false},{"StartTime":149883.0,"Position":416.0,"HyperDash":false}]},{"StartTime":150021.0,"Objects":[{"StartTime":150021.0,"Position":318.0,"HyperDash":false}]},{"StartTime":150157.0,"Objects":[{"StartTime":150157.0,"Position":318.0,"HyperDash":false}]},{"StartTime":150293.0,"Objects":[{"StartTime":150293.0,"Position":395.0,"HyperDash":false},{"StartTime":150429.0,"Position":388.707062,"HyperDash":false}]},{"StartTime":150566.0,"Objects":[{"StartTime":150566.0,"Position":502.0,"HyperDash":false}]},{"StartTime":150702.0,"Objects":[{"StartTime":150702.0,"Position":388.0,"HyperDash":false}]},{"StartTime":150839.0,"Objects":[{"StartTime":150839.0,"Position":388.0,"HyperDash":false}]},{"StartTime":150975.0,"Objects":[{"StartTime":150975.0,"Position":354.0,"HyperDash":false},{"StartTime":151043.0,"Position":308.8965,"HyperDash":false},{"StartTime":151111.0,"Position":257.082336,"HyperDash":false},{"StartTime":151179.0,"Position":200.233047,"HyperDash":false},{"StartTime":151247.0,"Position":179.148392,"HyperDash":false},{"StartTime":151315.0,"Position":121.330429,"HyperDash":false},{"StartTime":151383.0,"Position":91.3323,"HyperDash":false},{"StartTime":151451.0,"Position":105.334328,"HyperDash":false},{"StartTime":151520.0,"Position":163.270889,"HyperDash":false},{"StartTime":151588.0,"Position":222.52066,"HyperDash":false},{"StartTime":151656.0,"Position":236.967346,"HyperDash":false},{"StartTime":151724.0,"Position":197.664108,"HyperDash":false},{"StartTime":151793.0,"Position":170.657684,"HyperDash":false},{"StartTime":151929.0,"Position":122.385834,"HyperDash":false}]},{"StartTime":152066.0,"Objects":[{"StartTime":152066.0,"Position":37.0,"HyperDash":false},{"StartTime":152134.0,"Position":38.1493454,"HyperDash":false},{"StartTime":152202.0,"Position":25.2637386,"HyperDash":false},{"StartTime":152338.0,"Position":37.0,"HyperDash":false}]},{"StartTime":152475.0,"Objects":[{"StartTime":152475.0,"Position":73.0,"HyperDash":false},{"StartTime":152611.0,"Position":125.983765,"HyperDash":false}]},{"StartTime":152748.0,"Objects":[{"StartTime":152748.0,"Position":211.0,"HyperDash":false},{"StartTime":152807.0,"Position":232.132385,"HyperDash":false},{"StartTime":152866.0,"Position":265.062622,"HyperDash":false},{"StartTime":152925.0,"Position":293.685852,"HyperDash":false},{"StartTime":153020.0,"Position":353.2395,"HyperDash":false}]},{"StartTime":153157.0,"Objects":[{"StartTime":153157.0,"Position":499.0,"HyperDash":false},{"StartTime":153216.0,"Position":449.435883,"HyperDash":false},{"StartTime":153275.0,"Position":424.8718,"HyperDash":false},{"StartTime":153334.0,"Position":399.307678,"HyperDash":false},{"StartTime":153429.0,"Position":330.433258,"HyperDash":false}]},{"StartTime":153566.0,"Objects":[{"StartTime":153566.0,"Position":279.0,"HyperDash":false},{"StartTime":153702.0,"Position":299.1634,"HyperDash":false}]},{"StartTime":153839.0,"Objects":[{"StartTime":153839.0,"Position":236.0,"HyperDash":false}]},{"StartTime":153975.0,"Objects":[{"StartTime":153975.0,"Position":299.0,"HyperDash":false}]},{"StartTime":154111.0,"Objects":[{"StartTime":154111.0,"Position":375.0,"HyperDash":false}]},{"StartTime":154248.0,"Objects":[{"StartTime":154248.0,"Position":448.0,"HyperDash":false},{"StartTime":154316.0,"Position":448.704071,"HyperDash":false},{"StartTime":154384.0,"Position":459.51297,"HyperDash":false},{"StartTime":154452.0,"Position":410.957947,"HyperDash":false},{"StartTime":154520.0,"Position":385.861572,"HyperDash":false},{"StartTime":154570.0,"Position":337.801727,"HyperDash":false},{"StartTime":154657.0,"Position":317.3621,"HyperDash":false}]},{"StartTime":154930.0,"Objects":[{"StartTime":154930.0,"Position":41.0,"HyperDash":false}]},{"StartTime":155020.0,"Objects":[{"StartTime":155020.0,"Position":28.0,"HyperDash":false}]},{"StartTime":155111.0,"Objects":[{"StartTime":155111.0,"Position":40.0,"HyperDash":false}]},{"StartTime":155202.0,"Objects":[{"StartTime":155202.0,"Position":72.0,"HyperDash":false}]},{"StartTime":155293.0,"Objects":[{"StartTime":155293.0,"Position":115.0,"HyperDash":false}]},{"StartTime":155384.0,"Objects":[{"StartTime":155384.0,"Position":158.0,"HyperDash":false}]},{"StartTime":155475.0,"Objects":[{"StartTime":155475.0,"Position":198.0,"HyperDash":false}]},{"StartTime":155565.0,"Objects":[{"StartTime":155565.0,"Position":254.0,"HyperDash":false}]},{"StartTime":155656.0,"Objects":[{"StartTime":155656.0,"Position":309.0,"HyperDash":false}]},{"StartTime":155747.0,"Objects":[{"StartTime":155747.0,"Position":356.0,"HyperDash":false}]},{"StartTime":155838.0,"Objects":[{"StartTime":155838.0,"Position":392.0,"HyperDash":false}]},{"StartTime":155929.0,"Objects":[{"StartTime":155929.0,"Position":411.0,"HyperDash":false}]},{"StartTime":156021.0,"Objects":[{"StartTime":156021.0,"Position":411.0,"HyperDash":false},{"StartTime":156089.0,"Position":395.962219,"HyperDash":false},{"StartTime":156157.0,"Position":339.266174,"HyperDash":false},{"StartTime":156225.0,"Position":303.955,"HyperDash":false},{"StartTime":156293.0,"Position":318.589355,"HyperDash":false},{"StartTime":156361.0,"Position":368.6844,"HyperDash":false},{"StartTime":156429.0,"Position":387.036835,"HyperDash":false},{"StartTime":156497.0,"Position":393.426025,"HyperDash":false},{"StartTime":156566.0,"Position":373.163116,"HyperDash":false},{"StartTime":156625.0,"Position":341.85965,"HyperDash":false},{"StartTime":156684.0,"Position":283.9819,"HyperDash":false},{"StartTime":156743.0,"Position":246.838165,"HyperDash":false},{"StartTime":156839.0,"Position":212.31163,"HyperDash":false}]},{"StartTime":156907.0,"Objects":[{"StartTime":156907.0,"Position":213.0,"HyperDash":false}]},{"StartTime":156975.0,"Objects":[{"StartTime":156975.0,"Position":214.0,"HyperDash":false}]},{"StartTime":157043.0,"Objects":[{"StartTime":157043.0,"Position":215.0,"HyperDash":false}]},{"StartTime":157111.0,"Objects":[{"StartTime":157111.0,"Position":216.0,"HyperDash":false},{"StartTime":157247.0,"Position":114.869156,"HyperDash":false}]},{"StartTime":157384.0,"Objects":[{"StartTime":157384.0,"Position":3.0,"HyperDash":false},{"StartTime":157520.0,"Position":104.052589,"HyperDash":false}]},{"StartTime":157657.0,"Objects":[{"StartTime":157657.0,"Position":124.0,"HyperDash":false},{"StartTime":157793.0,"Position":225.052582,"HyperDash":false}]},{"StartTime":157930.0,"Objects":[{"StartTime":157930.0,"Position":13.0,"HyperDash":false},{"StartTime":158066.0,"Position":114.052589,"HyperDash":false}]},{"StartTime":158202.0,"Objects":[{"StartTime":158202.0,"Position":134.0,"HyperDash":false},{"StartTime":158338.0,"Position":235.052582,"HyperDash":false}]},{"StartTime":158475.0,"Objects":[{"StartTime":158475.0,"Position":23.0,"HyperDash":false},{"StartTime":158611.0,"Position":124.052589,"HyperDash":false}]},{"StartTime":158748.0,"Objects":[{"StartTime":158748.0,"Position":144.0,"HyperDash":false},{"StartTime":158884.0,"Position":245.052582,"HyperDash":false}]},{"StartTime":159021.0,"Objects":[{"StartTime":159021.0,"Position":33.0,"HyperDash":false},{"StartTime":159157.0,"Position":134.052582,"HyperDash":false}]},{"StartTime":159293.0,"Objects":[{"StartTime":159293.0,"Position":154.0,"HyperDash":false},{"StartTime":159429.0,"Position":255.052582,"HyperDash":false}]},{"StartTime":159566.0,"Objects":[{"StartTime":159566.0,"Position":43.0,"HyperDash":false},{"StartTime":159702.0,"Position":144.052582,"HyperDash":false}]},{"StartTime":159839.0,"Objects":[{"StartTime":159839.0,"Position":164.0,"HyperDash":false},{"StartTime":159975.0,"Position":265.052582,"HyperDash":false}]},{"StartTime":160112.0,"Objects":[{"StartTime":160112.0,"Position":53.0,"HyperDash":false},{"StartTime":160248.0,"Position":154.052582,"HyperDash":false}]},{"StartTime":160384.0,"Objects":[{"StartTime":160384.0,"Position":174.0,"HyperDash":false},{"StartTime":160520.0,"Position":275.052582,"HyperDash":false}]},{"StartTime":160657.0,"Objects":[{"StartTime":160657.0,"Position":63.0,"HyperDash":false},{"StartTime":160793.0,"Position":164.052582,"HyperDash":false}]},{"StartTime":160930.0,"Objects":[{"StartTime":160930.0,"Position":184.0,"HyperDash":false},{"StartTime":161066.0,"Position":285.052582,"HyperDash":true}]},{"StartTime":161202.0,"Objects":[{"StartTime":161202.0,"Position":73.0,"HyperDash":false},{"StartTime":161338.0,"Position":174.052582,"HyperDash":false}]},{"StartTime":161475.0,"Objects":[{"StartTime":161475.0,"Position":300.0,"HyperDash":false},{"StartTime":161611.0,"Position":401.130859,"HyperDash":false}]},{"StartTime":161748.0,"Objects":[{"StartTime":161748.0,"Position":512.0,"HyperDash":false},{"StartTime":161884.0,"Position":410.818481,"HyperDash":false}]},{"StartTime":162021.0,"Objects":[{"StartTime":162021.0,"Position":391.0,"HyperDash":false},{"StartTime":162157.0,"Position":289.818481,"HyperDash":false}]},{"StartTime":162294.0,"Objects":[{"StartTime":162294.0,"Position":502.0,"HyperDash":false},{"StartTime":162430.0,"Position":400.818481,"HyperDash":false}]},{"StartTime":162566.0,"Objects":[{"StartTime":162566.0,"Position":381.0,"HyperDash":false},{"StartTime":162702.0,"Position":279.818481,"HyperDash":false}]},{"StartTime":162839.0,"Objects":[{"StartTime":162839.0,"Position":492.0,"HyperDash":false},{"StartTime":162975.0,"Position":390.818481,"HyperDash":false}]},{"StartTime":163112.0,"Objects":[{"StartTime":163112.0,"Position":371.0,"HyperDash":false},{"StartTime":163248.0,"Position":269.818481,"HyperDash":false}]},{"StartTime":163385.0,"Objects":[{"StartTime":163385.0,"Position":482.0,"HyperDash":false},{"StartTime":163521.0,"Position":380.818481,"HyperDash":false}]},{"StartTime":163657.0,"Objects":[{"StartTime":163657.0,"Position":361.0,"HyperDash":false},{"StartTime":163793.0,"Position":259.818481,"HyperDash":false}]},{"StartTime":163930.0,"Objects":[{"StartTime":163930.0,"Position":472.0,"HyperDash":false},{"StartTime":164066.0,"Position":370.818481,"HyperDash":false}]},{"StartTime":164203.0,"Objects":[{"StartTime":164203.0,"Position":351.0,"HyperDash":false},{"StartTime":164339.0,"Position":249.818481,"HyperDash":false}]},{"StartTime":164476.0,"Objects":[{"StartTime":164476.0,"Position":462.0,"HyperDash":false},{"StartTime":164612.0,"Position":360.818481,"HyperDash":false}]},{"StartTime":164748.0,"Objects":[{"StartTime":164748.0,"Position":341.0,"HyperDash":false},{"StartTime":164884.0,"Position":239.818481,"HyperDash":false}]},{"StartTime":165021.0,"Objects":[{"StartTime":165021.0,"Position":452.0,"HyperDash":false},{"StartTime":165157.0,"Position":350.818481,"HyperDash":false}]},{"StartTime":165294.0,"Objects":[{"StartTime":165294.0,"Position":331.0,"HyperDash":false},{"StartTime":165430.0,"Position":229.818481,"HyperDash":false}]},{"StartTime":165566.0,"Objects":[{"StartTime":165566.0,"Position":396.0,"HyperDash":false}]},{"StartTime":165702.0,"Objects":[{"StartTime":165702.0,"Position":216.0,"HyperDash":false}]},{"StartTime":165771.0,"Objects":[{"StartTime":165771.0,"Position":216.0,"HyperDash":false}]},{"StartTime":165839.0,"Objects":[{"StartTime":165839.0,"Position":216.0,"HyperDash":false},{"StartTime":165975.0,"Position":229.287262,"HyperDash":false}]},{"StartTime":166112.0,"Objects":[{"StartTime":166112.0,"Position":103.0,"HyperDash":false},{"StartTime":166248.0,"Position":89.1300354,"HyperDash":false}]},{"StartTime":166385.0,"Objects":[{"StartTime":166385.0,"Position":218.0,"HyperDash":false},{"StartTime":166521.0,"Position":204.130035,"HyperDash":false}]},{"StartTime":166658.0,"Objects":[{"StartTime":166658.0,"Position":91.0,"HyperDash":false},{"StartTime":166794.0,"Position":77.1300354,"HyperDash":false}]},{"StartTime":166930.0,"Objects":[{"StartTime":166930.0,"Position":206.0,"HyperDash":false},{"StartTime":167066.0,"Position":192.130035,"HyperDash":false}]},{"StartTime":167203.0,"Objects":[{"StartTime":167203.0,"Position":79.0,"HyperDash":false},{"StartTime":167339.0,"Position":65.1300354,"HyperDash":false}]},{"StartTime":167476.0,"Objects":[{"StartTime":167476.0,"Position":194.0,"HyperDash":false},{"StartTime":167612.0,"Position":180.130035,"HyperDash":false}]},{"StartTime":167749.0,"Objects":[{"StartTime":167749.0,"Position":67.0,"HyperDash":false},{"StartTime":167885.0,"Position":53.1300354,"HyperDash":false}]},{"StartTime":168021.0,"Objects":[{"StartTime":168021.0,"Position":182.0,"HyperDash":false},{"StartTime":168157.0,"Position":168.130035,"HyperDash":false}]},{"StartTime":168294.0,"Objects":[{"StartTime":168294.0,"Position":55.0,"HyperDash":false},{"StartTime":168430.0,"Position":41.1300354,"HyperDash":false}]},{"StartTime":168567.0,"Objects":[{"StartTime":168567.0,"Position":170.0,"HyperDash":false},{"StartTime":168703.0,"Position":156.130035,"HyperDash":false}]},{"StartTime":168840.0,"Objects":[{"StartTime":168840.0,"Position":43.0,"HyperDash":false},{"StartTime":168976.0,"Position":29.1300373,"HyperDash":false}]},{"StartTime":169112.0,"Objects":[{"StartTime":169112.0,"Position":158.0,"HyperDash":false},{"StartTime":169248.0,"Position":144.130035,"HyperDash":false}]},{"StartTime":169385.0,"Objects":[{"StartTime":169385.0,"Position":31.0,"HyperDash":false},{"StartTime":169521.0,"Position":17.1300373,"HyperDash":false}]},{"StartTime":169658.0,"Objects":[{"StartTime":169658.0,"Position":146.0,"HyperDash":false},{"StartTime":169794.0,"Position":132.130035,"HyperDash":false}]},{"StartTime":169930.0,"Objects":[{"StartTime":169930.0,"Position":19.0,"HyperDash":false},{"StartTime":170066.0,"Position":5.13003731,"HyperDash":true}]},{"StartTime":170202.0,"Objects":[{"StartTime":170202.0,"Position":280.0,"HyperDash":false},{"StartTime":170338.0,"Position":266.712738,"HyperDash":false}]},{"StartTime":170475.0,"Objects":[{"StartTime":170475.0,"Position":393.0,"HyperDash":false},{"StartTime":170611.0,"Position":406.869965,"HyperDash":false}]},{"StartTime":170748.0,"Objects":[{"StartTime":170748.0,"Position":278.0,"HyperDash":false},{"StartTime":170884.0,"Position":291.869965,"HyperDash":false}]},{"StartTime":171021.0,"Objects":[{"StartTime":171021.0,"Position":405.0,"HyperDash":false},{"StartTime":171157.0,"Position":418.869965,"HyperDash":false}]},{"StartTime":171293.0,"Objects":[{"StartTime":171293.0,"Position":290.0,"HyperDash":false},{"StartTime":171429.0,"Position":303.869965,"HyperDash":false}]},{"StartTime":171566.0,"Objects":[{"StartTime":171566.0,"Position":417.0,"HyperDash":false},{"StartTime":171702.0,"Position":430.869965,"HyperDash":false}]},{"StartTime":171839.0,"Objects":[{"StartTime":171839.0,"Position":302.0,"HyperDash":false},{"StartTime":171975.0,"Position":315.869965,"HyperDash":false}]},{"StartTime":172112.0,"Objects":[{"StartTime":172112.0,"Position":429.0,"HyperDash":false},{"StartTime":172248.0,"Position":442.869965,"HyperDash":false}]},{"StartTime":172384.0,"Objects":[{"StartTime":172384.0,"Position":512.0,"HyperDash":false}]},{"StartTime":173278.0,"Objects":[{"StartTime":173278.0,"Position":512.0,"HyperDash":false},{"StartTime":173333.0,"Position":461.544647,"HyperDash":false},{"StartTime":173389.0,"Position":440.884155,"HyperDash":false},{"StartTime":173444.0,"Position":394.892883,"HyperDash":false},{"StartTime":173500.0,"Position":373.234924,"HyperDash":false},{"StartTime":173611.0,"Position":383.5925,"HyperDash":false}]},{"StartTime":173722.0,"Objects":[{"StartTime":173722.0,"Position":327.0,"HyperDash":false},{"StartTime":173796.0,"Position":271.28595,"HyperDash":false},{"StartTime":173870.0,"Position":327.0,"HyperDash":false},{"StartTime":173944.0,"Position":271.28595,"HyperDash":false},{"StartTime":174018.0,"Position":327.0,"HyperDash":false},{"StartTime":174092.0,"Position":271.28595,"HyperDash":false}]},{"StartTime":174166.0,"Objects":[{"StartTime":174166.0,"Position":178.0,"HyperDash":false},{"StartTime":174240.0,"Position":233.714035,"HyperDash":false},{"StartTime":174314.0,"Position":178.0,"HyperDash":false},{"StartTime":174388.0,"Position":233.714035,"HyperDash":false},{"StartTime":174462.0,"Position":178.0,"HyperDash":false},{"StartTime":174536.0,"Position":233.714035,"HyperDash":false}]},{"StartTime":174611.0,"Objects":[{"StartTime":174611.0,"Position":92.0,"HyperDash":false},{"StartTime":174685.0,"Position":36.285965,"HyperDash":false},{"StartTime":174759.0,"Position":92.0,"HyperDash":false}]},{"StartTime":174833.0,"Objects":[{"StartTime":174833.0,"Position":99.0,"HyperDash":false},{"StartTime":174907.0,"Position":43.285965,"HyperDash":false},{"StartTime":174981.0,"Position":99.0,"HyperDash":false}]},{"StartTime":175055.0,"Objects":[{"StartTime":175055.0,"Position":179.0,"HyperDash":false},{"StartTime":175166.0,"Position":178.317825,"HyperDash":false}]},{"StartTime":175278.0,"Objects":[{"StartTime":175278.0,"Position":84.0,"HyperDash":false}]},{"StartTime":175389.0,"Objects":[{"StartTime":175389.0,"Position":84.0,"HyperDash":false}]},{"StartTime":175500.0,"Objects":[{"StartTime":175500.0,"Position":84.0,"HyperDash":false},{"StartTime":175611.0,"Position":0.0,"HyperDash":false}]},{"StartTime":175722.0,"Objects":[{"StartTime":175722.0,"Position":176.0,"HyperDash":false},{"StartTime":175833.0,"Position":260.304535,"HyperDash":false}]},{"StartTime":175944.0,"Objects":[{"StartTime":175944.0,"Position":378.0,"HyperDash":false}]},{"StartTime":176055.0,"Objects":[{"StartTime":176055.0,"Position":359.0,"HyperDash":false}]},{"StartTime":176166.0,"Objects":[{"StartTime":176166.0,"Position":380.0,"HyperDash":false}]},{"StartTime":176278.0,"Objects":[{"StartTime":176278.0,"Position":437.0,"HyperDash":false}]},{"StartTime":176389.0,"Objects":[{"StartTime":176389.0,"Position":504.0,"HyperDash":false},{"StartTime":176500.0,"Position":500.1198,"HyperDash":false}]},{"StartTime":176611.0,"Objects":[{"StartTime":176611.0,"Position":464.0,"HyperDash":false},{"StartTime":176722.0,"Position":395.9769,"HyperDash":false}]},{"StartTime":176833.0,"Objects":[{"StartTime":176833.0,"Position":223.0,"HyperDash":false},{"StartTime":176888.0,"Position":222.049377,"HyperDash":false},{"StartTime":176944.0,"Position":265.432465,"HyperDash":false},{"StartTime":176999.0,"Position":288.0414,"HyperDash":false},{"StartTime":177055.0,"Position":311.180634,"HyperDash":false},{"StartTime":177166.0,"Position":320.170959,"HyperDash":false}]},{"StartTime":177278.0,"Objects":[{"StartTime":177278.0,"Position":314.0,"HyperDash":false}]},{"StartTime":177389.0,"Objects":[{"StartTime":177389.0,"Position":393.0,"HyperDash":false}]},{"StartTime":177500.0,"Objects":[{"StartTime":177500.0,"Position":393.0,"HyperDash":false},{"StartTime":177611.0,"Position":476.258362,"HyperDash":true}]},{"StartTime":177722.0,"Objects":[{"StartTime":177722.0,"Position":238.0,"HyperDash":false}]},{"StartTime":177833.0,"Objects":[{"StartTime":177833.0,"Position":238.0,"HyperDash":false}]},{"StartTime":177944.0,"Objects":[{"StartTime":177944.0,"Position":238.0,"HyperDash":false},{"StartTime":178055.0,"Position":154.741638,"HyperDash":false}]},{"StartTime":178166.0,"Objects":[{"StartTime":178166.0,"Position":51.0,"HyperDash":false},{"StartTime":178277.0,"Position":38.63025,"HyperDash":false}]},{"StartTime":178389.0,"Objects":[{"StartTime":178389.0,"Position":136.0,"HyperDash":false},{"StartTime":178500.0,"Position":149.298233,"HyperDash":false}]},{"StartTime":178611.0,"Objects":[{"StartTime":178611.0,"Position":311.0,"HyperDash":false},{"StartTime":178685.0,"Position":365.846741,"HyperDash":false},{"StartTime":178759.0,"Position":311.0,"HyperDash":false}]},{"StartTime":178833.0,"Objects":[{"StartTime":178833.0,"Position":361.0,"HyperDash":false},{"StartTime":178907.0,"Position":415.431976,"HyperDash":false},{"StartTime":178981.0,"Position":361.0,"HyperDash":false}]},{"StartTime":179055.0,"Objects":[{"StartTime":179055.0,"Position":368.0,"HyperDash":false},{"StartTime":179129.0,"Position":407.3476,"HyperDash":false},{"StartTime":179203.0,"Position":368.0,"HyperDash":false}]},{"StartTime":179278.0,"Objects":[{"StartTime":179278.0,"Position":330.0,"HyperDash":false},{"StartTime":179352.0,"Position":344.074615,"HyperDash":false},{"StartTime":179426.0,"Position":330.0,"HyperDash":false}]},{"StartTime":179500.0,"Objects":[{"StartTime":179500.0,"Position":442.0,"HyperDash":false}]},{"StartTime":179574.0,"Objects":[{"StartTime":179574.0,"Position":442.0,"HyperDash":false}]},{"StartTime":179648.0,"Objects":[{"StartTime":179648.0,"Position":442.0,"HyperDash":false}]},{"StartTime":179722.0,"Objects":[{"StartTime":179722.0,"Position":442.0,"HyperDash":false},{"StartTime":179796.0,"Position":427.7541,"HyperDash":false},{"StartTime":179870.0,"Position":442.0,"HyperDash":false}]},{"StartTime":179944.0,"Objects":[{"StartTime":179944.0,"Position":488.0,"HyperDash":false},{"StartTime":180037.0,"Position":417.2783,"HyperDash":false},{"StartTime":180166.0,"Position":350.759735,"HyperDash":false}]},{"StartTime":180389.0,"Objects":[{"StartTime":180389.0,"Position":114.0,"HyperDash":false},{"StartTime":180500.0,"Position":42.9713974,"HyperDash":false}]},{"StartTime":180611.0,"Objects":[{"StartTime":180611.0,"Position":0.0,"HyperDash":false},{"StartTime":180722.0,"Position":70.18777,"HyperDash":false}]},{"StartTime":180833.0,"Objects":[{"StartTime":180833.0,"Position":124.0,"HyperDash":false},{"StartTime":180944.0,"Position":110.17556,"HyperDash":false}]},{"StartTime":181055.0,"Objects":[{"StartTime":181055.0,"Position":201.0,"HyperDash":false},{"StartTime":181166.0,"Position":214.824432,"HyperDash":false}]},{"StartTime":181278.0,"Objects":[{"StartTime":181278.0,"Position":350.0,"HyperDash":false},{"StartTime":181389.0,"Position":414.6709,"HyperDash":false}]},{"StartTime":181500.0,"Objects":[{"StartTime":181500.0,"Position":497.0,"HyperDash":false},{"StartTime":181555.0,"Position":495.534149,"HyperDash":false},{"StartTime":181611.0,"Position":512.0,"HyperDash":false},{"StartTime":181722.0,"Position":497.0,"HyperDash":false}]},{"StartTime":181833.0,"Objects":[{"StartTime":181833.0,"Position":414.0,"HyperDash":false}]},{"StartTime":181944.0,"Objects":[{"StartTime":181944.0,"Position":414.0,"HyperDash":false},{"StartTime":182055.0,"Position":339.763519,"HyperDash":false}]},{"StartTime":182166.0,"Objects":[{"StartTime":182166.0,"Position":254.0,"HyperDash":false}]},{"StartTime":182278.0,"Objects":[{"StartTime":182278.0,"Position":186.0,"HyperDash":false}]},{"StartTime":182389.0,"Objects":[{"StartTime":182389.0,"Position":123.0,"HyperDash":false}]},{"StartTime":182500.0,"Objects":[{"StartTime":182500.0,"Position":89.0,"HyperDash":false}]},{"StartTime":182611.0,"Objects":[{"StartTime":182611.0,"Position":101.0,"HyperDash":false},{"StartTime":182666.0,"Position":108.090492,"HyperDash":false},{"StartTime":182722.0,"Position":101.513573,"HyperDash":false},{"StartTime":182777.0,"Position":95.97452,"HyperDash":false},{"StartTime":182833.0,"Position":76.8446,"HyperDash":false},{"StartTime":182944.0,"Position":79.74396,"HyperDash":false}]},{"StartTime":183055.0,"Objects":[{"StartTime":183055.0,"Position":0.0,"HyperDash":false},{"StartTime":183166.0,"Position":73.6922455,"HyperDash":false}]},{"StartTime":183278.0,"Objects":[{"StartTime":183278.0,"Position":176.0,"HyperDash":false},{"StartTime":183389.0,"Position":242.907639,"HyperDash":false}]},{"StartTime":183500.0,"Objects":[{"StartTime":183500.0,"Position":353.0,"HyperDash":false},{"StartTime":183611.0,"Position":361.0935,"HyperDash":false}]},{"StartTime":183722.0,"Objects":[{"StartTime":183722.0,"Position":473.0,"HyperDash":false},{"StartTime":183833.0,"Position":464.9065,"HyperDash":false}]},{"StartTime":183944.0,"Objects":[{"StartTime":183944.0,"Position":447.0,"HyperDash":false}]},{"StartTime":184055.0,"Objects":[{"StartTime":184055.0,"Position":447.0,"HyperDash":false}]},{"StartTime":184166.0,"Objects":[{"StartTime":184166.0,"Position":447.0,"HyperDash":false}]},{"StartTime":184277.0,"Objects":[{"StartTime":184277.0,"Position":463.0,"HyperDash":false}]},{"StartTime":184388.0,"Objects":[{"StartTime":184388.0,"Position":487.0,"HyperDash":false},{"StartTime":184499.0,"Position":478.9065,"HyperDash":false}]},{"StartTime":184611.0,"Objects":[{"StartTime":184611.0,"Position":344.0,"HyperDash":false},{"StartTime":184722.0,"Position":335.9065,"HyperDash":false}]},{"StartTime":184833.0,"Objects":[{"StartTime":184833.0,"Position":233.0,"HyperDash":false},{"StartTime":184944.0,"Position":153.960419,"HyperDash":false}]},{"StartTime":185055.0,"Objects":[{"StartTime":185055.0,"Position":19.0,"HyperDash":false},{"StartTime":185166.0,"Position":98.20671,"HyperDash":false}]},{"StartTime":185278.0,"Objects":[{"StartTime":185278.0,"Position":224.0,"HyperDash":false}]},{"StartTime":185389.0,"Objects":[{"StartTime":185389.0,"Position":229.0,"HyperDash":false}]},{"StartTime":185500.0,"Objects":[{"StartTime":185500.0,"Position":203.0,"HyperDash":false}]},{"StartTime":185611.0,"Objects":[{"StartTime":185611.0,"Position":148.0,"HyperDash":false}]},{"StartTime":185722.0,"Objects":[{"StartTime":185722.0,"Position":80.0,"HyperDash":false},{"StartTime":185833.0,"Position":31.5825539,"HyperDash":true}]},{"StartTime":185944.0,"Objects":[{"StartTime":185944.0,"Position":227.0,"HyperDash":false},{"StartTime":186018.0,"Position":266.7068,"HyperDash":false},{"StartTime":186092.0,"Position":227.0,"HyperDash":false}]},{"StartTime":186166.0,"Objects":[{"StartTime":186166.0,"Position":306.0,"HyperDash":false},{"StartTime":186240.0,"Position":360.619873,"HyperDash":false},{"StartTime":186314.0,"Position":306.0,"HyperDash":false}]},{"StartTime":186388.0,"Objects":[{"StartTime":186388.0,"Position":358.0,"HyperDash":false},{"StartTime":186462.0,"Position":412.8009,"HyperDash":false},{"StartTime":186536.0,"Position":358.0,"HyperDash":false}]},{"StartTime":186611.0,"Objects":[{"StartTime":186611.0,"Position":366.0,"HyperDash":false},{"StartTime":186685.0,"Position":406.4224,"HyperDash":false},{"StartTime":186759.0,"Position":366.0,"HyperDash":false}]},{"StartTime":186833.0,"Objects":[{"StartTime":186833.0,"Position":512.0,"HyperDash":false}]},{"StartTime":186907.0,"Objects":[{"StartTime":186907.0,"Position":512.0,"HyperDash":false}]},{"StartTime":186981.0,"Objects":[{"StartTime":186981.0,"Position":512.0,"HyperDash":false}]},{"StartTime":187055.0,"Objects":[{"StartTime":187055.0,"Position":512.0,"HyperDash":false},{"StartTime":187129.0,"Position":471.5776,"HyperDash":false},{"StartTime":187203.0,"Position":512.0,"HyperDash":false}]},{"StartTime":187277.0,"Objects":[{"StartTime":187277.0,"Position":469.0,"HyperDash":false},{"StartTime":187333.0,"Position":428.048767,"HyperDash":false},{"StartTime":187425.0,"Position":370.993652,"HyperDash":false}]},{"StartTime":187500.0,"Objects":[{"StartTime":187500.0,"Position":346.0,"HyperDash":false},{"StartTime":187555.0,"Position":324.884857,"HyperDash":false},{"StartTime":187611.0,"Position":281.551849,"HyperDash":false},{"StartTime":187666.0,"Position":288.422363,"HyperDash":false},{"StartTime":187722.0,"Position":306.796173,"HyperDash":false},{"StartTime":187833.0,"Position":357.976379,"HyperDash":false}]},{"StartTime":187944.0,"Objects":[{"StartTime":187944.0,"Position":326.0,"HyperDash":false}]},{"StartTime":188055.0,"Objects":[{"StartTime":188055.0,"Position":397.0,"HyperDash":false},{"StartTime":188166.0,"Position":479.230957,"HyperDash":true}]},{"StartTime":188278.0,"Objects":[{"StartTime":188278.0,"Position":269.0,"HyperDash":false}]},{"StartTime":188389.0,"Objects":[{"StartTime":188389.0,"Position":269.0,"HyperDash":false},{"StartTime":188500.0,"Position":220.272171,"HyperDash":false}]},{"StartTime":188611.0,"Objects":[{"StartTime":188611.0,"Position":209.0,"HyperDash":false},{"StartTime":188722.0,"Position":124.709274,"HyperDash":false}]},{"StartTime":188833.0,"Objects":[{"StartTime":188833.0,"Position":13.0,"HyperDash":false},{"StartTime":188944.0,"Position":97.2907257,"HyperDash":false}]},{"StartTime":189055.0,"Objects":[{"StartTime":189055.0,"Position":163.0,"HyperDash":false},{"StartTime":189166.0,"Position":78.7092743,"HyperDash":false}]},{"StartTime":189277.0,"Objects":[{"StartTime":189277.0,"Position":133.0,"HyperDash":false},{"StartTime":189388.0,"Position":217.403992,"HyperDash":false}]},{"StartTime":189499.0,"Objects":[{"StartTime":189499.0,"Position":248.0,"HyperDash":false},{"StartTime":189573.0,"Position":288.0694,"HyperDash":false},{"StartTime":189647.0,"Position":248.0,"HyperDash":false}]},{"StartTime":189721.0,"Objects":[{"StartTime":189721.0,"Position":309.0,"HyperDash":false},{"StartTime":189795.0,"Position":323.2212,"HyperDash":false},{"StartTime":189869.0,"Position":309.0,"HyperDash":false}]},{"StartTime":189944.0,"Objects":[{"StartTime":189944.0,"Position":414.0,"HyperDash":false},{"StartTime":190018.0,"Position":398.833527,"HyperDash":false},{"StartTime":190092.0,"Position":414.0,"HyperDash":false}]},{"StartTime":190166.0,"Objects":[{"StartTime":190166.0,"Position":468.0,"HyperDash":false},{"StartTime":190240.0,"Position":482.2459,"HyperDash":false},{"StartTime":190314.0,"Position":468.0,"HyperDash":false},{"StartTime":190388.0,"Position":482.2459,"HyperDash":false},{"StartTime":190462.0,"Position":468.0,"HyperDash":false},{"StartTime":190536.0,"Position":482.2459,"HyperDash":false}]},{"StartTime":190611.0,"Objects":[{"StartTime":190611.0,"Position":408.0,"HyperDash":false},{"StartTime":190685.0,"Position":422.909973,"HyperDash":false},{"StartTime":190759.0,"Position":408.0,"HyperDash":false}]},{"StartTime":190833.0,"Objects":[{"StartTime":190833.0,"Position":399.0,"HyperDash":false},{"StartTime":190907.0,"Position":413.2212,"HyperDash":false},{"StartTime":190981.0,"Position":399.0,"HyperDash":false}]},{"StartTime":191055.0,"Objects":[{"StartTime":191055.0,"Position":311.0,"HyperDash":false},{"StartTime":191148.0,"Position":357.4903,"HyperDash":false},{"StartTime":191277.0,"Position":394.428223,"HyperDash":false}]},{"StartTime":191389.0,"Objects":[{"StartTime":191389.0,"Position":272.0,"HyperDash":false}]},{"StartTime":191500.0,"Objects":[{"StartTime":191500.0,"Position":272.0,"HyperDash":false},{"StartTime":191611.0,"Position":336.857483,"HyperDash":false}]},{"StartTime":191722.0,"Objects":[{"StartTime":191722.0,"Position":461.0,"HyperDash":false},{"StartTime":191833.0,"Position":383.333038,"HyperDash":false}]},{"StartTime":191944.0,"Objects":[{"StartTime":191944.0,"Position":215.0,"HyperDash":false}]},{"StartTime":192055.0,"Objects":[{"StartTime":192055.0,"Position":189.0,"HyperDash":false}]},{"StartTime":192166.0,"Objects":[{"StartTime":192166.0,"Position":157.0,"HyperDash":false}]},{"StartTime":192277.0,"Objects":[{"StartTime":192277.0,"Position":123.0,"HyperDash":false}]},{"StartTime":192389.0,"Objects":[{"StartTime":192389.0,"Position":89.0,"HyperDash":false},{"StartTime":192500.0,"Position":17.9128532,"HyperDash":false}]},{"StartTime":192611.0,"Objects":[{"StartTime":192611.0,"Position":54.0,"HyperDash":false},{"StartTime":192722.0,"Position":52.84656,"HyperDash":false}]},{"StartTime":192833.0,"Objects":[{"StartTime":192833.0,"Position":208.0,"HyperDash":false},{"StartTime":192944.0,"Position":194.175568,"HyperDash":false}]},{"StartTime":193055.0,"Objects":[{"StartTime":193055.0,"Position":275.0,"HyperDash":false},{"StartTime":193166.0,"Position":288.824432,"HyperDash":false}]},{"StartTime":193277.0,"Objects":[{"StartTime":193277.0,"Position":415.0,"HyperDash":false}]},{"StartTime":193389.0,"Objects":[{"StartTime":193389.0,"Position":461.0,"HyperDash":false}]},{"StartTime":193500.0,"Objects":[{"StartTime":193500.0,"Position":458.0,"HyperDash":false}]},{"StartTime":193611.0,"Objects":[{"StartTime":193611.0,"Position":413.0,"HyperDash":false}]},{"StartTime":193722.0,"Objects":[{"StartTime":193722.0,"Position":329.0,"HyperDash":false},{"StartTime":193833.0,"Position":246.696991,"HyperDash":false}]},{"StartTime":193944.0,"Objects":[{"StartTime":193944.0,"Position":377.0,"HyperDash":false},{"StartTime":194055.0,"Position":459.303,"HyperDash":false}]},{"StartTime":194166.0,"Objects":[{"StartTime":194166.0,"Position":491.0,"HyperDash":false},{"StartTime":194259.0,"Position":480.782471,"HyperDash":false},{"StartTime":194388.0,"Position":427.072449,"HyperDash":true}]},{"StartTime":194611.0,"Objects":[{"StartTime":194611.0,"Position":51.0,"HyperDash":false},{"StartTime":194666.0,"Position":105.644623,"HyperDash":false},{"StartTime":194722.0,"Position":112.4984,"HyperDash":false},{"StartTime":194777.0,"Position":153.00119,"HyperDash":false},{"StartTime":194833.0,"Position":192.190445,"HyperDash":false},{"StartTime":194926.0,"Position":250.960892,"HyperDash":false},{"StartTime":195055.0,"Position":334.876526,"HyperDash":false}]},{"StartTime":195166.0,"Objects":[{"StartTime":195166.0,"Position":165.0,"HyperDash":false}]},{"StartTime":195277.0,"Objects":[{"StartTime":195277.0,"Position":201.0,"HyperDash":false},{"StartTime":195388.0,"Position":256.006256,"HyperDash":true}]},{"StartTime":195500.0,"Objects":[{"StartTime":195500.0,"Position":47.0,"HyperDash":false},{"StartTime":195611.0,"Position":70.89899,"HyperDash":false}]},{"StartTime":195722.0,"Objects":[{"StartTime":195722.0,"Position":238.0,"HyperDash":false}]},{"StartTime":195833.0,"Objects":[{"StartTime":195833.0,"Position":320.0,"HyperDash":false}]},{"StartTime":195944.0,"Objects":[{"StartTime":195944.0,"Position":402.0,"HyperDash":false}]},{"StartTime":196055.0,"Objects":[{"StartTime":196055.0,"Position":462.0,"HyperDash":false}]},{"StartTime":196166.0,"Objects":[{"StartTime":196166.0,"Position":484.0,"HyperDash":false},{"StartTime":196222.0,"Position":495.415375,"HyperDash":false},{"StartTime":196314.0,"Position":425.076019,"HyperDash":false}]},{"StartTime":196389.0,"Objects":[{"StartTime":196389.0,"Position":354.0,"HyperDash":false},{"StartTime":196463.0,"Position":360.907166,"HyperDash":false},{"StartTime":196537.0,"Position":354.0,"HyperDash":false}]},{"StartTime":196611.0,"Objects":[{"StartTime":196611.0,"Position":290.0,"HyperDash":false},{"StartTime":196685.0,"Position":296.907166,"HyperDash":false},{"StartTime":196759.0,"Position":290.0,"HyperDash":false},{"StartTime":196833.0,"Position":296.907166,"HyperDash":false}]},{"StartTime":196907.0,"Objects":[{"StartTime":196907.0,"Position":242.0,"HyperDash":false},{"StartTime":196981.0,"Position":233.986115,"HyperDash":false}]},{"StartTime":197055.0,"Objects":[{"StartTime":197055.0,"Position":192.0,"HyperDash":false},{"StartTime":197129.0,"Position":199.028641,"HyperDash":false},{"StartTime":197203.0,"Position":192.0,"HyperDash":false}]},{"StartTime":197277.0,"Objects":[{"StartTime":197277.0,"Position":108.0,"HyperDash":false},{"StartTime":197351.0,"Position":51.770916,"HyperDash":false},{"StartTime":197425.0,"Position":108.0,"HyperDash":false},{"StartTime":197499.0,"Position":51.770916,"HyperDash":false},{"StartTime":197573.0,"Position":108.0,"HyperDash":false},{"StartTime":197647.0,"Position":51.770916,"HyperDash":false}]},{"StartTime":197722.0,"Objects":[{"StartTime":197722.0,"Position":0.0,"HyperDash":false},{"StartTime":197815.0,"Position":60.3444443,"HyperDash":false},{"StartTime":197944.0,"Position":111.30835,"HyperDash":false}]},{"StartTime":198166.0,"Objects":[{"StartTime":198166.0,"Position":391.0,"HyperDash":false},{"StartTime":198240.0,"Position":446.9797,"HyperDash":false},{"StartTime":198314.0,"Position":391.0,"HyperDash":false},{"StartTime":198388.0,"Position":446.9797,"HyperDash":false},{"StartTime":198462.0,"Position":391.0,"HyperDash":false},{"StartTime":198536.0,"Position":446.9797,"HyperDash":false}]},{"StartTime":198611.0,"Objects":[{"StartTime":198611.0,"Position":317.0,"HyperDash":false}]},{"StartTime":198685.0,"Objects":[{"StartTime":198685.0,"Position":317.0,"HyperDash":false}]},{"StartTime":198759.0,"Objects":[{"StartTime":198759.0,"Position":317.0,"HyperDash":false}]},{"StartTime":198833.0,"Objects":[{"StartTime":198833.0,"Position":317.0,"HyperDash":false},{"StartTime":198907.0,"Position":261.0203,"HyperDash":false},{"StartTime":198981.0,"Position":317.0,"HyperDash":false}]},{"StartTime":199055.0,"Objects":[{"StartTime":199055.0,"Position":392.0,"HyperDash":false},{"StartTime":199129.0,"Position":400.7968,"HyperDash":false},{"StartTime":199203.0,"Position":392.0,"HyperDash":false},{"StartTime":199277.0,"Position":400.7968,"HyperDash":false},{"StartTime":199351.0,"Position":392.0,"HyperDash":false},{"StartTime":199425.0,"Position":400.7968,"HyperDash":false}]},{"StartTime":199500.0,"Objects":[{"StartTime":199500.0,"Position":494.0,"HyperDash":false},{"StartTime":199574.0,"Position":485.2032,"HyperDash":false},{"StartTime":199648.0,"Position":494.0,"HyperDash":false},{"StartTime":199722.0,"Position":485.2032,"HyperDash":false},{"StartTime":199796.0,"Position":494.0,"HyperDash":false},{"StartTime":199870.0,"Position":485.2032,"HyperDash":false}]},{"StartTime":199944.0,"Objects":[{"StartTime":199944.0,"Position":400.0,"HyperDash":false},{"StartTime":200018.0,"Position":344.0203,"HyperDash":false},{"StartTime":200092.0,"Position":400.0,"HyperDash":false},{"StartTime":200166.0,"Position":344.0203,"HyperDash":false},{"StartTime":200240.0,"Position":400.0,"HyperDash":false},{"StartTime":200314.0,"Position":344.0203,"HyperDash":false}]},{"StartTime":200389.0,"Objects":[{"StartTime":200389.0,"Position":267.0,"HyperDash":false}]},{"StartTime":200463.0,"Objects":[{"StartTime":200463.0,"Position":267.0,"HyperDash":false}]},{"StartTime":200537.0,"Objects":[{"StartTime":200537.0,"Position":267.0,"HyperDash":false}]},{"StartTime":200611.0,"Objects":[{"StartTime":200611.0,"Position":267.0,"HyperDash":false},{"StartTime":200685.0,"Position":211.0203,"HyperDash":false},{"StartTime":200759.0,"Position":267.0,"HyperDash":false}]},{"StartTime":200833.0,"Objects":[{"StartTime":200833.0,"Position":121.0,"HyperDash":false},{"StartTime":200907.0,"Position":112.203186,"HyperDash":false},{"StartTime":200981.0,"Position":121.0,"HyperDash":false},{"StartTime":201055.0,"Position":112.203186,"HyperDash":false},{"StartTime":201129.0,"Position":121.0,"HyperDash":false},{"StartTime":201203.0,"Position":112.203186,"HyperDash":false}]},{"StartTime":201277.0,"Objects":[{"StartTime":201277.0,"Position":179.0,"HyperDash":false},{"StartTime":201351.0,"Position":170.203186,"HyperDash":false},{"StartTime":201425.0,"Position":179.0,"HyperDash":false}]},{"StartTime":201500.0,"Objects":[{"StartTime":201500.0,"Position":67.0,"HyperDash":false},{"StartTime":201574.0,"Position":75.796814,"HyperDash":false},{"StartTime":201648.0,"Position":67.0,"HyperDash":false}]},{"StartTime":201722.0,"Objects":[{"StartTime":201722.0,"Position":11.0,"HyperDash":false}]},{"StartTime":201776.0,"Objects":[{"StartTime":201776.0,"Position":88.0,"HyperDash":false},{"StartTime":201830.0,"Position":257.0,"HyperDash":false},{"StartTime":201885.0,"Position":175.0,"HyperDash":false},{"StartTime":201940.0,"Position":38.0,"HyperDash":false},{"StartTime":201994.0,"Position":283.0,"HyperDash":false},{"StartTime":202049.0,"Position":138.0,"HyperDash":false},{"StartTime":202104.0,"Position":102.0,"HyperDash":false},{"StartTime":202158.0,"Position":494.0,"HyperDash":false},{"StartTime":202213.0,"Position":54.0,"HyperDash":false},{"StartTime":202268.0,"Position":29.0,"HyperDash":false},{"StartTime":202322.0,"Position":69.0,"HyperDash":false},{"StartTime":202377.0,"Position":110.0,"HyperDash":false},{"StartTime":202432.0,"Position":167.0,"HyperDash":false},{"StartTime":202486.0,"Position":56.0,"HyperDash":false},{"StartTime":202541.0,"Position":10.0,"HyperDash":false},{"StartTime":202596.0,"Position":308.0,"HyperDash":false},{"StartTime":202651.0,"Position":288.0,"HyperDash":false},{"StartTime":202705.0,"Position":57.0,"HyperDash":false},{"StartTime":202760.0,"Position":258.0,"HyperDash":false},{"StartTime":202815.0,"Position":180.0,"HyperDash":false},{"StartTime":202869.0,"Position":198.0,"HyperDash":false},{"StartTime":202924.0,"Position":211.0,"HyperDash":false},{"StartTime":202979.0,"Position":503.0,"HyperDash":false},{"StartTime":203033.0,"Position":324.0,"HyperDash":false},{"StartTime":203088.0,"Position":20.0,"HyperDash":false},{"StartTime":203143.0,"Position":169.0,"HyperDash":false},{"StartTime":203197.0,"Position":93.0,"HyperDash":false},{"StartTime":203252.0,"Position":267.0,"HyperDash":false},{"StartTime":203307.0,"Position":276.0,"HyperDash":false},{"StartTime":203361.0,"Position":367.0,"HyperDash":false},{"StartTime":203416.0,"Position":409.0,"HyperDash":false},{"StartTime":203471.0,"Position":117.0,"HyperDash":false},{"StartTime":203526.0,"Position":226.0,"HyperDash":false},{"StartTime":203580.0,"Position":469.0,"HyperDash":false},{"StartTime":203635.0,"Position":267.0,"HyperDash":false},{"StartTime":203690.0,"Position":477.0,"HyperDash":false},{"StartTime":203744.0,"Position":282.0,"HyperDash":false},{"StartTime":203799.0,"Position":216.0,"HyperDash":false},{"StartTime":203854.0,"Position":106.0,"HyperDash":false},{"StartTime":203908.0,"Position":353.0,"HyperDash":false},{"StartTime":203963.0,"Position":162.0,"HyperDash":false},{"StartTime":204018.0,"Position":473.0,"HyperDash":false},{"StartTime":204072.0,"Position":260.0,"HyperDash":false},{"StartTime":204127.0,"Position":367.0,"HyperDash":false},{"StartTime":204182.0,"Position":409.0,"HyperDash":false},{"StartTime":204236.0,"Position":145.0,"HyperDash":false},{"StartTime":204291.0,"Position":330.0,"HyperDash":false},{"StartTime":204346.0,"Position":104.0,"HyperDash":false},{"StartTime":204401.0,"Position":412.0,"HyperDash":false},{"StartTime":204455.0,"Position":104.0,"HyperDash":false},{"StartTime":204510.0,"Position":396.0,"HyperDash":false},{"StartTime":204565.0,"Position":192.0,"HyperDash":false},{"StartTime":204619.0,"Position":446.0,"HyperDash":false},{"StartTime":204674.0,"Position":110.0,"HyperDash":false},{"StartTime":204729.0,"Position":372.0,"HyperDash":false},{"StartTime":204783.0,"Position":100.0,"HyperDash":false},{"StartTime":204838.0,"Position":161.0,"HyperDash":false},{"StartTime":204893.0,"Position":456.0,"HyperDash":false},{"StartTime":204947.0,"Position":187.0,"HyperDash":false},{"StartTime":205002.0,"Position":99.0,"HyperDash":false},{"StartTime":205057.0,"Position":197.0,"HyperDash":false},{"StartTime":205111.0,"Position":116.0,"HyperDash":false},{"StartTime":205166.0,"Position":496.0,"HyperDash":false},{"StartTime":205221.0,"Position":143.0,"HyperDash":false},{"StartTime":205276.0,"Position":431.0,"HyperDash":false}]},{"StartTime":207943.0,"Objects":[{"StartTime":207943.0,"Position":171.0,"HyperDash":false},{"StartTime":208011.0,"Position":175.536011,"HyperDash":false},{"StartTime":208079.0,"Position":171.0,"HyperDash":false},{"StartTime":208147.0,"Position":175.536011,"HyperDash":false},{"StartTime":208215.0,"Position":171.0,"HyperDash":false},{"StartTime":208283.0,"Position":175.536011,"HyperDash":false},{"StartTime":208352.0,"Position":171.0,"HyperDash":false},{"StartTime":208420.0,"Position":175.536011,"HyperDash":false},{"StartTime":208488.0,"Position":171.0,"HyperDash":false},{"StartTime":208556.0,"Position":175.536011,"HyperDash":false},{"StartTime":208624.0,"Position":171.0,"HyperDash":false},{"StartTime":208693.0,"Position":175.536011,"HyperDash":false},{"StartTime":208761.0,"Position":171.0,"HyperDash":false},{"StartTime":208829.0,"Position":175.536011,"HyperDash":false},{"StartTime":208897.0,"Position":171.0,"HyperDash":false},{"StartTime":208965.0,"Position":175.536011,"HyperDash":false},{"StartTime":209033.0,"Position":171.0,"HyperDash":false},{"StartTime":209102.0,"Position":175.536011,"HyperDash":false},{"StartTime":209170.0,"Position":171.0,"HyperDash":false},{"StartTime":209238.0,"Position":175.536011,"HyperDash":false},{"StartTime":209306.0,"Position":171.0,"HyperDash":false},{"StartTime":209374.0,"Position":175.536011,"HyperDash":false},{"StartTime":209443.0,"Position":171.0,"HyperDash":false},{"StartTime":209511.0,"Position":175.536011,"HyperDash":false},{"StartTime":209579.0,"Position":171.0,"HyperDash":false},{"StartTime":209647.0,"Position":175.536011,"HyperDash":false},{"StartTime":209715.0,"Position":171.0,"HyperDash":false},{"StartTime":209783.0,"Position":175.536011,"HyperDash":false},{"StartTime":209852.0,"Position":171.0,"HyperDash":false},{"StartTime":209920.0,"Position":175.536011,"HyperDash":false},{"StartTime":209988.0,"Position":171.0,"HyperDash":false},{"StartTime":210056.0,"Position":175.536011,"HyperDash":false}]},{"StartTime":210124.0,"Objects":[{"StartTime":210124.0,"Position":85.0,"HyperDash":false}]},{"StartTime":210329.0,"Objects":[{"StartTime":210329.0,"Position":73.0,"HyperDash":false}]},{"StartTime":210533.0,"Objects":[{"StartTime":210533.0,"Position":243.0,"HyperDash":false}]},{"StartTime":210670.0,"Objects":[{"StartTime":210670.0,"Position":122.0,"HyperDash":false}]},{"StartTime":210875.0,"Objects":[{"StartTime":210875.0,"Position":61.0,"HyperDash":false}]},{"StartTime":211079.0,"Objects":[{"StartTime":211079.0,"Position":246.0,"HyperDash":false}]},{"StartTime":211215.0,"Objects":[{"StartTime":211215.0,"Position":294.0,"HyperDash":false},{"StartTime":211283.0,"Position":253.395386,"HyperDash":false},{"StartTime":211351.0,"Position":294.0,"HyperDash":false},{"StartTime":211419.0,"Position":253.395386,"HyperDash":false}]},{"StartTime":211488.0,"Objects":[{"StartTime":211488.0,"Position":369.0,"HyperDash":false},{"StartTime":211556.0,"Position":409.5123,"HyperDash":false},{"StartTime":211624.0,"Position":369.0,"HyperDash":false},{"StartTime":211692.0,"Position":409.5123,"HyperDash":false}]},{"StartTime":211761.0,"Objects":[{"StartTime":211761.0,"Position":319.0,"HyperDash":false},{"StartTime":211829.0,"Position":306.78772,"HyperDash":false},{"StartTime":211897.0,"Position":319.0,"HyperDash":false},{"StartTime":211965.0,"Position":306.78772,"HyperDash":false}]},{"StartTime":212033.0,"Objects":[{"StartTime":212033.0,"Position":221.0,"HyperDash":false},{"StartTime":212101.0,"Position":209.0618,"HyperDash":false},{"StartTime":212169.0,"Position":221.0,"HyperDash":false},{"StartTime":212237.0,"Position":209.0618,"HyperDash":false}]},{"StartTime":212306.0,"Objects":[{"StartTime":212306.0,"Position":121.0,"HyperDash":false}]},{"StartTime":212374.0,"Objects":[{"StartTime":212374.0,"Position":112.0,"HyperDash":false}]},{"StartTime":212442.0,"Objects":[{"StartTime":212442.0,"Position":103.0,"HyperDash":false}]},{"StartTime":212579.0,"Objects":[{"StartTime":212579.0,"Position":78.0,"HyperDash":false}]},{"StartTime":212647.0,"Objects":[{"StartTime":212647.0,"Position":87.0,"HyperDash":false}]},{"StartTime":212715.0,"Objects":[{"StartTime":212715.0,"Position":96.0,"HyperDash":false}]},{"StartTime":212851.0,"Objects":[{"StartTime":212851.0,"Position":0.0,"HyperDash":false},{"StartTime":212919.0,"Position":12.8453636,"HyperDash":false},{"StartTime":212987.0,"Position":0.0,"HyperDash":false}]},{"StartTime":213124.0,"Objects":[{"StartTime":213124.0,"Position":77.0,"HyperDash":false},{"StartTime":213192.0,"Position":65.0618057,"HyperDash":false},{"StartTime":213260.0,"Position":77.0,"HyperDash":false}]},{"StartTime":213397.0,"Objects":[{"StartTime":213397.0,"Position":131.0,"HyperDash":false},{"StartTime":213465.0,"Position":171.788834,"HyperDash":false},{"StartTime":213533.0,"Position":131.0,"HyperDash":false},{"StartTime":213601.0,"Position":171.788834,"HyperDash":false}]},{"StartTime":213670.0,"Objects":[{"StartTime":213670.0,"Position":261.0,"HyperDash":false},{"StartTime":213738.0,"Position":301.6046,"HyperDash":false},{"StartTime":213806.0,"Position":261.0,"HyperDash":false},{"StartTime":213874.0,"Position":301.6046,"HyperDash":false}]},{"StartTime":213942.0,"Objects":[{"StartTime":213942.0,"Position":366.0,"HyperDash":false},{"StartTime":214010.0,"Position":353.78772,"HyperDash":false},{"StartTime":214078.0,"Position":366.0,"HyperDash":false},{"StartTime":214146.0,"Position":353.78772,"HyperDash":false}]},{"StartTime":214215.0,"Objects":[{"StartTime":214215.0,"Position":456.0,"HyperDash":false},{"StartTime":214283.0,"Position":443.78772,"HyperDash":false},{"StartTime":214351.0,"Position":456.0,"HyperDash":false},{"StartTime":214419.0,"Position":443.78772,"HyperDash":false}]},{"StartTime":214488.0,"Objects":[{"StartTime":214488.0,"Position":490.0,"HyperDash":false}]},{"StartTime":214556.0,"Objects":[{"StartTime":214556.0,"Position":487.0,"HyperDash":false}]},{"StartTime":214624.0,"Objects":[{"StartTime":214624.0,"Position":484.0,"HyperDash":false}]},{"StartTime":214761.0,"Objects":[{"StartTime":214761.0,"Position":419.0,"HyperDash":false}]},{"StartTime":214829.0,"Objects":[{"StartTime":214829.0,"Position":422.0,"HyperDash":false}]},{"StartTime":214897.0,"Objects":[{"StartTime":214897.0,"Position":425.0,"HyperDash":false}]},{"StartTime":215033.0,"Objects":[{"StartTime":215033.0,"Position":344.0,"HyperDash":false}]},{"StartTime":215101.0,"Objects":[{"StartTime":215101.0,"Position":336.0,"HyperDash":false}]},{"StartTime":215170.0,"Objects":[{"StartTime":215170.0,"Position":328.0,"HyperDash":false},{"StartTime":215306.0,"Position":243.560242,"HyperDash":false}]},{"StartTime":215579.0,"Objects":[{"StartTime":215579.0,"Position":238.0,"HyperDash":false},{"StartTime":215647.0,"Position":250.21228,"HyperDash":false},{"StartTime":215715.0,"Position":238.0,"HyperDash":false},{"StartTime":215783.0,"Position":250.21228,"HyperDash":false}]},{"StartTime":215852.0,"Objects":[{"StartTime":215852.0,"Position":182.0,"HyperDash":false},{"StartTime":215920.0,"Position":169.78772,"HyperDash":false},{"StartTime":215988.0,"Position":182.0,"HyperDash":false},{"StartTime":216056.0,"Position":169.78772,"HyperDash":false}]},{"StartTime":216124.0,"Objects":[{"StartTime":216124.0,"Position":90.0,"HyperDash":false},{"StartTime":216192.0,"Position":49.2111626,"HyperDash":false},{"StartTime":216260.0,"Position":90.0,"HyperDash":false},{"StartTime":216328.0,"Position":49.2111626,"HyperDash":false}]},{"StartTime":216397.0,"Objects":[{"StartTime":216397.0,"Position":51.0,"HyperDash":false},{"StartTime":216465.0,"Position":10.3953857,"HyperDash":false},{"StartTime":216533.0,"Position":51.0,"HyperDash":false},{"StartTime":216601.0,"Position":10.3953857,"HyperDash":false}]},{"StartTime":216670.0,"Objects":[{"StartTime":216670.0,"Position":64.0,"HyperDash":false}]},{"StartTime":216738.0,"Objects":[{"StartTime":216738.0,"Position":73.0,"HyperDash":false}]},{"StartTime":216806.0,"Objects":[{"StartTime":216806.0,"Position":82.0,"HyperDash":false}]},{"StartTime":216942.0,"Objects":[{"StartTime":216942.0,"Position":191.0,"HyperDash":false}]},{"StartTime":217010.0,"Objects":[{"StartTime":217010.0,"Position":200.0,"HyperDash":false}]},{"StartTime":217078.0,"Objects":[{"StartTime":217078.0,"Position":209.0,"HyperDash":false}]},{"StartTime":217215.0,"Objects":[{"StartTime":217215.0,"Position":243.0,"HyperDash":false},{"StartTime":217283.0,"Position":283.6046,"HyperDash":false},{"StartTime":217351.0,"Position":243.0,"HyperDash":false}]},{"StartTime":217488.0,"Objects":[{"StartTime":217488.0,"Position":350.0,"HyperDash":false},{"StartTime":217556.0,"Position":309.211151,"HyperDash":false},{"StartTime":217624.0,"Position":350.0,"HyperDash":false}]},{"StartTime":217761.0,"Objects":[{"StartTime":217761.0,"Position":425.0,"HyperDash":false},{"StartTime":217829.0,"Position":412.78772,"HyperDash":false},{"StartTime":217897.0,"Position":425.0,"HyperDash":false},{"StartTime":217965.0,"Position":412.78772,"HyperDash":false}]},{"StartTime":218034.0,"Objects":[{"StartTime":218034.0,"Position":481.0,"HyperDash":false},{"StartTime":218102.0,"Position":493.21228,"HyperDash":false},{"StartTime":218170.0,"Position":481.0,"HyperDash":false},{"StartTime":218238.0,"Position":493.21228,"HyperDash":false}]},{"StartTime":218306.0,"Objects":[{"StartTime":218306.0,"Position":411.0,"HyperDash":false},{"StartTime":218374.0,"Position":370.211151,"HyperDash":false},{"StartTime":218442.0,"Position":411.0,"HyperDash":false},{"StartTime":218510.0,"Position":370.211151,"HyperDash":false}]},{"StartTime":218579.0,"Objects":[{"StartTime":218579.0,"Position":328.0,"HyperDash":false},{"StartTime":218647.0,"Position":287.3954,"HyperDash":false},{"StartTime":218715.0,"Position":328.0,"HyperDash":false},{"StartTime":218783.0,"Position":287.3954,"HyperDash":false}]},{"StartTime":218851.0,"Objects":[{"StartTime":218851.0,"Position":208.0,"HyperDash":false}]},{"StartTime":218919.0,"Objects":[{"StartTime":218919.0,"Position":205.0,"HyperDash":false}]},{"StartTime":218987.0,"Objects":[{"StartTime":218987.0,"Position":202.0,"HyperDash":false}]},{"StartTime":219124.0,"Objects":[{"StartTime":219124.0,"Position":120.0,"HyperDash":false}]},{"StartTime":219192.0,"Objects":[{"StartTime":219192.0,"Position":117.0,"HyperDash":false}]},{"StartTime":219260.0,"Objects":[{"StartTime":219260.0,"Position":114.0,"HyperDash":false}]},{"StartTime":219397.0,"Objects":[{"StartTime":219397.0,"Position":44.0,"HyperDash":false},{"StartTime":219465.0,"Position":51.91918,"HyperDash":false},{"StartTime":219533.0,"Position":44.0,"HyperDash":false},{"StartTime":219601.0,"Position":51.91918,"HyperDash":false},{"StartTime":219669.0,"Position":44.0,"HyperDash":false},{"StartTime":219737.0,"Position":51.91918,"HyperDash":false},{"StartTime":219806.0,"Position":44.0,"HyperDash":false},{"StartTime":219874.0,"Position":51.91918,"HyperDash":false}]},{"StartTime":219943.0,"Objects":[{"StartTime":219943.0,"Position":142.0,"HyperDash":false}]},{"StartTime":220011.0,"Objects":[{"StartTime":220011.0,"Position":146.0,"HyperDash":false}]},{"StartTime":220079.0,"Objects":[{"StartTime":220079.0,"Position":151.0,"HyperDash":false},{"StartTime":220147.0,"Position":192.8533,"HyperDash":false},{"StartTime":220215.0,"Position":151.0,"HyperDash":false},{"StartTime":220283.0,"Position":192.8533,"HyperDash":false}]},{"StartTime":220352.0,"Objects":[{"StartTime":220352.0,"Position":269.0,"HyperDash":false},{"StartTime":220420.0,"Position":310.8533,"HyperDash":false}]},{"StartTime":220488.0,"Objects":[{"StartTime":220488.0,"Position":320.0,"HyperDash":false},{"StartTime":220556.0,"Position":361.8533,"HyperDash":false},{"StartTime":220624.0,"Position":320.0,"HyperDash":false},{"StartTime":220692.0,"Position":361.8533,"HyperDash":false},{"StartTime":220760.0,"Position":320.0,"HyperDash":false},{"StartTime":220828.0,"Position":361.8533,"HyperDash":false},{"StartTime":220897.0,"Position":320.0,"HyperDash":false},{"StartTime":220965.0,"Position":361.8533,"HyperDash":false},{"StartTime":221033.0,"Position":320.0,"HyperDash":false}]},{"StartTime":222670.0,"Objects":[{"StartTime":222670.0,"Position":364.0,"HyperDash":false},{"StartTime":222738.0,"Position":404.113983,"HyperDash":false},{"StartTime":222806.0,"Position":364.0,"HyperDash":false},{"StartTime":222874.0,"Position":404.113983,"HyperDash":false},{"StartTime":222942.0,"Position":364.0,"HyperDash":false},{"StartTime":223010.0,"Position":404.113983,"HyperDash":false},{"StartTime":223079.0,"Position":364.0,"HyperDash":false},{"StartTime":223147.0,"Position":404.113983,"HyperDash":false}]},{"StartTime":223215.0,"Objects":[{"StartTime":223215.0,"Position":487.0,"HyperDash":false},{"StartTime":223351.0,"Position":471.39093,"HyperDash":false}]},{"StartTime":223488.0,"Objects":[{"StartTime":223488.0,"Position":437.0,"HyperDash":false},{"StartTime":223624.0,"Position":421.39093,"HyperDash":false}]},{"StartTime":223761.0,"Objects":[{"StartTime":223761.0,"Position":314.0,"HyperDash":false}]},{"StartTime":223897.0,"Objects":[{"StartTime":223897.0,"Position":240.0,"HyperDash":false}]},{"StartTime":223965.0,"Objects":[{"StartTime":223965.0,"Position":240.0,"HyperDash":false}]},{"StartTime":224033.0,"Objects":[{"StartTime":224033.0,"Position":240.0,"HyperDash":false},{"StartTime":224169.0,"Position":156.4455,"HyperDash":false}]},{"StartTime":224306.0,"Objects":[{"StartTime":224306.0,"Position":37.0,"HyperDash":false}]},{"StartTime":224443.0,"Objects":[{"StartTime":224443.0,"Position":37.0,"HyperDash":false}]},{"StartTime":224579.0,"Objects":[{"StartTime":224579.0,"Position":142.0,"HyperDash":false},{"StartTime":224715.0,"Position":225.463379,"HyperDash":false}]},{"StartTime":224852.0,"Objects":[{"StartTime":224852.0,"Position":304.0,"HyperDash":false},{"StartTime":224988.0,"Position":287.910675,"HyperDash":false}]},{"StartTime":225124.0,"Objects":[{"StartTime":225124.0,"Position":164.0,"HyperDash":false},{"StartTime":225192.0,"Position":172.139191,"HyperDash":false},{"StartTime":225260.0,"Position":164.0,"HyperDash":false},{"StartTime":225328.0,"Position":172.139191,"HyperDash":false}]},{"StartTime":225397.0,"Objects":[{"StartTime":225397.0,"Position":84.0,"HyperDash":false},{"StartTime":225533.0,"Position":144.172775,"HyperDash":false}]},{"StartTime":225670.0,"Objects":[{"StartTime":225670.0,"Position":86.0,"HyperDash":false},{"StartTime":225806.0,"Position":25.8272362,"HyperDash":false}]},{"StartTime":225943.0,"Objects":[{"StartTime":225943.0,"Position":39.0,"HyperDash":false},{"StartTime":226079.0,"Position":47.27571,"HyperDash":false}]},{"StartTime":226215.0,"Objects":[{"StartTime":226215.0,"Position":137.0,"HyperDash":false},{"StartTime":226351.0,"Position":128.724289,"HyperDash":false}]},{"StartTime":226488.0,"Objects":[{"StartTime":226488.0,"Position":237.0,"HyperDash":false},{"StartTime":226624.0,"Position":321.596161,"HyperDash":false}]},{"StartTime":226761.0,"Objects":[{"StartTime":226761.0,"Position":361.0,"HyperDash":false}]},{"StartTime":226897.0,"Objects":[{"StartTime":226897.0,"Position":361.0,"HyperDash":false}]},{"StartTime":227033.0,"Objects":[{"StartTime":227033.0,"Position":488.0,"HyperDash":false},{"StartTime":227169.0,"Position":479.724274,"HyperDash":false}]},{"StartTime":227306.0,"Objects":[{"StartTime":227306.0,"Position":429.0,"HyperDash":false},{"StartTime":227442.0,"Position":437.275726,"HyperDash":false}]},{"StartTime":227579.0,"Objects":[{"StartTime":227579.0,"Position":361.0,"HyperDash":false},{"StartTime":227715.0,"Position":346.8173,"HyperDash":false}]},{"StartTime":227852.0,"Objects":[{"StartTime":227852.0,"Position":195.0,"HyperDash":false},{"StartTime":227988.0,"Position":179.865,"HyperDash":false}]},{"StartTime":228124.0,"Objects":[{"StartTime":228124.0,"Position":211.0,"HyperDash":false}]},{"StartTime":228261.0,"Objects":[{"StartTime":228261.0,"Position":131.0,"HyperDash":false}]},{"StartTime":228329.0,"Objects":[{"StartTime":228329.0,"Position":131.0,"HyperDash":false}]},{"StartTime":228397.0,"Objects":[{"StartTime":228397.0,"Position":131.0,"HyperDash":false},{"StartTime":228533.0,"Position":46.3490829,"HyperDash":false}]},{"StartTime":228670.0,"Objects":[{"StartTime":228670.0,"Position":67.0,"HyperDash":false}]},{"StartTime":228738.0,"Objects":[{"StartTime":228738.0,"Position":59.0,"HyperDash":false}]},{"StartTime":228806.0,"Objects":[{"StartTime":228806.0,"Position":63.0,"HyperDash":false}]},{"StartTime":228874.0,"Objects":[{"StartTime":228874.0,"Position":79.0,"HyperDash":false}]},{"StartTime":228942.0,"Objects":[{"StartTime":228942.0,"Position":104.0,"HyperDash":false}]},{"StartTime":229079.0,"Objects":[{"StartTime":229079.0,"Position":210.0,"HyperDash":false}]},{"StartTime":229147.0,"Objects":[{"StartTime":229147.0,"Position":224.0,"HyperDash":false}]},{"StartTime":229215.0,"Objects":[{"StartTime":229215.0,"Position":238.0,"HyperDash":false},{"StartTime":229283.0,"Position":199.132248,"HyperDash":false},{"StartTime":229351.0,"Position":238.0,"HyperDash":false}]},{"StartTime":229488.0,"Objects":[{"StartTime":229488.0,"Position":353.0,"HyperDash":false},{"StartTime":229556.0,"Position":336.0176,"HyperDash":false}]},{"StartTime":229624.0,"Objects":[{"StartTime":229624.0,"Position":425.0,"HyperDash":false},{"StartTime":229692.0,"Position":408.0176,"HyperDash":false}]},{"StartTime":229760.0,"Objects":[{"StartTime":229760.0,"Position":495.0,"HyperDash":false}]},{"StartTime":231943.0,"Objects":[{"StartTime":231943.0,"Position":221.0,"HyperDash":false}]},{"StartTime":233579.0,"Objects":[{"StartTime":233579.0,"Position":102.0,"HyperDash":false},{"StartTime":233669.0,"Position":37.721508,"HyperDash":false},{"StartTime":233760.0,"Position":102.0,"HyperDash":false},{"StartTime":233851.0,"Position":37.721508,"HyperDash":false},{"StartTime":233942.0,"Position":102.0,"HyperDash":false},{"StartTime":234033.0,"Position":37.721508,"HyperDash":false}]},{"StartTime":234124.0,"Objects":[{"StartTime":234124.0,"Position":93.0,"HyperDash":false},{"StartTime":234214.0,"Position":65.15,"HyperDash":false},{"StartTime":234305.0,"Position":93.0,"HyperDash":false}]},{"StartTime":234397.0,"Objects":[{"StartTime":234397.0,"Position":185.0,"HyperDash":false},{"StartTime":234487.0,"Position":191.729935,"HyperDash":false},{"StartTime":234578.0,"Position":185.0,"HyperDash":false}]},{"StartTime":234670.0,"Objects":[{"StartTime":234670.0,"Position":257.0,"HyperDash":false},{"StartTime":234760.0,"Position":229.150009,"HyperDash":false},{"StartTime":234851.0,"Position":257.0,"HyperDash":false}]},{"StartTime":234943.0,"Objects":[{"StartTime":234943.0,"Position":349.0,"HyperDash":false},{"StartTime":235033.0,"Position":355.43277,"HyperDash":false},{"StartTime":235124.0,"Position":349.0,"HyperDash":false}]},{"StartTime":235215.0,"Objects":[{"StartTime":235215.0,"Position":431.0,"HyperDash":false}]},{"StartTime":235306.0,"Objects":[{"StartTime":235306.0,"Position":439.0,"HyperDash":false},{"StartTime":235396.0,"Position":505.57785,"HyperDash":false}]},{"StartTime":235488.0,"Objects":[{"StartTime":235488.0,"Position":502.0,"HyperDash":false}]},{"StartTime":235579.0,"Objects":[{"StartTime":235579.0,"Position":460.0,"HyperDash":false}]},{"StartTime":235670.0,"Objects":[{"StartTime":235670.0,"Position":406.0,"HyperDash":false}]},{"StartTime":235760.0,"Objects":[{"StartTime":235760.0,"Position":358.0,"HyperDash":false},{"StartTime":235819.0,"Position":304.872559,"HyperDash":false},{"StartTime":235878.0,"Position":274.370667,"HyperDash":false},{"StartTime":235937.0,"Position":254.265121,"HyperDash":false},{"StartTime":236032.0,"Position":204.708969,"HyperDash":false}]},{"StartTime":236306.0,"Objects":[{"StartTime":236306.0,"Position":204.0,"HyperDash":false},{"StartTime":236396.0,"Position":271.720734,"HyperDash":false},{"StartTime":236487.0,"Position":204.0,"HyperDash":false}]},{"StartTime":236579.0,"Objects":[{"StartTime":236579.0,"Position":161.0,"HyperDash":false},{"StartTime":236669.0,"Position":228.033157,"HyperDash":false},{"StartTime":236760.0,"Position":161.0,"HyperDash":false}]},{"StartTime":236852.0,"Objects":[{"StartTime":236852.0,"Position":77.0,"HyperDash":false},{"StartTime":236942.0,"Position":9.279259,"HyperDash":false},{"StartTime":237033.0,"Position":77.0,"HyperDash":false}]},{"StartTime":237125.0,"Objects":[{"StartTime":237125.0,"Position":120.0,"HyperDash":false},{"StartTime":237215.0,"Position":52.9668427,"HyperDash":false},{"StartTime":237306.0,"Position":120.0,"HyperDash":false}]},{"StartTime":237397.0,"Objects":[{"StartTime":237397.0,"Position":194.0,"HyperDash":false}]},{"StartTime":237488.0,"Objects":[{"StartTime":237488.0,"Position":203.0,"HyperDash":false},{"StartTime":237578.0,"Position":216.523163,"HyperDash":false}]},{"StartTime":237670.0,"Objects":[{"StartTime":237670.0,"Position":296.0,"HyperDash":false}]},{"StartTime":237760.0,"Objects":[{"StartTime":237760.0,"Position":349.0,"HyperDash":false}]},{"StartTime":237851.0,"Objects":[{"StartTime":237851.0,"Position":391.0,"HyperDash":false}]},{"StartTime":237942.0,"Objects":[{"StartTime":237942.0,"Position":400.0,"HyperDash":false},{"StartTime":238001.0,"Position":373.357147,"HyperDash":false},{"StartTime":238060.0,"Position":359.412537,"HyperDash":false},{"StartTime":238119.0,"Position":345.3219,"HyperDash":false},{"StartTime":238214.0,"Position":385.5706,"HyperDash":false}]},{"StartTime":238488.0,"Objects":[{"StartTime":238488.0,"Position":385.0,"HyperDash":false},{"StartTime":238578.0,"Position":370.624329,"HyperDash":false},{"StartTime":238669.0,"Position":385.0,"HyperDash":false}]},{"StartTime":238761.0,"Objects":[{"StartTime":238761.0,"Position":276.0,"HyperDash":false},{"StartTime":238851.0,"Position":295.94812,"HyperDash":false},{"StartTime":238942.0,"Position":276.0,"HyperDash":false}]},{"StartTime":239033.0,"Objects":[{"StartTime":239033.0,"Position":188.0,"HyperDash":false},{"StartTime":239123.0,"Position":208.0458,"HyperDash":false},{"StartTime":239214.0,"Position":188.0,"HyperDash":false}]},{"StartTime":239306.0,"Objects":[{"StartTime":239306.0,"Position":129.0,"HyperDash":false},{"StartTime":239396.0,"Position":177.393921,"HyperDash":false},{"StartTime":239487.0,"Position":129.0,"HyperDash":false}]},{"StartTime":239579.0,"Objects":[{"StartTime":239579.0,"Position":38.0,"HyperDash":false}]},{"StartTime":239670.0,"Objects":[{"StartTime":239670.0,"Position":32.0,"HyperDash":false},{"StartTime":239760.0,"Position":54.9123878,"HyperDash":false}]},{"StartTime":239851.0,"Objects":[{"StartTime":239851.0,"Position":20.0,"HyperDash":false}]},{"StartTime":239942.0,"Objects":[{"StartTime":239942.0,"Position":57.0,"HyperDash":false}]},{"StartTime":240033.0,"Objects":[{"StartTime":240033.0,"Position":108.0,"HyperDash":false}]},{"StartTime":240124.0,"Objects":[{"StartTime":240124.0,"Position":161.0,"HyperDash":false},{"StartTime":240183.0,"Position":220.613419,"HyperDash":false},{"StartTime":240242.0,"Position":252.59671,"HyperDash":false},{"StartTime":240301.0,"Position":306.131134,"HyperDash":false},{"StartTime":240396.0,"Position":360.49115,"HyperDash":false}]},{"StartTime":240670.0,"Objects":[{"StartTime":240670.0,"Position":360.0,"HyperDash":false},{"StartTime":240760.0,"Position":296.123718,"HyperDash":false},{"StartTime":240851.0,"Position":360.0,"HyperDash":false}]},{"StartTime":240942.0,"Objects":[{"StartTime":240942.0,"Position":460.0,"HyperDash":false},{"StartTime":241032.0,"Position":404.530151,"HyperDash":false},{"StartTime":241123.0,"Position":460.0,"HyperDash":false}]},{"StartTime":241215.0,"Objects":[{"StartTime":241215.0,"Position":448.0,"HyperDash":false},{"StartTime":241305.0,"Position":511.876282,"HyperDash":false},{"StartTime":241396.0,"Position":448.0,"HyperDash":false}]},{"StartTime":241488.0,"Objects":[{"StartTime":241488.0,"Position":430.0,"HyperDash":false},{"StartTime":241578.0,"Position":485.1262,"HyperDash":false},{"StartTime":241669.0,"Position":430.0,"HyperDash":false}]},{"StartTime":241760.0,"Objects":[{"StartTime":241760.0,"Position":365.0,"HyperDash":false}]},{"StartTime":241852.0,"Objects":[{"StartTime":241852.0,"Position":354.0,"HyperDash":false},{"StartTime":241942.0,"Position":330.3751,"HyperDash":false}]},{"StartTime":242033.0,"Objects":[{"StartTime":242033.0,"Position":244.0,"HyperDash":false}]},{"StartTime":242124.0,"Objects":[{"StartTime":242124.0,"Position":191.0,"HyperDash":false}]},{"StartTime":242215.0,"Objects":[{"StartTime":242215.0,"Position":145.0,"HyperDash":false}]},{"StartTime":242306.0,"Objects":[{"StartTime":242306.0,"Position":91.0,"HyperDash":false},{"StartTime":242396.0,"Position":116.832222,"HyperDash":false},{"StartTime":242487.0,"Position":96.35042,"HyperDash":false},{"StartTime":242560.0,"Position":123.330132,"HyperDash":false},{"StartTime":242669.0,"Position":91.0,"HyperDash":false}]},{"StartTime":242852.0,"Objects":[{"StartTime":242852.0,"Position":33.0,"HyperDash":false},{"StartTime":242920.0,"Position":40.2480125,"HyperDash":false},{"StartTime":242988.0,"Position":33.0,"HyperDash":false},{"StartTime":243056.0,"Position":40.2480125,"HyperDash":false}]},{"StartTime":243125.0,"Objects":[{"StartTime":243125.0,"Position":134.0,"HyperDash":false},{"StartTime":243193.0,"Position":126.751991,"HyperDash":false},{"StartTime":243261.0,"Position":134.0,"HyperDash":false},{"StartTime":243329.0,"Position":126.751991,"HyperDash":false}]},{"StartTime":243397.0,"Objects":[{"StartTime":243397.0,"Position":228.0,"HyperDash":false},{"StartTime":243465.0,"Position":269.713348,"HyperDash":false}]},{"StartTime":243534.0,"Objects":[{"StartTime":243534.0,"Position":251.0,"HyperDash":false},{"StartTime":243602.0,"Position":292.713348,"HyperDash":false}]},{"StartTime":243671.0,"Objects":[{"StartTime":243671.0,"Position":276.0,"HyperDash":false},{"StartTime":243739.0,"Position":317.713348,"HyperDash":false},{"StartTime":243807.0,"Position":276.0,"HyperDash":false},{"StartTime":243875.0,"Position":317.713348,"HyperDash":false}]},{"StartTime":243943.0,"Objects":[{"StartTime":243943.0,"Position":388.0,"HyperDash":false},{"StartTime":244011.0,"Position":380.751984,"HyperDash":false},{"StartTime":244079.0,"Position":388.0,"HyperDash":false}]},{"StartTime":244216.0,"Objects":[{"StartTime":244216.0,"Position":409.0,"HyperDash":false}]},{"StartTime":244284.0,"Objects":[{"StartTime":244284.0,"Position":407.0,"HyperDash":false}]},{"StartTime":244352.0,"Objects":[{"StartTime":244352.0,"Position":405.0,"HyperDash":false}]},{"StartTime":244489.0,"Objects":[{"StartTime":244489.0,"Position":495.0,"HyperDash":false},{"StartTime":244557.0,"Position":502.248016,"HyperDash":false},{"StartTime":244625.0,"Position":495.0,"HyperDash":false}]},{"StartTime":244762.0,"Objects":[{"StartTime":244762.0,"Position":426.0,"HyperDash":false}]},{"StartTime":244830.0,"Objects":[{"StartTime":244830.0,"Position":428.0,"HyperDash":false}]},{"StartTime":244898.0,"Objects":[{"StartTime":244898.0,"Position":430.0,"HyperDash":false}]},{"StartTime":245034.0,"Objects":[{"StartTime":245034.0,"Position":370.0,"HyperDash":false},{"StartTime":245102.0,"Position":328.1226,"HyperDash":false},{"StartTime":245170.0,"Position":370.0,"HyperDash":false},{"StartTime":245238.0,"Position":328.1226,"HyperDash":false}]},{"StartTime":245307.0,"Objects":[{"StartTime":245307.0,"Position":331.0,"HyperDash":false},{"StartTime":245375.0,"Position":289.1226,"HyperDash":false},{"StartTime":245443.0,"Position":331.0,"HyperDash":false},{"StartTime":245511.0,"Position":289.1226,"HyperDash":false}]},{"StartTime":245579.0,"Objects":[{"StartTime":245579.0,"Position":229.0,"HyperDash":false},{"StartTime":245647.0,"Position":235.986954,"HyperDash":false}]},{"StartTime":245716.0,"Objects":[{"StartTime":245716.0,"Position":140.0,"HyperDash":false},{"StartTime":245784.0,"Position":146.986954,"HyperDash":false}]},{"StartTime":245853.0,"Objects":[{"StartTime":245853.0,"Position":50.0,"HyperDash":false},{"StartTime":245921.0,"Position":56.9869576,"HyperDash":false},{"StartTime":245989.0,"Position":50.0,"HyperDash":false},{"StartTime":246057.0,"Position":56.9869576,"HyperDash":false}]},{"StartTime":246124.0,"Objects":[{"StartTime":246124.0,"Position":120.0,"HyperDash":false}]},{"StartTime":246193.0,"Objects":[{"StartTime":246193.0,"Position":122.0,"HyperDash":false}]},{"StartTime":246261.0,"Objects":[{"StartTime":246261.0,"Position":124.0,"HyperDash":false}]},{"StartTime":246397.0,"Objects":[{"StartTime":246397.0,"Position":171.0,"HyperDash":false}]},{"StartTime":246465.0,"Objects":[{"StartTime":246465.0,"Position":173.0,"HyperDash":false}]},{"StartTime":246533.0,"Objects":[{"StartTime":246533.0,"Position":175.0,"HyperDash":false}]},{"StartTime":246670.0,"Objects":[{"StartTime":246670.0,"Position":123.0,"HyperDash":false}]},{"StartTime":246738.0,"Objects":[{"StartTime":246738.0,"Position":125.0,"HyperDash":false}]},{"StartTime":246806.0,"Objects":[{"StartTime":246806.0,"Position":127.0,"HyperDash":false},{"StartTime":246942.0,"Position":118.059486,"HyperDash":false}]},{"StartTime":247215.0,"Objects":[{"StartTime":247215.0,"Position":289.0,"HyperDash":false},{"StartTime":247283.0,"Position":330.8774,"HyperDash":false},{"StartTime":247351.0,"Position":289.0,"HyperDash":false},{"StartTime":247419.0,"Position":330.8774,"HyperDash":false}]},{"StartTime":247488.0,"Objects":[{"StartTime":247488.0,"Position":306.0,"HyperDash":false},{"StartTime":247556.0,"Position":347.8774,"HyperDash":false},{"StartTime":247624.0,"Position":306.0,"HyperDash":false},{"StartTime":247692.0,"Position":347.8774,"HyperDash":false}]},{"StartTime":247761.0,"Objects":[{"StartTime":247761.0,"Position":440.0,"HyperDash":false},{"StartTime":247829.0,"Position":447.248016,"HyperDash":false}]},{"StartTime":247897.0,"Objects":[{"StartTime":247897.0,"Position":425.0,"HyperDash":false},{"StartTime":247965.0,"Position":432.248016,"HyperDash":false}]},{"StartTime":248033.0,"Objects":[{"StartTime":248033.0,"Position":410.0,"HyperDash":false},{"StartTime":248101.0,"Position":417.248016,"HyperDash":false},{"StartTime":248169.0,"Position":410.0,"HyperDash":false},{"StartTime":248237.0,"Position":417.248016,"HyperDash":false}]},{"StartTime":248306.0,"Objects":[{"StartTime":248306.0,"Position":346.0,"HyperDash":false},{"StartTime":248374.0,"Position":304.1226,"HyperDash":false},{"StartTime":248442.0,"Position":346.0,"HyperDash":false}]},{"StartTime":248579.0,"Objects":[{"StartTime":248579.0,"Position":287.0,"HyperDash":false}]},{"StartTime":248647.0,"Objects":[{"StartTime":248647.0,"Position":279.0,"HyperDash":false}]},{"StartTime":248715.0,"Objects":[{"StartTime":248715.0,"Position":271.0,"HyperDash":false}]},{"StartTime":248852.0,"Objects":[{"StartTime":248852.0,"Position":193.0,"HyperDash":false},{"StartTime":248920.0,"Position":151.1226,"HyperDash":false},{"StartTime":248988.0,"Position":193.0,"HyperDash":false}]},{"StartTime":249124.0,"Objects":[{"StartTime":249124.0,"Position":139.0,"HyperDash":false}]},{"StartTime":249194.0,"Objects":[{"StartTime":249194.0,"Position":131.0,"HyperDash":false}]},{"StartTime":249261.0,"Objects":[{"StartTime":249261.0,"Position":123.0,"HyperDash":false}]},{"StartTime":249397.0,"Objects":[{"StartTime":249397.0,"Position":53.0,"HyperDash":false},{"StartTime":249465.0,"Position":60.2480125,"HyperDash":false},{"StartTime":249533.0,"Position":53.0,"HyperDash":false},{"StartTime":249601.0,"Position":60.2480125,"HyperDash":false}]},{"StartTime":249670.0,"Objects":[{"StartTime":249670.0,"Position":0.0,"HyperDash":false},{"StartTime":249738.0,"Position":7.952265,"HyperDash":false},{"StartTime":249806.0,"Position":0.0,"HyperDash":false},{"StartTime":249874.0,"Position":7.952265,"HyperDash":false}]},{"StartTime":249943.0,"Objects":[{"StartTime":249943.0,"Position":41.0,"HyperDash":false},{"StartTime":250011.0,"Position":0.0,"HyperDash":false}]},{"StartTime":250079.0,"Objects":[{"StartTime":250079.0,"Position":127.0,"HyperDash":false},{"StartTime":250147.0,"Position":85.1226044,"HyperDash":false}]},{"StartTime":250215.0,"Objects":[{"StartTime":250215.0,"Position":212.0,"HyperDash":false},{"StartTime":250283.0,"Position":170.1226,"HyperDash":false},{"StartTime":250351.0,"Position":212.0,"HyperDash":false},{"StartTime":250419.0,"Position":170.1226,"HyperDash":false}]},{"StartTime":250488.0,"Objects":[{"StartTime":250488.0,"Position":210.0,"HyperDash":false}]},{"StartTime":250556.0,"Objects":[{"StartTime":250556.0,"Position":212.0,"HyperDash":false}]},{"StartTime":250624.0,"Objects":[{"StartTime":250624.0,"Position":214.0,"HyperDash":false}]},{"StartTime":250761.0,"Objects":[{"StartTime":250761.0,"Position":295.0,"HyperDash":false}]},{"StartTime":250829.0,"Objects":[{"StartTime":250829.0,"Position":293.0,"HyperDash":false}]},{"StartTime":250898.0,"Objects":[{"StartTime":250898.0,"Position":291.0,"HyperDash":false}]},{"StartTime":251033.0,"Objects":[{"StartTime":251033.0,"Position":235.0,"HyperDash":false}]},{"StartTime":251102.0,"Objects":[{"StartTime":251102.0,"Position":237.0,"HyperDash":false}]},{"StartTime":251170.0,"Objects":[{"StartTime":251170.0,"Position":239.0,"HyperDash":false},{"StartTime":251238.0,"Position":231.8979,"HyperDash":false},{"StartTime":251306.0,"Position":239.0,"HyperDash":false},{"StartTime":251374.0,"Position":231.8979,"HyperDash":false},{"StartTime":251442.0,"Position":239.0,"HyperDash":false},{"StartTime":251510.0,"Position":231.8979,"HyperDash":false}]},{"StartTime":251579.0,"Objects":[{"StartTime":251579.0,"Position":229.0,"HyperDash":false},{"StartTime":251715.0,"Position":317.623718,"HyperDash":false}]},{"StartTime":251852.0,"Objects":[{"StartTime":251852.0,"Position":475.0,"HyperDash":false},{"StartTime":251988.0,"Position":386.889038,"HyperDash":false}]},{"StartTime":252124.0,"Objects":[{"StartTime":252124.0,"Position":440.0,"HyperDash":false},{"StartTime":252260.0,"Position":463.840118,"HyperDash":false}]},{"StartTime":252397.0,"Objects":[{"StartTime":252397.0,"Position":297.0,"HyperDash":false},{"StartTime":252533.0,"Position":319.863068,"HyperDash":false}]},{"StartTime":252670.0,"Objects":[{"StartTime":252670.0,"Position":205.0,"HyperDash":false},{"StartTime":252806.0,"Position":105.595367,"HyperDash":false}]},{"StartTime":252942.0,"Objects":[{"StartTime":252942.0,"Position":42.0,"HyperDash":false}]},{"StartTime":253079.0,"Objects":[{"StartTime":253079.0,"Position":42.0,"HyperDash":false}]},{"StartTime":253215.0,"Objects":[{"StartTime":253215.0,"Position":1.0,"HyperDash":false},{"StartTime":253351.0,"Position":97.26073,"HyperDash":false}]},{"StartTime":253488.0,"Objects":[{"StartTime":253488.0,"Position":248.0,"HyperDash":false},{"StartTime":253624.0,"Position":148.595367,"HyperDash":true}]},{"StartTime":253760.0,"Objects":[{"StartTime":253760.0,"Position":408.0,"HyperDash":false},{"StartTime":253896.0,"Position":487.4551,"HyperDash":false}]},{"StartTime":254033.0,"Objects":[{"StartTime":254033.0,"Position":318.0,"HyperDash":false},{"StartTime":254169.0,"Position":309.7604,"HyperDash":false}]},{"StartTime":254306.0,"Objects":[{"StartTime":254306.0,"Position":202.0,"HyperDash":false}]},{"StartTime":254442.0,"Objects":[{"StartTime":254442.0,"Position":295.0,"HyperDash":false}]},{"StartTime":254510.0,"Objects":[{"StartTime":254510.0,"Position":295.0,"HyperDash":false}]},{"StartTime":254579.0,"Objects":[{"StartTime":254579.0,"Position":295.0,"HyperDash":false},{"StartTime":254715.0,"Position":395.898743,"HyperDash":false}]},{"StartTime":254851.0,"Objects":[{"StartTime":254851.0,"Position":486.0,"HyperDash":false}]},{"StartTime":254987.0,"Objects":[{"StartTime":254987.0,"Position":423.0,"HyperDash":false}]},{"StartTime":255124.0,"Objects":[{"StartTime":255124.0,"Position":424.0,"HyperDash":false}]},{"StartTime":255260.0,"Objects":[{"StartTime":255260.0,"Position":487.0,"HyperDash":false}]},{"StartTime":255397.0,"Objects":[{"StartTime":255397.0,"Position":412.0,"HyperDash":false},{"StartTime":255456.0,"Position":367.364532,"HyperDash":false},{"StartTime":255515.0,"Position":308.7291,"HyperDash":false},{"StartTime":255574.0,"Position":291.225,"HyperDash":false},{"StartTime":255669.0,"Position":215.507538,"HyperDash":false}]},{"StartTime":255806.0,"Objects":[{"StartTime":255806.0,"Position":80.0,"HyperDash":false}]},{"StartTime":255874.0,"Objects":[{"StartTime":255874.0,"Position":87.0,"HyperDash":false}]},{"StartTime":255942.0,"Objects":[{"StartTime":255942.0,"Position":94.0,"HyperDash":false},{"StartTime":256078.0,"Position":115.948105,"HyperDash":false}]},{"StartTime":256215.0,"Objects":[{"StartTime":256215.0,"Position":14.0,"HyperDash":false},{"StartTime":256351.0,"Position":35.94811,"HyperDash":false}]},{"StartTime":256488.0,"Objects":[{"StartTime":256488.0,"Position":172.0,"HyperDash":false},{"StartTime":256624.0,"Position":263.280975,"HyperDash":false}]},{"StartTime":256760.0,"Objects":[{"StartTime":256760.0,"Position":238.0,"HyperDash":false},{"StartTime":256896.0,"Position":146.7498,"HyperDash":false}]},{"StartTime":257033.0,"Objects":[{"StartTime":257033.0,"Position":115.0,"HyperDash":false},{"StartTime":257169.0,"Position":205.031708,"HyperDash":false}]},{"StartTime":257306.0,"Objects":[{"StartTime":257306.0,"Position":342.0,"HyperDash":false}]},{"StartTime":257442.0,"Objects":[{"StartTime":257442.0,"Position":342.0,"HyperDash":false}]},{"StartTime":257579.0,"Objects":[{"StartTime":257579.0,"Position":455.0,"HyperDash":false},{"StartTime":257715.0,"Position":467.65155,"HyperDash":false}]},{"StartTime":257851.0,"Objects":[{"StartTime":257851.0,"Position":381.0,"HyperDash":false},{"StartTime":257987.0,"Position":393.65155,"HyperDash":false}]},{"StartTime":258124.0,"Objects":[{"StartTime":258124.0,"Position":267.0,"HyperDash":false},{"StartTime":258260.0,"Position":183.076477,"HyperDash":false}]},{"StartTime":258397.0,"Objects":[{"StartTime":258397.0,"Position":95.0,"HyperDash":false},{"StartTime":258533.0,"Position":11.07647,"HyperDash":false}]},{"StartTime":258670.0,"Objects":[{"StartTime":258670.0,"Position":101.0,"HyperDash":false}]},{"StartTime":258806.0,"Objects":[{"StartTime":258806.0,"Position":22.0,"HyperDash":false}]},{"StartTime":258874.0,"Objects":[{"StartTime":258874.0,"Position":22.0,"HyperDash":false}]},{"StartTime":258942.0,"Objects":[{"StartTime":258942.0,"Position":22.0,"HyperDash":false},{"StartTime":259078.0,"Position":5.65008163,"HyperDash":false}]},{"StartTime":259215.0,"Objects":[{"StartTime":259215.0,"Position":158.0,"HyperDash":false}]},{"StartTime":259283.0,"Objects":[{"StartTime":259283.0,"Position":197.0,"HyperDash":false}]},{"StartTime":259351.0,"Objects":[{"StartTime":259351.0,"Position":239.0,"HyperDash":false}]},{"StartTime":259419.0,"Objects":[{"StartTime":259419.0,"Position":273.0,"HyperDash":false}]},{"StartTime":259487.0,"Objects":[{"StartTime":259487.0,"Position":291.0,"HyperDash":false}]},{"StartTime":259624.0,"Objects":[{"StartTime":259624.0,"Position":405.0,"HyperDash":false}]},{"StartTime":259692.0,"Objects":[{"StartTime":259692.0,"Position":415.0,"HyperDash":false}]},{"StartTime":259761.0,"Objects":[{"StartTime":259761.0,"Position":425.0,"HyperDash":false},{"StartTime":259829.0,"Position":436.342346,"HyperDash":false},{"StartTime":259897.0,"Position":425.0,"HyperDash":false}]},{"StartTime":260033.0,"Objects":[{"StartTime":260033.0,"Position":355.0,"HyperDash":false},{"StartTime":260101.0,"Position":366.342346,"HyperDash":false},{"StartTime":260169.0,"Position":355.0,"HyperDash":false},{"StartTime":260237.0,"Position":366.342346,"HyperDash":false}]},{"StartTime":260306.0,"Objects":[{"StartTime":260306.0,"Position":376.0,"HyperDash":false},{"StartTime":260442.0,"Position":287.376282,"HyperDash":false}]},{"StartTime":260578.0,"Objects":[{"StartTime":260578.0,"Position":112.0,"HyperDash":false},{"StartTime":260714.0,"Position":200.110962,"HyperDash":false}]},{"StartTime":260851.0,"Objects":[{"StartTime":260851.0,"Position":240.0,"HyperDash":false},{"StartTime":260987.0,"Position":140.825165,"HyperDash":false}]},{"StartTime":261124.0,"Objects":[{"StartTime":261124.0,"Position":1.0,"HyperDash":false},{"StartTime":261260.0,"Position":100.404633,"HyperDash":false}]},{"StartTime":261397.0,"Objects":[{"StartTime":261397.0,"Position":296.0,"HyperDash":false},{"StartTime":261533.0,"Position":196.595367,"HyperDash":false}]},{"StartTime":261669.0,"Objects":[{"StartTime":261669.0,"Position":324.0,"HyperDash":false}]},{"StartTime":261806.0,"Objects":[{"StartTime":261806.0,"Position":324.0,"HyperDash":false}]},{"StartTime":261942.0,"Objects":[{"StartTime":261942.0,"Position":445.0,"HyperDash":false},{"StartTime":262078.0,"Position":460.350983,"HyperDash":false}]},{"StartTime":262215.0,"Objects":[{"StartTime":262215.0,"Position":360.0,"HyperDash":false},{"StartTime":262351.0,"Position":456.028931,"HyperDash":false}]},{"StartTime":262487.0,"Objects":[{"StartTime":262487.0,"Position":274.0,"HyperDash":false},{"StartTime":262623.0,"Position":194.151871,"HyperDash":false}]},{"StartTime":262760.0,"Objects":[{"StartTime":262760.0,"Position":38.0,"HyperDash":false},{"StartTime":262896.0,"Position":125.37175,"HyperDash":false}]},{"StartTime":263033.0,"Objects":[{"StartTime":263033.0,"Position":194.0,"HyperDash":false}]},{"StartTime":263169.0,"Objects":[{"StartTime":263169.0,"Position":312.0,"HyperDash":false}]},{"StartTime":263237.0,"Objects":[{"StartTime":263237.0,"Position":312.0,"HyperDash":false}]},{"StartTime":263306.0,"Objects":[{"StartTime":263306.0,"Position":312.0,"HyperDash":false},{"StartTime":263442.0,"Position":412.898743,"HyperDash":false}]},{"StartTime":263578.0,"Objects":[{"StartTime":263578.0,"Position":503.0,"HyperDash":false}]},{"StartTime":263714.0,"Objects":[{"StartTime":263714.0,"Position":456.0,"HyperDash":false}]},{"StartTime":263851.0,"Objects":[{"StartTime":263851.0,"Position":367.0,"HyperDash":false}]},{"StartTime":263987.0,"Objects":[{"StartTime":263987.0,"Position":292.0,"HyperDash":false}]},{"StartTime":264124.0,"Objects":[{"StartTime":264124.0,"Position":206.0,"HyperDash":false},{"StartTime":264183.0,"Position":158.319275,"HyperDash":false},{"StartTime":264242.0,"Position":120.702431,"HyperDash":false},{"StartTime":264301.0,"Position":92.96848,"HyperDash":false},{"StartTime":264396.0,"Position":18.7677212,"HyperDash":false}]},{"StartTime":264533.0,"Objects":[{"StartTime":264533.0,"Position":173.0,"HyperDash":false}]},{"StartTime":264601.0,"Objects":[{"StartTime":264601.0,"Position":166.0,"HyperDash":false}]},{"StartTime":264669.0,"Objects":[{"StartTime":264669.0,"Position":159.0,"HyperDash":false},{"StartTime":264805.0,"Position":137.0519,"HyperDash":false}]},{"StartTime":264942.0,"Objects":[{"StartTime":264942.0,"Position":302.0,"HyperDash":false},{"StartTime":265078.0,"Position":280.834564,"HyperDash":false}]},{"StartTime":265215.0,"Objects":[{"StartTime":265215.0,"Position":399.0,"HyperDash":false},{"StartTime":265351.0,"Position":434.304535,"HyperDash":false}]},{"StartTime":265487.0,"Objects":[{"StartTime":265487.0,"Position":496.0,"HyperDash":false},{"StartTime":265623.0,"Position":404.622,"HyperDash":false}]},{"StartTime":265760.0,"Objects":[{"StartTime":265760.0,"Position":362.0,"HyperDash":false},{"StartTime":265896.0,"Position":452.0317,"HyperDash":false}]},{"StartTime":266033.0,"Objects":[{"StartTime":266033.0,"Position":288.0,"HyperDash":false}]},{"StartTime":266169.0,"Objects":[{"StartTime":266169.0,"Position":288.0,"HyperDash":false}]},{"StartTime":266306.0,"Objects":[{"StartTime":266306.0,"Position":171.0,"HyperDash":false},{"StartTime":266442.0,"Position":158.34845,"HyperDash":false}]},{"StartTime":266578.0,"Objects":[{"StartTime":266578.0,"Position":251.0,"HyperDash":false},{"StartTime":266714.0,"Position":238.34845,"HyperDash":false}]},{"StartTime":266851.0,"Objects":[{"StartTime":266851.0,"Position":56.0,"HyperDash":false},{"StartTime":266987.0,"Position":104.910339,"HyperDash":false}]},{"StartTime":267124.0,"Objects":[{"StartTime":267124.0,"Position":35.0,"HyperDash":false},{"StartTime":267260.0,"Position":33.814888,"HyperDash":false}]},{"StartTime":267397.0,"Objects":[{"StartTime":267397.0,"Position":123.0,"HyperDash":false}]},{"StartTime":267533.0,"Objects":[{"StartTime":267533.0,"Position":253.0,"HyperDash":false}]},{"StartTime":267601.0,"Objects":[{"StartTime":267601.0,"Position":253.0,"HyperDash":false}]},{"StartTime":267669.0,"Objects":[{"StartTime":267669.0,"Position":253.0,"HyperDash":false},{"StartTime":267805.0,"Position":353.6811,"HyperDash":false}]},{"StartTime":267942.0,"Objects":[{"StartTime":267942.0,"Position":463.0,"HyperDash":false}]},{"StartTime":268010.0,"Objects":[{"StartTime":268010.0,"Position":489.0,"HyperDash":false}]},{"StartTime":268078.0,"Objects":[{"StartTime":268078.0,"Position":498.0,"HyperDash":false}]},{"StartTime":268146.0,"Objects":[{"StartTime":268146.0,"Position":485.0,"HyperDash":false}]},{"StartTime":268214.0,"Objects":[{"StartTime":268214.0,"Position":455.0,"HyperDash":false}]},{"StartTime":268352.0,"Objects":[{"StartTime":268352.0,"Position":419.0,"HyperDash":false}]},{"StartTime":268420.0,"Objects":[{"StartTime":268420.0,"Position":403.0,"HyperDash":false}]},{"StartTime":268488.0,"Objects":[{"StartTime":268488.0,"Position":372.0,"HyperDash":false}]},{"StartTime":268556.0,"Objects":[{"StartTime":268556.0,"Position":332.0,"HyperDash":false}]},{"StartTime":268624.0,"Objects":[{"StartTime":268624.0,"Position":292.0,"HyperDash":false}]},{"StartTime":268761.0,"Objects":[{"StartTime":268761.0,"Position":231.0,"HyperDash":false},{"StartTime":268829.0,"Position":180.408112,"HyperDash":false},{"StartTime":268897.0,"Position":231.0,"HyperDash":false},{"StartTime":268965.0,"Position":180.408112,"HyperDash":false}]},{"StartTime":269033.0,"Objects":[{"StartTime":269033.0,"Position":96.0,"HyperDash":false},{"StartTime":269099.0,"Position":107.997719,"HyperDash":false},{"StartTime":269166.0,"Position":130.581879,"HyperDash":false},{"StartTime":269232.0,"Position":149.897186,"HyperDash":false},{"StartTime":269299.0,"Position":175.084061,"HyperDash":false},{"StartTime":269365.0,"Position":167.6238,"HyperDash":false},{"StartTime":269432.0,"Position":173.461578,"HyperDash":false},{"StartTime":269498.0,"Position":185.410263,"HyperDash":false},{"StartTime":269565.0,"Position":178.44928,"HyperDash":false},{"StartTime":269655.0,"Position":167.081726,"HyperDash":false},{"StartTime":269746.0,"Position":170.346115,"HyperDash":false},{"StartTime":269837.0,"Position":137.438,"HyperDash":false},{"StartTime":269964.0,"Position":125.546143,"HyperDash":false}]},{"StartTime":270097.0,"Objects":[{"StartTime":270097.0,"Position":121.0,"HyperDash":false},{"StartTime":270163.0,"Position":78.13265,"HyperDash":false},{"StartTime":270230.0,"Position":95.43977,"HyperDash":false},{"StartTime":270296.0,"Position":65.59505,"HyperDash":false},{"StartTime":270363.0,"Position":71.33265,"HyperDash":false},{"StartTime":270429.0,"Position":73.41984,"HyperDash":false},{"StartTime":270496.0,"Position":98.806366,"HyperDash":false},{"StartTime":270562.0,"Position":139.458054,"HyperDash":false},{"StartTime":270629.0,"Position":162.000076,"HyperDash":false},{"StartTime":270686.0,"Position":174.872726,"HyperDash":false},{"StartTime":270744.0,"Position":199.77951,"HyperDash":false},{"StartTime":270801.0,"Position":218.731812,"HyperDash":false},{"StartTime":270895.0,"Position":252.733368,"HyperDash":false}]},{"StartTime":271028.0,"Objects":[{"StartTime":271028.0,"Position":319.0,"HyperDash":false}]},{"StartTime":271161.0,"Objects":[{"StartTime":271161.0,"Position":312.0,"HyperDash":false},{"StartTime":271223.0,"Position":302.2162,"HyperDash":false},{"StartTime":271285.0,"Position":302.676941,"HyperDash":false},{"StartTime":271347.0,"Position":283.679169,"HyperDash":false},{"StartTime":271409.0,"Position":290.484436,"HyperDash":false},{"StartTime":271471.0,"Position":288.101379,"HyperDash":false},{"StartTime":271533.0,"Position":295.433258,"HyperDash":false},{"StartTime":271595.0,"Position":306.336884,"HyperDash":false},{"StartTime":271693.0,"Position":324.652863,"HyperDash":false}]},{"StartTime":271959.0,"Objects":[{"StartTime":271959.0,"Position":400.0,"HyperDash":false}]},{"StartTime":272225.0,"Objects":[{"StartTime":272225.0,"Position":400.0,"HyperDash":false},{"StartTime":272313.0,"Position":405.1424,"HyperDash":false},{"StartTime":272402.0,"Position":408.331879,"HyperDash":false},{"StartTime":272472.0,"Position":402.036774,"HyperDash":false},{"StartTime":272579.0,"Position":400.0,"HyperDash":false}]},{"StartTime":272758.0,"Objects":[{"StartTime":272758.0,"Position":442.0,"HyperDash":false},{"StartTime":272846.0,"Position":459.1424,"HyperDash":false},{"StartTime":272935.0,"Position":450.331879,"HyperDash":false},{"StartTime":273005.0,"Position":454.036774,"HyperDash":false},{"StartTime":273112.0,"Position":442.0,"HyperDash":false}]},{"StartTime":273290.0,"Objects":[{"StartTime":273290.0,"Position":512.0,"HyperDash":false},{"StartTime":273355.0,"Position":498.977875,"HyperDash":false},{"StartTime":273420.0,"Position":478.2446,"HyperDash":false},{"StartTime":273485.0,"Position":437.965363,"HyperDash":false},{"StartTime":273551.0,"Position":433.034943,"HyperDash":false},{"StartTime":273616.0,"Position":428.07312,"HyperDash":false},{"StartTime":273681.0,"Position":423.756653,"HyperDash":false},{"StartTime":273746.0,"Position":401.5979,"HyperDash":false},{"StartTime":273848.0,"Position":401.115448,"HyperDash":false}]},{"StartTime":274048.0,"Objects":[{"StartTime":274048.0,"Position":303.0,"HyperDash":false},{"StartTime":274129.0,"Position":308.57135,"HyperDash":false},{"StartTime":274247.0,"Position":297.033356,"HyperDash":false}]},{"StartTime":274498.0,"Objects":[{"StartTime":274498.0,"Position":202.0,"HyperDash":false},{"StartTime":274560.0,"Position":191.209839,"HyperDash":false},{"StartTime":274622.0,"Position":190.373,"HyperDash":false},{"StartTime":274747.0,"Position":202.0,"HyperDash":false}]},{"StartTime":274873.0,"Objects":[{"StartTime":274873.0,"Position":105.0,"HyperDash":false},{"StartTime":274939.0,"Position":120.3023,"HyperDash":false},{"StartTime":275006.0,"Position":107.624329,"HyperDash":false},{"StartTime":275139.0,"Position":105.0,"HyperDash":false}]},{"StartTime":275273.0,"Objects":[{"StartTime":275273.0,"Position":31.0,"HyperDash":false},{"StartTime":275349.0,"Position":42.15374,"HyperDash":false},{"StartTime":275426.0,"Position":47.4684143,"HyperDash":false},{"StartTime":275485.0,"Position":28.1921768,"HyperDash":false},{"StartTime":275580.0,"Position":31.0,"HyperDash":false}]},{"StartTime":275734.0,"Objects":[{"StartTime":275734.0,"Position":0.0,"HyperDash":false},{"StartTime":275813.0,"Position":0.0,"HyperDash":false},{"StartTime":275893.0,"Position":25.7255154,"HyperDash":false},{"StartTime":275955.0,"Position":16.8062725,"HyperDash":false},{"StartTime":276053.0,"Position":0.0,"HyperDash":false}]},{"StartTime":276254.0,"Objects":[{"StartTime":276254.0,"Position":21.0,"HyperDash":false}]},{"StartTime":276419.0,"Objects":[{"StartTime":276419.0,"Position":354.0,"HyperDash":false},{"StartTime":276494.0,"Position":270.0,"HyperDash":false},{"StartTime":276569.0,"Position":362.0,"HyperDash":false},{"StartTime":276645.0,"Position":255.0,"HyperDash":false},{"StartTime":276720.0,"Position":203.0,"HyperDash":false},{"StartTime":276795.0,"Position":67.0,"HyperDash":false},{"StartTime":276871.0,"Position":112.0,"HyperDash":false},{"StartTime":276946.0,"Position":326.0,"HyperDash":false},{"StartTime":277021.0,"Position":219.0,"HyperDash":false},{"StartTime":277097.0,"Position":351.0,"HyperDash":false},{"StartTime":277172.0,"Position":477.0,"HyperDash":false},{"StartTime":277247.0,"Position":439.0,"HyperDash":false},{"StartTime":277323.0,"Position":471.0,"HyperDash":false},{"StartTime":277398.0,"Position":449.0,"HyperDash":false},{"StartTime":277473.0,"Position":295.0,"HyperDash":false},{"StartTime":277549.0,"Position":217.0,"HyperDash":false},{"StartTime":277624.0,"Position":308.0,"HyperDash":false},{"StartTime":277699.0,"Position":430.0,"HyperDash":false},{"StartTime":277775.0,"Position":73.0,"HyperDash":false},{"StartTime":277850.0,"Position":53.0,"HyperDash":false},{"StartTime":277925.0,"Position":276.0,"HyperDash":false},{"StartTime":278001.0,"Position":289.0,"HyperDash":false},{"StartTime":278076.0,"Position":104.0,"HyperDash":false},{"StartTime":278151.0,"Position":212.0,"HyperDash":false},{"StartTime":278227.0,"Position":359.0,"HyperDash":false},{"StartTime":278302.0,"Position":500.0,"HyperDash":false},{"StartTime":278377.0,"Position":467.0,"HyperDash":false},{"StartTime":278453.0,"Position":303.0,"HyperDash":false},{"StartTime":278528.0,"Position":29.0,"HyperDash":false},{"StartTime":278603.0,"Position":482.0,"HyperDash":false},{"StartTime":278679.0,"Position":379.0,"HyperDash":false},{"StartTime":278754.0,"Position":93.0,"HyperDash":false},{"StartTime":278830.0,"Position":266.0,"HyperDash":false},{"StartTime":278905.0,"Position":342.0,"HyperDash":false},{"StartTime":278980.0,"Position":423.0,"HyperDash":false},{"StartTime":279056.0,"Position":190.0,"HyperDash":false},{"StartTime":279131.0,"Position":266.0,"HyperDash":false},{"StartTime":279206.0,"Position":56.0,"HyperDash":false},{"StartTime":279282.0,"Position":164.0,"HyperDash":false},{"StartTime":279357.0,"Position":44.0,"HyperDash":false},{"StartTime":279432.0,"Position":68.0,"HyperDash":false},{"StartTime":279508.0,"Position":476.0,"HyperDash":false},{"StartTime":279583.0,"Position":237.0,"HyperDash":false},{"StartTime":279658.0,"Position":146.0,"HyperDash":false},{"StartTime":279734.0,"Position":99.0,"HyperDash":false},{"StartTime":279809.0,"Position":52.0,"HyperDash":false},{"StartTime":279884.0,"Position":294.0,"HyperDash":false},{"StartTime":279960.0,"Position":346.0,"HyperDash":false},{"StartTime":280035.0,"Position":256.0,"HyperDash":false},{"StartTime":280110.0,"Position":353.0,"HyperDash":false},{"StartTime":280186.0,"Position":85.0,"HyperDash":false},{"StartTime":280261.0,"Position":473.0,"HyperDash":false},{"StartTime":280336.0,"Position":55.0,"HyperDash":false},{"StartTime":280412.0,"Position":158.0,"HyperDash":false},{"StartTime":280487.0,"Position":97.0,"HyperDash":false},{"StartTime":280562.0,"Position":288.0,"HyperDash":false},{"StartTime":280638.0,"Position":236.0,"HyperDash":false},{"StartTime":280713.0,"Position":226.0,"HyperDash":false},{"StartTime":280788.0,"Position":317.0,"HyperDash":false},{"StartTime":280864.0,"Position":227.0,"HyperDash":false},{"StartTime":280939.0,"Position":507.0,"HyperDash":false},{"StartTime":281014.0,"Position":144.0,"HyperDash":false},{"StartTime":281090.0,"Position":409.0,"HyperDash":false},{"StartTime":281165.0,"Position":76.0,"HyperDash":false},{"StartTime":281241.0,"Position":193.0,"HyperDash":false},{"StartTime":281316.0,"Position":456.0,"HyperDash":false},{"StartTime":281391.0,"Position":161.0,"HyperDash":false},{"StartTime":281467.0,"Position":417.0,"HyperDash":false},{"StartTime":281542.0,"Position":157.0,"HyperDash":false},{"StartTime":281617.0,"Position":464.0,"HyperDash":false},{"StartTime":281693.0,"Position":462.0,"HyperDash":false},{"StartTime":281768.0,"Position":254.0,"HyperDash":false},{"StartTime":281843.0,"Position":103.0,"HyperDash":false},{"StartTime":281919.0,"Position":125.0,"HyperDash":false},{"StartTime":281994.0,"Position":485.0,"HyperDash":false},{"StartTime":282069.0,"Position":350.0,"HyperDash":false},{"StartTime":282145.0,"Position":206.0,"HyperDash":false},{"StartTime":282220.0,"Position":285.0,"HyperDash":false},{"StartTime":282295.0,"Position":390.0,"HyperDash":false},{"StartTime":282371.0,"Position":463.0,"HyperDash":false},{"StartTime":282446.0,"Position":447.0,"HyperDash":false},{"StartTime":282521.0,"Position":126.0,"HyperDash":false},{"StartTime":282597.0,"Position":44.0,"HyperDash":false},{"StartTime":282672.0,"Position":451.0,"HyperDash":false},{"StartTime":282747.0,"Position":278.0,"HyperDash":false},{"StartTime":282823.0,"Position":24.0,"HyperDash":false},{"StartTime":282898.0,"Position":367.0,"HyperDash":false},{"StartTime":282973.0,"Position":221.0,"HyperDash":false},{"StartTime":283049.0,"Position":439.0,"HyperDash":false},{"StartTime":283124.0,"Position":243.0,"HyperDash":false},{"StartTime":283199.0,"Position":213.0,"HyperDash":false},{"StartTime":283275.0,"Position":120.0,"HyperDash":false},{"StartTime":283350.0,"Position":379.0,"HyperDash":false},{"StartTime":283425.0,"Position":353.0,"HyperDash":false},{"StartTime":283501.0,"Position":496.0,"HyperDash":false},{"StartTime":283576.0,"Position":288.0,"HyperDash":false},{"StartTime":283652.0,"Position":163.0,"HyperDash":false},{"StartTime":283727.0,"Position":314.0,"HyperDash":false},{"StartTime":283802.0,"Position":296.0,"HyperDash":false},{"StartTime":283878.0,"Position":488.0,"HyperDash":false},{"StartTime":283953.0,"Position":482.0,"HyperDash":false},{"StartTime":284028.0,"Position":321.0,"HyperDash":false},{"StartTime":284104.0,"Position":474.0,"HyperDash":false},{"StartTime":284179.0,"Position":252.0,"HyperDash":false},{"StartTime":284254.0,"Position":247.0,"HyperDash":false},{"StartTime":284330.0,"Position":406.0,"HyperDash":false},{"StartTime":284405.0,"Position":319.0,"HyperDash":false},{"StartTime":284480.0,"Position":253.0,"HyperDash":false},{"StartTime":284556.0,"Position":411.0,"HyperDash":false},{"StartTime":284631.0,"Position":205.0,"HyperDash":false},{"StartTime":284706.0,"Position":54.0,"HyperDash":false},{"StartTime":284782.0,"Position":224.0,"HyperDash":false},{"StartTime":284857.0,"Position":465.0,"HyperDash":false},{"StartTime":284932.0,"Position":432.0,"HyperDash":false},{"StartTime":285008.0,"Position":108.0,"HyperDash":false},{"StartTime":285083.0,"Position":95.0,"HyperDash":false},{"StartTime":285158.0,"Position":436.0,"HyperDash":false},{"StartTime":285234.0,"Position":61.0,"HyperDash":false},{"StartTime":285309.0,"Position":234.0,"HyperDash":false},{"StartTime":285384.0,"Position":394.0,"HyperDash":false},{"StartTime":285460.0,"Position":86.0,"HyperDash":false},{"StartTime":285535.0,"Position":491.0,"HyperDash":false},{"StartTime":285610.0,"Position":416.0,"HyperDash":false},{"StartTime":285686.0,"Position":44.0,"HyperDash":false},{"StartTime":285761.0,"Position":29.0,"HyperDash":false},{"StartTime":285836.0,"Position":402.0,"HyperDash":false},{"StartTime":285912.0,"Position":115.0,"HyperDash":false},{"StartTime":285987.0,"Position":87.0,"HyperDash":false}]},{"StartTime":286725.0,"Objects":[{"StartTime":286725.0,"Position":80.0,"HyperDash":false},{"StartTime":286776.0,"Position":116.003235,"HyperDash":false},{"StartTime":286827.0,"Position":150.517319,"HyperDash":false},{"StartTime":286878.0,"Position":201.896988,"HyperDash":false},{"StartTime":286930.0,"Position":241.944443,"HyperDash":false},{"StartTime":286981.0,"Position":259.183777,"HyperDash":false},{"StartTime":287032.0,"Position":320.093781,"HyperDash":false},{"StartTime":287084.0,"Position":319.821442,"HyperDash":false},{"StartTime":287135.0,"Position":270.5175,"HyperDash":false},{"StartTime":287186.0,"Position":225.266876,"HyperDash":false},{"StartTime":287238.0,"Position":212.995529,"HyperDash":false},{"StartTime":287289.0,"Position":225.29332,"HyperDash":false},{"StartTime":287340.0,"Position":285.537354,"HyperDash":false},{"StartTime":287392.0,"Position":301.644073,"HyperDash":false},{"StartTime":287443.0,"Position":366.0163,"HyperDash":false},{"StartTime":287494.0,"Position":394.099243,"HyperDash":false},{"StartTime":287582.0,"Position":465.1608,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3644427.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3644427.osu new file mode 100644 index 0000000000..522f8d5a4a --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3644427.osu @@ -0,0 +1,1450 @@ +osu file format v14 + +[General] +StackLeniency: 0.8 +Mode: 0 + +[Difficulty] +HPDrainRate:5 +CircleSize:3 +OverallDifficulty:8 +ApproachRate:9.2 +SliderMultiplier:1.7 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +2,96220,104148 +2,113675,117239 +2,205476,207343 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +22,272.727272727273,5,2,1,50,1,0 +22,-125,4,2,1,50,0,0 +840,-83.3333333333333,4,2,1,50,0,0 +1385,-125,4,2,1,50,0,0 +2203,-83.3333333333333,4,2,1,50,0,0 +2749,-125,4,2,1,50,0,0 +3567,-83.3333333333333,4,2,1,50,0,0 +4112,-125,4,2,1,50,0,0 +4931,-83.3333333333333,4,2,1,50,0,0 +5476,-125,4,2,1,50,0,0 +6294,-83.3333333333333,4,2,1,50,0,0 +6840,-125,4,2,1,50,0,0 +7658,-83.3333333333333,4,2,1,50,0,0 +8203,-125,4,2,1,50,0,0 +9022,-83.3333333333333,4,2,1,50,0,0 +9567,-125,4,2,1,50,0,0 +10385,-83.3333333333333,4,2,1,50,0,0 +10931,-125,4,2,1,50,0,0 +12021,272.727272727273,4,2,1,70,1,0 +12021,-83.3333333333333,4,2,1,70,0,0 +29475,-100,4,2,1,70,0,0 +38202,-125,4,2,1,50,0,0 +40384,-100,4,2,1,70,0,0 +42566,-125,4,2,1,50,0,0 +44748,-100,4,2,1,70,0,0 +46930,-83.3333333333333,4,2,1,80,0,1 +48702,-83.3333333333333,4,2,2,80,0,1 +48771,-83.3333333333333,4,2,1,80,0,1 +48975,-83.3333333333333,4,2,2,80,0,1 +49043,-83.3333333333333,4,2,1,80,0,1 +53066,-83.3333333333333,4,2,2,80,0,1 +53134,-83.3333333333333,4,2,1,80,0,1 +55657,-100,4,2,1,70,0,0 +64384,-125,4,2,1,60,0,0 +66566,-100,4,2,1,80,0,0 +68748,-125,4,2,1,60,0,0 +70930,-100,4,2,1,80,0,0 +73111,-100,4,2,1,50,0,0 +74202,-100,4,2,3,50,0,0 +74293,-100,4,2,2,50,0,0 +74475,-100,4,2,3,50,0,0 +74566,-100,4,2,2,50,0,0 +74748,-100,4,2,3,50,0,0 +74839,-100,4,2,2,50,0,0 +75021,-100,4,2,3,50,0,0 +75111,-100,4,2,2,50,0,0 +75293,-100,4,2,1,50,0,0 +76384,-100,4,2,4,50,0,0 +76475,-100,4,2,1,50,0,0 +76657,-100,4,2,4,50,0,0 +76748,-100,4,2,1,50,0,0 +76930,-83.3333333333333,4,2,1,55,0,0 +77475,-83.3333333333333,4,2,1,65,0,0 +86202,-100,4,2,1,75,0,0 +96021,-100,4,2,1,40,0,0 +103657,-100,4,2,2,50,0,0 +104202,-100,4,2,1,60,0,0 +104748,-83.3333333333333,4,2,1,80,0,1 +107066,-83.3333333333333,4,2,2,80,0,1 +107134,-83.3333333333333,4,2,1,80,0,1 +107339,-83.3333333333333,4,2,2,80,0,1 +107407,-83.3333333333333,4,2,1,80,0,1 +107611,-83.3333333333333,4,2,2,80,0,1 +107680,-83.3333333333333,4,2,1,80,0,1 +107884,-83.3333333333333,4,2,2,80,0,1 +107952,-83.3333333333333,4,2,1,80,0,1 +108157,-83.3333333333333,4,2,2,80,0,1 +108225,-83.3333333333333,4,2,1,80,0,1 +108430,-83.3333333333333,4,2,2,80,0,1 +108498,-83.3333333333333,4,2,1,80,0,1 +111430,-83.3333333333333,4,2,2,80,0,1 +111498,-83.3333333333333,4,2,1,80,0,1 +111702,-83.3333333333333,4,2,2,80,0,1 +111771,-83.3333333333333,4,2,1,80,0,1 +111975,-83.3333333333333,4,2,2,80,0,1 +112043,-83.3333333333333,4,2,1,80,0,1 +112248,-83.3333333333333,4,2,2,80,0,1 +112316,-83.3333333333333,4,2,1,80,0,1 +113475,-125,4,2,3,50,0,0 +113748,-125,4,2,4,50,0,0 +117839,-125,4,2,3,50,0,0 +117975,-125,4,2,1,50,0,0 +118111,-125,4,2,4,50,0,0 +118248,-125,4,2,1,50,0,0 +118384,-125,4,2,4,50,0,0 +118521,-125,4,2,1,50,0,0 +118657,-125,4,2,4,50,0,0 +118793,-125,4,2,1,50,0,0 +118930,-125,4,2,4,50,0,0 +119066,-125,4,2,1,50,0,0 +119202,-125,4,2,4,50,0,0 +119339,-125,4,2,1,50,0,0 +119475,-125,4,2,4,50,0,0 +119611,-125,4,2,1,50,0,0 +119748,-125,4,2,4,50,0,0 +119884,-125,4,2,1,50,0,0 +120021,-125,4,2,4,50,0,0 +120157,-125,4,2,1,50,0,0 +120293,-125,4,2,4,50,0,0 +120430,-125,4,2,1,50,0,0 +120566,-125,4,2,4,50,0,0 +120702,-125,4,2,1,50,0,0 +120839,-125,4,2,4,50,0,0 +120975,-125,4,2,1,50,0,0 +121111,-125,4,2,4,50,0,0 +121248,-125,4,2,1,50,0,0 +121384,-125,4,2,4,50,0,0 +121521,-125,4,2,1,50,0,0 +121657,-125,4,2,4,50,0,0 +121793,-125,4,2,1,50,0,0 +121930,-125,4,2,4,50,0,0 +122066,-125,4,2,1,50,0,0 +122202,-100,4,2,1,60,0,0 +148384,-83.3333333333333,4,2,1,60,0,0 +149611,-100,4,2,1,60,0,0 +150975,-83.3333333333333,4,2,1,60,0,0 +152066,-100,4,2,1,60,0,0 +156021,-83.3333333333333,4,2,1,60,0,0 +157111,-83.3333333333333,4,2,1,65,0,0 +172384,-83.3333333333333,4,2,3,65,0,0 +172566,-83.3333333333333,4,2,2,65,0,0 +172930,-83.3333333333333,4,2,1,65,0,0 +173067,210.526315789474,4,2,1,65,1,0 +173277,222.222222222222,4,2,1,85,1,0 +173277,-100,4,2,1,85,0,1 +207943,272.727272727273,4,2,1,50,1,1 +207943,-125,4,2,1,50,0,0 +211215,-100,4,2,1,70,0,0 +219943,-100,4,2,1,60,0,0 +223215,-100,4,2,1,80,0,0 +227715,-100,4,2,2,80,0,0 +227783,-100,4,2,1,80,0,0 +227988,-100,4,2,2,80,0,0 +228056,-100,4,2,1,80,0,0 +228261,-100,4,2,2,80,0,0 +228329,-100,4,2,1,80,0,0 +228533,-100,4,2,2,80,0,0 +228602,-100,4,2,1,80,0,0 +229761,-100,4,2,1,50,0,0 +230852,-100,4,2,3,50,0,0 +230943,-100,4,2,2,50,0,0 +231124,-100,4,2,3,50,0,0 +231215,-100,4,2,2,50,0,0 +231397,-100,4,2,3,50,0,0 +231488,-100,4,2,2,50,0,0 +231670,-100,4,2,3,50,0,0 +231761,-100,4,2,2,50,0,0 +231943,-100,4,2,1,50,0,0 +233033,-100,4,2,3,50,0,0 +233124,-100,4,2,1,50,0,0 +233306,-100,4,2,3,50,0,0 +233397,-100,4,2,1,50,0,0 +233579,-83.3333333333333,4,2,1,50,0,0 +234124,-83.3333333333333,4,2,1,65,0,0 +242852,-100,4,2,1,75,0,0 +251579,-83.3333333333333,4,2,1,80,0,1 +253897,-83.3333333333333,4,2,2,80,0,1 +253965,-83.3333333333333,4,2,1,80,0,1 +254170,-83.3333333333333,4,2,2,80,0,1 +254238,-83.3333333333333,4,2,1,80,0,1 +254443,-83.3333333333333,4,2,2,80,0,1 +254511,-83.3333333333333,4,2,1,80,0,1 +254715,-83.3333333333333,4,2,2,80,0,1 +254783,-83.3333333333333,4,2,1,80,0,1 +254988,-83.3333333333333,4,2,2,80,0,1 +255056,-83.3333333333333,4,2,1,80,0,1 +255261,-83.3333333333333,4,2,2,80,0,1 +255329,-83.3333333333333,4,2,1,80,0,1 +258261,-83.3333333333333,4,2,2,80,0,1 +258329,-83.3333333333333,4,2,1,80,0,1 +258533,-83.3333333333333,4,2,2,80,0,1 +258602,-83.3333333333333,4,2,1,80,0,1 +258806,-83.3333333333333,4,2,2,80,0,1 +258874,-83.3333333333333,4,2,1,80,0,1 +259079,-83.3333333333333,4,2,2,80,0,1 +259147,-100,4,2,1,80,0,1 +260033,-100,4,2,1,80,0,1 +260306,-83.3333333333333,4,2,1,90,0,1 +260313,-83.3333333333333,4,2,1,90,0,1 +262624,-83.3333333333333,4,2,2,90,0,1 +262693,-83.3333333333333,4,2,1,90,0,1 +262897,-83.3333333333333,4,2,2,90,0,1 +262965,-83.3333333333333,4,2,1,90,0,1 +263170,-83.3333333333333,4,2,2,90,0,1 +263238,-83.3333333333333,4,2,1,90,0,1 +263443,-83.3333333333333,4,2,2,90,0,1 +263511,-83.3333333333333,4,2,1,90,0,1 +263715,-83.3333333333333,4,2,2,90,0,1 +263783,-83.3333333333333,4,2,1,90,0,1 +263988,-83.3333333333333,4,2,2,90,0,1 +264056,-83.3333333333333,4,2,1,90,0,1 +266988,-83.3333333333333,4,2,2,90,0,1 +267056,-83.3333333333333,4,2,1,90,0,1 +267261,-83.3333333333333,4,2,2,90,0,1 +267329,-83.3333333333333,4,2,1,90,0,1 +267533,-83.3333333333333,4,2,2,90,0,1 +267602,-83.3333333333333,4,2,1,90,0,1 +267806,-83.3333333333333,4,2,2,90,0,1 +267874,-83.3333333333333,4,2,1,90,0,1 +269033,532.150776053215,4,2,1,60,1,1 +269033,-100,4,2,1,60,0,0 +269965,-100,4,2,1,5,0,0 +270097,-66.6666666666667,4,2,1,60,0,0 +272211,-100,4,2,1,60,0,0 +273282,-100,4,2,1,60,0,0 +273290,558.139534883721,4,2,1,60,1,0 +273848,600,4,2,1,60,1,0 +273848,-100,4,2,1,60,0,0 +274248,750,4,2,1,60,1,0 +274269,-100,4,2,1,60,0,0 +274498,750,4,2,1,60,1,0 +274498,-100,4,2,1,60,0,0 +274873,800,4,2,1,60,1,0 +274873,-100,4,2,1,60,0,0 +275273,923.076923076923,4,2,1,60,1,0 +275273,-100,4,2,1,60,0,0 +275734,960,4,2,1,60,1,0 +275734,-100,4,2,1,60,0,0 +276054,1200,4,2,1,60,1,0 +276254,995.850622406639,4,2,1,70,1,0 +276254,-100,4,2,1,70,0,0 +276257,-100,4,2,1,70,0,0 +277249,764.331210191083,4,2,1,70,1,0 +277257,-100,4,2,1,70,0,0 +277503,693.64161849711,4,2,1,70,1,0 +277737,-100,4,2,1,70,0,0 +278181,-100,4,2,1,70,0,0 +278196,631.578947368421,4,2,1,70,1,0 +278609,-100,4,2,1,70,0,0 +278617,588.235294117647,4,2,1,70,1,0 +278813,545.454545454546,4,2,1,70,1,0 +279009,-100,4,2,1,70,0,0 +279358,521.739130434783,4,2,1,70,1,0 +279372,-100,4,2,1,70,0,0 +279687,-100,4,2,1,70,0,0 +279705,718.562874251497,4,2,1,70,1,0 +279944,666.666666666667,4,2,1,70,1,0 +279947,-100,4,2,1,70,0,0 +280170,-100,4,2,1,70,0,0 +280604,-100,4,2,1,70,0,0 +280610,558.139534883721,4,2,1,70,1,0 +280889,521.739130434783,4,2,1,70,1,0 +281149,576.923076923077,4,2,1,70,1,0 +281436,-100,4,2,1,70,0,0 +281437,609.137055837563,4,2,1,70,1,0 +281736,-100,4,2,1,70,0,0 +281741,631.578947368421,4,2,1,70,1,0 +281843,-100,4,2,1,70,0,0 +282056,406.779661016949,4,2,1,70,1,0 +282259,415.22491349481,4,2,1,70,1,0 +282669,-100,4,2,1,70,0,0 +282674,428.571428571429,4,2,1,70,1,0 +283097,-100,4,2,1,70,0,0 +283497,-100,4,2,1,70,0,0 +283531,400,4,2,1,70,1,0 +283931,375,4,2,1,70,1,0 +284118,436.363636363636,4,2,1,70,1,0 +284247,-100,4,2,1,70,0,0 +284554,461.538461538462,4,2,1,70,1,0 +284647,-100,4,2,1,70,0,0 +285015,480,4,2,1,70,1,0 +285247,-100,4,2,1,70,0,0 +285255,500,4,2,1,70,1,0 +285599,-100,4,2,1,70,0,0 +285741,-100,4,2,1,70,0,0 +285755,369.230769230769,4,2,1,70,1,0 +286124,601.642483981269,4,2,1,70,1,0 +286725,857.142857142857,4,2,1,90,1,0 +286725,-25,4,2,1,90,0,0 + +[HitObjects] +28,123,22,6,0,L|40:187,5,34,6|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +106,58,431,2,0,L|122:-5,5,34,2|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +207,61,840,2,0,B|280:43|280:43|288:45|288:45|385:21,2,152.999995330811,6|6|6,3:2|3:2|3:2,0:0:0:0: +313,147,1385,6,0,L|377:159,5,34,6|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +347,252,1794,2,0,L|396:239,5,34,2|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +415,328,2203,2,0,B|433:255|433:255|431:247|431:247|455:150,2,152.999995330811,6|6|6,3:2|3:2|3:2,0:0:0:0: +235,343,2749,6,0,L|171:331,5,34,6|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +219,239,3158,2,0,L|236:187,5,34,2|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +299,136,3567,2,0,B|231:152|231:152|223:150|223:150|150:168,2,152.999995330811,6|6|6,3:2|3:2|3:2,0:0:0:0: +234,11,4112,6,0,L|182:-2,5,34,6|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +135,70,4522,2,0,L|83:83,5,34,2|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +35,15,4931,2,0,B|53:88|53:88|51:96|51:96|75:193,2,152.999995330811,6|6|6,3:2|3:2|3:2,0:0:0:0: +22,251,5476,6,0,L|17:306,5,34,6|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +120,238,5885,2,0,L|171:256,5,34,2|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +187,333,6294,2,0,B|114:351|114:351|106:349|106:349|9:373,2,152.999995330811,6|6|6,3:2|3:2|3:2,0:0:0:0: +363,340,6840,6,0,L|358:285,5,34,6|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +411,223,7249,2,0,L|462:205,5,34,2|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +355,148,7658,2,0,B|373:75|373:75|371:67|371:67|395:-30,2,152.999995330811,6|6|6,3:2|3:2|3:2,0:0:0:0: +502,158,8203,6,0,L|514:222,5,34,6|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +419,236,8612,2,0,L|436:288,5,34,2|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +364,341,9022,2,0,B|437:359|437:359|445:357|445:357|542:381,2,152.999995330811,6|6|6,3:2|3:2|3:2,0:0:0:0: +233,235,9567,6,0,L|222:181,5,34,6|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +284,125,9976,2,0,L|304:94,5,34,2|2|2|2|2|2,3:2|1:2|1:2|3:2|1:2|1:2,0:0:0:0: +245,16,10385,6,0,P|171:23|132:125,2,152.999995330811,6|6|6,3:2|3:2|3:2,0:0:0:0: +407,374,12021,6,0,P|406:316|461:265,1,101.999996887207,6|8,3:2|2:2,0:0:0:0: +484,281,12225,1,2,3:2:0:0: +484,281,12293,2,0,P|429:260|401:212,1,101.999996887207,0|8,3:3|2:2,0:0:0:0: +387,125,12566,2,0,P|462:119|484:41,2,152.999995330811,2|2|10,3:2|3:2|3:2,0:0:0:0: +274,30,13111,6,0,L|141:54,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +124,33,13316,1,2,3:2:0:0: +124,33,13384,2,0,L|-1:56,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +24,154,13657,2,0,P|81:177|106:268,1,152.999995330811,2|2,3:2|3:2,0:0:0:0: +229,353,14066,1,10,3:2:0:0: +328,376,14202,6,0,P|324:316|293:277,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +256,265,14407,1,2,3:2:0:0: +256,265,14475,2,0,P|306:242|339:189,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +378,113,14748,2,0,P|449:120|500:192,2,152.999995330811,2|2|10,3:2|3:2|3:2,0:0:0:0: +277,8,15293,6,0,L|246:133,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +212,137,15498,1,2,3:2:0:0: +212,137,15566,2,0,L|243:262,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +256,336,15839,2,0,P|314:314|423:379,1,152.999995330811,2|2,3:2|3:2,0:0:0:0: +473,159,16248,1,10,3:2:0:0: +486,58,16384,6,0,P|431:61|387:116,1,101.999996887207,6|8,3:2|3:2,0:0:0:0: +382,142,16589,1,2,3:2:0:0: +382,142,16657,2,0,P|336:101|269:103,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +201,131,16930,2,0,P|189:74|105:47,2,152.999995330811,2|2|10,3:2|3:2|3:2,0:0:0:0: +40,174,17475,6,0,L|63:312,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +97,307,17680,1,2,3:2:0:0: +97,307,17748,2,0,L|235:284,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +275,223,18021,2,0,P|243:290|273:374,1,152.999995330811,2|2,3:2|3:2,0:0:0:0: +415,382,18430,1,10,3:2:0:0: +355,299,18566,6,0,P|394:279|466:297,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +486,250,18771,1,2,3:2:0:0: +486,250,18839,2,0,P|453:208|460:142,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +476,62,19111,2,0,P|444:116|342:98,2,152.999995330811,2|2|10,3:2|3:2|3:2,0:0:0:0: +306,4,19657,6,0,L|183:50,1,101.999996887207,6|8,3:2|3:2,0:0:0:0: +161,32,19861,1,2,3:2:0:0: +161,32,19930,2,0,L|207:155,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +127,201,20202,2,0,P|67:223|6:192,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +41,380,20475,1,0,1:1:0:0: +48,355,20543,1,8,2:3:0:0: +64,336,20611,1,8,2:3:0:0: +86,323,20679,1,4,2:3:0:0: +111,319,20748,6,0,P|172:336|208:385,1,101.999996887207,6|8,3:2|3:2,0:0:0:0: +249,382,20952,1,2,3:2:0:0: +249,382,21021,2,0,L|374:366,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +451,381,21293,2,0,P|460:339|385:240,1,152.999995330811,2|2,3:2|3:2,0:0:0:0: +398,95,21702,1,10,3:2:0:0: +337,177,21839,6,0,P|288:208|226:199,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +202,192,22043,1,2,3:2:0:0: +202,192,22111,2,0,L|172:82,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +7,86,22384,1,2,3:2:0:0: +7,86,22589,1,2,3:2:0:0: +7,86,22793,1,10,3:2:0:0: +61,245,22930,6,0,L|48:373,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +92,384,23134,1,2,3:2:0:0: +92,384,23202,2,0,P|149:373|187:330,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +262,283,23475,2,0,P|328:313|350:411,1,152.999995330811,2|2,3:2|3:2,0:0:0:0: +467,280,23884,1,10,3:2:0:0: +430,184,24021,6,0,L|310:204,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +284,192,24225,1,2,3:2:0:0: +284,192,24293,2,0,P|257:131|272:74,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +386,4,24566,1,2,3:2:0:0: +386,4,24771,1,2,3:2:0:0: +386,4,24975,1,10,3:2:0:0: +432,136,25111,6,0,P|427:195|465:245,1,101.999996887207,6|8,3:2|3:2,0:0:0:0: +416,272,25316,1,2,3:2:0:0: +416,272,25384,2,0,L|306:247,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +219,215,25657,2,0,P|172:266|191:388,1,152.999995330811,2|2,3:2|3:2,0:0:0:0: +40,259,26066,1,10,3:2:0:0: +28,157,26202,6,0,P|69:144|104:73,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +125,53,26407,1,2,3:2:0:0: +125,53,26475,2,0,L|146:171,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +221,307,26748,1,2,3:2:0:0: +221,307,26953,1,2,3:2:0:0: +221,307,27157,1,10,3:2:0:0: +379,281,27293,6,0,L|497:303,1,101.999996887207,2|8,3:2|3:2,0:0:0:0: +510,259,27498,1,2,3:2:0:0: +510,259,27566,2,0,P|514:209|471:147,1,101.999996887207,0|10,3:3|3:2,0:0:0:0: +503,62,27839,2,0,P|461:116|373:111,1,152.999995330811,2|2,3:2|3:2,0:0:0:0: +256,28,28248,1,10,3:2:0:0: +190,105,28384,5,8,2:3:0:0: +269,169,28521,1,4,2:3:0:0: +272,178,28589,1,8,2:3:0:0: +275,187,28657,2,0,L|260:327,1,101.999996887207,8|8,2:3|2:3,0:0:0:0: +179,345,28930,1,8,2:3:0:0: +154,338,28998,1,8,2:3:0:0: +135,322,29066,1,4,2:3:0:0: +122,300,29134,1,8,2:3:0:0: +118,275,29202,2,0,L|106:333,3,50.9999984436036,8|4|8|8,2:3|2:3|2:3|2:3,0:0:0:0: +45,207,29475,6,0,L|-10:224,3,42.5,14|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +102,137,29748,2,0,L|157:154,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +193,228,30021,2,0,L|205:268,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +291,311,30293,2,0,L|303:270,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +391,243,30566,5,10,3:2:0:0: +400,246,30634,1,0,3:3:0:0: +409,249,30702,1,0,3:3:0:0: +434,344,30839,1,10,3:2:0:0: +425,347,30907,1,0,3:3:0:0: +416,350,30975,1,0,3:3:0:0: +512,269,31111,2,0,L|499:228,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +435,152,31384,2,0,L|447:111,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +381,34,31657,6,0,L|340:46,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +251,83,31930,2,0,L|196:66,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +146,137,32202,2,0,L|158:177,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +56,112,32475,2,0,L|68:72,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +22,199,32748,5,10,3:2:0:0: +25,208,32816,1,0,3:3:0:0: +28,217,32884,1,0,3:3:0:0: +93,292,33021,1,10,3:2:0:0: +90,301,33089,1,0,3:3:0:0: +87,310,33157,1,0,3:3:0:0: +168,367,33293,1,10,3:2:0:0: +176,365,33361,1,0,3:3:0:0: +184,363,33430,2,0,L|288:375,1,85,0|10,3:3|3:2,0:0:0:0: +274,168,33839,6,0,L|262:128,3,42.5,14|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +330,66,34112,2,0,L|342:26,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +422,109,34384,2,0,L|463:121,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +461,218,34657,2,0,L|516:201,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +448,314,34930,5,10,3:2:0:0: +439,311,34998,1,0,3:3:0:0: +430,308,35066,1,0,3:3:0:0: +321,262,35202,1,10,3:2:0:0: +312,265,35270,1,0,3:3:0:0: +303,268,35338,1,0,3:3:0:0: +269,366,35475,2,0,L|214:349,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +162,271,35748,2,0,L|203:259,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +87,207,36021,6,0,L|99:167,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +31,105,36294,2,0,L|19:65,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +101,9,36566,2,0,L|142:21,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +184,108,36839,2,0,L|239:91,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +304,31,37111,5,10,3:2:0:0: +307,22,37179,1,0,3:3:0:0: +310,13,37247,1,0,3:3:0:0: +392,90,37384,1,10,3:2:0:0: +395,99,37452,1,0,3:3:0:0: +398,108,37520,1,0,3:3:0:0: +341,194,37657,2,0,L|363:249,3,42.5,8|8|8|8,2:3|2:3|2:3|2:3,0:0:0:0: +352,320,37930,2,0,L|374:375,3,42.5,4|4|4|4,2:3|2:3|2:3|2:3,0:0:0:0: +449,384,38202,6,0,P|490:343|470:247,1,136,6|2,3:2|2:2,0:0:0:0: +487,268,38748,2,0,L|351:239,1,136,2|2,1:2|2:2,0:0:0:0: +403,58,39293,2,0,B|330:66|368:102|248:108,1,136,2|2,3:2|3:2,0:0:0:0: +277,105,39702,1,2,3:2:0:0: +155,6,39839,2,0,P|184:59|184:92,1,68,2|0,1:2|1:1,0:0:0:0: +65,163,40111,1,2,1:2:0:0: +65,163,40384,6,0,L|156:180,1,85,6|2,3:2|1:2,0:0:0:0: +90,336,40657,2,0,L|-1:353,1,85,0|2,3:3|1:2,0:0:0:0: +180,280,40930,1,2,3:2:0:0: +280,304,41066,1,2,1:2:0:0: +280,304,41134,1,2,2:2:0:0: +280,304,41202,2,0,L|371:321,1,85,2|2,3:2|1:2,0:0:0:0: +208,384,41475,5,2,3:2:0:0: +208,384,41611,1,2,1:2:0:0: +372,304,41748,2,0,L|281:287,1,85,2|2,3:2|1:2,0:0:0:0: +170,216,42021,2,0,L|190:119,1,85,2|0,3:2|1:1,0:0:0:0: +64,75,42293,2,0,L|72:31,3,42.5,8|8|4|4,2:3|2:3|2:3|2:3,0:0:0:0: +25,148,42566,6,0,P|49:229|11:298,1,136,6|2,3:2|2:2,0:0:0:0: +32,274,43111,2,0,L|187:310,1,136,2|2,1:2|2:2,0:0:0:0: +420,179,43657,2,0,B|347:187|385:223|265:229,1,136,2|2,3:2|3:2,0:0:0:0: +294,226,44066,1,2,3:2:0:0: +204,146,44202,2,0,P|204:111|236:62,1,68,2|0,1:2|1:1,0:0:0:0: +381,14,44475,1,2,1:2:0:0: +381,14,44748,6,0,L|394:111,1,85,6|2,3:2|1:2,0:0:0:0: +500,237,45021,2,0,L|487:334,1,85,2|2,2:2|1:2,0:0:0:0: +285,242,45293,1,2,2:2:0:0: +397,200,45430,1,2,1:2:0:0: +397,200,45498,1,2,3:2:0:0: +397,200,45566,2,0,L|384:297,1,85,2|2,2:2|1:2,0:0:0:0: +208,318,45839,5,0,1:1:0:0: +208,318,45907,1,0,1:1:0:0: +208,318,45975,2,0,P|166:292|113:291,1,85,8|4,2:3|2:3,0:0:0:0: +47,227,46248,1,0,1:1:0:0: +54,185,46316,1,0,1:1:0:0: +61,143,46384,1,8,2:3:0:0: +118,57,46521,2,0,L|108:-6,5,42.5,8|8|4|4|4|4,2:3|2:3|2:3|2:3|2:3|2:3,0:0:0:0: +186,106,46930,6,0,P|246:93|289:35,1,101.999996887207,6|0,3:2|1:1,0:0:0:0: +446,47,47202,2,0,P|407:14|357:7,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +367,108,47475,2,0,L|392:212,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +297,383,47748,2,0,L|320:283,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +243,216,48021,6,0,L|143:239,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +188,88,48293,1,2,3:2:0:0: +188,88,48430,1,2,1:2:0:0: +59,159,48566,2,0,P|39:239|63:287,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +174,359,48839,2,0,L|274:382,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +423,310,49111,6,0,P|430:244|402:199,1,101.999996887207,6|2,3:2|1:2,0:0:0:0: +346,71,49384,2,0,P|399:110|452:108,1,101.999996887207,2|2,3:2|1:2,0:0:0:0: +217,12,49657,1,2,3:2:0:0: +208,152,49793,1,2,1:2:0:0: +208,152,49861,1,2,2:2:0:0: +208,152,49930,2,0,L|73:172,1,101.999996887207,2|2,3:2|1:2,0:0:0:0: +45,14,50202,5,2,3:2:0:0: +108,77,50338,1,2,1:2:0:0: +107,167,50475,1,2,3:2:0:0: +44,230,50611,1,2,1:2:0:0: +70,316,50748,2,0,B|165:332|165:332|180:346|180:346|302:361,1,203.999993774414,8|4,3:3|2:3,0:0:0:0: +441,286,51157,5,4,2:3:0:0: +434,296,51225,1,4,2:3:0:0: +427,306,51293,2,0,L|401:188,1,101.999996887207,6|0,3:2|1:1,0:0:0:0: +482,12,51566,2,0,L|456:130,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +357,113,51839,2,0,P|316:142|257:142,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +119,20,52111,2,0,P|169:22|210:51,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +164,143,52384,6,0,P|123:174|31:168,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +0,304,52657,1,2,3:2:0:0: +0,304,52793,1,2,1:2:0:0: +124,339,52930,2,0,L|236:353,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +316,242,53202,2,0,L|302:130,1,101.999996887207,2|0,3:2|3:1,0:0:0:0: +332,0,53475,6,0,P|389:17|424:69,1,101.999996887207,6|0,3:2|1:1,0:0:0:0: +512,147,53748,2,0,P|455:164|420:216,1,101.999996887207,8|0,2:3|1:1,0:0:0:0: +512,332,54021,1,0,3:3:0:0: +363,319,54157,1,0,1:1:0:0: +363,319,54225,1,0,2:2:0:0: +363,319,54293,2,0,L|246:300,1,101.999996887207,4|0,3:3|1:1,0:0:0:0: +308,164,54566,5,0,3:3:0:0: +269,181,54634,1,0,3:3:0:0: +227,177,54702,1,0,1:1:0:0: +193,153,54770,1,0,1:1:0:0: +175,116,54838,1,8,2:3:0:0: +81,73,54975,1,0,1:1:0:0: +74,115,55043,1,0,1:1:0:0: +67,157,55111,1,4,2:3:0:0: +18,247,55248,2,0,L|28:310,5,50.9999984436036,0|0|8|8|4|4,1:1|1:1|2:3|2:3|2:3|2:3,0:0:0:0: +87,361,55657,6,0,L|128:349,3,42.5,14|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +175,263,55929,2,0,L|230:280,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +295,228,56202,2,0,L|307:188,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +265,105,56475,2,0,L|253:65,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +327,8,56748,5,10,3:2:0:0: +336,11,56816,1,0,3:3:0:0: +345,14,56884,1,0,3:3:0:0: +414,83,57021,1,10,3:2:0:0: +423,80,57089,1,0,3:3:0:0: +432,77,57157,1,0,3:3:0:0: +502,143,57293,2,0,L|490:183,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +431,255,57566,2,0,L|443:295,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +356,334,57839,6,0,L|344:374,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +294,256,58112,2,0,L|334:244,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +205,299,58384,2,0,L|193:259,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +151,377,58657,2,0,L|111:365,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +21,328,58930,5,10,3:2:0:0: +18,337,58998,1,0,3:3:0:0: +15,346,59066,1,0,3:3:0:0: +96,263,59202,1,10,3:2:0:0: +93,254,59270,1,0,3:3:0:0: +90,245,59338,1,0,3:3:0:0: +38,161,59475,1,10,3:2:0:0: +41,152,59543,1,0,3:3:0:0: +44,143,59611,2,0,L|32:18,1,85,0|10,3:3|3:2,0:0:0:0: +227,20,60021,6,0,L|215:60,3,42.5,14|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +257,143,60294,2,0,L|269:183,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +357,143,60566,2,0,L|398:131,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +445,45,60838,2,0,L|500:62,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +496,149,61111,5,10,3:2:0:0: +493,158,61179,1,0,3:3:0:0: +490,167,61247,1,0,3:3:0:0: +420,245,61384,1,10,3:2:0:0: +417,236,61452,1,0,3:3:0:0: +414,227,61521,1,0,3:3:0:0: +389,337,61657,2,0,L|349:325,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +277,266,61930,2,0,L|237:278,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +161,214,62202,6,0,L|149:174,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +142,307,62475,2,0,L|102:295,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +2,292,62748,2,0,L|14:252,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +0,158,63021,2,0,L|40:146,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +95,70,63293,5,10,3:2:0:0: +104,73,63361,1,0,3:3:0:0: +113,76,63429,1,0,3:3:0:0: +189,141,63566,1,10,3:2:0:0: +198,138,63634,1,0,3:3:0:0: +207,135,63702,1,0,3:3:0:0: +281,59,63839,2,0,L|338:73,3,42.5,8|8|8|8,2:3|2:3|2:3|2:3,0:0:0:0: +362,142,64111,2,0,L|419:156,3,42.5,4|4|4|4,2:3|2:3|2:3|2:3,0:0:0:0: +478,112,64384,6,0,P|441:165|461:260,1,136,6|2,3:2|1:2,0:0:0:0: +485,364,64930,2,0,L|325:332,1,136,2|0,3:2|1:1,0:0:0:0: +222,294,65475,2,0,B|156:309|190:338|97:360,1,136,2|2,3:2|1:2,0:0:0:0: +104,358,65884,1,2,3:2:0:0: +16,285,66021,2,0,P|18:244|44:201,1,68,2|0,3:2|1:1,0:0:0:0: +28,219,66225,1,0,1:1:0:0: +28,219,66293,1,10,2:3:0:0: +90,145,66566,6,0,L|76:55,1,85,6|0,3:2|1:1,0:0:0:0: +256,0,66839,2,0,L|242:90,1,85,0|0,3:2|1:1,0:0:0:0: +186,179,67111,1,0,3:3:0:0: +273,263,67248,1,2,1:2:0:0: +273,263,67316,1,2,3:2:0:0: +273,263,67384,2,0,L|395:248,1,85,2|2,3:2|1:2,0:0:0:0: +471,151,67657,5,2,3:2:0:0: +471,151,67793,1,2,1:2:0:0: +392,272,67930,2,0,L|307:282,1,85,2|2,3:2|1:2,0:0:0:0: +165,327,68202,2,0,L|179:237,1,85,2|0,3:2|1:1,0:0:0:0: +266,112,68475,2,0,L|307:119,3,42.5,8|8|4|4,3:3|2:3|2:3|2:3,0:0:0:0: +358,51,68748,6,0,P|439:27|508:65,1,136,6|2,3:2|1:2,0:0:0:0: +447,174,69293,2,0,L|473:336,1,136,2|2,3:2|1:2,0:0:0:0: +343,253,69839,2,0,B|308:188|278:221|230:145,1,136,2|2,3:2|1:2,0:0:0:0: +216,58,70248,1,0,1:1:0:0: +216,58,70316,1,0,1:1:0:0: +216,58,70384,2,0,P|177:80|140:84,1,68,8|8,2:3|2:3,0:0:0:0: +58,36,70657,1,4,2:3:0:0: +58,36,70930,6,0,L|45:155,1,85,6|2,3:2|1:2,0:0:0:0: +129,284,71202,2,0,L|142:403,1,85,2|2,3:2|1:2,0:0:0:0: +132,180,71475,1,2,3:2:0:0: +228,241,71611,1,2,1:2:0:0: +228,241,71680,1,2,3:2:0:0: +228,241,71748,2,0,L|312:250,1,85,2|2,3:2|1:2,0:0:0:0: +382,363,72021,5,2,3:2:0:0: +414,371,72089,1,0,1:1:0:0: +448,367,72157,1,0,1:1:0:0: +478,351,72225,1,0,1:1:0:0: +500,326,72293,1,0,1:1:0:0: +453,220,72430,1,0,1:1:0:0: +449,206,72498,1,0,1:1:0:0: +445,192,72566,2,0,L|422:244,2,42.5,0|0|0,3:3|1:1|1:1,0:0:0:0: +486,110,72839,2,0,L|503:71,1,42.5,0|0,1:1|1:1,0:0:0:0: +414,68,72975,2,0,L|431:29,1,42.5,0|0,1:1|1:1,0:0:0:0: +344,23,73111,5,6,3:2:0:0: +62,180,75293,1,6,3:2:0:0: +403,350,76930,2,0,P|452:342|476:326,5,67.9999979248048,2|2|2|8|8|4,1:2|1:2|1:2|2:3|2:3|2:3,0:0:0:0: +412,257,77475,6,0,P|419:224|443:195,2,67.9999979248048,6|2|2,3:2|2:2|2:2,0:0:0:0: +320,230,77748,2,0,P|309:197|315:160,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +248,289,78021,2,0,P|255:322|279:351,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +156,316,78294,2,0,P|145:348|151:385,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +97,240,78566,5,2,3:2:0:0: +89,250,78657,2,0,L|12:266,1,67.9999979248048,2|2,2:2|2:2,0:0:0:0: +10,169,78839,1,10,2:2:0:0: +52,134,78930,1,2,2:2:0:0: +106,132,79021,1,2,2:2:0:0: +154,154,79111,2,0,P|231:144|238:9,1,203.999993774414,2|10,3:2|2:2,0:0:0:0: +258,34,79657,6,0,L|170:26,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +226,127,79930,2,0,L|138:142,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +287,204,80202,2,0,L|374:219,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +293,302,80475,2,0,L|373:339,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +218,362,80748,5,2,3:2:0:0: +209,352,80839,2,0,P|194:313|204:265,1,67.9999979248048,2|2,2:2|2:2,0:0:0:0: +256,215,81021,1,10,2:2:0:0: +299,183,81111,1,2,2:2:0:0: +352,172,81202,1,2,2:2:0:0: +398,143,81293,2,0,B|402:238|466:224|462:346,1,203.999993774414,10|2,3:2|1:2,0:0:0:0: +462,332,81839,6,0,P|421:340|377:374,2,67.9999979248048,6|2|2,3:2|2:2|2:2,0:0:0:0: +347,273,82111,2,0,P|315:300|294:351,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +368,179,82384,2,0,P|336:151|315:100,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +238,172,82657,2,0,P|224:132|231:77,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +135,75,82930,5,2,3:2:0:0: +139,58,83021,2,0,P|156:36|228:13,1,67.9999979248048,2|2,2:2|2:2,0:0:0:0: +41,127,83202,1,10,2:2:0:0: +83,161,83293,1,2,2:2:0:0: +103,211,83384,1,2,2:2:0:0: +99,265,83475,2,0,P|143:371|254:349,1,203.999993774414,2|10,3:2|2:2,0:0:0:0: +219,374,84021,6,0,L|156:351,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +237,275,84293,2,0,L|182:236,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +291,189,84566,2,0,L|354:166,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +273,90,84839,2,0,L|327:51,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +210,14,85111,5,2,3:2:0:0: +199,27,85202,2,0,P|177:68|182:118,1,67.9999979248048,2|2,2:2|2:2,0:0:0:0: +227,174,85384,1,2,1:2:0:0: +280,183,85475,1,2,1:2:0:0: +326,210,85566,1,2,1:2:0:0: +380,206,85657,2,0,B|477:182|477:182|551:217,2,152.999995330811,6|6|2,1:2|1:2|1:2,0:0:0:0: +414,298,86202,6,0,L|405:350,3,42.5,6|0|8|0,3:2|3:3|3:2|0:0,0:0:0:0: +313,333,86475,2,0,L|322:385,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +229,285,86748,6,0,L|238:233,1,42.5,2|0,3:2|3:3,0:0:0:0: +140,308,86884,2,0,L|149:256,1,42.5,8|0,3:2|0:0,0:0:0:0: +51,334,87021,2,0,L|60:282,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +41,200,87293,6,0,L|-11:209,2,42.5,2|0|8,3:2|3:3|3:2,0:0:0:0: +111,132,87566,1,2,3:2:0:0: +119,134,87634,1,0,3:3:0:0: +127,136,87702,1,8,3:2:0:0: +152,45,87839,2,0,L|100:36,2,42.5,2|0|8,3:2|3:3|3:2,0:0:0:0: +222,113,88112,1,2,3:2:0:0: +230,111,88180,1,0,3:3:0:0: +238,109,88248,1,8,3:2:0:0: +295,32,88384,6,0,L|347:23,3,42.5,2|0|8|0,3:2|3:3|3:2|0:0,0:0:0:0: +334,129,88657,2,0,L|386:138,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +464,98,88930,6,0,L|473:150,1,42.5,2|0,3:2|3:3,0:0:0:0: +449,184,89066,2,0,L|458:236,1,42.5,8|0,3:2|0:0,0:0:0:0: +434,270,89202,2,0,L|443:322,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +362,365,89475,5,2,3:2:0:0: +360,372,89543,1,0,3:3:0:0: +358,381,89611,1,8,3:2:0:0: +288,302,89748,1,2,3:2:0:0: +286,295,89816,1,0,3:3:0:0: +284,286,89884,1,8,3:2:0:0: +201,348,90021,1,2,3:2:0:0: +193,346,90089,1,0,3:3:0:0: +185,344,90158,2,0,L|81:356,1,85,8|2,3:2|3:2,0:0:0:0: +67,179,90566,6,0,L|15:170,3,42.5,6|0|8|0,3:2|3:3|3:2|0:0,0:0:0:0: +50,69,90839,2,0,L|-2:78,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +147,88,91111,6,0,L|138:36,1,42.5,2|0,3:2|3:3,0:0:0:0: +236,111,91247,2,0,L|227:59,1,42.5,8|0,3:2|0:0,0:0:0:0: +325,137,91384,2,0,L|316:85,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +257,207,91657,6,0,L|248:259,2,42.5,2|0|8,3:2|3:3|3:2,0:0:0:0: +154,263,91930,1,2,3:2:0:0: +156,271,91998,1,0,3:3:0:0: +158,279,92066,1,8,3:2:0:0: +231,342,92203,2,0,L|240:394,2,42.5,2|0|8,3:2|3:3|3:2,0:0:0:0: +327,324,92476,1,2,3:2:0:0: +329,316,92544,1,0,3:3:0:0: +331,308,92612,1,8,3:2:0:0: +431,315,92748,6,0,L|422:367,3,42.5,2|0|8|0,3:2|3:3|3:2|0:0,0:0:0:0: +503,248,93021,2,0,L|495:206,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +457,113,93293,6,0,L|509:122,1,42.5,2|0,3:2|3:3,0:0:0:0: +371,79,93429,2,0,L|423:88,1,42.5,8|0,3:2|0:0,0:0:0:0: +286,47,93566,2,0,L|338:56,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +195,22,93839,5,2,3:2:0:0: +193,29,93907,1,0,3:3:0:0: +191,38,93975,1,8,3:2:0:0: +118,104,94112,1,2,3:2:0:0: +120,111,94180,1,0,3:3:0:0: +122,120,94248,1,8,3:2:0:0: +145,217,94385,1,2,3:2:0:0: +143,225,94453,1,0,3:3:0:0: +141,233,94522,2,0,L|153:337,1,85,8|2,3:2|3:2,0:0:0:0: +48,13,94930,5,0,1:1:0:0: +41,21,94998,1,0,1:1:0:0: +34,29,95066,2,0,L|85:20,3,42.5,0|0|0|0,1:1|1:1|1:1|1:1,0:0:0:0: +77,103,95339,2,0,L|128:94,1,42.5,0|0,1:1|1:1,0:0:0:0: +37,192,95475,2,0,L|88:183,8,42.5,0|0|0|0|0|0|0|0|6,1:1|1:1|1:1|1:1|1:1|1:1|1:1|1:1|2:2,0:0:0:0: +285,375,104748,6,0,P|225:362|182:304,1,101.999996887207,6|0,3:2|1:1,0:0:0:0: +372,333,105020,2,0,P|411:300|461:293,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +483,207,105293,2,0,L|508:103,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +381,19,105566,2,0,L|404:119,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +336,191,105839,6,0,L|236:214,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +190,349,106111,1,2,3:2:0:0: +190,349,106248,1,2,1:2:0:0: +66,289,106384,2,0,P|46:209|70:161,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +160,78,106657,2,0,P|210:83|256:62,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +419,106,106929,6,0,P|426:40|398:-5,1,101.999996887207,6|2,3:2|3:2,0:0:0:0: +350,180,107202,2,0,P|403:219|456:217,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +500,297,107475,1,2,3:2:0:0: +387,370,107611,1,2,3:2:0:0: +387,370,107679,1,2,3:2:0:0: +387,370,107748,2,0,L|252:390,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +126,374,108020,5,2,3:2:0:0: +139,286,108156,1,2,3:2:0:0: +213,233,108293,1,2,3:2:0:0: +301,247,108429,1,2,3:2:0:0: +267,163,108566,2,0,B|156:202|174:128|41:180,1,203.999993774414,2|8,3:2|2:3,0:0:0:0: +55,35,108975,5,4,2:3:0:0: +44,28,109043,1,4,2:3:0:0: +35,21,109111,2,0,L|153:-5,1,101.999996887207,6|0,3:2|1:1,0:0:0:0: +279,66,109384,2,0,L|378:87,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +474,77,109657,2,0,P|455:30|405:-1,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +357,183,109929,2,0,P|407:185|448:214,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +499,342,110202,6,0,P|458:373|366:367,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +280,304,110475,1,2,3:2:0:0: +280,304,110611,1,2,1:2:0:0: +357,183,110748,2,0,L|343:71,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +209,0,111020,2,0,L|195:112,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +65,166,111293,6,0,P|122:183|157:235,1,101.999996887207,6|2,3:2|3:2,0:0:0:0: +80,384,111566,2,0,P|66:326|93:269,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +148,213,111839,1,2,3:2:0:0: +269,287,111975,1,2,3:2:0:0: +269,287,112043,1,2,3:2:0:0: +269,287,112111,2,0,L|386:268,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +369,170,112384,5,8,2:3:0:0: +410,177,112452,1,8,2:3:0:0: +450,164,112520,1,8,2:3:0:0: +478,133,112588,1,8,2:3:0:0: +487,93,112656,1,4,2:3:0:0: +413,21,112793,1,4,2:3:0:0: +371,14,112861,1,4,2:3:0:0: +329,7,112929,1,8,2:3:0:0: +259,85,113066,2,0,L|196:95,6,50.9999984436036,8|8|4|4|4|4|6,2:3|2:3|2:3|2:3|2:3|2:3|3:2,0:0:0:0: +352,256,117839,6,0,P|366:320|331:396,2,136,6|2|2,3:2|1:3|3:3,0:0:0:0: +435,212,118521,1,2,3:2:0:0: +435,212,118657,2,0,P|363:208|306:147,1,136,2|2,1:3|3:3,0:0:0:0: +353,23,119203,1,2,1:3:0:0: +353,23,119339,2,0,L|508:50,1,136,2|2,3:2|3:2,0:0:0:0: +273,80,119748,1,2,1:3:0:0: +90,125,120021,6,0,P|84:60|27:-1,2,136,2|2|2,3:3|1:3|2:3,0:0:0:0: +128,215,120703,1,2,3:2:0:0: +128,215,120839,2,0,P|74:237|59:256,1,68,2|2,1:3|3:2,0:0:0:0: +14,317,121112,2,0,L|25:390,2,68,2|2|2,3:3|3:2|1:3,0:0:0:0: +68,243,121521,2,0,P|141:288|214:276,1,136,2|0,3:2|3:0,0:0:0:0: +267,337,121930,1,2,1:3:0:0: +267,337,122202,6,0,P|231:282|271:168,1,170,6|2,3:2|1:2,0:0:0:0: +252,185,122611,2,0,P|214:243|97:224,1,170,2|2,2:2|3:2,0:0:0:0: +58,185,123021,2,0,P|61:139|92:90,1,85,2|2,1:2|2:2,0:0:0:0: +6,0,123293,6,0,L|102:23,1,85,2|2,3:2|2:2,0:0:0:0: +156,71,123566,2,0,B|186:37|186:37|261:16,1,85,2|2,1:2|3:2,0:0:0:0: +349,103,123839,1,2,2:2:0:0: +375,21,123975,1,2,3:2:0:0: +456,45,124111,2,0,L|472:185,1,127.5,2|0,1:2|0:0,0:0:0:0: +498,203,124384,6,0,P|450:212|405:327,1,170,2|2,3:2|1:2,0:0:0:0: +400,312,124793,1,0,0:0:0:0: +320,342,124930,2,0,P|288:345|244:372,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +226,280,125202,2,0,P|199:298|175:343,2,56.6666666666667,2|2|2,1:2|2:2|2:2,0:0:0:0: +165,218,125475,6,0,P|151:188|152:137,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +64,166,125748,2,0,P|67:133|94:90,2,56.6666666666667,2|2|2,1:2|2:2|2:2,0:0:0:0: +98,29,126021,2,0,P|65:26|18:45,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +168,81,126293,1,2,1:2:0:0: +176,84,126384,2,0,P|208:86|256:67,1,56.6666666666667,2|2,2:2|2:2,0:0:0:0: +294,22,126566,6,0,L|272:227,1,170,6|2,3:2|1:2,0:0:0:0: +269,279,126975,2,0,P|216:221|108:227,1,170,2|2,2:2|3:2,0:0:0:0: +128,216,127384,2,0,P|84:282|118:385,1,170,2|2,1:2|3:2,0:0:0:0: +102,367,127930,6,0,L|211:350,1,85,2|2,1:2|3:2,0:0:0:0: +268,375,128202,2,0,B|286:335|286:335|274:283,1,85,2|2,2:2|3:2,0:0:0:0: +220,230,128475,1,2,1:2:0:0: +246,149,128611,1,2,2:2:0:0: +272,67,128748,6,0,P|269:35|242:-9,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +341,119,129021,2,0,P|354:89|353:38,2,56.6666666666667,2|2|2,1:2|2:2|2:2,0:0:0:0: +374,198,129293,2,0,P|400:179|424:134,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +363,283,129566,2,0,P|395:280|439:253,2,56.6666666666667,2|2|2,1:2|2:2|2:2,0:0:0:0: +399,365,129839,1,2,3:2:0:0: +363,336,129930,1,2,2:2:0:0: +319,321,130021,1,2,2:2:0:0: +274,327,130111,1,2,1:2:0:0: +233,348,130202,1,2,2:2:0:0: +188,355,130293,1,2,2:2:0:0: +144,341,130384,2,0,P|120:293|207:221,1,170,2|2,3:2|1:2,0:0:0:0: +282,129,130793,5,0,1:1:0:0: +282,129,130861,1,0,1:1:0:0: +282,129,130930,2,0,B|317:20|317:20|237:48,1,170,6|2,3:2|1:2,0:0:0:0: +264,38,131339,2,0,P|186:59|98:14,1,170,2|2,2:2|3:2,0:0:0:0: +107,24,131748,2,0,P|133:66|130:126,1,85,2|0,1:2|2:2,0:0:0:0: +88,171,132021,6,0,P|62:230|115:333,1,170,2|0,3:2|1:1,0:0:0:0: +100,322,132430,2,0,B|51:323|21:355|21:355|63:331|120:358,1,170,2|0,3:2|3:3,0:0:0:0: +100,350,132839,2,0,P|148:352|184:332,1,85,2|0,1:2|2:2,0:0:0:0: +246,281,133111,6,0,L|332:307,1,85,2|0,3:2|0:0,0:0:0:0: +390,362,133384,1,0,1:1:0:0: +472,339,133521,1,2,2:2:0:0: +491,256,133657,1,2,3:2:0:0: +439,188,133793,1,2,3:2:0:0: +420,104,133930,1,2,1:2:0:0: +461,29,134066,1,2,3:2:0:0: +448,181,134202,5,0,3:3:0:0: +381,127,134339,1,2,3:2:0:0: +296,115,134475,1,0,1:1:0:0: +214,139,134611,1,2,3:2:0:0: +164,208,134748,2,0,P|121:226|70:220,1,85,2|0,2:2|3:3,0:0:0:0: +19,113,135021,2,0,P|61:112|99:129,1,85,2|0,1:2|3:3,0:0:0:0: +25,309,135293,6,0,B|122:323|78:369|209:375,1,170,6|0,3:2|1:1,0:0:0:0: +252,328,135702,1,2,2:2:0:0: +252,328,135839,2,0,L|241:241,1,85,2|2,3:2|3:2,0:0:0:0: +175,190,136111,2,0,L|186:103,1,85,2|2,1:2|2:2,0:0:0:0: +138,34,136384,5,2,3:2:0:0: +194,98,136521,1,2,2:2:0:0: +278,109,136657,1,2,1:2:0:0: +360,89,136793,1,2,3:2:0:0: +407,17,136930,5,2,2:2:0:0: +447,139,137066,1,2,3:2:0:0: +367,239,137202,1,2,1:2:0:0: +407,361,137338,1,2,2:2:0:0: +280,384,137475,5,2,3:2:0:0: +194,371,137611,1,2,2:2:0:0: +207,285,137748,1,2,1:2:0:0: +293,298,137884,1,2,2:2:0:0: +198,273,138021,2,0,P|184:301|47:327,1,170,2|2,3:2|1:2,0:0:0:0: +20,80,138566,5,2,3:2:0:0: +67,49,138657,1,2,2:2:0:0: +122,40,138748,1,2,2:2:0:0: +178,47,138839,1,2,1:2:0:0: +221,83,138930,1,2,2:2:0:0: +244,135,139021,1,2,2:2:0:0: +248,190,139111,2,0,P|240:230|225:257,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +327,154,139384,6,0,L|485:175,1,127.5,8|4,2:3|2:3,0:0:0:0: +489,146,139657,2,0,P|448:57|374:68,1,170,6|2,3:2|1:2,0:0:0:0: +311,20,140066,2,0,P|284:80|187:82,1,170,2|2,2:2|3:2,0:0:0:0: +118,35,140475,2,0,P|72:33|32:60,1,85,2|2,1:2|2:2,0:0:0:0: +13,133,140748,5,2,3:2:0:0: +93,158,140884,1,2,2:2:0:0: +30,216,141021,1,2,1:2:0:0: +91,338,141157,2,0,B|171:350|171:350|180:362|180:362|285:375,1,170,2|2,3:2|3:2,0:0:0:0: +253,371,141566,2,0,B|265:333|265:333|249:279,1,85,2|2,1:2|2:2,0:0:0:0: +302,220,141839,6,0,P|255:180|262:73,1,170,2|2,3:2|1:2,0:0:0:0: +329,31,142248,1,0,0:0:0:0: +401,75,142384,2,0,L|476:57,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +430,153,142657,2,0,L|505:135,2,56.6666666666667,2|2|2,1:2|2:2|2:2,0:0:0:0: +474,226,142930,1,2,3:2:0:0: +433,207,143020,1,2,2:2:0:0: +389,215,143111,1,2,2:2:0:0: +356,246,143202,1,2,1:2:0:0: +347,289,143293,1,2,2:2:0:0: +363,331,143384,1,2,2:2:0:0: +403,353,143475,6,0,L|482:334,1,56.6666666666667,2|2,3:2|2:2,0:0:0:0: +315,310,143657,1,2,2:2:0:0: +303,314,143748,2,0,L|224:333,1,56.6666666666667,2|2,1:2|2:2,0:0:0:0: +152,306,143930,1,2,2:2:0:0: +140,310,144021,6,0,B|90:324|70:373|70:373|26:351|36:287,1,170,6|2,3:2|1:2,0:0:0:0: +34,314,144430,2,0,P|40:249|156:209,1,170,2|2,2:2|3:2,0:0:0:0: +151,40,144839,1,2,1:2:0:0: +151,40,144975,1,2,2:2:0:0: +91,111,145111,6,0,L|0:97,1,85,2|2,3:2|2:2,0:0:0:0: +124,200,145384,2,0,L|215:186,1,85,2|2,1:2|3:2,0:0:0:0: +284,148,145657,1,2,2:2:0:0: +330,77,145793,1,2,3:2:0:0: +412,55,145930,1,2,1:2:0:0: +494,75,146066,1,2,2:2:0:0: +422,196,146202,6,0,B|333:210|378:259|237:279,1,170,2|2,3:2|1:2,0:0:0:0: +273,272,146611,1,2,2:2:0:0: +242,384,146748,2,0,P|204:342|143:323,1,85,2|2,3:2|3:2,0:0:0:0: +33,327,147021,2,0,P|69:305|95:272,1,85,2|2,1:2|3:2,0:0:0:0: +120,188,147293,6,0,L|190:167,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +83,110,147566,2,0,L|-14:91,1,85,2|0,1:2|0:0,0:0:0:0: +175,0,147839,1,2,3:2:0:0: +256,22,147975,1,2,1:2:0:0: +195,80,148111,1,2,1:2:0:0: +300,176,148248,5,0,1:1:0:0: +300,176,148316,1,0,1:1:0:0: +300,176,148384,2,0,B|165:59|28:174|28:174|85:282|220:240|220:240|95:264|150:399|277:387|218:278|354:337,1,815.999975097657,6|0,3:2|3:3,0:0:0:0: +416,358,149611,2,0,P|476:322|492:287,2,85,2|2|2,2:2|1:2|3:2,0:0:0:0: +318,324,150021,1,2,2:2:0:0: +318,324,150157,1,2,3:2:0:0: +395,257,150293,2,0,P|383:208|403:147,1,85,2|2,1:2|2:2,0:0:0:0: +502,55,150566,5,2,3:2:0:0: +388,174,150702,1,2,2:2:0:0: +388,174,150839,1,2,1:2:0:0: +354,23,150975,2,0,B|185:40|253:129|72:146|72:146|193:127|252:221|252:221|114:248|122:369,1,713.99997821045,2|0,2:2|1:1,0:0:0:0: +37,281,152066,2,0,P|24:322|28:375,2,85,2|2|2,3:2|2:2|3:2,0:0:0:0: +73,147,152475,2,0,P|120:193|129:237,1,85,2|2,3:2|3:2,0:0:0:0: +211,372,152748,6,0,P|247:328|376:346,1,170,4|2,3:2|1:2,0:0:0:0: +499,342,153157,2,0,L|323:365,1,170,2|2,2:2|3:2,0:0:0:0: +279,292,153566,2,0,L|300:206,1,85,2|2,1:2|2:2,0:0:0:0: +236,151,153839,5,2,3:2:0:0: +299,209,153975,1,2,2:2:0:0: +375,172,154111,1,2,1:2:0:0: +448,128,154248,2,0,B|479:97|461:40|461:40|346:20|305:110,1,255,2|0,3:2|1:1,0:0:0:0: +41,18,154930,5,2,3:2:0:0: +28,61,155020,1,2,2:2:0:0: +40,104,155111,1,2,2:2:0:0: +72,135,155202,1,2,1:2:0:0: +115,146,155293,1,2,2:2:0:0: +158,134,155384,1,2,2:2:0:0: +198,111,155475,1,2,3:2:0:0: +254,104,155565,1,2,2:2:0:0: +309,117,155656,1,2,2:2:0:0: +356,146,155747,1,2,1:2:0:0: +392,190,155838,1,2,2:2:0:0: +411,243,155929,1,2,2:2:0:0: +411,300,156021,6,0,B|389:376|282:346|282:264|334:228|334:228|440:151|406:51|345:3|259:6|200:62|212:132,1,611.999981323243,2|8,3:2|2:3,0:0:0:0: +213,110,156907,1,8,2:3:0:0: +214,120,156975,1,4,2:3:0:0: +215,130,157043,1,4,2:3:0:0: +216,140,157111,6,0,L|79:122,1,101.999996887207,6|0,3:2|2:2,0:0:0:0: +3,253,157384,2,0,L|105:267,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +124,138,157657,2,0,L|226:152,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +13,265,157930,2,0,L|115:279,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +134,150,158202,2,0,L|236:164,1,101.999996887207,2|0,3:2|2:2,0:0:0:0: +23,277,158475,2,0,L|125:291,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +144,162,158748,2,0,L|246:176,1,101.999996887207,2|0,2:2|3:3,0:0:0:0: +33,289,159021,2,0,L|135:303,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +154,174,159293,2,0,L|256:188,1,101.999996887207,2|0,3:2|2:2,0:0:0:0: +43,301,159566,2,0,L|145:315,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +164,186,159839,2,0,L|266:200,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +53,313,160112,2,0,L|155:327,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +174,198,160384,2,0,L|276:212,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +63,325,160657,2,0,L|165:339,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +184,210,160930,2,0,L|286:224,1,101.999996887207,2|0,2:2|3:3,0:0:0:0: +73,337,161202,2,0,L|175:351,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +300,105,161475,6,0,L|437:87,1,101.999996887207,6|0,3:2|2:2,0:0:0:0: +512,218,161748,2,0,L|410:231,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +391,103,162021,2,0,L|289:116,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +502,230,162294,2,0,L|400:243,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +381,115,162566,2,0,L|279:128,1,101.999996887207,2|0,3:2|2:2,0:0:0:0: +492,242,162839,2,0,L|390:255,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +371,127,163112,2,0,L|269:140,1,101.999996887207,2|0,2:2|3:3,0:0:0:0: +482,254,163385,2,0,L|380:267,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +361,139,163657,2,0,L|259:152,1,101.999996887207,2|0,3:2|2:2,0:0:0:0: +472,266,163930,2,0,L|370:279,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +351,151,164203,2,0,L|249:164,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +462,278,164476,2,0,L|360:291,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +341,163,164748,2,0,L|239:176,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +452,290,165021,2,0,L|350:303,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +331,175,165294,2,0,L|229:188,1,101.999996887207,2|0,2:2|3:3,0:0:0:0: +396,99,165566,1,2,1:2:0:0: +216,86,165702,5,0,1:1:0:0: +216,86,165771,1,0,1:1:0:0: +216,86,165839,2,0,L|234:223,1,101.999996887207,6|0,3:2|2:2,0:0:0:0: +103,299,166112,2,0,L|89:197,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +218,178,166385,2,0,L|204:76,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +91,289,166658,2,0,L|77:187,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +206,168,166930,2,0,L|192:66,1,101.999996887207,2|0,3:2|2:2,0:0:0:0: +79,279,167203,2,0,L|65:177,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +194,158,167476,2,0,L|180:56,1,101.999996887207,2|0,2:2|3:3,0:0:0:0: +67,269,167749,2,0,L|53:167,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +182,148,168021,2,0,L|168:46,1,101.999996887207,2|0,3:2|2:2,0:0:0:0: +55,259,168294,2,0,L|41:157,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +170,138,168567,2,0,L|156:36,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +43,249,168840,2,0,L|29:147,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +158,128,169112,2,0,L|144:26,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +31,239,169385,2,0,L|17:137,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +146,118,169658,2,0,L|132:16,1,101.999996887207,2|0,2:2|3:3,0:0:0:0: +19,229,169930,2,0,L|5:127,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +280,171,170202,6,0,L|262:308,1,101.999996887207,6|0,3:2|2:2,0:0:0:0: +393,384,170475,2,0,L|407:282,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +278,263,170748,2,0,L|292:161,1,101.999996887207,2|0,3:2|3:3,0:0:0:0: +405,374,171021,2,0,L|419:272,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +290,253,171293,2,0,L|304:151,1,101.999996887207,2|0,3:2|2:2,0:0:0:0: +417,364,171566,2,0,L|431:262,1,101.999996887207,2|0,1:2|3:3,0:0:0:0: +302,243,171839,2,0,L|316:141,1,101.999996887207,2|0,2:2|3:3,0:0:0:0: +429,354,172112,2,0,L|443:252,1,101.999996887207,2|0,1:2|2:2,0:0:0:0: +512,181,172384,1,2,3:3:0:0: +512,181,173278,6,0,P|452:146|386:277,1,255,6|0,3:2|0:0,0:0:0:0: +327,334,173722,2,0,L|257:321,5,56.6666666666667,0|0|0|2|2|2,3:3|0:0|0:0|3:2|2:2|2:2,0:0:0:0: +178,230,174166,2,0,L|248:217,5,56.6666666666667,2|2|2|2|2|2,3:2|2:2|2:2|3:2|2:2|2:2,0:0:0:0: +92,334,174611,2,0,L|22:321,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +99,348,174833,2,0,L|29:335,2,56.6666666666667,2|0|0,3:2|0:0|0:0,0:0:0:0: +179,312,175055,6,0,P|188:278|169:215,1,85,2|2,3:2|1:2,0:0:0:0: +84,148,175278,1,2,3:2:0:0: +84,148,175389,1,2,1:2:0:0: +84,148,175500,2,0,L|-17:135,1,85,2|2,3:2|1:2,0:0:0:0: +176,61,175722,2,0,L|277:48,1,85,2|2,3:2|1:2,0:0:0:0: +378,32,175944,1,2,3:2:0:0: +359,97,176055,1,0,0:0:0:0: +380,161,176166,1,2,3:2:0:0: +437,198,176278,1,0,0:0:0:0: +504,198,176389,2,0,P|513:147|489:106,1,85,2|0,3:2|0:0,0:0:0:0: +464,293,176611,2,0,P|415:310|391:351,1,85,2|0,3:2|0:0,0:0:0:0: +223,292,176833,6,0,B|246:357|246:357|352:294|309:142,1,255,2|2,3:2|1:2,0:0:0:0: +314,26,177278,1,2,3:2:0:0: +393,73,177389,1,2,1:2:0:0: +393,73,177500,2,0,L|500:51,1,85,2|2,3:2|1:2,0:0:0:0: +238,144,177722,5,2,3:2:0:0: +238,144,177833,1,2,1:2:0:0: +238,144,177944,2,0,L|131:122,1,85,2|2,3:2|1:2,0:0:0:0: +51,179,178166,2,0,P|53:134|32:88,1,85,2|0,3:2|0:0,0:0:0:0: +136,321,178389,2,0,P|134:279|149:240,1,85,2|0,3:2|0:0,0:0:0:0: +311,365,178611,6,0,L|388:385,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +361,293,178833,2,0,L|437:271,2,56.6666666666667,2|0|0,3:2|0:0|0:0,0:0:0:0: +368,205,179055,2,0,L|423:148,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +330,125,179278,2,0,L|350:47,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +442,29,179500,5,2,3:2:0:0: +442,29,179574,1,2,2:2:0:0: +442,29,179648,1,2,2:2:0:0: +442,29,179722,2,0,L|422:106,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +488,149,179944,2,0,B|406:177|450:214|340:247,1,170,2|2,3:2|1:2,0:0:0:0: +114,91,180389,6,0,P|80:60|39:51,1,85,6|2,3:2|1:2,0:0:0:0: +0,130,180611,2,0,P|30:160|71:171,1,85,2|2,3:2|1:2,0:0:0:0: +124,301,180833,2,0,L|109:392,1,85,2|2,3:2|1:2,0:0:0:0: +201,378,181055,2,0,L|216:287,1,85,2|2,3:2|1:2,0:0:0:0: +350,243,181278,2,0,L|418:301,1,85,2|2,3:2|1:2,0:0:0:0: +497,261,181500,2,0,L|513:173,2,85,2|2|2,3:2|1:2|3:2,0:0:0:0: +414,298,181833,1,2,1:2:0:0: +414,298,181944,2,0,P|365:311|334:341,1,85,2|0,3:2|0:0,0:0:0:0: +254,216,182166,5,2,3:2:0:0: +186,206,182278,1,2,1:2:0:0: +123,233,182389,1,2,3:2:0:0: +89,291,182500,1,2,1:2:0:0: +101,357,182611,2,0,B|135:293|107:231|93:241|46:187|83:107,1,255,2|0,3:2|1:1,0:0:0:0: +0,29,183055,6,0,P|27:53|84:63,1,85,2|0,3:2|0:0,0:0:0:0: +176,171,183278,2,0,P|210:159|247:115,1,85,2|2,3:2|1:2,0:0:0:0: +353,40,183500,2,0,L|364:155,1,85,2|2,3:2|1:2,0:0:0:0: +473,10,183722,2,0,L|462:125,1,85,2|2,3:2|1:2,0:0:0:0: +447,199,183944,5,2,3:2:0:0: +447,199,184055,1,0,0:0:0:0: +447,199,184166,1,2,3:2:0:0: +463,223,184277,1,0,0:0:0:0: +487,237,184388,2,0,L|476:352,1,85,2|2,3:2|1:2,0:0:0:0: +344,381,184611,2,0,L|333:266,1,85,2|2,3:2|1:2,0:0:0:0: +233,174,184833,6,0,P|186:180|144:208,1,85,2|2,3:2|1:2,0:0:0:0: +19,319,185055,2,0,P|56:339|98:343,1,85,2|2,3:2|1:2,0:0:0:0: +224,268,185278,1,2,3:2:0:0: +229,200,185389,1,2,1:2:0:0: +203,136,185500,1,2,3:2:0:0: +148,95,185611,1,0,0:0:0:0: +80,84,185722,2,0,P|45:119|29:167,1,85,2|0,3:2|0:0,0:0:0:0: +227,49,185944,6,0,L|282:-7,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +306,84,186166,2,0,L|382:63,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +358,156,186388,2,0,L|434:176,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +366,244,186611,2,0,L|423:300,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +512,269,186833,5,2,3:2:0:0: +512,269,186907,1,2,2:2:0:0: +512,269,186981,1,2,0:0:0:0: +512,269,187055,2,0,L|455:213,2,56.6666666666667,2|0|0,1:2|0:0|0:0,0:0:0:0: +469,351,187277,2,0,P|423:346|367:392,1,113.333333333333,8|0,2:3|0:0,0:0:0:0: +346,383,187500,6,0,B|296:353|296:353|274:238|376:162,1,255,6|0,3:2|1:1,0:0:0:0: +326,22,187944,1,2,3:2:0:0: +397,68,188055,2,0,P|439:74|505:42,1,85,2|0,1:2|3:3,0:0:0:0: +269,143,188278,1,2,1:2:0:0: +269,143,188389,2,0,P|236:175|218:221,1,85,2|2,3:2|1:2,0:0:0:0: +209,352,188611,6,0,L|109:339,1,85,2|2,3:2|1:2,0:0:0:0: +13,230,188833,2,0,L|113:217,1,85,2|2,3:2|1:2,0:0:0:0: +163,98,189055,2,0,L|63:85,1,85,2|2,3:2|1:2,0:0:0:0: +133,9,189277,6,0,L|217:19,1,85,2|2,3:2|1:2,0:0:0:0: +248,145,189499,2,0,L|288:105,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +309,248,189721,2,0,L|323:194,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +414,304,189944,2,0,L|399:250,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +468,194,190166,6,0,L|488:117,5,56.6666666666667,2|2|2|2|2|2,3:2|2:2|2:2|3:2|2:2|2:2,0:0:0:0: +408,16,190611,2,0,L|423:71,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +399,25,190833,2,0,L|413:79,2,56.6666666666667,2|0|0,3:2|0:0|0:0,0:0:0:0: +311,21,191055,6,0,P|386:53|353:174,1,170,2|2,3:2|3:2,0:0:0:0: +272,212,191389,1,2,1:2:0:0: +272,212,191500,2,0,P|303:227|343:276,1,85,2|2,3:2|1:2,0:0:0:0: +461,327,191722,2,0,P|432:346|370:356,1,85,2|2,3:2|1:2,0:0:0:0: +215,380,191944,1,2,3:2:0:0: +189,357,192055,1,2,1:2:0:0: +157,343,192166,1,2,3:2:0:0: +123,340,192277,1,2,1:2:0:0: +89,347,192389,2,0,P|49:335|11:294,1,85,2|0,3:2|1:1,0:0:0:0: +54,172,192611,2,0,P|44:131|60:77,1,85,2|0,3:2|1:0,0:0:0:0: +208,24,192833,2,0,L|193:115,1,85,2|2,3:2|1:2,0:0:0:0: +275,157,193055,2,0,L|290:66,1,85,2|2,3:2|1:2,0:0:0:0: +415,27,193277,5,2,3:2:0:0: +461,98,193389,1,2,1:2:0:0: +458,182,193500,1,2,3:2:0:0: +413,254,193611,1,2,1:2:0:0: +329,269,193722,2,0,P|286:264|227:290,1,85,2|0,3:2|0:0,0:0:0:0: +377,373,193944,2,0,P|420:378|479:352,1,85,2|0,3:2|0:0,0:0:0:0: +491,288,194166,2,0,B|475:189|434:241|422:89,1,170,2|0,3:2|1:1,0:0:0:0: +51,35,194611,6,0,B|97:71|166:63|166:63|220:147|220:147|287:120|391:189,1,340,6|0,3:2|3:3,0:0:0:0: +165,279,195166,1,2,1:2:0:0: +201,189,195277,2,0,P|241:220|260:277,1,85,2|2,3:2|1:2,0:0:0:0: +47,321,195500,2,0,P|53:270|93:225,1,85,2|2,3:2|1:2,0:0:0:0: +238,346,195722,5,2,3:2:0:0: +320,365,195833,1,2,1:2:0:0: +402,345,195944,1,2,3:2:0:0: +462,285,196055,1,2,1:2:0:0: +484,203,196166,2,0,P|479:158|404:126,1,113.333333333333,2|2,3:2|0:0,0:0:0:0: +354,57,196389,6,0,L|361:0,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +290,124,196611,2,0,L|297:67,3,56.6666666666667,2|2|2|2,3:2|2:2|2:2|3:2,0:0:0:0: +242,209,196907,2,0,L|234:265,1,56.6666666666667,2|2,2:2|2:2,0:0:0:0: +192,279,197055,2,0,L|199:335,2,56.6666666666667,2|2|2,3:2|2:2|2:2,0:0:0:0: +108,239,197277,2,0,L|52:232,5,56.6666666666667,2|2|2|2|2|2,3:2|2:2|2:2|3:2|2:2|2:2,0:0:0:0: +0,305,197722,2,0,P|65:299|94:417,1,170,2|2,3:2|3:2,0:0:0:0: +391,327,198166,6,0,L|461:316,5,56.6666666666667,2|2|2|2|2|2,3:2|0:2|0:2|3:2|0:2|0:2,0:0:0:0: +317,265,198611,1,2,3:2:0:0: +317,265,198685,1,2,0:2:0:0: +317,265,198759,1,2,0:2:0:0: +317,265,198833,2,0,L|247:254,2,56.6666666666667,2|2|0,3:2|0:0|0:0,0:0:0:0: +392,180,199055,2,0,L|403:110,5,56.6666666666667,2|2|2|2|0|0,3:2|0:2|0:2|3:2|0:0|0:0,0:0:0:0: +494,85,199500,2,0,L|483:15,5,56.6666666666667,2|2|2|2|0|0,3:2|0:2|0:2|3:2|0:0|0:0,0:0:0:0: +400,124,199944,6,0,L|330:113,5,56.6666666666667,2|2|2|2|2|2,3:2|0:2|0:2|3:2|0:2|0:2,0:0:0:0: +267,59,200389,1,2,3:2:0:0: +267,59,200463,1,2,0:2:0:0: +267,59,200537,1,2,0:2:0:0: +267,59,200611,2,0,L|197:70,2,56.6666666666667,2|2|0,3:2|0:0|0:0,0:0:0:0: +121,115,200833,2,0,L|110:45,5,56.6666666666667,2|2|2|2|2|2,3:2|0:2|0:2|3:2|0:2|0:2,0:0:0:0: +179,202,201277,2,0,L|168:272,2,56.6666666666667,2|0|0,1:2|0:0|0:0,0:0:0:0: +67,245,201500,2,0,L|78:315,2,56.6666666666667,8|0|0,2:3|0:0|0:0,0:0:0:0: +11,377,201722,5,4,3:2:0:0: +256,192,201776,12,4,205276,3:2:0:0: +171,17,207943,6,0,L|178:69,31,34,0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0|0,3:3|0:0|0:0|0:0|0:0|3:3|0:0|0:0|0:0|0:0|3:3|0:0|0:0|0:0|0:0|3:3|0:0|0:0|0:0|0:0|3:3|0:0|0:0|0:0|0:0|3:3|0:0|0:0|0:0|0:0|3:3|0:0,0:0:0:0: +85,45,210124,5,8,3:3:0:0: +73,234,210329,1,4,3:3:0:0: +243,150,210533,1,8,3:3:0:0: +122,74,210670,5,8,3:3:0:0: +61,252,210875,1,4,3:3:0:0: +246,215,211079,1,8,3:3:0:0: +294,296,211215,6,0,L|239:313,3,42.5,14|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +369,234,211488,2,0,L|410:247,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +319,156,211761,2,0,L|307:116,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +221,73,212033,2,0,L|209:114,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +121,141,212306,5,10,3:2:0:0: +112,138,212374,1,0,3:3:0:0: +103,135,212442,1,0,3:3:0:0: +78,40,212579,1,10,3:2:0:0: +87,37,212647,1,0,3:3:0:0: +96,34,212715,1,0,3:3:0:0: +0,115,212851,2,0,L|13:156,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +77,232,213124,2,0,L|65:273,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +131,350,213397,6,0,L|172:338,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +261,301,213670,2,0,L|316:318,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +366,247,213942,2,0,L|354:207,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +456,272,214215,2,0,L|444:312,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +490,185,214488,5,10,3:2:0:0: +487,176,214556,1,0,3:3:0:0: +484,167,214624,1,0,3:3:0:0: +419,92,214761,1,10,3:2:0:0: +422,83,214829,1,0,3:3:0:0: +425,74,214897,1,0,3:3:0:0: +344,17,215033,1,10,3:2:0:0: +336,19,215101,1,0,3:3:0:0: +328,21,215170,2,0,L|224:9,1,85,0|10,3:3|3:2,0:0:0:0: +238,216,215579,6,0,L|250:256,3,42.5,14|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +182,318,215852,2,0,L|170:358,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +90,275,216124,2,0,L|49:263,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +51,166,216397,2,0,L|-4:183,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +64,70,216670,5,10,3:2:0:0: +73,73,216738,1,0,3:3:0:0: +82,76,216806,1,0,3:3:0:0: +191,122,216942,1,10,3:2:0:0: +200,119,217010,1,0,3:3:0:0: +209,116,217078,1,0,3:3:0:0: +243,18,217215,2,0,L|298:35,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +350,113,217488,2,0,L|309:125,2,42.5,10|0|0,3:2|3:3|3:3,0:0:0:0: +425,177,217761,6,0,L|413:217,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +481,279,218034,2,0,L|493:319,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +411,375,218306,2,0,L|370:363,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +328,276,218579,2,0,L|273:293,3,42.5,10|0|0|0,3:2|3:3|3:3|3:3,0:0:0:0: +208,353,218851,5,10,3:2:0:0: +205,362,218919,1,0,3:3:0:0: +202,371,218987,1,0,3:3:0:0: +120,294,219124,1,10,3:2:0:0: +117,285,219192,1,0,3:3:0:0: +114,276,219260,1,0,3:3:0:0: +44,203,219397,2,0,L|55:145,7,42.5,10|0|0|0|0|0|0|0,3:2|3:3|3:3|3:3|3:3|3:3|3:3|3:3,0:0:0:0: +142,171,219943,5,0,1:1:0:0: +146,181,220011,1,0,1:1:0:0: +151,190,220079,2,0,L|202:199,3,42.5,0|0|0|0,1:1|1:1|1:1|1:1,0:0:0:0: +269,153,220352,2,0,L|320:162,1,42.5,0|0,1:1|1:1,0:0:0:0: +320,248,220488,2,0,L|371:257,8,42.5,0|0|0|0|0|0|0|0|0,1:1|1:1|1:1|1:1|1:1|1:1|1:1|1:1|0:0,0:0:0:0: +364,28,222670,6,0,L|424:7,7,42.5,4|8|8|8|4|4|4|4,1:2|2:3|2:3|2:3|2:3|2:3|2:3|2:3,0:0:0:0: +487,58,223215,2,0,L|470:149,1,85,6|2,3:2|1:2,0:0:0:0: +437,312,223488,2,0,L|420:221,1,85,0|2,3:2|1:2,0:0:0:0: +314,245,223761,1,2,3:2:0:0: +240,320,223897,1,2,1:2:0:0: +240,320,223965,1,2,3:2:0:0: +240,320,224033,2,0,L|149:337,1,85,2|2,3:2|1:2,0:0:0:0: +37,266,224306,5,2,3:2:0:0: +37,266,224443,1,2,1:2:0:0: +142,352,224579,2,0,L|225:336,1,85,2|2,3:2|1:2,0:0:0:0: +304,206,224852,2,0,L|288:123,1,85,2|0,3:2|1:1,0:0:0:0: +164,41,225124,2,0,L|172:0,3,42.5,0|0|0|0,3:3|3:3|1:1|3:3,0:0:0:0: +84,68,225397,6,0,P|125:92|149:148,1,85,2|0,3:2|1:1,0:0:0:0: +86,190,225670,2,0,P|45:166|21:110,1,85,2|0,3:2|1:1,0:0:0:0: +39,266,225943,2,0,L|48:358,1,85,2|0,3:2|1:1,0:0:0:0: +137,365,226215,2,0,L|128:273,1,85,2|0,3:2|1:1,0:0:0:0: +237,209,226488,6,0,L|329:218,1,85,2|0,3:2|1:1,0:0:0:0: +361,127,226761,1,2,3:2:0:0: +361,127,226897,1,2,1:2:0:0: +488,185,227033,2,0,L|479:277,1,85,2|0,3:2|1:1,0:0:0:0: +429,362,227306,2,0,L|438:270,1,85,2|0,3:2|1:1,0:0:0:0: +361,127,227579,6,0,P|344:82|354:27,1,85,6|2,3:2|3:2,0:0:0:0: +195,127,227852,2,0,P|196:169|180:208,1,85,0|2,3:3|3:2,0:0:0:0: +211,346,228124,1,2,3:2:0:0: +131,297,228261,1,2,3:2:0:0: +131,297,228329,1,2,3:2:0:0: +131,297,228397,2,0,L|32:288,1,85,2|2,3:2|3:2,0:0:0:0: +67,158,228670,5,8,2:3:0:0: +59,126,228738,1,8,2:3:0:0: +63,92,228806,1,8,2:3:0:0: +79,62,228874,1,8,2:3:0:0: +104,40,228942,1,4,2:3:0:0: +210,91,229079,1,4,2:3:0:0: +224,95,229147,1,4,2:3:0:0: +238,99,229215,2,0,L|186:122,2,42.5,8|8|8,2:3|2:3|2:3,0:0:0:0: +353,24,229488,2,0,L|336:63,1,42.5,4|4,2:3|2:3,0:0:0:0: +425,66,229624,2,0,L|408:105,1,42.5,4|4,2:3|2:3,0:0:0:0: +495,111,229760,5,6,3:2:0:0: +221,375,231943,1,6,3:2:0:0: +102,54,233579,2,0,P|53:62|29:78,5,67.9999979248048,2|2|2|10|10|6,1:2|1:2|1:2|2:3|2:3|2:3,0:0:0:0: +93,147,234124,6,0,P|86:180|62:209,2,67.9999979248048,6|2|2,3:2|2:2|2:2,0:0:0:0: +185,174,234397,2,0,P|196:207|190:244,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +257,115,234670,2,0,P|250:82|226:53,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +349,88,234943,2,0,P|360:56|354:19,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +431,140,235215,5,2,3:2:0:0: +439,130,235306,2,0,L|516:114,1,67.9999979248048,2|2,2:2|2:2,0:0:0:0: +502,215,235488,1,10,2:2:0:0: +460,250,235579,1,2,2:2:0:0: +406,252,235670,1,2,2:2:0:0: +358,230,235760,2,0,P|289:219|204:322,1,203.999993774414,2|10,3:2|2:2,0:0:0:0: +204,309,236306,6,0,L|292:317,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +161,221,236579,2,0,L|249:206,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +77,165,236852,2,0,L|-11:173,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +120,77,237125,2,0,L|32:62,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +194,12,237397,5,2,3:2:0:0: +203,22,237488,2,0,P|218:61|208:109,1,67.9999979248048,2|2,2:2|2:2,0:0:0:0: +296,151,237670,1,10,2:2:0:0: +349,144,237760,1,2,2:2:0:0: +391,109,237851,1,2,2:2:0:0: +400,55,237942,2,0,P|349:167|431:250,1,203.999993774414,10|2,3:2|1:2,0:0:0:0: +385,228,238488,6,0,P|371:267|378:322,2,67.9999979248048,6|2|2,3:2|2:2|2:2,0:0:0:0: +276,298,238761,2,0,P|283:339|317:382,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +188,248,239033,2,0,P|196:206|229:162,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +129,131,239306,2,0,P|156:98|207:77,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +38,119,239579,5,2,3:2:0:0: +32,135,239670,2,0,P|35:162|86:218,1,67.9999979248048,2|2,2:2|2:2,0:0:0:0: +20,291,239851,1,10,2:2:0:0: +57,251,239942,1,2,2:2:0:0: +108,235,240033,1,2,2:2:0:0: +161,244,240124,2,0,B|269:295|276:214|401:275,1,203.999993774414,2|10,3:2|2:2,0:0:0:0: +360,258,240670,6,0,L|297:281,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +460,308,240942,2,0,L|405:347,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +448,213,241215,2,0,L|511:190,2,67.9999979248048,2|2|2,3:2|2:2|2:2,0:0:0:0: +430,114,241488,2,0,L|484:75,2,67.9999979248048,10|2|2,2:2|2:2|2:2,0:0:0:0: +365,38,241760,5,2,3:2:0:0: +354,51,241852,2,0,P|332:92|337:142,1,67.9999979248048,2|2,2:2|2:2,0:0:0:0: +244,165,242033,1,2,1:2:0:0: +191,156,242124,1,2,1:2:0:0: +145,129,242215,1,2,1:2:0:0: +91,133,242306,2,0,B|109:32|109:32|82:-34,2,135.99999584961,6|0|0,1:2|0:0|0:0,0:0:0:0: +33,221,242852,6,0,L|42:273,3,42.5,6|0|8|0,3:2|3:3|3:2|0:0,0:0:0:0: +134,256,243125,2,0,L|125:308,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +228,299,243397,6,0,L|269:291,1,42.5,2|0,3:2|3:3,0:0:0:0: +251,210,243534,2,0,L|292:202,1,42.5,8|0,3:2|0:0,0:0:0:0: +276,120,243671,2,0,L|317:112,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +388,48,243943,6,0,L|379:-4,2,42.5,2|0|8,3:2|3:3|3:2,0:0:0:0: +409,139,244216,1,2,3:2:0:0: +407,147,244284,1,0,3:3:0:0: +405,155,244352,1,8,3:2:0:0: +495,191,244489,2,0,L|504:139,2,42.5,2|0|8,3:2|3:3|3:2,0:0:0:0: +426,254,244762,1,2,3:2:0:0: +428,262,244830,1,0,3:3:0:0: +430,270,244898,1,8,3:2:0:0: +370,354,245034,6,0,L|318:363,3,42.5,2|0|8|0,3:2|3:3|3:2|0:0,0:0:0:0: +331,257,245307,2,0,L|279:248,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +229,187,245579,6,0,L|236:145,1,42.5,2|0,3:2|3:3,0:0:0:0: +140,210,245716,2,0,L|147:168,1,42.5,8|0,3:2|0:0,0:0:0:0: +50,235,245853,2,0,L|57:193,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +120,299,246124,5,2,3:2:0:0: +122,306,246193,1,0,3:3:0:0: +124,315,246261,1,8,3:2:0:0: +171,218,246397,1,2,3:2:0:0: +173,211,246465,1,0,3:3:0:0: +175,202,246533,1,8,3:2:0:0: +123,119,246670,1,2,3:2:0:0: +125,111,246738,1,0,3:3:0:0: +127,103,246806,2,0,L|116:-1,1,85,8|2,3:2|3:2,0:0:0:0: +289,8,247215,6,0,L|341:17,3,42.5,6|0|8|0,3:2|3:3|3:2|0:0,0:0:0:0: +306,118,247488,2,0,L|358:109,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +440,82,247761,6,0,L|449:134,1,42.5,2|0,3:2|3:3,0:0:0:0: +425,168,247897,2,0,L|434:220,1,42.5,8|0,3:2|0:0,0:0:0:0: +410,254,248033,2,0,L|419:306,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +346,361,248306,6,0,L|294:352,2,42.5,2|0|8,3:2|3:3|3:2,0:0:0:0: +287,258,248579,1,2,3:2:0:0: +279,260,248647,1,0,3:3:0:0: +271,262,248715,1,8,3:2:0:0: +193,320,248852,2,0,L|141:329,2,42.5,2|0|8,3:2|3:3|3:2,0:0:0:0: +139,231,249124,1,2,3:2:0:0: +131,229,249194,1,0,3:3:0:0: +123,227,249261,1,8,3:2:0:0: +53,294,249397,6,0,L|62:346,3,42.5,2|0|8|0,3:2|3:3|3:2|0:0,0:0:0:0: +0,214,249670,2,0,L|8:172,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +41,78,249943,6,0,L|-11:87,1,42.5,2|0,3:2|3:3,0:0:0:0: +127,44,250079,2,0,L|75:53,1,42.5,8|0,3:2|0:0,0:0:0:0: +212,12,250215,2,0,L|160:21,3,42.5,2|0|8|0,3:2|0:0|3:2|0:0,0:0:0:0: +210,113,250488,5,2,3:2:0:0: +212,120,250556,1,0,3:3:0:0: +214,129,250624,1,8,3:2:0:0: +295,186,250761,1,2,3:2:0:0: +293,193,250829,1,0,3:3:0:0: +291,202,250898,1,8,3:2:0:0: +235,284,251033,1,2,3:2:0:0: +237,292,251102,1,0,3:3:0:0: +239,300,251170,2,0,L|229:359,5,42.5,8|0|2|0|8|0,3:2|3:3|3:2|3:3|3:2|3:3,0:0:0:0: +229,205,251579,6,0,P|289:218|332:276,1,101.999996887207,6|0,3:2|1:1,0:0:0:0: +475,279,251852,2,0,P|436:312|386:319,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +440,188,252124,2,0,L|465:84,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +297,1,252397,2,0,L|320:101,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +205,178,252670,6,0,L|105:155,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +42,63,252942,1,2,3:2:0:0: +42,63,253079,1,2,1:2:0:0: +1,237,253215,2,0,P|81:257|129:233,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +248,325,253488,2,0,L|148:348,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +408,308,253760,6,0,P|468:334|493:381,1,101.999996887207,6|2,3:2|3:2,0:0:0:0: +318,250,254033,2,0,P|300:202|310:153,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +202,8,254306,1,2,3:2:0:0: +295,60,254442,1,2,3:2:0:0: +295,60,254510,1,2,3:2:0:0: +295,60,254579,2,0,L|430:40,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +486,147,254851,5,2,3:2:0:0: +423,210,254987,1,2,3:2:0:0: +424,300,255124,1,2,3:2:0:0: +487,363,255260,1,2,3:2:0:0: +412,309,255397,2,0,B|317:325|317:325|302:339|302:339|180:354,1,203.999993774414,2|8,3:2|2:3,0:0:0:0: +80,349,255806,5,4,2:3:0:0: +87,359,255874,1,4,2:3:0:0: +94,369,255942,2,0,L|120:251,1,101.999996887207,6|0,3:2|1:1,0:0:0:0: +14,99,256215,2,0,L|40:217,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +172,177,256488,2,0,P|222:174|263:145,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +238,37,256760,2,0,P|188:39|147:68,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +115,269,257033,6,0,P|164:276|205:307,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +342,384,257306,1,2,3:2:0:0: +342,384,257442,1,2,1:2:0:0: +455,305,257579,2,0,L|469:193,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +381,25,257851,2,0,L|395:137,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +267,206,258124,6,0,P|210:189|175:137,1,101.999996887207,6|2,3:2|3:2,0:0:0:0: +95,26,258397,2,0,P|38:43|3:95,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +101,216,258670,1,2,3:2:0:0: +22,284,258806,1,2,3:2:0:0: +22,284,258874,1,2,3:2:0:0: +22,284,258942,2,0,L|3:401,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +158,357,259215,5,8,2:3:0:0: +197,374,259283,1,8,2:3:0:0: +239,370,259351,1,8,2:3:0:0: +273,346,259419,1,8,2:3:0:0: +291,309,259487,1,4,2:3:0:0: +405,309,259624,1,4,2:3:0:0: +415,315,259692,1,4,2:3:0:0: +425,321,259761,2,0,L|443:386,2,42.5,8|8|8,2:3|2:3|2:3,0:0:0:0: +355,215,260033,2,0,L|373:150,3,42.5,4|4|4|4,2:3|2:3|2:3|2:3,0:0:0:0: +376,74,260306,6,0,P|316:87|273:145,1,101.999996887207,6|0,3:2|1:1,0:0:0:0: +112,21,260578,2,0,P|151:54|201:61,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +240,204,260851,2,0,L|136:229,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +1,306,261124,2,0,L|101:329,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +296,380,261397,6,0,L|196:357,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +324,269,261669,1,2,3:2:0:0: +324,269,261806,1,2,1:2:0:0: +445,346,261942,2,0,P|465:266|441:218,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +360,112,262215,2,0,P|410:107|456:128,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +274,175,262487,6,0,P|213:148|188:101,1,101.999996887207,6|2,3:2|3:2,0:0:0:0: +38,82,262760,2,0,P|91:43|144:45,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +194,119,263033,1,2,3:2:0:0: +312,17,263169,1,2,3:2:0:0: +312,17,263237,1,2,3:2:0:0: +312,17,263306,2,0,L|447:37,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +503,159,263578,5,2,3:2:0:0: +456,234,263714,1,2,3:2:0:0: +367,254,263851,1,2,3:2:0:0: +292,207,263987,1,2,3:2:0:0: +206,230,264124,2,0,B|88:237|134:298|-8:302,1,203.999993774414,2|8,3:2|2:3,0:0:0:0: +173,364,264533,5,4,2:3:0:0: +166,375,264601,1,4,2:3:0:0: +159,384,264669,2,0,L|133:266,1,101.999996887207,6|0,3:2|1:1,0:0:0:0: +302,214,264942,2,0,L|281:313,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +399,384,265215,2,0,P|430:344|432:285,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +496,158,265487,2,0,P|455:187|404:189,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +362,12,265760,6,0,P|411:19|452:50,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +288,107,266033,1,2,3:2:0:0: +288,107,266169,1,2,1:2:0:0: +171,18,266306,2,0,L|157:130,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +251,304,266578,2,0,L|237:192,1,101.999996887207,2|0,3:2|1:1,0:0:0:0: +56,123,266851,6,0,P|68:171|104:206,1,101.999996887207,6|2,3:2|3:2,0:0:0:0: +35,378,267124,2,0,P|21:320|48:263,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +123,331,267397,1,2,3:2:0:0: +253,263,267533,1,2,3:2:0:0: +253,263,267601,1,2,3:2:0:0: +253,263,267669,2,0,L|370:282,1,101.999996887207,2|2,3:2|3:2,0:0:0:0: +463,369,267942,5,8,2:3:0:0: +489,336,268010,1,8,2:3:0:0: +498,295,268078,1,8,2:3:0:0: +485,256,268146,1,8,2:3:0:0: +455,228,268214,1,4,2:3:0:0: +419,94,268352,1,4,2:3:0:0: +403,133,268420,1,4,2:3:0:0: +372,161,268488,1,8,2:3:0:0: +332,169,268556,1,8,2:3:0:0: +292,157,268624,1,8,2:3:0:0: +231,79,268761,2,0,L|176:72,3,50.9999984436036,4|4|4|4,2:3|2:3|2:3|2:3,0:0:0:0: +96,25,269033,6,0,P|145:65|95:296,1,297.5,6|0,3:2|0:0,0:0:0:0: +121,370,270097,2,0,P|70:261|250:314,1,382.500014591218,6|0,3:2|3:3,0:0:0:0: +319,356,271028,1,0,3:3:0:0: +312,347,271161,6,0,P|281:282|332:105,1,255.000009727478,6|0,1:2|3:3,0:0:0:0: +400,56,271959,1,0,3:3:0:0: +400,56,272225,2,0,L|411:-18,2,56.6666666666667,0|0|0,1:1|1:1|1:1,0:0:0:0: +442,224,272758,2,0,L|453:150,2,56.6666666666667,0|0|0,1:1|1:1|1:1,0:0:0:0: +512,288,273290,6,0,P|443:291|403:383,1,170,6|2,3:2|3:2,0:0:0:0: +303,339,274048,2,0,L|294:254,1,56.6666666666667,0|0,3:3|3:3,0:0:0:0: +202,300,274498,6,0,L|184:260,2,28.3333333333333,0|0|0,1:1|1:1|1:1,0:0:0:0: +105,278,274873,2,0,L|109:235,2,28.3333333333333,8|8|8,2:3|2:3|2:3,0:0:0:0: +31,211,275273,2,0,L|56:176,2,28.3333333333333,4|4|4,2:3|2:3|2:3,0:0:0:0: +0,115,275734,2,0,L|39:97,2,28.3333333333333,4|0|0,2:3|3:3|3:3,0:0:0:0: +21,17,276254,5,6,3:2:0:0: +256,192,276419,12,4,286062,2:3:0:0: +80,113,286725,6,0,B|137:185|228:143|230:143|231:143|330:119|372:183|321:239|260:239|196:214|196:214|299:265|347:186|469:261,1,680,6|4,3:2|3:2,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3689906-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3689906-expected-conversion.json new file mode 100644 index 0000000000..31743d99ac --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3689906-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":390.0,"Objects":[{"StartTime":390.0,"Position":124.0,"HyperDash":false},{"StartTime":480.0,"Position":109.0,"HyperDash":false},{"StartTime":571.0,"Position":124.0,"HyperDash":false},{"StartTime":644.0,"Position":121.0,"HyperDash":false},{"StartTime":753.0,"Position":124.0,"HyperDash":false}]},{"StartTime":935.0,"Objects":[{"StartTime":935.0,"Position":208.0,"HyperDash":false}]},{"StartTime":1117.0,"Objects":[{"StartTime":1117.0,"Position":380.0,"HyperDash":false},{"StartTime":1207.0,"Position":395.0,"HyperDash":false},{"StartTime":1298.0,"Position":380.0,"HyperDash":false},{"StartTime":1371.0,"Position":381.0,"HyperDash":false},{"StartTime":1480.0,"Position":380.0,"HyperDash":false}]},{"StartTime":1844.0,"Objects":[{"StartTime":1844.0,"Position":208.0,"HyperDash":false}]},{"StartTime":2208.0,"Objects":[{"StartTime":2208.0,"Position":360.0,"HyperDash":false}]},{"StartTime":2390.0,"Objects":[{"StartTime":2390.0,"Position":188.0,"HyperDash":false}]},{"StartTime":2480.0,"Objects":[{"StartTime":2480.0,"Position":152.0,"HyperDash":false}]},{"StartTime":2571.0,"Objects":[{"StartTime":2571.0,"Position":112.0,"HyperDash":false},{"StartTime":2643.0,"Position":111.0,"HyperDash":false},{"StartTime":2752.0,"Position":112.0,"HyperDash":false}]},{"StartTime":2935.0,"Objects":[{"StartTime":2935.0,"Position":196.0,"HyperDash":false}]},{"StartTime":3117.0,"Objects":[{"StartTime":3117.0,"Position":280.0,"HyperDash":false}]},{"StartTime":3299.0,"Objects":[{"StartTime":3299.0,"Position":196.0,"HyperDash":false}]},{"StartTime":3480.0,"Objects":[{"StartTime":3480.0,"Position":288.0,"HyperDash":false},{"StartTime":3570.0,"Position":273.0,"HyperDash":false},{"StartTime":3661.0,"Position":288.0,"HyperDash":false},{"StartTime":3734.0,"Position":276.0,"HyperDash":false},{"StartTime":3843.0,"Position":288.0,"HyperDash":false}]},{"StartTime":4026.0,"Objects":[{"StartTime":4026.0,"Position":116.0,"HyperDash":false}]},{"StartTime":4390.0,"Objects":[{"StartTime":4390.0,"Position":300.0,"HyperDash":false}]},{"StartTime":4753.0,"Objects":[{"StartTime":4753.0,"Position":28.0,"HyperDash":false},{"StartTime":4825.0,"Position":24.0,"HyperDash":false},{"StartTime":4934.0,"Position":28.0,"HyperDash":false}]},{"StartTime":5117.0,"Objects":[{"StartTime":5117.0,"Position":112.0,"HyperDash":false}]},{"StartTime":5299.0,"Objects":[{"StartTime":5299.0,"Position":20.0,"HyperDash":false}]},{"StartTime":5480.0,"Objects":[{"StartTime":5480.0,"Position":192.0,"HyperDash":false},{"StartTime":5570.0,"Position":248.148758,"HyperDash":false},{"StartTime":5661.0,"Position":277.0,"HyperDash":false},{"StartTime":5734.0,"Position":247.046844,"HyperDash":false},{"StartTime":5843.0,"Position":192.0,"HyperDash":false}]},{"StartTime":6208.0,"Objects":[{"StartTime":6208.0,"Position":484.0,"HyperDash":false},{"StartTime":6298.0,"Position":475.0,"HyperDash":false},{"StartTime":6389.0,"Position":484.0,"HyperDash":false},{"StartTime":6462.0,"Position":465.0,"HyperDash":false},{"StartTime":6571.0,"Position":484.0,"HyperDash":false}]},{"StartTime":6753.0,"Objects":[{"StartTime":6753.0,"Position":400.0,"HyperDash":false}]},{"StartTime":6935.0,"Objects":[{"StartTime":6935.0,"Position":228.0,"HyperDash":false},{"StartTime":7025.0,"Position":219.0,"HyperDash":false},{"StartTime":7116.0,"Position":228.0,"HyperDash":false},{"StartTime":7189.0,"Position":245.0,"HyperDash":false},{"StartTime":7298.0,"Position":228.0,"HyperDash":false}]},{"StartTime":7662.0,"Objects":[{"StartTime":7662.0,"Position":396.0,"HyperDash":false}]},{"StartTime":8026.0,"Objects":[{"StartTime":8026.0,"Position":244.0,"HyperDash":false}]},{"StartTime":8208.0,"Objects":[{"StartTime":8208.0,"Position":416.0,"HyperDash":false}]},{"StartTime":8298.0,"Objects":[{"StartTime":8298.0,"Position":452.0,"HyperDash":false}]},{"StartTime":8389.0,"Objects":[{"StartTime":8389.0,"Position":492.0,"HyperDash":false},{"StartTime":8461.0,"Position":505.0,"HyperDash":false},{"StartTime":8570.0,"Position":492.0,"HyperDash":false}]},{"StartTime":8753.0,"Objects":[{"StartTime":8753.0,"Position":396.0,"HyperDash":false}]},{"StartTime":8935.0,"Objects":[{"StartTime":8935.0,"Position":304.0,"HyperDash":false}]},{"StartTime":9117.0,"Objects":[{"StartTime":9117.0,"Position":212.0,"HyperDash":false}]},{"StartTime":9298.0,"Objects":[{"StartTime":9298.0,"Position":312.0,"HyperDash":false},{"StartTime":9388.0,"Position":304.0,"HyperDash":false},{"StartTime":9479.0,"Position":312.0,"HyperDash":false},{"StartTime":9552.0,"Position":325.0,"HyperDash":false},{"StartTime":9661.0,"Position":312.0,"HyperDash":false}]},{"StartTime":9844.0,"Objects":[{"StartTime":9844.0,"Position":140.0,"HyperDash":false}]},{"StartTime":10208.0,"Objects":[{"StartTime":10208.0,"Position":324.0,"HyperDash":false}]},{"StartTime":10571.0,"Objects":[{"StartTime":10571.0,"Position":136.0,"HyperDash":false},{"StartTime":10643.0,"Position":164.812149,"HyperDash":false},{"StartTime":10752.0,"Position":221.0,"HyperDash":false}]},{"StartTime":10935.0,"Objects":[{"StartTime":10935.0,"Position":128.0,"HyperDash":false},{"StartTime":11007.0,"Position":165.812149,"HyperDash":false},{"StartTime":11116.0,"Position":213.0,"HyperDash":false}]},{"StartTime":11299.0,"Objects":[{"StartTime":11299.0,"Position":384.0,"HyperDash":false}]},{"StartTime":11480.0,"Objects":[{"StartTime":11480.0,"Position":292.0,"HyperDash":false}]},{"StartTime":11662.0,"Objects":[{"StartTime":11662.0,"Position":200.0,"HyperDash":false}]},{"StartTime":12026.0,"Objects":[{"StartTime":12026.0,"Position":488.0,"HyperDash":false},{"StartTime":12116.0,"Position":473.0,"HyperDash":false},{"StartTime":12207.0,"Position":487.234161,"HyperDash":false},{"StartTime":12280.0,"Position":452.046844,"HyperDash":false},{"StartTime":12389.0,"Position":402.0,"HyperDash":false}]},{"StartTime":12571.0,"Objects":[{"StartTime":12571.0,"Position":316.0,"HyperDash":false}]},{"StartTime":12753.0,"Objects":[{"StartTime":12753.0,"Position":144.0,"HyperDash":false},{"StartTime":12843.0,"Position":158.0,"HyperDash":false},{"StartTime":12934.0,"Position":144.0,"HyperDash":false},{"StartTime":13007.0,"Position":125.0,"HyperDash":false},{"StartTime":13116.0,"Position":144.0,"HyperDash":false}]},{"StartTime":13480.0,"Objects":[{"StartTime":13480.0,"Position":314.0,"HyperDash":false},{"StartTime":13570.0,"Position":255.851257,"HyperDash":false},{"StartTime":13661.0,"Position":229.234161,"HyperDash":false},{"StartTime":13734.0,"Position":212.046844,"HyperDash":false},{"StartTime":13843.0,"Position":144.0,"HyperDash":false}]},{"StartTime":14026.0,"Objects":[{"StartTime":14026.0,"Position":144.0,"HyperDash":false}]},{"StartTime":14208.0,"Objects":[{"StartTime":14208.0,"Position":314.0,"HyperDash":false},{"StartTime":14280.0,"Position":346.812164,"HyperDash":false},{"StartTime":14389.0,"Position":399.0,"HyperDash":false}]},{"StartTime":14571.0,"Objects":[{"StartTime":14571.0,"Position":304.0,"HyperDash":false},{"StartTime":14643.0,"Position":297.0,"HyperDash":false},{"StartTime":14752.0,"Position":304.0,"HyperDash":false}]},{"StartTime":14935.0,"Objects":[{"StartTime":14935.0,"Position":132.0,"HyperDash":false},{"StartTime":15025.0,"Position":88.85124,"HyperDash":false},{"StartTime":15116.0,"Position":48.0,"HyperDash":false},{"StartTime":15189.0,"Position":42.0,"HyperDash":false},{"StartTime":15298.0,"Position":48.0,"HyperDash":false}]},{"StartTime":15480.0,"Objects":[{"StartTime":15480.0,"Position":132.0,"HyperDash":false}]},{"StartTime":15662.0,"Objects":[{"StartTime":15662.0,"Position":304.0,"HyperDash":false}]},{"StartTime":16026.0,"Objects":[{"StartTime":16026.0,"Position":132.0,"HyperDash":false}]},{"StartTime":16390.0,"Objects":[{"StartTime":16390.0,"Position":284.0,"HyperDash":false},{"StartTime":16462.0,"Position":289.0,"HyperDash":false},{"StartTime":16571.0,"Position":284.0,"HyperDash":false}]},{"StartTime":16753.0,"Objects":[{"StartTime":16753.0,"Position":192.0,"HyperDash":false}]},{"StartTime":16935.0,"Objects":[{"StartTime":16935.0,"Position":192.0,"HyperDash":false}]},{"StartTime":17117.0,"Objects":[{"StartTime":17117.0,"Position":364.0,"HyperDash":false},{"StartTime":17207.0,"Position":419.148743,"HyperDash":false},{"StartTime":17298.0,"Position":449.0,"HyperDash":false},{"StartTime":17371.0,"Position":432.046844,"HyperDash":false},{"StartTime":17480.0,"Position":364.0,"HyperDash":false}]},{"StartTime":17844.0,"Objects":[{"StartTime":17844.0,"Position":64.0,"HyperDash":false},{"StartTime":17916.0,"Position":81.0,"HyperDash":false},{"StartTime":18025.0,"Position":64.0,"HyperDash":false}]},{"StartTime":18208.0,"Objects":[{"StartTime":18208.0,"Position":148.0,"HyperDash":false},{"StartTime":18280.0,"Position":163.0,"HyperDash":false},{"StartTime":18389.0,"Position":148.0,"HyperDash":false}]},{"StartTime":18571.0,"Objects":[{"StartTime":18571.0,"Position":320.0,"HyperDash":false}]},{"StartTime":18935.0,"Objects":[{"StartTime":18935.0,"Position":132.0,"HyperDash":false}]},{"StartTime":19299.0,"Objects":[{"StartTime":19299.0,"Position":132.0,"HyperDash":false},{"StartTime":19389.0,"Position":191.148758,"HyperDash":false},{"StartTime":19480.0,"Position":216.765839,"HyperDash":false},{"StartTime":19553.0,"Position":233.953156,"HyperDash":false},{"StartTime":19662.0,"Position":302.0,"HyperDash":false}]},{"StartTime":19844.0,"Objects":[{"StartTime":19844.0,"Position":388.0,"HyperDash":false}]},{"StartTime":20026.0,"Objects":[{"StartTime":20026.0,"Position":216.0,"HyperDash":false},{"StartTime":20098.0,"Position":187.187851,"HyperDash":false},{"StartTime":20207.0,"Position":131.0,"HyperDash":false}]},{"StartTime":20390.0,"Objects":[{"StartTime":20390.0,"Position":224.0,"HyperDash":false},{"StartTime":20462.0,"Position":212.0,"HyperDash":false},{"StartTime":20571.0,"Position":224.0,"HyperDash":false}]},{"StartTime":20753.0,"Objects":[{"StartTime":20753.0,"Position":52.0,"HyperDash":false},{"StartTime":20843.0,"Position":37.0,"HyperDash":false},{"StartTime":20934.0,"Position":52.0,"HyperDash":false},{"StartTime":21007.0,"Position":74.95316,"HyperDash":false},{"StartTime":21116.0,"Position":134.0,"HyperDash":false}]},{"StartTime":21299.0,"Objects":[{"StartTime":21299.0,"Position":224.0,"HyperDash":false}]},{"StartTime":21480.0,"Objects":[{"StartTime":21480.0,"Position":396.0,"HyperDash":false}]},{"StartTime":21844.0,"Objects":[{"StartTime":21844.0,"Position":224.0,"HyperDash":false}]},{"StartTime":22026.0,"Objects":[{"StartTime":22026.0,"Position":132.0,"HyperDash":false}]},{"StartTime":22208.0,"Objects":[{"StartTime":22208.0,"Position":224.0,"HyperDash":false}]},{"StartTime":22299.0,"Objects":[{"StartTime":22299.0,"Position":176.0,"HyperDash":false}]},{"StartTime":22390.0,"Objects":[{"StartTime":22390.0,"Position":132.0,"HyperDash":false}]},{"StartTime":22571.0,"Objects":[{"StartTime":22571.0,"Position":232.0,"HyperDash":false}]},{"StartTime":22753.0,"Objects":[{"StartTime":22753.0,"Position":404.0,"HyperDash":false}]},{"StartTime":22935.0,"Objects":[{"StartTime":22935.0,"Position":232.0,"HyperDash":false},{"StartTime":23007.0,"Position":248.0,"HyperDash":false},{"StartTime":23116.0,"Position":232.0,"HyperDash":false}]},{"StartTime":23299.0,"Objects":[{"StartTime":23299.0,"Position":404.0,"HyperDash":false}]},{"StartTime":23389.0,"Objects":[{"StartTime":23389.0,"Position":448.0,"HyperDash":false}]},{"StartTime":23480.0,"Objects":[{"StartTime":23480.0,"Position":492.0,"HyperDash":true}]},{"StartTime":23662.0,"Objects":[{"StartTime":23662.0,"Position":212.0,"HyperDash":false},{"StartTime":23752.0,"Position":164.4215,"HyperDash":false},{"StartTime":23843.0,"Position":110.280991,"HyperDash":false},{"StartTime":23916.0,"Position":70.25621,"HyperDash":false},{"StartTime":24025.0,"Position":8.0,"HyperDash":false}]},{"StartTime":24208.0,"Objects":[{"StartTime":24208.0,"Position":92.0,"HyperDash":false}]},{"StartTime":24390.0,"Objects":[{"StartTime":24390.0,"Position":272.0,"HyperDash":false},{"StartTime":24462.0,"Position":262.0,"HyperDash":false},{"StartTime":24571.0,"Position":272.0,"HyperDash":false}]},{"StartTime":24753.0,"Objects":[{"StartTime":24753.0,"Position":180.0,"HyperDash":false}]},{"StartTime":25117.0,"Objects":[{"StartTime":25117.0,"Position":348.0,"HyperDash":false},{"StartTime":25189.0,"Position":314.187836,"HyperDash":false},{"StartTime":25298.0,"Position":263.0,"HyperDash":false}]},{"StartTime":25480.0,"Objects":[{"StartTime":25480.0,"Position":355.0,"HyperDash":false}]},{"StartTime":25662.0,"Objects":[{"StartTime":25662.0,"Position":179.0,"HyperDash":false}]},{"StartTime":25752.0,"Objects":[{"StartTime":25752.0,"Position":135.0,"HyperDash":false}]},{"StartTime":25843.0,"Objects":[{"StartTime":25843.0,"Position":91.0,"HyperDash":false},{"StartTime":25933.0,"Position":30.8512421,"HyperDash":false},{"StartTime":26024.0,"Position":6.0,"HyperDash":false},{"StartTime":26097.0,"Position":23.9531631,"HyperDash":false},{"StartTime":26206.0,"Position":91.0,"HyperDash":false}]},{"StartTime":26571.0,"Objects":[{"StartTime":26571.0,"Position":383.0,"HyperDash":false}]},{"StartTime":26753.0,"Objects":[{"StartTime":26753.0,"Position":299.0,"HyperDash":false},{"StartTime":26843.0,"Position":264.851257,"HyperDash":false},{"StartTime":26934.0,"Position":215.0,"HyperDash":false},{"StartTime":27007.0,"Position":195.0,"HyperDash":false},{"StartTime":27116.0,"Position":215.0,"HyperDash":false}]},{"StartTime":27299.0,"Objects":[{"StartTime":27299.0,"Position":391.0,"HyperDash":false}]},{"StartTime":27662.0,"Objects":[{"StartTime":27662.0,"Position":239.0,"HyperDash":false},{"StartTime":27734.0,"Position":234.0,"HyperDash":false},{"StartTime":27843.0,"Position":239.0,"HyperDash":false}]},{"StartTime":28026.0,"Objects":[{"StartTime":28026.0,"Position":323.0,"HyperDash":false}]},{"StartTime":28208.0,"Objects":[{"StartTime":28208.0,"Position":231.0,"HyperDash":false}]},{"StartTime":28390.0,"Objects":[{"StartTime":28390.0,"Position":315.0,"HyperDash":false}]},{"StartTime":28571.0,"Objects":[{"StartTime":28571.0,"Position":143.0,"HyperDash":false}]},{"StartTime":28753.0,"Objects":[{"StartTime":28753.0,"Position":315.0,"HyperDash":false}]},{"StartTime":28935.0,"Objects":[{"StartTime":28935.0,"Position":407.0,"HyperDash":false},{"StartTime":29025.0,"Position":446.57605,"HyperDash":false},{"StartTime":29116.0,"Position":508.0,"HyperDash":false},{"StartTime":29189.0,"Position":506.0,"HyperDash":false},{"StartTime":29298.0,"Position":508.0,"HyperDash":true}]},{"StartTime":29480.0,"Objects":[{"StartTime":29480.0,"Position":212.0,"HyperDash":false},{"StartTime":29570.0,"Position":178.4679,"HyperDash":false},{"StartTime":29661.0,"Position":110.374321,"HyperDash":false},{"StartTime":29752.0,"Position":113.0,"HyperDash":false},{"StartTime":29843.0,"Position":108.0,"HyperDash":false},{"StartTime":29916.0,"Position":158.8,"HyperDash":false},{"StartTime":30025.0,"Position":210.0,"HyperDash":false}]},{"StartTime":30208.0,"Objects":[{"StartTime":30208.0,"Position":304.0,"HyperDash":false},{"StartTime":30298.0,"Position":356.148743,"HyperDash":false},{"StartTime":30389.0,"Position":389.0,"HyperDash":false},{"StartTime":30462.0,"Position":359.046844,"HyperDash":false},{"StartTime":30571.0,"Position":304.0,"HyperDash":false}]},{"StartTime":30935.0,"Objects":[{"StartTime":30935.0,"Position":152.0,"HyperDash":false},{"StartTime":31007.0,"Position":159.0,"HyperDash":false},{"StartTime":31116.0,"Position":152.0,"HyperDash":false}]},{"StartTime":31299.0,"Objects":[{"StartTime":31299.0,"Position":236.0,"HyperDash":false},{"StartTime":31371.0,"Position":252.0,"HyperDash":false},{"StartTime":31480.0,"Position":236.0,"HyperDash":false}]},{"StartTime":31662.0,"Objects":[{"StartTime":31662.0,"Position":320.0,"HyperDash":false},{"StartTime":31752.0,"Position":262.851257,"HyperDash":false},{"StartTime":31843.0,"Position":235.0,"HyperDash":false},{"StartTime":31916.0,"Position":265.953156,"HyperDash":false},{"StartTime":32025.0,"Position":320.0,"HyperDash":false}]},{"StartTime":32390.0,"Objects":[{"StartTime":32390.0,"Position":136.0,"HyperDash":false},{"StartTime":32458.0,"Position":135.0,"HyperDash":false},{"StartTime":32526.0,"Position":346.0,"HyperDash":false},{"StartTime":32594.0,"Position":39.0,"HyperDash":false},{"StartTime":32662.0,"Position":300.0,"HyperDash":false},{"StartTime":32730.0,"Position":398.0,"HyperDash":false},{"StartTime":32798.0,"Position":151.0,"HyperDash":false},{"StartTime":32866.0,"Position":73.0,"HyperDash":false},{"StartTime":32935.0,"Position":311.0,"HyperDash":false},{"StartTime":33003.0,"Position":90.0,"HyperDash":false},{"StartTime":33071.0,"Position":264.0,"HyperDash":false},{"StartTime":33139.0,"Position":477.0,"HyperDash":false},{"StartTime":33207.0,"Position":473.0,"HyperDash":false},{"StartTime":33275.0,"Position":120.0,"HyperDash":false},{"StartTime":33343.0,"Position":115.0,"HyperDash":false},{"StartTime":33411.0,"Position":163.0,"HyperDash":false},{"StartTime":33480.0,"Position":447.0,"HyperDash":false}]},{"StartTime":33844.0,"Objects":[{"StartTime":33844.0,"Position":428.0,"HyperDash":false},{"StartTime":33934.0,"Position":428.0,"HyperDash":false},{"StartTime":34025.0,"Position":428.0,"HyperDash":false}]},{"StartTime":34208.0,"Objects":[{"StartTime":34208.0,"Position":256.0,"HyperDash":false},{"StartTime":34280.0,"Position":207.187851,"HyperDash":false},{"StartTime":34389.0,"Position":171.0,"HyperDash":false}]},{"StartTime":34480.0,"Objects":[{"StartTime":34480.0,"Position":216.0,"HyperDash":false}]},{"StartTime":34571.0,"Objects":[{"StartTime":34571.0,"Position":264.0,"HyperDash":false},{"StartTime":34661.0,"Position":306.5,"HyperDash":false},{"StartTime":34752.0,"Position":264.0,"HyperDash":false}]},{"StartTime":34935.0,"Objects":[{"StartTime":34935.0,"Position":92.0,"HyperDash":false},{"StartTime":35007.0,"Position":54.1878471,"HyperDash":false},{"StartTime":35116.0,"Position":7.0,"HyperDash":true}]},{"StartTime":35299.0,"Objects":[{"StartTime":35299.0,"Position":288.0,"HyperDash":false},{"StartTime":35389.0,"Position":341.578522,"HyperDash":false},{"StartTime":35480.0,"Position":389.719,"HyperDash":false},{"StartTime":35553.0,"Position":430.743774,"HyperDash":false},{"StartTime":35662.0,"Position":492.0,"HyperDash":false}]},{"StartTime":35844.0,"Objects":[{"StartTime":35844.0,"Position":400.0,"HyperDash":false}]},{"StartTime":36026.0,"Objects":[{"StartTime":36026.0,"Position":224.0,"HyperDash":false},{"StartTime":36098.0,"Position":203.187851,"HyperDash":false},{"StartTime":36207.0,"Position":139.0,"HyperDash":false}]},{"StartTime":36390.0,"Objects":[{"StartTime":36390.0,"Position":232.0,"HyperDash":false},{"StartTime":36462.0,"Position":229.0,"HyperDash":false},{"StartTime":36571.0,"Position":232.0,"HyperDash":false}]},{"StartTime":36753.0,"Objects":[{"StartTime":36753.0,"Position":56.0,"HyperDash":false},{"StartTime":36825.0,"Position":72.0,"HyperDash":false},{"StartTime":36934.0,"Position":56.0,"HyperDash":false}]},{"StartTime":37026.0,"Objects":[{"StartTime":37026.0,"Position":104.0,"HyperDash":false}]},{"StartTime":37117.0,"Objects":[{"StartTime":37117.0,"Position":152.0,"HyperDash":false}]},{"StartTime":37299.0,"Objects":[{"StartTime":37299.0,"Position":244.0,"HyperDash":false}]},{"StartTime":37480.0,"Objects":[{"StartTime":37480.0,"Position":152.0,"HyperDash":false},{"StartTime":37552.0,"Position":109.187851,"HyperDash":false},{"StartTime":37661.0,"Position":67.0,"HyperDash":false}]},{"StartTime":37844.0,"Objects":[{"StartTime":37844.0,"Position":244.0,"HyperDash":false},{"StartTime":37916.0,"Position":233.0,"HyperDash":false},{"StartTime":38025.0,"Position":244.0,"HyperDash":false}]},{"StartTime":38208.0,"Objects":[{"StartTime":38208.0,"Position":496.0,"HyperDash":false},{"StartTime":38298.0,"Position":482.0,"HyperDash":false},{"StartTime":38389.0,"Position":495.234161,"HyperDash":false},{"StartTime":38462.0,"Position":471.046844,"HyperDash":false},{"StartTime":38571.0,"Position":410.0,"HyperDash":false}]},{"StartTime":38753.0,"Objects":[{"StartTime":38753.0,"Position":504.0,"HyperDash":false},{"StartTime":38843.0,"Position":480.851257,"HyperDash":false},{"StartTime":38934.0,"Position":419.234161,"HyperDash":false},{"StartTime":39007.0,"Position":379.046844,"HyperDash":false},{"StartTime":39116.0,"Position":334.0,"HyperDash":false}]},{"StartTime":39299.0,"Objects":[{"StartTime":39299.0,"Position":156.0,"HyperDash":false},{"StartTime":39371.0,"Position":128.187851,"HyperDash":false},{"StartTime":39480.0,"Position":71.0,"HyperDash":false}]},{"StartTime":39662.0,"Objects":[{"StartTime":39662.0,"Position":252.0,"HyperDash":false},{"StartTime":39752.0,"Position":294.5,"HyperDash":false},{"StartTime":39843.0,"Position":252.0,"HyperDash":false}]},{"StartTime":40026.0,"Objects":[{"StartTime":40026.0,"Position":71.0,"HyperDash":false},{"StartTime":40098.0,"Position":83.0,"HyperDash":false},{"StartTime":40207.0,"Position":71.0,"HyperDash":false}]},{"StartTime":40390.0,"Objects":[{"StartTime":40390.0,"Position":164.0,"HyperDash":false},{"StartTime":40462.0,"Position":117.187851,"HyperDash":false},{"StartTime":40571.0,"Position":79.0,"HyperDash":false}]},{"StartTime":40753.0,"Objects":[{"StartTime":40753.0,"Position":256.0,"HyperDash":false},{"StartTime":40825.0,"Position":275.812164,"HyperDash":false},{"StartTime":40934.0,"Position":341.0,"HyperDash":false}]},{"StartTime":41117.0,"Objects":[{"StartTime":41117.0,"Position":84.0,"HyperDash":false},{"StartTime":41207.0,"Position":107.148758,"HyperDash":false},{"StartTime":41298.0,"Position":168.765839,"HyperDash":false},{"StartTime":41371.0,"Position":188.953156,"HyperDash":false},{"StartTime":41480.0,"Position":254.0,"HyperDash":false}]},{"StartTime":41662.0,"Objects":[{"StartTime":41662.0,"Position":432.0,"HyperDash":false},{"StartTime":41734.0,"Position":438.0,"HyperDash":false},{"StartTime":41843.0,"Position":432.0,"HyperDash":false}]},{"StartTime":42026.0,"Objects":[{"StartTime":42026.0,"Position":348.0,"HyperDash":false}]},{"StartTime":42208.0,"Objects":[{"StartTime":42208.0,"Position":432.0,"HyperDash":false},{"StartTime":42280.0,"Position":411.187836,"HyperDash":false},{"StartTime":42389.0,"Position":347.0,"HyperDash":false}]},{"StartTime":42571.0,"Objects":[{"StartTime":42571.0,"Position":176.0,"HyperDash":false},{"StartTime":42643.0,"Position":132.187851,"HyperDash":false},{"StartTime":42752.0,"Position":91.0,"HyperDash":false}]},{"StartTime":42844.0,"Objects":[{"StartTime":42844.0,"Position":132.0,"HyperDash":false}]},{"StartTime":42935.0,"Objects":[{"StartTime":42935.0,"Position":176.0,"HyperDash":false}]},{"StartTime":43117.0,"Objects":[{"StartTime":43117.0,"Position":260.0,"HyperDash":false},{"StartTime":43207.0,"Position":210.851242,"HyperDash":false},{"StartTime":43298.0,"Position":175.0,"HyperDash":false},{"StartTime":43371.0,"Position":218.953156,"HyperDash":false},{"StartTime":43480.0,"Position":260.0,"HyperDash":false}]},{"StartTime":43662.0,"Objects":[{"StartTime":43662.0,"Position":84.0,"HyperDash":false},{"StartTime":43734.0,"Position":93.0,"HyperDash":false},{"StartTime":43843.0,"Position":84.0,"HyperDash":false}]},{"StartTime":44026.0,"Objects":[{"StartTime":44026.0,"Position":336.0,"HyperDash":false},{"StartTime":44116.0,"Position":393.578522,"HyperDash":false},{"StartTime":44207.0,"Position":436.0,"HyperDash":false},{"StartTime":44280.0,"Position":442.0,"HyperDash":false},{"StartTime":44389.0,"Position":436.0,"HyperDash":false}]},{"StartTime":44571.0,"Objects":[{"StartTime":44571.0,"Position":344.0,"HyperDash":false}]},{"StartTime":44753.0,"Objects":[{"StartTime":44753.0,"Position":252.0,"HyperDash":false},{"StartTime":44825.0,"Position":246.0,"HyperDash":false},{"StartTime":44934.0,"Position":252.0,"HyperDash":false}]},{"StartTime":45117.0,"Objects":[{"StartTime":45117.0,"Position":428.0,"HyperDash":false},{"StartTime":45189.0,"Position":387.187836,"HyperDash":false},{"StartTime":45298.0,"Position":343.0,"HyperDash":false}]},{"StartTime":45480.0,"Objects":[{"StartTime":45480.0,"Position":164.0,"HyperDash":false}]},{"StartTime":45570.0,"Objects":[{"StartTime":45570.0,"Position":121.0,"HyperDash":false}]},{"StartTime":45661.0,"Objects":[{"StartTime":45661.0,"Position":79.0,"HyperDash":false}]},{"StartTime":45844.0,"Objects":[{"StartTime":45844.0,"Position":256.0,"HyperDash":false},{"StartTime":45916.0,"Position":275.0,"HyperDash":false},{"StartTime":46025.0,"Position":256.0,"HyperDash":false}]},{"StartTime":46208.0,"Objects":[{"StartTime":46208.0,"Position":160.0,"HyperDash":false},{"StartTime":46280.0,"Position":188.812149,"HyperDash":false},{"StartTime":46389.0,"Position":245.0,"HyperDash":false}]},{"StartTime":46571.0,"Objects":[{"StartTime":46571.0,"Position":68.0,"HyperDash":false},{"StartTime":46643.0,"Position":68.0,"HyperDash":false},{"StartTime":46752.0,"Position":68.0,"HyperDash":false}]},{"StartTime":46935.0,"Objects":[{"StartTime":46935.0,"Position":324.0,"HyperDash":false},{"StartTime":47025.0,"Position":381.148743,"HyperDash":false},{"StartTime":47116.0,"Position":409.0,"HyperDash":false},{"StartTime":47189.0,"Position":359.046844,"HyperDash":false},{"StartTime":47298.0,"Position":324.0,"HyperDash":false}]},{"StartTime":47480.0,"Objects":[{"StartTime":47480.0,"Position":154.0,"HyperDash":false},{"StartTime":47570.0,"Position":213.148758,"HyperDash":false},{"StartTime":47661.0,"Position":238.765839,"HyperDash":false},{"StartTime":47734.0,"Position":268.953156,"HyperDash":false},{"StartTime":47843.0,"Position":324.0,"HyperDash":false}]},{"StartTime":48026.0,"Objects":[{"StartTime":48026.0,"Position":420.0,"HyperDash":false},{"StartTime":48098.0,"Position":428.0,"HyperDash":false},{"StartTime":48207.0,"Position":420.0,"HyperDash":false}]},{"StartTime":48390.0,"Objects":[{"StartTime":48390.0,"Position":240.0,"HyperDash":false},{"StartTime":48462.0,"Position":205.187851,"HyperDash":false},{"StartTime":48571.0,"Position":155.0,"HyperDash":false}]},{"StartTime":48662.0,"Objects":[{"StartTime":48662.0,"Position":112.0,"HyperDash":false}]},{"StartTime":48753.0,"Objects":[{"StartTime":48753.0,"Position":68.0,"HyperDash":false}]},{"StartTime":48935.0,"Objects":[{"StartTime":48935.0,"Position":160.0,"HyperDash":false},{"StartTime":49025.0,"Position":132.851242,"HyperDash":false},{"StartTime":49116.0,"Position":75.0,"HyperDash":false},{"StartTime":49189.0,"Position":96.95316,"HyperDash":false},{"StartTime":49298.0,"Position":160.0,"HyperDash":false}]},{"StartTime":49480.0,"Objects":[{"StartTime":49480.0,"Position":336.0,"HyperDash":false},{"StartTime":49552.0,"Position":353.812164,"HyperDash":false},{"StartTime":49661.0,"Position":421.0,"HyperDash":false}]},{"StartTime":49844.0,"Objects":[{"StartTime":49844.0,"Position":164.0,"HyperDash":false},{"StartTime":49916.0,"Position":123.187851,"HyperDash":false},{"StartTime":50025.0,"Position":79.0,"HyperDash":false}]},{"StartTime":50117.0,"Objects":[{"StartTime":50117.0,"Position":79.0,"HyperDash":false}]},{"StartTime":50208.0,"Objects":[{"StartTime":50208.0,"Position":79.0,"HyperDash":false}]},{"StartTime":50390.0,"Objects":[{"StartTime":50390.0,"Position":172.0,"HyperDash":false},{"StartTime":50480.0,"Position":196.148758,"HyperDash":false},{"StartTime":50571.0,"Position":256.0,"HyperDash":false},{"StartTime":50644.0,"Position":261.0,"HyperDash":false},{"StartTime":50753.0,"Position":256.0,"HyperDash":false}]},{"StartTime":50935.0,"Objects":[{"StartTime":50935.0,"Position":80.0,"HyperDash":false},{"StartTime":51007.0,"Position":81.0,"HyperDash":false},{"StartTime":51116.0,"Position":80.0,"HyperDash":false}]},{"StartTime":51299.0,"Objects":[{"StartTime":51299.0,"Position":256.0,"HyperDash":false},{"StartTime":51389.0,"Position":296.148743,"HyperDash":false},{"StartTime":51480.0,"Position":340.765839,"HyperDash":false},{"StartTime":51553.0,"Position":371.953156,"HyperDash":false},{"StartTime":51662.0,"Position":426.0,"HyperDash":false}]},{"StartTime":51844.0,"Objects":[{"StartTime":51844.0,"Position":340.0,"HyperDash":false}]},{"StartTime":52026.0,"Objects":[{"StartTime":52026.0,"Position":426.0,"HyperDash":false},{"StartTime":52098.0,"Position":406.187836,"HyperDash":false},{"StartTime":52207.0,"Position":341.0,"HyperDash":false}]},{"StartTime":52390.0,"Objects":[{"StartTime":52390.0,"Position":164.0,"HyperDash":false},{"StartTime":52462.0,"Position":117.187851,"HyperDash":false},{"StartTime":52571.0,"Position":79.0,"HyperDash":true}]},{"StartTime":52753.0,"Objects":[{"StartTime":52753.0,"Position":336.0,"HyperDash":false},{"StartTime":52843.0,"Position":377.148743,"HyperDash":false},{"StartTime":52934.0,"Position":420.765839,"HyperDash":false},{"StartTime":53007.0,"Position":457.953156,"HyperDash":false},{"StartTime":53116.0,"Position":506.0,"HyperDash":false}]},{"StartTime":53299.0,"Objects":[{"StartTime":53299.0,"Position":328.0,"HyperDash":false},{"StartTime":53389.0,"Position":380.148743,"HyperDash":false},{"StartTime":53480.0,"Position":412.765839,"HyperDash":false},{"StartTime":53553.0,"Position":426.953156,"HyperDash":false},{"StartTime":53662.0,"Position":498.0,"HyperDash":false}]},{"StartTime":53844.0,"Objects":[{"StartTime":53844.0,"Position":412.0,"HyperDash":false},{"StartTime":53916.0,"Position":416.0,"HyperDash":false},{"StartTime":54025.0,"Position":412.0,"HyperDash":false}]},{"StartTime":54208.0,"Objects":[{"StartTime":54208.0,"Position":236.0,"HyperDash":false},{"StartTime":54280.0,"Position":207.187851,"HyperDash":false},{"StartTime":54389.0,"Position":151.0,"HyperDash":false}]},{"StartTime":54480.0,"Objects":[{"StartTime":54480.0,"Position":192.0,"HyperDash":false}]},{"StartTime":54571.0,"Objects":[{"StartTime":54571.0,"Position":236.0,"HyperDash":false}]},{"StartTime":54753.0,"Objects":[{"StartTime":54753.0,"Position":320.0,"HyperDash":false}]},{"StartTime":54935.0,"Objects":[{"StartTime":54935.0,"Position":236.0,"HyperDash":false}]},{"StartTime":55117.0,"Objects":[{"StartTime":55117.0,"Position":152.0,"HyperDash":false}]},{"StartTime":55299.0,"Objects":[{"StartTime":55299.0,"Position":328.0,"HyperDash":false},{"StartTime":55371.0,"Position":328.0,"HyperDash":false},{"StartTime":55480.0,"Position":328.0,"HyperDash":false}]},{"StartTime":55662.0,"Objects":[{"StartTime":55662.0,"Position":72.0,"HyperDash":false},{"StartTime":55734.0,"Position":54.0,"HyperDash":false},{"StartTime":55843.0,"Position":72.0,"HyperDash":false}]},{"StartTime":55935.0,"Objects":[{"StartTime":55935.0,"Position":116.0,"HyperDash":false}]},{"StartTime":56026.0,"Objects":[{"StartTime":56026.0,"Position":160.0,"HyperDash":false}]},{"StartTime":56208.0,"Objects":[{"StartTime":56208.0,"Position":244.0,"HyperDash":false},{"StartTime":56298.0,"Position":182.851242,"HyperDash":false},{"StartTime":56389.0,"Position":159.0,"HyperDash":false},{"StartTime":56462.0,"Position":181.953156,"HyperDash":false},{"StartTime":56571.0,"Position":244.0,"HyperDash":false}]},{"StartTime":56753.0,"Objects":[{"StartTime":56753.0,"Position":72.0,"HyperDash":false},{"StartTime":56825.0,"Position":81.0,"HyperDash":false},{"StartTime":56934.0,"Position":72.0,"HyperDash":false}]},{"StartTime":57117.0,"Objects":[{"StartTime":57117.0,"Position":248.0,"HyperDash":false},{"StartTime":57207.0,"Position":290.5,"HyperDash":false},{"StartTime":57298.0,"Position":248.0,"HyperDash":false}]},{"StartTime":57481.0,"Objects":[{"StartTime":57481.0,"Position":78.0,"HyperDash":false},{"StartTime":57553.0,"Position":71.67611,"HyperDash":false},{"StartTime":57662.0,"Position":79.69966,"HyperDash":false}]},{"StartTime":57844.0,"Objects":[{"StartTime":57844.0,"Position":164.0,"HyperDash":false},{"StartTime":57916.0,"Position":146.187851,"HyperDash":false},{"StartTime":58025.0,"Position":79.0,"HyperDash":false}]},{"StartTime":58208.0,"Objects":[{"StartTime":58208.0,"Position":248.0,"HyperDash":false},{"StartTime":58280.0,"Position":228.187851,"HyperDash":false},{"StartTime":58389.0,"Position":163.0,"HyperDash":false}]},{"StartTime":58571.0,"Objects":[{"StartTime":58571.0,"Position":416.0,"HyperDash":false},{"StartTime":58661.0,"Position":451.148743,"HyperDash":false},{"StartTime":58752.0,"Position":499.234161,"HyperDash":false},{"StartTime":58825.0,"Position":447.046844,"HyperDash":false},{"StartTime":58934.0,"Position":414.0,"HyperDash":false}]},{"StartTime":59117.0,"Objects":[{"StartTime":59117.0,"Position":320.0,"HyperDash":false}]},{"StartTime":59299.0,"Objects":[{"StartTime":59299.0,"Position":140.0,"HyperDash":false},{"StartTime":59389.0,"Position":111.851242,"HyperDash":false},{"StartTime":59480.0,"Position":55.0,"HyperDash":false},{"StartTime":59553.0,"Position":89.95316,"HyperDash":false},{"StartTime":59662.0,"Position":140.0,"HyperDash":false}]},{"StartTime":60026.0,"Objects":[{"StartTime":60026.0,"Position":428.0,"HyperDash":false},{"StartTime":60098.0,"Position":432.0,"HyperDash":false},{"StartTime":60207.0,"Position":428.0,"HyperDash":false}]},{"StartTime":60390.0,"Objects":[{"StartTime":60390.0,"Position":332.0,"HyperDash":false},{"StartTime":60462.0,"Position":362.812164,"HyperDash":false},{"StartTime":60571.0,"Position":417.0,"HyperDash":false}]},{"StartTime":60753.0,"Objects":[{"StartTime":60753.0,"Position":324.0,"HyperDash":false}]},{"StartTime":60843.0,"Objects":[{"StartTime":60843.0,"Position":366.0,"HyperDash":false}]},{"StartTime":60934.0,"Objects":[{"StartTime":60934.0,"Position":409.0,"HyperDash":false}]},{"StartTime":61117.0,"Objects":[{"StartTime":61117.0,"Position":228.0,"HyperDash":false},{"StartTime":61189.0,"Position":181.187851,"HyperDash":false},{"StartTime":61298.0,"Position":143.0,"HyperDash":false}]},{"StartTime":61480.0,"Objects":[{"StartTime":61480.0,"Position":324.0,"HyperDash":false},{"StartTime":61570.0,"Position":323.0,"HyperDash":false},{"StartTime":61661.0,"Position":324.0,"HyperDash":false},{"StartTime":61734.0,"Position":306.0,"HyperDash":false},{"StartTime":61843.0,"Position":324.0,"HyperDash":false}]},{"StartTime":62026.0,"Objects":[{"StartTime":62026.0,"Position":228.0,"HyperDash":false}]},{"StartTime":62208.0,"Objects":[{"StartTime":62208.0,"Position":408.0,"HyperDash":false},{"StartTime":62298.0,"Position":361.851257,"HyperDash":false},{"StartTime":62389.0,"Position":323.0,"HyperDash":false},{"StartTime":62462.0,"Position":339.953156,"HyperDash":false},{"StartTime":62571.0,"Position":408.0,"HyperDash":false}]},{"StartTime":62935.0,"Objects":[{"StartTime":62935.0,"Position":120.0,"HyperDash":false},{"StartTime":63025.0,"Position":77.5,"HyperDash":false},{"StartTime":63116.0,"Position":120.0,"HyperDash":false}]},{"StartTime":63299.0,"Objects":[{"StartTime":63299.0,"Position":216.0,"HyperDash":false},{"StartTime":63371.0,"Position":227.0,"HyperDash":false},{"StartTime":63480.0,"Position":216.0,"HyperDash":false}]},{"StartTime":63662.0,"Objects":[{"StartTime":63662.0,"Position":396.0,"HyperDash":false},{"StartTime":63734.0,"Position":343.187836,"HyperDash":false},{"StartTime":63843.0,"Position":311.0,"HyperDash":false}]},{"StartTime":64026.0,"Objects":[{"StartTime":64026.0,"Position":148.0,"HyperDash":false}]},{"StartTime":64208.0,"Objects":[{"StartTime":64208.0,"Position":320.0,"HyperDash":false}]},{"StartTime":64390.0,"Objects":[{"StartTime":64390.0,"Position":140.0,"HyperDash":false},{"StartTime":64480.0,"Position":114.851242,"HyperDash":false},{"StartTime":64571.0,"Position":56.0,"HyperDash":false},{"StartTime":64644.0,"Position":56.0,"HyperDash":false},{"StartTime":64753.0,"Position":56.0,"HyperDash":false}]},{"StartTime":64935.0,"Objects":[{"StartTime":64935.0,"Position":140.0,"HyperDash":false}]},{"StartTime":65117.0,"Objects":[{"StartTime":65117.0,"Position":396.0,"HyperDash":false},{"StartTime":65189.0,"Position":395.0,"HyperDash":false},{"StartTime":65298.0,"Position":396.0,"HyperDash":false}]},{"StartTime":65480.0,"Objects":[{"StartTime":65480.0,"Position":312.0,"HyperDash":false}]},{"StartTime":65662.0,"Objects":[{"StartTime":65662.0,"Position":404.0,"HyperDash":false}]},{"StartTime":65844.0,"Objects":[{"StartTime":65844.0,"Position":300.0,"HyperDash":false},{"StartTime":65916.0,"Position":278.187836,"HyperDash":false},{"StartTime":66025.0,"Position":215.0,"HyperDash":false}]},{"StartTime":66208.0,"Objects":[{"StartTime":66208.0,"Position":392.0,"HyperDash":false},{"StartTime":66280.0,"Position":394.0,"HyperDash":false},{"StartTime":66389.0,"Position":392.0,"HyperDash":false}]},{"StartTime":66571.0,"Objects":[{"StartTime":66571.0,"Position":136.0,"HyperDash":false},{"StartTime":66643.0,"Position":137.0,"HyperDash":false},{"StartTime":66752.0,"Position":136.0,"HyperDash":false}]},{"StartTime":66935.0,"Objects":[{"StartTime":66935.0,"Position":307.0,"HyperDash":false},{"StartTime":67007.0,"Position":327.812164,"HyperDash":false},{"StartTime":67116.0,"Position":392.0,"HyperDash":false}]},{"StartTime":67299.0,"Objects":[{"StartTime":67299.0,"Position":476.0,"HyperDash":false},{"StartTime":67371.0,"Position":479.0,"HyperDash":false},{"StartTime":67480.0,"Position":476.0,"HyperDash":false}]},{"StartTime":67662.0,"Objects":[{"StartTime":67662.0,"Position":307.0,"HyperDash":false},{"StartTime":67734.0,"Position":295.0,"HyperDash":false},{"StartTime":67843.0,"Position":307.0,"HyperDash":true}]},{"StartTime":68026.0,"Objects":[{"StartTime":68026.0,"Position":48.0,"HyperDash":false},{"StartTime":68098.0,"Position":74.81215,"HyperDash":false},{"StartTime":68207.0,"Position":133.0,"HyperDash":false}]},{"StartTime":68390.0,"Objects":[{"StartTime":68390.0,"Position":307.0,"HyperDash":false},{"StartTime":68462.0,"Position":288.0,"HyperDash":false},{"StartTime":68571.0,"Position":307.0,"HyperDash":false}]},{"StartTime":68753.0,"Objects":[{"StartTime":68753.0,"Position":222.0,"HyperDash":false},{"StartTime":68825.0,"Position":257.812134,"HyperDash":false},{"StartTime":68934.0,"Position":307.0,"HyperDash":false}]},{"StartTime":69117.0,"Objects":[{"StartTime":69117.0,"Position":136.0,"HyperDash":false},{"StartTime":69189.0,"Position":131.0,"HyperDash":false},{"StartTime":69298.0,"Position":136.0,"HyperDash":false}]},{"StartTime":69480.0,"Objects":[{"StartTime":69480.0,"Position":228.0,"HyperDash":false},{"StartTime":69552.0,"Position":175.187851,"HyperDash":false},{"StartTime":69661.0,"Position":143.0,"HyperDash":false}]},{"StartTime":69844.0,"Objects":[{"StartTime":69844.0,"Position":236.0,"HyperDash":false},{"StartTime":69916.0,"Position":254.812164,"HyperDash":false},{"StartTime":70025.0,"Position":321.0,"HyperDash":true}]},{"StartTime":70208.0,"Objects":[{"StartTime":70208.0,"Position":60.0,"HyperDash":false},{"StartTime":70298.0,"Position":66.0,"HyperDash":false},{"StartTime":70389.0,"Position":60.76584,"HyperDash":false},{"StartTime":70462.0,"Position":88.95316,"HyperDash":false},{"StartTime":70571.0,"Position":146.0,"HyperDash":false}]},{"StartTime":70753.0,"Objects":[{"StartTime":70753.0,"Position":232.0,"HyperDash":false}]},{"StartTime":70935.0,"Objects":[{"StartTime":70935.0,"Position":412.0,"HyperDash":false},{"StartTime":71025.0,"Position":356.851257,"HyperDash":false},{"StartTime":71116.0,"Position":327.0,"HyperDash":false},{"StartTime":71189.0,"Position":351.953156,"HyperDash":false},{"StartTime":71298.0,"Position":412.0,"HyperDash":false}]},{"StartTime":71662.0,"Objects":[{"StartTime":71662.0,"Position":124.0,"HyperDash":false},{"StartTime":71734.0,"Position":118.0,"HyperDash":false},{"StartTime":71843.0,"Position":124.0,"HyperDash":false}]},{"StartTime":72026.0,"Objects":[{"StartTime":72026.0,"Position":220.0,"HyperDash":false},{"StartTime":72098.0,"Position":242.812149,"HyperDash":false},{"StartTime":72207.0,"Position":305.0,"HyperDash":false}]},{"StartTime":72389.0,"Objects":[{"StartTime":72389.0,"Position":212.0,"HyperDash":false}]},{"StartTime":72571.0,"Objects":[{"StartTime":72571.0,"Position":316.0,"HyperDash":false}]},{"StartTime":72753.0,"Objects":[{"StartTime":72753.0,"Position":136.0,"HyperDash":false},{"StartTime":72825.0,"Position":102.187851,"HyperDash":false},{"StartTime":72934.0,"Position":51.0,"HyperDash":true}]},{"StartTime":73116.0,"Objects":[{"StartTime":73116.0,"Position":316.0,"HyperDash":false},{"StartTime":73206.0,"Position":344.148743,"HyperDash":false},{"StartTime":73297.0,"Position":400.0,"HyperDash":false},{"StartTime":73370.0,"Position":415.0,"HyperDash":false},{"StartTime":73479.0,"Position":400.0,"HyperDash":false}]},{"StartTime":73662.0,"Objects":[{"StartTime":73662.0,"Position":316.0,"HyperDash":false}]},{"StartTime":73844.0,"Objects":[{"StartTime":73844.0,"Position":144.0,"HyperDash":false}]},{"StartTime":74026.0,"Objects":[{"StartTime":74026.0,"Position":236.0,"HyperDash":false}]},{"StartTime":74208.0,"Objects":[{"StartTime":74208.0,"Position":328.0,"HyperDash":false}]},{"StartTime":74571.0,"Objects":[{"StartTime":74571.0,"Position":56.0,"HyperDash":false}]},{"StartTime":74753.0,"Objects":[{"StartTime":74753.0,"Position":228.0,"HyperDash":false}]},{"StartTime":74935.0,"Objects":[{"StartTime":74935.0,"Position":400.0,"HyperDash":false},{"StartTime":75007.0,"Position":389.0,"HyperDash":false},{"StartTime":75116.0,"Position":400.0,"HyperDash":false}]},{"StartTime":75298.0,"Objects":[{"StartTime":75298.0,"Position":308.0,"HyperDash":false},{"StartTime":75370.0,"Position":335.812164,"HyperDash":false},{"StartTime":75479.0,"Position":393.0,"HyperDash":false}]},{"StartTime":75662.0,"Objects":[{"StartTime":75662.0,"Position":232.0,"HyperDash":false}]},{"StartTime":75844.0,"Objects":[{"StartTime":75844.0,"Position":401.0,"HyperDash":false}]},{"StartTime":76026.0,"Objects":[{"StartTime":76026.0,"Position":224.0,"HyperDash":false},{"StartTime":76116.0,"Position":198.851242,"HyperDash":false},{"StartTime":76207.0,"Position":140.765839,"HyperDash":false},{"StartTime":76280.0,"Position":189.953156,"HyperDash":false},{"StartTime":76389.0,"Position":226.0,"HyperDash":false}]},{"StartTime":76571.0,"Objects":[{"StartTime":76571.0,"Position":312.0,"HyperDash":false}]},{"StartTime":76753.0,"Objects":[{"StartTime":76753.0,"Position":56.0,"HyperDash":false},{"StartTime":76825.0,"Position":74.0,"HyperDash":false},{"StartTime":76934.0,"Position":56.0,"HyperDash":false}]},{"StartTime":77116.0,"Objects":[{"StartTime":77116.0,"Position":140.0,"HyperDash":false}]},{"StartTime":77298.0,"Objects":[{"StartTime":77298.0,"Position":48.0,"HyperDash":false}]},{"StartTime":77480.0,"Objects":[{"StartTime":77480.0,"Position":148.0,"HyperDash":false},{"StartTime":77552.0,"Position":164.812149,"HyperDash":false},{"StartTime":77661.0,"Position":233.0,"HyperDash":false}]},{"StartTime":77844.0,"Objects":[{"StartTime":77844.0,"Position":408.0,"HyperDash":false},{"StartTime":77916.0,"Position":392.0,"HyperDash":false},{"StartTime":78025.0,"Position":408.0,"HyperDash":false}]},{"StartTime":78207.0,"Objects":[{"StartTime":78207.0,"Position":236.0,"HyperDash":false},{"StartTime":78279.0,"Position":281.812164,"HyperDash":false},{"StartTime":78388.0,"Position":321.0,"HyperDash":false}]},{"StartTime":78571.0,"Objects":[{"StartTime":78571.0,"Position":493.0,"HyperDash":false},{"StartTime":78643.0,"Position":471.187836,"HyperDash":false},{"StartTime":78752.0,"Position":408.0,"HyperDash":false}]},{"StartTime":78935.0,"Objects":[{"StartTime":78935.0,"Position":504.0,"HyperDash":false}]},{"StartTime":79117.0,"Objects":[{"StartTime":79117.0,"Position":332.0,"HyperDash":false}]},{"StartTime":79208.0,"Objects":[{"StartTime":79208.0,"Position":284.0,"HyperDash":false}]},{"StartTime":79298.0,"Objects":[{"StartTime":79298.0,"Position":236.0,"HyperDash":false},{"StartTime":79370.0,"Position":251.0,"HyperDash":false},{"StartTime":79479.0,"Position":236.0,"HyperDash":false}]},{"StartTime":79662.0,"Objects":[{"StartTime":79662.0,"Position":60.0,"HyperDash":false},{"StartTime":79734.0,"Position":52.0,"HyperDash":false},{"StartTime":79843.0,"Position":60.0,"HyperDash":false}]},{"StartTime":80026.0,"Objects":[{"StartTime":80026.0,"Position":236.0,"HyperDash":false},{"StartTime":80098.0,"Position":255.812164,"HyperDash":false},{"StartTime":80207.0,"Position":321.0,"HyperDash":false}]},{"StartTime":80389.0,"Objects":[{"StartTime":80389.0,"Position":228.0,"HyperDash":false}]},{"StartTime":80479.0,"Objects":[{"StartTime":80479.0,"Position":228.0,"HyperDash":false}]},{"StartTime":80570.0,"Objects":[{"StartTime":80570.0,"Position":228.0,"HyperDash":false}]},{"StartTime":80753.0,"Objects":[{"StartTime":80753.0,"Position":404.0,"HyperDash":false},{"StartTime":80825.0,"Position":400.0,"HyperDash":false},{"StartTime":80934.0,"Position":404.0,"HyperDash":false}]},{"StartTime":81116.0,"Objects":[{"StartTime":81116.0,"Position":227.0,"HyperDash":false},{"StartTime":81188.0,"Position":273.812164,"HyperDash":false},{"StartTime":81297.0,"Position":312.0,"HyperDash":false}]},{"StartTime":81480.0,"Objects":[{"StartTime":81480.0,"Position":404.0,"HyperDash":false},{"StartTime":81552.0,"Position":369.187836,"HyperDash":false},{"StartTime":81661.0,"Position":319.0,"HyperDash":false}]},{"StartTime":81844.0,"Objects":[{"StartTime":81844.0,"Position":133.0,"HyperDash":false},{"StartTime":81934.0,"Position":90.5,"HyperDash":false},{"StartTime":82025.0,"Position":133.0,"HyperDash":false}]},{"StartTime":82208.0,"Objects":[{"StartTime":82208.0,"Position":303.0,"HyperDash":false},{"StartTime":82280.0,"Position":269.187836,"HyperDash":false},{"StartTime":82389.0,"Position":218.0,"HyperDash":false}]},{"StartTime":82480.0,"Objects":[{"StartTime":82480.0,"Position":264.0,"HyperDash":false}]},{"StartTime":82572.0,"Objects":[{"StartTime":82572.0,"Position":313.0,"HyperDash":false},{"StartTime":82644.0,"Position":272.187836,"HyperDash":false},{"StartTime":82753.0,"Position":228.0,"HyperDash":false}]},{"StartTime":82935.0,"Objects":[{"StartTime":82935.0,"Position":48.0,"HyperDash":false},{"StartTime":83007.0,"Position":97.81215,"HyperDash":false},{"StartTime":83116.0,"Position":133.0,"HyperDash":true}]},{"StartTime":83299.0,"Objects":[{"StartTime":83299.0,"Position":392.0,"HyperDash":false},{"StartTime":83389.0,"Position":451.578522,"HyperDash":false},{"StartTime":83480.0,"Position":493.719,"HyperDash":false},{"StartTime":83553.0,"Position":512.0,"HyperDash":false},{"StartTime":83662.0,"Position":496.0,"HyperDash":false}]},{"StartTime":83753.0,"Objects":[{"StartTime":83753.0,"Position":452.0,"HyperDash":false}]},{"StartTime":83844.0,"Objects":[{"StartTime":83844.0,"Position":408.0,"HyperDash":false}]},{"StartTime":84026.0,"Objects":[{"StartTime":84026.0,"Position":324.0,"HyperDash":false},{"StartTime":84098.0,"Position":308.0,"HyperDash":false},{"StartTime":84207.0,"Position":324.0,"HyperDash":false}]},{"StartTime":84390.0,"Objects":[{"StartTime":84390.0,"Position":152.0,"HyperDash":false},{"StartTime":84480.0,"Position":152.0,"HyperDash":false}]},{"StartTime":84662.0,"Objects":[{"StartTime":84662.0,"Position":248.0,"HyperDash":false}]},{"StartTime":84753.0,"Objects":[{"StartTime":84753.0,"Position":248.0,"HyperDash":false},{"StartTime":84825.0,"Position":213.187851,"HyperDash":false},{"StartTime":84934.0,"Position":163.0,"HyperDash":false}]},{"StartTime":85117.0,"Objects":[{"StartTime":85117.0,"Position":332.0,"HyperDash":false},{"StartTime":85207.0,"Position":332.0,"HyperDash":false},{"StartTime":85298.0,"Position":332.0,"HyperDash":false}]},{"StartTime":85480.0,"Objects":[{"StartTime":85480.0,"Position":244.0,"HyperDash":false}]},{"StartTime":85662.0,"Objects":[{"StartTime":85662.0,"Position":332.0,"HyperDash":false}]},{"StartTime":85844.0,"Objects":[{"StartTime":85844.0,"Position":156.0,"HyperDash":false},{"StartTime":85916.0,"Position":105.187851,"HyperDash":false},{"StartTime":86025.0,"Position":71.0,"HyperDash":false}]},{"StartTime":86208.0,"Objects":[{"StartTime":86208.0,"Position":164.0,"HyperDash":false},{"StartTime":86280.0,"Position":185.812149,"HyperDash":false},{"StartTime":86389.0,"Position":249.0,"HyperDash":false}]},{"StartTime":86571.0,"Objects":[{"StartTime":86571.0,"Position":80.0,"HyperDash":false}]},{"StartTime":86661.0,"Objects":[{"StartTime":86661.0,"Position":122.0,"HyperDash":false}]},{"StartTime":86752.0,"Objects":[{"StartTime":86752.0,"Position":165.0,"HyperDash":false}]},{"StartTime":86935.0,"Objects":[{"StartTime":86935.0,"Position":252.0,"HyperDash":false}]},{"StartTime":87117.0,"Objects":[{"StartTime":87117.0,"Position":156.0,"HyperDash":false}]},{"StartTime":87299.0,"Objects":[{"StartTime":87299.0,"Position":328.0,"HyperDash":false},{"StartTime":87389.0,"Position":328.0,"HyperDash":false}]},{"StartTime":87662.0,"Objects":[{"StartTime":87662.0,"Position":152.0,"HyperDash":false},{"StartTime":87752.0,"Position":109.5,"HyperDash":false},{"StartTime":87843.0,"Position":152.0,"HyperDash":false}]},{"StartTime":88026.0,"Objects":[{"StartTime":88026.0,"Position":236.0,"HyperDash":false},{"StartTime":88098.0,"Position":190.187851,"HyperDash":false},{"StartTime":88207.0,"Position":151.0,"HyperDash":false}]},{"StartTime":88390.0,"Objects":[{"StartTime":88390.0,"Position":328.0,"HyperDash":false},{"StartTime":88462.0,"Position":320.0,"HyperDash":false},{"StartTime":88571.0,"Position":328.0,"HyperDash":false}]},{"StartTime":88753.0,"Objects":[{"StartTime":88753.0,"Position":152.0,"HyperDash":false},{"StartTime":88825.0,"Position":120.187851,"HyperDash":false},{"StartTime":88934.0,"Position":67.0,"HyperDash":false}]},{"StartTime":89117.0,"Objects":[{"StartTime":89117.0,"Position":324.0,"HyperDash":false},{"StartTime":89207.0,"Position":355.148743,"HyperDash":false},{"StartTime":89298.0,"Position":408.765839,"HyperDash":false},{"StartTime":89371.0,"Position":451.953156,"HyperDash":false},{"StartTime":89480.0,"Position":494.0,"HyperDash":false}]},{"StartTime":89571.0,"Objects":[{"StartTime":89571.0,"Position":452.0,"HyperDash":false}]},{"StartTime":89662.0,"Objects":[{"StartTime":89662.0,"Position":408.0,"HyperDash":false}]},{"StartTime":89844.0,"Objects":[{"StartTime":89844.0,"Position":324.0,"HyperDash":false},{"StartTime":89916.0,"Position":314.0,"HyperDash":false},{"StartTime":90025.0,"Position":324.0,"HyperDash":false}]},{"StartTime":90208.0,"Objects":[{"StartTime":90208.0,"Position":148.0,"HyperDash":false},{"StartTime":90298.0,"Position":148.0,"HyperDash":false}]},{"StartTime":90480.0,"Objects":[{"StartTime":90480.0,"Position":232.0,"HyperDash":false}]},{"StartTime":90571.0,"Objects":[{"StartTime":90571.0,"Position":284.0,"HyperDash":false},{"StartTime":90643.0,"Position":299.0,"HyperDash":false},{"StartTime":90752.0,"Position":284.0,"HyperDash":false}]},{"StartTime":90844.0,"Objects":[{"StartTime":90844.0,"Position":236.0,"HyperDash":false},{"StartTime":90916.0,"Position":193.187851,"HyperDash":false},{"StartTime":91025.0,"Position":151.0,"HyperDash":false}]},{"StartTime":91117.0,"Objects":[{"StartTime":91117.0,"Position":152.0,"HyperDash":false}]},{"StartTime":91299.0,"Objects":[{"StartTime":91299.0,"Position":236.0,"HyperDash":false}]},{"StartTime":91480.0,"Objects":[{"StartTime":91480.0,"Position":144.0,"HyperDash":false}]},{"StartTime":91662.0,"Objects":[{"StartTime":91662.0,"Position":320.0,"HyperDash":false},{"StartTime":91734.0,"Position":309.0,"HyperDash":false},{"StartTime":91843.0,"Position":320.0,"HyperDash":false}]},{"StartTime":92026.0,"Objects":[{"StartTime":92026.0,"Position":224.0,"HyperDash":false},{"StartTime":92098.0,"Position":177.187851,"HyperDash":false},{"StartTime":92207.0,"Position":139.0,"HyperDash":false}]},{"StartTime":92299.0,"Objects":[{"StartTime":92299.0,"Position":92.0,"HyperDash":false},{"StartTime":92371.0,"Position":115.812149,"HyperDash":false},{"StartTime":92480.0,"Position":177.0,"HyperDash":false}]},{"StartTime":92571.0,"Objects":[{"StartTime":92571.0,"Position":224.0,"HyperDash":false}]},{"StartTime":92753.0,"Objects":[{"StartTime":92753.0,"Position":132.0,"HyperDash":false},{"StartTime":92825.0,"Position":167.812149,"HyperDash":false},{"StartTime":92934.0,"Position":217.0,"HyperDash":false}]},{"StartTime":93117.0,"Objects":[{"StartTime":93117.0,"Position":392.0,"HyperDash":false},{"StartTime":93189.0,"Position":384.0,"HyperDash":false},{"StartTime":93298.0,"Position":392.0,"HyperDash":false}]},{"StartTime":93480.0,"Objects":[{"StartTime":93480.0,"Position":216.0,"HyperDash":false}]},{"StartTime":93570.0,"Objects":[{"StartTime":93570.0,"Position":173.0,"HyperDash":false}]},{"StartTime":93661.0,"Objects":[{"StartTime":93661.0,"Position":131.0,"HyperDash":false}]},{"StartTime":93844.0,"Objects":[{"StartTime":93844.0,"Position":224.0,"HyperDash":false}]},{"StartTime":93934.0,"Objects":[{"StartTime":93934.0,"Position":181.0,"HyperDash":false}]},{"StartTime":94025.0,"Objects":[{"StartTime":94025.0,"Position":139.0,"HyperDash":false}]},{"StartTime":94208.0,"Objects":[{"StartTime":94208.0,"Position":312.0,"HyperDash":false},{"StartTime":94280.0,"Position":363.812164,"HyperDash":false},{"StartTime":94389.0,"Position":397.0,"HyperDash":false}]},{"StartTime":94571.0,"Objects":[{"StartTime":94571.0,"Position":220.0,"HyperDash":false},{"StartTime":94643.0,"Position":192.187851,"HyperDash":false},{"StartTime":94752.0,"Position":135.0,"HyperDash":false}]},{"StartTime":94935.0,"Objects":[{"StartTime":94935.0,"Position":392.0,"HyperDash":false},{"StartTime":95007.0,"Position":440.812164,"HyperDash":false},{"StartTime":95116.0,"Position":477.0,"HyperDash":false}]},{"StartTime":95299.0,"Objects":[{"StartTime":95299.0,"Position":384.0,"HyperDash":false},{"StartTime":95371.0,"Position":389.0,"HyperDash":false},{"StartTime":95480.0,"Position":384.0,"HyperDash":false}]},{"StartTime":95662.0,"Objects":[{"StartTime":95662.0,"Position":212.0,"HyperDash":false}]},{"StartTime":95844.0,"Objects":[{"StartTime":95844.0,"Position":306.0,"HyperDash":false}]},{"StartTime":96026.0,"Objects":[{"StartTime":96026.0,"Position":477.0,"HyperDash":false},{"StartTime":96098.0,"Position":461.0,"HyperDash":false},{"StartTime":96207.0,"Position":477.0,"HyperDash":false}]},{"StartTime":96390.0,"Objects":[{"StartTime":96390.0,"Position":300.0,"HyperDash":false},{"StartTime":96462.0,"Position":249.187836,"HyperDash":false},{"StartTime":96571.0,"Position":215.0,"HyperDash":false}]},{"StartTime":96753.0,"Objects":[{"StartTime":96753.0,"Position":308.0,"HyperDash":false},{"StartTime":96825.0,"Position":320.0,"HyperDash":false},{"StartTime":96934.0,"Position":308.0,"HyperDash":false}]},{"StartTime":97117.0,"Objects":[{"StartTime":97117.0,"Position":136.0,"HyperDash":false}]},{"StartTime":97299.0,"Objects":[{"StartTime":97299.0,"Position":300.0,"HyperDash":false}]},{"StartTime":97480.0,"Objects":[{"StartTime":97480.0,"Position":128.0,"HyperDash":false},{"StartTime":97552.0,"Position":135.0,"HyperDash":false},{"StartTime":97661.0,"Position":128.0,"HyperDash":false}]},{"StartTime":97844.0,"Objects":[{"StartTime":97844.0,"Position":300.0,"HyperDash":false},{"StartTime":97916.0,"Position":248.187836,"HyperDash":false},{"StartTime":98025.0,"Position":215.0,"HyperDash":false}]},{"StartTime":98208.0,"Objects":[{"StartTime":98208.0,"Position":308.0,"HyperDash":false}]},{"StartTime":98298.0,"Objects":[{"StartTime":98298.0,"Position":308.0,"HyperDash":false}]},{"StartTime":98389.0,"Objects":[{"StartTime":98389.0,"Position":308.0,"HyperDash":false}]},{"StartTime":98571.0,"Objects":[{"StartTime":98571.0,"Position":136.0,"HyperDash":false},{"StartTime":98643.0,"Position":173.812149,"HyperDash":false},{"StartTime":98752.0,"Position":221.0,"HyperDash":false}]},{"StartTime":98935.0,"Objects":[{"StartTime":98935.0,"Position":404.0,"HyperDash":false},{"StartTime":99007.0,"Position":405.0,"HyperDash":false},{"StartTime":99116.0,"Position":404.0,"HyperDash":false}]},{"StartTime":99299.0,"Objects":[{"StartTime":99299.0,"Position":224.0,"HyperDash":false},{"StartTime":99371.0,"Position":198.187851,"HyperDash":false},{"StartTime":99480.0,"Position":139.0,"HyperDash":false}]},{"StartTime":99662.0,"Objects":[{"StartTime":99662.0,"Position":312.0,"HyperDash":false},{"StartTime":99734.0,"Position":297.0,"HyperDash":false},{"StartTime":99843.0,"Position":312.0,"HyperDash":false}]},{"StartTime":100026.0,"Objects":[{"StartTime":100026.0,"Position":220.0,"HyperDash":false}]},{"StartTime":100208.0,"Objects":[{"StartTime":100208.0,"Position":312.0,"HyperDash":false}]},{"StartTime":100390.0,"Objects":[{"StartTime":100390.0,"Position":136.0,"HyperDash":false},{"StartTime":100462.0,"Position":98.18785,"HyperDash":false},{"StartTime":100571.0,"Position":51.0,"HyperDash":true}]},{"StartTime":100753.0,"Objects":[{"StartTime":100753.0,"Position":308.0,"HyperDash":false},{"StartTime":100825.0,"Position":340.812164,"HyperDash":false},{"StartTime":100934.0,"Position":393.0,"HyperDash":false}]},{"StartTime":101117.0,"Objects":[{"StartTime":101117.0,"Position":216.0,"HyperDash":false},{"StartTime":101189.0,"Position":223.0,"HyperDash":false},{"StartTime":101298.0,"Position":216.0,"HyperDash":false}]},{"StartTime":101480.0,"Objects":[{"StartTime":101480.0,"Position":300.0,"HyperDash":false}]},{"StartTime":101662.0,"Objects":[{"StartTime":101662.0,"Position":208.0,"HyperDash":false}]},{"StartTime":101844.0,"Objects":[{"StartTime":101844.0,"Position":384.0,"HyperDash":false},{"StartTime":101916.0,"Position":372.0,"HyperDash":false},{"StartTime":102025.0,"Position":384.0,"HyperDash":false}]},{"StartTime":102208.0,"Objects":[{"StartTime":102208.0,"Position":208.0,"HyperDash":false},{"StartTime":102280.0,"Position":181.187851,"HyperDash":false},{"StartTime":102389.0,"Position":123.0,"HyperDash":false}]},{"StartTime":102571.0,"Objects":[{"StartTime":102571.0,"Position":216.0,"HyperDash":false},{"StartTime":102643.0,"Position":214.0,"HyperDash":false},{"StartTime":102752.0,"Position":216.0,"HyperDash":false}]},{"StartTime":102935.0,"Objects":[{"StartTime":102935.0,"Position":52.0,"HyperDash":false}]},{"StartTime":103117.0,"Objects":[{"StartTime":103117.0,"Position":224.0,"HyperDash":false}]},{"StartTime":103299.0,"Objects":[{"StartTime":103299.0,"Position":44.0,"HyperDash":false},{"StartTime":103371.0,"Position":43.0,"HyperDash":false},{"StartTime":103480.0,"Position":44.0,"HyperDash":false}]},{"StartTime":103662.0,"Objects":[{"StartTime":103662.0,"Position":136.0,"HyperDash":false},{"StartTime":103734.0,"Position":162.812149,"HyperDash":false},{"StartTime":103843.0,"Position":221.0,"HyperDash":false}]},{"StartTime":103935.0,"Objects":[{"StartTime":103935.0,"Position":268.0,"HyperDash":false}]},{"StartTime":104026.0,"Objects":[{"StartTime":104026.0,"Position":316.0,"HyperDash":false},{"StartTime":104098.0,"Position":314.0,"HyperDash":false},{"StartTime":104207.0,"Position":316.0,"HyperDash":false}]},{"StartTime":104390.0,"Objects":[{"StartTime":104390.0,"Position":140.0,"HyperDash":false},{"StartTime":104462.0,"Position":188.812149,"HyperDash":false},{"StartTime":104571.0,"Position":225.0,"HyperDash":false}]},{"StartTime":104753.0,"Objects":[{"StartTime":104753.0,"Position":400.0,"HyperDash":false},{"StartTime":104825.0,"Position":417.0,"HyperDash":false},{"StartTime":104934.0,"Position":400.0,"HyperDash":false}]},{"StartTime":105117.0,"Objects":[{"StartTime":105117.0,"Position":224.0,"HyperDash":false}]},{"StartTime":105207.0,"Objects":[{"StartTime":105207.0,"Position":181.0,"HyperDash":false}]},{"StartTime":105298.0,"Objects":[{"StartTime":105298.0,"Position":139.0,"HyperDash":false}]},{"StartTime":105480.0,"Objects":[{"StartTime":105480.0,"Position":309.0,"HyperDash":false},{"StartTime":105552.0,"Position":259.187836,"HyperDash":false},{"StartTime":105661.0,"Position":224.0,"HyperDash":false}]},{"StartTime":105844.0,"Objects":[{"StartTime":105844.0,"Position":128.0,"HyperDash":false}]},{"StartTime":106026.0,"Objects":[{"StartTime":106026.0,"Position":216.0,"HyperDash":false}]},{"StartTime":106208.0,"Objects":[{"StartTime":106208.0,"Position":393.0,"HyperDash":false},{"StartTime":106280.0,"Position":408.812164,"HyperDash":false},{"StartTime":106389.0,"Position":478.0,"HyperDash":true}]},{"StartTime":106571.0,"Objects":[{"StartTime":106571.0,"Position":216.0,"HyperDash":false},{"StartTime":106643.0,"Position":194.187851,"HyperDash":false},{"StartTime":106752.0,"Position":131.0,"HyperDash":false}]},{"StartTime":106844.0,"Objects":[{"StartTime":106844.0,"Position":84.0,"HyperDash":false}]},{"StartTime":106935.0,"Objects":[{"StartTime":106935.0,"Position":131.0,"HyperDash":false},{"StartTime":107007.0,"Position":171.812149,"HyperDash":false},{"StartTime":107116.0,"Position":216.0,"HyperDash":false}]},{"StartTime":107299.0,"Objects":[{"StartTime":107299.0,"Position":312.0,"HyperDash":false}]},{"StartTime":107480.0,"Objects":[{"StartTime":107480.0,"Position":212.0,"HyperDash":false}]},{"StartTime":107662.0,"Objects":[{"StartTime":107662.0,"Position":392.0,"HyperDash":false},{"StartTime":107734.0,"Position":372.0,"HyperDash":false},{"StartTime":107843.0,"Position":392.0,"HyperDash":false}]},{"StartTime":108026.0,"Objects":[{"StartTime":108026.0,"Position":136.0,"HyperDash":false},{"StartTime":108098.0,"Position":101.187851,"HyperDash":false},{"StartTime":108207.0,"Position":51.0,"HyperDash":false}]},{"StartTime":108390.0,"Objects":[{"StartTime":108390.0,"Position":144.0,"HyperDash":false},{"StartTime":108462.0,"Position":129.0,"HyperDash":false},{"StartTime":108571.0,"Position":144.0,"HyperDash":false}]},{"StartTime":108753.0,"Objects":[{"StartTime":108753.0,"Position":304.0,"HyperDash":false}]},{"StartTime":108935.0,"Objects":[{"StartTime":108935.0,"Position":140.0,"HyperDash":false}]},{"StartTime":109117.0,"Objects":[{"StartTime":109117.0,"Position":312.0,"HyperDash":false},{"StartTime":109189.0,"Position":293.0,"HyperDash":false},{"StartTime":109298.0,"Position":312.0,"HyperDash":false}]},{"StartTime":109480.0,"Objects":[{"StartTime":109480.0,"Position":56.0,"HyperDash":false},{"StartTime":109552.0,"Position":60.0,"HyperDash":false},{"StartTime":109661.0,"Position":56.0,"HyperDash":false}]},{"StartTime":109844.0,"Objects":[{"StartTime":109844.0,"Position":140.0,"HyperDash":false}]},{"StartTime":109934.0,"Objects":[{"StartTime":109934.0,"Position":182.0,"HyperDash":false}]},{"StartTime":110025.0,"Objects":[{"StartTime":110025.0,"Position":225.0,"HyperDash":false}]},{"StartTime":110208.0,"Objects":[{"StartTime":110208.0,"Position":56.0,"HyperDash":false}]},{"StartTime":110390.0,"Objects":[{"StartTime":110390.0,"Position":152.0,"HyperDash":false}]},{"StartTime":110571.0,"Objects":[{"StartTime":110571.0,"Position":52.0,"HyperDash":false},{"StartTime":110643.0,"Position":54.0,"HyperDash":false},{"StartTime":110752.0,"Position":52.0,"HyperDash":true}]},{"StartTime":110935.0,"Objects":[{"StartTime":110935.0,"Position":312.0,"HyperDash":false},{"StartTime":111007.0,"Position":362.812164,"HyperDash":false},{"StartTime":111116.0,"Position":397.0,"HyperDash":false}]},{"StartTime":111299.0,"Objects":[{"StartTime":111299.0,"Position":304.0,"HyperDash":false}]},{"StartTime":111480.0,"Objects":[{"StartTime":111480.0,"Position":404.0,"HyperDash":false}]},{"StartTime":111662.0,"Objects":[{"StartTime":111662.0,"Position":312.0,"HyperDash":false}]},{"StartTime":111752.0,"Objects":[{"StartTime":111752.0,"Position":269.0,"HyperDash":false}]},{"StartTime":111843.0,"Objects":[{"StartTime":111843.0,"Position":227.0,"HyperDash":false}]},{"StartTime":112026.0,"Objects":[{"StartTime":112026.0,"Position":328.0,"HyperDash":false},{"StartTime":112098.0,"Position":339.0,"HyperDash":false},{"StartTime":112207.0,"Position":328.0,"HyperDash":true}]},{"StartTime":112390.0,"Objects":[{"StartTime":112390.0,"Position":68.0,"HyperDash":false},{"StartTime":112462.0,"Position":70.0,"HyperDash":false},{"StartTime":112571.0,"Position":68.0,"HyperDash":false}]},{"StartTime":112753.0,"Objects":[{"StartTime":112753.0,"Position":160.0,"HyperDash":false},{"StartTime":112825.0,"Position":201.812149,"HyperDash":false},{"StartTime":112934.0,"Position":245.0,"HyperDash":false}]},{"StartTime":113117.0,"Objects":[{"StartTime":113117.0,"Position":420.0,"HyperDash":false},{"StartTime":113189.0,"Position":413.0,"HyperDash":false},{"StartTime":113298.0,"Position":420.0,"HyperDash":false}]},{"StartTime":113480.0,"Objects":[{"StartTime":113480.0,"Position":328.0,"HyperDash":false}]},{"StartTime":113570.0,"Objects":[{"StartTime":113570.0,"Position":285.0,"HyperDash":false}]},{"StartTime":113661.0,"Objects":[{"StartTime":113661.0,"Position":243.0,"HyperDash":false}]},{"StartTime":113844.0,"Objects":[{"StartTime":113844.0,"Position":492.0,"HyperDash":false},{"StartTime":113916.0,"Position":493.0,"HyperDash":false},{"StartTime":114025.0,"Position":492.0,"HyperDash":false}]},{"StartTime":114208.0,"Objects":[{"StartTime":114208.0,"Position":396.0,"HyperDash":false},{"StartTime":114280.0,"Position":346.187836,"HyperDash":false},{"StartTime":114389.0,"Position":311.0,"HyperDash":false}]},{"StartTime":114571.0,"Objects":[{"StartTime":114571.0,"Position":140.0,"HyperDash":false}]},{"StartTime":114753.0,"Objects":[{"StartTime":114753.0,"Position":311.0,"HyperDash":false}]},{"StartTime":114935.0,"Objects":[{"StartTime":114935.0,"Position":140.0,"HyperDash":false},{"StartTime":115007.0,"Position":121.0,"HyperDash":false},{"StartTime":115116.0,"Position":140.0,"HyperDash":false}]},{"StartTime":115299.0,"Objects":[{"StartTime":115299.0,"Position":396.0,"HyperDash":false},{"StartTime":115371.0,"Position":409.812164,"HyperDash":false},{"StartTime":115480.0,"Position":481.0,"HyperDash":false}]},{"StartTime":115662.0,"Objects":[{"StartTime":115662.0,"Position":308.0,"HyperDash":false},{"StartTime":115734.0,"Position":311.0,"HyperDash":false},{"StartTime":115843.0,"Position":308.0,"HyperDash":false}]},{"StartTime":116026.0,"Objects":[{"StartTime":116026.0,"Position":136.0,"HyperDash":false}]},{"StartTime":116208.0,"Objects":[{"StartTime":116208.0,"Position":228.0,"HyperDash":false}]},{"StartTime":116390.0,"Objects":[{"StartTime":116390.0,"Position":56.0,"HyperDash":false},{"StartTime":116462.0,"Position":56.0,"HyperDash":false},{"StartTime":116571.0,"Position":56.0,"HyperDash":false}]},{"StartTime":116753.0,"Objects":[{"StartTime":116753.0,"Position":312.0,"HyperDash":false},{"StartTime":116825.0,"Position":322.0,"HyperDash":false},{"StartTime":116934.0,"Position":312.0,"HyperDash":false}]},{"StartTime":117117.0,"Objects":[{"StartTime":117117.0,"Position":484.0,"HyperDash":false},{"StartTime":117207.0,"Position":484.0,"HyperDash":false},{"StartTime":117298.0,"Position":484.0,"HyperDash":false}]},{"StartTime":117480.0,"Objects":[{"StartTime":117480.0,"Position":392.0,"HyperDash":false}]},{"StartTime":117662.0,"Objects":[{"StartTime":117662.0,"Position":476.0,"HyperDash":false}]},{"StartTime":117844.0,"Objects":[{"StartTime":117844.0,"Position":304.0,"HyperDash":false}]},{"StartTime":117934.0,"Objects":[{"StartTime":117934.0,"Position":262.0,"HyperDash":false}]},{"StartTime":118025.0,"Objects":[{"StartTime":118025.0,"Position":219.0,"HyperDash":false}]},{"StartTime":118208.0,"Objects":[{"StartTime":118208.0,"Position":476.0,"HyperDash":false}]},{"StartTime":118299.0,"Objects":[{"StartTime":118299.0,"Position":476.0,"HyperDash":false}]},{"StartTime":118390.0,"Objects":[{"StartTime":118390.0,"Position":432.0,"HyperDash":false}]},{"StartTime":118571.0,"Objects":[{"StartTime":118571.0,"Position":260.0,"HyperDash":false}]},{"StartTime":118662.0,"Objects":[{"StartTime":118662.0,"Position":260.0,"HyperDash":false}]},{"StartTime":118753.0,"Objects":[{"StartTime":118753.0,"Position":260.0,"HyperDash":false}]},{"StartTime":118935.0,"Objects":[{"StartTime":118935.0,"Position":88.0,"HyperDash":false}]},{"StartTime":119026.0,"Objects":[{"StartTime":119026.0,"Position":88.0,"HyperDash":false}]},{"StartTime":119117.0,"Objects":[{"StartTime":119117.0,"Position":132.0,"HyperDash":false}]},{"StartTime":119299.0,"Objects":[{"StartTime":119299.0,"Position":304.0,"HyperDash":false},{"StartTime":119371.0,"Position":319.812164,"HyperDash":false},{"StartTime":119480.0,"Position":389.0,"HyperDash":true}]},{"StartTime":119662.0,"Objects":[{"StartTime":119662.0,"Position":112.0,"HyperDash":false}]},{"StartTime":120026.0,"Objects":[{"StartTime":120026.0,"Position":221.0,"HyperDash":false},{"StartTime":120111.0,"Position":407.0,"HyperDash":false},{"StartTime":120196.0,"Position":287.0,"HyperDash":false},{"StartTime":120281.0,"Position":135.0,"HyperDash":false},{"StartTime":120366.0,"Position":437.0,"HyperDash":false},{"StartTime":120452.0,"Position":289.0,"HyperDash":false},{"StartTime":120537.0,"Position":464.0,"HyperDash":false},{"StartTime":120622.0,"Position":36.0,"HyperDash":false},{"StartTime":120707.0,"Position":378.0,"HyperDash":false},{"StartTime":120792.0,"Position":297.0,"HyperDash":false},{"StartTime":120878.0,"Position":418.0,"HyperDash":false},{"StartTime":120963.0,"Position":329.0,"HyperDash":false},{"StartTime":121048.0,"Position":338.0,"HyperDash":false},{"StartTime":121133.0,"Position":394.0,"HyperDash":false},{"StartTime":121219.0,"Position":40.0,"HyperDash":false},{"StartTime":121304.0,"Position":13.0,"HyperDash":false},{"StartTime":121389.0,"Position":80.0,"HyperDash":false},{"StartTime":121474.0,"Position":138.0,"HyperDash":false},{"StartTime":121559.0,"Position":311.0,"HyperDash":false},{"StartTime":121645.0,"Position":216.0,"HyperDash":false},{"StartTime":121730.0,"Position":310.0,"HyperDash":false},{"StartTime":121815.0,"Position":397.0,"HyperDash":false},{"StartTime":121900.0,"Position":214.0,"HyperDash":false},{"StartTime":121986.0,"Position":505.0,"HyperDash":false},{"StartTime":122071.0,"Position":173.0,"HyperDash":false},{"StartTime":122156.0,"Position":295.0,"HyperDash":false},{"StartTime":122241.0,"Position":199.0,"HyperDash":false},{"StartTime":122326.0,"Position":494.0,"HyperDash":false},{"StartTime":122412.0,"Position":293.0,"HyperDash":false},{"StartTime":122497.0,"Position":115.0,"HyperDash":false},{"StartTime":122582.0,"Position":412.0,"HyperDash":false},{"StartTime":122667.0,"Position":506.0,"HyperDash":false},{"StartTime":122753.0,"Position":293.0,"HyperDash":false},{"StartTime":122838.0,"Position":346.0,"HyperDash":false},{"StartTime":122923.0,"Position":117.0,"HyperDash":false},{"StartTime":123008.0,"Position":285.0,"HyperDash":false},{"StartTime":123093.0,"Position":17.0,"HyperDash":false},{"StartTime":123179.0,"Position":238.0,"HyperDash":false},{"StartTime":123264.0,"Position":222.0,"HyperDash":false},{"StartTime":123349.0,"Position":450.0,"HyperDash":false},{"StartTime":123434.0,"Position":67.0,"HyperDash":false},{"StartTime":123519.0,"Position":219.0,"HyperDash":false},{"StartTime":123605.0,"Position":307.0,"HyperDash":false},{"StartTime":123690.0,"Position":367.0,"HyperDash":false},{"StartTime":123775.0,"Position":412.0,"HyperDash":false},{"StartTime":123860.0,"Position":413.0,"HyperDash":false},{"StartTime":123946.0,"Position":143.0,"HyperDash":false},{"StartTime":124031.0,"Position":339.0,"HyperDash":false},{"StartTime":124116.0,"Position":342.0,"HyperDash":false},{"StartTime":124201.0,"Position":249.0,"HyperDash":false},{"StartTime":124286.0,"Position":235.0,"HyperDash":false},{"StartTime":124372.0,"Position":323.0,"HyperDash":false},{"StartTime":124457.0,"Position":365.0,"HyperDash":false},{"StartTime":124542.0,"Position":74.0,"HyperDash":false},{"StartTime":124627.0,"Position":281.0,"HyperDash":false},{"StartTime":124713.0,"Position":398.0,"HyperDash":false},{"StartTime":124798.0,"Position":335.0,"HyperDash":false},{"StartTime":124883.0,"Position":388.0,"HyperDash":false},{"StartTime":124968.0,"Position":228.0,"HyperDash":false},{"StartTime":125053.0,"Position":323.0,"HyperDash":false},{"StartTime":125139.0,"Position":441.0,"HyperDash":false},{"StartTime":125224.0,"Position":442.0,"HyperDash":false},{"StartTime":125309.0,"Position":278.0,"HyperDash":false},{"StartTime":125394.0,"Position":90.0,"HyperDash":false},{"StartTime":125480.0,"Position":409.0,"HyperDash":false}]},{"StartTime":131299.0,"Objects":[{"StartTime":131299.0,"Position":296.0,"HyperDash":false},{"StartTime":131389.0,"Position":305.0,"HyperDash":false},{"StartTime":131480.0,"Position":296.0,"HyperDash":false},{"StartTime":131553.0,"Position":309.0,"HyperDash":false},{"StartTime":131662.0,"Position":296.0,"HyperDash":false}]},{"StartTime":132026.0,"Objects":[{"StartTime":132026.0,"Position":152.0,"HyperDash":false}]},{"StartTime":132208.0,"Objects":[{"StartTime":132208.0,"Position":244.0,"HyperDash":false}]},{"StartTime":132390.0,"Objects":[{"StartTime":132390.0,"Position":336.0,"HyperDash":false}]},{"StartTime":132571.0,"Objects":[{"StartTime":132571.0,"Position":244.0,"HyperDash":false}]},{"StartTime":132753.0,"Objects":[{"StartTime":132753.0,"Position":416.0,"HyperDash":false},{"StartTime":132843.0,"Position":402.0,"HyperDash":false},{"StartTime":132934.0,"Position":416.0,"HyperDash":false},{"StartTime":133025.0,"Position":411.0,"HyperDash":false},{"StartTime":133116.0,"Position":416.0,"HyperDash":false},{"StartTime":133207.0,"Position":416.0,"HyperDash":false},{"StartTime":133298.0,"Position":416.0,"HyperDash":false},{"StartTime":133371.0,"Position":427.0,"HyperDash":false},{"StartTime":133480.0,"Position":416.0,"HyperDash":false}]},{"StartTime":133844.0,"Objects":[{"StartTime":133844.0,"Position":280.0,"HyperDash":false}]},{"StartTime":134026.0,"Objects":[{"StartTime":134026.0,"Position":188.0,"HyperDash":false}]},{"StartTime":134208.0,"Objects":[{"StartTime":134208.0,"Position":16.0,"HyperDash":false},{"StartTime":134298.0,"Position":1.0,"HyperDash":false},{"StartTime":134389.0,"Position":16.0,"HyperDash":false},{"StartTime":134462.0,"Position":9.0,"HyperDash":false},{"StartTime":134571.0,"Position":16.0,"HyperDash":false}]},{"StartTime":134935.0,"Objects":[{"StartTime":134935.0,"Position":176.0,"HyperDash":false}]},{"StartTime":135299.0,"Objects":[{"StartTime":135299.0,"Position":32.0,"HyperDash":false}]},{"StartTime":135662.0,"Objects":[{"StartTime":135662.0,"Position":272.0,"HyperDash":false},{"StartTime":135752.0,"Position":255.0,"HyperDash":false},{"StartTime":135843.0,"Position":272.0,"HyperDash":false},{"StartTime":135916.0,"Position":286.0,"HyperDash":false},{"StartTime":136025.0,"Position":272.0,"HyperDash":false}]},{"StartTime":136390.0,"Objects":[{"StartTime":136390.0,"Position":428.0,"HyperDash":false},{"StartTime":136480.0,"Position":429.0,"HyperDash":false},{"StartTime":136571.0,"Position":428.0,"HyperDash":false},{"StartTime":136644.0,"Position":433.0,"HyperDash":false},{"StartTime":136753.0,"Position":428.0,"HyperDash":false}]},{"StartTime":137117.0,"Objects":[{"StartTime":137117.0,"Position":132.0,"HyperDash":false},{"StartTime":137207.0,"Position":168.09079,"HyperDash":false},{"StartTime":137298.0,"Position":216.649246,"HyperDash":false},{"StartTime":137389.0,"Position":265.2077,"HyperDash":false},{"StartTime":137480.0,"Position":302.0,"HyperDash":false},{"StartTime":137571.0,"Position":256.675385,"HyperDash":false},{"StartTime":137662.0,"Position":217.116913,"HyperDash":false},{"StartTime":137735.0,"Position":187.976624,"HyperDash":false},{"StartTime":137844.0,"Position":132.0,"HyperDash":false}]},{"StartTime":138571.0,"Objects":[{"StartTime":138571.0,"Position":336.0,"HyperDash":false},{"StartTime":138661.0,"Position":321.0,"HyperDash":false},{"StartTime":138752.0,"Position":336.0,"HyperDash":false},{"StartTime":138825.0,"Position":328.0,"HyperDash":false},{"StartTime":138934.0,"Position":336.0,"HyperDash":false}]},{"StartTime":139117.0,"Objects":[{"StartTime":139117.0,"Position":240.0,"HyperDash":false}]},{"StartTime":139299.0,"Objects":[{"StartTime":139299.0,"Position":336.0,"HyperDash":false}]},{"StartTime":139662.0,"Objects":[{"StartTime":139662.0,"Position":480.0,"HyperDash":false}]},{"StartTime":139844.0,"Objects":[{"StartTime":139844.0,"Position":388.0,"HyperDash":false}]},{"StartTime":140026.0,"Objects":[{"StartTime":140026.0,"Position":212.0,"HyperDash":false},{"StartTime":140116.0,"Position":200.0,"HyperDash":false},{"StartTime":140207.0,"Position":212.0,"HyperDash":false},{"StartTime":140298.0,"Position":229.0,"HyperDash":false},{"StartTime":140389.0,"Position":212.0,"HyperDash":false},{"StartTime":140480.0,"Position":203.0,"HyperDash":false},{"StartTime":140571.0,"Position":212.0,"HyperDash":false},{"StartTime":140644.0,"Position":211.0,"HyperDash":false},{"StartTime":140753.0,"Position":212.0,"HyperDash":false}]},{"StartTime":141480.0,"Objects":[{"StartTime":141480.0,"Position":448.0,"HyperDash":false},{"StartTime":141570.0,"Position":415.636353,"HyperDash":false},{"StartTime":141661.0,"Position":354.5,"HyperDash":false},{"StartTime":141734.0,"Position":391.84848,"HyperDash":false},{"StartTime":141843.0,"Position":448.0,"HyperDash":false}]},{"StartTime":142208.0,"Objects":[{"StartTime":142208.0,"Position":244.0,"HyperDash":false}]},{"StartTime":142390.0,"Objects":[{"StartTime":142390.0,"Position":348.0,"HyperDash":false}]},{"StartTime":142571.0,"Objects":[{"StartTime":142571.0,"Position":448.0,"HyperDash":false}]},{"StartTime":142935.0,"Objects":[{"StartTime":142935.0,"Position":152.0,"HyperDash":false},{"StartTime":143025.0,"Position":137.0,"HyperDash":false},{"StartTime":143116.0,"Position":152.0,"HyperDash":false},{"StartTime":143189.0,"Position":168.0,"HyperDash":false},{"StartTime":143298.0,"Position":152.0,"HyperDash":false}]},{"StartTime":143480.0,"Objects":[{"StartTime":143480.0,"Position":236.0,"HyperDash":false}]},{"StartTime":143662.0,"Objects":[{"StartTime":143662.0,"Position":144.0,"HyperDash":false},{"StartTime":143752.0,"Position":93.85124,"HyperDash":false},{"StartTime":143843.0,"Position":59.0,"HyperDash":false},{"StartTime":143916.0,"Position":76.95316,"HyperDash":false},{"StartTime":144025.0,"Position":144.0,"HyperDash":false}]},{"StartTime":144390.0,"Objects":[{"StartTime":144390.0,"Position":316.0,"HyperDash":false}]},{"StartTime":144571.0,"Objects":[{"StartTime":144571.0,"Position":232.0,"HyperDash":false}]},{"StartTime":144753.0,"Objects":[{"StartTime":144753.0,"Position":148.0,"HyperDash":false}]},{"StartTime":145117.0,"Objects":[{"StartTime":145117.0,"Position":316.0,"HyperDash":false},{"StartTime":145207.0,"Position":275.851257,"HyperDash":false},{"StartTime":145298.0,"Position":231.0,"HyperDash":false},{"StartTime":145371.0,"Position":279.953156,"HyperDash":false},{"StartTime":145480.0,"Position":316.0,"HyperDash":false}]},{"StartTime":145844.0,"Objects":[{"StartTime":145844.0,"Position":144.0,"HyperDash":false},{"StartTime":145916.0,"Position":147.0,"HyperDash":false},{"StartTime":146025.0,"Position":144.0,"HyperDash":false}]},{"StartTime":146208.0,"Objects":[{"StartTime":146208.0,"Position":228.0,"HyperDash":false}]},{"StartTime":146571.0,"Objects":[{"StartTime":146571.0,"Position":59.0,"HyperDash":false},{"StartTime":146661.0,"Position":108.148758,"HyperDash":false},{"StartTime":146752.0,"Position":144.0,"HyperDash":false},{"StartTime":146825.0,"Position":95.04684,"HyperDash":false},{"StartTime":146934.0,"Position":59.0,"HyperDash":false}]},{"StartTime":147299.0,"Objects":[{"StartTime":147299.0,"Position":228.0,"HyperDash":false},{"StartTime":147371.0,"Position":264.812164,"HyperDash":false},{"StartTime":147480.0,"Position":313.0,"HyperDash":false}]},{"StartTime":147662.0,"Objects":[{"StartTime":147662.0,"Position":220.0,"HyperDash":false},{"StartTime":147734.0,"Position":215.0,"HyperDash":false},{"StartTime":147843.0,"Position":220.0,"HyperDash":false}]},{"StartTime":148026.0,"Objects":[{"StartTime":148026.0,"Position":313.0,"HyperDash":false},{"StartTime":148098.0,"Position":313.0,"HyperDash":false},{"StartTime":148207.0,"Position":313.0,"HyperDash":false}]},{"StartTime":148390.0,"Objects":[{"StartTime":148390.0,"Position":228.0,"HyperDash":false}]},{"StartTime":148571.0,"Objects":[{"StartTime":148571.0,"Position":320.0,"HyperDash":false}]},{"StartTime":148753.0,"Objects":[{"StartTime":148753.0,"Position":64.0,"HyperDash":false},{"StartTime":148825.0,"Position":82.0,"HyperDash":false},{"StartTime":148934.0,"Position":64.0,"HyperDash":false}]},{"StartTime":149117.0,"Objects":[{"StartTime":149117.0,"Position":152.0,"HyperDash":false},{"StartTime":149189.0,"Position":148.0,"HyperDash":false},{"StartTime":149298.0,"Position":152.0,"HyperDash":false}]},{"StartTime":149480.0,"Objects":[{"StartTime":149480.0,"Position":328.0,"HyperDash":false}]},{"StartTime":149844.0,"Objects":[{"StartTime":149844.0,"Position":184.0,"HyperDash":false},{"StartTime":149916.0,"Position":215.812149,"HyperDash":false},{"StartTime":150025.0,"Position":269.0,"HyperDash":false}]},{"StartTime":150208.0,"Objects":[{"StartTime":150208.0,"Position":356.0,"HyperDash":false}]},{"StartTime":150571.0,"Objects":[{"StartTime":150571.0,"Position":204.0,"HyperDash":false},{"StartTime":150643.0,"Position":221.0,"HyperDash":false},{"StartTime":150752.0,"Position":204.0,"HyperDash":false}]},{"StartTime":150935.0,"Objects":[{"StartTime":150935.0,"Position":28.0,"HyperDash":false}]},{"StartTime":151299.0,"Objects":[{"StartTime":151299.0,"Position":172.0,"HyperDash":false},{"StartTime":151371.0,"Position":221.812149,"HyperDash":false},{"StartTime":151480.0,"Position":257.0,"HyperDash":false}]},{"StartTime":151662.0,"Objects":[{"StartTime":151662.0,"Position":164.0,"HyperDash":false},{"StartTime":151734.0,"Position":168.0,"HyperDash":false},{"StartTime":151843.0,"Position":164.0,"HyperDash":false}]},{"StartTime":152026.0,"Objects":[{"StartTime":152026.0,"Position":257.0,"HyperDash":false},{"StartTime":152098.0,"Position":274.0,"HyperDash":false},{"StartTime":152207.0,"Position":257.0,"HyperDash":false}]},{"StartTime":152390.0,"Objects":[{"StartTime":152390.0,"Position":432.0,"HyperDash":false}]},{"StartTime":152753.0,"Objects":[{"StartTime":152753.0,"Position":288.0,"HyperDash":false},{"StartTime":152825.0,"Position":271.187866,"HyperDash":false},{"StartTime":152934.0,"Position":203.0,"HyperDash":false}]},{"StartTime":153117.0,"Objects":[{"StartTime":153117.0,"Position":380.0,"HyperDash":false},{"StartTime":153189.0,"Position":381.0,"HyperDash":false},{"StartTime":153298.0,"Position":380.0,"HyperDash":false}]},{"StartTime":153480.0,"Objects":[{"StartTime":153480.0,"Position":288.0,"HyperDash":false},{"StartTime":153552.0,"Position":301.0,"HyperDash":false},{"StartTime":153661.0,"Position":288.0,"HyperDash":false}]},{"StartTime":153844.0,"Objects":[{"StartTime":153844.0,"Position":112.0,"HyperDash":false},{"StartTime":153916.0,"Position":121.0,"HyperDash":false},{"StartTime":154025.0,"Position":112.0,"HyperDash":false}]},{"StartTime":154208.0,"Objects":[{"StartTime":154208.0,"Position":203.0,"HyperDash":false},{"StartTime":154280.0,"Position":235.812149,"HyperDash":false},{"StartTime":154389.0,"Position":288.0,"HyperDash":false}]},{"StartTime":154571.0,"Objects":[{"StartTime":154571.0,"Position":32.0,"HyperDash":false},{"StartTime":154661.0,"Position":45.0,"HyperDash":false},{"StartTime":154752.0,"Position":32.0,"HyperDash":false},{"StartTime":154825.0,"Position":23.0,"HyperDash":false},{"StartTime":154934.0,"Position":32.0,"HyperDash":false}]},{"StartTime":155299.0,"Objects":[{"StartTime":155299.0,"Position":216.0,"HyperDash":false}]},{"StartTime":155480.0,"Objects":[{"StartTime":155480.0,"Position":124.0,"HyperDash":false}]},{"StartTime":155662.0,"Objects":[{"StartTime":155662.0,"Position":32.0,"HyperDash":false}]},{"StartTime":156026.0,"Objects":[{"StartTime":156026.0,"Position":216.0,"HyperDash":false},{"StartTime":156098.0,"Position":237.803421,"HyperDash":false},{"StartTime":156207.0,"Position":300.978058,"HyperDash":false}]},{"StartTime":156390.0,"Objects":[{"StartTime":156390.0,"Position":300.0,"HyperDash":false}]},{"StartTime":156753.0,"Objects":[{"StartTime":156753.0,"Position":132.0,"HyperDash":false},{"StartTime":156843.0,"Position":176.148758,"HyperDash":false},{"StartTime":156934.0,"Position":217.0,"HyperDash":false},{"StartTime":157007.0,"Position":167.046844,"HyperDash":false},{"StartTime":157116.0,"Position":132.0,"HyperDash":false}]},{"StartTime":157299.0,"Objects":[{"StartTime":157299.0,"Position":48.0,"HyperDash":false}]},{"StartTime":157480.0,"Objects":[{"StartTime":157480.0,"Position":140.0,"HyperDash":false},{"StartTime":157552.0,"Position":145.0,"HyperDash":false},{"StartTime":157661.0,"Position":140.0,"HyperDash":false}]},{"StartTime":157844.0,"Objects":[{"StartTime":157844.0,"Position":236.0,"HyperDash":false},{"StartTime":157916.0,"Position":252.0,"HyperDash":false},{"StartTime":158025.0,"Position":236.0,"HyperDash":false}]},{"StartTime":158208.0,"Objects":[{"StartTime":158208.0,"Position":412.0,"HyperDash":false},{"StartTime":158298.0,"Position":434.148743,"HyperDash":false},{"StartTime":158389.0,"Position":497.0,"HyperDash":false},{"StartTime":158462.0,"Position":464.046844,"HyperDash":false},{"StartTime":158571.0,"Position":412.0,"HyperDash":false}]},{"StartTime":158935.0,"Objects":[{"StartTime":158935.0,"Position":268.0,"HyperDash":false}]},{"StartTime":159117.0,"Objects":[{"StartTime":159117.0,"Position":344.0,"HyperDash":false}]},{"StartTime":159299.0,"Objects":[{"StartTime":159299.0,"Position":420.0,"HyperDash":false}]},{"StartTime":159480.0,"Objects":[{"StartTime":159480.0,"Position":496.0,"HyperDash":false}]},{"StartTime":159662.0,"Objects":[{"StartTime":159662.0,"Position":412.0,"HyperDash":false},{"StartTime":159734.0,"Position":448.812164,"HyperDash":false},{"StartTime":159843.0,"Position":497.0,"HyperDash":false}]},{"StartTime":160026.0,"Objects":[{"StartTime":160026.0,"Position":324.0,"HyperDash":false},{"StartTime":160098.0,"Position":341.0,"HyperDash":false},{"StartTime":160207.0,"Position":324.0,"HyperDash":false}]},{"StartTime":160390.0,"Objects":[{"StartTime":160390.0,"Position":68.0,"HyperDash":false},{"StartTime":160462.0,"Position":75.0,"HyperDash":false},{"StartTime":160571.0,"Position":68.0,"HyperDash":false}]},{"StartTime":160753.0,"Objects":[{"StartTime":160753.0,"Position":152.0,"HyperDash":false},{"StartTime":160825.0,"Position":187.812149,"HyperDash":false},{"StartTime":160934.0,"Position":237.0,"HyperDash":false}]},{"StartTime":161117.0,"Objects":[{"StartTime":161117.0,"Position":409.0,"HyperDash":false},{"StartTime":161189.0,"Position":409.0,"HyperDash":false},{"StartTime":161298.0,"Position":409.0,"HyperDash":false}]},{"StartTime":161480.0,"Objects":[{"StartTime":161480.0,"Position":324.0,"HyperDash":false},{"StartTime":161552.0,"Position":355.812164,"HyperDash":false},{"StartTime":161661.0,"Position":409.0,"HyperDash":false}]},{"StartTime":161844.0,"Objects":[{"StartTime":161844.0,"Position":313.0,"HyperDash":false},{"StartTime":161916.0,"Position":320.0,"HyperDash":false},{"StartTime":162025.0,"Position":313.0,"HyperDash":false}]},{"StartTime":162208.0,"Objects":[{"StartTime":162208.0,"Position":140.0,"HyperDash":false},{"StartTime":162280.0,"Position":128.0,"HyperDash":false},{"StartTime":162389.0,"Position":140.0,"HyperDash":false}]},{"StartTime":162480.0,"Objects":[{"StartTime":162480.0,"Position":184.0,"HyperDash":false}]},{"StartTime":162571.0,"Objects":[{"StartTime":162571.0,"Position":228.0,"HyperDash":false},{"StartTime":162643.0,"Position":255.812164,"HyperDash":false},{"StartTime":162752.0,"Position":313.0,"HyperDash":false}]},{"StartTime":162935.0,"Objects":[{"StartTime":162935.0,"Position":400.0,"HyperDash":false},{"StartTime":163007.0,"Position":417.0,"HyperDash":false},{"StartTime":163116.0,"Position":400.0,"HyperDash":false}]},{"StartTime":163299.0,"Objects":[{"StartTime":163299.0,"Position":217.0,"HyperDash":false},{"StartTime":163367.0,"Position":455.0,"HyperDash":false},{"StartTime":163435.0,"Position":229.0,"HyperDash":false},{"StartTime":163503.0,"Position":51.0,"HyperDash":false},{"StartTime":163571.0,"Position":199.0,"HyperDash":false},{"StartTime":163639.0,"Position":208.0,"HyperDash":false},{"StartTime":163707.0,"Position":173.0,"HyperDash":false},{"StartTime":163775.0,"Position":367.0,"HyperDash":false},{"StartTime":163844.0,"Position":193.0,"HyperDash":false},{"StartTime":163912.0,"Position":488.0,"HyperDash":false},{"StartTime":163980.0,"Position":314.0,"HyperDash":false},{"StartTime":164048.0,"Position":135.0,"HyperDash":false},{"StartTime":164116.0,"Position":399.0,"HyperDash":false},{"StartTime":164184.0,"Position":404.0,"HyperDash":false},{"StartTime":164252.0,"Position":152.0,"HyperDash":false},{"StartTime":164320.0,"Position":353.0,"HyperDash":false},{"StartTime":164389.0,"Position":358.0,"HyperDash":false}]},{"StartTime":164753.0,"Objects":[{"StartTime":164753.0,"Position":132.0,"HyperDash":false},{"StartTime":164843.0,"Position":132.0,"HyperDash":false},{"StartTime":164934.0,"Position":132.0,"HyperDash":false}]},{"StartTime":165117.0,"Objects":[{"StartTime":165117.0,"Position":304.0,"HyperDash":false}]},{"StartTime":165207.0,"Objects":[{"StartTime":165207.0,"Position":352.0,"HyperDash":false}]},{"StartTime":165298.0,"Objects":[{"StartTime":165298.0,"Position":372.0,"HyperDash":false}]},{"StartTime":165389.0,"Objects":[{"StartTime":165389.0,"Position":351.0,"HyperDash":false}]},{"StartTime":165480.0,"Objects":[{"StartTime":165480.0,"Position":303.0,"HyperDash":false}]},{"StartTime":165662.0,"Objects":[{"StartTime":165662.0,"Position":208.0,"HyperDash":false}]},{"StartTime":165844.0,"Objects":[{"StartTime":165844.0,"Position":388.0,"HyperDash":false},{"StartTime":165916.0,"Position":435.812164,"HyperDash":false},{"StartTime":166025.0,"Position":473.0,"HyperDash":false}]},{"StartTime":166208.0,"Objects":[{"StartTime":166208.0,"Position":216.0,"HyperDash":false},{"StartTime":166298.0,"Position":158.851242,"HyperDash":false},{"StartTime":166389.0,"Position":131.0,"HyperDash":false},{"StartTime":166462.0,"Position":155.953156,"HyperDash":false},{"StartTime":166571.0,"Position":216.0,"HyperDash":false}]},{"StartTime":166753.0,"Objects":[{"StartTime":166753.0,"Position":308.0,"HyperDash":false},{"StartTime":166843.0,"Position":274.851257,"HyperDash":false},{"StartTime":166934.0,"Position":223.234161,"HyperDash":false},{"StartTime":167007.0,"Position":206.046844,"HyperDash":false},{"StartTime":167116.0,"Position":138.0,"HyperDash":false}]},{"StartTime":167299.0,"Objects":[{"StartTime":167299.0,"Position":312.0,"HyperDash":false},{"StartTime":167371.0,"Position":305.0,"HyperDash":false},{"StartTime":167480.0,"Position":312.0,"HyperDash":false}]},{"StartTime":167662.0,"Objects":[{"StartTime":167662.0,"Position":138.0,"HyperDash":false},{"StartTime":167752.0,"Position":192.148758,"HyperDash":false},{"StartTime":167843.0,"Position":222.765839,"HyperDash":false},{"StartTime":167916.0,"Position":254.953156,"HyperDash":false},{"StartTime":168025.0,"Position":308.0,"HyperDash":false}]},{"StartTime":168208.0,"Objects":[{"StartTime":168208.0,"Position":404.0,"HyperDash":false},{"StartTime":168298.0,"Position":395.0,"HyperDash":false},{"StartTime":168389.0,"Position":403.234161,"HyperDash":false},{"StartTime":168462.0,"Position":382.046844,"HyperDash":false},{"StartTime":168571.0,"Position":318.0,"HyperDash":false}]},{"StartTime":168753.0,"Objects":[{"StartTime":168753.0,"Position":140.0,"HyperDash":false},{"StartTime":168825.0,"Position":131.0,"HyperDash":false},{"StartTime":168934.0,"Position":140.0,"HyperDash":false}]},{"StartTime":169117.0,"Objects":[{"StartTime":169117.0,"Position":320.0,"HyperDash":false},{"StartTime":169207.0,"Position":375.148743,"HyperDash":false},{"StartTime":169298.0,"Position":404.0,"HyperDash":false},{"StartTime":169371.0,"Position":419.0,"HyperDash":false},{"StartTime":169480.0,"Position":404.0,"HyperDash":false}]},{"StartTime":169662.0,"Objects":[{"StartTime":169662.0,"Position":232.0,"HyperDash":false},{"StartTime":169752.0,"Position":176.851242,"HyperDash":false},{"StartTime":169843.0,"Position":147.234161,"HyperDash":false},{"StartTime":169916.0,"Position":100.046837,"HyperDash":false},{"StartTime":170025.0,"Position":62.0,"HyperDash":false}]},{"StartTime":170208.0,"Objects":[{"StartTime":170208.0,"Position":232.0,"HyperDash":false},{"StartTime":170280.0,"Position":203.187851,"HyperDash":false},{"StartTime":170389.0,"Position":147.0,"HyperDash":false}]},{"StartTime":170571.0,"Objects":[{"StartTime":170571.0,"Position":52.0,"HyperDash":false},{"StartTime":170661.0,"Position":52.0,"HyperDash":false}]},{"StartTime":170753.0,"Objects":[{"StartTime":170753.0,"Position":100.0,"HyperDash":false}]},{"StartTime":170935.0,"Objects":[{"StartTime":170935.0,"Position":192.0,"HyperDash":false}]},{"StartTime":171117.0,"Objects":[{"StartTime":171117.0,"Position":448.0,"HyperDash":false},{"StartTime":171189.0,"Position":432.0,"HyperDash":false},{"StartTime":171298.0,"Position":448.0,"HyperDash":false}]},{"StartTime":171480.0,"Objects":[{"StartTime":171480.0,"Position":356.0,"HyperDash":false}]},{"StartTime":171662.0,"Objects":[{"StartTime":171662.0,"Position":184.0,"HyperDash":false},{"StartTime":171734.0,"Position":202.812149,"HyperDash":false},{"StartTime":171843.0,"Position":269.0,"HyperDash":false}]},{"StartTime":172026.0,"Objects":[{"StartTime":172026.0,"Position":20.0,"HyperDash":false},{"StartTime":172116.0,"Position":20.0,"HyperDash":false},{"StartTime":172207.0,"Position":20.0,"HyperDash":false}]},{"StartTime":172390.0,"Objects":[{"StartTime":172390.0,"Position":116.0,"HyperDash":false}]},{"StartTime":172571.0,"Objects":[{"StartTime":172571.0,"Position":32.0,"HyperDash":false}]},{"StartTime":172753.0,"Objects":[{"StartTime":172753.0,"Position":208.0,"HyperDash":false},{"StartTime":172825.0,"Position":252.812149,"HyperDash":false},{"StartTime":172934.0,"Position":293.0,"HyperDash":false}]},{"StartTime":173117.0,"Objects":[{"StartTime":173117.0,"Position":200.0,"HyperDash":false},{"StartTime":173189.0,"Position":212.0,"HyperDash":false},{"StartTime":173298.0,"Position":200.0,"HyperDash":false}]},{"StartTime":173480.0,"Objects":[{"StartTime":173480.0,"Position":376.0,"HyperDash":false},{"StartTime":173552.0,"Position":379.0,"HyperDash":false},{"StartTime":173661.0,"Position":376.0,"HyperDash":false}]},{"StartTime":173844.0,"Objects":[{"StartTime":173844.0,"Position":200.0,"HyperDash":false}]},{"StartTime":174026.0,"Objects":[{"StartTime":174026.0,"Position":116.0,"HyperDash":false},{"StartTime":174116.0,"Position":76.2682648,"HyperDash":false},{"StartTime":174207.0,"Position":64.10713,"HyperDash":false},{"StartTime":174280.0,"Position":75.55404,"HyperDash":false},{"StartTime":174389.0,"Position":115.499283,"HyperDash":false}]},{"StartTime":174571.0,"Objects":[{"StartTime":174571.0,"Position":372.0,"HyperDash":false},{"StartTime":174643.0,"Position":412.812164,"HyperDash":false},{"StartTime":174752.0,"Position":457.0,"HyperDash":false}]},{"StartTime":174935.0,"Objects":[{"StartTime":174935.0,"Position":280.0,"HyperDash":false},{"StartTime":175007.0,"Position":297.0,"HyperDash":false},{"StartTime":175116.0,"Position":280.0,"HyperDash":false}]},{"StartTime":175299.0,"Objects":[{"StartTime":175299.0,"Position":368.0,"HyperDash":false}]},{"StartTime":175480.0,"Objects":[{"StartTime":175480.0,"Position":192.0,"HyperDash":false},{"StartTime":175552.0,"Position":197.0,"HyperDash":false},{"StartTime":175661.0,"Position":192.0,"HyperDash":false}]},{"StartTime":175844.0,"Objects":[{"StartTime":175844.0,"Position":280.0,"HyperDash":false}]},{"StartTime":176026.0,"Objects":[{"StartTime":176026.0,"Position":453.0,"HyperDash":false},{"StartTime":176098.0,"Position":425.187836,"HyperDash":false},{"StartTime":176207.0,"Position":368.0,"HyperDash":false}]},{"StartTime":176390.0,"Objects":[{"StartTime":176390.0,"Position":112.0,"HyperDash":false},{"StartTime":176480.0,"Position":69.85124,"HyperDash":false},{"StartTime":176571.0,"Position":27.0,"HyperDash":false},{"StartTime":176644.0,"Position":44.9531631,"HyperDash":false},{"StartTime":176753.0,"Position":112.0,"HyperDash":false}]},{"StartTime":176935.0,"Objects":[{"StartTime":176935.0,"Position":292.0,"HyperDash":false},{"StartTime":177025.0,"Position":231.851242,"HyperDash":false},{"StartTime":177116.0,"Position":207.234161,"HyperDash":false},{"StartTime":177189.0,"Position":180.046844,"HyperDash":false},{"StartTime":177298.0,"Position":122.0,"HyperDash":false}]},{"StartTime":177480.0,"Objects":[{"StartTime":177480.0,"Position":304.0,"HyperDash":false},{"StartTime":177552.0,"Position":349.812164,"HyperDash":false},{"StartTime":177661.0,"Position":389.0,"HyperDash":false}]},{"StartTime":177844.0,"Objects":[{"StartTime":177844.0,"Position":132.0,"HyperDash":false},{"StartTime":177934.0,"Position":67.42149,"HyperDash":false},{"StartTime":178025.0,"Position":32.0,"HyperDash":false},{"StartTime":178098.0,"Position":18.0,"HyperDash":false},{"StartTime":178207.0,"Position":32.0,"HyperDash":false}]},{"StartTime":178390.0,"Objects":[{"StartTime":178390.0,"Position":208.0,"HyperDash":false},{"StartTime":178480.0,"Position":249.148758,"HyperDash":false},{"StartTime":178571.0,"Position":292.765839,"HyperDash":false},{"StartTime":178644.0,"Position":311.953156,"HyperDash":false},{"StartTime":178753.0,"Position":378.0,"HyperDash":false}]},{"StartTime":178935.0,"Objects":[{"StartTime":178935.0,"Position":284.0,"HyperDash":false},{"StartTime":179007.0,"Position":301.0,"HyperDash":false},{"StartTime":179116.0,"Position":284.0,"HyperDash":false}]},{"StartTime":179299.0,"Objects":[{"StartTime":179299.0,"Position":464.0,"HyperDash":false},{"StartTime":179371.0,"Position":479.0,"HyperDash":false},{"StartTime":179480.0,"Position":464.0,"HyperDash":false}]},{"StartTime":179662.0,"Objects":[{"StartTime":179662.0,"Position":380.0,"HyperDash":false}]},{"StartTime":179844.0,"Objects":[{"StartTime":179844.0,"Position":204.0,"HyperDash":false},{"StartTime":179934.0,"Position":249.148758,"HyperDash":false},{"StartTime":180025.0,"Position":288.765839,"HyperDash":false},{"StartTime":180098.0,"Position":306.953156,"HyperDash":false},{"StartTime":180207.0,"Position":374.0,"HyperDash":false}]},{"StartTime":180390.0,"Objects":[{"StartTime":180390.0,"Position":460.0,"HyperDash":false},{"StartTime":180462.0,"Position":450.0,"HyperDash":false},{"StartTime":180571.0,"Position":460.0,"HyperDash":false}]},{"StartTime":180753.0,"Objects":[{"StartTime":180753.0,"Position":284.0,"HyperDash":false},{"StartTime":180843.0,"Position":257.851257,"HyperDash":false},{"StartTime":180934.0,"Position":200.0,"HyperDash":false},{"StartTime":181007.0,"Position":192.0,"HyperDash":false},{"StartTime":181116.0,"Position":200.0,"HyperDash":false}]},{"StartTime":181299.0,"Objects":[{"StartTime":181299.0,"Position":380.0,"HyperDash":false},{"StartTime":181389.0,"Position":345.851257,"HyperDash":false},{"StartTime":181480.0,"Position":295.234161,"HyperDash":false},{"StartTime":181553.0,"Position":258.046844,"HyperDash":false},{"StartTime":181662.0,"Position":210.0,"HyperDash":false}]},{"StartTime":181844.0,"Objects":[{"StartTime":181844.0,"Position":302.0,"HyperDash":false},{"StartTime":181916.0,"Position":255.187836,"HyperDash":false},{"StartTime":182025.0,"Position":217.0,"HyperDash":false}]},{"StartTime":182208.0,"Objects":[{"StartTime":182208.0,"Position":124.0,"HyperDash":false},{"StartTime":182280.0,"Position":131.0,"HyperDash":false},{"StartTime":182389.0,"Position":124.0,"HyperDash":false}]},{"StartTime":182571.0,"Objects":[{"StartTime":182571.0,"Position":302.0,"HyperDash":false},{"StartTime":182643.0,"Position":248.187836,"HyperDash":false},{"StartTime":182752.0,"Position":217.0,"HyperDash":false}]},{"StartTime":182935.0,"Objects":[{"StartTime":182935.0,"Position":312.0,"HyperDash":false},{"StartTime":183025.0,"Position":354.5,"HyperDash":false},{"StartTime":183116.0,"Position":312.0,"HyperDash":false}]},{"StartTime":183299.0,"Objects":[{"StartTime":183299.0,"Position":132.0,"HyperDash":false},{"StartTime":183371.0,"Position":80.18785,"HyperDash":false},{"StartTime":183480.0,"Position":47.0,"HyperDash":true}]},{"StartTime":183662.0,"Objects":[{"StartTime":183662.0,"Position":312.0,"HyperDash":false},{"StartTime":183752.0,"Position":350.73175,"HyperDash":false},{"StartTime":183843.0,"Position":363.892883,"HyperDash":false},{"StartTime":183916.0,"Position":353.445984,"HyperDash":false},{"StartTime":184025.0,"Position":312.500732,"HyperDash":false}]},{"StartTime":184208.0,"Objects":[{"StartTime":184208.0,"Position":220.0,"HyperDash":false}]},{"StartTime":184390.0,"Objects":[{"StartTime":184390.0,"Position":324.0,"HyperDash":false},{"StartTime":184462.0,"Position":310.0,"HyperDash":false},{"StartTime":184571.0,"Position":324.0,"HyperDash":false}]},{"StartTime":184753.0,"Objects":[{"StartTime":184753.0,"Position":144.0,"HyperDash":false},{"StartTime":184825.0,"Position":142.0,"HyperDash":false},{"StartTime":184934.0,"Position":144.0,"HyperDash":false}]},{"StartTime":185117.0,"Objects":[{"StartTime":185117.0,"Position":324.0,"HyperDash":false},{"StartTime":185189.0,"Position":348.812164,"HyperDash":false},{"StartTime":185298.0,"Position":409.0,"HyperDash":false}]},{"StartTime":185480.0,"Objects":[{"StartTime":185480.0,"Position":232.0,"HyperDash":false},{"StartTime":185552.0,"Position":224.0,"HyperDash":false},{"StartTime":185661.0,"Position":232.0,"HyperDash":false}]},{"StartTime":185844.0,"Objects":[{"StartTime":185844.0,"Position":316.0,"HyperDash":false}]},{"StartTime":186026.0,"Objects":[{"StartTime":186026.0,"Position":232.0,"HyperDash":false}]},{"StartTime":186208.0,"Objects":[{"StartTime":186208.0,"Position":408.0,"HyperDash":false},{"StartTime":186280.0,"Position":427.0,"HyperDash":false},{"StartTime":186389.0,"Position":408.0,"HyperDash":false}]},{"StartTime":186571.0,"Objects":[{"StartTime":186571.0,"Position":152.0,"HyperDash":false},{"StartTime":186661.0,"Position":106.851242,"HyperDash":false},{"StartTime":186752.0,"Position":68.76584,"HyperDash":false},{"StartTime":186825.0,"Position":87.95316,"HyperDash":false},{"StartTime":186934.0,"Position":154.0,"HyperDash":false}]},{"StartTime":187117.0,"Objects":[{"StartTime":187117.0,"Position":332.0,"HyperDash":false},{"StartTime":187207.0,"Position":276.851257,"HyperDash":false},{"StartTime":187298.0,"Position":247.234161,"HyperDash":false},{"StartTime":187371.0,"Position":205.046844,"HyperDash":false},{"StartTime":187480.0,"Position":162.0,"HyperDash":false}]},{"StartTime":187662.0,"Objects":[{"StartTime":187662.0,"Position":76.0,"HyperDash":false},{"StartTime":187734.0,"Position":74.0,"HyperDash":false},{"StartTime":187843.0,"Position":76.0,"HyperDash":false}]},{"StartTime":188026.0,"Objects":[{"StartTime":188026.0,"Position":252.0,"HyperDash":false}]},{"StartTime":188116.0,"Objects":[{"StartTime":188116.0,"Position":294.0,"HyperDash":false}]},{"StartTime":188207.0,"Objects":[{"StartTime":188207.0,"Position":337.0,"HyperDash":false}]},{"StartTime":188390.0,"Objects":[{"StartTime":188390.0,"Position":176.0,"HyperDash":false}]},{"StartTime":188571.0,"Objects":[{"StartTime":188571.0,"Position":344.0,"HyperDash":false},{"StartTime":188661.0,"Position":370.214264,"HyperDash":false},{"StartTime":188752.0,"Position":396.42868,"HyperDash":false},{"StartTime":188825.0,"Position":403.238831,"HyperDash":false},{"StartTime":188934.0,"Position":343.061737,"HyperDash":false}]},{"StartTime":189117.0,"Objects":[{"StartTime":189117.0,"Position":168.0,"HyperDash":false},{"StartTime":189189.0,"Position":133.187851,"HyperDash":false},{"StartTime":189298.0,"Position":83.0,"HyperDash":true}]},{"StartTime":189480.0,"Objects":[{"StartTime":189480.0,"Position":344.0,"HyperDash":false},{"StartTime":189570.0,"Position":378.578522,"HyperDash":false},{"StartTime":189661.0,"Position":445.719,"HyperDash":false},{"StartTime":189734.0,"Position":443.0,"HyperDash":false},{"StartTime":189843.0,"Position":448.0,"HyperDash":false}]},{"StartTime":190026.0,"Objects":[{"StartTime":190026.0,"Position":352.0,"HyperDash":false},{"StartTime":190116.0,"Position":300.851257,"HyperDash":false},{"StartTime":190207.0,"Position":267.234161,"HyperDash":false},{"StartTime":190280.0,"Position":224.046844,"HyperDash":false},{"StartTime":190389.0,"Position":182.0,"HyperDash":false}]},{"StartTime":190571.0,"Objects":[{"StartTime":190571.0,"Position":276.0,"HyperDash":false},{"StartTime":190643.0,"Position":262.0,"HyperDash":false},{"StartTime":190752.0,"Position":276.0,"HyperDash":false}]},{"StartTime":190935.0,"Objects":[{"StartTime":190935.0,"Position":96.0,"HyperDash":false},{"StartTime":191007.0,"Position":114.0,"HyperDash":false},{"StartTime":191116.0,"Position":96.0,"HyperDash":false}]},{"StartTime":191299.0,"Objects":[{"StartTime":191299.0,"Position":192.0,"HyperDash":false},{"StartTime":191371.0,"Position":154.187851,"HyperDash":false},{"StartTime":191480.0,"Position":107.0,"HyperDash":false}]},{"StartTime":191662.0,"Objects":[{"StartTime":191662.0,"Position":284.0,"HyperDash":false},{"StartTime":191734.0,"Position":328.812164,"HyperDash":false},{"StartTime":191843.0,"Position":369.0,"HyperDash":false}]},{"StartTime":192026.0,"Objects":[{"StartTime":192026.0,"Position":464.0,"HyperDash":false},{"StartTime":192116.0,"Position":464.0,"HyperDash":false}]},{"StartTime":192208.0,"Objects":[{"StartTime":192208.0,"Position":420.0,"HyperDash":false}]},{"StartTime":192390.0,"Objects":[{"StartTime":192390.0,"Position":240.0,"HyperDash":false},{"StartTime":192480.0,"Position":193.851242,"HyperDash":false},{"StartTime":192571.0,"Position":155.234161,"HyperDash":false},{"StartTime":192644.0,"Position":139.046844,"HyperDash":false},{"StartTime":192753.0,"Position":70.0,"HyperDash":false}]},{"StartTime":192935.0,"Objects":[{"StartTime":192935.0,"Position":156.0,"HyperDash":false}]},{"StartTime":193117.0,"Objects":[{"StartTime":193117.0,"Position":64.0,"HyperDash":false},{"StartTime":193189.0,"Position":49.0,"HyperDash":false},{"StartTime":193298.0,"Position":64.0,"HyperDash":false}]},{"StartTime":193480.0,"Objects":[{"StartTime":193480.0,"Position":156.0,"HyperDash":false},{"StartTime":193552.0,"Position":173.0,"HyperDash":false},{"StartTime":193661.0,"Position":156.0,"HyperDash":false}]},{"StartTime":193844.0,"Objects":[{"StartTime":193844.0,"Position":332.0,"HyperDash":false},{"StartTime":193934.0,"Position":374.5,"HyperDash":false},{"StartTime":194025.0,"Position":332.0,"HyperDash":false}]},{"StartTime":194208.0,"Objects":[{"StartTime":194208.0,"Position":156.0,"HyperDash":false},{"StartTime":194280.0,"Position":194.812149,"HyperDash":false},{"StartTime":194389.0,"Position":241.0,"HyperDash":false}]},{"StartTime":194571.0,"Objects":[{"StartTime":194571.0,"Position":328.0,"HyperDash":false}]},{"StartTime":194753.0,"Objects":[{"StartTime":194753.0,"Position":236.0,"HyperDash":false}]},{"StartTime":194935.0,"Objects":[{"StartTime":194935.0,"Position":416.0,"HyperDash":false},{"StartTime":195007.0,"Position":430.0,"HyperDash":false},{"StartTime":195116.0,"Position":416.0,"HyperDash":false}]},{"StartTime":195299.0,"Objects":[{"StartTime":195299.0,"Position":160.0,"HyperDash":false},{"StartTime":195389.0,"Position":112.851242,"HyperDash":false},{"StartTime":195480.0,"Position":76.0,"HyperDash":false},{"StartTime":195553.0,"Position":72.0,"HyperDash":false},{"StartTime":195662.0,"Position":76.0,"HyperDash":false}]},{"StartTime":195844.0,"Objects":[{"StartTime":195844.0,"Position":164.0,"HyperDash":false},{"StartTime":195934.0,"Position":224.148758,"HyperDash":false},{"StartTime":196025.0,"Position":248.765839,"HyperDash":false},{"StartTime":196098.0,"Position":284.953156,"HyperDash":false},{"StartTime":196207.0,"Position":334.0,"HyperDash":false}]},{"StartTime":196389.0,"Objects":[{"StartTime":196389.0,"Position":240.0,"HyperDash":false},{"StartTime":196461.0,"Position":232.0,"HyperDash":false},{"StartTime":196570.0,"Position":240.0,"HyperDash":false}]},{"StartTime":196753.0,"Objects":[{"StartTime":196753.0,"Position":420.0,"HyperDash":false},{"StartTime":196825.0,"Position":435.0,"HyperDash":false},{"StartTime":196934.0,"Position":420.0,"HyperDash":false}]},{"StartTime":197026.0,"Objects":[{"StartTime":197026.0,"Position":372.0,"HyperDash":false}]},{"StartTime":197117.0,"Objects":[{"StartTime":197117.0,"Position":324.0,"HyperDash":false},{"StartTime":197189.0,"Position":282.187836,"HyperDash":false},{"StartTime":197298.0,"Position":239.0,"HyperDash":false}]},{"StartTime":197480.0,"Objects":[{"StartTime":197480.0,"Position":332.0,"HyperDash":false},{"StartTime":197552.0,"Position":346.0,"HyperDash":false},{"StartTime":197661.0,"Position":332.0,"HyperDash":false}]},{"StartTime":197844.0,"Objects":[{"StartTime":197844.0,"Position":152.0,"HyperDash":false},{"StartTime":197934.0,"Position":109.5,"HyperDash":false},{"StartTime":198025.0,"Position":152.0,"HyperDash":false}]},{"StartTime":198208.0,"Objects":[{"StartTime":198208.0,"Position":328.0,"HyperDash":false},{"StartTime":198298.0,"Position":387.148743,"HyperDash":false},{"StartTime":198389.0,"Position":412.765839,"HyperDash":false},{"StartTime":198462.0,"Position":458.953156,"HyperDash":false},{"StartTime":198571.0,"Position":498.0,"HyperDash":false}]},{"StartTime":198753.0,"Objects":[{"StartTime":198753.0,"Position":412.0,"HyperDash":false}]},{"StartTime":198935.0,"Objects":[{"StartTime":198935.0,"Position":236.0,"HyperDash":false},{"StartTime":199007.0,"Position":253.0,"HyperDash":false},{"StartTime":199116.0,"Position":236.0,"HyperDash":false}]},{"StartTime":199298.0,"Objects":[{"StartTime":199298.0,"Position":328.0,"HyperDash":false},{"StartTime":199370.0,"Position":276.187836,"HyperDash":false},{"StartTime":199479.0,"Position":243.0,"HyperDash":false}]},{"StartTime":199662.0,"Objects":[{"StartTime":199662.0,"Position":64.0,"HyperDash":false},{"StartTime":199734.0,"Position":66.0,"HyperDash":false},{"StartTime":199843.0,"Position":64.0,"HyperDash":false}]},{"StartTime":200026.0,"Objects":[{"StartTime":200026.0,"Position":160.0,"HyperDash":false}]},{"StartTime":200116.0,"Objects":[{"StartTime":200116.0,"Position":112.0,"HyperDash":false}]},{"StartTime":200207.0,"Objects":[{"StartTime":200207.0,"Position":64.0,"HyperDash":false}]},{"StartTime":200390.0,"Objects":[{"StartTime":200390.0,"Position":240.0,"HyperDash":false},{"StartTime":200462.0,"Position":232.0,"HyperDash":false},{"StartTime":200571.0,"Position":240.0,"HyperDash":false}]},{"StartTime":200753.0,"Objects":[{"StartTime":200753.0,"Position":416.0,"HyperDash":false},{"StartTime":200825.0,"Position":438.812164,"HyperDash":false},{"StartTime":200934.0,"Position":501.0,"HyperDash":true}]},{"StartTime":201117.0,"Objects":[{"StartTime":201117.0,"Position":240.0,"HyperDash":false},{"StartTime":201207.0,"Position":198.4215,"HyperDash":false},{"StartTime":201298.0,"Position":138.280991,"HyperDash":false},{"StartTime":201371.0,"Position":113.25621,"HyperDash":false},{"StartTime":201480.0,"Position":36.0,"HyperDash":false}]},{"StartTime":201662.0,"Objects":[{"StartTime":201662.0,"Position":128.0,"HyperDash":false},{"StartTime":201752.0,"Position":185.148758,"HyperDash":false},{"StartTime":201843.0,"Position":212.765839,"HyperDash":false},{"StartTime":201916.0,"Position":198.0,"HyperDash":false},{"StartTime":202025.0,"Position":216.0,"HyperDash":false}]},{"StartTime":202208.0,"Objects":[{"StartTime":202208.0,"Position":40.0,"HyperDash":false},{"StartTime":202280.0,"Position":56.0,"HyperDash":false},{"StartTime":202389.0,"Position":40.0,"HyperDash":false}]},{"StartTime":202571.0,"Objects":[{"StartTime":202571.0,"Position":216.0,"HyperDash":false},{"StartTime":202643.0,"Position":263.812134,"HyperDash":false},{"StartTime":202752.0,"Position":301.0,"HyperDash":false}]},{"StartTime":202844.0,"Objects":[{"StartTime":202844.0,"Position":348.0,"HyperDash":false}]},{"StartTime":202935.0,"Objects":[{"StartTime":202935.0,"Position":396.0,"HyperDash":false},{"StartTime":203007.0,"Position":411.0,"HyperDash":false},{"StartTime":203116.0,"Position":396.0,"HyperDash":false}]},{"StartTime":203299.0,"Objects":[{"StartTime":203299.0,"Position":492.0,"HyperDash":false},{"StartTime":203371.0,"Position":454.187836,"HyperDash":false},{"StartTime":203480.0,"Position":407.0,"HyperDash":false}]},{"StartTime":203662.0,"Objects":[{"StartTime":203662.0,"Position":232.0,"HyperDash":false},{"StartTime":203734.0,"Position":231.0,"HyperDash":false},{"StartTime":203843.0,"Position":232.0,"HyperDash":false}]},{"StartTime":204026.0,"Objects":[{"StartTime":204026.0,"Position":408.0,"HyperDash":false},{"StartTime":204116.0,"Position":436.148743,"HyperDash":false},{"StartTime":204207.0,"Position":493.0,"HyperDash":false},{"StartTime":204280.0,"Position":447.046844,"HyperDash":false},{"StartTime":204389.0,"Position":408.0,"HyperDash":false}]},{"StartTime":204571.0,"Objects":[{"StartTime":204571.0,"Position":316.0,"HyperDash":false},{"StartTime":204661.0,"Position":377.148743,"HyperDash":false},{"StartTime":204752.0,"Position":400.765839,"HyperDash":false},{"StartTime":204825.0,"Position":421.953156,"HyperDash":false},{"StartTime":204934.0,"Position":486.0,"HyperDash":false}]},{"StartTime":205117.0,"Objects":[{"StartTime":205117.0,"Position":308.0,"HyperDash":false},{"StartTime":205189.0,"Position":279.187836,"HyperDash":false},{"StartTime":205298.0,"Position":223.0,"HyperDash":false}]},{"StartTime":205480.0,"Objects":[{"StartTime":205480.0,"Position":48.0,"HyperDash":false},{"StartTime":205552.0,"Position":51.0,"HyperDash":false},{"StartTime":205661.0,"Position":48.0,"HyperDash":false}]},{"StartTime":205844.0,"Objects":[{"StartTime":205844.0,"Position":224.0,"HyperDash":false},{"StartTime":205916.0,"Position":246.812164,"HyperDash":false},{"StartTime":206025.0,"Position":309.0,"HyperDash":false}]},{"StartTime":206208.0,"Objects":[{"StartTime":206208.0,"Position":216.0,"HyperDash":false}]},{"StartTime":206390.0,"Objects":[{"StartTime":206390.0,"Position":320.0,"HyperDash":false}]},{"StartTime":206571.0,"Objects":[{"StartTime":206571.0,"Position":144.0,"HyperDash":false},{"StartTime":206643.0,"Position":107.187851,"HyperDash":false},{"StartTime":206752.0,"Position":59.0,"HyperDash":true}]},{"StartTime":206935.0,"Objects":[{"StartTime":206935.0,"Position":320.0,"HyperDash":false},{"StartTime":207007.0,"Position":361.812164,"HyperDash":false},{"StartTime":207116.0,"Position":405.0,"HyperDash":false}]},{"StartTime":207208.0,"Objects":[{"StartTime":207208.0,"Position":405.0,"HyperDash":false}]},{"StartTime":207299.0,"Objects":[{"StartTime":207299.0,"Position":405.0,"HyperDash":false}]},{"StartTime":207480.0,"Objects":[{"StartTime":207480.0,"Position":312.0,"HyperDash":false},{"StartTime":207570.0,"Position":265.367828,"HyperDash":false},{"StartTime":207661.0,"Position":263.844818,"HyperDash":false},{"StartTime":207734.0,"Position":266.8324,"HyperDash":false},{"StartTime":207843.0,"Position":312.8251,"HyperDash":false}]},{"StartTime":208026.0,"Objects":[{"StartTime":208026.0,"Position":488.0,"HyperDash":false},{"StartTime":208098.0,"Position":506.0,"HyperDash":false},{"StartTime":208207.0,"Position":488.0,"HyperDash":false}]},{"StartTime":208390.0,"Objects":[{"StartTime":208390.0,"Position":308.0,"HyperDash":false},{"StartTime":208462.0,"Position":292.187836,"HyperDash":false},{"StartTime":208571.0,"Position":223.0,"HyperDash":false}]},{"StartTime":208753.0,"Objects":[{"StartTime":208753.0,"Position":404.0,"HyperDash":false},{"StartTime":208825.0,"Position":411.0,"HyperDash":false},{"StartTime":208934.0,"Position":404.0,"HyperDash":false}]},{"StartTime":209117.0,"Objects":[{"StartTime":209117.0,"Position":308.0,"HyperDash":false}]},{"StartTime":209299.0,"Objects":[{"StartTime":209299.0,"Position":392.0,"HyperDash":false}]},{"StartTime":209480.0,"Objects":[{"StartTime":209480.0,"Position":216.0,"HyperDash":false},{"StartTime":209552.0,"Position":192.187851,"HyperDash":false},{"StartTime":209661.0,"Position":131.0,"HyperDash":false}]},{"StartTime":209844.0,"Objects":[{"StartTime":209844.0,"Position":308.0,"HyperDash":false},{"StartTime":209916.0,"Position":293.0,"HyperDash":false},{"StartTime":210025.0,"Position":308.0,"HyperDash":false}]},{"StartTime":210117.0,"Objects":[{"StartTime":210117.0,"Position":264.0,"HyperDash":false}]},{"StartTime":210208.0,"Objects":[{"StartTime":210208.0,"Position":220.0,"HyperDash":false}]},{"StartTime":210390.0,"Objects":[{"StartTime":210390.0,"Position":308.0,"HyperDash":false},{"StartTime":210480.0,"Position":347.148743,"HyperDash":false},{"StartTime":210571.0,"Position":392.765839,"HyperDash":false},{"StartTime":210644.0,"Position":414.953156,"HyperDash":false},{"StartTime":210753.0,"Position":478.0,"HyperDash":false}]},{"StartTime":210935.0,"Objects":[{"StartTime":210935.0,"Position":296.0,"HyperDash":false},{"StartTime":211007.0,"Position":313.0,"HyperDash":false},{"StartTime":211116.0,"Position":296.0,"HyperDash":false}]},{"StartTime":211299.0,"Objects":[{"StartTime":211299.0,"Position":120.0,"HyperDash":false}]},{"StartTime":211389.0,"Objects":[{"StartTime":211389.0,"Position":120.0,"HyperDash":false}]},{"StartTime":211480.0,"Objects":[{"StartTime":211480.0,"Position":120.0,"HyperDash":false}]},{"StartTime":211662.0,"Objects":[{"StartTime":211662.0,"Position":296.0,"HyperDash":false},{"StartTime":211734.0,"Position":276.187836,"HyperDash":false},{"StartTime":211843.0,"Position":211.0,"HyperDash":false}]},{"StartTime":212026.0,"Objects":[{"StartTime":212026.0,"Position":120.0,"HyperDash":false},{"StartTime":212098.0,"Position":122.0,"HyperDash":false},{"StartTime":212207.0,"Position":120.0,"HyperDash":false}]},{"StartTime":212390.0,"Objects":[{"StartTime":212390.0,"Position":296.0,"HyperDash":false}]},{"StartTime":212571.0,"Objects":[{"StartTime":212571.0,"Position":196.0,"HyperDash":true}]},{"StartTime":212753.0,"Objects":[{"StartTime":212753.0,"Position":456.0,"HyperDash":false},{"StartTime":212825.0,"Position":465.0,"HyperDash":false},{"StartTime":212934.0,"Position":456.0,"HyperDash":false}]},{"StartTime":213117.0,"Objects":[{"StartTime":213117.0,"Position":276.0,"HyperDash":false},{"StartTime":213189.0,"Position":223.187851,"HyperDash":false},{"StartTime":213298.0,"Position":191.0,"HyperDash":false}]},{"StartTime":213480.0,"Objects":[{"StartTime":213480.0,"Position":284.0,"HyperDash":false},{"StartTime":213552.0,"Position":282.0,"HyperDash":false},{"StartTime":213661.0,"Position":284.0,"HyperDash":false}]},{"StartTime":213844.0,"Objects":[{"StartTime":213844.0,"Position":104.0,"HyperDash":false},{"StartTime":213916.0,"Position":147.812149,"HyperDash":false},{"StartTime":214025.0,"Position":189.0,"HyperDash":true}]},{"StartTime":214208.0,"Objects":[{"StartTime":214208.0,"Position":448.0,"HyperDash":false},{"StartTime":214280.0,"Position":454.0,"HyperDash":false},{"StartTime":214389.0,"Position":448.0,"HyperDash":false}]},{"StartTime":214480.0,"Objects":[{"StartTime":214480.0,"Position":400.0,"HyperDash":false}]},{"StartTime":214571.0,"Objects":[{"StartTime":214571.0,"Position":352.0,"HyperDash":false}]},{"StartTime":214753.0,"Objects":[{"StartTime":214753.0,"Position":448.0,"HyperDash":false}]},{"StartTime":214935.0,"Objects":[{"StartTime":214935.0,"Position":272.0,"HyperDash":false},{"StartTime":215007.0,"Position":280.0,"HyperDash":false},{"StartTime":215116.0,"Position":272.0,"HyperDash":false}]},{"StartTime":215299.0,"Objects":[{"StartTime":215299.0,"Position":96.0,"HyperDash":false},{"StartTime":215371.0,"Position":74.18785,"HyperDash":false},{"StartTime":215480.0,"Position":11.0,"HyperDash":true}]},{"StartTime":215662.0,"Objects":[{"StartTime":215662.0,"Position":272.0,"HyperDash":false},{"StartTime":215734.0,"Position":321.812164,"HyperDash":false},{"StartTime":215843.0,"Position":357.0,"HyperDash":false}]},{"StartTime":216026.0,"Objects":[{"StartTime":216026.0,"Position":180.0,"HyperDash":false},{"StartTime":216098.0,"Position":185.0,"HyperDash":false},{"StartTime":216207.0,"Position":180.0,"HyperDash":false}]},{"StartTime":216390.0,"Objects":[{"StartTime":216390.0,"Position":356.0,"HyperDash":false}]},{"StartTime":216571.0,"Objects":[{"StartTime":216571.0,"Position":256.0,"HyperDash":false}]},{"StartTime":216753.0,"Objects":[{"StartTime":216753.0,"Position":436.0,"HyperDash":false},{"StartTime":216825.0,"Position":411.187836,"HyperDash":false},{"StartTime":216934.0,"Position":351.0,"HyperDash":false}]},{"StartTime":217117.0,"Objects":[{"StartTime":217117.0,"Position":96.0,"HyperDash":false},{"StartTime":217207.0,"Position":60.8512421,"HyperDash":false},{"StartTime":217298.0,"Position":12.7658386,"HyperDash":false},{"StartTime":217371.0,"Position":64.95316,"HyperDash":false},{"StartTime":217480.0,"Position":98.0,"HyperDash":false}]},{"StartTime":217662.0,"Objects":[{"StartTime":217662.0,"Position":276.0,"HyperDash":false},{"StartTime":217752.0,"Position":324.148743,"HyperDash":false},{"StartTime":217843.0,"Position":361.0,"HyperDash":false},{"StartTime":217916.0,"Position":327.046844,"HyperDash":false},{"StartTime":218025.0,"Position":276.0,"HyperDash":false}]},{"StartTime":218208.0,"Objects":[{"StartTime":218208.0,"Position":98.0,"HyperDash":false},{"StartTime":218280.0,"Position":87.0,"HyperDash":false},{"StartTime":218389.0,"Position":98.0,"HyperDash":true}]},{"StartTime":218571.0,"Objects":[{"StartTime":218571.0,"Position":360.0,"HyperDash":false},{"StartTime":218661.0,"Position":414.2143,"HyperDash":false},{"StartTime":218752.0,"Position":412.42868,"HyperDash":false},{"StartTime":218825.0,"Position":397.238861,"HyperDash":false},{"StartTime":218934.0,"Position":359.061737,"HyperDash":false}]},{"StartTime":219026.0,"Objects":[{"StartTime":219026.0,"Position":312.0,"HyperDash":false}]},{"StartTime":219117.0,"Objects":[{"StartTime":219117.0,"Position":264.0,"HyperDash":false}]},{"StartTime":219299.0,"Objects":[{"StartTime":219299.0,"Position":88.0,"HyperDash":false},{"StartTime":219371.0,"Position":104.812149,"HyperDash":false},{"StartTime":219480.0,"Position":173.0,"HyperDash":false}]},{"StartTime":219662.0,"Objects":[{"StartTime":219662.0,"Position":268.0,"HyperDash":false},{"StartTime":219734.0,"Position":274.0,"HyperDash":false},{"StartTime":219843.0,"Position":268.0,"HyperDash":false}]},{"StartTime":220026.0,"Objects":[{"StartTime":220026.0,"Position":88.0,"HyperDash":false},{"StartTime":220098.0,"Position":105.0,"HyperDash":false},{"StartTime":220207.0,"Position":88.0,"HyperDash":false}]},{"StartTime":220390.0,"Objects":[{"StartTime":220390.0,"Position":268.0,"HyperDash":false}]},{"StartTime":220571.0,"Objects":[{"StartTime":220571.0,"Position":180.0,"HyperDash":false}]},{"StartTime":220753.0,"Objects":[{"StartTime":220753.0,"Position":436.0,"HyperDash":false},{"StartTime":220825.0,"Position":425.0,"HyperDash":false},{"StartTime":220934.0,"Position":436.0,"HyperDash":false}]},{"StartTime":221117.0,"Objects":[{"StartTime":221117.0,"Position":260.0,"HyperDash":false},{"StartTime":221189.0,"Position":241.187851,"HyperDash":false},{"StartTime":221298.0,"Position":175.0,"HyperDash":true}]},{"StartTime":221480.0,"Objects":[{"StartTime":221480.0,"Position":436.0,"HyperDash":false},{"StartTime":221552.0,"Position":398.187836,"HyperDash":false},{"StartTime":221661.0,"Position":351.0,"HyperDash":false}]},{"StartTime":221753.0,"Objects":[{"StartTime":221753.0,"Position":308.0,"HyperDash":false}]},{"StartTime":221844.0,"Objects":[{"StartTime":221844.0,"Position":264.0,"HyperDash":false}]},{"StartTime":222026.0,"Objects":[{"StartTime":222026.0,"Position":356.0,"HyperDash":false}]},{"StartTime":222208.0,"Objects":[{"StartTime":222208.0,"Position":100.0,"HyperDash":false},{"StartTime":222280.0,"Position":74.18785,"HyperDash":false},{"StartTime":222389.0,"Position":15.0,"HyperDash":false}]},{"StartTime":222571.0,"Objects":[{"StartTime":222571.0,"Position":108.0,"HyperDash":false},{"StartTime":222643.0,"Position":119.0,"HyperDash":false},{"StartTime":222752.0,"Position":108.0,"HyperDash":true}]},{"StartTime":222935.0,"Objects":[{"StartTime":222935.0,"Position":368.0,"HyperDash":false},{"StartTime":223025.0,"Position":410.5,"HyperDash":false},{"StartTime":223116.0,"Position":368.0,"HyperDash":false}]},{"StartTime":223299.0,"Objects":[{"StartTime":223299.0,"Position":188.0,"HyperDash":false}]},{"StartTime":223480.0,"Objects":[{"StartTime":223480.0,"Position":280.0,"HyperDash":false}]},{"StartTime":223571.0,"Objects":[{"StartTime":223571.0,"Position":328.0,"HyperDash":false}]},{"StartTime":223662.0,"Objects":[{"StartTime":223662.0,"Position":376.0,"HyperDash":false},{"StartTime":223734.0,"Position":377.0,"HyperDash":false},{"StartTime":223843.0,"Position":376.0,"HyperDash":false}]},{"StartTime":224026.0,"Objects":[{"StartTime":224026.0,"Position":196.0,"HyperDash":false},{"StartTime":224098.0,"Position":161.187851,"HyperDash":false},{"StartTime":224207.0,"Position":111.0,"HyperDash":true}]},{"StartTime":224390.0,"Objects":[{"StartTime":224390.0,"Position":376.0,"HyperDash":false},{"StartTime":224480.0,"Position":398.812927,"HyperDash":false},{"StartTime":224571.0,"Position":435.886963,"HyperDash":false},{"StartTime":224644.0,"Position":405.66272,"HyperDash":false},{"StartTime":224753.0,"Position":375.3338,"HyperDash":false}]},{"StartTime":225117.0,"Objects":[{"StartTime":225117.0,"Position":96.0,"HyperDash":false},{"StartTime":225189.0,"Position":107.0,"HyperDash":false},{"StartTime":225298.0,"Position":96.0,"HyperDash":false}]},{"StartTime":225480.0,"Objects":[{"StartTime":225480.0,"Position":180.0,"HyperDash":false}]},{"StartTime":225662.0,"Objects":[{"StartTime":225662.0,"Position":356.0,"HyperDash":false}]},{"StartTime":225753.0,"Objects":[{"StartTime":225753.0,"Position":400.0,"HyperDash":false}]},{"StartTime":225844.0,"Objects":[{"StartTime":225844.0,"Position":444.0,"HyperDash":false},{"StartTime":225916.0,"Position":453.0,"HyperDash":false},{"StartTime":226025.0,"Position":444.0,"HyperDash":false}]},{"StartTime":226208.0,"Objects":[{"StartTime":226208.0,"Position":360.0,"HyperDash":false},{"StartTime":226280.0,"Position":323.187836,"HyperDash":false},{"StartTime":226389.0,"Position":275.0,"HyperDash":false}]},{"StartTime":226571.0,"Objects":[{"StartTime":226571.0,"Position":96.0,"HyperDash":false},{"StartTime":226643.0,"Position":104.0,"HyperDash":false},{"StartTime":226752.0,"Position":96.0,"HyperDash":false}]},{"StartTime":226935.0,"Objects":[{"StartTime":226935.0,"Position":181.0,"HyperDash":false},{"StartTime":227007.0,"Position":146.187851,"HyperDash":false},{"StartTime":227116.0,"Position":96.0,"HyperDash":false}]},{"StartTime":227299.0,"Objects":[{"StartTime":227299.0,"Position":276.0,"HyperDash":false},{"StartTime":227389.0,"Position":322.148743,"HyperDash":false},{"StartTime":227480.0,"Position":360.0,"HyperDash":false},{"StartTime":227553.0,"Position":357.0,"HyperDash":false},{"StartTime":227662.0,"Position":360.0,"HyperDash":false}]},{"StartTime":227844.0,"Objects":[{"StartTime":227844.0,"Position":276.0,"HyperDash":false}]},{"StartTime":228026.0,"Objects":[{"StartTime":228026.0,"Position":96.0,"HyperDash":false},{"StartTime":228098.0,"Position":82.0,"HyperDash":false},{"StartTime":228207.0,"Position":96.0,"HyperDash":false}]},{"StartTime":228390.0,"Objects":[{"StartTime":228390.0,"Position":180.0,"HyperDash":false},{"StartTime":228462.0,"Position":193.0,"HyperDash":false},{"StartTime":228571.0,"Position":180.0,"HyperDash":false}]},{"StartTime":228753.0,"Objects":[{"StartTime":228753.0,"Position":356.0,"HyperDash":false}]},{"StartTime":228935.0,"Objects":[{"StartTime":228935.0,"Position":440.0,"HyperDash":false}]},{"StartTime":229117.0,"Objects":[{"StartTime":229117.0,"Position":440.0,"HyperDash":false}]},{"StartTime":229299.0,"Objects":[{"StartTime":229299.0,"Position":356.0,"HyperDash":false}]},{"StartTime":229480.0,"Objects":[{"StartTime":229480.0,"Position":176.0,"HyperDash":false},{"StartTime":229552.0,"Position":177.0,"HyperDash":false},{"StartTime":229661.0,"Position":176.0,"HyperDash":false}]},{"StartTime":229844.0,"Objects":[{"StartTime":229844.0,"Position":264.0,"HyperDash":false}]},{"StartTime":229934.0,"Objects":[{"StartTime":229934.0,"Position":310.0,"HyperDash":false}]},{"StartTime":230025.0,"Objects":[{"StartTime":230025.0,"Position":356.0,"HyperDash":false}]},{"StartTime":230208.0,"Objects":[{"StartTime":230208.0,"Position":176.0,"HyperDash":false},{"StartTime":230298.0,"Position":147.851242,"HyperDash":false},{"StartTime":230389.0,"Position":91.23416,"HyperDash":false},{"StartTime":230462.0,"Position":41.0468369,"HyperDash":false},{"StartTime":230571.0,"Position":6.0,"HyperDash":false}]},{"StartTime":230753.0,"Objects":[{"StartTime":230753.0,"Position":92.0,"HyperDash":false}]},{"StartTime":230935.0,"Objects":[{"StartTime":230935.0,"Position":268.0,"HyperDash":false},{"StartTime":231007.0,"Position":314.812164,"HyperDash":false},{"StartTime":231116.0,"Position":353.0,"HyperDash":false}]},{"StartTime":231299.0,"Objects":[{"StartTime":231299.0,"Position":260.0,"HyperDash":false},{"StartTime":231371.0,"Position":259.0,"HyperDash":false},{"StartTime":231480.0,"Position":260.0,"HyperDash":false}]},{"StartTime":231571.0,"Objects":[{"StartTime":231571.0,"Position":308.0,"HyperDash":false}]},{"StartTime":231662.0,"Objects":[{"StartTime":231662.0,"Position":356.0,"HyperDash":false},{"StartTime":231752.0,"Position":386.148743,"HyperDash":false},{"StartTime":231843.0,"Position":440.0,"HyperDash":false},{"StartTime":231916.0,"Position":455.0,"HyperDash":false},{"StartTime":232025.0,"Position":440.0,"HyperDash":false}]},{"StartTime":232208.0,"Objects":[{"StartTime":232208.0,"Position":356.0,"HyperDash":false}]},{"StartTime":232390.0,"Objects":[{"StartTime":232390.0,"Position":180.0,"HyperDash":false},{"StartTime":232462.0,"Position":176.0,"HyperDash":false},{"StartTime":232571.0,"Position":180.0,"HyperDash":false}]},{"StartTime":232753.0,"Objects":[{"StartTime":232753.0,"Position":272.0,"HyperDash":false},{"StartTime":232843.0,"Position":272.0,"HyperDash":false},{"StartTime":232934.0,"Position":272.0,"HyperDash":false}]},{"StartTime":233117.0,"Objects":[{"StartTime":233117.0,"Position":92.0,"HyperDash":false},{"StartTime":233207.0,"Position":62.53518,"HyperDash":false},{"StartTime":233298.0,"Position":40.0084,"HyperDash":false},{"StartTime":233371.0,"Position":54.41962,"HyperDash":false},{"StartTime":233480.0,"Position":88.82208,"HyperDash":false}]},{"StartTime":233662.0,"Objects":[{"StartTime":233662.0,"Position":172.0,"HyperDash":false}]},{"StartTime":233844.0,"Objects":[{"StartTime":233844.0,"Position":352.0,"HyperDash":false},{"StartTime":233916.0,"Position":341.0,"HyperDash":false},{"StartTime":234025.0,"Position":352.0,"HyperDash":false}]},{"StartTime":234208.0,"Objects":[{"StartTime":234208.0,"Position":268.0,"HyperDash":false}]},{"StartTime":234390.0,"Objects":[{"StartTime":234390.0,"Position":360.0,"HyperDash":false}]},{"StartTime":234571.0,"Objects":[{"StartTime":234571.0,"Position":172.0,"HyperDash":false},{"StartTime":234661.0,"Position":172.0,"HyperDash":false},{"StartTime":234752.0,"Position":172.0,"HyperDash":false}]},{"StartTime":234935.0,"Objects":[{"StartTime":234935.0,"Position":268.0,"HyperDash":false},{"StartTime":235007.0,"Position":228.187851,"HyperDash":false},{"StartTime":235116.0,"Position":183.0,"HyperDash":false}]},{"StartTime":235298.0,"Objects":[{"StartTime":235298.0,"Position":364.0,"HyperDash":false},{"StartTime":235370.0,"Position":353.0,"HyperDash":false},{"StartTime":235479.0,"Position":364.0,"HyperDash":false}]},{"StartTime":235662.0,"Objects":[{"StartTime":235662.0,"Position":183.0,"HyperDash":false}]},{"StartTime":235752.0,"Objects":[{"StartTime":235752.0,"Position":140.0,"HyperDash":false}]},{"StartTime":235843.0,"Objects":[{"StartTime":235843.0,"Position":98.0,"HyperDash":true}]},{"StartTime":236026.0,"Objects":[{"StartTime":236026.0,"Position":376.0,"HyperDash":false}]},{"StartTime":236390.0,"Objects":[{"StartTime":236390.0,"Position":224.0,"HyperDash":false}]},{"StartTime":236753.0,"Objects":[{"StartTime":236753.0,"Position":496.0,"HyperDash":false},{"StartTime":236843.0,"Position":487.0,"HyperDash":false},{"StartTime":236934.0,"Position":496.0,"HyperDash":false},{"StartTime":237007.0,"Position":494.0,"HyperDash":false},{"StartTime":237116.0,"Position":496.0,"HyperDash":false}]},{"StartTime":237480.0,"Objects":[{"StartTime":237480.0,"Position":266.0,"HyperDash":false},{"StartTime":237548.0,"Position":100.0,"HyperDash":false},{"StartTime":237616.0,"Position":57.0,"HyperDash":false},{"StartTime":237684.0,"Position":199.0,"HyperDash":false},{"StartTime":237752.0,"Position":129.0,"HyperDash":false},{"StartTime":237820.0,"Position":232.0,"HyperDash":false},{"StartTime":237889.0,"Position":464.0,"HyperDash":false},{"StartTime":237957.0,"Position":364.0,"HyperDash":false},{"StartTime":238025.0,"Position":170.0,"HyperDash":false},{"StartTime":238093.0,"Position":496.0,"HyperDash":false},{"StartTime":238161.0,"Position":27.0,"HyperDash":false},{"StartTime":238230.0,"Position":477.0,"HyperDash":false},{"StartTime":238298.0,"Position":163.0,"HyperDash":false},{"StartTime":238366.0,"Position":260.0,"HyperDash":false},{"StartTime":238434.0,"Position":253.0,"HyperDash":false},{"StartTime":238502.0,"Position":423.0,"HyperDash":false},{"StartTime":238571.0,"Position":367.0,"HyperDash":false}]},{"StartTime":238935.0,"Objects":[{"StartTime":238935.0,"Position":256.0,"HyperDash":false},{"StartTime":239025.0,"Position":247.0,"HyperDash":false},{"StartTime":239116.0,"Position":256.0,"HyperDash":false},{"StartTime":239189.0,"Position":240.0,"HyperDash":false},{"StartTime":239298.0,"Position":256.0,"HyperDash":false}]},{"StartTime":239662.0,"Objects":[{"StartTime":239662.0,"Position":78.0,"HyperDash":false},{"StartTime":239713.0,"Position":446.0,"HyperDash":false},{"StartTime":239764.0,"Position":99.0,"HyperDash":false},{"StartTime":239815.0,"Position":155.0,"HyperDash":false},{"StartTime":239866.0,"Position":322.0,"HyperDash":false},{"StartTime":239917.0,"Position":261.0,"HyperDash":false},{"StartTime":239968.0,"Position":22.0,"HyperDash":false},{"StartTime":240019.0,"Position":481.0,"HyperDash":false},{"StartTime":240071.0,"Position":103.0,"HyperDash":false},{"StartTime":240122.0,"Position":316.0,"HyperDash":false},{"StartTime":240173.0,"Position":175.0,"HyperDash":false},{"StartTime":240224.0,"Position":48.0,"HyperDash":false},{"StartTime":240275.0,"Position":307.0,"HyperDash":false},{"StartTime":240326.0,"Position":375.0,"HyperDash":false},{"StartTime":240377.0,"Position":149.0,"HyperDash":false},{"StartTime":240429.0,"Position":250.0,"HyperDash":false},{"StartTime":240480.0,"Position":142.0,"HyperDash":false},{"StartTime":240531.0,"Position":170.0,"HyperDash":false},{"StartTime":240582.0,"Position":281.0,"HyperDash":false},{"StartTime":240633.0,"Position":444.0,"HyperDash":false},{"StartTime":240684.0,"Position":414.0,"HyperDash":false},{"StartTime":240735.0,"Position":321.0,"HyperDash":false},{"StartTime":240787.0,"Position":328.0,"HyperDash":false},{"StartTime":240838.0,"Position":32.0,"HyperDash":false},{"StartTime":240889.0,"Position":259.0,"HyperDash":false},{"StartTime":240940.0,"Position":169.0,"HyperDash":false},{"StartTime":240991.0,"Position":207.0,"HyperDash":false},{"StartTime":241042.0,"Position":464.0,"HyperDash":false},{"StartTime":241093.0,"Position":192.0,"HyperDash":false},{"StartTime":241145.0,"Position":317.0,"HyperDash":false},{"StartTime":241196.0,"Position":376.0,"HyperDash":false},{"StartTime":241247.0,"Position":100.0,"HyperDash":false},{"StartTime":241298.0,"Position":70.0,"HyperDash":false},{"StartTime":241349.0,"Position":287.0,"HyperDash":false},{"StartTime":241400.0,"Position":468.0,"HyperDash":false},{"StartTime":241451.0,"Position":58.0,"HyperDash":false},{"StartTime":241503.0,"Position":352.0,"HyperDash":false},{"StartTime":241554.0,"Position":305.0,"HyperDash":false},{"StartTime":241605.0,"Position":177.0,"HyperDash":false},{"StartTime":241656.0,"Position":414.0,"HyperDash":false},{"StartTime":241707.0,"Position":182.0,"HyperDash":false},{"StartTime":241758.0,"Position":174.0,"HyperDash":false},{"StartTime":241809.0,"Position":89.0,"HyperDash":false},{"StartTime":241861.0,"Position":254.0,"HyperDash":false},{"StartTime":241912.0,"Position":320.0,"HyperDash":false},{"StartTime":241963.0,"Position":406.0,"HyperDash":false},{"StartTime":242014.0,"Position":182.0,"HyperDash":false},{"StartTime":242065.0,"Position":301.0,"HyperDash":false},{"StartTime":242116.0,"Position":169.0,"HyperDash":false},{"StartTime":242167.0,"Position":470.0,"HyperDash":false},{"StartTime":242219.0,"Position":278.0,"HyperDash":false},{"StartTime":242270.0,"Position":146.0,"HyperDash":false},{"StartTime":242321.0,"Position":480.0,"HyperDash":false},{"StartTime":242372.0,"Position":41.0,"HyperDash":false},{"StartTime":242423.0,"Position":51.0,"HyperDash":false},{"StartTime":242474.0,"Position":295.0,"HyperDash":false},{"StartTime":242525.0,"Position":145.0,"HyperDash":false},{"StartTime":242577.0,"Position":237.0,"HyperDash":false},{"StartTime":242628.0,"Position":152.0,"HyperDash":false},{"StartTime":242679.0,"Position":500.0,"HyperDash":false},{"StartTime":242730.0,"Position":278.0,"HyperDash":false},{"StartTime":242781.0,"Position":174.0,"HyperDash":false},{"StartTime":242832.0,"Position":92.0,"HyperDash":false},{"StartTime":242883.0,"Position":248.0,"HyperDash":false},{"StartTime":242935.0,"Position":284.0,"HyperDash":false},{"StartTime":242986.0,"Position":296.0,"HyperDash":false},{"StartTime":243037.0,"Position":325.0,"HyperDash":false},{"StartTime":243088.0,"Position":116.0,"HyperDash":false},{"StartTime":243139.0,"Position":293.0,"HyperDash":false},{"StartTime":243190.0,"Position":511.0,"HyperDash":false},{"StartTime":243241.0,"Position":17.0,"HyperDash":false},{"StartTime":243292.0,"Position":64.0,"HyperDash":false},{"StartTime":243344.0,"Position":486.0,"HyperDash":false},{"StartTime":243395.0,"Position":209.0,"HyperDash":false},{"StartTime":243446.0,"Position":264.0,"HyperDash":false},{"StartTime":243497.0,"Position":47.0,"HyperDash":false},{"StartTime":243548.0,"Position":206.0,"HyperDash":false},{"StartTime":243599.0,"Position":353.0,"HyperDash":false},{"StartTime":243650.0,"Position":244.0,"HyperDash":false},{"StartTime":243702.0,"Position":157.0,"HyperDash":false},{"StartTime":243753.0,"Position":227.0,"HyperDash":false},{"StartTime":243804.0,"Position":167.0,"HyperDash":false},{"StartTime":243855.0,"Position":420.0,"HyperDash":false},{"StartTime":243906.0,"Position":103.0,"HyperDash":false},{"StartTime":243957.0,"Position":188.0,"HyperDash":false},{"StartTime":244008.0,"Position":300.0,"HyperDash":false},{"StartTime":244060.0,"Position":60.0,"HyperDash":false},{"StartTime":244111.0,"Position":120.0,"HyperDash":false},{"StartTime":244162.0,"Position":501.0,"HyperDash":false},{"StartTime":244213.0,"Position":341.0,"HyperDash":false},{"StartTime":244264.0,"Position":181.0,"HyperDash":false},{"StartTime":244315.0,"Position":337.0,"HyperDash":false},{"StartTime":244366.0,"Position":269.0,"HyperDash":false},{"StartTime":244418.0,"Position":398.0,"HyperDash":false},{"StartTime":244469.0,"Position":308.0,"HyperDash":false},{"StartTime":244520.0,"Position":323.0,"HyperDash":false},{"StartTime":244571.0,"Position":201.0,"HyperDash":false},{"StartTime":244622.0,"Position":204.0,"HyperDash":false},{"StartTime":244673.0,"Position":44.0,"HyperDash":false},{"StartTime":244724.0,"Position":217.0,"HyperDash":false},{"StartTime":244776.0,"Position":510.0,"HyperDash":false},{"StartTime":244827.0,"Position":324.0,"HyperDash":false},{"StartTime":244878.0,"Position":131.0,"HyperDash":false},{"StartTime":244929.0,"Position":13.0,"HyperDash":false},{"StartTime":244980.0,"Position":360.0,"HyperDash":false},{"StartTime":245031.0,"Position":510.0,"HyperDash":false},{"StartTime":245082.0,"Position":203.0,"HyperDash":false},{"StartTime":245134.0,"Position":416.0,"HyperDash":false},{"StartTime":245185.0,"Position":162.0,"HyperDash":false},{"StartTime":245236.0,"Position":277.0,"HyperDash":false},{"StartTime":245287.0,"Position":329.0,"HyperDash":false},{"StartTime":245338.0,"Position":357.0,"HyperDash":false},{"StartTime":245389.0,"Position":388.0,"HyperDash":false},{"StartTime":245440.0,"Position":87.0,"HyperDash":false},{"StartTime":245492.0,"Position":462.0,"HyperDash":false},{"StartTime":245543.0,"Position":357.0,"HyperDash":false},{"StartTime":245594.0,"Position":343.0,"HyperDash":false},{"StartTime":245645.0,"Position":248.0,"HyperDash":false},{"StartTime":245696.0,"Position":174.0,"HyperDash":false},{"StartTime":245747.0,"Position":112.0,"HyperDash":false},{"StartTime":245798.0,"Position":420.0,"HyperDash":false},{"StartTime":245850.0,"Position":229.0,"HyperDash":false},{"StartTime":245901.0,"Position":270.0,"HyperDash":false},{"StartTime":245952.0,"Position":3.0,"HyperDash":false},{"StartTime":246003.0,"Position":446.0,"HyperDash":false},{"StartTime":246054.0,"Position":78.0,"HyperDash":false},{"StartTime":246105.0,"Position":157.0,"HyperDash":false},{"StartTime":246156.0,"Position":344.0,"HyperDash":false},{"StartTime":246208.0,"Position":72.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3689906.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3689906.osu new file mode 100644 index 0000000000..070143fcf1 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3689906.osu @@ -0,0 +1,942 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 2 + +[Difficulty] +HPDrainRate:4 +CircleSize:3.2 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:1.7 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +2,125844,129844 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +390,363.636363636364,4,2,1,60,1,0 +3480,-100,4,2,2,70,0,0 +3662,-100,4,2,1,60,0,0 +4753,-100,4,2,2,50,0,0 +4935,-100,4,2,1,60,0,0 +6208,-100,4,2,3,60,0,0 +6390,-100,4,2,1,60,0,0 +9299,-100,4,2,2,70,0,0 +9480,-100,4,2,1,60,0,0 +12026,-100,4,2,3,70,0,0 +12208,-100,4,2,1,70,0,0 +23662,-83.3333333333333,4,2,3,70,0,0 +24026,-100,4,2,1,80,0,0 +26753,-100,4,2,2,80,0,0 +26935,-100,4,2,1,80,0,0 +28935,-83.3333333333333,4,2,1,80,0,0 +29480,-83.3333333333333,4,2,3,70,0,0 +30026,-100,4,2,1,70,0,0 +30935,-100,4,2,1,30,0,0 +31662,-100,4,2,1,40,0,0 +32390,-100,4,2,1,30,0,0 +32753,-100,4,2,1,40,0,0 +33117,-100,4,2,1,50,0,0 +33480,-100,4,2,1,60,0,0 +33844,-100,4,2,1,70,0,0 +34117,-100,4,2,1,40,0,0 +34208,-100,4,2,1,70,0,0 +34299,-100,4,2,1,40,0,0 +34480,-100,4,2,1,70,0,0 +34662,-100,4,2,1,40,0,0 +34753,-100,4,2,1,70,0,0 +34935,-100,4,2,77,80,0,0 +35299,-83.3333333333333,4,2,3,80,0,0 +35662,-100,4,2,1,80,0,0 +38753,-100,4,2,1,80,0,0 +39117,-100,4,2,1,80,0,0 +44026,-83.3333333333333,4,2,1,80,0,0 +44390,-100,4,2,1,80,0,0 +46208,-100,4,2,1,80,0,0 +46571,-100,4,2,77,90,0,0 +46753,-100,4,2,1,80,0,0 +46935,-100,4,2,3,80,0,0 +47117,-100,4,2,1,80,0,0 +52390,-100,4,2,1,80,0,0 +52753,-100,4,2,1,80,0,0 +55662,-100,4,2,1,85,0,0 +57117,-100,4,2,1,90,0,0 +58208,-100,4,2,77,90,0,0 +58390,-100,4,2,1,80,0,0 +58571,-100,4,2,3,90,0,1 +58753,-100,4,2,1,90,0,1 +69844,-100,4,2,1,90,0,0 +70208,-100,4,2,3,90,0,1 +70390,-100,4,2,1,90,0,1 +82935,-100,4,2,77,90,0,1 +83299,-83.3333333333333,4,2,3,80,0,0 +83662,-100,4,2,1,80,0,0 +88753,-100,4,2,1,80,0,0 +89117,-100,4,2,1,80,0,0 +94571,-100,4,2,77,80,0,0 +94753,-100,4,2,1,80,0,0 +94935,-100,4,2,3,80,0,0 +95117,-100,4,2,1,80,0,0 +106208,-100,4,2,77,80,0,0 +106571,-100,4,2,1,80,0,0 +112390,-100,4,2,77,90,0,0 +112571,-100,4,2,1,80,0,0 +117117,-100,4,2,1,40,0,0 +117480,-100,4,2,1,50,0,0 +117844,-100,4,2,1,60,0,0 +118208,-100,4,2,1,55,0,0 +118571,-100,4,2,1,65,0,0 +118935,-100,4,2,1,75,0,0 +119299,-100,4,2,1,85,0,0 +119662,-100,4,2,3,100,0,0 +120026,-100,4,2,1,30,0,0 +125480,-100,4,2,1,5,0,0 +131299,-100,4,2,4,60,0,0 +136390,-100,4,2,4,60,0,0 +137117,-100,4,2,4,70,0,0 +137480,-100,4,2,4,50,0,0 +138571,-100,4,2,4,60,0,0 +141480,-90.9090909090909,4,2,4,50,0,0 +141844,-100,4,2,4,50,0,0 +142662,-100,4,2,78,50,0,0 +143117,-100,4,2,4,60,0,0 +148753,-100,4,2,78,50,0,0 +148935,-100,4,2,4,60,0,0 +153117,-100,4,2,4,50,0,0 +154571,-100,4,2,78,50,0,0 +154753,-100,4,2,4,60,0,0 +160390,-100,4,2,3,60,0,0 +160571,-100,4,2,4,60,0,0 +163299,-100,4,2,1,30,0,0 +163480,-100,4,2,5,30,0,0 +163571,-100,4,2,1,30,0,0 +163662,-100,4,2,1,40,0,0 +163844,-100,4,2,5,40,0,0 +163935,-100,4,2,1,40,0,0 +164026,-100,4,2,1,50,0,0 +164208,-100,4,2,5,50,0,0 +164299,-100,4,2,1,50,0,0 +164390,-100,4,2,1,60,0,0 +164571,-100,4,2,5,60,0,0 +164662,-100,4,2,1,60,0,0 +164753,-100,4,2,1,70,0,0 +165117,-100,4,2,1,70,0,0 +165480,-100,4,2,1,70,0,0 +165844,-100,4,2,77,80,0,0 +166208,-100,4,2,1,80,0,0 +174571,-100,4,2,1,80,0,0 +174935,-100,4,2,1,80,0,0 +177844,-83.3333333333333,4,2,1,80,0,0 +178208,-100,4,2,1,80,0,0 +186571,-100,4,2,1,80,0,0 +187117,-100,4,2,1,80,0,0 +187480,-100,4,2,1,80,0,0 +188571,-100,4,2,1,80,0,0 +188934,-100,4,2,1,80,0,0 +189480,-83.3333333333333,4,2,3,90,0,1 +189844,-100,4,2,1,90,0,1 +194208,-100,4,2,1,90,0,1 +194571,-100,4,2,1,90,0,1 +195844,-100,4,2,1,90,0,1 +196208,-100,4,2,1,90,0,1 +200753,-100,4,2,1,90,0,1 +200935,-100,4,2,1,90,0,0 +201117,-83.3333333333333,4,2,1,90,0,1 +201480,-100,4,2,1,90,0,1 +212026,-100,4,2,77,90,0,1 +212571,-100,4,2,1,90,0,0 +212753,-100,4,2,3,90,0,1 +212935,-100,4,2,1,90,0,1 +214026,-100,4,2,5,60,0,1 +214117,-100,4,2,1,90,0,1 +215480,-100,4,2,5,60,0,1 +215571,-100,4,2,1,90,0,1 +216935,-100,4,2,5,60,0,1 +217026,-100,4,2,1,90,0,1 +218390,-100,4,2,5,60,0,1 +218481,-100,4,2,1,90,0,1 +219844,-100,4,2,5,60,0,1 +219935,-100,4,2,1,90,0,1 +221299,-100,4,2,5,60,0,1 +221390,-100,4,2,1,90,0,1 +222753,-100,4,2,5,60,0,1 +222844,-100,4,2,1,90,0,1 +224208,-100,4,2,77,90,0,1 +224390,-83.3333333333333,4,2,3,80,0,0 +224753,-100,4,2,1,80,0,0 +225662,-100,4,2,5,60,0,0 +225753,-100,4,2,1,90,0,0 +226026,-100,4,2,5,60,0,0 +226117,-100,4,2,1,90,0,0 +227844,-100,4,2,5,60,0,0 +227935,-100,4,2,1,90,0,0 +228571,-100,4,2,5,60,0,0 +228662,-100,4,2,1,90,0,0 +230026,-100,4,2,5,60,0,0 +230117,-100,4,2,1,90,0,0 +230753,-100,4,2,5,60,0,0 +230844,-100,4,2,1,90,0,0 +231480,-100,4,2,5,60,0,0 +231571,-100,4,2,1,90,0,0 +232208,-100,4,2,5,60,0,0 +232299,-100,4,2,1,90,0,0 +233117,-100,4,2,1,35,0,0 +233480,-100,4,2,1,45,0,0 +233844,-100,4,2,1,55,0,0 +234208,-100,4,2,1,75,0,0 +234299,-100,4,2,1,65,0,0 +234390,-100,4,2,1,75,0,0 +234480,-100,4,2,1,65,0,0 +234571,-100,4,2,1,75,0,0 +234935,-100,4,2,1,85,0,0 +235299,-100,4,2,1,95,0,0 +235662,-100,4,2,1,85,0,0 +236026,-100,4,2,3,80,0,0 +236390,-100,4,2,4,70,0,0 +236753,-100,4,2,78,70,0,0 +237480,-100,4,2,0,50,0,0 +237844,-100,4,2,0,40,0,0 +238208,-100,4,2,0,30,0,0 +238571,-100,4,2,0,20,0,0 +238935,-100,4,2,78,50,0,0 +239662,-100,4,2,0,50,0,0 +240390,-100,4,2,0,45,0,0 +241117,-100,4,2,0,40,0,0 +241844,-100,4,2,0,35,0,0 +242571,-100,4,2,0,30,0,0 +243299,-100,4,2,0,25,0,0 +244026,-100,4,2,0,20,0,0 +244753,-100,4,2,0,15,0,0 +245480,-100,4,2,0,10,0,0 +246208,-100,4,2,0,5,0,0 + +[HitObjects] +124,320,390,6,0,L|124:128,1,170,4|0,0:0|0:0,0:0:0:0: +208,148,935,1,0,0:0:0:0: +380,192,1117,2,0,L|380:16,1,170,8|2,0:0|0:0,0:0:0:0: +208,24,1844,5,0,0:0:0:0: +360,24,2208,1,2,0:0:0:0: +188,24,2390,1,2,0:0:0:0: +152,24,2480,1,2,0:0:0:0: +112,24,2571,2,0,L|112:128,1,85,8|2,0:0|0:0,0:0:0:0: +196,108,2935,1,0,0:0:0:0: +280,108,3117,1,0,0:0:0:0: +196,108,3299,5,2,0:0:0:0: +288,108,3480,2,0,L|288:292,1,170,2|0,0:0|0:0,0:0:0:0: +116,312,4026,1,8,0:0:0:0: +300,280,4390,1,2,0:0:0:0: +28,192,4753,6,0,L|28:100,1,85,4|2,0:0|0:0,0:0:0:0: +112,108,5117,1,0,0:0:0:0: +20,108,5299,1,2,0:0:0:0: +192,108,5480,2,0,L|280:108,2,85,8|2|0,0:0|0:0|0:0,0:0:0:0: +484,364,6208,6,0,L|484:172,1,170,14|0,0:0|0:0,0:0:0:0: +400,192,6753,1,0,0:0:0:0: +228,236,6935,2,0,L|228:60,1,170,8|2,0:0|0:0,0:0:0:0: +396,64,7662,5,0,0:0:0:0: +244,64,8026,1,2,0:0:0:0: +416,64,8208,1,2,0:0:0:0: +452,64,8298,1,2,0:0:0:0: +492,64,8389,2,0,L|492:168,1,85,8|2,0:0|0:0,0:0:0:0: +396,148,8753,1,0,0:0:0:0: +304,148,8935,1,0,0:0:0:0: +212,148,9117,5,2,0:0:0:0: +312,148,9298,2,0,L|312:332,1,170,2|0,0:0|0:0,0:0:0:0: +140,352,9844,1,8,0:0:0:0: +324,320,10208,1,2,0:0:0:0: +136,192,10571,6,0,L|232:192,1,85,2|2,0:0|0:0,0:0:0:0: +128,192,10935,2,0,L|216:192,1,85,0|2,0:0|0:0,0:0:0:0: +384,192,11299,1,8,0:0:0:0: +292,192,11480,1,2,0:0:0:0: +200,192,11662,1,0,0:0:0:0: +488,192,12026,6,0,B|488:108|488:108|400:108,1,170,10|0,0:0|0:0,0:0:0:0: +316,108,12571,1,0,0:0:0:0: +144,108,12753,2,0,L|144:296,1,170,8|2,0:0|0:0,0:0:0:0: +314,278,13480,6,0,L|134:278,1,170,0|2,0:0|0:0,0:0:0:0: +144,278,14026,1,2,0:0:0:0: +314,278,14208,2,0,L|406:278,1,85,8|2,0:0|0:0,0:0:0:0: +304,276,14571,2,0,L|304:172,1,85,0|0,0:0|0:0,0:0:0:0: +132,192,14935,6,0,B|48:192|48:192|48:104,1,170,2|0,0:0|0:0,0:0:0:0: +132,104,15480,1,0,0:0:0:0: +304,48,15662,1,8,0:0:0:0: +132,104,16026,1,2,0:0:0:0: +284,104,16390,6,0,L|284:188,1,85,0|0,0:0|0:0,0:0:0:0: +192,192,16753,1,2,0:0:0:0: +192,192,16935,1,2,0:0:0:0: +364,192,17117,2,0,L|456:192,2,85,8|2|0,0:0|0:0|0:0,0:0:0:0: +64,192,17844,6,0,L|64:292,1,85,2|0,0:0|0:0,0:0:0:0: +148,192,18208,2,0,L|148:288,1,85,0|0,0:0|0:0,0:0:0:0: +320,192,18571,1,8,0:0:0:0: +132,192,18935,1,2,0:0:0:0: +132,192,19299,6,0,L|304:192,1,170,0|2,0:0|0:0,0:0:0:0: +388,192,19844,1,2,0:0:0:0: +216,192,20026,2,0,L|124:192,1,85,8|2,0:0|0:0,0:0:0:0: +224,192,20390,2,0,L|224:100,1,85,0|0,0:0|0:0,0:0:0:0: +52,20,20753,6,0,B|52:108|52:108|140:108,1,170,2|0,0:0|0:0,0:0:0:0: +224,107,21299,1,0,0:0:0:0: +396,192,21480,1,8,0:0:0:0: +224,192,21844,1,2,0:0:0:0: +132,192,22026,1,2,0:0:0:0: +224,192,22208,5,0,0:0:0:0: +176,192,22299,1,2,0:0:0:0: +132,192,22390,1,2,0:0:0:0: +232,192,22571,1,2,0:0:0:0: +404,192,22753,1,8,0:0:0:0: +232,192,22935,2,0,L|232:288,1,85,8|2,0:0|0:0,0:0:0:0: +404,277,23299,1,2,0:0:0:0: +448,276,23389,1,2,0:0:0:0: +492,276,23480,1,2,0:0:0:0: +212,192,23662,6,0,L|8:192,1,203.999993774414,10|0,0:0|0:0,0:0:0:0: +92,192,24208,1,0,0:0:0:0: +272,192,24390,2,0,L|272:96,1,85,8|0,0:0|0:0,0:0:0:0: +180,108,24753,1,2,0:0:0:0: +348,104,25117,6,0,L|252:104,1,85,0|0,0:0|0:0,0:0:0:0: +355,105,25480,1,2,0:0:0:0: +179,105,25662,1,2,0:0:0:0: +135,105,25752,1,2,0:0:0:0: +91,105,25843,2,0,L|7:105,2,85,8|2|0,0:0|0:0|0:0,0:0:0:0: +383,105,26571,5,2,0:0:0:0: +299,105,26753,2,0,B|215:105|215:105|215:193,1,170,2|0,0:0|0:0,0:0:0:0: +391,105,27299,1,8,0:0:0:0: +239,193,27662,2,0,L|239:281,1,85,2|0,0:0|0:0,0:0:0:0: +323,277,28026,5,0,0:0:0:0: +231,277,28208,1,2,0:0:0:0: +315,277,28390,1,0,0:0:0:0: +143,277,28571,1,2,0:0:0:0: +315,277,28753,1,8,0:0:0:0: +407,277,28935,2,0,B|508:276|508:276|508:168,1,203.999993774414,2|0,0:0|0:0,0:0:0:0: +212,192,29480,6,0,B|108:192|108:192|108:92|108:92|212:92,1,305.999990661621,6|0,0:0|0:0,0:0:0:0: +304,92,30208,2,0,L|392:92,2,85,2|0|2,0:0|0:0|0:0,0:0:0:0: +152,96,30935,6,0,L|152:180,1,85,2|2,0:0|0:0,0:0:0:0: +236,192,31299,2,0,L|236:296,1,85,2|2,0:0|0:0,0:0:0:0: +320,276,31662,2,0,L|232:276,2,85,2|2|2,0:0|0:0|0:0,0:0:0:0: +256,192,32390,12,8,33480,0:0:0:0: +428,192,33844,6,0,L|428:132,2,42.5,2|2|2,0:0|0:0|0:0,0:0:0:0: +256,192,34208,2,0,L|160:192,1,85,2|8,0:0|0:0,0:0:0:0: +216,192,34480,1,2,0:0:0:0: +264,192,34571,2,0,L|316:192,2,42.5,2|8|2,0:0|0:0|0:0,0:0:0:0: +92,192,34935,2,0,L|8:192,1,85,12|8,0:0|0:0,0:0:0:0: +288,192,35299,6,0,L|492:192,1,203.999993774414,10|8,3:2|3:2,3:3:0:0: +400,192,35844,1,2,3:2:0:0: +224,192,36026,2,0,L|136:192,1,85,0|2,3:2|3:2,0:0:0:0: +232,192,36390,2,0,L|232:104,1,85,8|0,3:2|3:2,3:3:0:0: +56,32,36753,6,0,L|56:116,1,85,6|0,3:2|3:2,3:3:0:0: +104,120,37026,1,0,3:2:0:0: +152,124,37117,1,8,3:2:0:0: +244,124,37299,1,2,3:2:0:0: +152,124,37480,2,0,L|64:124,1,85,0|2,3:2|3:2,0:0:0:0: +244,124,37844,2,0,L|244:216,1,85,8|0,3:2|3:2,3:3:0:0: +496,296,38208,6,0,B|496:212|496:212|408:212,1,170,6|8,3:2|3:2,3:3:0:0: +504,212,38753,2,0,L|324:212,1,170,2|2,3:2|3:2,3:3:0:0: +156,192,39299,2,0,L|60:192,1,85,8|0,3:2|3:2,3:3:0:0: +252,192,39662,6,0,L|312:192,2,42.5,6|2|2,3:2|3:2|3:2,3:3:0:0: +71,192,40026,2,0,L|71:92,1,85,8|2,3:2|3:2,3:3:0:0: +164,108,40390,2,0,L|80:108,1,85,0|2,3:2|3:2,0:0:0:0: +256,108,40753,2,0,L|340:108,1,85,8|0,3:2|3:2,3:3:0:0: +84,192,41117,6,0,L|276:192,1,170,6|8,3:2|3:2,3:3:0:0: +432,192,41662,2,0,L|432:104,1,85,2|0,3:2|3:2,3:3:0:0: +348,108,42026,1,2,3:2:0:0: +432,192,42208,2,0,L|348:192,1,85,8|0,3:2|3:2,3:3:0:0: +176,192,42571,6,0,L|84:192,1,85,6|0,3:2|3:2,3:3:0:0: +132,192,42844,1,0,3:2:0:0: +176,192,42935,1,8,3:2:0:0: +260,192,43117,2,0,L|176:192,2,85,2|0|2,3:2|3:2|3:2,3:3:0:0: +84,192,43662,2,0,L|84:288,1,85,8|0,3:2|3:2,3:3:0:0: +336,192,44026,6,0,B|436:192|436:192|436:296,1,203.999993774414,6|8,3:2|3:2,3:3:0:0: +344,296,44571,1,2,3:2:0:0: +252,296,44753,2,0,L|252:212,1,85,0|2,3:2|3:2,3:3:0:0: +428,192,45117,2,0,L|340:192,1,85,8|0,3:2|3:2,3:3:0:0: +164,192,45480,5,6,3:2:0:0: +121,192,45570,1,2,3:2:0:0: +79,192,45661,1,2,3:2:0:0: +256,192,45844,2,0,L|256:104,1,85,8|2,3:2|3:2,3:3:0:0: +160,104,46208,2,0,L|244:104,1,85,2|2,3:2|3:2,3:3:0:0: +68,32,46571,2,0,L|68:120,1,85,12|2,3:2|3:2,3:3:0:0: +324,192,46935,6,0,L|408:192,2,85,10|0|8,3:2|3:2|3:2,3:3:0:0: +154,192,47480,2,0,L|338:192,1,170,2|2,3:2|3:2,3:3:0:0: +420,192,48026,2,0,L|420:280,1,85,8|0,3:2|3:2,3:3:0:0: +240,328,48390,6,0,B|156:328,1,85,6|0,3:2|3:2,3:3:0:0: +112,328,48662,1,0,3:2:0:0: +68,328,48753,1,8,3:2:0:0: +160,244,48935,2,0,L|72:244,2,85,2|0|2,3:2|3:2|3:2,0:0:0:0: +336,244,49480,2,0,L|420:244,1,85,8|0,3:2|3:2,3:3:0:0: +164,116,49844,6,0,B|80:116,1,85,6|0,3:2|3:2,3:3:0:0: +79,116,50117,1,0,3:2:0:0: +79,116,50208,1,8,3:2:0:0: +172,116,50390,2,0,B|256:116|256:116|256:28,1,170,2|2,3:2|3:2,3:3:0:0: +80,30,50935,2,0,L|80:126,1,85,8|0,3:2|3:2,3:3:0:0: +256,192,51299,6,0,L|436:192,1,170,6|8,3:2|3:2,3:3:0:0: +340,192,51844,1,2,3:2:0:0: +426,192,52026,2,0,L|338:192,1,85,0|2,3:2|3:2,3:3:0:0: +164,192,52390,2,0,L|64:192,1,85,8|0,3:2|0:0,3:3:0:0: +336,72,52753,6,0,L|508:72,1,170,6|8,3:2|3:2,3:3:0:0: +328,160,53299,2,0,L|500:160,1,170,2|2,3:2|3:2,3:3:0:0: +412,160,53844,2,0,L|412:260,1,85,8|0,3:2|3:2,3:3:0:0: +236,192,54208,6,0,L|144:192,1,85,6|0,3:2|3:2,3:3:0:0: +192,192,54480,1,0,3:2:0:0: +236,192,54571,1,8,3:2:0:0: +320,192,54753,1,2,3:2:0:0: +236,192,54935,1,0,3:2:0:0: +152,192,55117,1,2,3:2:0:0: +328,192,55299,2,0,L|328:280,1,85,8|0,3:2|3:2,3:3:0:0: +72,192,55662,6,0,L|72:100,1,85,6|0,3:2|3:2,3:3:0:0: +116,104,55935,1,0,3:2:0:0: +160,100,56026,1,8,3:2:0:0: +244,100,56208,2,0,L|156:100,2,85,2|0|2,3:2|3:2|3:2,3:3:0:0: +72,107,56753,2,0,L|72:19,1,85,8|0,3:2|3:2,0:0:0:0: +248,192,57117,6,0,L|292:192,2,42.5,2|2|2,3:2|3:2|3:2,0:0:0:0: +78,192,57481,2,0,L|80:92,1,85,8|2,3:2|3:2,0:0:0:0: +164,107,57844,2,0,L|64:107,1,85,8|2,3:2|3:2,3:3:0:0: +248,192,58208,2,0,L|164:192,1,85,12|2,3:2|3:2,3:3:0:0: +416,192,58571,6,0,B|500:192|500:192|412:192,1,170,10|8,3:2|3:2,3:3:0:0: +320,192,59117,1,2,3:2:0:0: +140,192,59299,2,0,L|56:192,2,85,0|2|8,3:2|3:2|3:2,0:0:0:0: +428,192,60026,6,0,L|428:104,1,85,2|0,3:2|3:2,3:3:0:0: +332,108,60390,2,0,L|420:108,1,85,8|2,3:2|3:2,3:3:0:0: +324,108,60753,1,2,3:2:0:0: +366,108,60843,1,2,3:2:0:0: +409,108,60934,1,2,3:2:0:0: +228,108,61117,2,0,L|140:108,1,85,8|0,3:2|3:2,3:3:0:0: +324,108,61480,6,0,L|324:280,1,170,2|8,3:2|3:2,3:3:0:0: +228,280,62026,1,2,3:2:0:0: +408,192,62208,2,0,L|312:192,2,85,0|2|8,3:2|3:2|3:2,3:3:0:0: +120,192,62935,6,0,L|72:192,2,42.5,2|2|2,3:2|3:2|3:2,0:0:0:0: +216,192,63299,2,0,L|216:96,1,85,8|0,3:2|3:2,3:3:0:0: +396,60,63662,2,0,L|312:60,1,85,2|0,3:2|3:2,3:3:0:0: +148,192,64026,1,8,3:2:0:0: +320,60,64208,1,2,3:2:0:0: +140,192,64390,6,0,B|56:192|56:192|56:104,1,170,2|8,3:2|3:2,0:0:0:0: +140,104,64935,1,2,3:2:0:0: +396,145,65117,2,0,L|396:57,1,85,0|2,3:2|3:2,0:0:0:0: +312,61,65480,1,8,3:2:0:0: +404,61,65662,1,0,3:2:0:0: +300,60,65844,6,0,L|212:60,1,85,2|0,3:2|3:2,3:3:0:0: +392,60,66208,2,0,L|392:160,1,85,8|2,3:2|3:2,3:3:0:0: +136,192,66571,2,0,L|136:104,1,85,2|2,3:2|3:2,3:3:0:0: +307,145,66935,2,0,L|395:145,1,85,8|0,3:2|3:2,3:3:0:0: +476,144,67299,6,0,L|476:244,1,85,2|0,3:2|3:2,3:3:0:0: +307,145,67662,2,0,L|307:45,1,85,8|2,3:2|3:2,3:3:0:0: +48,192,68026,2,0,L|140:192,1,85,0|2,3:2|3:2,3:3:0:0: +307,145,68390,2,0,L|307:233,1,85,8|0,3:2|3:2,3:3:0:0: +222,230,68753,6,0,L|326:230,1,85,2|2,3:2|3:2,0:0:0:0: +136,228,69117,2,0,L|136:324,1,85,8|2,3:2|3:2,3:3:0:0: +228,312,69480,2,0,L|132:312,1,85,2|2,3:2|3:2,3:3:0:0: +236,312,69844,2,0,L|327:312,1,85,8|0,3:2|3:2,3:3:0:0: +60,312,70208,6,0,B|60:228|60:228|148:228,1,170,10|8,3:2|3:2,3:3:0:0: +232,228,70753,1,2,3:2:0:0: +412,192,70935,2,0,L|320:192,2,85,0|2|8,3:2|3:2|3:2,0:0:0:0: +124,192,71662,6,0,L|124:104,1,85,2|0,3:2|3:2,3:3:0:0: +220,108,72026,2,0,L|320:108,1,85,8|2,3:2|3:2,3:3:0:0: +212,108,72389,1,2,3:2:0:0: +316,108,72571,1,2,3:2:0:0: +136,108,72753,2,0,L|48:108,1,85,8|0,3:2|3:2,3:3:0:0: +316,108,73116,6,0,B|400:108|400:108|400:200,1,170,2|8,3:2|3:2,3:3:0:0: +316,192,73662,1,2,3:2:0:0: +144,192,73844,1,2,3:2:0:0: +236,192,74026,1,2,3:2:0:0: +328,192,74208,1,8,3:2:0:0: +56,192,74571,5,2,3:2:0:0: +228,192,74753,1,2,3:2:0:0: +400,192,74935,2,0,L|400:96,1,85,8|0,3:2|3:2,3:3:0:0: +308,108,75298,2,0,L|392:108,1,85,2|2,3:2|3:2,3:3:0:0: +232,192,75662,1,8,3:2:0:0: +401,107,75844,1,2,3:2:0:0: +224,192,76026,6,0,B|140:192|140:192|228:192,1,170,2|8,3:2|3:2,0:0:0:0: +312,192,76571,1,2,3:2:0:0: +56,192,76753,2,0,L|56:104,1,85,0|2,3:2|3:2,0:0:0:0: +140,108,77116,1,8,3:2:0:0: +48,108,77298,1,0,3:2:0:0: +148,107,77480,6,0,L|236:107,1,85,2|0,3:2|3:2,3:3:0:0: +408,108,77844,2,0,L|408:208,1,85,8|2,3:2|3:2,3:3:0:0: +236,192,78207,2,0,L|320:192,1,85,0|2,3:2|3:2,3:3:0:0: +493,193,78571,2,0,L|409:193,1,85,8|0,3:2|3:2,3:3:0:0: +504,192,78935,5,2,3:2:0:0: +332,192,79117,1,2,3:2:0:0: +284,192,79208,1,0,0:0:0:0: +236,192,79298,2,0,L|236:92,1,85,8|0,3:2|3:2,3:3:0:0: +60,28,79662,2,0,L|60:119,1,85,0|2,3:2|3:2,3:3:0:0: +236,107,80026,2,0,L|328:107,1,85,8|2,3:2|3:2,3:3:0:0: +228,108,80389,5,2,3:2:0:0: +228,150,80479,1,2,3:2:0:0: +228,193,80570,1,2,3:2:0:0: +404,192,80753,2,0,L|404:288,1,85,8|2,3:2|3:2,3:3:0:0: +227,280,81116,2,0,L|323:280,1,85,0|2,3:2|3:2,3:3:0:0: +404,277,81480,2,0,L|313:277,1,85,8|2,3:2|3:2,3:3:0:0: +133,193,81844,6,0,L|89:193,2,42.5,2|2|2,3:2|3:2|3:2,0:0:0:0: +303,193,82208,2,0,L|217:193,1,85,8|0,3:2|3:2,3:3:0:0: +264,192,82480,1,2,3:2:0:0: +313,193,82572,2,0,L|229:193,1,85,8|2,3:2|3:2,3:3:0:0: +48,193,82935,2,0,L|132:193,1,85,12|0,3:2|3:2,3:3:0:0: +392,192,83299,6,0,B|496:192|496:192|496:88,1,203.999993774414,10|8,3:2|3:2,0:0:0:0: +452,92,83753,1,0,3:2:0:0: +408,92,83844,1,2,3:2:0:0: +324,92,84026,2,0,L|324:-8,1,85,0|2,3:2|3:2,3:3:0:0: +152,8,84390,2,0,L|152:56,1,42.5,8|2,3:2|3:2,3:3:0:0: +248,92,84662,1,2,3:2:0:0: +248,92,84753,6,0,L|156:92,1,85,2|0,3:2|3:2,3:3:0:0: +332,92,85117,2,0,L|332:152,2,42.5,8|0|2,3:2|3:2|3:2,3:3:0:0: +244,192,85480,1,0,3:2:0:0: +332,92,85662,1,2,3:2:0:0: +156,192,85844,2,0,L|68:192,1,85,8|2,3:2|3:2,3:3:0:0: +164,192,86208,6,0,L|256:192,1,85,2|0,3:2|3:2,3:3:0:0: +80,296,86571,1,8,3:2:0:0: +122,296,86661,1,0,3:2:0:0: +165,296,86752,1,2,3:2:0:0: +252,296,86935,1,0,3:2:0:0: +156,296,87117,1,2,3:2:0:0: +328,296,87299,2,0,L|328:232,1,42.5,8|2,3:2|3:2,3:3:0:0: +152,192,87662,6,0,L|104:192,2,42.5,2|0|2,3:2|3:2|3:2,0:0:0:0: +236,192,88026,2,0,L|144:192,1,85,8|2,3:2|3:2,3:3:0:0: +328,192,88390,2,0,L|328:104,1,85,2|2,3:2|3:2,3:3:0:0: +152,32,88753,2,0,L|64:32,1,85,8|0,3:2|3:2,3:3:0:0: +324,32,89117,6,0,L|496:32,1,170,2|8,3:2|3:2,3:3:0:0: +452,32,89571,1,0,3:2:0:0: +408,32,89662,1,0,3:2:0:0: +324,32,89844,2,0,L|324:128,1,85,2|2,3:2|3:2,3:3:0:0: +148,192,90208,2,0,L|148:244,1,42.5,8|2,3:2|3:2,3:3:0:0: +232,192,90480,1,2,3:2:0:0: +284,192,90571,6,0,L|284:280,1,85,2|0,3:2|3:2,3:3:0:0: +236,316,90844,2,0,L|144:316,1,85,2|0,3:2|3:2,3:3:0:0: +152,316,91117,1,2,3:2:0:0: +236,316,91299,1,2,3:2:0:0: +144,316,91480,1,2,3:2:0:0: +320,316,91662,2,0,L|320:216,1,85,8|2,3:2|3:2,3:3:0:0: +224,192,92026,6,0,L|136:192,1,85,2|0,3:2|3:2,3:3:0:0: +92,192,92299,2,0,L|184:192,1,85,2|0,3:2|3:2,3:3:0:0: +224,192,92571,1,2,3:2:0:0: +132,192,92753,2,0,L|216:192,1,85,2|2,3:2|3:2,3:3:0:0: +392,192,93117,2,0,L|392:104,1,85,8|2,3:2|3:2,0:0:0:0: +216,44,93480,5,2,3:2:0:0: +173,44,93570,1,2,3:2:0:0: +131,44,93661,1,2,3:2:0:0: +224,128,93844,1,8,3:2:0:0: +181,128,93934,1,0,3:2:0:0: +139,128,94025,1,2,3:2:0:0: +312,128,94208,2,0,L|396:128,1,85,8|2,3:2|3:2,3:3:0:0: +220,224,94571,2,0,L|136:224,1,85,12|2,3:2|3:2,3:3:0:0: +392,224,94935,6,0,L|484:224,1,85,10|0,3:2|3:2,3:3:0:0: +384,224,95299,2,0,L|384:128,1,85,8|0,3:2|3:2,3:3:0:0: +212,224,95662,1,2,3:2:0:0: +306,224,95844,1,2,3:2:0:0: +477,224,96026,2,0,L|477:136,1,85,8|0,3:2|3:2,3:3:0:0: +300,136,96390,6,0,L|212:136,1,85,6|0,3:2|3:2,3:3:0:0: +308,136,96753,2,0,L|308:44,1,85,8|2,3:2|3:2,3:2:0:0: +136,136,97117,1,2,3:2:0:0: +300,136,97299,1,2,3:2:0:0: +128,136,97480,2,0,L|128:40,1,85,8|0,3:2|3:2,3:3:0:0: +300,136,97844,6,0,L|212:136,1,85,6|0,3:2|3:2,3:3:0:0: +308,136,98208,1,8,3:2:0:0: +308,93,98298,1,0,0:0:0:0: +308,51,98389,1,0,3:2:0:0: +136,40,98571,2,0,L|224:40,1,85,2|2,3:2|3:2,3:3:0:0: +404,140,98935,2,0,L|404:240,1,85,8|0,3:2|3:2,0:0:0:0: +224,288,99299,6,0,L|136:288,1,85,2|2,3:2|3:2,3:3:0:0: +312,288,99662,2,0,L|312:196,1,85,8|2,3:2|3:2,3:3:0:0: +220,192,100026,1,0,3:2:0:0: +312,288,100208,1,2,3:2:0:0: +136,192,100390,2,0,L|52:192,1,85,8|0,3:2|3:2,3:3:0:0: +308,192,100753,6,0,B|392:192,1,85,6|0,3:2|3:2,3:3:0:0: +216,192,101117,2,0,L|216:104,1,85,8|2,3:2|3:2,3:3:0:0: +300,108,101480,1,0,3:2:0:0: +208,108,101662,1,2,3:2:0:0: +384,108,101844,2,0,L|384:12,1,85,8|0,3:2|3:2,3:3:0:0: +208,108,102208,6,0,L|104:108,1,85,6|0,3:2|3:2,3:3:0:0: +216,108,102571,2,0,L|216:192,1,85,8|2,3:2|3:2,0:0:0:0: +52,108,102935,1,2,3:2:0:0: +224,192,103117,1,2,3:2:0:0: +44,108,103299,2,0,L|44:204,1,85,8|2,3:2|3:2,3:3:0:0: +136,192,103662,6,0,L|224:192,1,85,6|0,3:2|3:2,3:3:0:0: +268,192,103935,1,0,3:2:0:0: +316,192,104026,2,0,L|316:96,1,85,8|2,3:2|3:2,3:3:0:0: +140,36,104390,2,0,L|228:36,1,85,2|2,3:2|3:2,0:0:0:0: +400,36,104753,2,0,L|400:136,1,85,8|0,3:2|3:2,3:3:0:0: +224,192,105117,5,2,3:2:0:0: +181,192,105207,1,2,3:2:0:0: +139,192,105298,1,2,3:2:0:0: +309,192,105480,2,0,L|221:192,1,85,8|2,3:2|3:2,3:3:0:0: +128,192,105844,1,0,3:2:0:0: +216,192,106026,1,2,3:2:0:0: +393,192,106208,2,0,L|493:192,1,85,12|0,3:2|0:0,3:3:0:0: +216,276,106571,6,0,L|128:276,1,85,6|0,3:2|3:2,3:3:0:0: +84,276,106844,1,0,3:2:0:0: +131,276,106935,2,0,L|216:276,1,85,8|2,3:2|3:2,3:3:0:0: +312,276,107299,1,0,3:2:0:0: +212,276,107480,1,2,3:2:0:0: +392,276,107662,2,0,L|392:176,1,85,8|2,3:2|3:2,3:3:0:0: +136,192,108026,6,0,B|44:192,1,85,6|0,3:2|3:2,3:3:0:0: +144,192,108390,2,0,L|144:104,1,85,8|0,3:2|3:2,0:0:0:0: +304,68,108753,1,2,3:2:0:0: +140,192,108935,1,2,3:2:0:0: +312,68,109117,2,0,L|312:168,1,85,8|2,3:2|3:2,3:3:0:0: +56,192,109480,6,0,L|56:284,1,85,6|0,3:2|3:2,3:3:0:0: +140,280,109844,1,8,3:2:0:0: +182,280,109934,1,0,3:2:0:0: +225,280,110025,1,2,3:2:0:0: +56,277,110208,1,2,3:2:0:0: +152,280,110390,1,2,3:2:0:0: +52,277,110571,2,0,L|52:189,1,85,8|0,3:2|0:0,3:3:0:0: +312,192,110935,6,0,L|396:192,1,85,2|2,3:2|3:2,3:3:0:0: +304,192,111299,1,8,3:2:0:0: +404,192,111480,1,2,3:2:0:0: +312,192,111662,1,0,3:2:0:0: +269,192,111752,1,0,3:2:0:0: +227,192,111843,1,2,3:2:0:0: +328,192,112026,2,0,L|328:96,1,85,8|0,3:2|3:2,3:3:0:0: +68,192,112390,6,0,L|68:104,1,85,6|0,3:2|3:2,3:3:0:0: +160,108,112753,2,0,L|248:108,1,85,8|2,3:2|3:2,0:0:0:0: +420,108,113117,2,0,L|420:196,1,85,0|2,3:2|3:2,0:0:0:0: +328,192,113480,1,8,3:2:0:0: +285,192,113570,1,0,0:0:0:0: +243,192,113661,1,0,3:2:0:0: +492,192,113844,6,4,L|492:292,1,85,6|4,3:2|3:2,3:3:0:0: +396,276,114208,2,0,L|304:276,1,85,8|2,3:2|3:2,3:3:0:0: +140,276,114571,1,2,3:2:0:0: +311,276,114753,1,2,3:2:0:0: +140,276,114935,2,0,L|140:192,1,85,8|0,3:2|3:2,3:3:0:0: +396,192,115299,6,0,L|492:192,1,85,6|0,3:2|3:2,3:3:0:0: +308,192,115662,2,0,L|308:96,1,85,8|2,3:2|3:2,0:0:0:0: +136,192,116026,1,2,3:2:0:0: +228,192,116208,1,2,3:2:0:0: +56,192,116390,2,0,L|56:96,1,85,8|2,3:2|3:2,0:0:0:0: +312,192,116753,6,0,L|312:96,1,85,10|2,3:2|3:2,3:3:0:0: +484,28,117117,2,0,L|484:84,2,42.5,8|2|2,3:2|3:2|3:2,3:3:0:0: +392,28,117480,1,8,3:2:0:0: +476,28,117662,1,2,3:2:0:0: +304,28,117844,1,8,3:2:0:0: +262,28,117934,1,2,3:2:0:0: +219,28,118025,1,2,3:2:0:0: +476,28,118208,5,0,0:0:0:0: +476,28,118299,1,0,0:0:0:0: +432,28,118390,1,0,0:0:0:0: +260,132,118571,1,0,0:0:0:0: +260,132,118662,1,0,0:0:0:0: +260,132,118753,1,0,0:0:0:0: +88,236,118935,1,8,0:0:0:0: +88,236,119026,1,2,0:0:0:0: +132,236,119117,1,2,0:0:0:0: +304,288,119299,2,0,L|392:288,1,85,8|8,0:0|0:0,0:0:0:0: +112,236,119662,5,10,0:0:0:0: +256,192,120026,12,0,125480,0:0:0:0: +296,284,131299,6,0,L|296:108,1,170,4|0,0:0|0:0,0:0:0:0: +152,192,132026,1,2,0:0:0:0: +244,192,132208,1,0,0:0:0:0: +336,192,132390,1,0,0:0:0:0: +244,192,132571,1,0,0:0:0:0: +416,192,132753,6,0,L|416:20,2,170,2|0|0,0:0|0:0|0:0,0:0:0:0: +280,192,133844,1,0,0:0:0:0: +188,192,134026,1,0,0:0:0:0: +16,192,134208,6,0,L|16:16,1,170,2|0,0:0|0:0,0:0:0:0: +176,20,134935,1,2,0:0:0:0: +32,24,135299,1,0,0:0:0:0: +272,16,135662,6,0,L|272:192,1,170,2|0,0:0|0:0,0:0:0:0: +428,80,136390,2,0,L|428:272,1,170,2|0,0:0|0:0,0:0:0:0: +132,52,137117,6,0,B|304:52,2,170,4|8|8,0:0|0:0|0:0,0:0:0:0: +336,52,138571,6,0,L|336:224,1,170,2|0,0:0|0:0,0:0:0:0: +240,224,139117,1,0,0:0:0:0: +336,222,139299,1,2,0:0:0:0: +480,192,139662,1,2,0:0:0:0: +388,192,139844,1,0,0:0:0:0: +212,192,140026,6,0,L|212:364,2,170,2|0|2,0:0|0:0|0:0,0:0:0:0: +448,192,141480,6,0,L|344:192,2,93.5000028533936,8|8|8,0:0|0:0|0:0,0:0:0:0: +244,192,142208,1,8,0:0:0:0: +348,192,142390,1,8,0:0:0:0: +448,192,142571,1,8,0:0:0:0: +152,192,142935,6,0,L|152:12,1,170,4|0,0:0|0:0,0:0:0:0: +236,20,143480,1,0,0:0:0:0: +144,20,143662,2,0,L|60:20,2,85,2|0|0,0:0|0:0|0:0,0:0:0:0: +316,136,144390,5,2,0:0:0:0: +232,136,144571,1,0,0:0:0:0: +148,136,144753,1,0,0:0:0:0: +316,136,145117,2,0,L|232:136,2,85,2|0|0,0:0|0:0|0:0,0:0:0:0: +144,136,145844,6,0,L|144:224,1,85,2|0,0:0|0:0,0:0:0:0: +228,220,146208,1,2,0:0:0:0: +59,221,146571,2,0,L|159:221,2,85,2|0|0,0:0|0:0|0:0,0:0:0:0: +228,224,147299,6,0,L|312:224,1,85,2|0,0:0|0:0,0:0:0:0: +220,224,147662,2,0,L|220:320,1,85,0|0,0:0|0:0,0:0:0:0: +313,309,148026,2,0,L|313:225,1,85,2|0,0:0|0:0,0:0:0:0: +228,224,148390,1,0,0:0:0:0: +320,224,148571,1,0,0:0:0:0: +64,276,148753,6,0,L|64:192,1,85,4|0,0:0|0:0,0:0:0:0: +152,192,149117,2,0,L|152:104,1,85,2|0,0:0|0:0,0:0:0:0: +328,108,149480,1,2,0:0:0:0: +184,108,149844,2,0,L|268:108,1,85,2|0,0:0|0:0,0:0:0:0: +356,108,150208,5,2,0:0:0:0: +204,108,150571,2,0,L|204:208,1,85,2|0,0:0|0:0,0:0:0:0: +28,192,150935,1,2,0:0:0:0: +172,192,151299,2,0,L|256:192,1,85,2|0,0:0|0:0,0:0:0:0: +164,192,151662,6,0,L|164:292,1,85,2|0,0:0|0:0,0:0:0:0: +257,277,152026,2,0,L|257:193,1,85,2|0,0:0|0:0,0:0:0:0: +432,192,152390,1,2,0:0:0:0: +288,192,152753,2,0,L|200:192,1,85,2|0,0:0|0:0,0:0:0:0: +380,192,153117,6,0,L|380:104,1,85,8|8,0:0|0:0,0:0:0:0: +288,108,153480,2,0,L|288:20,1,85,8|0,0:0|0:0,0:0:0:0: +112,24,153844,2,0,L|112:108,1,85,8|8,0:0|0:0,0:0:0:0: +203,108,154208,2,0,L|291:108,1,85,8|0,0:0|0:0,0:0:0:0: +32,108,154571,6,0,L|32:288,1,170,4|0,0:0|0:0,0:0:0:0: +216,278,155299,1,2,0:0:0:0: +124,278,155480,1,0,0:0:0:0: +32,278,155662,1,0,0:0:0:0: +216,278,156026,6,0,L|304:280,1,85,8|0,0:0|0:0,0:0:0:0: +300,279,156390,1,0,0:0:0:0: +132,192,156753,2,0,L|220:192,2,85,2|0|0,0:0|0:0|0:0,0:0:0:0: +48,192,157299,1,0,0:0:0:0: +140,192,157480,6,0,L|140:104,1,85,8|0,0:0|0:0,0:0:0:0: +236,108,157844,2,0,L|236:20,1,85,0|0,0:0|0:0,0:0:0:0: +412,48,158208,2,0,L|496:48,2,85,2|0|0,0:0|0:0|0:0,0:0:0:0: +268,192,158935,5,8,0:0:0:0: +344,192,159117,1,8,0:0:0:0: +420,192,159299,1,8,0:0:0:0: +496,192,159480,1,8,0:0:0:0: +412,192,159662,2,0,L|496:192,1,85,2|0,0:0|0:0,0:0:0:0: +324,192,160026,2,0,L|324:104,1,85,2|0,0:0|0:0,0:0:0:0: +68,192,160390,6,0,L|68:108,1,85,10|0,0:0|0:0,0:0:0:0: +152,108,160753,2,0,L|240:108,1,85,8|0,0:0|0:0,0:0:0:0: +409,107,161117,2,0,L|409:191,1,85,2|2,0:0|0:0,0:0:0:0: +324,192,161480,2,0,L|412:192,1,85,8|0,0:0|0:0,0:0:0:0: +313,191,161844,6,0,L|313:299,1,85,2|0,0:0|0:0,0:0:0:0: +140,192,162208,2,0,L|140:284,1,85,8|0,0:0|0:0,0:0:0:0: +184,276,162480,1,0,0:0:0:0: +228,276,162571,2,0,L|312:276,1,85,2|2,0:0|0:0,0:0:0:0: +400,276,162935,2,0,L|400:192,1,85,8|8,0:0|0:0,0:0:0:0: +256,192,163299,12,8,164389,0:0:0:0: +132,192,164753,6,0,L|132:132,2,42.5,8|2|2,0:0|0:0|0:0,0:0:0:0: +304,192,165117,1,8,0:0:0:0: +352,173,165207,1,2,0:0:0:0: +372,125,165298,1,2,0:0:0:0: +351,78,165389,1,2,0:0:0:0: +303,59,165480,1,8,0:0:0:0: +208,60,165662,1,2,0:0:0:0: +388,8,165844,2,0,L|472:8,1,85,12|0,0:0|0:0,0:0:0:0: +216,192,166208,6,0,L|120:192,2,85,6|0|8,3:2|3:2|3:2,3:3:0:0: +308,192,166753,2,0,L|136:192,1,170,6|2,3:2|3:2,3:3:0:0: +312,192,167299,2,0,L|312:296,1,85,8|0,3:2|3:2,3:3:0:0: +138,192,167662,6,0,L|310:192,1,170,6|8,3:2|3:2,3:3:0:0: +404,192,168208,2,0,B|404:276|404:276|316:276,1,170,2|2,3:2|3:2,3:3:0:0: +140,336,168753,2,0,L|140:248,1,85,8|0,3:2|3:2,3:3:0:0: +320,192,169117,6,0,B|404:192|404:192|404:104,1,170,2|8,3:2|3:2,3:3:0:0: +232,32,169662,2,0,L|52:32,1,170,2|2,3:2|3:2,3:3:0:0: +232,32,170208,2,0,L|128:32,1,85,8|0,3:2|3:2,3:3:0:0: +52,32,170571,6,0,L|52:88,1,42.5,2|2,3:2|3:2,3:3:0:0: +100,76,170753,1,2,3:2:0:0: +192,76,170935,1,8,3:2:0:0: +448,192,171117,2,0,L|448:104,1,85,2|0,3:2|3:2,0:0:0:0: +356,104,171480,1,0,3:2:0:0: +184,192,171662,2,0,L|268:192,1,85,8|0,3:2|3:2,3:3:0:0: +20,192,172026,6,0,L|20:144,2,42.5,6|0|0,3:2|3:2|3:2,3:3:0:0: +116,192,172390,1,8,3:2:0:0: +32,192,172571,1,2,3:2:0:0: +208,192,172753,2,0,L|312:192,1,85,0|2,3:2|3:2,3:3:0:0: +200,192,173117,2,0,L|200:280,1,85,8|0,3:2|3:2,3:3:0:0: +376,192,173480,6,0,L|376:108,1,85,6|0,3:2|3:2,3:3:0:0: +200,192,173844,1,8,3:2:0:0: +116,192,174026,2,0,P|64:132|116:76,1,170,2|2,3:2|3:2,3:3:0:0: +372,76,174571,2,0,L|460:76,1,85,8|0,3:2|3:2,3:3:0:0: +280,76,174935,6,0,L|280:172,1,85,2|2,3:2|3:2,3:3:0:0: +368,192,175299,1,8,3:2:0:0: +192,192,175480,2,0,L|192:288,1,85,2|0,3:2|3:2,3:3:0:0: +280,308,175844,1,2,3:2:0:0: +453,192,176026,2,0,L|365:192,1,85,8|2,3:2|3:2,0:0:0:0: +112,192,176390,6,0,L|20:192,2,85,8|2|8,3:2|3:2|3:2,3:3:0:0: +292,192,176935,2,0,L|116:192,1,170,2|2,3:2|3:2,3:3:0:0: +304,192,177480,2,0,L|402:192,1,85,8|0,3:2|3:2,3:3:0:0: +132,192,177844,6,0,B|32:192|32:192|32:88,1,203.999993774414,6|8,3:2|3:2,3:3:0:0: +208,44,178390,2,0,L|380:44,1,170,6|2,3:2|3:2,3:3:0:0: +284,44,178935,2,0,L|284:140,1,85,8|0,3:2|3:2,3:3:0:0: +464,136,179299,6,0,L|464:232,1,85,2|0,3:2|3:2,3:3:0:0: +380,220,179662,1,8,3:2:0:0: +204,192,179844,2,0,L|376:192,1,170,2|2,3:2|1:3,3:3:0:0: +460,192,180390,2,0,L|460:92,1,85,8|0,3:2|3:2,3:3:0:0: +284,16,180753,6,0,B|200:16|200:16|200:104,1,170,2|8,3:2|3:2,3:3:0:0: +380,192,181299,2,0,L|204:192,1,170,2|2,3:2|3:2,3:3:0:0: +302,193,181844,2,0,L|210:193,1,85,8|0,3:2|3:2,3:3:0:0: +124,192,182208,6,0,L|124:288,1,85,2|2,3:2|3:2,3:3:0:0: +302,193,182571,2,0,L|210:193,1,85,8|2,3:2|3:2,0:0:0:0: +312,192,182935,2,0,L|360:192,2,42.5,0|0|2,3:2|3:2|3:2,3:3:0:0: +132,192,183299,2,0,L|32:192,1,85,8|0,3:2|3:2,3:3:0:0: +312,192,183662,6,0,P|364:248|312:308,1,170,6|8,3:2|3:2,3:3:0:0: +220,308,184208,1,2,3:2:0:0: +324,308,184390,2,0,L|324:216,1,85,0|2,3:2|3:2,3:3:0:0: +144,192,184753,2,0,L|144:280,1,85,8|0,3:2|3:2,3:3:0:0: +324,224,185117,6,0,L|408:224,1,85,2|2,3:2|3:2,3:3:0:0: +232,192,185480,2,0,L|232:96,1,85,8|2,3:2|3:2,3:3:0:0: +316,108,185844,1,0,3:2:0:0: +232,108,186026,1,2,3:2:0:0: +408,108,186208,2,0,L|408:16,1,85,8|0,3:2|3:2,3:3:0:0: +152,20,186571,6,0,B|68:20|68:20|156:20,1,170,6|0,3:2|3:2,3:3:0:0: +332,132,187117,2,0,L|152:132,1,170,6|2,3:2|3:2,3:3:0:0: +76,132,187662,2,0,L|76:216,1,85,8|0,3:2|3:2,3:3:0:0: +252,280,188026,5,2,3:2:0:0: +294,280,188116,1,2,3:2:0:0: +337,280,188207,1,2,3:2:0:0: +176,280,188390,1,8,3:2:0:0: +344,280,188571,2,0,P|396:232|344:168,1,170,6|2,3:2|3:2,3:3:0:0: +168,192,189117,2,0,L|80:192,1,85,8|0,3:2|3:2,3:3:0:0: +344,168,189480,6,0,B|448:168|448:168|448:64,1,203.999993774414,10|8,3:2|3:2,3:3:0:0: +352,68,190026,2,0,L|172:68,1,170,0|2,3:2|3:2,0:0:0:0: +276,68,190571,2,0,L|276:164,1,85,8|0,3:2|3:2,3:3:0:0: +96,192,190935,6,0,L|96:96,1,85,2|0,3:2|3:2,3:3:0:0: +192,104,191299,2,0,L|100:104,1,85,8|2,3:2|3:2,3:3:0:0: +284,192,191662,2,0,L|372:192,1,85,0|2,3:2|3:2,3:3:0:0: +464,192,192026,2,0,L|464:148,1,42.5,8|0,3:2|0:0,3:3:0:0: +420,132,192208,1,0,3:2:0:0: +240,192,192390,6,0,L|64:192,1,170,2|8,3:2|3:2,3:3:0:0: +156,192,192935,1,2,3:2:0:0: +64,192,193117,2,0,L|64:100,1,85,2|2,3:2|3:2,3:3:0:0: +156,192,193480,2,0,L|156:108,1,85,8|0,3:2|3:2,3:3:0:0: +332,192,193844,6,0,L|376:192,2,42.5,2|2|2,3:2|3:2|3:2,0:0:0:0: +156,192,194208,2,0,L|244:192,1,85,8|0,3:2|3:2,3:3:0:0: +328,192,194571,1,2,3:2:0:0: +236,192,194753,1,2,3:2:0:0: +416,192,194935,2,0,L|416:284,1,85,8|0,3:2|3:2,3:3:0:0: +160,336,195299,6,0,B|76:336|76:336|76:244,1,170,6|8,3:2|3:2,3:3:0:0: +164,192,195844,2,0,L|344:192,1,170,6|2,3:2|3:2,3:3:0:0: +240,192,196389,2,0,L|240:96,1,85,8|0,3:2|3:2,3:3:0:0: +420,68,196753,6,0,L|420:164,1,85,6|2,3:2|3:2,3:3:0:0: +372,156,197026,1,2,3:2:0:0: +324,156,197117,2,0,L|240:156,1,85,8|2,3:2|3:2,3:3:0:0: +332,156,197480,2,0,L|332:72,1,85,0|2,3:2|3:2,3:3:0:0: +152,20,197844,2,0,L|108:20,2,42.5,8|0|0,3:2|3:2|3:2,0:0:0:0: +328,192,198208,6,0,L|504:192,1,170,6|8,3:2|3:2,3:3:0:0: +412,192,198753,1,2,3:2:0:0: +236,192,198935,2,0,L|236:100,1,85,2|2,3:2|3:2,3:3:0:0: +328,192,199298,2,0,L|240:192,1,85,8|2,3:2|3:2,0:0:0:0: +64,192,199662,6,0,L|64:280,1,85,6|2,3:2|3:2,3:3:0:0: +160,276,200026,1,8,3:2:0:0: +112,276,200116,1,2,3:2:0:0: +64,277,200207,1,8,3:2:0:0: +240,192,200390,2,0,L|240:280,1,85,8|2,3:2|3:2,3:3:0:0: +416,192,200753,2,0,L|508:192,1,85,8|2,3:2|3:2,3:3:0:0: +240,192,201117,6,0,L|36:192,1,203.999993774414,6|8,3:2|3:2,3:3:0:0: +128,192,201662,2,0,B|216:192|216:192|216:104,1,170,2|2,3:2|3:2,0:0:0:0: +40,16,202208,2,0,L|40:104,1,85,8|0,3:2|3:2,3:3:0:0: +216,110,202571,6,0,L|308:110,1,85,6|2,3:2|3:2,3:3:0:0: +348,112,202844,1,2,3:2:0:0: +396,112,202935,2,0,L|396:24,1,85,8|2,3:2|3:2,3:3:0:0: +492,28,203299,2,0,L|404:28,1,85,4|2,3:2|3:2,3:3:0:0: +232,32,203662,2,0,L|232:116,1,85,8|0,3:2|3:2,0:0:0:0: +408,192,204026,6,0,L|500:192,2,85,6|2|8,3:2|3:2|3:2,3:3:0:0: +316,192,204571,2,0,L|492:192,1,170,2|2,3:2|3:2,0:0:0:0: +308,192,205117,2,0,L|220:192,1,85,8|0,3:2|3:2,3:3:0:0: +48,192,205480,6,0,L|48:284,1,85,2|2,3:2|3:2,3:3:0:0: +224,192,205844,2,0,L|312:192,1,85,8|2,3:2|3:2,0:0:0:0: +216,192,206208,1,2,3:2:0:0: +320,192,206390,1,2,3:2:0:0: +144,192,206571,2,0,L|60:192,1,85,8|2,3:2|3:2,3:3:0:0: +320,192,206935,6,0,L|408:192,1,85,6|2,3:2|3:2,3:3:0:0: +405,192,207208,1,2,3:2:0:0: +405,192,207299,1,8,3:2:0:0: +312,192,207480,2,0,P|264:136|312:68,1,170,2|0,3:2|3:2,0:0:0:0: +488,68,208026,2,0,L|488:152,1,85,8|2,3:2|3:2,3:3:0:0: +308,192,208390,6,0,L|220:192,1,85,6|2,3:2|3:2,3:3:0:0: +404,192,208753,2,0,L|404:280,1,85,8|2,3:2|3:2,3:3:0:0: +308,276,209117,1,4,3:2:0:0: +392,276,209299,1,2,3:2:0:0: +216,276,209480,2,0,L|120:276,1,85,8|2,3:2|3:2,3:3:0:0: +308,276,209844,6,0,L|308:192,1,85,6|2,3:2|3:2,3:3:0:0: +264,192,210117,1,2,3:2:0:0: +220,192,210208,1,8,3:2:0:0: +308,192,210390,2,0,L|480:192,1,170,6|2,3:2|3:2,3:3:0:0: +296,192,210935,2,0,L|296:100,1,85,8|2,3:2|3:2,3:3:0:0: +120,28,211299,5,2,3:2:0:0: +120,70,211389,1,2,3:2:0:0: +120,113,211480,1,2,3:2:0:0: +296,192,211662,2,0,L|200:192,1,85,8|8,3:2|3:2,3:3:0:0: +120,113,212026,2,0,L|120:200,1,85,12|0,3:2|3:2,3:3:0:0: +296,192,212390,1,12,3:2:0:0: +196,192,212571,1,2,3:2:0:0: +456,192,212753,6,0,L|456:280,1,85,10|0,3:2|3:2,3:3:0:0: +276,336,213117,2,0,L|180:336,1,85,8|2,3:2|3:2,0:0:0:0: +284,336,213480,2,0,L|284:240,1,85,2|2,3:2|3:2,0:0:0:0: +104,192,213844,2,0,L|188:192,1,85,8|2,3:2|3:2,0:0:0:0: +448,192,214208,6,0,L|448:100,1,85,2|2,3:2|3:2,3:3:0:0: +400,108,214480,1,2,3:2:0:0: +352,108,214571,1,8,3:2:0:0: +448,192,214753,1,2,3:2:0:0: +272,192,214935,2,0,L|272:108,1,85,0|2,3:2|3:2,3:3:0:0: +96,192,215299,2,0,L|8:192,1,85,8|2,3:2|3:2,0:0:0:0: +272,192,215662,6,0,L|360:192,1,85,6|2,3:2|3:2,3:3:0:0: +180,192,216026,2,0,L|180:104,1,85,8|2,3:2|3:2,3:3:0:0: +356,192,216390,1,2,3:2:0:0: +256,192,216571,1,2,3:2:0:0: +436,192,216753,2,0,L|332:192,1,85,8|2,3:2|3:2,3:3:0:0: +96,192,217117,6,0,B|12:192|12:192|100:192,1,170,2|8,3:2|3:2,3:3:0:0: +276,192,217662,2,0,L|364:192,2,85,2|2|2,3:2|3:2|3:2,0:0:0:0: +98,192,218208,2,0,L|98:104,1,85,8|2,3:2|3:2,3:3:0:0: +360,192,218571,6,0,P|412:128|360:80,1,170,6|8,3:2|3:2,3:3:0:0: +312,80,219026,1,2,3:2:0:0: +264,80,219117,1,2,3:2:0:0: +88,80,219299,2,0,L|172:80,1,85,4|2,3:2|3:2,3:3:0:0: +268,80,219662,2,0,L|268:168,1,85,8|2,3:2|3:2,3:3:0:0: +88,192,220026,6,0,L|88:280,1,85,6|2,3:2|3:2,3:3:0:0: +268,164,220390,1,8,3:2:0:0: +180,192,220571,1,2,3:2:0:0: +436,192,220753,2,0,L|436:96,1,85,0|2,0:0|3:2,0:0:0:0: +260,44,221117,2,0,L|168:44,1,85,8|2,3:2|3:2,3:3:0:0: +436,192,221480,6,0,L|352:192,1,85,6|2,3:2|3:2,3:3:0:0: +308,192,221753,1,2,3:2:0:0: +264,192,221844,1,8,3:2:0:0: +356,192,222026,1,2,3:2:0:0: +100,192,222208,2,0,L|16:192,1,85,4|2,3:2|3:2,3:3:0:0: +108,192,222571,2,0,L|108:104,1,85,8|2,3:2|3:2,3:3:0:0: +368,192,222935,6,0,L|416:192,2,42.5,2|2|2,3:2|3:2|3:2,3:3:0:0: +188,192,223299,1,8,3:2:0:0: +280,192,223480,1,0,3:2:0:0: +328,192,223571,1,2,3:2:0:0: +376,192,223662,2,0,L|376:104,1,85,8|2,3:2|3:2,0:0:0:0: +196,48,224026,2,0,L|104:48,1,85,8|0,3:2|0:0,0:0:0:0: +376,24,224390,6,0,P|436:96|376:168,1,203.999993774414,14|2,0:0|0:0,0:0:0:0: +96,192,225117,2,0,L|96:280,1,85,8|0,0:0|0:0,0:0:0:0: +180,276,225480,1,2,0:0:0:0: +356,192,225662,1,2,0:0:0:0: +400,192,225753,1,0,0:0:0:0: +444,192,225844,6,0,L|444:280,1,85,0|0,0:0|0:0,0:0:0:0: +360,276,226208,2,0,L|276:276,1,85,2|2,0:0|0:0,0:0:0:0: +96,192,226571,2,0,L|96:276,1,85,8|0,0:0|0:0,0:0:0:0: +181,277,226935,2,0,L|97:277,1,85,2|0,0:0|0:0,0:0:0:0: +276,192,227299,6,0,B|360:192|360:192|360:104,1,170,2|2,0:0|0:0,0:0:0:0: +276,104,227844,1,2,0:0:0:0: +96,104,228026,2,0,L|96:188,1,85,8|0,0:0|0:0,0:0:0:0: +180,192,228390,2,0,L|180:104,1,85,2|2,0:0|0:0,0:0:0:0: +356,192,228753,5,2,0:0:0:0: +440,192,228935,1,2,0:0:0:0: +440,108,229117,1,0,0:0:0:0: +356,108,229299,1,2,0:0:0:0: +176,108,229480,2,0,L|176:192,1,85,8|0,0:0|0:0,0:0:0:0: +264,192,229844,1,2,0:0:0:0: +310,192,229934,1,0,0:0:0:0: +356,192,230025,1,2,0:0:0:0: +176,192,230208,6,0,L|4:192,1,170,6|2,0:0|0:0,0:0:0:0: +92,192,230753,1,2,0:0:0:0: +268,192,230935,2,0,L|356:192,1,85,8|0,0:0|0:0,0:0:0:0: +260,192,231299,2,0,L|260:108,1,85,2|2,0:0|0:0,0:0:0:0: +308,104,231571,1,0,0:0:0:0: +356,104,231662,6,0,B|440:104|440:104|440:192,1,170,2|2,0:0|0:0,0:0:0:0: +356,192,232208,1,2,0:0:0:0: +180,192,232390,2,0,L|180:304,1,85,8|0,0:0|0:0,0:0:0:0: +272,280,232753,2,0,L|272:232,2,42.5,2|0|0,0:0|0:0|0:0,0:0:0:0: +92,280,233117,6,0,P|40:224|92:160,1,170,8|8,0:0|0:0,0:0:0:0: +172,160,233662,1,8,0:0:0:0: +352,160,233844,2,0,L|352:68,1,85,8|8,0:0|0:0,0:0:0:0: +268,76,234208,1,2,0:0:0:0: +360,76,234390,1,2,0:0:0:0: +172,160,234571,6,0,L|172:100,2,42.5,2|2|2,0:0|0:0|0:0,0:0:0:0: +268,192,234935,2,0,L|172:192,1,85,8|2,0:0|0:0,0:0:0:0: +364,192,235298,2,0,L|364:280,1,85,8|2,0:0|0:0,0:0:0:0: +183,192,235662,1,2,0:0:0:0: +140,192,235752,1,2,0:0:0:0: +98,192,235843,1,2,0:0:0:0: +376,192,236026,5,6,0:0:0:0: +224,192,236390,1,2,0:0:0:0: +496,192,236753,6,0,L|496:20,1,170,4|0,0:0|0:0,0:0:0:0: +256,192,237480,12,0,238571,0:0:0:0: +256,192,238935,6,0,L|256:368,1,170,4|0,0:0|0:0,0:0:0:0: +256,192,239662,12,0,246208,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/37902-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/37902-expected-conversion.json new file mode 100644 index 0000000000..efc1144d05 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/37902-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":12017.0,"Objects":[{"StartTime":12017.0,"Position":48.0,"HyperDash":false},{"StartTime":12091.0,"Position":44.67537,"HyperDash":false},{"StartTime":12166.0,"Position":74.08286,"HyperDash":false},{"StartTime":12241.0,"Position":88.0374,"HyperDash":false},{"StartTime":12316.0,"Position":110.33316,"HyperDash":false},{"StartTime":12391.0,"Position":148.554672,"HyperDash":false},{"StartTime":12466.0,"Position":154.24501,"HyperDash":false},{"StartTime":12541.0,"Position":176.957489,"HyperDash":false},{"StartTime":12616.0,"Position":202.32959,"HyperDash":false},{"StartTime":12673.0,"Position":213.120667,"HyperDash":false},{"StartTime":12766.0,"Position":252.041336,"HyperDash":false}]},{"StartTime":13067.0,"Objects":[{"StartTime":13067.0,"Position":320.0,"HyperDash":false}]},{"StartTime":13367.0,"Objects":[{"StartTime":13367.0,"Position":464.0,"HyperDash":false}]},{"StartTime":13667.0,"Objects":[{"StartTime":13667.0,"Position":484.0,"HyperDash":false}]},{"StartTime":13966.0,"Objects":[{"StartTime":13966.0,"Position":444.0,"HyperDash":false}]},{"StartTime":14116.0,"Objects":[{"StartTime":14116.0,"Position":444.0,"HyperDash":false}]},{"StartTime":14416.0,"Objects":[{"StartTime":14416.0,"Position":464.0,"HyperDash":false},{"StartTime":14490.0,"Position":453.158569,"HyperDash":false},{"StartTime":14565.0,"Position":455.987671,"HyperDash":false},{"StartTime":14640.0,"Position":425.82608,"HyperDash":false},{"StartTime":14715.0,"Position":428.8319,"HyperDash":false},{"StartTime":14790.0,"Position":427.066162,"HyperDash":false},{"StartTime":14865.0,"Position":386.833649,"HyperDash":false},{"StartTime":14940.0,"Position":376.186218,"HyperDash":false},{"StartTime":15015.0,"Position":338.7702,"HyperDash":false},{"StartTime":15072.0,"Position":302.942566,"HyperDash":false},{"StartTime":15165.0,"Position":288.993134,"HyperDash":false}]},{"StartTime":15466.0,"Objects":[{"StartTime":15466.0,"Position":216.0,"HyperDash":false}]},{"StartTime":15766.0,"Objects":[{"StartTime":15766.0,"Position":72.0,"HyperDash":false}]},{"StartTime":16066.0,"Objects":[{"StartTime":16066.0,"Position":92.0,"HyperDash":false}]},{"StartTime":16366.0,"Objects":[{"StartTime":16366.0,"Position":52.0,"HyperDash":false}]},{"StartTime":16815.0,"Objects":[{"StartTime":16815.0,"Position":72.0,"HyperDash":false},{"StartTime":16889.0,"Position":79.642746,"HyperDash":false},{"StartTime":16964.0,"Position":89.107,"HyperDash":false},{"StartTime":17039.0,"Position":108.5208,"HyperDash":false},{"StartTime":17114.0,"Position":136.488754,"HyperDash":false},{"StartTime":17189.0,"Position":172.402725,"HyperDash":false},{"StartTime":17264.0,"Position":179.293137,"HyperDash":false},{"StartTime":17339.0,"Position":180.858765,"HyperDash":false},{"StartTime":17414.0,"Position":220.396072,"HyperDash":false},{"StartTime":17471.0,"Position":249.039856,"HyperDash":false},{"StartTime":17564.0,"Position":261.951355,"HyperDash":false}]},{"StartTime":17865.0,"Objects":[{"StartTime":17865.0,"Position":320.0,"HyperDash":false}]},{"StartTime":18165.0,"Objects":[{"StartTime":18165.0,"Position":432.0,"HyperDash":false}]},{"StartTime":18465.0,"Objects":[{"StartTime":18465.0,"Position":448.0,"HyperDash":false}]},{"StartTime":18765.0,"Objects":[{"StartTime":18765.0,"Position":504.0,"HyperDash":false}]},{"StartTime":18915.0,"Objects":[{"StartTime":18915.0,"Position":484.0,"HyperDash":false}]},{"StartTime":19215.0,"Objects":[{"StartTime":19215.0,"Position":504.0,"HyperDash":false},{"StartTime":19289.0,"Position":501.08197,"HyperDash":false},{"StartTime":19364.0,"Position":495.2131,"HyperDash":false},{"StartTime":19439.0,"Position":481.1128,"HyperDash":false},{"StartTime":19514.0,"Position":463.28656,"HyperDash":false},{"StartTime":19589.0,"Position":434.907227,"HyperDash":false},{"StartTime":19664.0,"Position":416.885864,"HyperDash":false},{"StartTime":19739.0,"Position":405.201477,"HyperDash":false},{"StartTime":19814.0,"Position":367.272461,"HyperDash":false},{"StartTime":19871.0,"Position":365.267731,"HyperDash":false},{"StartTime":19964.0,"Position":317.231384,"HyperDash":false}]},{"StartTime":20264.0,"Objects":[{"StartTime":20264.0,"Position":248.0,"HyperDash":false}]},{"StartTime":20564.0,"Objects":[{"StartTime":20564.0,"Position":268.0,"HyperDash":false}]},{"StartTime":20864.0,"Objects":[{"StartTime":20864.0,"Position":104.0,"HyperDash":false}]},{"StartTime":21164.0,"Objects":[{"StartTime":21164.0,"Position":248.0,"HyperDash":false}]},{"StartTime":21614.0,"Objects":[{"StartTime":21614.0,"Position":72.0,"HyperDash":false},{"StartTime":21688.0,"Position":89.44662,"HyperDash":false},{"StartTime":21763.0,"Position":74.09614,"HyperDash":false},{"StartTime":21838.0,"Position":60.5660629,"HyperDash":false},{"StartTime":21913.0,"Position":83.94954,"HyperDash":false},{"StartTime":21988.0,"Position":82.8251,"HyperDash":false},{"StartTime":22063.0,"Position":111.00235,"HyperDash":false},{"StartTime":22138.0,"Position":149.062637,"HyperDash":false},{"StartTime":22213.0,"Position":152.832413,"HyperDash":false},{"StartTime":22270.0,"Position":185.730072,"HyperDash":false},{"StartTime":22363.0,"Position":197.239868,"HyperDash":false}]},{"StartTime":22663.0,"Objects":[{"StartTime":22663.0,"Position":264.0,"HyperDash":false},{"StartTime":22737.0,"Position":291.67392,"HyperDash":false},{"StartTime":22812.0,"Position":313.532043,"HyperDash":false},{"StartTime":22887.0,"Position":338.985229,"HyperDash":false},{"StartTime":22962.0,"Position":361.614532,"HyperDash":false},{"StartTime":23037.0,"Position":383.778625,"HyperDash":false},{"StartTime":23112.0,"Position":403.659546,"HyperDash":false},{"StartTime":23187.0,"Position":404.466278,"HyperDash":false},{"StartTime":23262.0,"Position":433.744751,"HyperDash":false},{"StartTime":23337.0,"Position":430.5013,"HyperDash":false},{"StartTime":23412.0,"Position":450.112335,"HyperDash":false},{"StartTime":23469.0,"Position":448.32254,"HyperDash":false},{"StartTime":23562.0,"Position":455.8164,"HyperDash":false}]},{"StartTime":23863.0,"Objects":[{"StartTime":23863.0,"Position":456.0,"HyperDash":false},{"StartTime":23937.0,"Position":420.344849,"HyperDash":false},{"StartTime":24012.0,"Position":406.676758,"HyperDash":false},{"StartTime":24087.0,"Position":381.029877,"HyperDash":false},{"StartTime":24162.0,"Position":361.682678,"HyperDash":false},{"StartTime":24237.0,"Position":326.453217,"HyperDash":false},{"StartTime":24312.0,"Position":325.5777,"HyperDash":false},{"StartTime":24387.0,"Position":323.0864,"HyperDash":false},{"StartTime":24462.0,"Position":280.111542,"HyperDash":false},{"StartTime":24537.0,"Position":265.3847,"HyperDash":false},{"StartTime":24612.0,"Position":230.444534,"HyperDash":false},{"StartTime":24669.0,"Position":218.443909,"HyperDash":false},{"StartTime":24762.0,"Position":180.416458,"HyperDash":false}]},{"StartTime":25063.0,"Objects":[{"StartTime":25063.0,"Position":184.0,"HyperDash":false}]},{"StartTime":25662.0,"Objects":[{"StartTime":25662.0,"Position":204.0,"HyperDash":false}]},{"StartTime":26262.0,"Objects":[{"StartTime":26262.0,"Position":320.0,"HyperDash":false}]},{"StartTime":26862.0,"Objects":[{"StartTime":26862.0,"Position":300.0,"HyperDash":false}]},{"StartTime":27612.0,"Objects":[{"StartTime":27612.0,"Position":96.0,"HyperDash":false},{"StartTime":27686.0,"Position":93.6587143,"HyperDash":false},{"StartTime":27761.0,"Position":98.89105,"HyperDash":false},{"StartTime":27836.0,"Position":108.2196,"HyperDash":false},{"StartTime":27911.0,"Position":110.334862,"HyperDash":false},{"StartTime":27986.0,"Position":125.092537,"HyperDash":false},{"StartTime":28061.0,"Position":136.262375,"HyperDash":false},{"StartTime":28136.0,"Position":145.71701,"HyperDash":false},{"StartTime":28211.0,"Position":178.315811,"HyperDash":false},{"StartTime":28268.0,"Position":210.647934,"HyperDash":false},{"StartTime":28361.0,"Position":227.43338,"HyperDash":false}]},{"StartTime":28661.0,"Objects":[{"StartTime":28661.0,"Position":296.0,"HyperDash":false},{"StartTime":28735.0,"Position":302.1624,"HyperDash":false},{"StartTime":28810.0,"Position":292.488281,"HyperDash":false},{"StartTime":28885.0,"Position":289.777161,"HyperDash":false},{"StartTime":28960.0,"Position":280.749847,"HyperDash":false},{"StartTime":29035.0,"Position":254.2413,"HyperDash":false},{"StartTime":29110.0,"Position":259.131836,"HyperDash":false},{"StartTime":29185.0,"Position":252.401169,"HyperDash":false},{"StartTime":29260.0,"Position":227.176636,"HyperDash":false},{"StartTime":29335.0,"Position":210.735916,"HyperDash":false},{"StartTime":29410.0,"Position":186.45578,"HyperDash":false},{"StartTime":29467.0,"Position":186.31813,"HyperDash":false},{"StartTime":29560.0,"Position":140.066452,"HyperDash":false}]},{"StartTime":29861.0,"Objects":[{"StartTime":29861.0,"Position":72.0,"HyperDash":false},{"StartTime":29935.0,"Position":47.3521729,"HyperDash":false},{"StartTime":30010.0,"Position":57.88796,"HyperDash":false},{"StartTime":30085.0,"Position":33.71809,"HyperDash":false},{"StartTime":30160.0,"Position":48.9088,"HyperDash":false},{"StartTime":30235.0,"Position":57.53698,"HyperDash":false},{"StartTime":30310.0,"Position":45.7225456,"HyperDash":false},{"StartTime":30385.0,"Position":39.74385,"HyperDash":false},{"StartTime":30460.0,"Position":49.84991,"HyperDash":false},{"StartTime":30535.0,"Position":61.4995155,"HyperDash":false},{"StartTime":30610.0,"Position":64.31644,"HyperDash":false},{"StartTime":30667.0,"Position":81.55949,"HyperDash":false},{"StartTime":30760.0,"Position":93.98463,"HyperDash":false}]},{"StartTime":31060.0,"Objects":[{"StartTime":31060.0,"Position":160.0,"HyperDash":false}]},{"StartTime":31660.0,"Objects":[{"StartTime":31660.0,"Position":432.0,"HyperDash":false}]},{"StartTime":32260.0,"Objects":[{"StartTime":32260.0,"Position":412.0,"HyperDash":false}]},{"StartTime":32860.0,"Objects":[{"StartTime":32860.0,"Position":432.0,"HyperDash":false}]},{"StartTime":33610.0,"Objects":[{"StartTime":33610.0,"Position":256.0,"HyperDash":false},{"StartTime":33684.0,"Position":223.29216,"HyperDash":false},{"StartTime":33759.0,"Position":206.250412,"HyperDash":false},{"StartTime":33834.0,"Position":193.208679,"HyperDash":false},{"StartTime":33909.0,"Position":156.0,"HyperDash":false},{"StartTime":33984.0,"Position":175.874786,"HyperDash":false},{"StartTime":34059.0,"Position":205.916534,"HyperDash":false},{"StartTime":34116.0,"Position":211.948242,"HyperDash":false},{"StartTime":34209.0,"Position":256.0,"HyperDash":false}]},{"StartTime":34359.0,"Objects":[{"StartTime":34359.0,"Position":376.0,"HyperDash":false}]},{"StartTime":34659.0,"Objects":[{"StartTime":34659.0,"Position":256.0,"HyperDash":false}]},{"StartTime":34809.0,"Objects":[{"StartTime":34809.0,"Position":256.0,"HyperDash":false},{"StartTime":34883.0,"Position":283.707855,"HyperDash":false},{"StartTime":34958.0,"Position":305.749573,"HyperDash":false},{"StartTime":35033.0,"Position":327.791321,"HyperDash":false},{"StartTime":35108.0,"Position":356.0,"HyperDash":false},{"StartTime":35183.0,"Position":331.1252,"HyperDash":false},{"StartTime":35258.0,"Position":306.083466,"HyperDash":false},{"StartTime":35315.0,"Position":273.051758,"HyperDash":false},{"StartTime":35408.0,"Position":256.0,"HyperDash":false}]},{"StartTime":35559.0,"Objects":[{"StartTime":35559.0,"Position":128.0,"HyperDash":false}]},{"StartTime":35859.0,"Objects":[{"StartTime":35859.0,"Position":256.0,"HyperDash":false}]},{"StartTime":36009.0,"Objects":[{"StartTime":36009.0,"Position":256.0,"HyperDash":false},{"StartTime":36083.0,"Position":230.29216,"HyperDash":false},{"StartTime":36158.0,"Position":206.250412,"HyperDash":false},{"StartTime":36233.0,"Position":193.208679,"HyperDash":false},{"StartTime":36308.0,"Position":156.0,"HyperDash":false},{"StartTime":36383.0,"Position":191.874786,"HyperDash":false},{"StartTime":36458.0,"Position":205.916534,"HyperDash":false},{"StartTime":36515.0,"Position":241.948242,"HyperDash":false},{"StartTime":36608.0,"Position":256.0,"HyperDash":false}]},{"StartTime":36758.0,"Objects":[{"StartTime":36758.0,"Position":376.0,"HyperDash":false}]},{"StartTime":37058.0,"Objects":[{"StartTime":37058.0,"Position":328.0,"HyperDash":false},{"StartTime":37132.0,"Position":343.611969,"HyperDash":false},{"StartTime":37207.0,"Position":376.99472,"HyperDash":false},{"StartTime":37282.0,"Position":386.735321,"HyperDash":false},{"StartTime":37357.0,"Position":419.270874,"HyperDash":false},{"StartTime":37432.0,"Position":438.334564,"HyperDash":false},{"StartTime":37507.0,"Position":444.7913,"HyperDash":false},{"StartTime":37582.0,"Position":467.3238,"HyperDash":false},{"StartTime":37657.0,"Position":454.839142,"HyperDash":false},{"StartTime":37732.0,"Position":439.412842,"HyperDash":false},{"StartTime":37807.0,"Position":444.935333,"HyperDash":false},{"StartTime":37882.0,"Position":421.561951,"HyperDash":false},{"StartTime":37957.0,"Position":419.5829,"HyperDash":false},{"StartTime":38032.0,"Position":392.116547,"HyperDash":false},{"StartTime":38107.0,"Position":377.418579,"HyperDash":false},{"StartTime":38182.0,"Position":348.0527,"HyperDash":false},{"StartTime":38257.0,"Position":328.0,"HyperDash":false},{"StartTime":38332.0,"Position":347.832336,"HyperDash":false},{"StartTime":38407.0,"Position":377.206635,"HyperDash":false},{"StartTime":38482.0,"Position":402.925934,"HyperDash":false},{"StartTime":38557.0,"Position":419.42688,"HyperDash":false},{"StartTime":38632.0,"Position":422.448273,"HyperDash":false},{"StartTime":38707.0,"Position":444.8633,"HyperDash":false},{"StartTime":38764.0,"Position":446.1127,"HyperDash":false},{"StartTime":38857.0,"Position":454.839142,"HyperDash":false}]},{"StartTime":39607.0,"Objects":[{"StartTime":39607.0,"Position":440.0,"HyperDash":false}]},{"StartTime":39907.0,"Objects":[{"StartTime":39907.0,"Position":296.0,"HyperDash":false}]},{"StartTime":40207.0,"Objects":[{"StartTime":40207.0,"Position":316.0,"HyperDash":false}]},{"StartTime":40357.0,"Objects":[{"StartTime":40357.0,"Position":256.0,"HyperDash":false},{"StartTime":40431.0,"Position":212.250839,"HyperDash":false},{"StartTime":40506.0,"Position":206.167221,"HyperDash":false},{"StartTime":40563.0,"Position":200.103668,"HyperDash":false},{"StartTime":40656.0,"Position":156.0,"HyperDash":false}]},{"StartTime":41107.0,"Objects":[{"StartTime":41107.0,"Position":64.0,"HyperDash":false}]},{"StartTime":41407.0,"Objects":[{"StartTime":41407.0,"Position":256.0,"HyperDash":false}]},{"StartTime":41557.0,"Objects":[{"StartTime":41557.0,"Position":192.0,"HyperDash":false},{"StartTime":41631.0,"Position":213.749161,"HyperDash":false},{"StartTime":41706.0,"Position":241.832779,"HyperDash":false},{"StartTime":41763.0,"Position":251.896332,"HyperDash":false},{"StartTime":41856.0,"Position":292.0,"HyperDash":false}]},{"StartTime":42307.0,"Objects":[{"StartTime":42307.0,"Position":392.0,"HyperDash":false}]},{"StartTime":42606.0,"Objects":[{"StartTime":42606.0,"Position":288.0,"HyperDash":false}]},{"StartTime":42756.0,"Objects":[{"StartTime":42756.0,"Position":256.0,"HyperDash":false},{"StartTime":42830.0,"Position":220.250839,"HyperDash":false},{"StartTime":42905.0,"Position":206.167221,"HyperDash":false},{"StartTime":42962.0,"Position":205.103668,"HyperDash":false},{"StartTime":43055.0,"Position":156.0,"HyperDash":false}]},{"StartTime":43356.0,"Objects":[{"StartTime":43356.0,"Position":172.0,"HyperDash":false}]},{"StartTime":43506.0,"Objects":[{"StartTime":43506.0,"Position":144.0,"HyperDash":false}]},{"StartTime":43656.0,"Objects":[{"StartTime":43656.0,"Position":172.0,"HyperDash":false}]},{"StartTime":43956.0,"Objects":[{"StartTime":43956.0,"Position":288.0,"HyperDash":false}]},{"StartTime":44106.0,"Objects":[{"StartTime":44106.0,"Position":230.0,"HyperDash":false}]},{"StartTime":44256.0,"Objects":[{"StartTime":44256.0,"Position":250.0,"HyperDash":false}]},{"StartTime":44556.0,"Objects":[{"StartTime":44556.0,"Position":374.0,"HyperDash":false}]},{"StartTime":44706.0,"Objects":[{"StartTime":44706.0,"Position":302.0,"HyperDash":false}]},{"StartTime":44856.0,"Objects":[{"StartTime":44856.0,"Position":282.0,"HyperDash":false}]},{"StartTime":45605.0,"Objects":[{"StartTime":45605.0,"Position":256.0,"HyperDash":false},{"StartTime":45679.0,"Position":263.6996,"HyperDash":false},{"StartTime":45754.0,"Position":306.0,"HyperDash":false},{"StartTime":45829.0,"Position":275.233643,"HyperDash":false},{"StartTime":45904.0,"Position":256.0,"HyperDash":false},{"StartTime":45979.0,"Position":286.8331,"HyperDash":false},{"StartTime":46054.0,"Position":306.0,"HyperDash":false},{"StartTime":46129.0,"Position":293.100128,"HyperDash":false},{"StartTime":46204.0,"Position":256.0,"HyperDash":false},{"StartTime":46261.0,"Position":261.958618,"HyperDash":false},{"StartTime":46354.0,"Position":306.0,"HyperDash":false}]},{"StartTime":46655.0,"Objects":[{"StartTime":46655.0,"Position":376.0,"HyperDash":false}]},{"StartTime":46955.0,"Objects":[{"StartTime":46955.0,"Position":448.0,"HyperDash":false}]},{"StartTime":47255.0,"Objects":[{"StartTime":47255.0,"Position":459.0,"HyperDash":false}]},{"StartTime":47555.0,"Objects":[{"StartTime":47555.0,"Position":304.0,"HyperDash":false}]},{"StartTime":47705.0,"Objects":[{"StartTime":47705.0,"Position":376.0,"HyperDash":false}]},{"StartTime":48005.0,"Objects":[{"StartTime":48005.0,"Position":376.0,"HyperDash":false},{"StartTime":48079.0,"Position":381.749176,"HyperDash":false},{"StartTime":48154.0,"Position":426.0,"HyperDash":false},{"StartTime":48211.0,"Position":410.103668,"HyperDash":false},{"StartTime":48304.0,"Position":376.0,"HyperDash":false}]},{"StartTime":48454.0,"Objects":[{"StartTime":48454.0,"Position":232.0,"HyperDash":false}]},{"StartTime":48604.0,"Objects":[{"StartTime":48604.0,"Position":304.0,"HyperDash":false}]},{"StartTime":48754.0,"Objects":[{"StartTime":48754.0,"Position":224.0,"HyperDash":false}]},{"StartTime":49054.0,"Objects":[{"StartTime":49054.0,"Position":160.0,"HyperDash":false}]},{"StartTime":49354.0,"Objects":[{"StartTime":49354.0,"Position":80.0,"HyperDash":false}]},{"StartTime":49654.0,"Objects":[{"StartTime":49654.0,"Position":16.0,"HyperDash":false}]},{"StartTime":49954.0,"Objects":[{"StartTime":49954.0,"Position":80.0,"HyperDash":false}]},{"StartTime":50404.0,"Objects":[{"StartTime":50404.0,"Position":48.0,"HyperDash":false},{"StartTime":50460.0,"Position":52.7919464,"HyperDash":false},{"StartTime":50553.0,"Position":98.0,"HyperDash":false}]},{"StartTime":50704.0,"Objects":[{"StartTime":50704.0,"Position":136.0,"HyperDash":false},{"StartTime":50760.0,"Position":160.791946,"HyperDash":false},{"StartTime":50853.0,"Position":186.0,"HyperDash":false}]},{"StartTime":51003.0,"Objects":[{"StartTime":51003.0,"Position":224.0,"HyperDash":false},{"StartTime":51059.0,"Position":255.791946,"HyperDash":false},{"StartTime":51152.0,"Position":274.0,"HyperDash":false}]},{"StartTime":51453.0,"Objects":[{"StartTime":51453.0,"Position":400.0,"HyperDash":false}]},{"StartTime":51753.0,"Objects":[{"StartTime":51753.0,"Position":432.0,"HyperDash":false}]},{"StartTime":52053.0,"Objects":[{"StartTime":52053.0,"Position":488.0,"HyperDash":false}]},{"StartTime":52353.0,"Objects":[{"StartTime":52353.0,"Position":507.0,"HyperDash":false}]},{"StartTime":52503.0,"Objects":[{"StartTime":52503.0,"Position":508.0,"HyperDash":false}]},{"StartTime":52803.0,"Objects":[{"StartTime":52803.0,"Position":488.0,"HyperDash":false},{"StartTime":52877.0,"Position":473.278381,"HyperDash":false},{"StartTime":52952.0,"Position":438.0,"HyperDash":false},{"StartTime":53027.0,"Position":471.832977,"HyperDash":false},{"StartTime":53102.0,"Position":488.0,"HyperDash":false},{"StartTime":53159.0,"Position":476.069031,"HyperDash":false},{"StartTime":53252.0,"Position":438.0,"HyperDash":false}]},{"StartTime":53403.0,"Objects":[{"StartTime":53403.0,"Position":368.0,"HyperDash":false}]},{"StartTime":53553.0,"Objects":[{"StartTime":53553.0,"Position":368.0,"HyperDash":false},{"StartTime":53627.0,"Position":341.4955,"HyperDash":false},{"StartTime":53702.0,"Position":320.428864,"HyperDash":false},{"StartTime":53777.0,"Position":295.8305,"HyperDash":false},{"StartTime":53852.0,"Position":289.023224,"HyperDash":false},{"StartTime":53927.0,"Position":294.625671,"HyperDash":false},{"StartTime":54002.0,"Position":320.1455,"HyperDash":false},{"StartTime":54059.0,"Position":332.386719,"HyperDash":false},{"StartTime":54152.0,"Position":368.0,"HyperDash":false}]},{"StartTime":54452.0,"Objects":[{"StartTime":54452.0,"Position":368.0,"HyperDash":false},{"StartTime":54526.0,"Position":361.6083,"HyperDash":false},{"StartTime":54601.0,"Position":346.408356,"HyperDash":false},{"StartTime":54676.0,"Position":309.5409,"HyperDash":false},{"StartTime":54751.0,"Position":302.165344,"HyperDash":false},{"StartTime":54826.0,"Position":280.769684,"HyperDash":false},{"StartTime":54901.0,"Position":252.958511,"HyperDash":false},{"StartTime":54976.0,"Position":235.981033,"HyperDash":false},{"StartTime":55051.0,"Position":202.952667,"HyperDash":false},{"StartTime":55108.0,"Position":198.931656,"HyperDash":false},{"StartTime":55201.0,"Position":152.9338,"HyperDash":false}]},{"StartTime":60000.0,"Objects":[{"StartTime":60000.0,"Position":256.0,"HyperDash":false},{"StartTime":60074.0,"Position":256.3498,"HyperDash":false},{"StartTime":60149.0,"Position":264.8665,"HyperDash":false},{"StartTime":60224.0,"Position":286.383179,"HyperDash":false},{"StartTime":60299.0,"Position":305.899872,"HyperDash":false},{"StartTime":60374.0,"Position":314.416565,"HyperDash":false},{"StartTime":60449.0,"Position":335.933228,"HyperDash":false},{"StartTime":60524.0,"Position":344.449921,"HyperDash":false},{"StartTime":60599.0,"Position":355.9666,"HyperDash":false},{"StartTime":60656.0,"Position":381.4793,"HyperDash":false},{"StartTime":60749.0,"Position":381.0,"HyperDash":false}]},{"StartTime":61050.0,"Objects":[{"StartTime":61050.0,"Position":416.0,"HyperDash":false},{"StartTime":61124.0,"Position":413.0,"HyperDash":false},{"StartTime":61199.0,"Position":430.0,"HyperDash":false},{"StartTime":61274.0,"Position":403.0,"HyperDash":false},{"StartTime":61349.0,"Position":416.0,"HyperDash":false},{"StartTime":61424.0,"Position":404.0,"HyperDash":false},{"StartTime":61499.0,"Position":419.0,"HyperDash":false},{"StartTime":61574.0,"Position":426.0,"HyperDash":false},{"StartTime":61649.0,"Position":416.0,"HyperDash":false},{"StartTime":61715.0,"Position":396.0,"HyperDash":false},{"StartTime":61781.0,"Position":420.0,"HyperDash":false},{"StartTime":61847.0,"Position":421.0,"HyperDash":false},{"StartTime":61949.0,"Position":416.0,"HyperDash":false}]},{"StartTime":62250.0,"Objects":[{"StartTime":62250.0,"Position":416.0,"HyperDash":false},{"StartTime":62324.0,"Position":403.652954,"HyperDash":false},{"StartTime":62399.0,"Position":373.139038,"HyperDash":false},{"StartTime":62474.0,"Position":359.625122,"HyperDash":false},{"StartTime":62549.0,"Position":366.111237,"HyperDash":false},{"StartTime":62624.0,"Position":362.597321,"HyperDash":false},{"StartTime":62699.0,"Position":334.083435,"HyperDash":false},{"StartTime":62774.0,"Position":344.569519,"HyperDash":false},{"StartTime":62849.0,"Position":316.0556,"HyperDash":false},{"StartTime":62915.0,"Position":298.0434,"HyperDash":false},{"StartTime":62981.0,"Position":275.031158,"HyperDash":false},{"StartTime":63047.0,"Position":265.018921,"HyperDash":false},{"StartTime":63149.0,"Position":266.0,"HyperDash":false}]},{"StartTime":63449.0,"Objects":[{"StartTime":63449.0,"Position":232.0,"HyperDash":false},{"StartTime":63523.0,"Position":246.0,"HyperDash":false},{"StartTime":63598.0,"Position":233.0,"HyperDash":false},{"StartTime":63673.0,"Position":236.0,"HyperDash":false},{"StartTime":63748.0,"Position":232.0,"HyperDash":false},{"StartTime":63823.0,"Position":219.0,"HyperDash":false},{"StartTime":63898.0,"Position":231.0,"HyperDash":false},{"StartTime":63973.0,"Position":242.0,"HyperDash":false},{"StartTime":64048.0,"Position":232.0,"HyperDash":false},{"StartTime":64123.0,"Position":228.0,"HyperDash":false},{"StartTime":64198.0,"Position":215.0,"HyperDash":false},{"StartTime":64273.0,"Position":243.0,"HyperDash":false},{"StartTime":64348.0,"Position":232.0,"HyperDash":false},{"StartTime":64405.0,"Position":249.0,"HyperDash":false},{"StartTime":64498.0,"Position":232.0,"HyperDash":false}]},{"StartTime":64799.0,"Objects":[{"StartTime":64799.0,"Position":160.0,"HyperDash":false},{"StartTime":64873.0,"Position":144.3059,"HyperDash":false},{"StartTime":64948.0,"Position":110.278084,"HyperDash":false},{"StartTime":65023.0,"Position":84.25028,"HyperDash":false},{"StartTime":65098.0,"Position":60.0,"HyperDash":false},{"StartTime":65173.0,"Position":96.80534,"HyperDash":false},{"StartTime":65248.0,"Position":109.833145,"HyperDash":false},{"StartTime":65323.0,"Position":135.860962,"HyperDash":false},{"StartTime":65398.0,"Position":160.0,"HyperDash":false},{"StartTime":65473.0,"Position":122.08342,"HyperDash":false},{"StartTime":65548.0,"Position":110.055618,"HyperDash":false},{"StartTime":65605.0,"Position":79.03449,"HyperDash":false},{"StartTime":65698.0,"Position":60.0,"HyperDash":false}]},{"StartTime":65998.0,"Objects":[{"StartTime":65998.0,"Position":56.0,"HyperDash":false}]},{"StartTime":66298.0,"Objects":[{"StartTime":66298.0,"Position":36.0,"HyperDash":false}]},{"StartTime":66598.0,"Objects":[{"StartTime":66598.0,"Position":63.0,"HyperDash":false}]},{"StartTime":66898.0,"Objects":[{"StartTime":66898.0,"Position":200.0,"HyperDash":false}]},{"StartTime":67198.0,"Objects":[{"StartTime":67198.0,"Position":287.0,"HyperDash":false},{"StartTime":67272.0,"Position":341.0,"HyperDash":false},{"StartTime":67347.0,"Position":145.0,"HyperDash":false},{"StartTime":67422.0,"Position":84.0,"HyperDash":false},{"StartTime":67497.0,"Position":189.0,"HyperDash":false},{"StartTime":67572.0,"Position":498.0,"HyperDash":false},{"StartTime":67647.0,"Position":416.0,"HyperDash":false},{"StartTime":67722.0,"Position":211.0,"HyperDash":false},{"StartTime":67797.0,"Position":167.0,"HyperDash":false},{"StartTime":67872.0,"Position":466.0,"HyperDash":false},{"StartTime":67947.0,"Position":114.0,"HyperDash":false},{"StartTime":68022.0,"Position":125.0,"HyperDash":false},{"StartTime":68097.0,"Position":457.0,"HyperDash":false},{"StartTime":68172.0,"Position":131.0,"HyperDash":false},{"StartTime":68247.0,"Position":337.0,"HyperDash":false},{"StartTime":68322.0,"Position":39.0,"HyperDash":false},{"StartTime":68397.0,"Position":311.0,"HyperDash":false},{"StartTime":68472.0,"Position":208.0,"HyperDash":false},{"StartTime":68547.0,"Position":357.0,"HyperDash":false},{"StartTime":68622.0,"Position":240.0,"HyperDash":false},{"StartTime":68697.0,"Position":35.0,"HyperDash":false},{"StartTime":68772.0,"Position":254.0,"HyperDash":false},{"StartTime":68847.0,"Position":292.0,"HyperDash":false},{"StartTime":68922.0,"Position":369.0,"HyperDash":false},{"StartTime":68997.0,"Position":14.0,"HyperDash":false},{"StartTime":69072.0,"Position":390.0,"HyperDash":false},{"StartTime":69147.0,"Position":286.0,"HyperDash":false},{"StartTime":69222.0,"Position":92.0,"HyperDash":false},{"StartTime":69297.0,"Position":170.0,"HyperDash":false},{"StartTime":69372.0,"Position":93.0,"HyperDash":false},{"StartTime":69447.0,"Position":139.0,"HyperDash":false},{"StartTime":69522.0,"Position":301.0,"HyperDash":false},{"StartTime":69597.0,"Position":137.0,"HyperDash":false}]},{"StartTime":69897.0,"Objects":[{"StartTime":69897.0,"Position":256.0,"HyperDash":false}]},{"StartTime":70047.0,"Objects":[{"StartTime":70047.0,"Position":320.0,"HyperDash":false}]},{"StartTime":70197.0,"Objects":[{"StartTime":70197.0,"Position":340.0,"HyperDash":false}]},{"StartTime":70497.0,"Objects":[{"StartTime":70497.0,"Position":340.0,"HyperDash":false}]},{"StartTime":70797.0,"Objects":[{"StartTime":70797.0,"Position":300.0,"HyperDash":false}]},{"StartTime":71096.0,"Objects":[{"StartTime":71096.0,"Position":248.0,"HyperDash":false}]},{"StartTime":71246.0,"Objects":[{"StartTime":71246.0,"Position":168.0,"HyperDash":false}]},{"StartTime":71396.0,"Objects":[{"StartTime":71396.0,"Position":184.0,"HyperDash":false}]},{"StartTime":71696.0,"Objects":[{"StartTime":71696.0,"Position":24.0,"HyperDash":false}]},{"StartTime":71996.0,"Objects":[{"StartTime":71996.0,"Position":104.0,"HyperDash":false},{"StartTime":72070.0,"Position":79.25084,"HyperDash":false},{"StartTime":72145.0,"Position":54.0,"HyperDash":false},{"StartTime":72202.0,"Position":54.8963242,"HyperDash":false},{"StartTime":72295.0,"Position":104.0,"HyperDash":false}]},{"StartTime":72446.0,"Objects":[{"StartTime":72446.0,"Position":192.0,"HyperDash":false}]},{"StartTime":72746.0,"Objects":[{"StartTime":72746.0,"Position":16.0,"HyperDash":false}]},{"StartTime":73046.0,"Objects":[{"StartTime":73046.0,"Position":104.0,"HyperDash":false},{"StartTime":73120.0,"Position":123.738281,"HyperDash":false},{"StartTime":73195.0,"Position":123.252632,"HyperDash":false},{"StartTime":73270.0,"Position":144.834549,"HyperDash":false},{"StartTime":73345.0,"Position":166.952621,"HyperDash":false},{"StartTime":73420.0,"Position":208.089325,"HyperDash":false},{"StartTime":73495.0,"Position":215.686081,"HyperDash":false},{"StartTime":73570.0,"Position":248.499512,"HyperDash":false},{"StartTime":73645.0,"Position":265.421631,"HyperDash":false},{"StartTime":73720.0,"Position":275.398376,"HyperDash":false},{"StartTime":73795.0,"Position":315.4022,"HyperDash":false},{"StartTime":73870.0,"Position":351.418854,"HyperDash":false},{"StartTime":73945.0,"Position":365.440521,"HyperDash":false},{"StartTime":74002.0,"Position":402.458252,"HyperDash":false},{"StartTime":74095.0,"Position":415.4878,"HyperDash":false}]},{"StartTime":74395.0,"Objects":[{"StartTime":74395.0,"Position":416.0,"HyperDash":false},{"StartTime":74469.0,"Position":417.7021,"HyperDash":false},{"StartTime":74544.0,"Position":397.104645,"HyperDash":false},{"StartTime":74619.0,"Position":387.306122,"HyperDash":false},{"StartTime":74694.0,"Position":350.779144,"HyperDash":false},{"StartTime":74769.0,"Position":366.889374,"HyperDash":false},{"StartTime":74844.0,"Position":396.758,"HyperDash":false},{"StartTime":74919.0,"Position":408.543182,"HyperDash":false},{"StartTime":74994.0,"Position":416.0,"HyperDash":false},{"StartTime":75069.0,"Position":394.62265,"HyperDash":false},{"StartTime":75144.0,"Position":396.931335,"HyperDash":false},{"StartTime":75201.0,"Position":363.6833,"HyperDash":false},{"StartTime":75294.0,"Position":350.779144,"HyperDash":false}]},{"StartTime":75595.0,"Objects":[{"StartTime":75595.0,"Position":280.0,"HyperDash":false}]},{"StartTime":75895.0,"Objects":[{"StartTime":75895.0,"Position":136.0,"HyperDash":false}]},{"StartTime":76195.0,"Objects":[{"StartTime":76195.0,"Position":280.0,"HyperDash":false}]},{"StartTime":76345.0,"Objects":[{"StartTime":76345.0,"Position":208.0,"HyperDash":false}]},{"StartTime":76495.0,"Objects":[{"StartTime":76495.0,"Position":228.0,"HyperDash":false}]},{"StartTime":76794.0,"Objects":[{"StartTime":76794.0,"Position":21.0,"HyperDash":false},{"StartTime":76859.0,"Position":193.0,"HyperDash":false},{"StartTime":76925.0,"Position":52.0,"HyperDash":false},{"StartTime":76990.0,"Position":466.0,"HyperDash":false},{"StartTime":77056.0,"Position":135.0,"HyperDash":false},{"StartTime":77121.0,"Position":121.0,"HyperDash":false},{"StartTime":77187.0,"Position":427.0,"HyperDash":false},{"StartTime":77253.0,"Position":176.0,"HyperDash":false},{"StartTime":77318.0,"Position":96.0,"HyperDash":false},{"StartTime":77384.0,"Position":345.0,"HyperDash":false},{"StartTime":77449.0,"Position":11.0,"HyperDash":false},{"StartTime":77515.0,"Position":393.0,"HyperDash":false},{"StartTime":77581.0,"Position":440.0,"HyperDash":false},{"StartTime":77646.0,"Position":179.0,"HyperDash":false},{"StartTime":77712.0,"Position":470.0,"HyperDash":false},{"StartTime":77777.0,"Position":89.0,"HyperDash":false},{"StartTime":77843.0,"Position":408.0,"HyperDash":false},{"StartTime":77909.0,"Position":243.0,"HyperDash":false},{"StartTime":77974.0,"Position":78.0,"HyperDash":false},{"StartTime":78040.0,"Position":172.0,"HyperDash":false},{"StartTime":78105.0,"Position":450.0,"HyperDash":false},{"StartTime":78171.0,"Position":231.0,"HyperDash":false},{"StartTime":78237.0,"Position":118.0,"HyperDash":false},{"StartTime":78302.0,"Position":511.0,"HyperDash":false},{"StartTime":78368.0,"Position":333.0,"HyperDash":false},{"StartTime":78433.0,"Position":234.0,"HyperDash":false},{"StartTime":78499.0,"Position":228.0,"HyperDash":false},{"StartTime":78565.0,"Position":302.0,"HyperDash":false},{"StartTime":78630.0,"Position":390.0,"HyperDash":false},{"StartTime":78696.0,"Position":75.0,"HyperDash":false},{"StartTime":78761.0,"Position":506.0,"HyperDash":false},{"StartTime":78827.0,"Position":3.0,"HyperDash":false},{"StartTime":78893.0,"Position":289.0,"HyperDash":false}]},{"StartTime":79194.0,"Objects":[{"StartTime":79194.0,"Position":256.0,"HyperDash":false},{"StartTime":79268.0,"Position":249.6807,"HyperDash":false},{"StartTime":79343.0,"Position":245.6988,"HyperDash":false},{"StartTime":79418.0,"Position":237.299881,"HyperDash":false},{"StartTime":79493.0,"Position":211.7363,"HyperDash":false},{"StartTime":79550.0,"Position":208.713608,"HyperDash":false},{"StartTime":79643.0,"Position":165.0138,"HyperDash":false}]},{"StartTime":79793.0,"Objects":[{"StartTime":79793.0,"Position":128.0,"HyperDash":false},{"StartTime":79867.0,"Position":121.464394,"HyperDash":false},{"StartTime":79942.0,"Position":81.6096039,"HyperDash":false},{"StartTime":80017.0,"Position":52.0348129,"HyperDash":false},{"StartTime":80092.0,"Position":60.8326073,"HyperDash":false},{"StartTime":80149.0,"Position":53.9088326,"HyperDash":false},{"StartTime":80242.0,"Position":56.0562,"HyperDash":false}]},{"StartTime":80543.0,"Objects":[{"StartTime":80543.0,"Position":76.0,"HyperDash":false}]},{"StartTime":80843.0,"Objects":[{"StartTime":80843.0,"Position":56.0,"HyperDash":false}]},{"StartTime":81143.0,"Objects":[{"StartTime":81143.0,"Position":200.0,"HyperDash":false}]},{"StartTime":81443.0,"Objects":[{"StartTime":81443.0,"Position":180.0,"HyperDash":false}]},{"StartTime":81593.0,"Objects":[{"StartTime":81593.0,"Position":200.0,"HyperDash":false},{"StartTime":81667.0,"Position":218.643234,"HyperDash":false},{"StartTime":81742.0,"Position":249.328659,"HyperDash":false},{"StartTime":81817.0,"Position":278.3164,"HyperDash":false},{"StartTime":81892.0,"Position":296.1659,"HyperDash":false},{"StartTime":81967.0,"Position":318.451416,"HyperDash":false},{"StartTime":82042.0,"Position":336.820862,"HyperDash":false},{"StartTime":82117.0,"Position":362.178284,"HyperDash":false},{"StartTime":82192.0,"Position":369.602051,"HyperDash":false},{"StartTime":82267.0,"Position":338.393555,"HyperDash":false},{"StartTime":82342.0,"Position":337.067169,"HyperDash":false},{"StartTime":82417.0,"Position":321.719727,"HyperDash":false},{"StartTime":82492.0,"Position":296.459869,"HyperDash":false},{"StartTime":82567.0,"Position":256.630157,"HyperDash":false},{"StartTime":82642.0,"Position":249.6552,"HyperDash":false},{"StartTime":82699.0,"Position":228.9382,"HyperDash":false},{"StartTime":82792.0,"Position":200.0,"HyperDash":false}]},{"StartTime":82942.0,"Objects":[{"StartTime":82942.0,"Position":200.0,"HyperDash":false}]},{"StartTime":83242.0,"Objects":[{"StartTime":83242.0,"Position":180.0,"HyperDash":false}]},{"StartTime":83542.0,"Objects":[{"StartTime":83542.0,"Position":180.0,"HyperDash":false}]},{"StartTime":83692.0,"Objects":[{"StartTime":83692.0,"Position":220.0,"HyperDash":false}]},{"StartTime":83842.0,"Objects":[{"StartTime":83842.0,"Position":220.0,"HyperDash":false}]},{"StartTime":83992.0,"Objects":[{"StartTime":83992.0,"Position":200.0,"HyperDash":false},{"StartTime":84066.0,"Position":214.895981,"HyperDash":false},{"StartTime":84141.0,"Position":217.903488,"HyperDash":false},{"StartTime":84216.0,"Position":225.305542,"HyperDash":false},{"StartTime":84291.0,"Position":263.285431,"HyperDash":false},{"StartTime":84348.0,"Position":288.04718,"HyperDash":false},{"StartTime":84441.0,"Position":312.975067,"HyperDash":false}]},{"StartTime":84592.0,"Objects":[{"StartTime":84592.0,"Position":344.0,"HyperDash":false},{"StartTime":84666.0,"Position":386.711,"HyperDash":false},{"StartTime":84741.0,"Position":393.655243,"HyperDash":false},{"StartTime":84816.0,"Position":433.20578,"HyperDash":false},{"StartTime":84891.0,"Position":441.4496,"HyperDash":false},{"StartTime":84948.0,"Position":466.8295,"HyperDash":false},{"StartTime":85041.0,"Position":473.5803,"HyperDash":false}]},{"StartTime":85341.0,"Objects":[{"StartTime":85341.0,"Position":464.0,"HyperDash":false}]},{"StartTime":85641.0,"Objects":[{"StartTime":85641.0,"Position":480.0,"HyperDash":false}]},{"StartTime":85941.0,"Objects":[{"StartTime":85941.0,"Position":464.0,"HyperDash":false}]},{"StartTime":86241.0,"Objects":[{"StartTime":86241.0,"Position":336.0,"HyperDash":false}]},{"StartTime":86391.0,"Objects":[{"StartTime":86391.0,"Position":400.0,"HyperDash":false},{"StartTime":86465.0,"Position":384.340973,"HyperDash":false},{"StartTime":86540.0,"Position":350.5981,"HyperDash":false},{"StartTime":86615.0,"Position":323.677429,"HyperDash":false},{"StartTime":86690.0,"Position":304.500153,"HyperDash":false},{"StartTime":86765.0,"Position":291.3181,"HyperDash":false},{"StartTime":86840.0,"Position":264.219,"HyperDash":false},{"StartTime":86915.0,"Position":246.938583,"HyperDash":false},{"StartTime":86990.0,"Position":217.532028,"HyperDash":false},{"StartTime":87065.0,"Position":225.6241,"HyperDash":false},{"StartTime":87140.0,"Position":263.9408,"HyperDash":false},{"StartTime":87215.0,"Position":291.0559,"HyperDash":false},{"StartTime":87290.0,"Position":304.220367,"HyperDash":false},{"StartTime":87365.0,"Position":325.3685,"HyperDash":false},{"StartTime":87440.0,"Position":350.271576,"HyperDash":false},{"StartTime":87497.0,"Position":364.034241,"HyperDash":false},{"StartTime":87590.0,"Position":400.0,"HyperDash":false}]},{"StartTime":87741.0,"Objects":[{"StartTime":87741.0,"Position":400.0,"HyperDash":false}]},{"StartTime":88041.0,"Objects":[{"StartTime":88041.0,"Position":420.0,"HyperDash":false}]},{"StartTime":88340.0,"Objects":[{"StartTime":88340.0,"Position":380.0,"HyperDash":false}]},{"StartTime":88490.0,"Objects":[{"StartTime":88490.0,"Position":320.0,"HyperDash":false}]},{"StartTime":88640.0,"Objects":[{"StartTime":88640.0,"Position":314.0,"HyperDash":false}]},{"StartTime":88940.0,"Objects":[{"StartTime":88940.0,"Position":0.0,"HyperDash":false},{"StartTime":89033.0,"Position":111.0,"HyperDash":false},{"StartTime":89127.0,"Position":358.0,"HyperDash":false},{"StartTime":89221.0,"Position":476.0,"HyperDash":false},{"StartTime":89315.0,"Position":87.0,"HyperDash":false},{"StartTime":89408.0,"Position":33.0,"HyperDash":false},{"StartTime":89502.0,"Position":166.0,"HyperDash":false},{"StartTime":89596.0,"Position":275.0,"HyperDash":false},{"StartTime":89690.0,"Position":119.0,"HyperDash":false}]},{"StartTime":89990.0,"Objects":[{"StartTime":89990.0,"Position":56.0,"HyperDash":false}]},{"StartTime":90140.0,"Objects":[{"StartTime":90140.0,"Position":76.0,"HyperDash":false}]},{"StartTime":90290.0,"Objects":[{"StartTime":90290.0,"Position":36.0,"HyperDash":false}]},{"StartTime":90590.0,"Objects":[{"StartTime":90590.0,"Position":200.0,"HyperDash":false}]},{"StartTime":90740.0,"Objects":[{"StartTime":90740.0,"Position":160.0,"HyperDash":false},{"StartTime":90814.0,"Position":176.321808,"HyperDash":false},{"StartTime":90889.0,"Position":206.339,"HyperDash":false},{"StartTime":90964.0,"Position":213.574585,"HyperDash":false},{"StartTime":91039.0,"Position":236.2215,"HyperDash":false},{"StartTime":91114.0,"Position":240.839,"HyperDash":false},{"StartTime":91189.0,"Position":206.688522,"HyperDash":false},{"StartTime":91264.0,"Position":182.7456,"HyperDash":false},{"StartTime":91339.0,"Position":160.0,"HyperDash":false},{"StartTime":91414.0,"Position":183.5337,"HyperDash":false},{"StartTime":91489.0,"Position":206.513763,"HyperDash":false},{"StartTime":91546.0,"Position":220.042145,"HyperDash":false},{"StartTime":91639.0,"Position":236.2215,"HyperDash":false}]},{"StartTime":91939.0,"Objects":[{"StartTime":91939.0,"Position":264.0,"HyperDash":false}]},{"StartTime":92089.0,"Objects":[{"StartTime":92089.0,"Position":259.0,"HyperDash":false}]},{"StartTime":92389.0,"Objects":[{"StartTime":92389.0,"Position":408.0,"HyperDash":false}]},{"StartTime":92539.0,"Objects":[{"StartTime":92539.0,"Position":328.0,"HyperDash":false}]},{"StartTime":92689.0,"Objects":[{"StartTime":92689.0,"Position":400.0,"HyperDash":false}]},{"StartTime":92839.0,"Objects":[{"StartTime":92839.0,"Position":464.0,"HyperDash":false}]},{"StartTime":92989.0,"Objects":[{"StartTime":92989.0,"Position":484.0,"HyperDash":false}]},{"StartTime":93139.0,"Objects":[{"StartTime":93139.0,"Position":496.0,"HyperDash":false}]},{"StartTime":93439.0,"Objects":[{"StartTime":93439.0,"Position":496.0,"HyperDash":false},{"StartTime":93513.0,"Position":508.470551,"HyperDash":false},{"StartTime":93588.0,"Position":481.299042,"HyperDash":false},{"StartTime":93663.0,"Position":447.88858,"HyperDash":false},{"StartTime":93738.0,"Position":442.8401,"HyperDash":false},{"StartTime":93813.0,"Position":434.920868,"HyperDash":false},{"StartTime":93888.0,"Position":396.039459,"HyperDash":false},{"StartTime":93963.0,"Position":378.642273,"HyperDash":false},{"StartTime":94038.0,"Position":346.954773,"HyperDash":false},{"StartTime":94113.0,"Position":325.103058,"HyperDash":false},{"StartTime":94188.0,"Position":297.157654,"HyperDash":false},{"StartTime":94245.0,"Position":278.1643,"HyperDash":false},{"StartTime":94338.0,"Position":247.142532,"HyperDash":false}]},{"StartTime":94788.0,"Objects":[{"StartTime":94788.0,"Position":160.0,"HyperDash":false},{"StartTime":94862.0,"Position":178.0,"HyperDash":false},{"StartTime":94937.0,"Position":160.0,"HyperDash":false},{"StartTime":94994.0,"Position":156.0,"HyperDash":false},{"StartTime":95087.0,"Position":160.0,"HyperDash":false}]},{"StartTime":95238.0,"Objects":[{"StartTime":95238.0,"Position":180.0,"HyperDash":false}]},{"StartTime":95388.0,"Objects":[{"StartTime":95388.0,"Position":140.0,"HyperDash":false}]},{"StartTime":95538.0,"Objects":[{"StartTime":95538.0,"Position":160.0,"HyperDash":false},{"StartTime":95612.0,"Position":178.7418,"HyperDash":false},{"StartTime":95687.0,"Position":173.08049,"HyperDash":false},{"StartTime":95744.0,"Position":203.788971,"HyperDash":false},{"StartTime":95837.0,"Position":215.526108,"HyperDash":false}]},{"StartTime":96138.0,"Objects":[{"StartTime":96138.0,"Position":296.0,"HyperDash":false},{"StartTime":96212.0,"Position":337.7064,"HyperDash":false},{"StartTime":96287.0,"Position":345.412964,"HyperDash":false},{"StartTime":96344.0,"Position":376.616638,"HyperDash":false},{"StartTime":96437.0,"Position":391.160645,"HyperDash":false}]},{"StartTime":96737.0,"Objects":[{"StartTime":96737.0,"Position":464.0,"HyperDash":false}]},{"StartTime":96887.0,"Objects":[{"StartTime":96887.0,"Position":416.0,"HyperDash":false}]},{"StartTime":97187.0,"Objects":[{"StartTime":97187.0,"Position":440.0,"HyperDash":false},{"StartTime":97261.0,"Position":447.4056,"HyperDash":false},{"StartTime":97336.0,"Position":432.9317,"HyperDash":false},{"StartTime":97411.0,"Position":435.742554,"HyperDash":false},{"StartTime":97486.0,"Position":407.575775,"HyperDash":false},{"StartTime":97561.0,"Position":379.243927,"HyperDash":false},{"StartTime":97636.0,"Position":366.1177,"HyperDash":false},{"StartTime":97711.0,"Position":344.4165,"HyperDash":false},{"StartTime":97786.0,"Position":317.914917,"HyperDash":false},{"StartTime":97843.0,"Position":304.0325,"HyperDash":false},{"StartTime":97936.0,"Position":268.035461,"HyperDash":false}]},{"StartTime":98237.0,"Objects":[{"StartTime":98237.0,"Position":200.0,"HyperDash":false}]},{"StartTime":98537.0,"Objects":[{"StartTime":98537.0,"Position":56.0,"HyperDash":false}]},{"StartTime":98837.0,"Objects":[{"StartTime":98837.0,"Position":76.0,"HyperDash":false}]},{"StartTime":99137.0,"Objects":[{"StartTime":99137.0,"Position":56.0,"HyperDash":false},{"StartTime":99211.0,"Position":60.74916,"HyperDash":false},{"StartTime":99286.0,"Position":106.0,"HyperDash":false},{"StartTime":99343.0,"Position":88.1036758,"HyperDash":false},{"StartTime":99436.0,"Position":56.0,"HyperDash":false}]},{"StartTime":99586.0,"Objects":[{"StartTime":99586.0,"Position":56.0,"HyperDash":false},{"StartTime":99660.0,"Position":61.48453,"HyperDash":false},{"StartTime":99735.0,"Position":67.69644,"HyperDash":false},{"StartTime":99810.0,"Position":92.0094,"HyperDash":false},{"StartTime":99885.0,"Position":107.968033,"HyperDash":false},{"StartTime":99960.0,"Position":87.38017,"HyperDash":false},{"StartTime":100035.0,"Position":67.9301,"HyperDash":false},{"StartTime":100110.0,"Position":56.5835648,"HyperDash":false},{"StartTime":100185.0,"Position":56.0,"HyperDash":false},{"StartTime":100260.0,"Position":65.53404,"HyperDash":false},{"StartTime":100335.0,"Position":67.81326,"HyperDash":false},{"StartTime":100392.0,"Position":74.3646545,"HyperDash":false},{"StartTime":100485.0,"Position":107.968033,"HyperDash":false}]},{"StartTime":100636.0,"Objects":[{"StartTime":100636.0,"Position":144.0,"HyperDash":false}]},{"StartTime":100936.0,"Objects":[{"StartTime":100936.0,"Position":288.0,"HyperDash":false}]},{"StartTime":101236.0,"Objects":[{"StartTime":101236.0,"Position":268.0,"HyperDash":false}]},{"StartTime":101536.0,"Objects":[{"StartTime":101536.0,"Position":360.0,"HyperDash":false},{"StartTime":101610.0,"Position":381.602356,"HyperDash":false},{"StartTime":101685.0,"Position":408.7352,"HyperDash":false},{"StartTime":101760.0,"Position":420.921082,"HyperDash":false},{"StartTime":101835.0,"Position":450.0819,"HyperDash":false},{"StartTime":101910.0,"Position":480.7513,"HyperDash":false},{"StartTime":101985.0,"Position":478.132416,"HyperDash":false},{"StartTime":102042.0,"Position":481.652863,"HyperDash":false},{"StartTime":102135.0,"Position":495.055,"HyperDash":false}]},{"StartTime":102435.0,"Objects":[{"StartTime":102435.0,"Position":496.0,"HyperDash":false},{"StartTime":102509.0,"Position":478.3312,"HyperDash":false},{"StartTime":102584.0,"Position":446.623962,"HyperDash":false},{"StartTime":102659.0,"Position":441.667145,"HyperDash":false},{"StartTime":102734.0,"Position":400.097137,"HyperDash":false},{"StartTime":102809.0,"Position":373.617828,"HyperDash":false},{"StartTime":102884.0,"Position":361.791168,"HyperDash":false},{"StartTime":102941.0,"Position":366.174866,"HyperDash":false},{"StartTime":103034.0,"Position":334.736969,"HyperDash":false}]},{"StartTime":103335.0,"Objects":[{"StartTime":103335.0,"Position":288.0,"HyperDash":false}]},{"StartTime":103635.0,"Objects":[{"StartTime":103635.0,"Position":272.0,"HyperDash":false}]},{"StartTime":103935.0,"Objects":[{"StartTime":103935.0,"Position":176.0,"HyperDash":false}]},{"StartTime":104385.0,"Objects":[{"StartTime":104385.0,"Position":64.0,"HyperDash":false}]},{"StartTime":104535.0,"Objects":[{"StartTime":104535.0,"Position":120.0,"HyperDash":false}]},{"StartTime":104685.0,"Objects":[{"StartTime":104685.0,"Position":104.0,"HyperDash":false}]},{"StartTime":104835.0,"Objects":[{"StartTime":104835.0,"Position":140.0,"HyperDash":false}]},{"StartTime":104985.0,"Objects":[{"StartTime":104985.0,"Position":140.0,"HyperDash":false}]},{"StartTime":105135.0,"Objects":[{"StartTime":105135.0,"Position":120.0,"HyperDash":false},{"StartTime":105209.0,"Position":126.278061,"HyperDash":false},{"StartTime":105284.0,"Position":134.685547,"HyperDash":false},{"StartTime":105359.0,"Position":146.535583,"HyperDash":false},{"StartTime":105434.0,"Position":176.619583,"HyperDash":false},{"StartTime":105509.0,"Position":149.913956,"HyperDash":false},{"StartTime":105584.0,"Position":134.963058,"HyperDash":false},{"StartTime":105659.0,"Position":122.398125,"HyperDash":false},{"StartTime":105734.0,"Position":120.0,"HyperDash":false},{"StartTime":105809.0,"Position":138.336151,"HyperDash":false},{"StartTime":105884.0,"Position":134.8243,"HyperDash":false},{"StartTime":105941.0,"Position":165.701263,"HyperDash":false},{"StartTime":106034.0,"Position":176.619583,"HyperDash":false}]},{"StartTime":106334.0,"Objects":[{"StartTime":106334.0,"Position":248.0,"HyperDash":false},{"StartTime":106408.0,"Position":283.699738,"HyperDash":false},{"StartTime":106483.0,"Position":297.674133,"HyperDash":false},{"StartTime":106558.0,"Position":330.484833,"HyperDash":false},{"StartTime":106633.0,"Position":346.951965,"HyperDash":false},{"StartTime":106708.0,"Position":384.7983,"HyperDash":false},{"StartTime":106783.0,"Position":393.6557,"HyperDash":false},{"StartTime":106840.0,"Position":405.136719,"HyperDash":false},{"StartTime":106933.0,"Position":435.388184,"HyperDash":false}]},{"StartTime":107234.0,"Objects":[{"StartTime":107234.0,"Position":464.0,"HyperDash":false},{"StartTime":107308.0,"Position":456.621124,"HyperDash":false},{"StartTime":107383.0,"Position":471.782776,"HyperDash":false},{"StartTime":107458.0,"Position":457.492584,"HyperDash":false},{"StartTime":107533.0,"Position":461.751678,"HyperDash":false},{"StartTime":107608.0,"Position":448.0888,"HyperDash":false},{"StartTime":107683.0,"Position":429.117279,"HyperDash":false},{"StartTime":107740.0,"Position":423.223846,"HyperDash":false},{"StartTime":107833.0,"Position":382.2534,"HyperDash":false}]},{"StartTime":108134.0,"Objects":[{"StartTime":108134.0,"Position":24.0,"HyperDash":false}]},{"StartTime":108433.0,"Objects":[{"StartTime":108433.0,"Position":88.0,"HyperDash":false}]},{"StartTime":108733.0,"Objects":[{"StartTime":108733.0,"Position":200.0,"HyperDash":false}]},{"StartTime":108883.0,"Objects":[{"StartTime":108883.0,"Position":220.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/37902.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/37902.osu new file mode 100644 index 0000000000..04942acb1e --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/37902.osu @@ -0,0 +1,230 @@ +osu file format v5 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:5 +CircleSize:3 +OverallDifficulty:5 +SliderMultiplier:1 +SliderTickRate:2 + +[Events] +//Break Periods +2,55404,58804 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +2421,299.895036737142,4,1,0,100,1,0 +27079,-100,4,2,0,100,0,0 +27529,-100,4,1,0,100,0,0 +33077,-100,4,2,0,100,0,0 +33527,-100,4,1,0,100,0,0 +39075,-100,4,2,0,100,0,0 +39525,-100,4,1,0,100,0,0 +45073,-100,4,2,0,100,0,0 +53696,-100,4,1,0,100,0,0 +60000,-200,4,1,0,100,0,0 +64799,-100,4,1,0,100,0,0 + +[HitObjects] +48,192,12017,2,0,B|104:312|272:312,1,250,0|2 +320,312,13067,1,0 +392,312,13367,1,2 +464,312,13667,1,0 +464,240,13966,1,4 +464,240,14116,1,4 +464,168,14416,6,0,B|464:80|400:32|272:32,1,250,0|2 +216,32,15466,1,0 +144,32,15766,1,2 +72,32,16066,1,0 +72,104,16366,1,4 +72,208,16815,6,0,B|72:288|152:288|152:208|248:208|248:160|296:160,1,250,0|2 +320,128,17865,1,0 +376,88,18165,1,2 +440,64,18465,1,0 +504,48,18765,1,4 +504,48,18915,1,4 +504,120,19215,6,0,B|504:232|400:232|296:232,1,250,0|2 +248,232,20264,1,0 +248,160,20564,1,2 +176,160,20864,1,0 +176,232,21164,1,4 +72,232,21614,6,0,B|72:88|112:88|200:40,1,250,0|2 +264,32,22663,2,0,B|456:32|456:224,1,300,0|2 +456,280,23863,2,0,B|360:280|336:320|336:352|320:368|168:368,1,300,0|2 +184,296,25063,5,4 +184,152,25662,1,4 +320,152,26262,1,4 +320,296,26862,1,4 +96,296,27612,6,0,B|96:168|144:120|240:120,1,250,0|2 +296,120,28661,2,0,B|296:248|232:328|128:352,1,300,0|2 +72,352,29861,2,0,B|32:240|32:96|112:56,1,300,0|2 +160,64,31060,5,4 +296,64,31660,1,4 +432,64,32260,1,4 +432,200,32860,1,4 +256,192,33610,6,0,B|136:192,2,100 +256,192,34359,1,2 +256,264,34659,1,2 +256,264,34809,2,0,B|384:264,2,100 +256,264,35559,1,2 +256,336,35859,1,2 +256,336,36009,2,0,B|136:336,2,100 +256,336,36758,1,2 +328,336,37058,2,0,B|456:336|456:184,3,200,4|4|4|4 +440,40,39607,5,0 +368,40,39907,1,0 +296,40,40207,1,0 +256,40,40357,2,2,B|112:40,1,100 +88,120,41107,1,0 +160,120,41407,1,0 +192,120,41557,2,2,B|328:120,1,100 +360,192,42307,1,0 +288,192,42606,1,0 +256,192,42756,2,2,B|144:192,1,100,2|4 +158,262,43356,5,0 +158,262,43506,1,0 +158,262,43656,1,4 +230,262,43956,1,0 +230,262,44106,1,0 +230,262,44256,1,4 +302,262,44556,1,0 +302,262,44706,1,0 +302,262,44856,1,4 +256,88,45605,6,2,B|328:88,5,50,2|2|2|2|0|2 +376,88,46655,1,0 +448,88,46955,1,2 +448,160,47255,1,0 +376,160,47555,1,0 +376,160,47705,1,4 +376,232,48005,6,2,B|440:232,2,50 +336,232,48454,1,2 +304,232,48604,1,0 +264,232,48754,1,2 +192,232,49054,1,0 +120,232,49354,1,2 +48,232,49654,1,0 +48,160,49954,1,4 +48,56,50404,6,2,B|112:56,1,50 +136,56,50704,2,2,B|208:56,1,50 +224,56,51003,2,2,B|288:56,1,50,0|2 +344,56,51453,1,0 +416,56,51753,1,2 +488,56,52053,1,0 +488,128,52353,1,0 +488,128,52503,1,4 +488,200,52803,6,2,B|432:200,3,50 +400,200,53403,1,0 +368,200,53553,2,0,B|296:200|280:120,2,100,2|4|4 +368,272,54452,2,4,B|360:368|120:344,1,250,4|4 +256,288,60000,6,0,B|400:288,1,125 +416,288,61050,2,0,B|416:128,1,150 +416,104,62250,2,0,B|240:104,1,150,0|0 +232,104,63449,2,0,B|232:296,1,175,0|4 +160,280,64799,6,0,B|48:280,3,100,0|8|0|8 +56,208,65998,1,0 +56,136,66298,1,8 +56,64,66598,1,0 +128,64,66898,1,8 +256,192,67198,12,0,69597 +256,192,69897,5,8 +288,192,70047,1,0 +320,192,70197,1,0 +320,120,70497,1,8 +320,48,70797,1,0 +248,48,71096,1,8 +208,48,71246,1,0 +176,48,71396,1,0 +104,48,71696,1,8 +104,120,71996,6,0,B|16:120,2,50,0|0|8 +104,120,72446,1,2 +104,192,72746,1,2 +104,264,73046,2,2,B|104:352|264:352|424:352,1,350,2|4 +416,280,74395,6,0,B|416:216|320:216,3,100,0|8|0|8 +280,216,75595,1,0 +208,216,75895,1,8 +208,144,76195,1,0 +208,112,76345,1,0 +208,80,76495,1,8 +256,192,76794,12,0,78893 +256,192,79194,6,2,B|256:104|152:88,1,150,6|0 +128,88,79793,2,2,B|56:72|56:200,1,150,2|0 +56,264,80543,1,2 +56,336,80843,1,0 +128,336,81143,1,2 +200,336,81443,1,4 +200,336,81593,6,2,B|320:336|384:224,2,200,6|2|0 +200,336,82942,1,2 +200,264,83242,1,2 +200,192,83542,1,4 +200,160,83692,1,4 +200,128,83842,1,4 +200,96,83992,6,2,B|200:40|248:24|360:24,1,150,6|0 +344,24,84592,2,2,B|440:24|480:48|480:120,1,150,2|0 +472,144,85341,1,2 +472,216,85641,1,0 +472,288,85941,1,2 +400,288,86241,1,4 +400,288,86391,6,2,B|272:288|296:216|192:216,2,200,6|2|0 +400,288,87741,5,2 +400,216,88041,1,2 +400,144,88340,1,4 +360,144,88490,1,4 +320,144,88640,1,4 +256,192,88940,12,0,89690 +56,192,89990,5,0 +56,192,90140,1,0 +56,192,90290,1,8 +128,192,90590,1,0 +160,192,90740,2,2,B|224:192|248:104,3,100 +264,72,91939,1,4 +264,72,92089,1,4 +336,72,92389,5,0 +368,72,92539,1,0 +400,72,92689,1,8 +432,72,92839,1,0 +464,72,92989,1,0 +496,72,93139,1,2 +496,144,93439,2,2,B|496:256|232:256,1,300,2|4 +160,192,94788,6,0,B|160:136,2,50,0|0|8 +160,224,95238,1,0 +160,256,95388,1,0 +160,288,95538,2,2,B|160:360|238:362,1,100 +296,360,96138,2,2,B|376:360|416:312,1,100 +440,288,96737,1,4 +440,288,96887,1,4 +440,216,97187,6,0,B|440:80|264:80,1,250,0|2 +200,80,98237,1,2 +128,80,98537,1,2 +56,80,98837,1,4 +56,152,99137,6,0,B|136:152,2,50 +56,184,99586,2,0,B|56:264|144:264,3,100,8|8|8|8 +144,264,100636,1,4 +216,264,100936,1,4 +288,264,101236,1,4 +360,264,101536,2,0,B|464:264|496:136,1,200,4|0 +496,72,102435,6,0,B|360:72|320:208,1,200 +304,232,103335,1,2 +280,296,103635,1,0 +224,344,103935,1,4 +120,296,104385,5,8 +120,264,104535,1,0 +120,232,104685,1,8 +120,200,104835,1,0 +120,168,104985,1,8 +120,136,105135,2,4,B|120:64|216:56,3,100,0|4|4|4 +248,48,106334,2,0,B|376:48|416:88|464:128,1,200,4|0 +464,168,107234,2,0,B|488:248|456:312|376:320,1,200,0|4 +200,320,108134,5,4 +56,192,108433,1,4 +200,64,108733,1,4 +200,64,108883,1,4 diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/39206-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/39206-expected-conversion.json new file mode 100644 index 0000000000..35fcd88d4e --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/39206-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":678.0,"Objects":[{"StartTime":678.0,"Position":256.0,"HyperDash":false}]},{"StartTime":1021.0,"Objects":[{"StartTime":1021.0,"Position":456.0,"HyperDash":false}]},{"StartTime":1193.0,"Objects":[{"StartTime":1193.0,"Position":456.0,"HyperDash":false}]},{"StartTime":1364.0,"Objects":[{"StartTime":1364.0,"Position":456.0,"HyperDash":false}]},{"StartTime":1707.0,"Objects":[{"StartTime":1707.0,"Position":312.0,"HyperDash":false}]},{"StartTime":1878.0,"Objects":[{"StartTime":1878.0,"Position":312.0,"HyperDash":false}]},{"StartTime":2050.0,"Objects":[{"StartTime":2050.0,"Position":312.0,"HyperDash":false}]},{"StartTime":2393.0,"Objects":[{"StartTime":2393.0,"Position":168.0,"HyperDash":false}]},{"StartTime":2564.0,"Objects":[{"StartTime":2564.0,"Position":168.0,"HyperDash":false}]},{"StartTime":2736.0,"Objects":[{"StartTime":2736.0,"Position":168.0,"HyperDash":false}]},{"StartTime":3078.0,"Objects":[{"StartTime":3078.0,"Position":24.0,"HyperDash":false}]},{"StartTime":3250.0,"Objects":[{"StartTime":3250.0,"Position":24.0,"HyperDash":false}]},{"StartTime":3421.0,"Objects":[{"StartTime":3421.0,"Position":24.0,"HyperDash":false}]},{"StartTime":3764.0,"Objects":[{"StartTime":3764.0,"Position":56.0,"HyperDash":false}]},{"StartTime":3936.0,"Objects":[{"StartTime":3936.0,"Position":136.0,"HyperDash":false}]},{"StartTime":4107.0,"Objects":[{"StartTime":4107.0,"Position":216.0,"HyperDash":false}]},{"StartTime":4450.0,"Objects":[{"StartTime":4450.0,"Position":296.0,"HyperDash":false}]},{"StartTime":4621.0,"Objects":[{"StartTime":4621.0,"Position":376.0,"HyperDash":false}]},{"StartTime":4793.0,"Objects":[{"StartTime":4793.0,"Position":456.0,"HyperDash":false}]},{"StartTime":5135.0,"Objects":[{"StartTime":5135.0,"Position":456.0,"HyperDash":false}]},{"StartTime":5307.0,"Objects":[{"StartTime":5307.0,"Position":376.0,"HyperDash":false}]},{"StartTime":5478.0,"Objects":[{"StartTime":5478.0,"Position":296.0,"HyperDash":false}]},{"StartTime":5821.0,"Objects":[{"StartTime":5821.0,"Position":216.0,"HyperDash":false}]},{"StartTime":5993.0,"Objects":[{"StartTime":5993.0,"Position":136.0,"HyperDash":false}]},{"StartTime":6164.0,"Objects":[{"StartTime":6164.0,"Position":56.0,"HyperDash":false}]},{"StartTime":6507.0,"Objects":[{"StartTime":6507.0,"Position":24.0,"HyperDash":false},{"StartTime":6583.0,"Position":9.0,"HyperDash":false},{"StartTime":6660.0,"Position":13.0,"HyperDash":false},{"StartTime":6736.0,"Position":21.0,"HyperDash":false},{"StartTime":6849.0,"Position":24.0,"HyperDash":false}]},{"StartTime":7193.0,"Objects":[{"StartTime":7193.0,"Position":144.0,"HyperDash":false},{"StartTime":7269.0,"Position":159.0,"HyperDash":false},{"StartTime":7346.0,"Position":161.0,"HyperDash":false},{"StartTime":7422.0,"Position":145.0,"HyperDash":false},{"StartTime":7535.0,"Position":144.0,"HyperDash":false}]},{"StartTime":7878.0,"Objects":[{"StartTime":7878.0,"Position":256.0,"HyperDash":false},{"StartTime":7954.0,"Position":255.0,"HyperDash":false},{"StartTime":8031.0,"Position":241.0,"HyperDash":false},{"StartTime":8107.0,"Position":248.0,"HyperDash":false},{"StartTime":8220.0,"Position":256.0,"HyperDash":false}]},{"StartTime":8564.0,"Objects":[{"StartTime":8564.0,"Position":376.0,"HyperDash":false},{"StartTime":8640.0,"Position":364.0,"HyperDash":false},{"StartTime":8717.0,"Position":372.0,"HyperDash":false},{"StartTime":8793.0,"Position":390.0,"HyperDash":false},{"StartTime":8906.0,"Position":376.0,"HyperDash":false}]},{"StartTime":9250.0,"Objects":[{"StartTime":9250.0,"Position":488.0,"HyperDash":false},{"StartTime":9326.0,"Position":492.0,"HyperDash":false},{"StartTime":9403.0,"Position":479.0,"HyperDash":false},{"StartTime":9479.0,"Position":493.0,"HyperDash":false},{"StartTime":9592.0,"Position":488.0,"HyperDash":false}]},{"StartTime":9935.0,"Objects":[{"StartTime":9935.0,"Position":17.0,"HyperDash":false},{"StartTime":10004.0,"Position":433.0,"HyperDash":false},{"StartTime":10074.0,"Position":201.0,"HyperDash":false},{"StartTime":10144.0,"Position":244.0,"HyperDash":false},{"StartTime":10213.0,"Position":55.0,"HyperDash":false},{"StartTime":10283.0,"Position":166.0,"HyperDash":false},{"StartTime":10353.0,"Position":332.0,"HyperDash":false},{"StartTime":10422.0,"Position":460.0,"HyperDash":false},{"StartTime":10492.0,"Position":329.0,"HyperDash":false},{"StartTime":10562.0,"Position":156.0,"HyperDash":false},{"StartTime":10631.0,"Position":273.0,"HyperDash":false},{"StartTime":10701.0,"Position":57.0,"HyperDash":false},{"StartTime":10771.0,"Position":199.0,"HyperDash":false},{"StartTime":10840.0,"Position":485.0,"HyperDash":false},{"StartTime":10910.0,"Position":388.0,"HyperDash":false},{"StartTime":10980.0,"Position":470.0,"HyperDash":false},{"StartTime":11050.0,"Position":326.0,"HyperDash":false}]},{"StartTime":11307.0,"Objects":[{"StartTime":11307.0,"Position":40.0,"HyperDash":false}]},{"StartTime":11393.0,"Objects":[{"StartTime":11393.0,"Position":56.0,"HyperDash":false}]},{"StartTime":11478.0,"Objects":[{"StartTime":11478.0,"Position":80.0,"HyperDash":false}]},{"StartTime":11564.0,"Objects":[{"StartTime":11564.0,"Position":104.0,"HyperDash":false}]},{"StartTime":11650.0,"Objects":[{"StartTime":11650.0,"Position":128.0,"HyperDash":false},{"StartTime":11726.0,"Position":139.513672,"HyperDash":false},{"StartTime":11803.0,"Position":178.88179,"HyperDash":false},{"StartTime":11879.0,"Position":208.079636,"HyperDash":false},{"StartTime":11992.0,"Position":226.574265,"HyperDash":false}]},{"StartTime":12336.0,"Objects":[{"StartTime":12336.0,"Position":288.0,"HyperDash":false},{"StartTime":12412.0,"Position":273.486328,"HyperDash":false},{"StartTime":12489.0,"Position":256.118225,"HyperDash":false},{"StartTime":12565.0,"Position":223.920364,"HyperDash":false},{"StartTime":12678.0,"Position":189.425735,"HyperDash":false}]},{"StartTime":13021.0,"Objects":[{"StartTime":13021.0,"Position":344.0,"HyperDash":false},{"StartTime":13097.0,"Position":346.513672,"HyperDash":false},{"StartTime":13174.0,"Position":370.8818,"HyperDash":false},{"StartTime":13250.0,"Position":413.079651,"HyperDash":false},{"StartTime":13363.0,"Position":442.574249,"HyperDash":false}]},{"StartTime":13707.0,"Objects":[{"StartTime":13707.0,"Position":504.0,"HyperDash":false},{"StartTime":13783.0,"Position":490.486328,"HyperDash":false},{"StartTime":13860.0,"Position":453.1182,"HyperDash":false},{"StartTime":13936.0,"Position":440.920349,"HyperDash":false},{"StartTime":14049.0,"Position":405.425751,"HyperDash":false}]},{"StartTime":14221.0,"Objects":[{"StartTime":14221.0,"Position":328.0,"HyperDash":false}]},{"StartTime":14307.0,"Objects":[{"StartTime":14307.0,"Position":312.0,"HyperDash":false}]},{"StartTime":14393.0,"Objects":[{"StartTime":14393.0,"Position":296.0,"HyperDash":false},{"StartTime":14469.0,"Position":285.453,"HyperDash":false},{"StartTime":14546.0,"Position":295.793518,"HyperDash":false},{"StartTime":14622.0,"Position":253.246521,"HyperDash":false},{"StartTime":14735.0,"Position":257.538452,"HyperDash":false}]},{"StartTime":15078.0,"Objects":[{"StartTime":15078.0,"Position":160.0,"HyperDash":false},{"StartTime":15154.0,"Position":179.547012,"HyperDash":false},{"StartTime":15231.0,"Position":158.206482,"HyperDash":false},{"StartTime":15307.0,"Position":192.7535,"HyperDash":false},{"StartTime":15420.0,"Position":198.461548,"HyperDash":false}]},{"StartTime":15764.0,"Objects":[{"StartTime":15764.0,"Position":296.0,"HyperDash":false},{"StartTime":15840.0,"Position":298.453,"HyperDash":false},{"StartTime":15917.0,"Position":269.793518,"HyperDash":false},{"StartTime":15993.0,"Position":263.246521,"HyperDash":false},{"StartTime":16106.0,"Position":257.538452,"HyperDash":false}]},{"StartTime":16450.0,"Objects":[{"StartTime":16450.0,"Position":160.0,"HyperDash":false},{"StartTime":16526.0,"Position":168.547012,"HyperDash":false},{"StartTime":16603.0,"Position":183.206482,"HyperDash":false},{"StartTime":16679.0,"Position":170.7535,"HyperDash":false},{"StartTime":16792.0,"Position":198.461548,"HyperDash":false}]},{"StartTime":16964.0,"Objects":[{"StartTime":16964.0,"Position":112.0,"HyperDash":false}]},{"StartTime":17050.0,"Objects":[{"StartTime":17050.0,"Position":96.0,"HyperDash":false}]},{"StartTime":17136.0,"Objects":[{"StartTime":17136.0,"Position":88.0,"HyperDash":false},{"StartTime":17221.0,"Position":108.141563,"HyperDash":false},{"StartTime":17307.0,"Position":125.724724,"HyperDash":false},{"StartTime":17392.0,"Position":123.658127,"HyperDash":false},{"StartTime":17478.0,"Position":151.393967,"HyperDash":false},{"StartTime":17564.0,"Position":185.463791,"HyperDash":false},{"StartTime":17650.0,"Position":197.255447,"HyperDash":false},{"StartTime":17735.0,"Position":168.730637,"HyperDash":false},{"StartTime":17821.0,"Position":151.639252,"HyperDash":false},{"StartTime":17897.0,"Position":121.04126,"HyperDash":false},{"StartTime":17974.0,"Position":121.285477,"HyperDash":false},{"StartTime":18051.0,"Position":123.615044,"HyperDash":false},{"StartTime":18164.0,"Position":88.0,"HyperDash":false}]},{"StartTime":18507.0,"Objects":[{"StartTime":18507.0,"Position":424.0,"HyperDash":false},{"StartTime":18592.0,"Position":408.858429,"HyperDash":false},{"StartTime":18678.0,"Position":397.275269,"HyperDash":false},{"StartTime":18763.0,"Position":362.3419,"HyperDash":false},{"StartTime":18849.0,"Position":360.606018,"HyperDash":false},{"StartTime":18935.0,"Position":337.536224,"HyperDash":false},{"StartTime":19021.0,"Position":314.744537,"HyperDash":false},{"StartTime":19106.0,"Position":355.269379,"HyperDash":false},{"StartTime":19192.0,"Position":360.360748,"HyperDash":false},{"StartTime":19268.0,"Position":388.95874,"HyperDash":false},{"StartTime":19345.0,"Position":391.7145,"HyperDash":false},{"StartTime":19422.0,"Position":424.384949,"HyperDash":false},{"StartTime":19535.0,"Position":424.0,"HyperDash":false}]},{"StartTime":19707.0,"Objects":[{"StartTime":19707.0,"Position":368.0,"HyperDash":false}]},{"StartTime":19793.0,"Objects":[{"StartTime":19793.0,"Position":352.0,"HyperDash":false}]},{"StartTime":19878.0,"Objects":[{"StartTime":19878.0,"Position":336.0,"HyperDash":false},{"StartTime":19954.0,"Position":304.777771,"HyperDash":false},{"StartTime":20031.0,"Position":306.263153,"HyperDash":false},{"StartTime":20107.0,"Position":256.040924,"HyperDash":false},{"StartTime":20220.0,"Position":236.0,"HyperDash":false}]},{"StartTime":20564.0,"Objects":[{"StartTime":20564.0,"Position":136.0,"HyperDash":false},{"StartTime":20640.0,"Position":147.222229,"HyperDash":false},{"StartTime":20717.0,"Position":184.736847,"HyperDash":false},{"StartTime":20793.0,"Position":190.959076,"HyperDash":false},{"StartTime":20906.0,"Position":236.0,"HyperDash":false}]},{"StartTime":21250.0,"Objects":[{"StartTime":21250.0,"Position":392.0,"HyperDash":false},{"StartTime":21335.0,"Position":420.1406,"HyperDash":false},{"StartTime":21421.0,"Position":400.481,"HyperDash":false},{"StartTime":21506.0,"Position":414.916046,"HyperDash":false},{"StartTime":21592.0,"Position":414.21582,"HyperDash":false},{"StartTime":21660.0,"Position":403.507965,"HyperDash":false},{"StartTime":21764.0,"Position":397.683655,"HyperDash":true}]},{"StartTime":21936.0,"Objects":[{"StartTime":21936.0,"Position":120.0,"HyperDash":false},{"StartTime":22021.0,"Position":99.85941,"HyperDash":false},{"StartTime":22107.0,"Position":90.5190048,"HyperDash":false},{"StartTime":22192.0,"Position":91.0839539,"HyperDash":false},{"StartTime":22278.0,"Position":97.78417,"HyperDash":false},{"StartTime":22346.0,"Position":116.49202,"HyperDash":false},{"StartTime":22450.0,"Position":114.31633,"HyperDash":false}]},{"StartTime":22621.0,"Objects":[{"StartTime":22621.0,"Position":176.0,"HyperDash":false},{"StartTime":22706.0,"Position":203.4664,"HyperDash":false},{"StartTime":22792.0,"Position":212.3834,"HyperDash":false},{"StartTime":22877.0,"Position":234.448669,"HyperDash":false},{"StartTime":22963.0,"Position":266.38324,"HyperDash":false},{"StartTime":23031.0,"Position":276.057281,"HyperDash":false},{"StartTime":23135.0,"Position":297.221375,"HyperDash":false}]},{"StartTime":23307.0,"Objects":[{"StartTime":23307.0,"Position":297.0,"HyperDash":false}]},{"StartTime":23821.0,"Objects":[{"StartTime":23821.0,"Position":448.0,"HyperDash":false}]},{"StartTime":23993.0,"Objects":[{"StartTime":23993.0,"Position":352.0,"HyperDash":false},{"StartTime":24069.0,"Position":334.661774,"HyperDash":false},{"StartTime":24146.0,"Position":291.266022,"HyperDash":false},{"StartTime":24222.0,"Position":288.570435,"HyperDash":false},{"StartTime":24335.0,"Position":255.710861,"HyperDash":false}]},{"StartTime":24507.0,"Objects":[{"StartTime":24507.0,"Position":160.0,"HyperDash":false}]},{"StartTime":24593.0,"Objects":[{"StartTime":24593.0,"Position":160.0,"HyperDash":false}]},{"StartTime":24678.0,"Objects":[{"StartTime":24678.0,"Position":160.0,"HyperDash":false}]},{"StartTime":25021.0,"Objects":[{"StartTime":25021.0,"Position":88.0,"HyperDash":false}]},{"StartTime":25193.0,"Objects":[{"StartTime":25193.0,"Position":176.0,"HyperDash":false}]},{"StartTime":25364.0,"Objects":[{"StartTime":25364.0,"Position":256.0,"HyperDash":false}]},{"StartTime":25707.0,"Objects":[{"StartTime":25707.0,"Position":424.0,"HyperDash":false}]},{"StartTime":25878.0,"Objects":[{"StartTime":25878.0,"Position":448.0,"HyperDash":false}]},{"StartTime":26050.0,"Objects":[{"StartTime":26050.0,"Position":472.0,"HyperDash":false},{"StartTime":26135.0,"Position":467.1815,"HyperDash":false},{"StartTime":26221.0,"Position":430.508972,"HyperDash":false},{"StartTime":26306.0,"Position":426.762726,"HyperDash":false},{"StartTime":26392.0,"Position":386.21756,"HyperDash":false},{"StartTime":26460.0,"Position":355.4007,"HyperDash":false},{"StartTime":26564.0,"Position":336.352875,"HyperDash":false}]},{"StartTime":26736.0,"Objects":[{"StartTime":26736.0,"Position":304.0,"HyperDash":false},{"StartTime":26812.0,"Position":269.465637,"HyperDash":false},{"StartTime":26889.0,"Position":283.52124,"HyperDash":false},{"StartTime":26965.0,"Position":262.29248,"HyperDash":false},{"StartTime":27078.0,"Position":241.4827,"HyperDash":false}]},{"StartTime":27250.0,"Objects":[{"StartTime":27250.0,"Position":508.0,"HyperDash":false},{"StartTime":27303.0,"Position":417.0,"HyperDash":false},{"StartTime":27357.0,"Position":302.0,"HyperDash":false},{"StartTime":27410.0,"Position":132.0,"HyperDash":false},{"StartTime":27464.0,"Position":352.0,"HyperDash":false},{"StartTime":27517.0,"Position":174.0,"HyperDash":false},{"StartTime":27571.0,"Position":453.0,"HyperDash":false},{"StartTime":27624.0,"Position":205.0,"HyperDash":false},{"StartTime":27678.0,"Position":105.0,"HyperDash":false},{"StartTime":27732.0,"Position":213.0,"HyperDash":false},{"StartTime":27785.0,"Position":472.0,"HyperDash":false},{"StartTime":27839.0,"Position":251.0,"HyperDash":false},{"StartTime":27892.0,"Position":208.0,"HyperDash":false},{"StartTime":27946.0,"Position":261.0,"HyperDash":false},{"StartTime":27999.0,"Position":382.0,"HyperDash":false},{"StartTime":28053.0,"Position":170.0,"HyperDash":false},{"StartTime":28107.0,"Position":269.0,"HyperDash":false}]},{"StartTime":28621.0,"Objects":[{"StartTime":28621.0,"Position":32.0,"HyperDash":false}]},{"StartTime":28793.0,"Objects":[{"StartTime":28793.0,"Position":80.0,"HyperDash":false}]},{"StartTime":29307.0,"Objects":[{"StartTime":29307.0,"Position":352.0,"HyperDash":false}]},{"StartTime":29478.0,"Objects":[{"StartTime":29478.0,"Position":424.0,"HyperDash":false}]},{"StartTime":29650.0,"Objects":[{"StartTime":29650.0,"Position":472.0,"HyperDash":false}]},{"StartTime":29821.0,"Objects":[{"StartTime":29821.0,"Position":432.0,"HyperDash":false}]},{"StartTime":29993.0,"Objects":[{"StartTime":29993.0,"Position":360.0,"HyperDash":false}]},{"StartTime":30078.0,"Objects":[{"StartTime":30078.0,"Position":360.0,"HyperDash":false}]},{"StartTime":30164.0,"Objects":[{"StartTime":30164.0,"Position":360.0,"HyperDash":false}]},{"StartTime":30507.0,"Objects":[{"StartTime":30507.0,"Position":184.0,"HyperDash":false},{"StartTime":30592.0,"Position":194.11496,"HyperDash":false},{"StartTime":30678.0,"Position":206.360687,"HyperDash":false},{"StartTime":30745.0,"Position":207.599487,"HyperDash":false},{"StartTime":30849.0,"Position":184.0,"HyperDash":false}]},{"StartTime":31193.0,"Objects":[{"StartTime":31193.0,"Position":64.0,"HyperDash":false},{"StartTime":31278.0,"Position":59.6773758,"HyperDash":false},{"StartTime":31364.0,"Position":91.51566,"HyperDash":false},{"StartTime":31431.0,"Position":76.73467,"HyperDash":false},{"StartTime":31535.0,"Position":64.0,"HyperDash":false}]},{"StartTime":31878.0,"Objects":[{"StartTime":31878.0,"Position":352.0,"HyperDash":false},{"StartTime":31963.0,"Position":310.184479,"HyperDash":false},{"StartTime":32049.0,"Position":302.077,"HyperDash":false},{"StartTime":32116.0,"Position":332.637482,"HyperDash":false},{"StartTime":32220.0,"Position":352.0,"HyperDash":false}]},{"StartTime":32393.0,"Objects":[{"StartTime":32393.0,"Position":320.0,"HyperDash":false},{"StartTime":32435.0,"Position":297.345428,"HyperDash":false},{"StartTime":32478.0,"Position":320.0,"HyperDash":false},{"StartTime":32521.0,"Position":297.345428,"HyperDash":false},{"StartTime":32564.0,"Position":320.0,"HyperDash":false},{"StartTime":32607.0,"Position":297.345428,"HyperDash":false}]},{"StartTime":32736.0,"Objects":[{"StartTime":32736.0,"Position":342.0,"HyperDash":false},{"StartTime":32778.0,"Position":319.345428,"HyperDash":false},{"StartTime":32821.0,"Position":342.0,"HyperDash":false},{"StartTime":32864.0,"Position":319.345428,"HyperDash":false},{"StartTime":32907.0,"Position":342.0,"HyperDash":false},{"StartTime":32950.0,"Position":319.345428,"HyperDash":false}]},{"StartTime":33078.0,"Objects":[{"StartTime":33078.0,"Position":399.0,"HyperDash":false},{"StartTime":33120.0,"Position":376.345428,"HyperDash":false},{"StartTime":33163.0,"Position":399.0,"HyperDash":false},{"StartTime":33206.0,"Position":376.345428,"HyperDash":false},{"StartTime":33249.0,"Position":399.0,"HyperDash":false},{"StartTime":33292.0,"Position":376.345428,"HyperDash":false}]},{"StartTime":33421.0,"Objects":[{"StartTime":33421.0,"Position":422.0,"HyperDash":false},{"StartTime":33463.0,"Position":399.345428,"HyperDash":false},{"StartTime":33506.0,"Position":422.0,"HyperDash":false},{"StartTime":33549.0,"Position":399.345428,"HyperDash":false},{"StartTime":33592.0,"Position":422.0,"HyperDash":false},{"StartTime":33635.0,"Position":399.345428,"HyperDash":false},{"StartTime":33678.0,"Position":422.0,"HyperDash":false}]},{"StartTime":34107.0,"Objects":[{"StartTime":34107.0,"Position":368.0,"HyperDash":false}]},{"StartTime":34278.0,"Objects":[{"StartTime":34278.0,"Position":280.0,"HyperDash":false}]},{"StartTime":34793.0,"Objects":[{"StartTime":34793.0,"Position":280.0,"HyperDash":false}]},{"StartTime":34964.0,"Objects":[{"StartTime":34964.0,"Position":184.0,"HyperDash":false}]},{"StartTime":35136.0,"Objects":[{"StartTime":35136.0,"Position":112.0,"HyperDash":false}]},{"StartTime":35307.0,"Objects":[{"StartTime":35307.0,"Position":64.0,"HyperDash":false}]},{"StartTime":35478.0,"Objects":[{"StartTime":35478.0,"Position":32.0,"HyperDash":false}]},{"StartTime":35564.0,"Objects":[{"StartTime":35564.0,"Position":32.0,"HyperDash":false}]},{"StartTime":35650.0,"Objects":[{"StartTime":35650.0,"Position":32.0,"HyperDash":false}]},{"StartTime":35993.0,"Objects":[{"StartTime":35993.0,"Position":232.0,"HyperDash":false}]},{"StartTime":36164.0,"Objects":[{"StartTime":36164.0,"Position":328.0,"HyperDash":false}]},{"StartTime":36336.0,"Objects":[{"StartTime":36336.0,"Position":408.0,"HyperDash":false}]},{"StartTime":36507.0,"Objects":[{"StartTime":36507.0,"Position":464.0,"HyperDash":false}]},{"StartTime":36678.0,"Objects":[{"StartTime":36678.0,"Position":408.0,"HyperDash":false}]},{"StartTime":36850.0,"Objects":[{"StartTime":36850.0,"Position":328.0,"HyperDash":false}]},{"StartTime":37021.0,"Objects":[{"StartTime":37021.0,"Position":232.0,"HyperDash":false}]},{"StartTime":37535.0,"Objects":[{"StartTime":37535.0,"Position":72.0,"HyperDash":false}]},{"StartTime":37707.0,"Objects":[{"StartTime":37707.0,"Position":112.0,"HyperDash":false}]},{"StartTime":37878.0,"Objects":[{"StartTime":37878.0,"Position":144.0,"HyperDash":false},{"StartTime":37920.0,"Position":119.0,"HyperDash":false},{"StartTime":37963.0,"Position":144.0,"HyperDash":false},{"StartTime":38006.0,"Position":119.0,"HyperDash":false},{"StartTime":38049.0,"Position":144.0,"HyperDash":false}]},{"StartTime":38221.0,"Objects":[{"StartTime":38221.0,"Position":232.0,"HyperDash":false},{"StartTime":38263.0,"Position":207.0,"HyperDash":false},{"StartTime":38306.0,"Position":232.0,"HyperDash":false},{"StartTime":38349.0,"Position":207.0,"HyperDash":false},{"StartTime":38392.0,"Position":232.0,"HyperDash":false}]},{"StartTime":38564.0,"Objects":[{"StartTime":38564.0,"Position":320.0,"HyperDash":false},{"StartTime":38606.0,"Position":295.0,"HyperDash":false},{"StartTime":38649.0,"Position":320.0,"HyperDash":false},{"StartTime":38692.0,"Position":295.0,"HyperDash":false},{"StartTime":38735.0,"Position":320.0,"HyperDash":false}]},{"StartTime":38907.0,"Objects":[{"StartTime":38907.0,"Position":408.0,"HyperDash":false},{"StartTime":38949.0,"Position":383.0,"HyperDash":false},{"StartTime":38992.0,"Position":408.0,"HyperDash":false},{"StartTime":39035.0,"Position":383.0,"HyperDash":false},{"StartTime":39078.0,"Position":408.0,"HyperDash":false}]},{"StartTime":39593.0,"Objects":[{"StartTime":39593.0,"Position":304.0,"HyperDash":false}]},{"StartTime":39764.0,"Objects":[{"StartTime":39764.0,"Position":208.0,"HyperDash":false}]},{"StartTime":40278.0,"Objects":[{"StartTime":40278.0,"Position":40.0,"HyperDash":false}]},{"StartTime":40450.0,"Objects":[{"StartTime":40450.0,"Position":112.0,"HyperDash":false}]},{"StartTime":40621.0,"Objects":[{"StartTime":40621.0,"Position":200.0,"HyperDash":false}]},{"StartTime":40793.0,"Objects":[{"StartTime":40793.0,"Position":264.0,"HyperDash":false}]},{"StartTime":40964.0,"Objects":[{"StartTime":40964.0,"Position":352.0,"HyperDash":false}]},{"StartTime":41050.0,"Objects":[{"StartTime":41050.0,"Position":352.0,"HyperDash":false}]},{"StartTime":41135.0,"Objects":[{"StartTime":41135.0,"Position":352.0,"HyperDash":false}]},{"StartTime":41478.0,"Objects":[{"StartTime":41478.0,"Position":480.0,"HyperDash":false}]},{"StartTime":41650.0,"Objects":[{"StartTime":41650.0,"Position":422.0,"HyperDash":false}]},{"StartTime":41821.0,"Objects":[{"StartTime":41821.0,"Position":364.0,"HyperDash":false}]},{"StartTime":41993.0,"Objects":[{"StartTime":41993.0,"Position":422.0,"HyperDash":false}]},{"StartTime":42164.0,"Objects":[{"StartTime":42164.0,"Position":327.0,"HyperDash":false}]},{"StartTime":42335.0,"Objects":[{"StartTime":42335.0,"Position":226.0,"HyperDash":false}]},{"StartTime":42507.0,"Objects":[{"StartTime":42507.0,"Position":327.0,"HyperDash":false}]},{"StartTime":42678.0,"Objects":[{"StartTime":42678.0,"Position":381.0,"HyperDash":false}]},{"StartTime":42850.0,"Objects":[{"StartTime":42850.0,"Position":437.0,"HyperDash":false}]},{"StartTime":43021.0,"Objects":[{"StartTime":43021.0,"Position":381.0,"HyperDash":false}]},{"StartTime":43193.0,"Objects":[{"StartTime":43193.0,"Position":327.0,"HyperDash":false}]},{"StartTime":43278.0,"Objects":[{"StartTime":43278.0,"Position":16.0,"HyperDash":false},{"StartTime":43374.0,"Position":248.0,"HyperDash":false},{"StartTime":43471.0,"Position":100.0,"HyperDash":false},{"StartTime":43567.0,"Position":24.0,"HyperDash":false},{"StartTime":43664.0,"Position":66.0,"HyperDash":false},{"StartTime":43760.0,"Position":97.0,"HyperDash":false},{"StartTime":43857.0,"Position":267.0,"HyperDash":false},{"StartTime":43953.0,"Position":116.0,"HyperDash":false},{"StartTime":44050.0,"Position":451.0,"HyperDash":false}]},{"StartTime":44221.0,"Objects":[{"StartTime":44221.0,"Position":328.0,"HyperDash":false},{"StartTime":44297.0,"Position":357.352631,"HyperDash":false},{"StartTime":44374.0,"Position":374.9336,"HyperDash":false},{"StartTime":44450.0,"Position":395.286255,"HyperDash":false},{"StartTime":44563.0,"Position":406.086884,"HyperDash":false}]},{"StartTime":44907.0,"Objects":[{"StartTime":44907.0,"Position":184.0,"HyperDash":false},{"StartTime":44983.0,"Position":158.647354,"HyperDash":false},{"StartTime":45060.0,"Position":135.0664,"HyperDash":false},{"StartTime":45136.0,"Position":127.713745,"HyperDash":false},{"StartTime":45249.0,"Position":105.913116,"HyperDash":false}]},{"StartTime":45421.0,"Objects":[{"StartTime":45421.0,"Position":192.0,"HyperDash":false}]},{"StartTime":45507.0,"Objects":[{"StartTime":45507.0,"Position":192.0,"HyperDash":false}]},{"StartTime":45593.0,"Objects":[{"StartTime":45593.0,"Position":192.0,"HyperDash":false}]},{"StartTime":45764.0,"Objects":[{"StartTime":45764.0,"Position":106.0,"HyperDash":false}]},{"StartTime":45850.0,"Objects":[{"StartTime":45850.0,"Position":106.0,"HyperDash":false}]},{"StartTime":45935.0,"Objects":[{"StartTime":45935.0,"Position":106.0,"HyperDash":false}]},{"StartTime":46107.0,"Objects":[{"StartTime":46107.0,"Position":154.0,"HyperDash":false}]},{"StartTime":46278.0,"Objects":[{"StartTime":46278.0,"Position":237.0,"HyperDash":false}]},{"StartTime":46364.0,"Objects":[{"StartTime":46364.0,"Position":237.0,"HyperDash":false}]},{"StartTime":46450.0,"Objects":[{"StartTime":46450.0,"Position":237.0,"HyperDash":false}]},{"StartTime":46535.0,"Objects":[{"StartTime":46535.0,"Position":237.0,"HyperDash":false}]},{"StartTime":46621.0,"Objects":[{"StartTime":46621.0,"Position":237.0,"HyperDash":false}]},{"StartTime":46964.0,"Objects":[{"StartTime":46964.0,"Position":410.0,"HyperDash":false}]},{"StartTime":47135.0,"Objects":[{"StartTime":47135.0,"Position":410.0,"HyperDash":false}]},{"StartTime":47307.0,"Objects":[{"StartTime":47307.0,"Position":462.0,"HyperDash":false}]},{"StartTime":47478.0,"Objects":[{"StartTime":47478.0,"Position":462.0,"HyperDash":false}]},{"StartTime":47650.0,"Objects":[{"StartTime":47650.0,"Position":379.0,"HyperDash":false}]},{"StartTime":47821.0,"Objects":[{"StartTime":47821.0,"Position":379.0,"HyperDash":false}]},{"StartTime":47993.0,"Objects":[{"StartTime":47993.0,"Position":328.0,"HyperDash":false}]},{"StartTime":48164.0,"Objects":[{"StartTime":48164.0,"Position":328.0,"HyperDash":false}]},{"StartTime":48335.0,"Objects":[{"StartTime":48335.0,"Position":237.0,"HyperDash":false}]},{"StartTime":48507.0,"Objects":[{"StartTime":48507.0,"Position":328.0,"HyperDash":false}]},{"StartTime":48678.0,"Objects":[{"StartTime":48678.0,"Position":410.0,"HyperDash":false}]},{"StartTime":48935.0,"Objects":[{"StartTime":48935.0,"Position":264.0,"HyperDash":false}]},{"StartTime":49021.0,"Objects":[{"StartTime":49021.0,"Position":264.0,"HyperDash":false}]},{"StartTime":49193.0,"Objects":[{"StartTime":49193.0,"Position":304.0,"HyperDash":false}]},{"StartTime":49364.0,"Objects":[{"StartTime":49364.0,"Position":368.0,"HyperDash":false}]},{"StartTime":49707.0,"Objects":[{"StartTime":49707.0,"Position":368.0,"HyperDash":false},{"StartTime":49783.0,"Position":403.222229,"HyperDash":false},{"StartTime":49860.0,"Position":411.736847,"HyperDash":false},{"StartTime":49936.0,"Position":434.959076,"HyperDash":false},{"StartTime":50049.0,"Position":468.0,"HyperDash":false}]},{"StartTime":50393.0,"Objects":[{"StartTime":50393.0,"Position":280.0,"HyperDash":false},{"StartTime":50469.0,"Position":295.222229,"HyperDash":false},{"StartTime":50546.0,"Position":340.736847,"HyperDash":false},{"StartTime":50622.0,"Position":355.959076,"HyperDash":false},{"StartTime":50735.0,"Position":380.0,"HyperDash":false}]},{"StartTime":51250.0,"Objects":[{"StartTime":51250.0,"Position":88.0,"HyperDash":false},{"StartTime":51326.0,"Position":103.222221,"HyperDash":false},{"StartTime":51403.0,"Position":148.736847,"HyperDash":false},{"StartTime":51479.0,"Position":138.959076,"HyperDash":false},{"StartTime":51592.0,"Position":188.0,"HyperDash":false}]},{"StartTime":51764.0,"Objects":[{"StartTime":51764.0,"Position":264.0,"HyperDash":false}]},{"StartTime":51850.0,"Objects":[{"StartTime":51850.0,"Position":280.0,"HyperDash":false}]},{"StartTime":51935.0,"Objects":[{"StartTime":51935.0,"Position":296.0,"HyperDash":false}]},{"StartTime":52021.0,"Objects":[{"StartTime":52021.0,"Position":312.0,"HyperDash":false}]},{"StartTime":52107.0,"Objects":[{"StartTime":52107.0,"Position":328.0,"HyperDash":false}]},{"StartTime":52450.0,"Objects":[{"StartTime":52450.0,"Position":208.0,"HyperDash":false}]},{"StartTime":52621.0,"Objects":[{"StartTime":52621.0,"Position":304.0,"HyperDash":false}]},{"StartTime":52793.0,"Objects":[{"StartTime":52793.0,"Position":256.0,"HyperDash":false}]},{"StartTime":53135.0,"Objects":[{"StartTime":53135.0,"Position":208.0,"HyperDash":false}]},{"StartTime":53307.0,"Objects":[{"StartTime":53307.0,"Position":304.0,"HyperDash":false}]},{"StartTime":53478.0,"Objects":[{"StartTime":53478.0,"Position":208.0,"HyperDash":false}]},{"StartTime":53650.0,"Objects":[{"StartTime":53650.0,"Position":304.0,"HyperDash":false}]},{"StartTime":53821.0,"Objects":[{"StartTime":53821.0,"Position":208.0,"HyperDash":false}]},{"StartTime":53993.0,"Objects":[{"StartTime":53993.0,"Position":304.0,"HyperDash":false}]},{"StartTime":54164.0,"Objects":[{"StartTime":54164.0,"Position":247.0,"HyperDash":false},{"StartTime":54217.0,"Position":162.0,"HyperDash":false},{"StartTime":54271.0,"Position":383.0,"HyperDash":false},{"StartTime":54324.0,"Position":127.0,"HyperDash":false},{"StartTime":54378.0,"Position":161.0,"HyperDash":false},{"StartTime":54431.0,"Position":332.0,"HyperDash":false},{"StartTime":54485.0,"Position":356.0,"HyperDash":false},{"StartTime":54538.0,"Position":362.0,"HyperDash":false},{"StartTime":54592.0,"Position":347.0,"HyperDash":false},{"StartTime":54646.0,"Position":252.0,"HyperDash":false},{"StartTime":54699.0,"Position":477.0,"HyperDash":false},{"StartTime":54753.0,"Position":358.0,"HyperDash":false},{"StartTime":54806.0,"Position":17.0,"HyperDash":false},{"StartTime":54860.0,"Position":399.0,"HyperDash":false},{"StartTime":54913.0,"Position":280.0,"HyperDash":false},{"StartTime":54967.0,"Position":304.0,"HyperDash":false},{"StartTime":55021.0,"Position":221.0,"HyperDash":false}]},{"StartTime":55193.0,"Objects":[{"StartTime":55193.0,"Position":256.0,"HyperDash":false},{"StartTime":55269.0,"Position":251.286514,"HyperDash":false},{"StartTime":55346.0,"Position":219.366272,"HyperDash":false},{"StartTime":55422.0,"Position":195.652786,"HyperDash":false},{"StartTime":55535.0,"Position":185.289337,"HyperDash":false}]},{"StartTime":55878.0,"Objects":[{"StartTime":55878.0,"Position":256.0,"HyperDash":false},{"StartTime":55954.0,"Position":280.71347,"HyperDash":false},{"StartTime":56031.0,"Position":289.633728,"HyperDash":false},{"StartTime":56107.0,"Position":299.3472,"HyperDash":false},{"StartTime":56220.0,"Position":326.710663,"HyperDash":false}]},{"StartTime":56393.0,"Objects":[{"StartTime":56393.0,"Position":256.0,"HyperDash":false}]},{"StartTime":56564.0,"Objects":[{"StartTime":56564.0,"Position":160.0,"HyperDash":false}]},{"StartTime":56650.0,"Objects":[{"StartTime":56650.0,"Position":160.0,"HyperDash":false}]},{"StartTime":56735.0,"Objects":[{"StartTime":56735.0,"Position":160.0,"HyperDash":false}]},{"StartTime":56907.0,"Objects":[{"StartTime":56907.0,"Position":160.0,"HyperDash":false}]},{"StartTime":57078.0,"Objects":[{"StartTime":57078.0,"Position":256.0,"HyperDash":false}]},{"StartTime":57250.0,"Objects":[{"StartTime":57250.0,"Position":352.0,"HyperDash":false}]},{"StartTime":57335.0,"Objects":[{"StartTime":57335.0,"Position":360.0,"HyperDash":false}]},{"StartTime":57421.0,"Objects":[{"StartTime":57421.0,"Position":368.0,"HyperDash":false}]},{"StartTime":57507.0,"Objects":[{"StartTime":57507.0,"Position":376.0,"HyperDash":false}]},{"StartTime":57593.0,"Objects":[{"StartTime":57593.0,"Position":384.0,"HyperDash":false}]},{"StartTime":57935.0,"Objects":[{"StartTime":57935.0,"Position":472.0,"HyperDash":false}]},{"StartTime":58107.0,"Objects":[{"StartTime":58107.0,"Position":387.0,"HyperDash":false}]},{"StartTime":58278.0,"Objects":[{"StartTime":58278.0,"Position":284.0,"HyperDash":false}]},{"StartTime":58450.0,"Objects":[{"StartTime":58450.0,"Position":193.0,"HyperDash":false}]},{"StartTime":58621.0,"Objects":[{"StartTime":58621.0,"Position":139.0,"HyperDash":false}]},{"StartTime":58793.0,"Objects":[{"StartTime":58793.0,"Position":132.0,"HyperDash":false}]},{"StartTime":58964.0,"Objects":[{"StartTime":58964.0,"Position":174.0,"HyperDash":false}]},{"StartTime":59307.0,"Objects":[{"StartTime":59307.0,"Position":256.0,"HyperDash":false}]},{"StartTime":59478.0,"Objects":[{"StartTime":59478.0,"Position":208.0,"HyperDash":false}]},{"StartTime":59650.0,"Objects":[{"StartTime":59650.0,"Position":304.0,"HyperDash":false}]},{"StartTime":59821.0,"Objects":[{"StartTime":59821.0,"Position":344.0,"HyperDash":false}]},{"StartTime":59907.0,"Objects":[{"StartTime":59907.0,"Position":312.0,"HyperDash":false}]},{"StartTime":59993.0,"Objects":[{"StartTime":59993.0,"Position":280.0,"HyperDash":false}]},{"StartTime":60164.0,"Objects":[{"StartTime":60164.0,"Position":208.0,"HyperDash":false}]},{"StartTime":60335.0,"Objects":[{"StartTime":60335.0,"Position":304.0,"HyperDash":false}]},{"StartTime":60678.0,"Objects":[{"StartTime":60678.0,"Position":200.0,"HyperDash":false},{"StartTime":60754.0,"Position":175.647354,"HyperDash":false},{"StartTime":60831.0,"Position":176.0664,"HyperDash":false},{"StartTime":60907.0,"Position":137.713745,"HyperDash":false},{"StartTime":61020.0,"Position":121.913116,"HyperDash":false}]},{"StartTime":61364.0,"Objects":[{"StartTime":61364.0,"Position":312.0,"HyperDash":false},{"StartTime":61440.0,"Position":348.352631,"HyperDash":false},{"StartTime":61517.0,"Position":333.9336,"HyperDash":false},{"StartTime":61593.0,"Position":362.286255,"HyperDash":false},{"StartTime":61706.0,"Position":390.086884,"HyperDash":false}]},{"StartTime":62050.0,"Objects":[{"StartTime":62050.0,"Position":390.0,"HyperDash":false}]},{"StartTime":62393.0,"Objects":[{"StartTime":62393.0,"Position":121.0,"HyperDash":false}]},{"StartTime":62735.0,"Objects":[{"StartTime":62735.0,"Position":256.0,"HyperDash":false}]},{"StartTime":62821.0,"Objects":[{"StartTime":62821.0,"Position":256.0,"HyperDash":false}]},{"StartTime":62907.0,"Objects":[{"StartTime":62907.0,"Position":256.0,"HyperDash":false}]},{"StartTime":62993.0,"Objects":[{"StartTime":62993.0,"Position":256.0,"HyperDash":false}]},{"StartTime":63078.0,"Objects":[{"StartTime":63078.0,"Position":256.0,"HyperDash":false}]},{"StartTime":63421.0,"Objects":[{"StartTime":63421.0,"Position":432.0,"HyperDash":false}]},{"StartTime":63593.0,"Objects":[{"StartTime":63593.0,"Position":496.0,"HyperDash":false}]},{"StartTime":63764.0,"Objects":[{"StartTime":63764.0,"Position":496.0,"HyperDash":false}]},{"StartTime":63935.0,"Objects":[{"StartTime":63935.0,"Position":440.0,"HyperDash":false}]},{"StartTime":64107.0,"Objects":[{"StartTime":64107.0,"Position":352.0,"HyperDash":false}]},{"StartTime":64278.0,"Objects":[{"StartTime":64278.0,"Position":256.0,"HyperDash":false}]},{"StartTime":64450.0,"Objects":[{"StartTime":64450.0,"Position":160.0,"HyperDash":false}]},{"StartTime":64621.0,"Objects":[{"StartTime":64621.0,"Position":72.0,"HyperDash":false}]},{"StartTime":64793.0,"Objects":[{"StartTime":64793.0,"Position":8.0,"HyperDash":false}]},{"StartTime":64964.0,"Objects":[{"StartTime":64964.0,"Position":8.0,"HyperDash":false}]},{"StartTime":65135.0,"Objects":[{"StartTime":65135.0,"Position":56.0,"HyperDash":false}]},{"StartTime":65221.0,"Objects":[{"StartTime":65221.0,"Position":437.0,"HyperDash":false},{"StartTime":65317.0,"Position":289.0,"HyperDash":false},{"StartTime":65414.0,"Position":464.0,"HyperDash":false},{"StartTime":65510.0,"Position":36.0,"HyperDash":false},{"StartTime":65607.0,"Position":378.0,"HyperDash":false},{"StartTime":65703.0,"Position":297.0,"HyperDash":false},{"StartTime":65800.0,"Position":418.0,"HyperDash":false},{"StartTime":65896.0,"Position":329.0,"HyperDash":false},{"StartTime":65993.0,"Position":338.0,"HyperDash":false}]},{"StartTime":66164.0,"Objects":[{"StartTime":66164.0,"Position":296.0,"HyperDash":false},{"StartTime":66240.0,"Position":317.930573,"HyperDash":false},{"StartTime":66317.0,"Position":317.018127,"HyperDash":false},{"StartTime":66393.0,"Position":314.9487,"HyperDash":false},{"StartTime":66506.0,"Position":349.687561,"HyperDash":false}]},{"StartTime":66850.0,"Objects":[{"StartTime":66850.0,"Position":216.0,"HyperDash":false},{"StartTime":66926.0,"Position":184.069427,"HyperDash":false},{"StartTime":67003.0,"Position":174.981888,"HyperDash":false},{"StartTime":67079.0,"Position":196.051315,"HyperDash":false},{"StartTime":67192.0,"Position":162.312454,"HyperDash":false}]},{"StartTime":67535.0,"Objects":[{"StartTime":67535.0,"Position":296.0,"HyperDash":false},{"StartTime":67611.0,"Position":288.930573,"HyperDash":false},{"StartTime":67688.0,"Position":339.018127,"HyperDash":false},{"StartTime":67764.0,"Position":312.9487,"HyperDash":false},{"StartTime":67877.0,"Position":349.687561,"HyperDash":false}]},{"StartTime":67964.0,"Objects":[{"StartTime":67964.0,"Position":267.0,"HyperDash":false},{"StartTime":68060.0,"Position":477.0,"HyperDash":false},{"StartTime":68156.0,"Position":282.0,"HyperDash":false},{"StartTime":68253.0,"Position":216.0,"HyperDash":false},{"StartTime":68349.0,"Position":106.0,"HyperDash":false},{"StartTime":68445.0,"Position":353.0,"HyperDash":false},{"StartTime":68542.0,"Position":162.0,"HyperDash":false},{"StartTime":68638.0,"Position":473.0,"HyperDash":false},{"StartTime":68735.0,"Position":260.0,"HyperDash":false}]},{"StartTime":68907.0,"Objects":[{"StartTime":68907.0,"Position":304.0,"HyperDash":false},{"StartTime":68983.0,"Position":334.222229,"HyperDash":false},{"StartTime":69060.0,"Position":328.736847,"HyperDash":false},{"StartTime":69136.0,"Position":355.959076,"HyperDash":false},{"StartTime":69249.0,"Position":404.0,"HyperDash":false}]},{"StartTime":69593.0,"Objects":[{"StartTime":69593.0,"Position":208.0,"HyperDash":false},{"StartTime":69669.0,"Position":188.777771,"HyperDash":false},{"StartTime":69746.0,"Position":175.263153,"HyperDash":false},{"StartTime":69822.0,"Position":151.040924,"HyperDash":false},{"StartTime":69935.0,"Position":108.0,"HyperDash":false}]},{"StartTime":70278.0,"Objects":[{"StartTime":70278.0,"Position":304.0,"HyperDash":false},{"StartTime":70354.0,"Position":332.222229,"HyperDash":false},{"StartTime":70431.0,"Position":343.736847,"HyperDash":false},{"StartTime":70507.0,"Position":361.959076,"HyperDash":false},{"StartTime":70620.0,"Position":404.0,"HyperDash":false}]},{"StartTime":71307.0,"Objects":[{"StartTime":71307.0,"Position":56.0,"HyperDash":false},{"StartTime":71392.0,"Position":57.8317223,"HyperDash":false},{"StartTime":71478.0,"Position":43.0449677,"HyperDash":false},{"StartTime":71563.0,"Position":62.4877777,"HyperDash":false},{"StartTime":71649.0,"Position":54.65224,"HyperDash":false},{"StartTime":71725.0,"Position":50.5213776,"HyperDash":false},{"StartTime":71802.0,"Position":33.41652,"HyperDash":false},{"StartTime":71879.0,"Position":58.5728569,"HyperDash":false},{"StartTime":71992.0,"Position":56.0,"HyperDash":false}]},{"StartTime":72335.0,"Objects":[{"StartTime":72335.0,"Position":256.0,"HyperDash":false}]},{"StartTime":72507.0,"Objects":[{"StartTime":72507.0,"Position":328.0,"HyperDash":false}]},{"StartTime":72678.0,"Objects":[{"StartTime":72678.0,"Position":400.0,"HyperDash":false}]},{"StartTime":73021.0,"Objects":[{"StartTime":73021.0,"Position":400.0,"HyperDash":false},{"StartTime":73097.0,"Position":415.600647,"HyperDash":false},{"StartTime":73174.0,"Position":409.291138,"HyperDash":false},{"StartTime":73250.0,"Position":437.285278,"HyperDash":false},{"StartTime":73363.0,"Position":410.36676,"HyperDash":false}]},{"StartTime":73707.0,"Objects":[{"StartTime":73707.0,"Position":112.0,"HyperDash":false},{"StartTime":73783.0,"Position":117.399353,"HyperDash":false},{"StartTime":73860.0,"Position":79.7088547,"HyperDash":false},{"StartTime":73936.0,"Position":102.714714,"HyperDash":false},{"StartTime":74049.0,"Position":101.633247,"HyperDash":false}]},{"StartTime":74393.0,"Objects":[{"StartTime":74393.0,"Position":304.0,"HyperDash":false},{"StartTime":74469.0,"Position":301.197144,"HyperDash":false},{"StartTime":74546.0,"Position":342.5416,"HyperDash":false},{"StartTime":74622.0,"Position":349.738739,"HyperDash":false},{"StartTime":74735.0,"Position":354.387115,"HyperDash":false}]},{"StartTime":75078.0,"Objects":[{"StartTime":75078.0,"Position":304.0,"HyperDash":false},{"StartTime":75154.0,"Position":303.197144,"HyperDash":false},{"StartTime":75231.0,"Position":337.5416,"HyperDash":false},{"StartTime":75307.0,"Position":353.738739,"HyperDash":false},{"StartTime":75420.0,"Position":354.387115,"HyperDash":false}]},{"StartTime":75764.0,"Objects":[{"StartTime":75764.0,"Position":464.0,"HyperDash":false}]},{"StartTime":75935.0,"Objects":[{"StartTime":75935.0,"Position":384.0,"HyperDash":false}]},{"StartTime":76107.0,"Objects":[{"StartTime":76107.0,"Position":304.0,"HyperDash":false}]},{"StartTime":76278.0,"Objects":[{"StartTime":76278.0,"Position":232.0,"HyperDash":false}]},{"StartTime":76450.0,"Objects":[{"StartTime":76450.0,"Position":160.0,"HyperDash":false},{"StartTime":76535.0,"Position":135.0,"HyperDash":false},{"StartTime":76621.0,"Position":160.0,"HyperDash":false}]},{"StartTime":76793.0,"Objects":[{"StartTime":76793.0,"Position":80.0,"HyperDash":false}]},{"StartTime":77135.0,"Objects":[{"StartTime":77135.0,"Position":120.0,"HyperDash":false},{"StartTime":77211.0,"Position":119.7057,"HyperDash":false},{"StartTime":77288.0,"Position":91.15754,"HyperDash":false},{"StartTime":77364.0,"Position":60.8632431,"HyperDash":false},{"StartTime":77477.0,"Position":33.1756821,"HyperDash":false}]},{"StartTime":77821.0,"Objects":[{"StartTime":77821.0,"Position":232.0,"HyperDash":false},{"StartTime":77897.0,"Position":234.148621,"HyperDash":false},{"StartTime":77974.0,"Position":272.5492,"HyperDash":false},{"StartTime":78050.0,"Position":284.6978,"HyperDash":false},{"StartTime":78163.0,"Position":318.1688,"HyperDash":false}]},{"StartTime":78507.0,"Objects":[{"StartTime":78507.0,"Position":176.0,"HyperDash":false},{"StartTime":78583.0,"Position":142.7057,"HyperDash":false},{"StartTime":78660.0,"Position":129.157532,"HyperDash":false},{"StartTime":78736.0,"Position":124.863243,"HyperDash":false},{"StartTime":78849.0,"Position":89.17568,"HyperDash":false}]},{"StartTime":79193.0,"Objects":[{"StartTime":79193.0,"Position":288.0,"HyperDash":false},{"StartTime":79269.0,"Position":321.241455,"HyperDash":false},{"StartTime":79346.0,"Position":319.736084,"HyperDash":false},{"StartTime":79422.0,"Position":360.9775,"HyperDash":false},{"StartTime":79535.0,"Position":374.586517,"HyperDash":false}]},{"StartTime":79878.0,"Objects":[{"StartTime":79878.0,"Position":240.0,"HyperDash":false},{"StartTime":79954.0,"Position":209.7057,"HyperDash":false},{"StartTime":80031.0,"Position":189.157532,"HyperDash":false},{"StartTime":80107.0,"Position":196.863251,"HyperDash":false},{"StartTime":80220.0,"Position":153.17569,"HyperDash":false}]},{"StartTime":80564.0,"Objects":[{"StartTime":80564.0,"Position":32.0,"HyperDash":false}]},{"StartTime":80735.0,"Objects":[{"StartTime":80735.0,"Position":66.0,"HyperDash":false}]},{"StartTime":80907.0,"Objects":[{"StartTime":80907.0,"Position":161.0,"HyperDash":false}]},{"StartTime":81078.0,"Objects":[{"StartTime":81078.0,"Position":190.0,"HyperDash":false}]},{"StartTime":81250.0,"Objects":[{"StartTime":81250.0,"Position":285.0,"HyperDash":false}]},{"StartTime":81593.0,"Objects":[{"StartTime":81593.0,"Position":384.0,"HyperDash":false},{"StartTime":81652.0,"Position":401.608948,"HyperDash":false},{"StartTime":81712.0,"Position":403.3638,"HyperDash":false},{"StartTime":81772.0,"Position":419.118683,"HyperDash":false},{"StartTime":81832.0,"Position":416.873535,"HyperDash":false},{"StartTime":81891.0,"Position":412.482483,"HyperDash":false},{"StartTime":81951.0,"Position":417.237366,"HyperDash":false},{"StartTime":82011.0,"Position":431.992218,"HyperDash":false},{"StartTime":82107.0,"Position":459.0,"HyperDash":false}]},{"StartTime":82278.0,"Objects":[{"StartTime":82278.0,"Position":440.0,"HyperDash":false},{"StartTime":82345.0,"Position":418.133057,"HyperDash":false},{"StartTime":82449.0,"Position":412.264984,"HyperDash":false}]},{"StartTime":82621.0,"Objects":[{"StartTime":82621.0,"Position":320.0,"HyperDash":false},{"StartTime":82688.0,"Position":303.133057,"HyperDash":false},{"StartTime":82792.0,"Position":292.264984,"HyperDash":false}]},{"StartTime":82964.0,"Objects":[{"StartTime":82964.0,"Position":200.0,"HyperDash":false},{"StartTime":83031.0,"Position":187.133057,"HyperDash":false},{"StartTime":83135.0,"Position":172.264984,"HyperDash":false}]},{"StartTime":83307.0,"Objects":[{"StartTime":83307.0,"Position":248.0,"HyperDash":false}]},{"StartTime":83478.0,"Objects":[{"StartTime":83478.0,"Position":344.0,"HyperDash":false}]},{"StartTime":83650.0,"Objects":[{"StartTime":83650.0,"Position":448.0,"HyperDash":false}]},{"StartTime":83993.0,"Objects":[{"StartTime":83993.0,"Position":400.0,"HyperDash":false},{"StartTime":84060.0,"Position":372.409363,"HyperDash":false},{"StartTime":84164.0,"Position":350.0,"HyperDash":false}]},{"StartTime":84335.0,"Objects":[{"StartTime":84335.0,"Position":400.0,"HyperDash":false},{"StartTime":84402.0,"Position":438.590637,"HyperDash":false},{"StartTime":84506.0,"Position":450.0,"HyperDash":false}]},{"StartTime":84678.0,"Objects":[{"StartTime":84678.0,"Position":408.0,"HyperDash":false}]},{"StartTime":84850.0,"Objects":[{"StartTime":84850.0,"Position":304.0,"HyperDash":false}]},{"StartTime":85021.0,"Objects":[{"StartTime":85021.0,"Position":208.0,"HyperDash":false}]},{"StartTime":85364.0,"Objects":[{"StartTime":85364.0,"Position":208.0,"HyperDash":false},{"StartTime":85440.0,"Position":179.777771,"HyperDash":false},{"StartTime":85517.0,"Position":160.263153,"HyperDash":false},{"StartTime":85593.0,"Position":156.040924,"HyperDash":false},{"StartTime":85706.0,"Position":108.0,"HyperDash":false}]},{"StartTime":86050.0,"Objects":[{"StartTime":86050.0,"Position":304.0,"HyperDash":false},{"StartTime":86126.0,"Position":332.222229,"HyperDash":false},{"StartTime":86203.0,"Position":364.736847,"HyperDash":false},{"StartTime":86279.0,"Position":385.959076,"HyperDash":false},{"StartTime":86392.0,"Position":404.0,"HyperDash":false}]},{"StartTime":86735.0,"Objects":[{"StartTime":86735.0,"Position":480.0,"HyperDash":false}]},{"StartTime":86907.0,"Objects":[{"StartTime":86907.0,"Position":376.0,"HyperDash":false}]},{"StartTime":87078.0,"Objects":[{"StartTime":87078.0,"Position":272.0,"HyperDash":false}]},{"StartTime":87250.0,"Objects":[{"StartTime":87250.0,"Position":168.0,"HyperDash":false}]},{"StartTime":87421.0,"Objects":[{"StartTime":87421.0,"Position":64.0,"HyperDash":false},{"StartTime":87506.0,"Position":39.0,"HyperDash":false},{"StartTime":87592.0,"Position":64.0,"HyperDash":false}]},{"StartTime":87764.0,"Objects":[{"StartTime":87764.0,"Position":64.0,"HyperDash":false}]},{"StartTime":88107.0,"Objects":[{"StartTime":88107.0,"Position":208.0,"HyperDash":false},{"StartTime":88183.0,"Position":190.802856,"HyperDash":false},{"StartTime":88260.0,"Position":183.4584,"HyperDash":false},{"StartTime":88336.0,"Position":187.261261,"HyperDash":false},{"StartTime":88449.0,"Position":157.612885,"HyperDash":false}]},{"StartTime":88793.0,"Objects":[{"StartTime":88793.0,"Position":304.0,"HyperDash":false},{"StartTime":88869.0,"Position":300.197144,"HyperDash":false},{"StartTime":88946.0,"Position":313.5416,"HyperDash":false},{"StartTime":89022.0,"Position":334.738739,"HyperDash":false},{"StartTime":89135.0,"Position":354.387115,"HyperDash":false}]},{"StartTime":89478.0,"Objects":[{"StartTime":89478.0,"Position":208.0,"HyperDash":false},{"StartTime":89554.0,"Position":197.802872,"HyperDash":false},{"StartTime":89631.0,"Position":182.4584,"HyperDash":false},{"StartTime":89707.0,"Position":169.261261,"HyperDash":false},{"StartTime":89820.0,"Position":157.612885,"HyperDash":false}]},{"StartTime":90164.0,"Objects":[{"StartTime":90164.0,"Position":304.0,"HyperDash":false},{"StartTime":90249.0,"Position":316.8624,"HyperDash":false},{"StartTime":90335.0,"Position":304.0,"HyperDash":false}]},{"StartTime":90507.0,"Objects":[{"StartTime":90507.0,"Position":208.0,"HyperDash":false}]},{"StartTime":90850.0,"Objects":[{"StartTime":90850.0,"Position":56.0,"HyperDash":false}]},{"StartTime":91021.0,"Objects":[{"StartTime":91021.0,"Position":56.0,"HyperDash":false}]},{"StartTime":91193.0,"Objects":[{"StartTime":91193.0,"Position":144.0,"HyperDash":false}]},{"StartTime":91536.0,"Objects":[{"StartTime":91536.0,"Position":344.0,"HyperDash":false}]},{"StartTime":91707.0,"Objects":[{"StartTime":91707.0,"Position":424.0,"HyperDash":false}]},{"StartTime":91878.0,"Objects":[{"StartTime":91878.0,"Position":424.0,"HyperDash":false}]},{"StartTime":92050.0,"Objects":[{"StartTime":92050.0,"Position":344.0,"HyperDash":false}]},{"StartTime":92221.0,"Objects":[{"StartTime":92221.0,"Position":256.0,"HyperDash":false}]},{"StartTime":92564.0,"Objects":[{"StartTime":92564.0,"Position":160.0,"HyperDash":false},{"StartTime":92649.0,"Position":142.69455,"HyperDash":false},{"StartTime":92735.0,"Position":131.871,"HyperDash":false},{"StartTime":92820.0,"Position":111.443245,"HyperDash":false},{"StartTime":92906.0,"Position":100.496979,"HyperDash":false},{"StartTime":92974.0,"Position":108.492638,"HyperDash":false},{"StartTime":93078.0,"Position":101.630577,"HyperDash":true}]},{"StartTime":93250.0,"Objects":[{"StartTime":93250.0,"Position":352.0,"HyperDash":false},{"StartTime":93335.0,"Position":389.30545,"HyperDash":false},{"StartTime":93421.0,"Position":398.129,"HyperDash":false},{"StartTime":93506.0,"Position":421.556763,"HyperDash":false},{"StartTime":93592.0,"Position":411.503021,"HyperDash":false},{"StartTime":93660.0,"Position":408.507355,"HyperDash":false},{"StartTime":93764.0,"Position":410.369415,"HyperDash":false}]},{"StartTime":93936.0,"Objects":[{"StartTime":93936.0,"Position":256.0,"HyperDash":false}]},{"StartTime":94021.0,"Objects":[{"StartTime":94021.0,"Position":256.0,"HyperDash":false}]},{"StartTime":94107.0,"Objects":[{"StartTime":94107.0,"Position":256.0,"HyperDash":false}]},{"StartTime":94193.0,"Objects":[{"StartTime":94193.0,"Position":256.0,"HyperDash":false}]},{"StartTime":94278.0,"Objects":[{"StartTime":94278.0,"Position":256.0,"HyperDash":false}]},{"StartTime":94364.0,"Objects":[{"StartTime":94364.0,"Position":256.0,"HyperDash":false}]},{"StartTime":94450.0,"Objects":[{"StartTime":94450.0,"Position":256.0,"HyperDash":false}]},{"StartTime":94536.0,"Objects":[{"StartTime":94536.0,"Position":256.0,"HyperDash":false}]},{"StartTime":94621.0,"Objects":[{"StartTime":94621.0,"Position":256.0,"HyperDash":false},{"StartTime":94706.0,"Position":317.7076,"HyperDash":false},{"StartTime":94792.0,"Position":356.0,"HyperDash":false},{"StartTime":94859.0,"Position":307.8187,"HyperDash":false},{"StartTime":94963.0,"Position":256.0,"HyperDash":false}]},{"StartTime":95136.0,"Objects":[{"StartTime":95136.0,"Position":448.0,"HyperDash":false},{"StartTime":95203.0,"Position":427.818726,"HyperDash":false},{"StartTime":95307.0,"Position":348.0,"HyperDash":false}]},{"StartTime":95650.0,"Objects":[{"StartTime":95650.0,"Position":40.0,"HyperDash":false},{"StartTime":95735.0,"Position":86.81512,"HyperDash":false},{"StartTime":95821.0,"Position":118.086884,"HyperDash":false},{"StartTime":95888.0,"Position":133.0,"HyperDash":false},{"StartTime":95992.0,"Position":120.0,"HyperDash":false}]},{"StartTime":96336.0,"Objects":[{"StartTime":96336.0,"Position":480.0,"HyperDash":false},{"StartTime":96403.0,"Position":422.173248,"HyperDash":false},{"StartTime":96507.0,"Position":383.4571,"HyperDash":false}]},{"StartTime":96678.0,"Objects":[{"StartTime":96678.0,"Position":176.0,"HyperDash":false},{"StartTime":96745.0,"Position":193.826752,"HyperDash":false},{"StartTime":96849.0,"Position":272.5429,"HyperDash":false}]},{"StartTime":97021.0,"Objects":[{"StartTime":97021.0,"Position":440.0,"HyperDash":false},{"StartTime":97106.0,"Position":383.010834,"HyperDash":false},{"StartTime":97192.0,"Position":343.4571,"HyperDash":false},{"StartTime":97259.0,"Position":370.283844,"HyperDash":false},{"StartTime":97363.0,"Position":440.0,"HyperDash":false}]},{"StartTime":97707.0,"Objects":[{"StartTime":97707.0,"Position":40.0,"HyperDash":false},{"StartTime":97792.0,"Position":41.5126953,"HyperDash":false},{"StartTime":97878.0,"Position":39.0196533,"HyperDash":false},{"StartTime":97945.0,"Position":73.99493,"HyperDash":false},{"StartTime":98049.0,"Position":115.429176,"HyperDash":false}]},{"StartTime":98393.0,"Objects":[{"StartTime":98393.0,"Position":440.0,"HyperDash":false},{"StartTime":98478.0,"Position":400.2924,"HyperDash":false},{"StartTime":98564.0,"Position":340.0,"HyperDash":false},{"StartTime":98631.0,"Position":332.355255,"HyperDash":false},{"StartTime":98735.0,"Position":276.290741,"HyperDash":false}]},{"StartTime":99078.0,"Objects":[{"StartTime":99078.0,"Position":32.0,"HyperDash":false},{"StartTime":99145.0,"Position":65.70535,"HyperDash":false},{"StartTime":99249.0,"Position":102.710678,"HyperDash":false}]},{"StartTime":99421.0,"Objects":[{"StartTime":99421.0,"Position":296.0,"HyperDash":false},{"StartTime":99488.0,"Position":284.294647,"HyperDash":false},{"StartTime":99592.0,"Position":225.289322,"HyperDash":false}]},{"StartTime":99764.0,"Objects":[{"StartTime":99764.0,"Position":408.0,"HyperDash":false},{"StartTime":99849.0,"Position":457.1486,"HyperDash":false},{"StartTime":99935.0,"Position":478.7107,"HyperDash":false},{"StartTime":100002.0,"Position":469.0053,"HyperDash":false},{"StartTime":100106.0,"Position":408.0,"HyperDash":false}]},{"StartTime":100450.0,"Objects":[{"StartTime":100450.0,"Position":32.0,"HyperDash":false},{"StartTime":100535.0,"Position":80.7076,"HyperDash":false},{"StartTime":100621.0,"Position":132.0,"HyperDash":false},{"StartTime":100688.0,"Position":176.18129,"HyperDash":false},{"StartTime":100792.0,"Position":232.0,"HyperDash":false}]},{"StartTime":101136.0,"Objects":[{"StartTime":101136.0,"Position":480.0,"HyperDash":false},{"StartTime":101221.0,"Position":426.2924,"HyperDash":false},{"StartTime":101307.0,"Position":380.0,"HyperDash":false},{"StartTime":101374.0,"Position":346.818726,"HyperDash":false},{"StartTime":101478.0,"Position":280.0,"HyperDash":false}]},{"StartTime":101821.0,"Objects":[{"StartTime":101821.0,"Position":256.0,"HyperDash":false},{"StartTime":101906.0,"Position":250.0,"HyperDash":false},{"StartTime":101992.0,"Position":256.0,"HyperDash":false},{"StartTime":102059.0,"Position":256.0,"HyperDash":false},{"StartTime":102163.0,"Position":256.0,"HyperDash":false}]},{"StartTime":102336.0,"Objects":[{"StartTime":102336.0,"Position":256.0,"HyperDash":false}]},{"StartTime":102507.0,"Objects":[{"StartTime":102507.0,"Position":256.0,"HyperDash":false},{"StartTime":102592.0,"Position":261.0,"HyperDash":false},{"StartTime":102678.0,"Position":256.0,"HyperDash":false},{"StartTime":102745.0,"Position":274.0,"HyperDash":false},{"StartTime":102849.0,"Position":256.0,"HyperDash":false}]},{"StartTime":103193.0,"Objects":[{"StartTime":103193.0,"Position":432.0,"HyperDash":false}]},{"StartTime":103364.0,"Objects":[{"StartTime":103364.0,"Position":256.0,"HyperDash":false}]},{"StartTime":103536.0,"Objects":[{"StartTime":103536.0,"Position":80.0,"HyperDash":true}]},{"StartTime":103878.0,"Objects":[{"StartTime":103878.0,"Position":480.0,"HyperDash":false}]},{"StartTime":104050.0,"Objects":[{"StartTime":104050.0,"Position":408.0,"HyperDash":false}]},{"StartTime":104221.0,"Objects":[{"StartTime":104221.0,"Position":336.0,"HyperDash":false}]},{"StartTime":104393.0,"Objects":[{"StartTime":104393.0,"Position":264.0,"HyperDash":false}]},{"StartTime":104564.0,"Objects":[{"StartTime":104564.0,"Position":184.0,"HyperDash":false}]},{"StartTime":104736.0,"Objects":[{"StartTime":104736.0,"Position":104.0,"HyperDash":false}]},{"StartTime":104907.0,"Objects":[{"StartTime":104907.0,"Position":32.0,"HyperDash":false}]},{"StartTime":105593.0,"Objects":[{"StartTime":105593.0,"Position":376.0,"HyperDash":false},{"StartTime":105678.0,"Position":422.963257,"HyperDash":false}]},{"StartTime":105764.0,"Objects":[{"StartTime":105764.0,"Position":411.0,"HyperDash":false},{"StartTime":105849.0,"Position":461.0,"HyperDash":false}]},{"StartTime":105936.0,"Objects":[{"StartTime":105936.0,"Position":438.0,"HyperDash":false},{"StartTime":106021.0,"Position":486.1759,"HyperDash":false}]},{"StartTime":106107.0,"Objects":[{"StartTime":106107.0,"Position":447.0,"HyperDash":false},{"StartTime":106192.0,"Position":492.579346,"HyperDash":false}]},{"StartTime":106278.0,"Objects":[{"StartTime":106278.0,"Position":492.0,"HyperDash":false}]},{"StartTime":106621.0,"Objects":[{"StartTime":106621.0,"Position":120.0,"HyperDash":false},{"StartTime":106706.0,"Position":80.04692,"HyperDash":false},{"StartTime":106792.0,"Position":49.0288429,"HyperDash":false},{"StartTime":106859.0,"Position":45.9070129,"HyperDash":false},{"StartTime":106963.0,"Position":40.4979248,"HyperDash":false}]},{"StartTime":107307.0,"Objects":[{"StartTime":107307.0,"Position":400.0,"HyperDash":false},{"StartTime":107392.0,"Position":359.373077,"HyperDash":false}]},{"StartTime":107478.0,"Objects":[{"StartTime":107478.0,"Position":422.0,"HyperDash":false},{"StartTime":107563.0,"Position":374.933044,"HyperDash":false}]},{"StartTime":107650.0,"Objects":[{"StartTime":107650.0,"Position":436.0,"HyperDash":false},{"StartTime":107735.0,"Position":386.191254,"HyperDash":false}]},{"StartTime":107821.0,"Objects":[{"StartTime":107821.0,"Position":430.0,"HyperDash":false},{"StartTime":107906.0,"Position":380.633484,"HyperDash":false}]},{"StartTime":107993.0,"Objects":[{"StartTime":107993.0,"Position":410.0,"HyperDash":false},{"StartTime":108078.0,"Position":364.420654,"HyperDash":false}]},{"StartTime":108164.0,"Objects":[{"StartTime":108164.0,"Position":377.0,"HyperDash":false},{"StartTime":108249.0,"Position":339.099457,"HyperDash":false}]},{"StartTime":108336.0,"Objects":[{"StartTime":108336.0,"Position":343.0,"HyperDash":false}]},{"StartTime":108678.0,"Objects":[{"StartTime":108678.0,"Position":48.0,"HyperDash":false},{"StartTime":108763.0,"Position":72.7952957,"HyperDash":false},{"StartTime":108849.0,"Position":118.469154,"HyperDash":false},{"StartTime":108916.0,"Position":133.274063,"HyperDash":false},{"StartTime":109020.0,"Position":126.387558,"HyperDash":false}]},{"StartTime":109364.0,"Objects":[{"StartTime":109364.0,"Position":464.0,"HyperDash":false},{"StartTime":109449.0,"Position":407.88382,"HyperDash":false},{"StartTime":109535.0,"Position":392.819672,"HyperDash":false},{"StartTime":109602.0,"Position":400.819,"HyperDash":false},{"StartTime":109706.0,"Position":384.6482,"HyperDash":false}]},{"StartTime":110050.0,"Objects":[{"StartTime":110050.0,"Position":32.0,"HyperDash":false},{"StartTime":110135.0,"Position":80.1758957,"HyperDash":false}]},{"StartTime":110221.0,"Objects":[{"StartTime":110221.0,"Position":16.0,"HyperDash":false},{"StartTime":110306.0,"Position":59.1889458,"HyperDash":false}]},{"StartTime":110393.0,"Objects":[{"StartTime":110393.0,"Position":27.0,"HyperDash":false},{"StartTime":110478.0,"Position":62.3553429,"HyperDash":false}]},{"StartTime":110564.0,"Objects":[{"StartTime":110564.0,"Position":42.0,"HyperDash":false},{"StartTime":110649.0,"Position":66.13023,"HyperDash":false}]},{"StartTime":110736.0,"Objects":[{"StartTime":110736.0,"Position":76.0,"HyperDash":false},{"StartTime":110821.0,"Position":88.54811,"HyperDash":false}]},{"StartTime":110907.0,"Objects":[{"StartTime":110907.0,"Position":134.0,"HyperDash":false},{"StartTime":110992.0,"Position":133.107285,"HyperDash":false}]},{"StartTime":111078.0,"Objects":[{"StartTime":111078.0,"Position":134.0,"HyperDash":false}]},{"StartTime":111421.0,"Objects":[{"StartTime":111421.0,"Position":456.0,"HyperDash":false},{"StartTime":111506.0,"Position":404.2924,"HyperDash":false},{"StartTime":111592.0,"Position":356.0,"HyperDash":false},{"StartTime":111659.0,"Position":397.1813,"HyperDash":false},{"StartTime":111763.0,"Position":456.0,"HyperDash":false}]},{"StartTime":112107.0,"Objects":[{"StartTime":112107.0,"Position":56.0,"HyperDash":false},{"StartTime":112192.0,"Position":97.7076,"HyperDash":false},{"StartTime":112278.0,"Position":156.0,"HyperDash":false},{"StartTime":112345.0,"Position":112.81871,"HyperDash":false},{"StartTime":112449.0,"Position":56.0,"HyperDash":false}]},{"StartTime":112793.0,"Objects":[{"StartTime":112793.0,"Position":16.0,"HyperDash":false}]},{"StartTime":112964.0,"Objects":[{"StartTime":112964.0,"Position":96.0,"HyperDash":false}]},{"StartTime":113136.0,"Objects":[{"StartTime":113136.0,"Position":176.0,"HyperDash":false}]},{"StartTime":113307.0,"Objects":[{"StartTime":113307.0,"Position":256.0,"HyperDash":false}]},{"StartTime":113478.0,"Objects":[{"StartTime":113478.0,"Position":336.0,"HyperDash":false}]},{"StartTime":113650.0,"Objects":[{"StartTime":113650.0,"Position":416.0,"HyperDash":false}]},{"StartTime":113821.0,"Objects":[{"StartTime":113821.0,"Position":496.0,"HyperDash":false}]},{"StartTime":114164.0,"Objects":[{"StartTime":114164.0,"Position":312.0,"HyperDash":false}]},{"StartTime":114336.0,"Objects":[{"StartTime":114336.0,"Position":256.0,"HyperDash":false}]},{"StartTime":114507.0,"Objects":[{"StartTime":114507.0,"Position":192.0,"HyperDash":false}]},{"StartTime":114850.0,"Objects":[{"StartTime":114850.0,"Position":256.0,"HyperDash":false}]},{"StartTime":115021.0,"Objects":[{"StartTime":115021.0,"Position":344.0,"HyperDash":false}]},{"StartTime":115193.0,"Objects":[{"StartTime":115193.0,"Position":312.0,"HyperDash":false}]},{"StartTime":115364.0,"Objects":[{"StartTime":115364.0,"Position":208.0,"HyperDash":false}]},{"StartTime":115536.0,"Objects":[{"StartTime":115536.0,"Position":176.0,"HyperDash":false}]},{"StartTime":115707.0,"Objects":[{"StartTime":115707.0,"Position":256.0,"HyperDash":false}]},{"StartTime":115878.0,"Objects":[{"StartTime":115878.0,"Position":256.0,"HyperDash":false}]},{"StartTime":116564.0,"Objects":[{"StartTime":116564.0,"Position":120.0,"HyperDash":false},{"StartTime":116640.0,"Position":111.647354,"HyperDash":false},{"StartTime":116717.0,"Position":96.06639,"HyperDash":false},{"StartTime":116793.0,"Position":53.7137451,"HyperDash":false},{"StartTime":116906.0,"Position":41.9131165,"HyperDash":false}]},{"StartTime":117250.0,"Objects":[{"StartTime":117250.0,"Position":368.0,"HyperDash":false},{"StartTime":117326.0,"Position":376.156769,"HyperDash":false},{"StartTime":117403.0,"Position":397.605072,"HyperDash":false},{"StartTime":117479.0,"Position":440.761841,"HyperDash":false},{"StartTime":117592.0,"Position":467.705444,"HyperDash":false}]},{"StartTime":117936.0,"Objects":[{"StartTime":117936.0,"Position":72.0,"HyperDash":false},{"StartTime":118012.0,"Position":82.97272,"HyperDash":false},{"StartTime":118089.0,"Position":63.8529663,"HyperDash":false},{"StartTime":118165.0,"Position":65.82568,"HyperDash":false},{"StartTime":118278.0,"Position":40.377224,"HyperDash":false}]},{"StartTime":118621.0,"Objects":[{"StartTime":118621.0,"Position":368.0,"HyperDash":false},{"StartTime":118706.0,"Position":353.255585,"HyperDash":false},{"StartTime":118792.0,"Position":328.220032,"HyperDash":false},{"StartTime":118877.0,"Position":302.475647,"HyperDash":false},{"StartTime":118963.0,"Position":268.294556,"HyperDash":false},{"StartTime":119039.0,"Position":291.273438,"HyperDash":false},{"StartTime":119116.0,"Position":309.688965,"HyperDash":false},{"StartTime":119193.0,"Position":348.1045,"HyperDash":false},{"StartTime":119306.0,"Position":368.0,"HyperDash":false}]},{"StartTime":119650.0,"Objects":[{"StartTime":119650.0,"Position":392.0,"HyperDash":false}]},{"StartTime":119821.0,"Objects":[{"StartTime":119821.0,"Position":392.0,"HyperDash":false}]},{"StartTime":119993.0,"Objects":[{"StartTime":119993.0,"Position":448.0,"HyperDash":false}]},{"StartTime":120164.0,"Objects":[{"StartTime":120164.0,"Position":448.0,"HyperDash":false}]},{"StartTime":120336.0,"Objects":[{"StartTime":120336.0,"Position":480.0,"HyperDash":false}]},{"StartTime":120507.0,"Objects":[{"StartTime":120507.0,"Position":480.0,"HyperDash":false}]},{"StartTime":120678.0,"Objects":[{"StartTime":120678.0,"Position":480.0,"HyperDash":false}]},{"StartTime":121021.0,"Objects":[{"StartTime":121021.0,"Position":448.0,"HyperDash":false}]},{"StartTime":121107.0,"Objects":[{"StartTime":121107.0,"Position":440.0,"HyperDash":false}]},{"StartTime":121193.0,"Objects":[{"StartTime":121193.0,"Position":432.0,"HyperDash":false}]},{"StartTime":121278.0,"Objects":[{"StartTime":121278.0,"Position":424.0,"HyperDash":false}]},{"StartTime":121364.0,"Objects":[{"StartTime":121364.0,"Position":416.0,"HyperDash":false}]},{"StartTime":121621.0,"Objects":[{"StartTime":121621.0,"Position":312.0,"HyperDash":false}]},{"StartTime":121707.0,"Objects":[{"StartTime":121707.0,"Position":312.0,"HyperDash":false}]},{"StartTime":121878.0,"Objects":[{"StartTime":121878.0,"Position":232.0,"HyperDash":false}]},{"StartTime":122050.0,"Objects":[{"StartTime":122050.0,"Position":168.0,"HyperDash":false}]},{"StartTime":122393.0,"Objects":[{"StartTime":122393.0,"Position":352.0,"HyperDash":false}]},{"StartTime":122564.0,"Objects":[{"StartTime":122564.0,"Position":376.0,"HyperDash":false}]},{"StartTime":122736.0,"Objects":[{"StartTime":122736.0,"Position":352.0,"HyperDash":false}]},{"StartTime":123078.0,"Objects":[{"StartTime":123078.0,"Position":168.0,"HyperDash":false}]},{"StartTime":123250.0,"Objects":[{"StartTime":123250.0,"Position":144.0,"HyperDash":false}]},{"StartTime":123421.0,"Objects":[{"StartTime":123421.0,"Position":168.0,"HyperDash":false}]},{"StartTime":123936.0,"Objects":[{"StartTime":123936.0,"Position":400.0,"HyperDash":false}]},{"StartTime":124107.0,"Objects":[{"StartTime":124107.0,"Position":467.0,"HyperDash":false}]},{"StartTime":124278.0,"Objects":[{"StartTime":124278.0,"Position":400.0,"HyperDash":false}]},{"StartTime":124450.0,"Objects":[{"StartTime":124450.0,"Position":326.0,"HyperDash":false}]},{"StartTime":124536.0,"Objects":[{"StartTime":124536.0,"Position":320.0,"HyperDash":false}]},{"StartTime":124621.0,"Objects":[{"StartTime":124621.0,"Position":315.0,"HyperDash":false}]},{"StartTime":124707.0,"Objects":[{"StartTime":124707.0,"Position":309.0,"HyperDash":false}]},{"StartTime":124793.0,"Objects":[{"StartTime":124793.0,"Position":303.0,"HyperDash":false}]},{"StartTime":125136.0,"Objects":[{"StartTime":125136.0,"Position":112.0,"HyperDash":false}]},{"StartTime":125307.0,"Objects":[{"StartTime":125307.0,"Position":112.0,"HyperDash":false}]},{"StartTime":125478.0,"Objects":[{"StartTime":125478.0,"Position":44.0,"HyperDash":false}]},{"StartTime":125650.0,"Objects":[{"StartTime":125650.0,"Position":44.0,"HyperDash":false}]},{"StartTime":125821.0,"Objects":[{"StartTime":125821.0,"Position":112.0,"HyperDash":false}]},{"StartTime":125993.0,"Objects":[{"StartTime":125993.0,"Position":112.0,"HyperDash":false}]},{"StartTime":126164.0,"Objects":[{"StartTime":126164.0,"Position":184.0,"HyperDash":false}]},{"StartTime":126250.0,"Objects":[{"StartTime":126250.0,"Position":189.0,"HyperDash":false}]},{"StartTime":126336.0,"Objects":[{"StartTime":126336.0,"Position":195.0,"HyperDash":false}]},{"StartTime":126421.0,"Objects":[{"StartTime":126421.0,"Position":200.0,"HyperDash":false}]},{"StartTime":126507.0,"Objects":[{"StartTime":126507.0,"Position":206.0,"HyperDash":false}]},{"StartTime":126593.0,"Objects":[{"StartTime":126593.0,"Position":212.0,"HyperDash":false}]},{"StartTime":126678.0,"Objects":[{"StartTime":126678.0,"Position":217.0,"HyperDash":false}]},{"StartTime":126764.0,"Objects":[{"StartTime":126764.0,"Position":223.0,"HyperDash":false}]},{"StartTime":126850.0,"Objects":[{"StartTime":126850.0,"Position":229.0,"HyperDash":false}]},{"StartTime":127536.0,"Objects":[{"StartTime":127536.0,"Position":72.0,"HyperDash":false}]},{"StartTime":127707.0,"Objects":[{"StartTime":127707.0,"Position":72.0,"HyperDash":false}]},{"StartTime":127878.0,"Objects":[{"StartTime":127878.0,"Position":112.0,"HyperDash":false}]},{"StartTime":128050.0,"Objects":[{"StartTime":128050.0,"Position":112.0,"HyperDash":false}]},{"StartTime":128221.0,"Objects":[{"StartTime":128221.0,"Position":152.0,"HyperDash":false}]},{"StartTime":128393.0,"Objects":[{"StartTime":128393.0,"Position":152.0,"HyperDash":false}]},{"StartTime":128564.0,"Objects":[{"StartTime":128564.0,"Position":192.0,"HyperDash":false}]},{"StartTime":128736.0,"Objects":[{"StartTime":128736.0,"Position":192.0,"HyperDash":false}]},{"StartTime":128907.0,"Objects":[{"StartTime":128907.0,"Position":280.0,"HyperDash":false}]},{"StartTime":129250.0,"Objects":[{"StartTime":129250.0,"Position":296.0,"HyperDash":false}]},{"StartTime":129421.0,"Objects":[{"StartTime":129421.0,"Position":395.0,"HyperDash":false}]},{"StartTime":129593.0,"Objects":[{"StartTime":129593.0,"Position":395.0,"HyperDash":false}]},{"StartTime":129764.0,"Objects":[{"StartTime":129764.0,"Position":295.0,"HyperDash":false}]},{"StartTime":129936.0,"Objects":[{"StartTime":129936.0,"Position":295.0,"HyperDash":false}]},{"StartTime":130107.0,"Objects":[{"StartTime":130107.0,"Position":391.0,"HyperDash":false}]},{"StartTime":130278.0,"Objects":[{"StartTime":130278.0,"Position":391.0,"HyperDash":false}]},{"StartTime":130621.0,"Objects":[{"StartTime":130621.0,"Position":256.0,"HyperDash":false}]},{"StartTime":130793.0,"Objects":[{"StartTime":130793.0,"Position":168.0,"HyperDash":false}]},{"StartTime":130964.0,"Objects":[{"StartTime":130964.0,"Position":256.0,"HyperDash":false}]},{"StartTime":131136.0,"Objects":[{"StartTime":131136.0,"Position":344.0,"HyperDash":false}]},{"StartTime":131307.0,"Objects":[{"StartTime":131307.0,"Position":344.0,"HyperDash":false}]},{"StartTime":131478.0,"Objects":[{"StartTime":131478.0,"Position":344.0,"HyperDash":false}]},{"StartTime":131650.0,"Objects":[{"StartTime":131650.0,"Position":344.0,"HyperDash":false}]},{"StartTime":131993.0,"Objects":[{"StartTime":131993.0,"Position":168.0,"HyperDash":false}]},{"StartTime":132164.0,"Objects":[{"StartTime":132164.0,"Position":168.0,"HyperDash":false}]},{"StartTime":132336.0,"Objects":[{"StartTime":132336.0,"Position":168.0,"HyperDash":false}]},{"StartTime":132593.0,"Objects":[{"StartTime":132593.0,"Position":272.0,"HyperDash":false}]},{"StartTime":132678.0,"Objects":[{"StartTime":132678.0,"Position":272.0,"HyperDash":false}]},{"StartTime":132850.0,"Objects":[{"StartTime":132850.0,"Position":168.0,"HyperDash":false}]},{"StartTime":133021.0,"Objects":[{"StartTime":133021.0,"Position":168.0,"HyperDash":false}]},{"StartTime":133364.0,"Objects":[{"StartTime":133364.0,"Position":40.0,"HyperDash":false},{"StartTime":133440.0,"Position":47.0,"HyperDash":false},{"StartTime":133517.0,"Position":46.0,"HyperDash":false},{"StartTime":133593.0,"Position":45.0,"HyperDash":false},{"StartTime":133706.0,"Position":40.0,"HyperDash":false}]},{"StartTime":134050.0,"Objects":[{"StartTime":134050.0,"Position":208.0,"HyperDash":false},{"StartTime":134126.0,"Position":192.0,"HyperDash":false},{"StartTime":134203.0,"Position":205.0,"HyperDash":false},{"StartTime":134279.0,"Position":215.0,"HyperDash":false},{"StartTime":134392.0,"Position":208.0,"HyperDash":false}]},{"StartTime":134736.0,"Objects":[{"StartTime":134736.0,"Position":208.0,"HyperDash":false}]},{"StartTime":134907.0,"Objects":[{"StartTime":134907.0,"Position":208.0,"HyperDash":false}]},{"StartTime":135078.0,"Objects":[{"StartTime":135078.0,"Position":304.0,"HyperDash":false}]},{"StartTime":135250.0,"Objects":[{"StartTime":135250.0,"Position":304.0,"HyperDash":false}]},{"StartTime":135421.0,"Objects":[{"StartTime":135421.0,"Position":400.0,"HyperDash":false}]},{"StartTime":135593.0,"Objects":[{"StartTime":135593.0,"Position":400.0,"HyperDash":false}]},{"StartTime":135764.0,"Objects":[{"StartTime":135764.0,"Position":496.0,"HyperDash":false}]},{"StartTime":136107.0,"Objects":[{"StartTime":136107.0,"Position":296.0,"HyperDash":false}]},{"StartTime":136278.0,"Objects":[{"StartTime":136278.0,"Position":216.0,"HyperDash":false}]},{"StartTime":136450.0,"Objects":[{"StartTime":136450.0,"Position":296.0,"HyperDash":false}]},{"StartTime":136621.0,"Objects":[{"StartTime":136621.0,"Position":216.0,"HyperDash":false}]},{"StartTime":136793.0,"Objects":[{"StartTime":136793.0,"Position":296.0,"HyperDash":false}]},{"StartTime":136964.0,"Objects":[{"StartTime":136964.0,"Position":292.0,"HyperDash":false}]},{"StartTime":137050.0,"Objects":[{"StartTime":137050.0,"Position":300.0,"HyperDash":false}]},{"StartTime":137136.0,"Objects":[{"StartTime":137136.0,"Position":308.0,"HyperDash":false}]},{"StartTime":137307.0,"Objects":[{"StartTime":137307.0,"Position":220.0,"HyperDash":false}]},{"StartTime":137393.0,"Objects":[{"StartTime":137393.0,"Position":212.0,"HyperDash":false}]},{"StartTime":137478.0,"Objects":[{"StartTime":137478.0,"Position":204.0,"HyperDash":false}]},{"StartTime":137650.0,"Objects":[{"StartTime":137650.0,"Position":260.0,"HyperDash":false}]},{"StartTime":137736.0,"Objects":[{"StartTime":137736.0,"Position":260.0,"HyperDash":false}]},{"StartTime":137821.0,"Objects":[{"StartTime":137821.0,"Position":260.0,"HyperDash":false}]},{"StartTime":137993.0,"Objects":[{"StartTime":137993.0,"Position":441.0,"HyperDash":false},{"StartTime":138057.0,"Position":442.0,"HyperDash":false},{"StartTime":138121.0,"Position":278.0,"HyperDash":false},{"StartTime":138185.0,"Position":90.0,"HyperDash":false},{"StartTime":138250.0,"Position":409.0,"HyperDash":false},{"StartTime":138314.0,"Position":377.0,"HyperDash":false},{"StartTime":138378.0,"Position":457.0,"HyperDash":false},{"StartTime":138442.0,"Position":409.0,"HyperDash":false},{"StartTime":138507.0,"Position":43.0,"HyperDash":false},{"StartTime":138571.0,"Position":162.0,"HyperDash":false},{"StartTime":138635.0,"Position":341.0,"HyperDash":false},{"StartTime":138699.0,"Position":72.0,"HyperDash":false},{"StartTime":138764.0,"Position":135.0,"HyperDash":false},{"StartTime":138828.0,"Position":252.0,"HyperDash":false},{"StartTime":138892.0,"Position":446.0,"HyperDash":false},{"StartTime":138956.0,"Position":284.0,"HyperDash":false},{"StartTime":139021.0,"Position":70.0,"HyperDash":false}]},{"StartTime":139193.0,"Objects":[{"StartTime":139193.0,"Position":256.0,"HyperDash":false}]},{"StartTime":139536.0,"Objects":[{"StartTime":139536.0,"Position":256.0,"HyperDash":false},{"StartTime":139612.0,"Position":285.1111,"HyperDash":false},{"StartTime":139689.0,"Position":274.3684,"HyperDash":false},{"StartTime":139765.0,"Position":287.479523,"HyperDash":false},{"StartTime":139878.0,"Position":306.0,"HyperDash":false}]},{"StartTime":140221.0,"Objects":[{"StartTime":140221.0,"Position":256.0,"HyperDash":false},{"StartTime":140297.0,"Position":261.8889,"HyperDash":false},{"StartTime":140374.0,"Position":249.631577,"HyperDash":false},{"StartTime":140450.0,"Position":226.520462,"HyperDash":false},{"StartTime":140563.0,"Position":206.0,"HyperDash":false}]},{"StartTime":140907.0,"Objects":[{"StartTime":140907.0,"Position":256.0,"HyperDash":false},{"StartTime":140983.0,"Position":284.1111,"HyperDash":false},{"StartTime":141060.0,"Position":295.3684,"HyperDash":false},{"StartTime":141136.0,"Position":290.479523,"HyperDash":false},{"StartTime":141249.0,"Position":306.0,"HyperDash":false}]},{"StartTime":141593.0,"Objects":[{"StartTime":141593.0,"Position":256.0,"HyperDash":false},{"StartTime":141669.0,"Position":257.8889,"HyperDash":false},{"StartTime":141746.0,"Position":242.631577,"HyperDash":false},{"StartTime":141822.0,"Position":221.520462,"HyperDash":false},{"StartTime":141935.0,"Position":206.0,"HyperDash":false}]},{"StartTime":142278.0,"Objects":[{"StartTime":142278.0,"Position":425.0,"HyperDash":false},{"StartTime":142363.0,"Position":281.0,"HyperDash":false},{"StartTime":142449.0,"Position":3.0,"HyperDash":false},{"StartTime":142535.0,"Position":346.0,"HyperDash":false},{"StartTime":142620.0,"Position":350.0,"HyperDash":false},{"StartTime":142706.0,"Position":217.0,"HyperDash":false},{"StartTime":142792.0,"Position":455.0,"HyperDash":false},{"StartTime":142878.0,"Position":229.0,"HyperDash":false},{"StartTime":142963.0,"Position":51.0,"HyperDash":false},{"StartTime":143049.0,"Position":199.0,"HyperDash":false},{"StartTime":143135.0,"Position":208.0,"HyperDash":false},{"StartTime":143220.0,"Position":173.0,"HyperDash":false},{"StartTime":143306.0,"Position":367.0,"HyperDash":false},{"StartTime":143392.0,"Position":193.0,"HyperDash":false},{"StartTime":143478.0,"Position":488.0,"HyperDash":false},{"StartTime":143563.0,"Position":314.0,"HyperDash":false},{"StartTime":143649.0,"Position":135.0,"HyperDash":false},{"StartTime":143735.0,"Position":399.0,"HyperDash":false},{"StartTime":143820.0,"Position":404.0,"HyperDash":false},{"StartTime":143906.0,"Position":152.0,"HyperDash":false},{"StartTime":143992.0,"Position":353.0,"HyperDash":false},{"StartTime":144078.0,"Position":358.0,"HyperDash":false},{"StartTime":144163.0,"Position":447.0,"HyperDash":false},{"StartTime":144249.0,"Position":222.0,"HyperDash":false},{"StartTime":144335.0,"Position":382.0,"HyperDash":false},{"StartTime":144420.0,"Position":433.0,"HyperDash":false},{"StartTime":144506.0,"Position":450.0,"HyperDash":false},{"StartTime":144592.0,"Position":326.0,"HyperDash":false},{"StartTime":144678.0,"Position":414.0,"HyperDash":false},{"StartTime":144763.0,"Position":285.0,"HyperDash":false},{"StartTime":144849.0,"Position":336.0,"HyperDash":false},{"StartTime":144935.0,"Position":509.0,"HyperDash":false},{"StartTime":145021.0,"Position":334.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/39206.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/39206.osu new file mode 100644 index 0000000000..3aeb80e9d5 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/39206.osu @@ -0,0 +1,524 @@ +osu file format v7 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:5 +CircleSize:4 +OverallDifficulty:8 +SliderMultiplier:1 +SliderTickRate:1 + +[Events] +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +336,342.857142857143,4,1,0,100,1,0 +1020,-100,4,2,0,100,0,0 +21250,-100,4,1,0,100,0,0 +23131,-100,4,1,0,100,0,0 +26731,-100,4,1,0,100,0,0 +27931,-50,4,1,0,100,0,0 +28616,-100,4,1,0,100,0,0 +32388,-50,4,1,0,100,0,0 +34102,-100,4,1,0,100,0,0 +37874,-50,4,1,0,100,0,0 +39588,-100,4,1,0,100,0,0 +51759,-100,4,2,0,100,0,0 +52445,-100,4,1,0,100,0,0 +62730,-100,4,2,0,100,0,0 +63416,-100,4,2,0,100,0,0 +66159,-100,4,2,0,100,0,0 +81588,-200,4,2,0,100,0,0 +82278,-100,4,2,0,100,0,0 +85359,-100,4,2,0,100,0,0 +92564,-100,4,1,0,100,0,0 +94616,-50,4,1,0,100,0,0 +116559,-100,4,1,0,100,0,0 +139188,-200,4,2,0,100,0,0 + +[HitObjects] +256,192,678,1,0 +456,216,1021,5,2 +456,264,1193,1,2 +456,312,1364,1,2 +312,168,1707,1,2 +312,120,1878,1,2 +312,72,2050,1,2 +168,216,2393,1,2 +168,264,2564,1,2 +168,312,2736,1,2 +24,168,3078,1,2 +24,120,3250,1,2 +24,72,3421,1,2 +56,272,3764,5,2 +136,336,3936,1,2 +216,272,4107,1,2 +296,88,4450,1,2 +376,24,4621,1,2 +456,88,4793,1,2 +456,288,5135,1,2 +376,352,5307,1,2 +296,288,5478,1,2 +216,104,5821,1,2 +136,40,5993,1,2 +56,104,6164,1,2 +24,304,6507,6,2,B|24:200,1,100 +144,40,7193,2,2,B|144:144,1,100 +256,304,7878,2,2,B|256:200,1,100 +376,40,8564,2,2,B|376:144,1,100 +488,304,9250,2,2,B|488:200,1,100 +256,208,9935,12,0,11050 +40,104,11307,5,2 +56,88,11393,1,2 +80,72,11478,1,2 +104,64,11564,1,2 +128,56,11650,2,2,B|176:40|232:56,1,100 +288,248,12336,2,2,B|240:264|184:248,1,100 +344,120,13021,2,2,B|392:104|448:120,1,100 +504,312,13707,2,2,B|456:328|400:312,1,100 +328,264,14221,5,2 +312,264,14307,1,2 +296,264,14393,2,2,B|256:360,1,100 +160,184,15078,2,2,B|200:280,1,100,2|2 +296,104,15764,2,2,B|256:200,1,100 +160,24,16450,2,2,B|200:120,1,100 +112,160,16964,5,2 +96,176,17050,1,2 +88,200,17136,2,2,B|128:280|200:296,2,150 +424,184,18507,2,2,B|384:104|312:88,2,150 +368,256,19707,1,2 +352,256,19793,1,2 +336,256,19878,2,2,B|232:256,1,100 +136,80,20564,2,2,B|240:80,1,100 +392,208,21250,6,0,B|440:280|392:360,1,150 +120,176,21936,2,0,B|72:104|120:24,1,150 +176,112,22621,6,0,B|269:103|307:15,1,150 +297,35,23307,1,0 +448,296,23821,1,0 +352,328,23993,2,0,B|304:352|248:352,1,100 +160,352,24507,1,0 +160,352,24593,1,0 +160,352,24678,1,0 +88,168,25021,5,0 +176,112,25193,1,0 +256,56,25364,1,0 +424,160,25707,1,0 +448,256,25878,1,0 +472,352,26050,2,0,B|414:287|325:312,1,150,0|0 +304,216,26736,2,0,B|248:232|240:296,1,100 +256,208,27250,12,0,28107 +32,248,28621,5,0 +80,160,28793,1,0 +352,32,29307,1,0 +424,104,29478,1,0 +472,192,29650,1,0 +432,280,29821,1,0 +360,352,29993,1,0 +360,352,30078,1,0 +360,352,30164,1,0 +184,256,30507,6,0,B|208:208,2,50 +64,56,31193,2,0,B|93:100,2,50,0|0|0 +352,40,31878,2,0,B|298:43,2,50,0|0|0 +320,136,32393,6,0,B|290:122,5,25 +342,181,32736,2,0,B|312:167,5,25,0|0|0|0|0|0 +399,173,33078,2,0,B|369:159,5,25 +422,219,33421,2,0,B|392:205,6,25 +368,104,34107,5,0 +280,48,34278,1,0 +280,344,34793,1,0 +184,320,34964,1,0 +112,248,35136,1,0 +64,160,35307,1,0 +32,64,35478,1,0 +32,64,35564,1,0 +32,64,35650,1,0 +232,32,35993,5,0 +328,56,36164,1,0 +408,120,36336,1,0 +464,200,36507,1,0 +408,120,36678,1,0 +328,56,36850,1,0 +232,32,37021,1,0 +72,288,37535,5,0 +112,192,37707,1,0 +144,96,37878,6,0,B|112:96,4,25 +232,144,38221,2,0,B|200:144,4,25,0|0|0|0|0 +320,96,38564,2,0,B|288:96,4,25 +408,144,38907,2,0,B|376:144,4,25 +304,248,39593,5,0 +208,280,39764,1,0 +40,48,40278,1,0 +112,120,40450,1,0 +200,72,40621,1,0 +264,152,40793,1,0 +352,104,40964,1,0 +352,104,41050,1,0 +352,104,41135,1,0 +480,256,41478,5,0 +422,179,41650,1,0 +364,102,41821,1,0 +422,179,41993,1,0 +327,199,42164,1,0 +226,220,42335,1,0 +327,199,42507,1,0 +381,118,42678,1,0 +437,32,42850,1,0 +381,118,43021,1,0 +327,199,43193,1,0 +256,208,43278,12,0,44050 +328,184,44221,6,0,B|408:248,1,100 +184,200,44907,2,0,B|104:136,1,100 +192,88,45421,5,0 +192,88,45507,1,0 +192,88,45593,1,0 +106,135,45764,1,0 +106,135,45850,1,0 +106,135,45935,1,0 +154,219,46107,1,0 +237,170,46278,1,0 +237,170,46364,1,0 +237,170,46450,1,0 +237,170,46535,1,0 +237,170,46621,1,0 +410,70,46964,5,0 +410,70,47135,1,0 +462,160,47307,1,0 +462,160,47478,1,0 +379,209,47650,1,0 +379,209,47821,1,0 +328,119,47993,1,0 +328,119,48164,1,0 +237,170,48335,1,0 +328,119,48507,1,0 +410,71,48678,1,0 +264,88,48935,5,0 +264,88,49021,1,0 +304,184,49193,1,0 +368,256,49364,1,0 +368,256,49707,6,0,B|472:256,1,100,0|0 +280,184,50393,2,0,B|392:184,1,100 +88,248,51250,2,0,B|200:248,1,100 +264,312,51764,1,4 +280,312,51850,1,4 +296,312,51935,1,4 +312,312,52021,1,4 +328,312,52107,1,4 +208,152,52450,5,0 +304,152,52621,1,0 +256,64,52793,1,0 +208,256,53135,1,0 +304,256,53307,1,0 +208,216,53478,1,0 +304,216,53650,1,0 +208,176,53821,1,0 +304,176,53993,1,0 +256,208,54164,12,0,55021 +256,320,55193,6,0,B|184:248,1,100 +256,64,55878,2,0,B|328:136,1,100 +256,192,56393,5,4 +160,192,56564,1,4 +160,192,56650,1,4 +160,192,56735,1,4 +160,88,56907,1,4 +256,88,57078,1,4 +352,88,57250,1,0 +360,88,57335,1,0 +368,88,57421,1,0 +376,88,57507,1,0 +384,88,57593,1,0 +472,264,57935,5,0 +387,318,58107,1,0 +284,325,58278,1,0 +193,291,58450,1,0 +139,207,58621,1,0 +132,103,58793,1,0 +174,12,58964,1,0 +256,200,59307,5,0 +208,288,59478,1,0 +304,288,59650,1,0 +344,200,59821,1,0 +312,160,59907,1,0 +280,120,59993,1,0 +208,56,60164,1,0 +304,56,60335,1,0 +200,224,60678,6,0,B|120:288,1,100 +312,224,61364,2,0,B|392:288,1,100 +390,286,62050,1,0 +121,286,62393,1,0 +256,224,62735,1,4 +256,232,62821,1,4 +256,240,62907,1,4 +256,248,62993,1,4 +256,256,63078,1,4 +432,352,63421,5,2 +496,272,63593,1,2 +496,168,63764,1,2 +440,88,63935,1,2 +352,32,64107,1,2 +256,8,64278,1,2 +160,32,64450,1,2 +72,88,64621,1,2 +8,168,64793,1,2 +8,264,64964,1,2 +56,352,65135,1,2 +256,208,65221,12,4,65993 +296,232,66164,6,2,B|352:320,1,100 +216,160,66850,2,2,B|160:248,1,100 +296,88,67535,2,2,B|352:176,1,100 +256,208,67964,12,4,68735 +304,136,68907,6,2,B|408:136,1,100 +208,192,69593,2,2,B|104:192,1,100 +304,248,70278,2,2,B|408:248,1,100 +56,48,71307,6,0,B|24:88|56:144,2,100 +256,48,72335,1,2 +328,120,72507,1,2 +400,48,72678,1,2 +400,48,73021,2,4,B|440:88|408:144,1,100,4|0 +112,336,73707,2,4,B|72:296|104:240,1,100,4|0 +304,264,74393,6,2,B|360:360,1,100 +304,120,75078,2,2,B|360:24,1,100 +464,200,75764,1,2 +384,264,75935,1,2 +304,200,76107,1,2 +232,264,76278,1,2 +160,200,76450,2,4,B|120:200,2,25 +80,264,76793,1,4 +120,72,77135,6,2,B|29:124,1,100,2|2 +232,96,77821,2,2,B|322:43,1,100 +176,184,78507,2,2,B|85:236,1,100 +288,208,79193,2,2,B|378:156,1,100,2|2 +240,304,79878,2,2,B|149:356,1,100,2|2 +32,192,80564,5,2 +66,95,80735,1,2 +161,131,80907,1,2 +190,38,81078,1,2 +285,73,81250,1,2 +384,72,81593,2,12,B|464:72,1,75,4|4 +440,176,82278,6,0,B|408:224,1,50 +320,176,82621,2,0,B|288:128,1,50 +200,176,82964,2,0,B|168:224,1,50 +248,280,83307,1,2 +344,280,83478,1,2 +448,280,83650,1,2 +400,80,83993,2,0,L|344:80,1,50 +400,80,84335,2,0,L|456:80,1,50 +408,168,84678,1,2 +304,168,84850,1,2 +208,168,85021,1,2 +208,168,85364,6,0,B|104:168,1,100,0|4 +304,216,86050,2,0,B|408:216,1,100,0|4 +480,32,86735,1,2 +376,32,86907,1,2 +272,32,87078,1,2 +168,32,87250,1,2 +64,32,87421,2,2,B|16:32,2,25 +64,32,87764,1,2 +208,168,88107,6,0,B|152:72,1,100,0|2 +304,224,88793,2,0,B|360:128,1,100,0|4 +208,272,89478,2,0,B|152:176,1,100,0|4 +304,328,90164,2,2,B|328:288,2,25 +208,368,90507,1,2 +56,232,90850,5,2 +56,128,91021,1,2 +144,80,91193,1,2 +344,80,91536,5,2 +424,136,91707,1,2 +424,232,91878,1,2 +344,288,92050,1,2 +256,248,92221,1,2 +160,56,92564,6,0,B|80:104|104:192,1,150 +352,328,93250,2,0,B|432:280|408:192,1,150 +256,192,93936,1,0 +256,200,94021,1,0 +256,208,94107,1,0 +256,216,94193,1,0 +256,224,94278,1,0 +256,232,94364,1,0 +256,240,94450,1,0 +256,248,94536,1,4 +256,256,94621,6,0,B|360:256,2,100 +448,328,95136,2,0,B|344:328,1,100 +40,72,95650,2,0,L|120:136|120:240,1,200 +480,64,96336,2,0,B|380:37,1,100 +176,48,96678,2,0,B|276:75,1,100 +440,184,97021,2,0,B|340:157,2,100 +40,176,97707,6,0,L|39:278|120:343,1,200,0|0 +440,112,98393,2,0,L|337:112|272:31,1,200,0|0 +32,344,99078,2,0,B|111:265,1,100,0|0 +296,200,99421,2,0,B|217:279,1,100,0|0 +408,184,99764,2,0,B|487:105,2,100 +32,32,100450,6,0,L|232:32,1,200 +480,352,101136,2,0,L|280:352,1,200 +256,192,101821,2,0,B|256:296,2,100,0|0|0 +256,192,102336,1,0 +256,192,102507,2,0,B|256:88,2,100,0|0|0 +432,344,103193,5,0 +256,248,103364,1,0 +80,344,103536,1,0 +480,256,103878,5,0 +408,72,104050,1,0 +336,256,104221,1,0 +264,72,104393,1,0 +184,256,104564,1,0 +104,72,104736,1,0 +32,256,104907,1,8 +376,48,105593,6,0,B|428:29,1,50,0|0 +411,78,105764,2,0,B|467:78,1,50,0|0 +438,127,105936,2,0,B|492:142,1,50,0|0 +447,176,106107,2,0,B|498:199,1,50,0|0 +492,196,106278,1,8 +120,344,106621,2,0,B|13:289|43:167,1,200 +400,352,107307,6,0,B|354:319,1,50,0|0 +422,286,107478,2,0,B|369:267,1,50,0|0 +436,219,107650,2,0,B|379:214,1,50,0|0 +430,152,107821,2,0,B|374:161,1,50,0|0 +410,89,107993,2,0,B|359:112,1,50,0|0 +377,34,108164,2,0,B|334:71,1,50,0|0 +343,68,108336,1,8 +48,344,108678,6,0,B|154:289|124:167,1,200 +464,40,109364,2,0,B|357:94|387:216,1,200 +32,32,110050,6,0,B|86:17,1,50 +16,94,110221,2,0,B|64:66,1,50 +27,165,110393,2,0,B|67:125,1,50 +42,226,110564,2,0,B|69:177,1,50 +76,282,110736,2,0,B|90:228,1,50 +134,324,110907,2,0,B|133:268,1,50 +134,274,111078,1,8 +456,40,111421,6,0,B|352:40,2,100,0|0|8 +56,40,112107,2,0,B|160:40,2,100,0|0|8 +16,192,112793,5,0 +96,192,112964,1,0 +176,192,113136,1,0 +256,192,113307,1,0 +336,192,113478,1,0 +416,192,113650,1,0 +496,192,113821,1,8 +312,112,114164,5,0 +256,192,114336,1,0 +192,112,114507,1,0 +256,304,114850,5,0 +344,256,115021,1,0 +312,160,115193,1,0 +208,160,115364,1,0 +176,256,115536,1,0 +256,304,115707,1,0 +256,304,115878,1,2 +120,160,116564,6,0,B|40:96,1,100,0|0 +368,336,117250,2,0,B|472:328,1,100,0|0 +72,248,117936,2,0,B|40:344,1,100 +368,112,118621,2,0,B|264:104,2,100 +392,312,119650,5,0 +392,312,119821,1,0 +448,264,119993,1,0 +448,264,120164,1,0 +480,200,120336,1,0 +480,200,120507,1,0 +480,200,120678,1,0 +448,48,121021,5,0 +440,48,121107,1,0 +432,48,121193,1,0 +424,48,121278,1,0 +416,48,121364,1,0 +312,96,121621,1,0 +312,96,121707,1,0 +232,104,121878,1,0 +168,144,122050,1,0 +352,232,122393,5,0 +376,192,122564,1,0 +352,144,122736,1,0 +168,144,123078,1,0 +144,184,123250,1,0 +168,232,123421,1,0 +400,48,123936,5,0 +467,115,124107,1,0 +400,183,124278,1,0 +326,110,124450,1,0 +320,104,124536,1,0 +315,98,124621,1,0 +309,93,124707,1,0 +303,87,124793,1,0 +112,336,125136,5,0 +112,336,125307,1,0 +44,268,125478,1,0 +44,268,125650,1,0 +112,200,125821,1,0 +112,200,125993,1,0 +184,264,126164,1,0 +189,258,126250,1,0 +195,252,126336,1,0 +200,247,126421,1,0 +206,241,126507,1,0 +212,235,126593,1,0 +217,230,126678,1,0 +223,224,126764,1,0 +229,218,126850,1,0 +72,96,127536,5,0 +72,192,127707,1,0 +112,96,127878,1,0 +112,192,128050,1,0 +152,96,128221,1,0 +152,192,128393,1,0 +192,96,128564,1,0 +192,192,128736,1,0 +280,144,128907,1,0 +296,344,129250,5,0 +395,344,129421,1,0 +395,243,129593,1,0 +295,241,129764,1,0 +295,137,129936,1,0 +391,137,130107,1,0 +391,33,130278,1,0 +256,104,130621,5,0 +168,192,130793,1,0 +256,280,130964,1,0 +344,192,131136,1,0 +344,192,131307,1,0 +344,288,131478,1,0 +344,96,131650,1,0 +168,184,131993,5,0 +168,184,132164,1,0 +168,184,132336,1,0 +272,80,132593,1,0 +272,80,132678,1,0 +168,80,132850,1,0 +168,80,133021,1,0 +40,240,133364,6,0,B|40:344,1,100 +208,224,134050,2,0,B|208:120,1,100 +208,328,134736,1,0 +208,224,134907,1,0 +304,224,135078,1,0 +304,120,135250,1,0 +400,120,135421,1,0 +400,16,135593,1,0 +496,16,135764,1,0 +296,56,136107,5,0 +216,112,136278,1,0 +296,168,136450,1,0 +216,232,136621,1,0 +296,288,136793,1,0 +292,188,136964,5,4 +300,188,137050,1,4 +308,188,137136,1,4 +220,188,137307,1,4 +212,188,137393,1,4 +204,188,137478,1,4 +260,268,137650,1,4 +260,276,137736,1,4 +260,284,137821,1,4 +256,208,137993,12,4,139021 +256,16,139193,5,2 +256,112,139536,2,2,B|312:112,1,50,2|2 +256,200,140221,2,2,B|200:200,1,50,2|2 +256,288,140907,2,2,B|312:288,1,50,2|2 +256,376,141593,2,2,B|200:376,1,50 +256,208,142278,12,4,145021 diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3949367-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3949367-expected-conversion.json new file mode 100644 index 0000000000..da0e4e120a --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3949367-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":6003.0,"Objects":[{"StartTime":6003.0,"Position":64.0,"HyperDash":false}]},{"StartTime":6366.0,"Objects":[{"StartTime":6366.0,"Position":192.0,"HyperDash":false}]},{"StartTime":6730.0,"Objects":[{"StartTime":6730.0,"Position":64.0,"HyperDash":false}]},{"StartTime":7094.0,"Objects":[{"StartTime":7094.0,"Position":192.0,"HyperDash":false}]},{"StartTime":7457.0,"Objects":[{"StartTime":7457.0,"Position":320.0,"HyperDash":false}]},{"StartTime":7821.0,"Objects":[{"StartTime":7821.0,"Position":192.0,"HyperDash":false}]},{"StartTime":8185.0,"Objects":[{"StartTime":8185.0,"Position":320.0,"HyperDash":false}]},{"StartTime":8548.0,"Objects":[{"StartTime":8548.0,"Position":192.0,"HyperDash":false}]},{"StartTime":8912.0,"Objects":[{"StartTime":8912.0,"Position":320.0,"HyperDash":false}]},{"StartTime":9275.0,"Objects":[{"StartTime":9275.0,"Position":448.0,"HyperDash":false}]},{"StartTime":9639.0,"Objects":[{"StartTime":9639.0,"Position":320.0,"HyperDash":false}]},{"StartTime":10003.0,"Objects":[{"StartTime":10003.0,"Position":448.0,"HyperDash":false}]},{"StartTime":10366.0,"Objects":[{"StartTime":10366.0,"Position":65.0,"HyperDash":false},{"StartTime":10434.0,"Position":482.0,"HyperDash":false},{"StartTime":10502.0,"Position":164.0,"HyperDash":false},{"StartTime":10570.0,"Position":315.0,"HyperDash":false},{"StartTime":10638.0,"Position":145.0,"HyperDash":false},{"StartTime":10706.0,"Position":159.0,"HyperDash":false},{"StartTime":10775.0,"Position":310.0,"HyperDash":false},{"StartTime":10843.0,"Position":441.0,"HyperDash":false},{"StartTime":10911.0,"Position":428.0,"HyperDash":false},{"StartTime":10979.0,"Position":243.0,"HyperDash":false},{"StartTime":11047.0,"Position":422.0,"HyperDash":false},{"StartTime":11116.0,"Position":481.0,"HyperDash":false},{"StartTime":11184.0,"Position":104.0,"HyperDash":false},{"StartTime":11252.0,"Position":473.0,"HyperDash":false},{"StartTime":11320.0,"Position":135.0,"HyperDash":false},{"StartTime":11388.0,"Position":360.0,"HyperDash":false},{"StartTime":11457.0,"Position":123.0,"HyperDash":false}]},{"StartTime":11821.0,"Objects":[{"StartTime":11821.0,"Position":96.0,"HyperDash":false}]},{"StartTime":12003.0,"Objects":[{"StartTime":12003.0,"Position":176.0,"HyperDash":false},{"StartTime":12093.0,"Position":204.284271,"HyperDash":false},{"StartTime":12184.0,"Position":176.0,"HyperDash":false}]},{"StartTime":12366.0,"Objects":[{"StartTime":12366.0,"Position":64.0,"HyperDash":false}]},{"StartTime":12730.0,"Objects":[{"StartTime":12730.0,"Position":224.0,"HyperDash":false},{"StartTime":12820.0,"Position":252.284271,"HyperDash":false},{"StartTime":12911.0,"Position":224.0,"HyperDash":false}]},{"StartTime":13094.0,"Objects":[{"StartTime":13094.0,"Position":144.0,"HyperDash":false}]},{"StartTime":13275.0,"Objects":[{"StartTime":13275.0,"Position":320.0,"HyperDash":false}]},{"StartTime":13366.0,"Objects":[{"StartTime":13366.0,"Position":368.0,"HyperDash":false}]},{"StartTime":13457.0,"Objects":[{"StartTime":13457.0,"Position":320.0,"HyperDash":false}]},{"StartTime":13639.0,"Objects":[{"StartTime":13639.0,"Position":208.0,"HyperDash":false}]},{"StartTime":13730.0,"Objects":[{"StartTime":13730.0,"Position":160.0,"HyperDash":false},{"StartTime":13820.0,"Position":160.0,"HyperDash":false}]},{"StartTime":14003.0,"Objects":[{"StartTime":14003.0,"Position":240.0,"HyperDash":false}]},{"StartTime":14185.0,"Objects":[{"StartTime":14185.0,"Position":128.0,"HyperDash":false}]},{"StartTime":14366.0,"Objects":[{"StartTime":14366.0,"Position":208.0,"HyperDash":false},{"StartTime":14456.0,"Position":208.0,"HyperDash":false}]},{"StartTime":14548.0,"Objects":[{"StartTime":14548.0,"Position":160.0,"HyperDash":false}]},{"StartTime":14730.0,"Objects":[{"StartTime":14730.0,"Position":336.0,"HyperDash":false}]},{"StartTime":14912.0,"Objects":[{"StartTime":14912.0,"Position":256.0,"HyperDash":false},{"StartTime":15002.0,"Position":227.715729,"HyperDash":false},{"StartTime":15093.0,"Position":256.0,"HyperDash":false}]},{"StartTime":15275.0,"Objects":[{"StartTime":15275.0,"Position":368.0,"HyperDash":false}]},{"StartTime":15639.0,"Objects":[{"StartTime":15639.0,"Position":208.0,"HyperDash":false},{"StartTime":15729.0,"Position":179.715729,"HyperDash":false},{"StartTime":15820.0,"Position":208.0,"HyperDash":false}]},{"StartTime":16003.0,"Objects":[{"StartTime":16003.0,"Position":288.0,"HyperDash":false}]},{"StartTime":16185.0,"Objects":[{"StartTime":16185.0,"Position":112.0,"HyperDash":false}]},{"StartTime":16275.0,"Objects":[{"StartTime":16275.0,"Position":64.0,"HyperDash":false}]},{"StartTime":16366.0,"Objects":[{"StartTime":16366.0,"Position":112.0,"HyperDash":false}]},{"StartTime":16548.0,"Objects":[{"StartTime":16548.0,"Position":224.0,"HyperDash":false}]},{"StartTime":16639.0,"Objects":[{"StartTime":16639.0,"Position":272.0,"HyperDash":false},{"StartTime":16729.0,"Position":272.0,"HyperDash":false}]},{"StartTime":16912.0,"Objects":[{"StartTime":16912.0,"Position":160.0,"HyperDash":false}]},{"StartTime":17003.0,"Objects":[{"StartTime":17003.0,"Position":208.0,"HyperDash":false}]},{"StartTime":17094.0,"Objects":[{"StartTime":17094.0,"Position":256.0,"HyperDash":false}]},{"StartTime":17275.0,"Objects":[{"StartTime":17275.0,"Position":144.0,"HyperDash":false}]},{"StartTime":17366.0,"Objects":[{"StartTime":17366.0,"Position":80.0,"HyperDash":false}]},{"StartTime":17457.0,"Objects":[{"StartTime":17457.0,"Position":144.0,"HyperDash":false}]},{"StartTime":17639.0,"Objects":[{"StartTime":17639.0,"Position":320.0,"HyperDash":false}]},{"StartTime":17821.0,"Objects":[{"StartTime":17821.0,"Position":400.0,"HyperDash":false}]},{"StartTime":17912.0,"Objects":[{"StartTime":17912.0,"Position":352.0,"HyperDash":false}]},{"StartTime":18003.0,"Objects":[{"StartTime":18003.0,"Position":304.0,"HyperDash":false}]},{"StartTime":18185.0,"Objects":[{"StartTime":18185.0,"Position":416.0,"HyperDash":false},{"StartTime":18275.0,"Position":406.779816,"HyperDash":false},{"StartTime":18366.0,"Position":431.646057,"HyperDash":false},{"StartTime":18439.0,"Position":420.6284,"HyperDash":false},{"StartTime":18548.0,"Position":353.58432,"HyperDash":false}]},{"StartTime":18639.0,"Objects":[{"StartTime":18639.0,"Position":400.0,"HyperDash":false}]},{"StartTime":18730.0,"Objects":[{"StartTime":18730.0,"Position":448.0,"HyperDash":false}]},{"StartTime":18912.0,"Objects":[{"StartTime":18912.0,"Position":368.0,"HyperDash":false}]},{"StartTime":19094.0,"Objects":[{"StartTime":19094.0,"Position":192.0,"HyperDash":false}]},{"StartTime":19185.0,"Objects":[{"StartTime":19185.0,"Position":144.0,"HyperDash":false}]},{"StartTime":19275.0,"Objects":[{"StartTime":19275.0,"Position":192.0,"HyperDash":false}]},{"StartTime":19457.0,"Objects":[{"StartTime":19457.0,"Position":304.0,"HyperDash":false}]},{"StartTime":19548.0,"Objects":[{"StartTime":19548.0,"Position":352.0,"HyperDash":false},{"StartTime":19638.0,"Position":352.0,"HyperDash":false}]},{"StartTime":19821.0,"Objects":[{"StartTime":19821.0,"Position":272.0,"HyperDash":false}]},{"StartTime":20003.0,"Objects":[{"StartTime":20003.0,"Position":384.0,"HyperDash":false}]},{"StartTime":20185.0,"Objects":[{"StartTime":20185.0,"Position":304.0,"HyperDash":false},{"StartTime":20275.0,"Position":304.0,"HyperDash":false}]},{"StartTime":20366.0,"Objects":[{"StartTime":20366.0,"Position":352.0,"HyperDash":false}]},{"StartTime":20548.0,"Objects":[{"StartTime":20548.0,"Position":176.0,"HyperDash":false}]},{"StartTime":20730.0,"Objects":[{"StartTime":20730.0,"Position":96.0,"HyperDash":false}]},{"StartTime":20821.0,"Objects":[{"StartTime":20821.0,"Position":144.0,"HyperDash":false}]},{"StartTime":20912.0,"Objects":[{"StartTime":20912.0,"Position":192.0,"HyperDash":false}]},{"StartTime":21094.0,"Objects":[{"StartTime":21094.0,"Position":80.0,"HyperDash":false},{"StartTime":21184.0,"Position":82.2201843,"HyperDash":false},{"StartTime":21275.0,"Position":64.35393,"HyperDash":false},{"StartTime":21348.0,"Position":98.3716049,"HyperDash":false},{"StartTime":21457.0,"Position":142.41568,"HyperDash":false}]},{"StartTime":21548.0,"Objects":[{"StartTime":21548.0,"Position":96.0,"HyperDash":false}]},{"StartTime":21639.0,"Objects":[{"StartTime":21639.0,"Position":48.0,"HyperDash":false}]},{"StartTime":21821.0,"Objects":[{"StartTime":21821.0,"Position":128.0,"HyperDash":false}]},{"StartTime":22003.0,"Objects":[{"StartTime":22003.0,"Position":304.0,"HyperDash":false}]},{"StartTime":22094.0,"Objects":[{"StartTime":22094.0,"Position":352.0,"HyperDash":false}]},{"StartTime":22185.0,"Objects":[{"StartTime":22185.0,"Position":304.0,"HyperDash":false}]},{"StartTime":22366.0,"Objects":[{"StartTime":22366.0,"Position":192.0,"HyperDash":false}]},{"StartTime":22457.0,"Objects":[{"StartTime":22457.0,"Position":144.0,"HyperDash":false},{"StartTime":22547.0,"Position":144.0,"HyperDash":false}]},{"StartTime":22730.0,"Objects":[{"StartTime":22730.0,"Position":224.0,"HyperDash":false},{"StartTime":22820.0,"Position":191.366974,"HyperDash":false},{"StartTime":22911.0,"Position":144.293579,"HyperDash":false},{"StartTime":23002.0,"Position":168.779816,"HyperDash":false},{"StartTime":23093.0,"Position":223.85321,"HyperDash":false},{"StartTime":23166.0,"Position":182.0,"HyperDash":false},{"StartTime":23275.0,"Position":144.0,"HyperDash":true}]},{"StartTime":23457.0,"Objects":[{"StartTime":23457.0,"Position":400.0,"HyperDash":false}]},{"StartTime":23639.0,"Objects":[{"StartTime":23639.0,"Position":480.0,"HyperDash":false},{"StartTime":23729.0,"Position":480.0,"HyperDash":false}]},{"StartTime":23821.0,"Objects":[{"StartTime":23821.0,"Position":432.0,"HyperDash":false}]},{"StartTime":24003.0,"Objects":[{"StartTime":24003.0,"Position":320.0,"HyperDash":true}]},{"StartTime":24185.0,"Objects":[{"StartTime":24185.0,"Position":64.0,"HyperDash":false},{"StartTime":24257.0,"Position":62.7589569,"HyperDash":false},{"StartTime":24366.0,"Position":48.3107071,"HyperDash":false}]},{"StartTime":24457.0,"Objects":[{"StartTime":24457.0,"Position":96.0,"HyperDash":false}]},{"StartTime":24548.0,"Objects":[{"StartTime":24548.0,"Position":144.0,"HyperDash":false}]},{"StartTime":24730.0,"Objects":[{"StartTime":24730.0,"Position":64.0,"HyperDash":false}]},{"StartTime":24912.0,"Objects":[{"StartTime":24912.0,"Position":240.0,"HyperDash":false}]},{"StartTime":25094.0,"Objects":[{"StartTime":25094.0,"Position":320.0,"HyperDash":false},{"StartTime":25184.0,"Position":360.0,"HyperDash":false},{"StartTime":25275.0,"Position":320.0,"HyperDash":false}]},{"StartTime":25457.0,"Objects":[{"StartTime":25457.0,"Position":208.0,"HyperDash":true}]},{"StartTime":25639.0,"Objects":[{"StartTime":25639.0,"Position":464.0,"HyperDash":false},{"StartTime":25711.0,"Position":466.758942,"HyperDash":false},{"StartTime":25820.0,"Position":448.3107,"HyperDash":false}]},{"StartTime":26003.0,"Objects":[{"StartTime":26003.0,"Position":336.0,"HyperDash":false},{"StartTime":26075.0,"Position":333.758942,"HyperDash":false},{"StartTime":26184.0,"Position":320.3107,"HyperDash":false}]},{"StartTime":26366.0,"Objects":[{"StartTime":26366.0,"Position":496.0,"HyperDash":false}]},{"StartTime":26548.0,"Objects":[{"StartTime":26548.0,"Position":416.0,"HyperDash":false},{"StartTime":26638.0,"Position":416.0,"HyperDash":false}]},{"StartTime":26730.0,"Objects":[{"StartTime":26730.0,"Position":464.0,"HyperDash":false}]},{"StartTime":26912.0,"Objects":[{"StartTime":26912.0,"Position":352.0,"HyperDash":true}]},{"StartTime":27094.0,"Objects":[{"StartTime":27094.0,"Position":96.0,"HyperDash":false},{"StartTime":27166.0,"Position":79.75896,"HyperDash":false},{"StartTime":27275.0,"Position":80.31071,"HyperDash":false}]},{"StartTime":27457.0,"Objects":[{"StartTime":27457.0,"Position":192.0,"HyperDash":false}]},{"StartTime":27548.0,"Objects":[{"StartTime":27548.0,"Position":240.0,"HyperDash":false},{"StartTime":27638.0,"Position":240.0,"HyperDash":false}]},{"StartTime":27821.0,"Objects":[{"StartTime":27821.0,"Position":64.0,"HyperDash":false}]},{"StartTime":28003.0,"Objects":[{"StartTime":28003.0,"Position":176.0,"HyperDash":false}]},{"StartTime":28185.0,"Objects":[{"StartTime":28185.0,"Position":64.0,"HyperDash":false}]},{"StartTime":28275.0,"Objects":[{"StartTime":28275.0,"Position":16.0,"HyperDash":false},{"StartTime":28365.0,"Position":16.0,"HyperDash":false}]},{"StartTime":28548.0,"Objects":[{"StartTime":28548.0,"Position":272.0,"HyperDash":false},{"StartTime":28620.0,"Position":293.8232,"HyperDash":false},{"StartTime":28729.0,"Position":352.0,"HyperDash":false}]},{"StartTime":28912.0,"Objects":[{"StartTime":28912.0,"Position":240.0,"HyperDash":false},{"StartTime":28984.0,"Position":215.176788,"HyperDash":false},{"StartTime":29093.0,"Position":160.0,"HyperDash":true}]},{"StartTime":29275.0,"Objects":[{"StartTime":29275.0,"Position":416.0,"HyperDash":false}]},{"StartTime":29457.0,"Objects":[{"StartTime":29457.0,"Position":496.0,"HyperDash":false},{"StartTime":29547.0,"Position":496.0,"HyperDash":false}]},{"StartTime":29639.0,"Objects":[{"StartTime":29639.0,"Position":448.0,"HyperDash":false}]},{"StartTime":29821.0,"Objects":[{"StartTime":29821.0,"Position":336.0,"HyperDash":true}]},{"StartTime":30003.0,"Objects":[{"StartTime":30003.0,"Position":80.0,"HyperDash":false},{"StartTime":30075.0,"Position":74.90608,"HyperDash":false},{"StartTime":30184.0,"Position":32.0,"HyperDash":false}]},{"StartTime":30275.0,"Objects":[{"StartTime":30275.0,"Position":32.0,"HyperDash":false}]},{"StartTime":30366.0,"Objects":[{"StartTime":30366.0,"Position":64.0,"HyperDash":false}]},{"StartTime":30548.0,"Objects":[{"StartTime":30548.0,"Position":144.0,"HyperDash":false}]},{"StartTime":30730.0,"Objects":[{"StartTime":30730.0,"Position":320.0,"HyperDash":false}]},{"StartTime":30912.0,"Objects":[{"StartTime":30912.0,"Position":240.0,"HyperDash":false},{"StartTime":31002.0,"Position":200.0,"HyperDash":false},{"StartTime":31093.0,"Position":240.0,"HyperDash":false}]},{"StartTime":31275.0,"Objects":[{"StartTime":31275.0,"Position":352.0,"HyperDash":true}]},{"StartTime":31457.0,"Objects":[{"StartTime":31457.0,"Position":96.0,"HyperDash":false},{"StartTime":31529.0,"Position":96.75896,"HyperDash":false},{"StartTime":31638.0,"Position":80.31071,"HyperDash":false}]},{"StartTime":31821.0,"Objects":[{"StartTime":31821.0,"Position":192.0,"HyperDash":false}]},{"StartTime":32003.0,"Objects":[{"StartTime":32003.0,"Position":80.0,"HyperDash":false}]},{"StartTime":32185.0,"Objects":[{"StartTime":32185.0,"Position":256.0,"HyperDash":false}]},{"StartTime":32366.0,"Objects":[{"StartTime":32366.0,"Position":336.0,"HyperDash":false},{"StartTime":32456.0,"Position":336.0,"HyperDash":false}]},{"StartTime":32548.0,"Objects":[{"StartTime":32548.0,"Position":288.0,"HyperDash":false}]},{"StartTime":32730.0,"Objects":[{"StartTime":32730.0,"Position":400.0,"HyperDash":true}]},{"StartTime":32912.0,"Objects":[{"StartTime":32912.0,"Position":144.0,"HyperDash":false},{"StartTime":32984.0,"Position":149.758957,"HyperDash":false},{"StartTime":33093.0,"Position":128.310715,"HyperDash":false}]},{"StartTime":33275.0,"Objects":[{"StartTime":33275.0,"Position":240.0,"HyperDash":false}]},{"StartTime":33366.0,"Objects":[{"StartTime":33366.0,"Position":288.0,"HyperDash":false}]},{"StartTime":33457.0,"Objects":[{"StartTime":33457.0,"Position":240.0,"HyperDash":false}]},{"StartTime":33639.0,"Objects":[{"StartTime":33639.0,"Position":128.0,"HyperDash":false}]},{"StartTime":33821.0,"Objects":[{"StartTime":33821.0,"Position":240.0,"HyperDash":false}]},{"StartTime":34003.0,"Objects":[{"StartTime":34003.0,"Position":128.0,"HyperDash":false}]},{"StartTime":34094.0,"Objects":[{"StartTime":34094.0,"Position":80.0,"HyperDash":false},{"StartTime":34184.0,"Position":80.0,"HyperDash":true}]},{"StartTime":34366.0,"Objects":[{"StartTime":34366.0,"Position":336.0,"HyperDash":false},{"StartTime":34438.0,"Position":369.8232,"HyperDash":false},{"StartTime":34547.0,"Position":416.0,"HyperDash":false}]},{"StartTime":34730.0,"Objects":[{"StartTime":34730.0,"Position":240.0,"HyperDash":false},{"StartTime":34802.0,"Position":189.176788,"HyperDash":false},{"StartTime":34911.0,"Position":160.0,"HyperDash":true}]},{"StartTime":35094.0,"Objects":[{"StartTime":35094.0,"Position":432.0,"HyperDash":false},{"StartTime":35184.0,"Position":432.0,"HyperDash":false}]},{"StartTime":35275.0,"Objects":[{"StartTime":35275.0,"Position":384.0,"HyperDash":false}]},{"StartTime":35457.0,"Objects":[{"StartTime":35457.0,"Position":208.0,"HyperDash":false},{"StartTime":35529.0,"Position":159.176788,"HyperDash":false},{"StartTime":35638.0,"Position":128.0,"HyperDash":false}]},{"StartTime":35821.0,"Objects":[{"StartTime":35821.0,"Position":384.0,"HyperDash":false}]},{"StartTime":36003.0,"Objects":[{"StartTime":36003.0,"Position":464.0,"HyperDash":false}]},{"StartTime":36094.0,"Objects":[{"StartTime":36094.0,"Position":384.0,"HyperDash":false}]},{"StartTime":36185.0,"Objects":[{"StartTime":36185.0,"Position":336.0,"HyperDash":false}]},{"StartTime":36366.0,"Objects":[{"StartTime":36366.0,"Position":448.0,"HyperDash":true}]},{"StartTime":36548.0,"Objects":[{"StartTime":36548.0,"Position":192.0,"HyperDash":false},{"StartTime":36638.0,"Position":152.0,"HyperDash":false},{"StartTime":36729.0,"Position":192.0,"HyperDash":false}]},{"StartTime":36912.0,"Objects":[{"StartTime":36912.0,"Position":368.0,"HyperDash":false}]},{"StartTime":37003.0,"Objects":[{"StartTime":37003.0,"Position":416.0,"HyperDash":false},{"StartTime":37093.0,"Position":416.0,"HyperDash":true}]},{"StartTime":37275.0,"Objects":[{"StartTime":37275.0,"Position":160.0,"HyperDash":false},{"StartTime":37347.0,"Position":156.758957,"HyperDash":false},{"StartTime":37456.0,"Position":144.310715,"HyperDash":false}]},{"StartTime":37548.0,"Objects":[{"StartTime":37548.0,"Position":192.0,"HyperDash":false}]},{"StartTime":37639.0,"Objects":[{"StartTime":37639.0,"Position":272.0,"HyperDash":false}]},{"StartTime":37821.0,"Objects":[{"StartTime":37821.0,"Position":160.0,"HyperDash":true}]},{"StartTime":38003.0,"Objects":[{"StartTime":38003.0,"Position":416.0,"HyperDash":false}]},{"StartTime":38185.0,"Objects":[{"StartTime":38185.0,"Position":496.0,"HyperDash":false},{"StartTime":38275.0,"Position":496.0,"HyperDash":false}]},{"StartTime":38366.0,"Objects":[{"StartTime":38366.0,"Position":416.0,"HyperDash":false}]},{"StartTime":38548.0,"Objects":[{"StartTime":38548.0,"Position":496.0,"HyperDash":true}]},{"StartTime":38730.0,"Objects":[{"StartTime":38730.0,"Position":240.0,"HyperDash":false}]},{"StartTime":38821.0,"Objects":[{"StartTime":38821.0,"Position":192.0,"HyperDash":false}]},{"StartTime":38912.0,"Objects":[{"StartTime":38912.0,"Position":240.0,"HyperDash":false}]},{"StartTime":39094.0,"Objects":[{"StartTime":39094.0,"Position":352.0,"HyperDash":false},{"StartTime":39166.0,"Position":328.1768,"HyperDash":false},{"StartTime":39275.0,"Position":272.0,"HyperDash":true}]},{"StartTime":39457.0,"Objects":[{"StartTime":39457.0,"Position":16.0,"HyperDash":false},{"StartTime":39547.0,"Position":16.0,"HyperDash":false}]},{"StartTime":39639.0,"Objects":[{"StartTime":39639.0,"Position":64.0,"HyperDash":false}]},{"StartTime":39821.0,"Objects":[{"StartTime":39821.0,"Position":240.0,"HyperDash":false},{"StartTime":39911.0,"Position":211.715729,"HyperDash":false}]},{"StartTime":40003.0,"Objects":[{"StartTime":40003.0,"Position":160.0,"HyperDash":true}]},{"StartTime":40185.0,"Objects":[{"StartTime":40185.0,"Position":416.0,"HyperDash":false}]},{"StartTime":40275.0,"Objects":[{"StartTime":40275.0,"Position":464.0,"HyperDash":false}]},{"StartTime":40366.0,"Objects":[{"StartTime":40366.0,"Position":416.0,"HyperDash":false}]},{"StartTime":40548.0,"Objects":[{"StartTime":40548.0,"Position":240.0,"HyperDash":false}]},{"StartTime":40639.0,"Objects":[{"StartTime":40639.0,"Position":288.0,"HyperDash":false}]},{"StartTime":40730.0,"Objects":[{"StartTime":40730.0,"Position":336.0,"HyperDash":true}]},{"StartTime":40912.0,"Objects":[{"StartTime":40912.0,"Position":64.0,"HyperDash":false},{"StartTime":41002.0,"Position":64.0,"HyperDash":false}]},{"StartTime":41094.0,"Objects":[{"StartTime":41094.0,"Position":112.0,"HyperDash":false}]},{"StartTime":41275.0,"Objects":[{"StartTime":41275.0,"Position":288.0,"HyperDash":false},{"StartTime":41347.0,"Position":312.8232,"HyperDash":false},{"StartTime":41456.0,"Position":368.0,"HyperDash":false}]},{"StartTime":41639.0,"Objects":[{"StartTime":41639.0,"Position":112.0,"HyperDash":false}]},{"StartTime":41821.0,"Objects":[{"StartTime":41821.0,"Position":32.0,"HyperDash":false}]},{"StartTime":41912.0,"Objects":[{"StartTime":41912.0,"Position":112.0,"HyperDash":false}]},{"StartTime":42003.0,"Objects":[{"StartTime":42003.0,"Position":160.0,"HyperDash":false}]},{"StartTime":42185.0,"Objects":[{"StartTime":42185.0,"Position":48.0,"HyperDash":true}]},{"StartTime":42366.0,"Objects":[{"StartTime":42366.0,"Position":304.0,"HyperDash":false},{"StartTime":42438.0,"Position":338.8232,"HyperDash":false},{"StartTime":42547.0,"Position":384.0,"HyperDash":false}]},{"StartTime":42730.0,"Objects":[{"StartTime":42730.0,"Position":208.0,"HyperDash":false},{"StartTime":42802.0,"Position":174.176788,"HyperDash":false},{"StartTime":42911.0,"Position":128.0,"HyperDash":false}]},{"StartTime":43094.0,"Objects":[{"StartTime":43094.0,"Position":384.0,"HyperDash":false},{"StartTime":43166.0,"Position":407.241058,"HyperDash":false},{"StartTime":43275.0,"Position":399.6893,"HyperDash":false}]},{"StartTime":43366.0,"Objects":[{"StartTime":43366.0,"Position":352.0,"HyperDash":false}]},{"StartTime":43457.0,"Objects":[{"StartTime":43457.0,"Position":272.0,"HyperDash":false}]},{"StartTime":43639.0,"Objects":[{"StartTime":43639.0,"Position":384.0,"HyperDash":true}]},{"StartTime":43821.0,"Objects":[{"StartTime":43821.0,"Position":128.0,"HyperDash":false}]},{"StartTime":44003.0,"Objects":[{"StartTime":44003.0,"Position":48.0,"HyperDash":false},{"StartTime":44093.0,"Position":48.0,"HyperDash":false}]},{"StartTime":44185.0,"Objects":[{"StartTime":44185.0,"Position":128.0,"HyperDash":false}]},{"StartTime":44366.0,"Objects":[{"StartTime":44366.0,"Position":48.0,"HyperDash":true}]},{"StartTime":44548.0,"Objects":[{"StartTime":44548.0,"Position":304.0,"HyperDash":false}]},{"StartTime":44730.0,"Objects":[{"StartTime":44730.0,"Position":384.0,"HyperDash":false}]},{"StartTime":44821.0,"Objects":[{"StartTime":44821.0,"Position":336.0,"HyperDash":false}]},{"StartTime":44912.0,"Objects":[{"StartTime":44912.0,"Position":256.0,"HyperDash":false}]},{"StartTime":45094.0,"Objects":[{"StartTime":45094.0,"Position":368.0,"HyperDash":true}]},{"StartTime":45275.0,"Objects":[{"StartTime":45275.0,"Position":112.0,"HyperDash":false}]},{"StartTime":45366.0,"Objects":[{"StartTime":45366.0,"Position":64.0,"HyperDash":false}]},{"StartTime":45457.0,"Objects":[{"StartTime":45457.0,"Position":112.0,"HyperDash":false}]},{"StartTime":45639.0,"Objects":[{"StartTime":45639.0,"Position":288.0,"HyperDash":false}]},{"StartTime":45730.0,"Objects":[{"StartTime":45730.0,"Position":336.0,"HyperDash":false},{"StartTime":45820.0,"Position":336.0,"HyperDash":false}]},{"StartTime":46003.0,"Objects":[{"StartTime":46003.0,"Position":80.0,"HyperDash":false},{"StartTime":46093.0,"Position":80.0,"HyperDash":false}]},{"StartTime":46185.0,"Objects":[{"StartTime":46185.0,"Position":128.0,"HyperDash":false}]},{"StartTime":46366.0,"Objects":[{"StartTime":46366.0,"Position":304.0,"HyperDash":false}]},{"StartTime":46457.0,"Objects":[{"StartTime":46457.0,"Position":256.0,"HyperDash":false}]},{"StartTime":46548.0,"Objects":[{"StartTime":46548.0,"Position":208.0,"HyperDash":true}]},{"StartTime":46730.0,"Objects":[{"StartTime":46730.0,"Position":464.0,"HyperDash":false}]},{"StartTime":46912.0,"Objects":[{"StartTime":46912.0,"Position":45.0,"HyperDash":false},{"StartTime":46997.0,"Position":397.0,"HyperDash":false},{"StartTime":47082.0,"Position":342.0,"HyperDash":false},{"StartTime":47167.0,"Position":163.0,"HyperDash":false},{"StartTime":47252.0,"Position":278.0,"HyperDash":false},{"StartTime":47338.0,"Position":220.0,"HyperDash":false},{"StartTime":47423.0,"Position":253.0,"HyperDash":false},{"StartTime":47508.0,"Position":233.0,"HyperDash":false},{"StartTime":47593.0,"Position":97.0,"HyperDash":false},{"StartTime":47678.0,"Position":473.0,"HyperDash":false},{"StartTime":47764.0,"Position":189.0,"HyperDash":false},{"StartTime":47849.0,"Position":194.0,"HyperDash":false},{"StartTime":47934.0,"Position":107.0,"HyperDash":false},{"StartTime":48019.0,"Position":21.0,"HyperDash":false},{"StartTime":48105.0,"Position":461.0,"HyperDash":false},{"StartTime":48190.0,"Position":498.0,"HyperDash":false},{"StartTime":48275.0,"Position":184.0,"HyperDash":false},{"StartTime":48360.0,"Position":78.0,"HyperDash":false},{"StartTime":48445.0,"Position":338.0,"HyperDash":false},{"StartTime":48531.0,"Position":392.0,"HyperDash":false},{"StartTime":48616.0,"Position":335.0,"HyperDash":false},{"StartTime":48701.0,"Position":193.0,"HyperDash":false},{"StartTime":48786.0,"Position":478.0,"HyperDash":false},{"StartTime":48872.0,"Position":255.0,"HyperDash":false},{"StartTime":48957.0,"Position":175.0,"HyperDash":false},{"StartTime":49042.0,"Position":274.0,"HyperDash":false},{"StartTime":49127.0,"Position":442.0,"HyperDash":false},{"StartTime":49212.0,"Position":295.0,"HyperDash":false},{"StartTime":49298.0,"Position":311.0,"HyperDash":false},{"StartTime":49383.0,"Position":17.0,"HyperDash":false},{"StartTime":49468.0,"Position":467.0,"HyperDash":false},{"StartTime":49553.0,"Position":30.0,"HyperDash":false},{"StartTime":49639.0,"Position":218.0,"HyperDash":false}]},{"StartTime":52548.0,"Objects":[{"StartTime":52548.0,"Position":200.0,"HyperDash":false},{"StartTime":52620.0,"Position":175.758957,"HyperDash":false},{"StartTime":52729.0,"Position":184.310715,"HyperDash":false}]},{"StartTime":52912.0,"Objects":[{"StartTime":52912.0,"Position":280.0,"HyperDash":false},{"StartTime":52984.0,"Position":269.758942,"HyperDash":false},{"StartTime":53093.0,"Position":264.3107,"HyperDash":false}]},{"StartTime":53457.0,"Objects":[{"StartTime":53457.0,"Position":104.0,"HyperDash":false}]},{"StartTime":53639.0,"Objects":[{"StartTime":53639.0,"Position":184.0,"HyperDash":false}]},{"StartTime":54003.0,"Objects":[{"StartTime":54003.0,"Position":344.0,"HyperDash":false},{"StartTime":54075.0,"Position":333.241058,"HyperDash":false},{"StartTime":54184.0,"Position":359.6893,"HyperDash":false}]},{"StartTime":54366.0,"Objects":[{"StartTime":54366.0,"Position":256.0,"HyperDash":false},{"StartTime":54438.0,"Position":273.241058,"HyperDash":false},{"StartTime":54547.0,"Position":271.6893,"HyperDash":false}]},{"StartTime":54912.0,"Objects":[{"StartTime":54912.0,"Position":448.0,"HyperDash":false}]},{"StartTime":55094.0,"Objects":[{"StartTime":55094.0,"Position":360.0,"HyperDash":false}]},{"StartTime":55457.0,"Objects":[{"StartTime":55457.0,"Position":176.0,"HyperDash":false},{"StartTime":55529.0,"Position":150.758957,"HyperDash":false},{"StartTime":55638.0,"Position":160.310715,"HyperDash":false}]},{"StartTime":55821.0,"Objects":[{"StartTime":55821.0,"Position":272.0,"HyperDash":false},{"StartTime":55893.0,"Position":282.758942,"HyperDash":false},{"StartTime":56002.0,"Position":256.3107,"HyperDash":false}]},{"StartTime":56366.0,"Objects":[{"StartTime":56366.0,"Position":64.0,"HyperDash":false}]},{"StartTime":56548.0,"Objects":[{"StartTime":56548.0,"Position":160.0,"HyperDash":false}]},{"StartTime":56912.0,"Objects":[{"StartTime":56912.0,"Position":368.0,"HyperDash":false},{"StartTime":56984.0,"Position":383.241058,"HyperDash":false},{"StartTime":57093.0,"Position":383.6893,"HyperDash":false}]},{"StartTime":57275.0,"Objects":[{"StartTime":57275.0,"Position":264.0,"HyperDash":false},{"StartTime":57347.0,"Position":270.241058,"HyperDash":false},{"StartTime":57456.0,"Position":279.6893,"HyperDash":false}]},{"StartTime":57639.0,"Objects":[{"StartTime":57639.0,"Position":400.0,"HyperDash":false},{"StartTime":57711.0,"Position":367.1768,"HyperDash":false},{"StartTime":57820.0,"Position":320.0,"HyperDash":false}]},{"StartTime":58003.0,"Objects":[{"StartTime":58003.0,"Position":464.0,"HyperDash":false}]},{"StartTime":58185.0,"Objects":[{"StartTime":58185.0,"Position":320.0,"HyperDash":false}]},{"StartTime":58366.0,"Objects":[{"StartTime":58366.0,"Position":144.0,"HyperDash":false}]},{"StartTime":58548.0,"Objects":[{"StartTime":58548.0,"Position":224.0,"HyperDash":false},{"StartTime":58638.0,"Position":264.0,"HyperDash":false},{"StartTime":58729.0,"Position":224.0,"HyperDash":false}]},{"StartTime":58912.0,"Objects":[{"StartTime":58912.0,"Position":144.0,"HyperDash":false}]},{"StartTime":59094.0,"Objects":[{"StartTime":59094.0,"Position":16.0,"HyperDash":false},{"StartTime":59184.0,"Position":16.0,"HyperDash":false}]},{"StartTime":59275.0,"Objects":[{"StartTime":59275.0,"Position":64.0,"HyperDash":false}]},{"StartTime":59457.0,"Objects":[{"StartTime":59457.0,"Position":144.0,"HyperDash":false}]},{"StartTime":59639.0,"Objects":[{"StartTime":59639.0,"Position":64.0,"HyperDash":false}]},{"StartTime":59821.0,"Objects":[{"StartTime":59821.0,"Position":240.0,"HyperDash":false},{"StartTime":59911.0,"Position":240.0,"HyperDash":false}]},{"StartTime":60003.0,"Objects":[{"StartTime":60003.0,"Position":192.0,"HyperDash":false}]},{"StartTime":60185.0,"Objects":[{"StartTime":60185.0,"Position":80.0,"HyperDash":false}]},{"StartTime":60275.0,"Objects":[{"StartTime":60275.0,"Position":128.0,"HyperDash":false}]},{"StartTime":60366.0,"Objects":[{"StartTime":60366.0,"Position":176.0,"HyperDash":false}]},{"StartTime":60548.0,"Objects":[{"StartTime":60548.0,"Position":64.0,"HyperDash":false},{"StartTime":60638.0,"Position":64.0,"HyperDash":false}]},{"StartTime":60730.0,"Objects":[{"StartTime":60730.0,"Position":112.0,"HyperDash":false}]},{"StartTime":60912.0,"Objects":[{"StartTime":60912.0,"Position":224.0,"HyperDash":false},{"StartTime":60984.0,"Position":204.176788,"HyperDash":false},{"StartTime":61093.0,"Position":144.0,"HyperDash":false}]},{"StartTime":61275.0,"Objects":[{"StartTime":61275.0,"Position":320.0,"HyperDash":false}]},{"StartTime":61457.0,"Objects":[{"StartTime":61457.0,"Position":400.0,"HyperDash":false},{"StartTime":61547.0,"Position":400.0,"HyperDash":false}]},{"StartTime":61639.0,"Objects":[{"StartTime":61639.0,"Position":352.0,"HyperDash":false}]},{"StartTime":61821.0,"Objects":[{"StartTime":61821.0,"Position":432.0,"HyperDash":false}]},{"StartTime":62003.0,"Objects":[{"StartTime":62003.0,"Position":320.0,"HyperDash":false},{"StartTime":62093.0,"Position":320.0,"HyperDash":false}]},{"StartTime":62185.0,"Objects":[{"StartTime":62185.0,"Position":368.0,"HyperDash":false}]},{"StartTime":62366.0,"Objects":[{"StartTime":62366.0,"Position":448.0,"HyperDash":false}]},{"StartTime":62548.0,"Objects":[{"StartTime":62548.0,"Position":368.0,"HyperDash":false}]},{"StartTime":62730.0,"Objects":[{"StartTime":62730.0,"Position":192.0,"HyperDash":false}]},{"StartTime":62912.0,"Objects":[{"StartTime":62912.0,"Position":272.0,"HyperDash":false}]},{"StartTime":63094.0,"Objects":[{"StartTime":63094.0,"Position":192.0,"HyperDash":false},{"StartTime":63184.0,"Position":152.0,"HyperDash":false},{"StartTime":63275.0,"Position":192.0,"HyperDash":false}]},{"StartTime":63457.0,"Objects":[{"StartTime":63457.0,"Position":304.0,"HyperDash":false},{"StartTime":63529.0,"Position":274.1768,"HyperDash":false},{"StartTime":63638.0,"Position":224.0,"HyperDash":false}]},{"StartTime":63821.0,"Objects":[{"StartTime":63821.0,"Position":112.0,"HyperDash":false},{"StartTime":63893.0,"Position":144.823212,"HyperDash":false},{"StartTime":64002.0,"Position":192.0,"HyperDash":false}]},{"StartTime":64185.0,"Objects":[{"StartTime":64185.0,"Position":368.0,"HyperDash":false}]},{"StartTime":64366.0,"Objects":[{"StartTime":64366.0,"Position":288.0,"HyperDash":false},{"StartTime":64456.0,"Position":248.0,"HyperDash":false},{"StartTime":64547.0,"Position":288.0,"HyperDash":false}]},{"StartTime":64730.0,"Objects":[{"StartTime":64730.0,"Position":368.0,"HyperDash":false}]},{"StartTime":64912.0,"Objects":[{"StartTime":64912.0,"Position":448.0,"HyperDash":false}]},{"StartTime":65094.0,"Objects":[{"StartTime":65094.0,"Position":368.0,"HyperDash":false},{"StartTime":65184.0,"Position":328.0,"HyperDash":false},{"StartTime":65275.0,"Position":368.0,"HyperDash":false}]},{"StartTime":65457.0,"Objects":[{"StartTime":65457.0,"Position":448.0,"HyperDash":false}]},{"StartTime":65639.0,"Objects":[{"StartTime":65639.0,"Position":272.0,"HyperDash":false},{"StartTime":65729.0,"Position":272.0,"HyperDash":false}]},{"StartTime":65821.0,"Objects":[{"StartTime":65821.0,"Position":320.0,"HyperDash":false}]},{"StartTime":66003.0,"Objects":[{"StartTime":66003.0,"Position":432.0,"HyperDash":false}]},{"StartTime":66094.0,"Objects":[{"StartTime":66094.0,"Position":384.0,"HyperDash":false}]},{"StartTime":66185.0,"Objects":[{"StartTime":66185.0,"Position":336.0,"HyperDash":false}]},{"StartTime":66366.0,"Objects":[{"StartTime":66366.0,"Position":224.0,"HyperDash":false}]},{"StartTime":66457.0,"Objects":[{"StartTime":66457.0,"Position":272.0,"HyperDash":false}]},{"StartTime":66548.0,"Objects":[{"StartTime":66548.0,"Position":320.0,"HyperDash":false}]},{"StartTime":66730.0,"Objects":[{"StartTime":66730.0,"Position":432.0,"HyperDash":false}]},{"StartTime":66912.0,"Objects":[{"StartTime":66912.0,"Position":320.0,"HyperDash":false}]},{"StartTime":67094.0,"Objects":[{"StartTime":67094.0,"Position":144.0,"HyperDash":false}]},{"StartTime":67275.0,"Objects":[{"StartTime":67275.0,"Position":64.0,"HyperDash":false},{"StartTime":67365.0,"Position":64.0,"HyperDash":false}]},{"StartTime":67457.0,"Objects":[{"StartTime":67457.0,"Position":112.0,"HyperDash":false}]},{"StartTime":67639.0,"Objects":[{"StartTime":67639.0,"Position":192.0,"HyperDash":false}]},{"StartTime":67821.0,"Objects":[{"StartTime":67821.0,"Position":80.0,"HyperDash":false},{"StartTime":67911.0,"Position":92.64911,"HyperDash":false}]},{"StartTime":68003.0,"Objects":[{"StartTime":68003.0,"Position":144.0,"HyperDash":false}]},{"StartTime":68185.0,"Objects":[{"StartTime":68185.0,"Position":224.0,"HyperDash":false}]},{"StartTime":68366.0,"Objects":[{"StartTime":68366.0,"Position":144.0,"HyperDash":false}]},{"StartTime":68548.0,"Objects":[{"StartTime":68548.0,"Position":320.0,"HyperDash":false}]},{"StartTime":68730.0,"Objects":[{"StartTime":68730.0,"Position":400.0,"HyperDash":false},{"StartTime":68820.0,"Position":407.844635,"HyperDash":false}]},{"StartTime":69003.0,"Objects":[{"StartTime":69003.0,"Position":296.0,"HyperDash":false},{"StartTime":69093.0,"Position":256.0,"HyperDash":false}]},{"StartTime":69275.0,"Objects":[{"StartTime":69275.0,"Position":368.0,"HyperDash":false}]},{"StartTime":69457.0,"Objects":[{"StartTime":69457.0,"Position":256.0,"HyperDash":false}]},{"StartTime":69639.0,"Objects":[{"StartTime":69639.0,"Position":80.0,"HyperDash":false}]},{"StartTime":69821.0,"Objects":[{"StartTime":69821.0,"Position":192.0,"HyperDash":true}]},{"StartTime":70003.0,"Objects":[{"StartTime":70003.0,"Position":448.0,"HyperDash":false}]},{"StartTime":75821.0,"Objects":[{"StartTime":75821.0,"Position":160.0,"HyperDash":false},{"StartTime":75893.0,"Position":115.176788,"HyperDash":false},{"StartTime":76002.0,"Position":80.0,"HyperDash":false}]},{"StartTime":76185.0,"Objects":[{"StartTime":76185.0,"Position":160.0,"HyperDash":false},{"StartTime":76257.0,"Position":131.176788,"HyperDash":false},{"StartTime":76366.0,"Position":80.0,"HyperDash":false}]},{"StartTime":76548.0,"Objects":[{"StartTime":76548.0,"Position":160.0,"HyperDash":false}]},{"StartTime":76730.0,"Objects":[{"StartTime":76730.0,"Position":240.0,"HyperDash":false}]},{"StartTime":76912.0,"Objects":[{"StartTime":76912.0,"Position":240.0,"HyperDash":false}]},{"StartTime":77094.0,"Objects":[{"StartTime":77094.0,"Position":240.0,"HyperDash":false}]},{"StartTime":77275.0,"Objects":[{"StartTime":77275.0,"Position":368.0,"HyperDash":false},{"StartTime":77347.0,"Position":387.8232,"HyperDash":false},{"StartTime":77456.0,"Position":448.0,"HyperDash":false}]},{"StartTime":77639.0,"Objects":[{"StartTime":77639.0,"Position":368.0,"HyperDash":false},{"StartTime":77711.0,"Position":392.8232,"HyperDash":false},{"StartTime":77820.0,"Position":448.0,"HyperDash":false}]},{"StartTime":78003.0,"Objects":[{"StartTime":78003.0,"Position":352.0,"HyperDash":false}]},{"StartTime":78185.0,"Objects":[{"StartTime":78185.0,"Position":256.0,"HyperDash":false}]},{"StartTime":78366.0,"Objects":[{"StartTime":78366.0,"Position":256.0,"HyperDash":false}]},{"StartTime":78548.0,"Objects":[{"StartTime":78548.0,"Position":352.0,"HyperDash":false}]},{"StartTime":78730.0,"Objects":[{"StartTime":78730.0,"Position":176.0,"HyperDash":false},{"StartTime":78802.0,"Position":125.176788,"HyperDash":false},{"StartTime":78911.0,"Position":96.0,"HyperDash":false}]},{"StartTime":79094.0,"Objects":[{"StartTime":79094.0,"Position":176.0,"HyperDash":false},{"StartTime":79166.0,"Position":146.176788,"HyperDash":false},{"StartTime":79275.0,"Position":96.0,"HyperDash":false}]},{"StartTime":79457.0,"Objects":[{"StartTime":79457.0,"Position":192.0,"HyperDash":false}]},{"StartTime":79639.0,"Objects":[{"StartTime":79639.0,"Position":288.0,"HyperDash":false}]},{"StartTime":79821.0,"Objects":[{"StartTime":79821.0,"Position":192.0,"HyperDash":false}]},{"StartTime":80003.0,"Objects":[{"StartTime":80003.0,"Position":288.0,"HyperDash":false}]},{"StartTime":80185.0,"Objects":[{"StartTime":80185.0,"Position":194.0,"HyperDash":false},{"StartTime":80253.0,"Position":234.0,"HyperDash":false},{"StartTime":80321.0,"Position":179.0,"HyperDash":false},{"StartTime":80389.0,"Position":278.0,"HyperDash":false},{"StartTime":80457.0,"Position":474.0,"HyperDash":false},{"StartTime":80525.0,"Position":50.0,"HyperDash":false},{"StartTime":80593.0,"Position":458.0,"HyperDash":false},{"StartTime":80661.0,"Position":425.0,"HyperDash":false},{"StartTime":80730.0,"Position":466.0,"HyperDash":false},{"StartTime":80798.0,"Position":56.0,"HyperDash":false},{"StartTime":80866.0,"Position":109.0,"HyperDash":false},{"StartTime":80934.0,"Position":482.0,"HyperDash":false},{"StartTime":81002.0,"Position":147.0,"HyperDash":false},{"StartTime":81070.0,"Position":285.0,"HyperDash":false},{"StartTime":81138.0,"Position":452.0,"HyperDash":false},{"StartTime":81206.0,"Position":419.0,"HyperDash":false},{"StartTime":81275.0,"Position":269.0,"HyperDash":false}]},{"StartTime":81639.0,"Objects":[{"StartTime":81639.0,"Position":416.0,"HyperDash":false}]},{"StartTime":81821.0,"Objects":[{"StartTime":81821.0,"Position":336.0,"HyperDash":false},{"StartTime":81911.0,"Position":307.715729,"HyperDash":false},{"StartTime":82002.0,"Position":336.0,"HyperDash":false}]},{"StartTime":82185.0,"Objects":[{"StartTime":82185.0,"Position":448.0,"HyperDash":false}]},{"StartTime":82548.0,"Objects":[{"StartTime":82548.0,"Position":288.0,"HyperDash":false},{"StartTime":82638.0,"Position":259.715729,"HyperDash":false},{"StartTime":82729.0,"Position":288.0,"HyperDash":false}]},{"StartTime":82912.0,"Objects":[{"StartTime":82912.0,"Position":368.0,"HyperDash":false}]},{"StartTime":83094.0,"Objects":[{"StartTime":83094.0,"Position":192.0,"HyperDash":false}]},{"StartTime":83185.0,"Objects":[{"StartTime":83185.0,"Position":144.0,"HyperDash":false}]},{"StartTime":83275.0,"Objects":[{"StartTime":83275.0,"Position":192.0,"HyperDash":false}]},{"StartTime":83457.0,"Objects":[{"StartTime":83457.0,"Position":304.0,"HyperDash":false}]},{"StartTime":83548.0,"Objects":[{"StartTime":83548.0,"Position":352.0,"HyperDash":false},{"StartTime":83638.0,"Position":352.0,"HyperDash":false}]},{"StartTime":83821.0,"Objects":[{"StartTime":83821.0,"Position":272.0,"HyperDash":false}]},{"StartTime":84003.0,"Objects":[{"StartTime":84003.0,"Position":384.0,"HyperDash":false}]},{"StartTime":84185.0,"Objects":[{"StartTime":84185.0,"Position":304.0,"HyperDash":false},{"StartTime":84275.0,"Position":304.0,"HyperDash":false}]},{"StartTime":84366.0,"Objects":[{"StartTime":84366.0,"Position":352.0,"HyperDash":false}]},{"StartTime":84548.0,"Objects":[{"StartTime":84548.0,"Position":176.0,"HyperDash":false}]},{"StartTime":84730.0,"Objects":[{"StartTime":84730.0,"Position":256.0,"HyperDash":false},{"StartTime":84820.0,"Position":284.284271,"HyperDash":false},{"StartTime":84911.0,"Position":256.0,"HyperDash":false}]},{"StartTime":85094.0,"Objects":[{"StartTime":85094.0,"Position":144.0,"HyperDash":false}]},{"StartTime":85457.0,"Objects":[{"StartTime":85457.0,"Position":304.0,"HyperDash":false},{"StartTime":85547.0,"Position":332.284271,"HyperDash":false},{"StartTime":85638.0,"Position":304.0,"HyperDash":false}]},{"StartTime":85821.0,"Objects":[{"StartTime":85821.0,"Position":224.0,"HyperDash":false}]},{"StartTime":86003.0,"Objects":[{"StartTime":86003.0,"Position":400.0,"HyperDash":false}]},{"StartTime":86094.0,"Objects":[{"StartTime":86094.0,"Position":448.0,"HyperDash":false}]},{"StartTime":86185.0,"Objects":[{"StartTime":86185.0,"Position":400.0,"HyperDash":false}]},{"StartTime":86366.0,"Objects":[{"StartTime":86366.0,"Position":288.0,"HyperDash":false}]},{"StartTime":86457.0,"Objects":[{"StartTime":86457.0,"Position":240.0,"HyperDash":false},{"StartTime":86547.0,"Position":240.0,"HyperDash":false}]},{"StartTime":86730.0,"Objects":[{"StartTime":86730.0,"Position":352.0,"HyperDash":false}]},{"StartTime":86821.0,"Objects":[{"StartTime":86821.0,"Position":304.0,"HyperDash":false}]},{"StartTime":86912.0,"Objects":[{"StartTime":86912.0,"Position":256.0,"HyperDash":false}]},{"StartTime":87094.0,"Objects":[{"StartTime":87094.0,"Position":368.0,"HyperDash":false}]},{"StartTime":87185.0,"Objects":[{"StartTime":87185.0,"Position":432.0,"HyperDash":false}]},{"StartTime":87275.0,"Objects":[{"StartTime":87275.0,"Position":368.0,"HyperDash":false}]},{"StartTime":87457.0,"Objects":[{"StartTime":87457.0,"Position":192.0,"HyperDash":false}]},{"StartTime":87639.0,"Objects":[{"StartTime":87639.0,"Position":112.0,"HyperDash":false}]},{"StartTime":87730.0,"Objects":[{"StartTime":87730.0,"Position":160.0,"HyperDash":false}]},{"StartTime":87821.0,"Objects":[{"StartTime":87821.0,"Position":208.0,"HyperDash":false}]},{"StartTime":88003.0,"Objects":[{"StartTime":88003.0,"Position":96.0,"HyperDash":false},{"StartTime":88093.0,"Position":87.2201843,"HyperDash":false},{"StartTime":88184.0,"Position":80.35393,"HyperDash":false},{"StartTime":88257.0,"Position":98.3716049,"HyperDash":false},{"StartTime":88366.0,"Position":158.41568,"HyperDash":false}]},{"StartTime":88457.0,"Objects":[{"StartTime":88457.0,"Position":112.0,"HyperDash":false}]},{"StartTime":88548.0,"Objects":[{"StartTime":88548.0,"Position":64.0,"HyperDash":false}]},{"StartTime":88730.0,"Objects":[{"StartTime":88730.0,"Position":144.0,"HyperDash":false}]},{"StartTime":88912.0,"Objects":[{"StartTime":88912.0,"Position":320.0,"HyperDash":false}]},{"StartTime":89003.0,"Objects":[{"StartTime":89003.0,"Position":368.0,"HyperDash":false}]},{"StartTime":89094.0,"Objects":[{"StartTime":89094.0,"Position":320.0,"HyperDash":false}]},{"StartTime":89275.0,"Objects":[{"StartTime":89275.0,"Position":208.0,"HyperDash":false}]},{"StartTime":89366.0,"Objects":[{"StartTime":89366.0,"Position":160.0,"HyperDash":false},{"StartTime":89456.0,"Position":160.0,"HyperDash":false}]},{"StartTime":89639.0,"Objects":[{"StartTime":89639.0,"Position":240.0,"HyperDash":false}]},{"StartTime":89821.0,"Objects":[{"StartTime":89821.0,"Position":128.0,"HyperDash":false}]},{"StartTime":90003.0,"Objects":[{"StartTime":90003.0,"Position":208.0,"HyperDash":false},{"StartTime":90093.0,"Position":208.0,"HyperDash":false}]},{"StartTime":90185.0,"Objects":[{"StartTime":90185.0,"Position":160.0,"HyperDash":false}]},{"StartTime":90366.0,"Objects":[{"StartTime":90366.0,"Position":336.0,"HyperDash":false}]},{"StartTime":90548.0,"Objects":[{"StartTime":90548.0,"Position":416.0,"HyperDash":false}]},{"StartTime":90639.0,"Objects":[{"StartTime":90639.0,"Position":368.0,"HyperDash":false}]},{"StartTime":90730.0,"Objects":[{"StartTime":90730.0,"Position":320.0,"HyperDash":false}]},{"StartTime":90912.0,"Objects":[{"StartTime":90912.0,"Position":432.0,"HyperDash":false},{"StartTime":91002.0,"Position":446.779816,"HyperDash":false},{"StartTime":91093.0,"Position":447.646057,"HyperDash":false},{"StartTime":91166.0,"Position":416.6284,"HyperDash":false},{"StartTime":91275.0,"Position":369.58432,"HyperDash":false}]},{"StartTime":91366.0,"Objects":[{"StartTime":91366.0,"Position":416.0,"HyperDash":false}]},{"StartTime":91457.0,"Objects":[{"StartTime":91457.0,"Position":464.0,"HyperDash":false}]},{"StartTime":91639.0,"Objects":[{"StartTime":91639.0,"Position":384.0,"HyperDash":false}]},{"StartTime":91821.0,"Objects":[{"StartTime":91821.0,"Position":208.0,"HyperDash":false}]},{"StartTime":91912.0,"Objects":[{"StartTime":91912.0,"Position":160.0,"HyperDash":false}]},{"StartTime":92003.0,"Objects":[{"StartTime":92003.0,"Position":208.0,"HyperDash":false}]},{"StartTime":92185.0,"Objects":[{"StartTime":92185.0,"Position":320.0,"HyperDash":false}]},{"StartTime":92275.0,"Objects":[{"StartTime":92275.0,"Position":368.0,"HyperDash":false},{"StartTime":92365.0,"Position":368.0,"HyperDash":false}]},{"StartTime":92548.0,"Objects":[{"StartTime":92548.0,"Position":288.0,"HyperDash":false},{"StartTime":92638.0,"Position":241.366974,"HyperDash":false},{"StartTime":92729.0,"Position":208.293579,"HyperDash":false},{"StartTime":92820.0,"Position":262.779816,"HyperDash":false},{"StartTime":92911.0,"Position":287.8532,"HyperDash":false},{"StartTime":92984.0,"Position":304.0,"HyperDash":false},{"StartTime":93093.0,"Position":368.0,"HyperDash":true}]},{"StartTime":93275.0,"Objects":[{"StartTime":93275.0,"Position":112.0,"HyperDash":false}]},{"StartTime":93457.0,"Objects":[{"StartTime":93457.0,"Position":32.0,"HyperDash":false},{"StartTime":93547.0,"Position":32.0,"HyperDash":false}]},{"StartTime":93639.0,"Objects":[{"StartTime":93639.0,"Position":80.0,"HyperDash":false}]},{"StartTime":93821.0,"Objects":[{"StartTime":93821.0,"Position":192.0,"HyperDash":true}]},{"StartTime":94003.0,"Objects":[{"StartTime":94003.0,"Position":448.0,"HyperDash":false},{"StartTime":94075.0,"Position":436.241058,"HyperDash":false},{"StartTime":94184.0,"Position":463.6893,"HyperDash":false}]},{"StartTime":94275.0,"Objects":[{"StartTime":94275.0,"Position":416.0,"HyperDash":false}]},{"StartTime":94366.0,"Objects":[{"StartTime":94366.0,"Position":368.0,"HyperDash":false}]},{"StartTime":94548.0,"Objects":[{"StartTime":94548.0,"Position":448.0,"HyperDash":false}]},{"StartTime":94730.0,"Objects":[{"StartTime":94730.0,"Position":272.0,"HyperDash":false}]},{"StartTime":94912.0,"Objects":[{"StartTime":94912.0,"Position":192.0,"HyperDash":false},{"StartTime":95002.0,"Position":152.0,"HyperDash":false},{"StartTime":95093.0,"Position":192.0,"HyperDash":false}]},{"StartTime":95275.0,"Objects":[{"StartTime":95275.0,"Position":304.0,"HyperDash":true}]},{"StartTime":95457.0,"Objects":[{"StartTime":95457.0,"Position":48.0,"HyperDash":false},{"StartTime":95529.0,"Position":66.24104,"HyperDash":false},{"StartTime":95638.0,"Position":63.6892929,"HyperDash":false}]},{"StartTime":95821.0,"Objects":[{"StartTime":95821.0,"Position":176.0,"HyperDash":false},{"StartTime":95893.0,"Position":189.241043,"HyperDash":false},{"StartTime":96002.0,"Position":191.689285,"HyperDash":false}]},{"StartTime":96185.0,"Objects":[{"StartTime":96185.0,"Position":16.0,"HyperDash":false}]},{"StartTime":96366.0,"Objects":[{"StartTime":96366.0,"Position":96.0,"HyperDash":false},{"StartTime":96456.0,"Position":96.0,"HyperDash":false}]},{"StartTime":96548.0,"Objects":[{"StartTime":96548.0,"Position":48.0,"HyperDash":false}]},{"StartTime":96730.0,"Objects":[{"StartTime":96730.0,"Position":160.0,"HyperDash":true}]},{"StartTime":96912.0,"Objects":[{"StartTime":96912.0,"Position":416.0,"HyperDash":false},{"StartTime":96984.0,"Position":402.241058,"HyperDash":false},{"StartTime":97093.0,"Position":431.6893,"HyperDash":false}]},{"StartTime":97275.0,"Objects":[{"StartTime":97275.0,"Position":320.0,"HyperDash":false}]},{"StartTime":97366.0,"Objects":[{"StartTime":97366.0,"Position":272.0,"HyperDash":false},{"StartTime":97456.0,"Position":272.0,"HyperDash":false}]},{"StartTime":97639.0,"Objects":[{"StartTime":97639.0,"Position":448.0,"HyperDash":false}]},{"StartTime":97821.0,"Objects":[{"StartTime":97821.0,"Position":336.0,"HyperDash":false}]},{"StartTime":98003.0,"Objects":[{"StartTime":98003.0,"Position":448.0,"HyperDash":false}]},{"StartTime":98094.0,"Objects":[{"StartTime":98094.0,"Position":496.0,"HyperDash":false},{"StartTime":98184.0,"Position":496.0,"HyperDash":true}]},{"StartTime":98366.0,"Objects":[{"StartTime":98366.0,"Position":240.0,"HyperDash":false},{"StartTime":98438.0,"Position":199.221,"HyperDash":false},{"StartTime":98547.0,"Position":140.0,"HyperDash":false}]},{"StartTime":98730.0,"Objects":[{"StartTime":98730.0,"Position":240.0,"HyperDash":false},{"StartTime":98802.0,"Position":264.779,"HyperDash":false},{"StartTime":98911.0,"Position":340.0,"HyperDash":false}]},{"StartTime":99094.0,"Objects":[{"StartTime":99094.0,"Position":96.0,"HyperDash":false}]},{"StartTime":99275.0,"Objects":[{"StartTime":99275.0,"Position":16.0,"HyperDash":false},{"StartTime":99365.0,"Position":16.0,"HyperDash":false}]},{"StartTime":99457.0,"Objects":[{"StartTime":99457.0,"Position":64.0,"HyperDash":false}]},{"StartTime":99639.0,"Objects":[{"StartTime":99639.0,"Position":176.0,"HyperDash":true}]},{"StartTime":99821.0,"Objects":[{"StartTime":99821.0,"Position":432.0,"HyperDash":false},{"StartTime":99893.0,"Position":432.093933,"HyperDash":false},{"StartTime":100002.0,"Position":480.0,"HyperDash":false}]},{"StartTime":100094.0,"Objects":[{"StartTime":100094.0,"Position":480.0,"HyperDash":false}]},{"StartTime":100185.0,"Objects":[{"StartTime":100185.0,"Position":448.0,"HyperDash":false}]},{"StartTime":100366.0,"Objects":[{"StartTime":100366.0,"Position":368.0,"HyperDash":false}]},{"StartTime":100548.0,"Objects":[{"StartTime":100548.0,"Position":192.0,"HyperDash":false}]},{"StartTime":100730.0,"Objects":[{"StartTime":100730.0,"Position":272.0,"HyperDash":false},{"StartTime":100820.0,"Position":312.0,"HyperDash":false},{"StartTime":100911.0,"Position":272.0,"HyperDash":false}]},{"StartTime":101094.0,"Objects":[{"StartTime":101094.0,"Position":160.0,"HyperDash":true}]},{"StartTime":101275.0,"Objects":[{"StartTime":101275.0,"Position":416.0,"HyperDash":false},{"StartTime":101347.0,"Position":426.241058,"HyperDash":false},{"StartTime":101456.0,"Position":431.6893,"HyperDash":false}]},{"StartTime":101639.0,"Objects":[{"StartTime":101639.0,"Position":320.0,"HyperDash":false}]},{"StartTime":101821.0,"Objects":[{"StartTime":101821.0,"Position":432.0,"HyperDash":false}]},{"StartTime":102003.0,"Objects":[{"StartTime":102003.0,"Position":256.0,"HyperDash":false}]},{"StartTime":102185.0,"Objects":[{"StartTime":102185.0,"Position":176.0,"HyperDash":false},{"StartTime":102275.0,"Position":176.0,"HyperDash":false}]},{"StartTime":102366.0,"Objects":[{"StartTime":102366.0,"Position":224.0,"HyperDash":false}]},{"StartTime":102548.0,"Objects":[{"StartTime":102548.0,"Position":112.0,"HyperDash":true}]},{"StartTime":102730.0,"Objects":[{"StartTime":102730.0,"Position":368.0,"HyperDash":false},{"StartTime":102802.0,"Position":376.241058,"HyperDash":false},{"StartTime":102911.0,"Position":383.6893,"HyperDash":false}]},{"StartTime":103094.0,"Objects":[{"StartTime":103094.0,"Position":272.0,"HyperDash":false}]},{"StartTime":103185.0,"Objects":[{"StartTime":103185.0,"Position":224.0,"HyperDash":false}]},{"StartTime":103275.0,"Objects":[{"StartTime":103275.0,"Position":272.0,"HyperDash":false}]},{"StartTime":103457.0,"Objects":[{"StartTime":103457.0,"Position":384.0,"HyperDash":false}]},{"StartTime":103639.0,"Objects":[{"StartTime":103639.0,"Position":272.0,"HyperDash":false}]},{"StartTime":103821.0,"Objects":[{"StartTime":103821.0,"Position":384.0,"HyperDash":false}]},{"StartTime":103912.0,"Objects":[{"StartTime":103912.0,"Position":432.0,"HyperDash":false},{"StartTime":104002.0,"Position":432.0,"HyperDash":false}]},{"StartTime":104185.0,"Objects":[{"StartTime":104185.0,"Position":176.0,"HyperDash":false},{"StartTime":104257.0,"Position":161.176788,"HyperDash":false},{"StartTime":104366.0,"Position":96.0,"HyperDash":false}]},{"StartTime":104548.0,"Objects":[{"StartTime":104548.0,"Position":272.0,"HyperDash":false},{"StartTime":104620.0,"Position":314.8232,"HyperDash":false},{"StartTime":104729.0,"Position":352.0,"HyperDash":true}]},{"StartTime":104912.0,"Objects":[{"StartTime":104912.0,"Position":80.0,"HyperDash":false},{"StartTime":105002.0,"Position":80.0,"HyperDash":false}]},{"StartTime":105094.0,"Objects":[{"StartTime":105094.0,"Position":128.0,"HyperDash":false}]},{"StartTime":105275.0,"Objects":[{"StartTime":105275.0,"Position":304.0,"HyperDash":false},{"StartTime":105347.0,"Position":337.8232,"HyperDash":false},{"StartTime":105456.0,"Position":384.0,"HyperDash":false}]},{"StartTime":105639.0,"Objects":[{"StartTime":105639.0,"Position":128.0,"HyperDash":false}]},{"StartTime":105821.0,"Objects":[{"StartTime":105821.0,"Position":48.0,"HyperDash":false}]},{"StartTime":105912.0,"Objects":[{"StartTime":105912.0,"Position":128.0,"HyperDash":false}]},{"StartTime":106003.0,"Objects":[{"StartTime":106003.0,"Position":176.0,"HyperDash":false}]},{"StartTime":106185.0,"Objects":[{"StartTime":106185.0,"Position":64.0,"HyperDash":true}]},{"StartTime":106366.0,"Objects":[{"StartTime":106366.0,"Position":320.0,"HyperDash":false},{"StartTime":106456.0,"Position":360.0,"HyperDash":false},{"StartTime":106547.0,"Position":320.0,"HyperDash":false}]},{"StartTime":106730.0,"Objects":[{"StartTime":106730.0,"Position":144.0,"HyperDash":false}]},{"StartTime":106821.0,"Objects":[{"StartTime":106821.0,"Position":96.0,"HyperDash":false},{"StartTime":106911.0,"Position":96.0,"HyperDash":false}]},{"StartTime":107094.0,"Objects":[{"StartTime":107094.0,"Position":352.0,"HyperDash":false},{"StartTime":107166.0,"Position":366.241058,"HyperDash":false},{"StartTime":107275.0,"Position":367.6893,"HyperDash":false}]},{"StartTime":107366.0,"Objects":[{"StartTime":107366.0,"Position":320.0,"HyperDash":false}]},{"StartTime":107457.0,"Objects":[{"StartTime":107457.0,"Position":240.0,"HyperDash":false}]},{"StartTime":107639.0,"Objects":[{"StartTime":107639.0,"Position":352.0,"HyperDash":true}]},{"StartTime":107821.0,"Objects":[{"StartTime":107821.0,"Position":96.0,"HyperDash":false}]},{"StartTime":108003.0,"Objects":[{"StartTime":108003.0,"Position":16.0,"HyperDash":false},{"StartTime":108093.0,"Position":16.0,"HyperDash":false}]},{"StartTime":108185.0,"Objects":[{"StartTime":108185.0,"Position":96.0,"HyperDash":false}]},{"StartTime":108366.0,"Objects":[{"StartTime":108366.0,"Position":16.0,"HyperDash":true}]},{"StartTime":108548.0,"Objects":[{"StartTime":108548.0,"Position":272.0,"HyperDash":false}]},{"StartTime":108639.0,"Objects":[{"StartTime":108639.0,"Position":320.0,"HyperDash":false}]},{"StartTime":108730.0,"Objects":[{"StartTime":108730.0,"Position":272.0,"HyperDash":false}]},{"StartTime":108912.0,"Objects":[{"StartTime":108912.0,"Position":160.0,"HyperDash":false},{"StartTime":108984.0,"Position":184.823212,"HyperDash":false},{"StartTime":109093.0,"Position":240.0,"HyperDash":true}]},{"StartTime":109275.0,"Objects":[{"StartTime":109275.0,"Position":496.0,"HyperDash":false},{"StartTime":109365.0,"Position":496.0,"HyperDash":false}]},{"StartTime":109457.0,"Objects":[{"StartTime":109457.0,"Position":448.0,"HyperDash":false}]},{"StartTime":109639.0,"Objects":[{"StartTime":109639.0,"Position":272.0,"HyperDash":false},{"StartTime":109729.0,"Position":300.284271,"HyperDash":false}]},{"StartTime":109821.0,"Objects":[{"StartTime":109821.0,"Position":352.0,"HyperDash":true}]},{"StartTime":110003.0,"Objects":[{"StartTime":110003.0,"Position":96.0,"HyperDash":false}]},{"StartTime":110094.0,"Objects":[{"StartTime":110094.0,"Position":48.0,"HyperDash":false}]},{"StartTime":110185.0,"Objects":[{"StartTime":110185.0,"Position":96.0,"HyperDash":false}]},{"StartTime":110366.0,"Objects":[{"StartTime":110366.0,"Position":272.0,"HyperDash":false}]},{"StartTime":110457.0,"Objects":[{"StartTime":110457.0,"Position":224.0,"HyperDash":false}]},{"StartTime":110548.0,"Objects":[{"StartTime":110548.0,"Position":176.0,"HyperDash":true}]},{"StartTime":110730.0,"Objects":[{"StartTime":110730.0,"Position":448.0,"HyperDash":false},{"StartTime":110820.0,"Position":448.0,"HyperDash":false}]},{"StartTime":110912.0,"Objects":[{"StartTime":110912.0,"Position":400.0,"HyperDash":false}]},{"StartTime":111094.0,"Objects":[{"StartTime":111094.0,"Position":224.0,"HyperDash":false},{"StartTime":111166.0,"Position":193.176788,"HyperDash":false},{"StartTime":111275.0,"Position":144.0,"HyperDash":true}]},{"StartTime":111457.0,"Objects":[{"StartTime":111457.0,"Position":400.0,"HyperDash":false}]},{"StartTime":111639.0,"Objects":[{"StartTime":111639.0,"Position":480.0,"HyperDash":false}]},{"StartTime":111730.0,"Objects":[{"StartTime":111730.0,"Position":400.0,"HyperDash":false}]},{"StartTime":111821.0,"Objects":[{"StartTime":111821.0,"Position":352.0,"HyperDash":false}]},{"StartTime":112003.0,"Objects":[{"StartTime":112003.0,"Position":464.0,"HyperDash":true}]},{"StartTime":112185.0,"Objects":[{"StartTime":112185.0,"Position":208.0,"HyperDash":false},{"StartTime":112257.0,"Position":160.176788,"HyperDash":false},{"StartTime":112366.0,"Position":128.0,"HyperDash":false}]},{"StartTime":112548.0,"Objects":[{"StartTime":112548.0,"Position":304.0,"HyperDash":false},{"StartTime":112620.0,"Position":316.8232,"HyperDash":false},{"StartTime":112729.0,"Position":384.0,"HyperDash":false}]},{"StartTime":112912.0,"Objects":[{"StartTime":112912.0,"Position":128.0,"HyperDash":false},{"StartTime":112984.0,"Position":101.758957,"HyperDash":false},{"StartTime":113093.0,"Position":112.310707,"HyperDash":false}]},{"StartTime":113185.0,"Objects":[{"StartTime":113185.0,"Position":160.0,"HyperDash":false}]},{"StartTime":113275.0,"Objects":[{"StartTime":113275.0,"Position":240.0,"HyperDash":false}]},{"StartTime":113457.0,"Objects":[{"StartTime":113457.0,"Position":128.0,"HyperDash":true}]},{"StartTime":113639.0,"Objects":[{"StartTime":113639.0,"Position":384.0,"HyperDash":false}]},{"StartTime":113821.0,"Objects":[{"StartTime":113821.0,"Position":464.0,"HyperDash":false},{"StartTime":113911.0,"Position":464.0,"HyperDash":false}]},{"StartTime":114003.0,"Objects":[{"StartTime":114003.0,"Position":384.0,"HyperDash":false}]},{"StartTime":114185.0,"Objects":[{"StartTime":114185.0,"Position":464.0,"HyperDash":true}]},{"StartTime":114366.0,"Objects":[{"StartTime":114366.0,"Position":208.0,"HyperDash":false}]},{"StartTime":114548.0,"Objects":[{"StartTime":114548.0,"Position":128.0,"HyperDash":false}]},{"StartTime":114639.0,"Objects":[{"StartTime":114639.0,"Position":176.0,"HyperDash":false}]},{"StartTime":114730.0,"Objects":[{"StartTime":114730.0,"Position":256.0,"HyperDash":false}]},{"StartTime":114912.0,"Objects":[{"StartTime":114912.0,"Position":144.0,"HyperDash":true}]},{"StartTime":115094.0,"Objects":[{"StartTime":115094.0,"Position":400.0,"HyperDash":false}]},{"StartTime":115185.0,"Objects":[{"StartTime":115185.0,"Position":448.0,"HyperDash":false}]},{"StartTime":115275.0,"Objects":[{"StartTime":115275.0,"Position":400.0,"HyperDash":false}]},{"StartTime":115457.0,"Objects":[{"StartTime":115457.0,"Position":224.0,"HyperDash":false}]},{"StartTime":115548.0,"Objects":[{"StartTime":115548.0,"Position":176.0,"HyperDash":false},{"StartTime":115638.0,"Position":176.0,"HyperDash":false}]},{"StartTime":115821.0,"Objects":[{"StartTime":115821.0,"Position":432.0,"HyperDash":false},{"StartTime":115911.0,"Position":432.0,"HyperDash":false}]},{"StartTime":116003.0,"Objects":[{"StartTime":116003.0,"Position":384.0,"HyperDash":false}]},{"StartTime":116185.0,"Objects":[{"StartTime":116185.0,"Position":208.0,"HyperDash":false}]},{"StartTime":116275.0,"Objects":[{"StartTime":116275.0,"Position":256.0,"HyperDash":false}]},{"StartTime":116366.0,"Objects":[{"StartTime":116366.0,"Position":304.0,"HyperDash":true}]},{"StartTime":116548.0,"Objects":[{"StartTime":116548.0,"Position":48.0,"HyperDash":false}]},{"StartTime":116730.0,"Objects":[{"StartTime":116730.0,"Position":304.0,"HyperDash":false},{"StartTime":116815.0,"Position":221.0,"HyperDash":false},{"StartTime":116900.0,"Position":407.0,"HyperDash":false},{"StartTime":116985.0,"Position":287.0,"HyperDash":false},{"StartTime":117070.0,"Position":135.0,"HyperDash":false},{"StartTime":117156.0,"Position":437.0,"HyperDash":false},{"StartTime":117241.0,"Position":289.0,"HyperDash":false},{"StartTime":117326.0,"Position":464.0,"HyperDash":false},{"StartTime":117411.0,"Position":36.0,"HyperDash":false},{"StartTime":117496.0,"Position":378.0,"HyperDash":false},{"StartTime":117582.0,"Position":297.0,"HyperDash":false},{"StartTime":117667.0,"Position":418.0,"HyperDash":false},{"StartTime":117752.0,"Position":329.0,"HyperDash":false},{"StartTime":117837.0,"Position":338.0,"HyperDash":false},{"StartTime":117923.0,"Position":394.0,"HyperDash":false},{"StartTime":118008.0,"Position":40.0,"HyperDash":false},{"StartTime":118093.0,"Position":13.0,"HyperDash":false},{"StartTime":118178.0,"Position":80.0,"HyperDash":false},{"StartTime":118263.0,"Position":138.0,"HyperDash":false},{"StartTime":118349.0,"Position":311.0,"HyperDash":false},{"StartTime":118434.0,"Position":216.0,"HyperDash":false},{"StartTime":118519.0,"Position":310.0,"HyperDash":false},{"StartTime":118604.0,"Position":397.0,"HyperDash":false},{"StartTime":118690.0,"Position":214.0,"HyperDash":false},{"StartTime":118775.0,"Position":505.0,"HyperDash":false},{"StartTime":118860.0,"Position":173.0,"HyperDash":false},{"StartTime":118945.0,"Position":295.0,"HyperDash":false},{"StartTime":119030.0,"Position":199.0,"HyperDash":false},{"StartTime":119116.0,"Position":494.0,"HyperDash":false},{"StartTime":119201.0,"Position":293.0,"HyperDash":false},{"StartTime":119286.0,"Position":115.0,"HyperDash":false},{"StartTime":119371.0,"Position":412.0,"HyperDash":false},{"StartTime":119457.0,"Position":506.0,"HyperDash":false}]},{"StartTime":122366.0,"Objects":[{"StartTime":122366.0,"Position":312.0,"HyperDash":false},{"StartTime":122438.0,"Position":320.241058,"HyperDash":false},{"StartTime":122547.0,"Position":327.6893,"HyperDash":false}]},{"StartTime":122730.0,"Objects":[{"StartTime":122730.0,"Position":232.0,"HyperDash":false},{"StartTime":122802.0,"Position":233.241043,"HyperDash":false},{"StartTime":122911.0,"Position":247.689285,"HyperDash":false}]},{"StartTime":123275.0,"Objects":[{"StartTime":123275.0,"Position":408.0,"HyperDash":false}]},{"StartTime":123457.0,"Objects":[{"StartTime":123457.0,"Position":328.0,"HyperDash":false}]},{"StartTime":123821.0,"Objects":[{"StartTime":123821.0,"Position":168.0,"HyperDash":false},{"StartTime":123893.0,"Position":147.758957,"HyperDash":false},{"StartTime":124002.0,"Position":152.310715,"HyperDash":false}]},{"StartTime":124185.0,"Objects":[{"StartTime":124185.0,"Position":256.0,"HyperDash":false},{"StartTime":124257.0,"Position":241.758957,"HyperDash":false},{"StartTime":124366.0,"Position":240.310715,"HyperDash":false}]},{"StartTime":124730.0,"Objects":[{"StartTime":124730.0,"Position":64.0,"HyperDash":false}]},{"StartTime":124912.0,"Objects":[{"StartTime":124912.0,"Position":152.0,"HyperDash":false}]},{"StartTime":125275.0,"Objects":[{"StartTime":125275.0,"Position":336.0,"HyperDash":false},{"StartTime":125347.0,"Position":349.241058,"HyperDash":false},{"StartTime":125456.0,"Position":351.6893,"HyperDash":false}]},{"StartTime":125639.0,"Objects":[{"StartTime":125639.0,"Position":240.0,"HyperDash":false},{"StartTime":125711.0,"Position":260.241028,"HyperDash":false},{"StartTime":125820.0,"Position":255.689285,"HyperDash":false}]},{"StartTime":126185.0,"Objects":[{"StartTime":126185.0,"Position":448.0,"HyperDash":false}]},{"StartTime":126366.0,"Objects":[{"StartTime":126366.0,"Position":352.0,"HyperDash":false}]},{"StartTime":126730.0,"Objects":[{"StartTime":126730.0,"Position":144.0,"HyperDash":false},{"StartTime":126802.0,"Position":130.758957,"HyperDash":false},{"StartTime":126911.0,"Position":128.310715,"HyperDash":false}]},{"StartTime":127094.0,"Objects":[{"StartTime":127094.0,"Position":248.0,"HyperDash":false},{"StartTime":127166.0,"Position":256.758972,"HyperDash":false},{"StartTime":127275.0,"Position":232.310715,"HyperDash":false}]},{"StartTime":127457.0,"Objects":[{"StartTime":127457.0,"Position":112.0,"HyperDash":false},{"StartTime":127529.0,"Position":132.823212,"HyperDash":false},{"StartTime":127638.0,"Position":192.0,"HyperDash":false}]},{"StartTime":127821.0,"Objects":[{"StartTime":127821.0,"Position":48.0,"HyperDash":false}]},{"StartTime":128003.0,"Objects":[{"StartTime":128003.0,"Position":192.0,"HyperDash":false}]},{"StartTime":128185.0,"Objects":[{"StartTime":128185.0,"Position":368.0,"HyperDash":false}]},{"StartTime":128366.0,"Objects":[{"StartTime":128366.0,"Position":288.0,"HyperDash":false},{"StartTime":128456.0,"Position":248.0,"HyperDash":false},{"StartTime":128547.0,"Position":288.0,"HyperDash":false}]},{"StartTime":128730.0,"Objects":[{"StartTime":128730.0,"Position":368.0,"HyperDash":false}]},{"StartTime":128912.0,"Objects":[{"StartTime":128912.0,"Position":496.0,"HyperDash":false},{"StartTime":129002.0,"Position":496.0,"HyperDash":false}]},{"StartTime":129094.0,"Objects":[{"StartTime":129094.0,"Position":448.0,"HyperDash":false}]},{"StartTime":129275.0,"Objects":[{"StartTime":129275.0,"Position":368.0,"HyperDash":false}]},{"StartTime":129457.0,"Objects":[{"StartTime":129457.0,"Position":448.0,"HyperDash":false}]},{"StartTime":129639.0,"Objects":[{"StartTime":129639.0,"Position":272.0,"HyperDash":false},{"StartTime":129729.0,"Position":272.0,"HyperDash":false}]},{"StartTime":129821.0,"Objects":[{"StartTime":129821.0,"Position":320.0,"HyperDash":false}]},{"StartTime":130003.0,"Objects":[{"StartTime":130003.0,"Position":432.0,"HyperDash":false}]},{"StartTime":130094.0,"Objects":[{"StartTime":130094.0,"Position":384.0,"HyperDash":false}]},{"StartTime":130185.0,"Objects":[{"StartTime":130185.0,"Position":336.0,"HyperDash":false}]},{"StartTime":130366.0,"Objects":[{"StartTime":130366.0,"Position":448.0,"HyperDash":false},{"StartTime":130456.0,"Position":448.0,"HyperDash":false}]},{"StartTime":130548.0,"Objects":[{"StartTime":130548.0,"Position":400.0,"HyperDash":false}]},{"StartTime":130730.0,"Objects":[{"StartTime":130730.0,"Position":288.0,"HyperDash":false},{"StartTime":130802.0,"Position":307.8232,"HyperDash":false},{"StartTime":130911.0,"Position":368.0,"HyperDash":false}]},{"StartTime":131094.0,"Objects":[{"StartTime":131094.0,"Position":192.0,"HyperDash":false}]},{"StartTime":131275.0,"Objects":[{"StartTime":131275.0,"Position":112.0,"HyperDash":false},{"StartTime":131365.0,"Position":112.0,"HyperDash":false}]},{"StartTime":131457.0,"Objects":[{"StartTime":131457.0,"Position":160.0,"HyperDash":false}]},{"StartTime":131639.0,"Objects":[{"StartTime":131639.0,"Position":80.0,"HyperDash":false}]},{"StartTime":131821.0,"Objects":[{"StartTime":131821.0,"Position":192.0,"HyperDash":false},{"StartTime":131911.0,"Position":192.0,"HyperDash":false}]},{"StartTime":132003.0,"Objects":[{"StartTime":132003.0,"Position":144.0,"HyperDash":false}]},{"StartTime":132185.0,"Objects":[{"StartTime":132185.0,"Position":64.0,"HyperDash":false}]},{"StartTime":132366.0,"Objects":[{"StartTime":132366.0,"Position":144.0,"HyperDash":false}]},{"StartTime":132548.0,"Objects":[{"StartTime":132548.0,"Position":320.0,"HyperDash":false}]},{"StartTime":132730.0,"Objects":[{"StartTime":132730.0,"Position":240.0,"HyperDash":false}]},{"StartTime":132912.0,"Objects":[{"StartTime":132912.0,"Position":320.0,"HyperDash":false},{"StartTime":133002.0,"Position":360.0,"HyperDash":false},{"StartTime":133093.0,"Position":320.0,"HyperDash":false}]},{"StartTime":133275.0,"Objects":[{"StartTime":133275.0,"Position":208.0,"HyperDash":false},{"StartTime":133347.0,"Position":254.823212,"HyperDash":false},{"StartTime":133456.0,"Position":288.0,"HyperDash":false}]},{"StartTime":133639.0,"Objects":[{"StartTime":133639.0,"Position":400.0,"HyperDash":false},{"StartTime":133711.0,"Position":377.1768,"HyperDash":false},{"StartTime":133820.0,"Position":320.0,"HyperDash":false}]},{"StartTime":134003.0,"Objects":[{"StartTime":134003.0,"Position":144.0,"HyperDash":false}]},{"StartTime":134185.0,"Objects":[{"StartTime":134185.0,"Position":224.0,"HyperDash":false},{"StartTime":134275.0,"Position":264.0,"HyperDash":false},{"StartTime":134366.0,"Position":224.0,"HyperDash":false}]},{"StartTime":134548.0,"Objects":[{"StartTime":134548.0,"Position":144.0,"HyperDash":false}]},{"StartTime":134730.0,"Objects":[{"StartTime":134730.0,"Position":64.0,"HyperDash":false}]},{"StartTime":134912.0,"Objects":[{"StartTime":134912.0,"Position":144.0,"HyperDash":false},{"StartTime":135002.0,"Position":184.0,"HyperDash":false},{"StartTime":135093.0,"Position":144.0,"HyperDash":false}]},{"StartTime":135275.0,"Objects":[{"StartTime":135275.0,"Position":64.0,"HyperDash":false}]},{"StartTime":135457.0,"Objects":[{"StartTime":135457.0,"Position":240.0,"HyperDash":false},{"StartTime":135547.0,"Position":240.0,"HyperDash":false}]},{"StartTime":135639.0,"Objects":[{"StartTime":135639.0,"Position":192.0,"HyperDash":false}]},{"StartTime":135821.0,"Objects":[{"StartTime":135821.0,"Position":80.0,"HyperDash":false}]},{"StartTime":135912.0,"Objects":[{"StartTime":135912.0,"Position":128.0,"HyperDash":false}]},{"StartTime":136003.0,"Objects":[{"StartTime":136003.0,"Position":176.0,"HyperDash":false}]},{"StartTime":136185.0,"Objects":[{"StartTime":136185.0,"Position":288.0,"HyperDash":false}]},{"StartTime":136275.0,"Objects":[{"StartTime":136275.0,"Position":240.0,"HyperDash":false}]},{"StartTime":136366.0,"Objects":[{"StartTime":136366.0,"Position":192.0,"HyperDash":false}]},{"StartTime":136548.0,"Objects":[{"StartTime":136548.0,"Position":80.0,"HyperDash":false}]},{"StartTime":136730.0,"Objects":[{"StartTime":136730.0,"Position":192.0,"HyperDash":false}]},{"StartTime":136912.0,"Objects":[{"StartTime":136912.0,"Position":368.0,"HyperDash":false}]},{"StartTime":137094.0,"Objects":[{"StartTime":137094.0,"Position":448.0,"HyperDash":false},{"StartTime":137184.0,"Position":448.0,"HyperDash":false}]},{"StartTime":137275.0,"Objects":[{"StartTime":137275.0,"Position":400.0,"HyperDash":false}]},{"StartTime":137457.0,"Objects":[{"StartTime":137457.0,"Position":320.0,"HyperDash":false}]},{"StartTime":137639.0,"Objects":[{"StartTime":137639.0,"Position":432.0,"HyperDash":false},{"StartTime":137729.0,"Position":419.3509,"HyperDash":false}]},{"StartTime":137821.0,"Objects":[{"StartTime":137821.0,"Position":368.0,"HyperDash":false}]},{"StartTime":138003.0,"Objects":[{"StartTime":138003.0,"Position":288.0,"HyperDash":false}]},{"StartTime":138185.0,"Objects":[{"StartTime":138185.0,"Position":368.0,"HyperDash":false}]},{"StartTime":138366.0,"Objects":[{"StartTime":138366.0,"Position":192.0,"HyperDash":false}]},{"StartTime":138548.0,"Objects":[{"StartTime":138548.0,"Position":112.0,"HyperDash":false},{"StartTime":138638.0,"Position":104.155357,"HyperDash":false}]},{"StartTime":138821.0,"Objects":[{"StartTime":138821.0,"Position":216.0,"HyperDash":false},{"StartTime":138911.0,"Position":256.0,"HyperDash":false}]},{"StartTime":139094.0,"Objects":[{"StartTime":139094.0,"Position":144.0,"HyperDash":false}]},{"StartTime":139275.0,"Objects":[{"StartTime":139275.0,"Position":224.0,"HyperDash":false}]},{"StartTime":139457.0,"Objects":[{"StartTime":139457.0,"Position":48.0,"HyperDash":false}]},{"StartTime":139639.0,"Objects":[{"StartTime":139639.0,"Position":160.0,"HyperDash":true}]},{"StartTime":139821.0,"Objects":[{"StartTime":139821.0,"Position":416.0,"HyperDash":false}]},{"StartTime":140003.0,"Objects":[{"StartTime":140003.0,"Position":285.0,"HyperDash":false},{"StartTime":140056.0,"Position":17.0,"HyperDash":false},{"StartTime":140110.0,"Position":238.0,"HyperDash":false},{"StartTime":140164.0,"Position":222.0,"HyperDash":false},{"StartTime":140218.0,"Position":450.0,"HyperDash":false},{"StartTime":140272.0,"Position":67.0,"HyperDash":false},{"StartTime":140326.0,"Position":219.0,"HyperDash":false},{"StartTime":140380.0,"Position":307.0,"HyperDash":false},{"StartTime":140434.0,"Position":367.0,"HyperDash":false},{"StartTime":140488.0,"Position":412.0,"HyperDash":false},{"StartTime":140542.0,"Position":413.0,"HyperDash":false},{"StartTime":140596.0,"Position":143.0,"HyperDash":false},{"StartTime":140650.0,"Position":339.0,"HyperDash":false},{"StartTime":140704.0,"Position":342.0,"HyperDash":false},{"StartTime":140758.0,"Position":249.0,"HyperDash":false},{"StartTime":140812.0,"Position":235.0,"HyperDash":false},{"StartTime":140866.0,"Position":323.0,"HyperDash":false},{"StartTime":140920.0,"Position":365.0,"HyperDash":false},{"StartTime":140974.0,"Position":74.0,"HyperDash":false},{"StartTime":141028.0,"Position":281.0,"HyperDash":false},{"StartTime":141082.0,"Position":398.0,"HyperDash":false},{"StartTime":141136.0,"Position":335.0,"HyperDash":false},{"StartTime":141190.0,"Position":388.0,"HyperDash":false},{"StartTime":141244.0,"Position":228.0,"HyperDash":false},{"StartTime":141298.0,"Position":323.0,"HyperDash":false},{"StartTime":141352.0,"Position":441.0,"HyperDash":false},{"StartTime":141406.0,"Position":442.0,"HyperDash":false},{"StartTime":141460.0,"Position":278.0,"HyperDash":false},{"StartTime":141514.0,"Position":90.0,"HyperDash":false},{"StartTime":141568.0,"Position":409.0,"HyperDash":false},{"StartTime":141622.0,"Position":377.0,"HyperDash":false},{"StartTime":141676.0,"Position":457.0,"HyperDash":false},{"StartTime":141730.0,"Position":409.0,"HyperDash":false},{"StartTime":141783.0,"Position":43.0,"HyperDash":false},{"StartTime":141837.0,"Position":162.0,"HyperDash":false},{"StartTime":141891.0,"Position":341.0,"HyperDash":false},{"StartTime":141945.0,"Position":72.0,"HyperDash":false},{"StartTime":141999.0,"Position":135.0,"HyperDash":false},{"StartTime":142053.0,"Position":252.0,"HyperDash":false},{"StartTime":142107.0,"Position":446.0,"HyperDash":false},{"StartTime":142161.0,"Position":284.0,"HyperDash":false},{"StartTime":142215.0,"Position":70.0,"HyperDash":false},{"StartTime":142269.0,"Position":494.0,"HyperDash":false},{"StartTime":142323.0,"Position":463.0,"HyperDash":false},{"StartTime":142377.0,"Position":277.0,"HyperDash":false},{"StartTime":142431.0,"Position":425.0,"HyperDash":false},{"StartTime":142485.0,"Position":281.0,"HyperDash":false},{"StartTime":142539.0,"Position":3.0,"HyperDash":false},{"StartTime":142593.0,"Position":346.0,"HyperDash":false},{"StartTime":142647.0,"Position":350.0,"HyperDash":false},{"StartTime":142701.0,"Position":217.0,"HyperDash":false},{"StartTime":142755.0,"Position":455.0,"HyperDash":false},{"StartTime":142809.0,"Position":229.0,"HyperDash":false},{"StartTime":142863.0,"Position":51.0,"HyperDash":false},{"StartTime":142917.0,"Position":199.0,"HyperDash":false},{"StartTime":142971.0,"Position":208.0,"HyperDash":false},{"StartTime":143025.0,"Position":173.0,"HyperDash":false},{"StartTime":143079.0,"Position":367.0,"HyperDash":false},{"StartTime":143133.0,"Position":193.0,"HyperDash":false},{"StartTime":143187.0,"Position":488.0,"HyperDash":false},{"StartTime":143241.0,"Position":314.0,"HyperDash":false},{"StartTime":143295.0,"Position":135.0,"HyperDash":false},{"StartTime":143349.0,"Position":399.0,"HyperDash":false},{"StartTime":143403.0,"Position":404.0,"HyperDash":false},{"StartTime":143457.0,"Position":152.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3949367.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3949367.osu new file mode 100644 index 0000000000..19fab1c61c --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/3949367.osu @@ -0,0 +1,832 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 2 + +[Difficulty] +HPDrainRate:4 +CircleSize:3.5 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:1.6 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +2,49839,51798 +2,70203,75071 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Layer 4 (Overlay) +//Storyboard Sound Samples + +[TimingPoints] +185,363.636363636364,4,2,0,50,1,0 +7457,-100,4,2,0,55,0,0 +8912,-100,4,2,0,60,0,0 +10366,-100,4,2,0,65,0,0 +10730,-100,4,2,0,70,0,0 +11094,-100,4,2,0,75,0,0 +11457,-100,4,2,0,80,0,0 +11821,-100,4,1,1,80,0,0 +17821,-100,4,1,0,70,0,0 +17912,-100,4,1,1,80,0,0 +18003,-100,4,1,0,70,0,0 +18094,-100,4,1,1,80,0,0 +18366,-100,4,1,0,70,0,0 +18457,-100,4,1,1,80,0,0 +19275,-100,4,1,0,70,0,0 +19366,-100,4,1,1,80,0,0 +19457,-100,4,1,0,70,0,0 +19548,-100,4,1,1,80,0,0 +19821,-100,4,1,0,70,0,0 +19912,-100,4,1,1,80,0,0 +20639,-100,4,1,0,70,0,0 +21094,-100,4,1,1,80,0,0 +21185,-100,4,1,0,70,0,0 +21457,-100,4,1,1,80,0,0 +21548,-100,4,1,0,70,0,0 +21639,-100,4,1,1,80,0,0 +22094,-100,4,1,0,70,0,0 +22548,-100,4,1,1,80,0,0 +22639,-100,4,1,0,70,0,0 +22730,-100,4,2,0,60,0,0 +22912,-100,4,2,0,30,0,0 +23094,-100,4,2,0,60,0,0 +23276,-100,4,2,0,30,0,0 +23457,-100,4,3,1,80,0,0 +24185,-100,4,3,2,80,0,0 +24275,-100,4,3,1,80,0,0 +25275,-100,4,3,1,80,0,0 +25639,-100,4,3,2,80,0,0 +25730,-100,4,3,1,80,0,0 +26003,-100,4,3,1,80,0,0 +27094,-100,4,3,2,80,0,0 +27184,-100,4,3,1,80,0,0 +28548,-100,4,3,2,80,0,0 +28638,-100,4,3,1,80,0,0 +30003,-100,4,3,2,80,0,0 +30093,-100,4,3,1,80,0,0 +31094,-100,4,3,1,80,0,0 +31457,-100,4,3,2,80,0,0 +31548,-100,4,3,1,80,0,0 +31821,-100,4,3,1,80,0,0 +32185,-100,4,3,1,80,0,0 +32912,-100,4,3,2,80,0,0 +33002,-100,4,3,1,80,0,0 +34366,-100,4,3,2,80,0,0 +34456,-100,4,3,1,80,0,0 +35094,-100,4,3,3,90,0,1 +35821,-100,4,1,0,80,0,1 +35912,-100,4,3,3,90,0,1 +37275,-100,4,1,0,80,0,1 +37366,-100,4,3,3,90,0,1 +38730,-100,4,1,0,80,0,1 +38821,-100,4,3,3,90,0,1 +40185,-100,4,1,0,80,0,1 +40366,-100,4,3,3,90,0,1 +40457,-100,4,1,0,80,0,1 +40548,-100,4,3,3,90,0,1 +40639,-100,4,1,0,80,0,1 +40730,-100,4,3,3,90,0,1 +40821,-100,4,1,0,80,0,1 +40912,-100,4,3,3,90,0,1 +41639,-100,4,1,0,80,0,1 +41730,-100,4,3,3,90,0,1 +43093,-100,4,1,0,80,0,1 +43184,-100,4,3,3,90,0,1 +43457,-100,4,3,3,90,0,1 +43639,-100,4,3,3,90,0,1 +44548,-100,4,1,0,80,0,1 +44639,-100,4,3,3,90,0,1 +46003,-100,4,1,0,80,0,1 +46184,-100,4,3,3,90,0,1 +46275,-100,4,1,0,80,0,1 +46366,-100,4,3,3,90,0,1 +46457,-100,4,1,0,80,0,1 +46548,-100,4,3,3,90,0,1 +46639,-100,4,1,0,80,0,1 +46730,-100,4,1,2,80,0,0 +46912,-100,4,2,0,50,0,0 +51094,-100,4,2,0,55,0,0 +52548,-100,4,2,0,60,0,0 +54003,-100,4,2,0,65,0,0 +55457,-100,4,2,0,70,0,0 +56912,-100,4,2,0,75,0,0 +57639,-100,4,2,0,80,0,0 +58003,-100,4,2,0,85,0,0 +58366,-100,4,1,1,80,0,0 +58548,-100,4,2,1,60,0,0 +69275,-100,4,2,1,70,0,0 +69639,-100,4,2,1,80,0,0 +70003,-100,4,1,1,80,0,0 +70185,-100,4,2,1,50,0,0 +70548,-100,4,3,0,50,0,0 +71457,-100,4,2,1,50,0,0 +72003,-100,4,3,0,50,0,0 +72912,-100,4,2,1,60,0,0 +73457,-100,4,3,0,50,0,0 +74366,-100,4,2,1,60,0,0 +74912,-100,4,3,0,50,0,0 +75094,-100,4,2,1,60,0,0 +75275,-100,4,3,0,50,0,0 +75457,-100,4,2,1,60,0,0 +75639,-100,4,3,0,50,0,0 +75821,-100,4,2,1,70,0,0 +76003,-100,4,3,0,50,0,0 +76185,-100,4,2,1,70,0,0 +76366,-100,4,3,0,50,0,0 +76548,-100,4,2,1,70,0,0 +76730,-100,4,3,0,50,0,0 +76912,-100,4,2,1,70,0,0 +77094,-100,4,3,0,50,0,0 +77275,-100,4,2,1,70,0,0 +77457,-100,4,3,0,50,0,0 +77639,-100,4,2,1,70,0,0 +77820,-100,4,3,0,50,0,0 +78002,-100,4,2,1,70,0,0 +78184,-100,4,3,0,50,0,0 +78366,-100,4,2,1,70,0,0 +78548,-100,4,3,0,50,0,0 +78730,-100,4,2,1,75,0,0 +78912,-100,4,3,0,50,0,0 +79094,-100,4,2,1,75,0,0 +79275,-100,4,3,0,50,0,0 +79457,-100,4,2,1,75,0,0 +79639,-100,4,3,0,50,0,0 +79821,-100,4,2,1,75,0,0 +80003,-100,4,3,0,50,0,0 +80185,-100,4,2,1,75,0,0 +80367,-100,4,3,0,50,0,0 +80549,-100,4,2,1,75,0,0 +80730,-100,4,3,0,50,0,0 +80912,-100,4,2,1,80,0,0 +81094,-100,4,3,0,50,0,0 +81276,-100,4,2,1,80,0,0 +81458,-100,4,3,0,50,0,0 +81639,-100,4,1,1,80,0,0 +87639,-100,4,1,0,70,0,0 +87730,-100,4,1,1,80,0,0 +87821,-100,4,1,0,70,0,0 +87912,-100,4,1,1,80,0,0 +88184,-100,4,1,0,70,0,0 +88275,-100,4,1,1,80,0,0 +89093,-100,4,1,0,70,0,0 +89184,-100,4,1,1,80,0,0 +89275,-100,4,1,0,70,0,0 +89366,-100,4,1,1,80,0,0 +89639,-100,4,1,0,70,0,0 +89730,-100,4,1,1,80,0,0 +90457,-100,4,1,0,70,0,0 +90912,-100,4,1,1,80,0,0 +91003,-100,4,1,0,70,0,0 +91275,-100,4,1,1,80,0,0 +91366,-100,4,1,0,70,0,0 +91457,-100,4,1,1,80,0,0 +91912,-100,4,1,0,70,0,0 +92366,-100,4,1,1,80,0,0 +92457,-100,4,1,0,70,0,0 +92548,-100,4,2,0,60,0,0 +92594,-100,4,2,0,30,0,0 +92730,-100,4,2,0,60,0,0 +92776,-100,4,2,0,30,0,0 +92912,-100,4,2,0,60,0,0 +92958,-100,4,2,0,30,0,0 +93094,-100,4,2,0,60,0,0 +93140,-100,4,2,0,30,0,0 +93275,-100,4,3,1,80,0,0 +94003,-100,4,3,2,80,0,0 +94093,-100,4,3,1,80,0,0 +95094,-100,4,3,1,80,0,0 +95457,-100,4,3,2,80,0,0 +95548,-100,4,3,1,80,0,0 +95821,-100,4,3,1,80,0,0 +96912,-100,4,3,2,80,0,0 +97002,-100,4,3,1,80,0,0 +98366,-80,4,3,2,80,0,0 +98456,-80,4,3,1,80,0,0 +99094,-100,4,3,1,80,0,0 +99821,-100,4,3,2,80,0,0 +99911,-100,4,3,1,80,0,0 +100912,-100,4,3,1,80,0,0 +101275,-100,4,3,2,80,0,0 +101366,-100,4,3,1,80,0,0 +101639,-100,4,3,1,80,0,0 +102003,-100,4,3,1,80,0,0 +102730,-100,4,3,2,80,0,0 +102820,-100,4,3,1,80,0,0 +104184,-100,4,3,2,80,0,0 +104274,-100,4,3,1,80,0,0 +104912,-100,4,3,3,90,0,1 +105639,-100,4,1,0,80,0,1 +105730,-100,4,3,3,90,0,1 +107093,-100,4,1,0,80,0,1 +107184,-100,4,3,3,90,0,1 +108548,-100,4,1,0,80,0,1 +108639,-100,4,3,3,90,0,1 +110003,-100,4,1,0,80,0,1 +110184,-100,4,3,3,90,0,1 +110275,-100,4,1,0,80,0,1 +110366,-100,4,3,3,90,0,1 +110457,-100,4,1,0,80,0,1 +110548,-100,4,3,3,90,0,1 +110639,-100,4,1,0,80,0,1 +110730,-100,4,3,3,90,0,1 +111457,-100,4,1,0,80,0,1 +111548,-100,4,3,3,90,0,1 +112911,-100,4,1,0,80,0,1 +113002,-100,4,3,3,90,0,1 +113275,-100,4,3,3,90,0,1 +113457,-100,4,3,3,90,0,1 +114366,-100,4,1,0,80,0,1 +114457,-100,4,3,3,90,0,1 +115821,-100,4,1,0,80,0,1 +116002,-100,4,3,3,90,0,1 +116093,-100,4,1,0,80,0,1 +116184,-100,4,3,3,90,0,1 +116275,-100,4,1,0,80,0,1 +116366,-100,4,3,3,90,0,1 +116457,-100,4,1,0,80,0,1 +116548,-100,4,1,2,80,0,0 +116730,-100,4,2,0,50,0,0 +120912,-100,4,2,0,55,0,0 +122366,-100,4,2,0,60,0,0 +123821,-100,4,2,0,65,0,0 +125275,-100,4,2,0,70,0,0 +126730,-100,4,2,0,75,0,0 +127457,-100,4,2,0,80,0,0 +127821,-100,4,2,0,85,0,0 +128184,-100,4,1,1,80,0,0 +128366,-100,4,2,1,60,0,0 +139093,-100,4,2,1,70,0,0 +139457,-100,4,2,1,80,0,0 +139821,-100,4,1,1,80,0,0 +140003,-100,4,2,1,50,0,0 +140548,-100,4,2,1,45,0,0 +140912,-100,4,2,1,40,0,0 +141275,-100,4,2,1,35,0,0 +141639,-100,4,2,1,30,0,0 +142003,-100,4,2,1,25,0,0 +142366,-100,4,2,1,20,0,0 +142730,-100,4,2,1,15,0,0 +143094,-100,4,2,1,10,0,0 +143457,-100,4,2,1,5,0,0 + +[HitObjects] +64,192,6003,5,2,0:0:0:0: +192,192,6366,1,0,0:0:0:0: +64,192,6730,1,2,0:0:0:0: +192,192,7094,1,0,0:0:0:0: +320,192,7457,5,2,0:0:0:0: +192,192,7821,1,0,0:0:0:0: +320,192,8185,1,2,0:0:0:0: +192,192,8548,1,0,0:0:0:0: +320,192,8912,5,2,0:0:0:0: +448,192,9275,1,0,0:0:0:0: +320,192,9639,1,2,0:0:0:0: +448,192,10003,1,0,0:0:0:0: +256,192,10366,12,2,11457,0:0:0:0: +96,192,11821,5,14,0:0:0:0: +176,192,12003,2,0,L|208:160,2,40 +64,192,12366,1,2,0:0:0:0: +224,192,12730,2,0,L|252:220,2,40,2|0|2,0:0|0:0|0:0,0:0:0:0: +144,192,13094,1,2,0:0:0:0: +320,192,13275,5,10,0:0:0:0: +368,192,13366,1,0,0:0:0:0: +320,192,13457,1,0,0:0:0:0: +208,192,13639,1,0,0:0:0:0: +160,192,13730,2,0,L|160:144,1,40,0|2,0:0|0:0,0:0:0:0: +240,112,14003,1,0,0:0:0:0: +128,112,14185,1,2,0:0:0:0: +208,112,14366,2,0,L|208:160,1,40,2|0,0:0|0:0,0:0:0:0: +160,192,14548,1,2,0:0:0:0: +336,192,14730,5,10,0:0:0:0: +256,192,14912,2,0,L|224:224,2,40,0|0|0,0:0|0:0|0:0,0:0:0:0: +368,192,15275,1,2,0:0:0:0: +208,192,15639,2,0,L|180:164,2,40,2|0|2,0:0|0:0|0:0,0:0:0:0: +288,192,16003,1,2,0:0:0:0: +112,192,16185,5,10,0:0:0:0: +64,192,16275,1,0,0:0:0:0: +112,192,16366,1,0,0:0:0:0: +224,192,16548,1,0,0:0:0:0: +272,192,16639,2,0,L|272:240,1,40,0|2,0:0|0:0,0:0:0:0: +160,192,16912,1,0,0:0:0:0: +208,192,17003,1,0,0:0:0:0: +256,192,17094,1,2,0:0:0:0: +144,112,17275,1,2,0:0:0:0: +80,112,17366,1,0,0:0:0:0: +144,112,17457,1,2,0:0:0:0: +320,112,17639,5,10,0:0:0:0: +400,112,17821,1,0,0:0:0:0: +352,112,17912,1,0,0:0:0:0: +304,112,18003,1,0,0:0:0:0: +416,112,18185,2,0,B|432:192|432:192|352:192,1,160,2|2,0:0|0:0,0:0:0:0: +400,192,18639,1,0,0:0:0:0: +448,192,18730,1,2,0:0:0:0: +368,192,18912,1,2,0:0:0:0: +192,192,19094,5,10,0:0:0:0: +144,192,19185,1,0,0:0:0:0: +192,192,19275,1,0,0:0:0:0: +304,192,19457,1,0,0:0:0:0: +352,192,19548,2,0,L|352:240,1,40,0|2,0:0|0:0,0:0:0:0: +272,272,19821,1,0,0:0:0:0: +384,272,20003,1,2,0:0:0:0: +304,272,20185,2,0,L|304:224,1,40,2|0,0:0|0:0,0:0:0:0: +352,192,20366,1,2,0:0:0:0: +176,272,20548,5,10,0:0:0:0: +96,272,20730,1,0,0:0:0:0: +144,272,20821,1,0,0:0:0:0: +192,272,20912,1,0,0:0:0:0: +80,272,21094,2,0,B|64:192|64:192|144:192,1,160,2|2,0:0|0:0,0:0:0:0: +96,192,21548,1,0,0:0:0:0: +48,192,21639,1,2,0:0:0:0: +128,192,21821,1,2,0:0:0:0: +304,192,22003,5,10,0:0:0:0: +352,192,22094,1,0,0:0:0:0: +304,192,22185,1,0,0:0:0:0: +192,192,22366,1,0,0:0:0:0: +144,192,22457,2,0,L|144:144,1,40,0|2,0:0|0:0,0:0:0:0: +224,80,22730,6,0,B|144:80|144:80|224:80|224:80|144:80,1,240,2|2,0:0|0:0,0:0:0:0: +400,80,23457,5,12,0:0:0:0: +480,80,23639,2,0,L|480:128,1,40,2|0,0:0|0:0,0:0:0:0: +432,144,23821,1,2,0:0:0:0: +320,144,24003,1,8,0:0:0:0: +64,192,24185,2,0,L|48:112,1,80,10|2,0:0|0:0,0:0:0:0: +96,96,24457,1,0,0:0:0:0: +144,80,24548,1,0,0:0:0:0: +64,80,24730,1,2,0:0:0:0: +240,80,24912,5,8,0:0:0:0: +320,80,25094,2,0,L|368:80,2,40,2|0|2,0:0|0:0|0:0,0:0:0:0: +208,80,25457,1,8,0:0:0:0: +464,192,25639,2,0,L|448:112,1,80,8|2,0:0|2:0,0:0:0:0: +336,192,26003,2,0,L|320:112,1,80,2|2,0:0|2:0,0:0:0:0: +496,48,26366,5,8,0:0:0:0: +416,48,26548,2,0,L|416:96,1,40,2|0,0:0|0:0,0:0:0:0: +464,128,26730,1,2,0:0:0:0: +352,128,26912,1,8,0:0:0:0: +96,128,27094,2,0,L|80:48,1,80,10|2,0:0|0:0,0:0:0:0: +192,48,27457,1,0,0:0:0:0: +240,48,27548,2,0,L|240:96,1,40,0|2,0:0|0:0,0:0:0:0: +64,192,27821,5,8,0:0:0:0: +176,192,28003,1,2,0:0:0:0: +64,192,28185,1,2,0:0:0:0: +16,192,28275,2,0,L|16:144,1,40,0|8,0:0|0:0,0:0:0:0: +272,192,28548,2,0,L|352:192,1,80,10|10,0:0|0:0,0:0:0:0: +240,128,28912,2,0,L|160:128,1,80,8|10,0:0|0:0,0:0:0:0: +416,128,29275,5,12,0:0:0:0: +496,128,29457,2,0,L|496:80,1,40,2|0,0:0|0:0,0:0:0:0: +448,64,29639,1,2,0:0:0:0: +336,64,29821,1,8,0:0:0:0: +80,192,30003,2,0,L|32:128,1,80,10|2,0:0|0:0,0:0:0:0: +32,80,30275,1,0,0:0:0:0: +64,80,30366,1,0,0:0:0:0: +144,80,30548,1,2,0:0:0:0: +320,80,30730,5,8,0:0:0:0: +240,80,30912,2,0,L|192:80,2,40,2|0|2,0:0|0:0|0:0,0:0:0:0: +352,80,31275,1,8,0:0:0:0: +96,192,31457,2,0,L|80:112,1,80,8|2,0:0|2:0,0:0:0:0: +192,112,31821,1,2,0:0:0:0: +80,112,32003,1,2,2:0:0:0: +256,112,32185,5,8,0:0:0:0: +336,112,32366,2,0,L|336:160,1,40,2|0,0:0|0:0,0:0:0:0: +288,192,32548,1,2,0:0:0:0: +400,192,32730,1,8,0:0:0:0: +144,192,32912,2,0,L|128:112,1,80,10|2,0:0|0:0,0:0:0:0: +240,112,33275,1,0,0:0:0:0: +288,112,33366,1,0,0:0:0:0: +240,112,33457,1,2,0:0:0:0: +128,192,33639,5,8,0:0:0:0: +240,192,33821,1,2,0:0:0:0: +128,192,34003,1,2,0:0:0:0: +80,192,34094,2,0,L|80:144,1,40,0|8,0:0|0:0,0:0:0:0: +336,192,34366,2,0,L|416:192,1,80,10|0,0:0|0:0,0:0:0:0: +240,128,34730,2,0,L|160:128,1,80,10|0,0:0|0:0,0:0:0:0: +432,128,35094,6,0,L|432:80,1,40,12|0,0:0|0:0,0:0:0:0: +384,64,35275,1,2,0:0:0:0: +208,64,35457,2,0,L|128:64,1,80,8|0,0:0|0:0,0:0:0:0: +384,192,35821,1,8,0:0:0:0: +464,128,36003,1,2,0:0:0:0: +384,128,36094,1,2,0:0:0:0: +336,128,36185,1,8,0:0:0:0: +448,128,36366,1,2,0:0:0:0: +192,128,36548,6,0,L|144:128,2,40,8|0|2,0:0|0:0|0:0,0:0:0:0: +368,128,36912,1,8,0:0:0:0: +416,144,37003,2,0,L|416:192,1,40,0|2,0:0|0:0,0:0:0:0: +160,192,37275,2,0,L|144:112,1,80,8|2,0:0|0:0,0:0:0:0: +192,80,37548,1,2,0:0:0:0: +272,80,37639,1,8,0:0:0:0: +160,80,37821,1,2,0:0:0:0: +416,192,38003,5,8,0:0:0:0: +496,192,38185,2,0,L|496:144,1,40,2|0,0:0|0:0,0:0:0:0: +416,144,38366,1,8,0:0:0:0: +496,144,38548,1,0,0:0:0:0: +240,144,38730,1,8,0:0:0:0: +192,144,38821,1,0,0:0:0:0: +240,144,38912,1,2,0:0:0:0: +352,144,39094,2,0,L|272:144,1,80,8|2,0:0|0:0,0:0:0:0: +16,192,39457,6,0,L|16:144,1,40,8|0,0:0|0:0,0:0:0:0: +64,128,39639,1,2,0:0:0:0: +240,176,39821,2,0,L|208:144,1,40,8|0,0:0|0:0,0:0:0:0: +160,128,40003,1,2,0:0:0:0: +416,128,40185,1,8,0:0:0:0: +464,128,40275,1,0,0:0:0:0: +416,128,40366,1,2,0:0:0:0: +240,128,40548,1,8,0:0:0:0: +288,128,40639,1,0,0:0:0:0: +336,128,40730,1,2,0:0:0:0: +64,128,40912,6,0,L|64:80,1,40,12|0,0:0|0:0,0:0:0:0: +112,64,41094,1,2,0:0:0:0: +288,64,41275,2,0,L|368:64,1,80,8|0,0:0|0:0,0:0:0:0: +112,192,41639,1,8,0:0:0:0: +32,128,41821,1,2,0:0:0:0: +112,128,41912,1,2,0:0:0:0: +160,128,42003,1,8,0:0:0:0: +48,128,42185,1,2,0:0:0:0: +304,192,42366,6,0,L|384:192,1,80,8|2,0:0|0:0,0:0:0:0: +208,96,42730,2,0,L|128:96,1,80,8|2,0:0|0:0,0:0:0:0: +384,192,43094,2,0,L|400:272,1,80,8|2,0:0|0:0,0:0:0:0: +352,304,43366,1,2,0:0:0:0: +272,304,43457,1,8,0:0:0:0: +384,304,43639,1,2,0:0:0:0: +128,192,43821,5,8,0:0:0:0: +48,192,44003,2,0,L|48:144,1,40,2|0,0:0|0:0,0:0:0:0: +128,144,44185,1,8,0:0:0:0: +48,144,44366,1,2,0:0:0:0: +304,144,44548,1,8,0:0:0:0: +384,144,44730,1,2,0:0:0:0: +336,144,44821,1,0,0:0:0:0: +256,144,44912,1,8,0:0:0:0: +368,144,45094,1,2,0:0:0:0: +112,256,45275,5,8,0:0:0:0: +64,256,45366,1,0,0:0:0:0: +112,256,45457,1,2,0:0:0:0: +288,256,45639,1,8,0:0:0:0: +336,224,45730,2,0,L|336:176,1,40,0|2,0:0|0:0,0:0:0:0: +80,192,46003,2,0,L|80:152,1,40,8|0,0:0|0:0,0:0:0:0: +128,120,46185,1,2,0:0:0:0: +304,128,46366,1,8,0:0:0:0: +256,128,46457,1,0,0:0:0:0: +208,128,46548,1,2,0:0:0:0: +464,192,46730,5,12,0:0:0:0: +256,192,46912,12,2,49639,0:0:0:0: +200,192,52548,6,0,L|184:112,1,80,2|0,0:0|0:0,0:0:0:0: +280,192,52912,2,0,L|264:112,1,80,2|0,0:0|0:0,0:0:0:0: +104,112,53457,1,2,0:0:0:0: +184,113,53639,1,2,0:0:0:0: +344,192,54003,6,0,L|360:112,1,80,2|0,0:0|0:0,0:0:0:0: +256,192,54366,2,0,L|272:112,1,80,2|0,0:0|0:0,0:0:0:0: +448,112,54912,1,0,0:0:0:0: +360,112,55094,1,2,0:0:0:0: +176,192,55457,6,0,L|160:112,1,80,2|0,0:0|0:0,0:0:0:0: +272,192,55821,2,0,L|256:112,1,80,2|0,0:0|0:0,0:0:0:0: +64,112,56366,1,2,0:0:0:0: +160,112,56548,1,2,0:0:0:0: +368,192,56912,6,0,L|384:112,1,80,2|0,0:0|0:0,0:0:0:0: +264,192,57275,2,0,L|280:112,1,80,2|0,0:0|0:0,0:0:0:0: +400,112,57639,2,0,L|320:112,1,80,2|2,0:0|0:0,0:0:0:0: +464,112,58003,1,2,0:0:0:0: +320,112,58185,1,2,0:0:0:0: +144,112,58366,5,12,0:0:0:0: +224,112,58548,2,0,L|272:112,2,40,2|0|8,0:0|0:0|0:0,0:0:0:0: +144,112,58912,1,2,0:0:0:0: +16,192,59094,2,0,L|16:144,1,40,2|0,3:0|0:0,0:0:0:0: +64,112,59275,1,2,0:0:0:0: +144,112,59457,1,8,0:0:0:0: +64,112,59639,1,2,0:0:0:0: +240,192,59821,6,0,L|240:144,1,40,2|0,3:0|0:0,0:0:0:0: +192,128,60003,1,2,0:0:0:0: +80,192,60185,1,8,0:0:0:0: +128,192,60275,1,0,0:0:0:0: +176,192,60366,1,2,0:0:0:0: +64,192,60548,2,0,L|64:144,1,40,2|0,3:0|0:0,0:0:0:0: +112,128,60730,1,2,0:0:0:0: +224,128,60912,2,0,L|144:128,1,80,10|10,0:0|0:0,0:0:0:0: +320,128,61275,5,2,3:0:0:0: +400,128,61457,2,0,L|400:176,1,40,2|0,0:0|0:0,0:0:0:0: +352,192,61639,1,8,0:0:0:0: +432,192,61821,1,2,0:0:0:0: +320,192,62003,2,0,L|320:240,1,40,2|0,3:0|0:0,0:0:0:0: +368,272,62185,1,2,0:0:0:0: +448,272,62366,1,8,0:0:0:0: +368,272,62548,1,2,0:0:0:0: +192,272,62730,5,2,3:0:0:0: +272,272,62912,1,2,0:0:0:0: +192,272,63094,2,0,L|144:272,2,40,8|0|2,0:0|0:0|0:0,0:0:0:0: +304,192,63457,2,0,L|224:192,1,80,2|2,3:0|0:0,0:0:0:0: +112,112,63821,2,0,L|192:112,1,80,10|10,0:0|0:0,0:0:0:0: +368,112,64185,5,2,3:0:0:0: +288,112,64366,2,0,L|240:112,2,40,2|0|8,0:0|0:0|0:0,0:0:0:0: +368,112,64730,1,2,0:0:0:0: +448,192,64912,1,2,3:0:0:0: +368,192,65094,2,0,L|320:192,2,40,2|0|8,0:0|0:0|0:0,0:0:0:0: +448,192,65457,1,2,0:0:0:0: +272,192,65639,6,0,L|272:240,1,40,2|0,3:0|0:0,0:0:0:0: +320,256,65821,1,2,0:0:0:0: +432,192,66003,1,8,0:0:0:0: +384,192,66094,1,0,0:0:0:0: +336,192,66185,1,2,0:0:0:0: +224,112,66366,1,2,3:0:0:0: +272,112,66457,1,0,0:0:0:0: +320,112,66548,1,2,0:0:0:0: +432,112,66730,1,10,0:0:0:0: +320,112,66912,1,10,0:0:0:0: +144,112,67094,5,2,3:0:0:0: +64,112,67275,2,0,L|64:160,1,40,2|0,0:0|0:0,0:0:0:0: +112,176,67457,1,8,0:0:0:0: +192,176,67639,1,2,0:0:0:0: +80,176,67821,2,0,L|96:224,1,40,2|0,3:0|0:0,0:0:0:0: +144,256,68003,1,2,0:0:0:0: +224,256,68185,1,8,0:0:0:0: +144,256,68366,1,2,0:0:0:0: +320,192,68548,5,2,3:0:0:0: +400,112,68730,2,0,L|408:72,1,40,8|2,0:0|0:0,0:0:0:0: +296,64,69003,2,0,L|256:64,1,40,2|10,0:0|0:0,0:0:0:0: +368,192,69275,1,2,3:0:0:0: +256,192,69457,1,10,0:0:0:0: +80,192,69639,1,10,0:0:0:0: +192,192,69821,1,10,0:0:0:0: +448,192,70003,5,12,0:0:0:0: +160,192,75821,6,0,L|80:192,1,80,10|0,0:0|0:0,0:0:0:0: +160,112,76185,2,0,L|80:112,1,80,10|0,0:0|0:0,0:0:0:0: +160,112,76548,1,2,0:0:0:0: +240,112,76730,1,0,0:0:0:0: +240,112,76912,1,2,0:0:0:0: +240,112,77094,1,0,0:0:0:0: +368,192,77275,6,0,L|448:192,1,80,10|0,0:0|0:0,0:0:0:0: +368,112,77639,2,0,L|448:112,1,80,10|0,0:0|0:0,0:0:0:0: +352,112,78003,1,2,0:0:0:0: +256,112,78185,1,0,0:0:0:0: +256,208,78366,1,2,0:0:0:0: +352,208,78548,1,0,0:0:0:0: +176,192,78730,6,0,L|96:192,1,80,10|0,0:0|0:0,0:0:0:0: +176,112,79094,2,0,L|96:112,1,80,10|0,0:0|0:0,0:0:0:0: +192,112,79457,1,2,0:0:0:0: +288,112,79639,1,0,0:0:0:0: +192,208,79821,1,2,0:0:0:0: +288,208,80003,1,0,0:0:0:0: +256,192,80185,12,10,81275,0:0:0:0: +416,192,81639,5,14,0:0:0:0: +336,192,81821,2,0,L|304:160,2,40 +448,192,82185,1,2,0:0:0:0: +288,192,82548,2,0,L|260:220,2,40,2|0|2,0:0|0:0|0:0,0:0:0:0: +368,192,82912,1,2,0:0:0:0: +192,192,83094,5,10,0:0:0:0: +144,192,83185,1,0,0:0:0:0: +192,192,83275,1,0,0:0:0:0: +304,192,83457,1,0,0:0:0:0: +352,192,83548,2,0,L|352:144,1,40,0|2,0:0|0:0,0:0:0:0: +272,112,83821,1,0,0:0:0:0: +384,112,84003,1,2,0:0:0:0: +304,112,84185,2,0,L|304:160,1,40,2|0,0:0|0:0,0:0:0:0: +352,192,84366,1,2,0:0:0:0: +176,192,84548,5,10,0:0:0:0: +256,192,84730,2,0,L|288:224,2,40,0|0|0,0:0|0:0|0:0,0:0:0:0: +144,192,85094,1,2,0:0:0:0: +304,192,85457,2,0,L|332:164,2,40,2|0|2,0:0|0:0|0:0,0:0:0:0: +224,192,85821,1,2,0:0:0:0: +400,192,86003,5,10,0:0:0:0: +448,192,86094,1,0,0:0:0:0: +400,192,86185,1,0,0:0:0:0: +288,192,86366,1,0,0:0:0:0: +240,192,86457,2,0,L|240:240,1,40,0|2,0:0|0:0,0:0:0:0: +352,192,86730,1,0,0:0:0:0: +304,192,86821,1,0,0:0:0:0: +256,192,86912,1,2,0:0:0:0: +368,112,87094,1,2,0:0:0:0: +432,112,87185,1,0,0:0:0:0: +368,112,87275,1,2,0:0:0:0: +192,112,87457,5,10,0:0:0:0: +112,112,87639,1,0,0:0:0:0: +160,112,87730,1,0,0:0:0:0: +208,112,87821,1,0,0:0:0:0: +96,112,88003,2,0,B|80:192|80:192|160:192,1,160,2|2,0:0|0:0,0:0:0:0: +112,192,88457,1,0,0:0:0:0: +64,192,88548,1,2,0:0:0:0: +144,192,88730,1,2,0:0:0:0: +320,192,88912,5,10,0:0:0:0: +368,192,89003,1,0,0:0:0:0: +320,192,89094,1,0,0:0:0:0: +208,192,89275,1,0,0:0:0:0: +160,192,89366,2,0,L|160:240,1,40,0|2,0:0|0:0,0:0:0:0: +240,272,89639,1,0,0:0:0:0: +128,272,89821,1,2,0:0:0:0: +208,272,90003,2,0,L|208:224,1,40,2|0,0:0|0:0,0:0:0:0: +160,192,90185,1,2,0:0:0:0: +336,272,90366,5,10,0:0:0:0: +416,272,90548,1,0,0:0:0:0: +368,272,90639,1,0,0:0:0:0: +320,272,90730,1,0,0:0:0:0: +432,272,90912,2,0,B|448:192|448:192|368:192,1,160,2|2,0:0|0:0,0:0:0:0: +416,192,91366,1,0,0:0:0:0: +464,192,91457,1,2,0:0:0:0: +384,192,91639,1,2,0:0:0:0: +208,192,91821,5,10,0:0:0:0: +160,192,91912,1,0,0:0:0:0: +208,192,92003,1,0,0:0:0:0: +320,192,92185,1,0,0:0:0:0: +368,192,92275,2,0,L|368:144,1,40,0|2,0:0|0:0,0:0:0:0: +288,80,92548,6,0,B|208:80|208:80|288:80|288:80|368:80,1,240,2|2,0:0|0:0,0:0:0:0: +112,80,93275,5,12,0:0:0:0: +32,80,93457,2,0,L|32:128,1,40,2|0,0:0|0:0,0:0:0:0: +80,144,93639,1,2,0:0:0:0: +192,144,93821,1,8,0:0:0:0: +448,192,94003,2,0,L|464:112,1,80,10|2,0:0|0:0,0:0:0:0: +416,96,94275,1,0,0:0:0:0: +368,80,94366,1,0,0:0:0:0: +448,80,94548,1,2,0:0:0:0: +272,80,94730,5,8,0:0:0:0: +192,80,94912,2,0,L|144:80,2,40,2|0|2,0:0|0:0|0:0,0:0:0:0: +304,80,95275,1,8,0:0:0:0: +48,192,95457,2,0,L|64:112,1,80,8|2,0:0|2:0,0:0:0:0: +176,192,95821,2,0,L|192:112,1,80,2|2,0:0|2:0,0:0:0:0: +16,48,96185,5,8,0:0:0:0: +96,48,96366,2,0,L|96:96,1,40,2|0,0:0|0:0,0:0:0:0: +48,128,96548,1,2,0:0:0:0: +160,128,96730,1,8,0:0:0:0: +416,128,96912,2,0,L|432:48,1,80,10|2,0:0|0:0,0:0:0:0: +320,48,97275,1,0,0:0:0:0: +272,48,97366,2,0,L|272:96,1,40,0|2,0:0|0:0,0:0:0:0: +448,192,97639,5,8,0:0:0:0: +336,192,97821,1,2,0:0:0:0: +448,192,98003,1,2,0:0:0:0: +496,192,98094,2,0,L|496:144,1,40,0|8,0:0|0:0,0:0:0:0: +240,192,98366,2,0,L|128:192,1,100,10|10,0:0|0:0,0:0:0:0: +240,128,98730,2,0,L|352:128,1,100,8|10,0:0|0:0,0:0:0:0: +96,128,99094,5,12,0:0:0:0: +16,128,99275,2,0,L|16:80,1,40,2|0,0:0|0:0,0:0:0:0: +64,64,99457,1,2,0:0:0:0: +176,64,99639,1,8,0:0:0:0: +432,192,99821,2,0,L|480:128,1,80,10|2,0:0|0:0,0:0:0:0: +480,80,100094,1,0,0:0:0:0: +448,80,100185,1,0,0:0:0:0: +368,80,100366,1,2,0:0:0:0: +192,80,100548,5,8,0:0:0:0: +272,80,100730,2,0,L|320:80,2,40,2|0|2,0:0|0:0|0:0,0:0:0:0: +160,80,101094,1,8,0:0:0:0: +416,192,101275,2,0,L|432:112,1,80,8|2,0:0|2:0,0:0:0:0: +320,112,101639,1,2,0:0:0:0: +432,112,101821,1,2,2:0:0:0: +256,112,102003,5,8,0:0:0:0: +176,112,102185,2,0,L|176:160,1,40,2|0,0:0|0:0,0:0:0:0: +224,192,102366,1,2,0:0:0:0: +112,192,102548,1,8,0:0:0:0: +368,192,102730,2,0,L|384:112,1,80,10|2,0:0|0:0,0:0:0:0: +272,112,103094,1,0,0:0:0:0: +224,112,103185,1,0,0:0:0:0: +272,112,103275,1,2,0:0:0:0: +384,192,103457,5,8,0:0:0:0: +272,192,103639,1,2,0:0:0:0: +384,192,103821,1,2,0:0:0:0: +432,192,103912,2,0,L|432:144,1,40,0|8,0:0|0:0,0:0:0:0: +176,192,104185,2,0,L|96:192,1,80,10|0,0:0|0:0,0:0:0:0: +272,128,104548,2,0,L|352:128,1,80,10|0,0:0|0:0,0:0:0:0: +80,128,104912,6,0,L|80:80,1,40,12|0,0:0|0:0,0:0:0:0: +128,64,105094,1,2,0:0:0:0: +304,64,105275,2,0,L|384:64,1,80,8|0,0:0|0:0,0:0:0:0: +128,192,105639,1,8,0:0:0:0: +48,128,105821,1,2,0:0:0:0: +128,128,105912,1,2,0:0:0:0: +176,128,106003,1,8,0:0:0:0: +64,128,106185,1,2,0:0:0:0: +320,128,106366,6,0,L|368:128,2,40,8|0|2,0:0|0:0|0:0,0:0:0:0: +144,128,106730,1,8,0:0:0:0: +96,144,106821,2,0,L|96:192,1,40,0|2,0:0|0:0,0:0:0:0: +352,192,107094,2,0,L|368:112,1,80,8|2,0:0|0:0,0:0:0:0: +320,80,107366,1,2,0:0:0:0: +240,80,107457,1,8,0:0:0:0: +352,80,107639,1,2,0:0:0:0: +96,192,107821,5,8,0:0:0:0: +16,192,108003,2,0,L|16:144,1,40,2|0,0:0|0:0,0:0:0:0: +96,144,108185,1,8,0:0:0:0: +16,144,108366,1,0,0:0:0:0: +272,144,108548,1,8,0:0:0:0: +320,144,108639,1,0,0:0:0:0: +272,144,108730,1,2,0:0:0:0: +160,144,108912,2,0,L|240:144,1,80,8|2,0:0|0:0,0:0:0:0: +496,192,109275,6,0,L|496:144,1,40,8|0,0:0|0:0,0:0:0:0: +448,128,109457,1,2,0:0:0:0: +272,176,109639,2,0,L|304:144,1,40,8|0,0:0|0:0,0:0:0:0: +352,128,109821,1,2,0:0:0:0: +96,128,110003,1,8,0:0:0:0: +48,128,110094,1,0,0:0:0:0: +96,128,110185,1,2,0:0:0:0: +272,128,110366,1,8,0:0:0:0: +224,128,110457,1,0,0:0:0:0: +176,128,110548,1,2,0:0:0:0: +448,128,110730,6,0,L|448:80,1,40,12|0,0:0|0:0,0:0:0:0: +400,64,110912,1,2,0:0:0:0: +224,64,111094,2,0,L|144:64,1,80,8|0,0:0|0:0,0:0:0:0: +400,192,111457,1,8,0:0:0:0: +480,128,111639,1,2,0:0:0:0: +400,128,111730,1,2,0:0:0:0: +352,128,111821,1,8,0:0:0:0: +464,128,112003,1,2,0:0:0:0: +208,192,112185,6,0,L|128:192,1,80,8|2,0:0|0:0,0:0:0:0: +304,96,112548,2,0,L|384:96,1,80,8|2,0:0|0:0,0:0:0:0: +128,192,112912,2,0,L|112:272,1,80,8|2,0:0|0:0,0:0:0:0: +160,304,113185,1,2,0:0:0:0: +240,304,113275,1,8,0:0:0:0: +128,304,113457,1,2,0:0:0:0: +384,192,113639,5,8,0:0:0:0: +464,192,113821,2,0,L|464:144,1,40,2|0,0:0|0:0,0:0:0:0: +384,144,114003,1,8,0:0:0:0: +464,144,114185,1,2,0:0:0:0: +208,144,114366,1,8,0:0:0:0: +128,144,114548,1,2,0:0:0:0: +176,144,114639,1,0,0:0:0:0: +256,144,114730,1,8,0:0:0:0: +144,144,114912,1,2,0:0:0:0: +400,256,115094,5,8,0:0:0:0: +448,256,115185,1,0,0:0:0:0: +400,256,115275,1,2,0:0:0:0: +224,256,115457,1,8,0:0:0:0: +176,224,115548,2,0,L|176:176,1,40,0|2,0:0|0:0,0:0:0:0: +432,192,115821,2,0,L|432:152,1,40,8|0,0:0|0:0,0:0:0:0: +384,120,116003,1,2,0:0:0:0: +208,128,116185,1,8,0:0:0:0: +256,128,116275,1,0,0:0:0:0: +304,128,116366,1,2,0:0:0:0: +48,192,116548,5,12,0:0:0:0: +256,192,116730,12,2,119457,0:0:0:0: +312,192,122366,6,0,L|328:112,1,80,2|0,0:0|0:0,0:0:0:0: +232,192,122730,2,0,L|248:112,1,80,2|0,0:0|0:0,0:0:0:0: +408,112,123275,1,2,0:0:0:0: +328,113,123457,1,2,0:0:0:0: +168,192,123821,6,0,L|152:112,1,80,2|0,0:0|0:0,0:0:0:0: +256,192,124185,2,0,L|240:112,1,80,2|0,0:0|0:0,0:0:0:0: +64,112,124730,1,0,0:0:0:0: +152,112,124912,1,2,0:0:0:0: +336,192,125275,6,0,L|352:112,1,80,2|0,0:0|0:0,0:0:0:0: +240,192,125639,2,0,L|256:112,1,80,2|0,0:0|0:0,0:0:0:0: +448,112,126185,1,2,0:0:0:0: +352,112,126366,1,2,0:0:0:0: +144,192,126730,6,0,L|128:112,1,80,2|0,0:0|0:0,0:0:0:0: +248,192,127094,2,0,L|232:112,1,80,2|0,0:0|0:0,0:0:0:0: +112,112,127457,2,0,L|192:112,1,80,2|2,0:0|0:0,0:0:0:0: +48,112,127821,1,2,0:0:0:0: +192,112,128003,1,2,0:0:0:0: +368,112,128185,5,12,0:0:0:0: +288,112,128366,2,0,L|240:112,2,40,2|0|8,0:0|0:0|0:0,0:0:0:0: +368,112,128730,1,2,0:0:0:0: +496,192,128912,2,0,L|496:144,1,40,2|0,3:0|0:0,0:0:0:0: +448,112,129094,1,2,0:0:0:0: +368,112,129275,1,8,0:0:0:0: +448,112,129457,1,2,0:0:0:0: +272,192,129639,6,0,L|272:144,1,40,2|0,3:0|0:0,0:0:0:0: +320,128,129821,1,2,0:0:0:0: +432,192,130003,1,8,0:0:0:0: +384,192,130094,1,0,0:0:0:0: +336,192,130185,1,2,0:0:0:0: +448,192,130366,2,0,L|448:144,1,40,2|0,3:0|0:0,0:0:0:0: +400,128,130548,1,2,0:0:0:0: +288,128,130730,2,0,L|368:128,1,80,10|10,0:0|0:0,0:0:0:0: +192,128,131094,5,2,3:0:0:0: +112,128,131275,2,0,L|112:176,1,40,2|0,0:0|0:0,0:0:0:0: +160,192,131457,1,8,0:0:0:0: +80,192,131639,1,2,0:0:0:0: +192,192,131821,2,0,L|192:240,1,40,2|0,3:0|0:0,0:0:0:0: +144,272,132003,1,2,0:0:0:0: +64,272,132185,1,8,0:0:0:0: +144,272,132366,1,2,0:0:0:0: +320,272,132548,5,2,3:0:0:0: +240,272,132730,1,2,0:0:0:0: +320,272,132912,2,0,L|368:272,2,40,8|0|2,0:0|0:0|0:0,0:0:0:0: +208,192,133275,2,0,L|288:192,1,80,2|2,3:0|0:0,0:0:0:0: +400,112,133639,2,0,L|320:112,1,80,10|10,0:0|0:0,0:0:0:0: +144,112,134003,5,2,3:0:0:0: +224,112,134185,2,0,L|272:112,2,40,2|0|8,0:0|0:0|0:0,0:0:0:0: +144,112,134548,1,2,0:0:0:0: +64,192,134730,1,2,3:0:0:0: +144,192,134912,2,0,L|192:192,2,40,2|0|8,0:0|0:0|0:0,0:0:0:0: +64,192,135275,1,2,0:0:0:0: +240,192,135457,6,0,L|240:240,1,40,2|0,3:0|0:0,0:0:0:0: +192,256,135639,1,2,0:0:0:0: +80,192,135821,1,8,0:0:0:0: +128,192,135912,1,0,0:0:0:0: +176,192,136003,1,2,0:0:0:0: +288,112,136185,1,2,3:0:0:0: +240,112,136275,1,0,0:0:0:0: +192,112,136366,1,2,0:0:0:0: +80,112,136548,1,10,0:0:0:0: +192,112,136730,1,10,0:0:0:0: +368,112,136912,5,2,3:0:0:0: +448,112,137094,2,0,L|448:160,1,40,2|0,0:0|0:0,0:0:0:0: +400,176,137275,1,8,0:0:0:0: +320,176,137457,1,2,0:0:0:0: +432,176,137639,2,0,L|416:224,1,40,2|0,3:0|0:0,0:0:0:0: +368,256,137821,1,2,0:0:0:0: +288,256,138003,1,8,0:0:0:0: +368,256,138185,1,2,0:0:0:0: +192,192,138366,5,2,3:0:0:0: +112,112,138548,2,0,L|104:72,1,40,8|2,0:0|0:0,0:0:0:0: +216,64,138821,2,0,L|256:64,1,40,2|10,0:0|0:0,0:0:0:0: +144,192,139094,1,2,3:0:0:0: +224,192,139275,1,10,0:0:0:0: +48,192,139457,1,10,0:0:0:0: +160,192,139639,1,10,0:0:0:0: +416,192,139821,5,12,0:0:0:0: +256,192,140003,12,0,143457,0:0:0:0: diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/42587-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/42587-expected-conversion.json new file mode 100644 index 0000000000..42df40a57e --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/42587-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":24383.0,"Objects":[{"StartTime":24383.0,"Position":376.0,"HyperDash":false}]},{"StartTime":24478.0,"Objects":[{"StartTime":24478.0,"Position":392.0,"HyperDash":false}]},{"StartTime":24573.0,"Objects":[{"StartTime":24573.0,"Position":408.0,"HyperDash":false}]},{"StartTime":24763.0,"Objects":[{"StartTime":24763.0,"Position":448.0,"HyperDash":false},{"StartTime":24839.0,"Position":396.8095,"HyperDash":false},{"StartTime":24952.0,"Position":358.0,"HyperDash":false}]},{"StartTime":25143.0,"Objects":[{"StartTime":25143.0,"Position":280.0,"HyperDash":false}]},{"StartTime":25333.0,"Objects":[{"StartTime":25333.0,"Position":232.0,"HyperDash":false}]},{"StartTime":25523.0,"Objects":[{"StartTime":25523.0,"Position":152.0,"HyperDash":false},{"StartTime":25599.0,"Position":104.809525,"HyperDash":false},{"StartTime":25712.0,"Position":62.0,"HyperDash":false}]},{"StartTime":25902.0,"Objects":[{"StartTime":25902.0,"Position":32.0,"HyperDash":false}]},{"StartTime":26092.0,"Objects":[{"StartTime":26092.0,"Position":96.0,"HyperDash":false},{"StartTime":26186.0,"Position":119.750992,"HyperDash":false},{"StartTime":26281.0,"Position":168.644272,"HyperDash":false},{"StartTime":26376.0,"Position":215.703568,"HyperDash":false},{"StartTime":26471.0,"Position":244.0,"HyperDash":false},{"StartTime":26557.0,"Position":204.446625,"HyperDash":false},{"StartTime":26643.0,"Position":161.656128,"HyperDash":false},{"StartTime":26729.0,"Position":115.71936,"HyperDash":false},{"StartTime":26851.0,"Position":96.0,"HyperDash":false}]},{"StartTime":27042.0,"Objects":[{"StartTime":27042.0,"Position":96.0,"HyperDash":false}]},{"StartTime":27232.0,"Objects":[{"StartTime":27232.0,"Position":176.0,"HyperDash":false},{"StartTime":27308.0,"Position":204.190475,"HyperDash":false},{"StartTime":27421.0,"Position":266.0,"HyperDash":false}]},{"StartTime":27801.0,"Objects":[{"StartTime":27801.0,"Position":448.0,"HyperDash":false}]},{"StartTime":27991.0,"Objects":[{"StartTime":27991.0,"Position":360.0,"HyperDash":false}]},{"StartTime":28371.0,"Objects":[{"StartTime":28371.0,"Position":192.0,"HyperDash":false}]},{"StartTime":28561.0,"Objects":[{"StartTime":28561.0,"Position":280.0,"HyperDash":false}]},{"StartTime":28751.0,"Objects":[{"StartTime":28751.0,"Position":368.0,"HyperDash":false}]},{"StartTime":28940.0,"Objects":[{"StartTime":28940.0,"Position":456.0,"HyperDash":false}]},{"StartTime":29130.0,"Objects":[{"StartTime":29130.0,"Position":456.0,"HyperDash":false},{"StartTime":29224.0,"Position":417.249023,"HyperDash":false},{"StartTime":29319.0,"Position":394.3557,"HyperDash":false},{"StartTime":29414.0,"Position":367.296448,"HyperDash":false},{"StartTime":29509.0,"Position":308.0,"HyperDash":false},{"StartTime":29595.0,"Position":352.553375,"HyperDash":false},{"StartTime":29681.0,"Position":380.343872,"HyperDash":false},{"StartTime":29767.0,"Position":426.28064,"HyperDash":false},{"StartTime":29889.0,"Position":456.0,"HyperDash":false}]},{"StartTime":30080.0,"Objects":[{"StartTime":30080.0,"Position":456.0,"HyperDash":false}]},{"StartTime":30270.0,"Objects":[{"StartTime":30270.0,"Position":376.0,"HyperDash":false},{"StartTime":30346.0,"Position":320.8095,"HyperDash":false},{"StartTime":30459.0,"Position":286.0,"HyperDash":false}]},{"StartTime":30839.0,"Objects":[{"StartTime":30839.0,"Position":112.0,"HyperDash":false}]},{"StartTime":31029.0,"Objects":[{"StartTime":31029.0,"Position":176.0,"HyperDash":false}]},{"StartTime":31219.0,"Objects":[{"StartTime":31219.0,"Position":112.0,"HyperDash":false}]},{"StartTime":31314.0,"Objects":[{"StartTime":31314.0,"Position":112.0,"HyperDash":false}]},{"StartTime":31409.0,"Objects":[{"StartTime":31409.0,"Position":112.0,"HyperDash":false}]},{"StartTime":31599.0,"Objects":[{"StartTime":31599.0,"Position":176.0,"HyperDash":false}]},{"StartTime":31788.0,"Objects":[{"StartTime":31788.0,"Position":240.0,"HyperDash":false}]},{"StartTime":31978.0,"Objects":[{"StartTime":31978.0,"Position":176.0,"HyperDash":false}]},{"StartTime":32168.0,"Objects":[{"StartTime":32168.0,"Position":240.0,"HyperDash":false},{"StartTime":32262.0,"Position":264.85144,"HyperDash":false},{"StartTime":32357.0,"Position":329.8879,"HyperDash":false},{"StartTime":32452.0,"Position":373.9472,"HyperDash":false},{"StartTime":32547.0,"Position":402.243652,"HyperDash":false},{"StartTime":32633.0,"Position":374.690277,"HyperDash":false},{"StartTime":32719.0,"Position":312.89978,"HyperDash":false},{"StartTime":32805.0,"Position":266.934845,"HyperDash":false},{"StartTime":32927.0,"Position":240.0,"HyperDash":false}]},{"StartTime":33118.0,"Objects":[{"StartTime":33118.0,"Position":240.0,"HyperDash":false}]},{"StartTime":33307.0,"Objects":[{"StartTime":33307.0,"Position":328.0,"HyperDash":false},{"StartTime":33383.0,"Position":341.0,"HyperDash":false},{"StartTime":33496.0,"Position":298.93808,"HyperDash":false}]},{"StartTime":33877.0,"Objects":[{"StartTime":33877.0,"Position":136.0,"HyperDash":false}]},{"StartTime":34067.0,"Objects":[{"StartTime":34067.0,"Position":80.0,"HyperDash":false}]},{"StartTime":34257.0,"Objects":[{"StartTime":34257.0,"Position":24.0,"HyperDash":false}]},{"StartTime":34352.0,"Objects":[{"StartTime":34352.0,"Position":24.0,"HyperDash":false}]},{"StartTime":34447.0,"Objects":[{"StartTime":34447.0,"Position":24.0,"HyperDash":false}]},{"StartTime":34542.0,"Objects":[{"StartTime":34542.0,"Position":40.0,"HyperDash":false}]},{"StartTime":34637.0,"Objects":[{"StartTime":34637.0,"Position":56.0,"HyperDash":false}]},{"StartTime":34826.0,"Objects":[{"StartTime":34826.0,"Position":144.0,"HyperDash":false}]},{"StartTime":35016.0,"Objects":[{"StartTime":35016.0,"Position":232.0,"HyperDash":false}]},{"StartTime":35206.0,"Objects":[{"StartTime":35206.0,"Position":376.0,"HyperDash":false},{"StartTime":35300.0,"Position":413.2887,"HyperDash":false},{"StartTime":35395.0,"Position":455.196533,"HyperDash":false},{"StartTime":35472.0,"Position":429.631622,"HyperDash":false},{"StartTime":35585.0,"Position":376.0,"HyperDash":false}]},{"StartTime":35776.0,"Objects":[{"StartTime":35776.0,"Position":232.0,"HyperDash":false},{"StartTime":35852.0,"Position":181.590073,"HyperDash":false},{"StartTime":35965.0,"Position":152.803467,"HyperDash":false}]},{"StartTime":36156.0,"Objects":[{"StartTime":36156.0,"Position":304.0,"HyperDash":false}]},{"StartTime":36345.0,"Objects":[{"StartTime":36345.0,"Position":304.0,"HyperDash":false},{"StartTime":36421.0,"Position":300.0,"HyperDash":false},{"StartTime":36534.0,"Position":304.0,"HyperDash":false}]},{"StartTime":36725.0,"Objects":[{"StartTime":36725.0,"Position":112.0,"HyperDash":false},{"StartTime":36801.0,"Position":78.63026,"HyperDash":false},{"StartTime":36914.0,"Position":31.5015564,"HyperDash":false}]},{"StartTime":37105.0,"Objects":[{"StartTime":37105.0,"Position":112.0,"HyperDash":false},{"StartTime":37181.0,"Position":93.63026,"HyperDash":false},{"StartTime":37294.0,"Position":31.5015564,"HyperDash":false}]},{"StartTime":37485.0,"Objects":[{"StartTime":37485.0,"Position":112.0,"HyperDash":false}]},{"StartTime":37675.0,"Objects":[{"StartTime":37675.0,"Position":112.0,"HyperDash":false},{"StartTime":37769.0,"Position":57.8074036,"HyperDash":false},{"StartTime":37864.0,"Position":32.9893951,"HyperDash":false},{"StartTime":37941.0,"Position":45.885498,"HyperDash":false},{"StartTime":38054.0,"Position":112.0,"HyperDash":false}]},{"StartTime":38244.0,"Objects":[{"StartTime":38244.0,"Position":112.0,"HyperDash":false}]},{"StartTime":38434.0,"Objects":[{"StartTime":38434.0,"Position":32.0,"HyperDash":false}]},{"StartTime":38624.0,"Objects":[{"StartTime":38624.0,"Position":112.0,"HyperDash":false}]},{"StartTime":38814.0,"Objects":[{"StartTime":38814.0,"Position":32.0,"HyperDash":false}]},{"StartTime":39004.0,"Objects":[{"StartTime":39004.0,"Position":112.0,"HyperDash":false}]},{"StartTime":39194.0,"Objects":[{"StartTime":39194.0,"Position":200.0,"HyperDash":false},{"StartTime":39270.0,"Position":220.190475,"HyperDash":false},{"StartTime":39383.0,"Position":290.0,"HyperDash":false}]},{"StartTime":39573.0,"Objects":[{"StartTime":39573.0,"Position":384.0,"HyperDash":false},{"StartTime":39649.0,"Position":360.8095,"HyperDash":false},{"StartTime":39762.0,"Position":294.0,"HyperDash":false}]},{"StartTime":39953.0,"Objects":[{"StartTime":39953.0,"Position":200.0,"HyperDash":false},{"StartTime":40047.0,"Position":261.604553,"HyperDash":false},{"StartTime":40142.0,"Position":290.0,"HyperDash":false},{"StartTime":40237.0,"Position":244.237259,"HyperDash":false},{"StartTime":40332.0,"Position":200.0,"HyperDash":false},{"StartTime":40409.0,"Position":229.379608,"HyperDash":false},{"StartTime":40522.0,"Position":290.0,"HyperDash":false}]},{"StartTime":40713.0,"Objects":[{"StartTime":40713.0,"Position":408.0,"HyperDash":false}]},{"StartTime":40902.0,"Objects":[{"StartTime":40902.0,"Position":360.0,"HyperDash":false}]},{"StartTime":41092.0,"Objects":[{"StartTime":41092.0,"Position":280.0,"HyperDash":false},{"StartTime":41177.0,"Position":240.0683,"HyperDash":false},{"StartTime":41263.0,"Position":219.442886,"HyperDash":false},{"StartTime":41349.0,"Position":153.743317,"HyperDash":false},{"StartTime":41471.0,"Position":103.528549,"HyperDash":false}]},{"StartTime":41662.0,"Objects":[{"StartTime":41662.0,"Position":168.0,"HyperDash":false},{"StartTime":41756.0,"Position":130.327,"HyperDash":false},{"StartTime":41851.0,"Position":85.20778,"HyperDash":false},{"StartTime":41928.0,"Position":130.082031,"HyperDash":false},{"StartTime":42041.0,"Position":168.0,"HyperDash":false}]},{"StartTime":42232.0,"Objects":[{"StartTime":42232.0,"Position":264.0,"HyperDash":false},{"StartTime":42317.0,"Position":321.196838,"HyperDash":false},{"StartTime":42403.0,"Position":360.600128,"HyperDash":false},{"StartTime":42489.0,"Position":394.9759,"HyperDash":false},{"StartTime":42611.0,"Position":423.349854,"HyperDash":false}]},{"StartTime":42801.0,"Objects":[{"StartTime":42801.0,"Position":320.0,"HyperDash":false},{"StartTime":42877.0,"Position":322.0243,"HyperDash":false},{"StartTime":42990.0,"Position":282.757751,"HyperDash":false}]},{"StartTime":43181.0,"Objects":[{"StartTime":43181.0,"Position":184.0,"HyperDash":false},{"StartTime":43257.0,"Position":210.32988,"HyperDash":false},{"StartTime":43370.0,"Position":227.0967,"HyperDash":false}]},{"StartTime":43561.0,"Objects":[{"StartTime":43561.0,"Position":227.0,"HyperDash":false}]},{"StartTime":43751.0,"Objects":[{"StartTime":43751.0,"Position":192.0,"HyperDash":false},{"StartTime":43845.0,"Position":152.03096,"HyperDash":false},{"StartTime":43940.0,"Position":145.695374,"HyperDash":false},{"StartTime":44017.0,"Position":169.388275,"HyperDash":false},{"StartTime":44130.0,"Position":192.0,"HyperDash":false}]},{"StartTime":44320.0,"Objects":[{"StartTime":44320.0,"Position":128.0,"HyperDash":false},{"StartTime":44405.0,"Position":142.777267,"HyperDash":false},{"StartTime":44491.0,"Position":173.576416,"HyperDash":false},{"StartTime":44577.0,"Position":244.930679,"HyperDash":false},{"StartTime":44699.0,"Position":284.40564,"HyperDash":false}]},{"StartTime":44890.0,"Objects":[{"StartTime":44890.0,"Position":376.0,"HyperDash":false}]},{"StartTime":45270.0,"Objects":[{"StartTime":45270.0,"Position":440.0,"HyperDash":false}]},{"StartTime":45459.0,"Objects":[{"StartTime":45459.0,"Position":384.0,"HyperDash":false}]},{"StartTime":45649.0,"Objects":[{"StartTime":45649.0,"Position":304.0,"HyperDash":false}]},{"StartTime":45839.0,"Objects":[{"StartTime":45839.0,"Position":216.0,"HyperDash":false},{"StartTime":45924.0,"Position":169.327576,"HyperDash":false},{"StartTime":46010.0,"Position":156.035263,"HyperDash":false},{"StartTime":46096.0,"Position":110.220467,"HyperDash":false},{"StartTime":46218.0,"Position":68.60332,"HyperDash":false}]},{"StartTime":46409.0,"Objects":[{"StartTime":46409.0,"Position":56.0,"HyperDash":false}]},{"StartTime":46788.0,"Objects":[{"StartTime":46788.0,"Position":216.0,"HyperDash":false}]},{"StartTime":46978.0,"Objects":[{"StartTime":46978.0,"Position":296.0,"HyperDash":false}]},{"StartTime":47168.0,"Objects":[{"StartTime":47168.0,"Position":216.0,"HyperDash":false}]},{"StartTime":47358.0,"Objects":[{"StartTime":47358.0,"Position":296.0,"HyperDash":false}]},{"StartTime":47738.0,"Objects":[{"StartTime":47738.0,"Position":136.0,"HyperDash":false}]},{"StartTime":48118.0,"Objects":[{"StartTime":48118.0,"Position":376.0,"HyperDash":false}]},{"StartTime":48497.0,"Objects":[{"StartTime":48497.0,"Position":136.0,"HyperDash":false}]},{"StartTime":48877.0,"Objects":[{"StartTime":48877.0,"Position":376.0,"HyperDash":false},{"StartTime":48953.0,"Position":329.8095,"HyperDash":false},{"StartTime":49066.0,"Position":286.0,"HyperDash":false}]},{"StartTime":49257.0,"Objects":[{"StartTime":49257.0,"Position":192.0,"HyperDash":false}]},{"StartTime":49447.0,"Objects":[{"StartTime":49447.0,"Position":128.0,"HyperDash":false}]},{"StartTime":49637.0,"Objects":[{"StartTime":49637.0,"Position":216.0,"HyperDash":false},{"StartTime":49731.0,"Position":261.604553,"HyperDash":false},{"StartTime":49826.0,"Position":306.0,"HyperDash":false},{"StartTime":49921.0,"Position":251.237244,"HyperDash":false},{"StartTime":50016.0,"Position":216.0,"HyperDash":false},{"StartTime":50093.0,"Position":252.379608,"HyperDash":false},{"StartTime":50206.0,"Position":306.0,"HyperDash":false}]},{"StartTime":50396.0,"Objects":[{"StartTime":50396.0,"Position":400.0,"HyperDash":false},{"StartTime":50481.0,"Position":405.0538,"HyperDash":false},{"StartTime":50567.0,"Position":419.1245,"HyperDash":false},{"StartTime":50653.0,"Position":441.139374,"HyperDash":false},{"StartTime":50775.0,"Position":411.5338,"HyperDash":false}]},{"StartTime":50966.0,"Objects":[{"StartTime":50966.0,"Position":336.0,"HyperDash":false},{"StartTime":51042.0,"Position":300.082825,"HyperDash":false},{"StartTime":51155.0,"Position":279.0086,"HyperDash":false}]},{"StartTime":51345.0,"Objects":[{"StartTime":51345.0,"Position":208.0,"HyperDash":false}]},{"StartTime":51535.0,"Objects":[{"StartTime":51535.0,"Position":168.0,"HyperDash":false}]},{"StartTime":51725.0,"Objects":[{"StartTime":51725.0,"Position":120.0,"HyperDash":false}]},{"StartTime":51915.0,"Objects":[{"StartTime":51915.0,"Position":72.0,"HyperDash":false},{"StartTime":52000.0,"Position":46.90651,"HyperDash":false},{"StartTime":52086.0,"Position":66.65258,"HyperDash":false},{"StartTime":52172.0,"Position":76.80537,"HyperDash":false},{"StartTime":52294.0,"Position":126.392281,"HyperDash":false}]},{"StartTime":52485.0,"Objects":[{"StartTime":52485.0,"Position":216.0,"HyperDash":false}]},{"StartTime":52675.0,"Objects":[{"StartTime":52675.0,"Position":304.0,"HyperDash":false}]},{"StartTime":52864.0,"Objects":[{"StartTime":52864.0,"Position":232.0,"HyperDash":false}]},{"StartTime":53054.0,"Objects":[{"StartTime":53054.0,"Position":312.0,"HyperDash":false}]},{"StartTime":53244.0,"Objects":[{"StartTime":53244.0,"Position":288.0,"HyperDash":false},{"StartTime":53329.0,"Position":335.2697,"HyperDash":false},{"StartTime":53415.0,"Position":365.515228,"HyperDash":false},{"StartTime":53501.0,"Position":421.4718,"HyperDash":false},{"StartTime":53623.0,"Position":449.9475,"HyperDash":false}]},{"StartTime":53814.0,"Objects":[{"StartTime":53814.0,"Position":392.0,"HyperDash":false},{"StartTime":53890.0,"Position":357.8421,"HyperDash":false},{"StartTime":54003.0,"Position":349.331024,"HyperDash":false}]},{"StartTime":54194.0,"Objects":[{"StartTime":54194.0,"Position":280.0,"HyperDash":false},{"StartTime":54270.0,"Position":256.0476,"HyperDash":false},{"StartTime":54383.0,"Position":208.0,"HyperDash":false}]},{"StartTime":54573.0,"Objects":[{"StartTime":54573.0,"Position":176.0,"HyperDash":false}]},{"StartTime":54763.0,"Objects":[{"StartTime":54763.0,"Position":104.0,"HyperDash":false},{"StartTime":54829.0,"Position":110.83445,"HyperDash":false},{"StartTime":54896.0,"Position":93.97989,"HyperDash":false},{"StartTime":54962.0,"Position":92.00793,"HyperDash":false},{"StartTime":55029.0,"Position":91.28976,"HyperDash":false},{"StartTime":55096.0,"Position":107.652794,"HyperDash":false},{"StartTime":55162.0,"Position":137.152725,"HyperDash":false},{"StartTime":55229.0,"Position":132.523422,"HyperDash":false},{"StartTime":55332.0,"Position":193.99971,"HyperDash":false}]},{"StartTime":55523.0,"Objects":[{"StartTime":55523.0,"Position":216.0,"HyperDash":false}]},{"StartTime":55713.0,"Objects":[{"StartTime":55713.0,"Position":264.0,"HyperDash":false}]},{"StartTime":55902.0,"Objects":[{"StartTime":55902.0,"Position":352.0,"HyperDash":false}]},{"StartTime":56092.0,"Objects":[{"StartTime":56092.0,"Position":440.0,"HyperDash":false}]},{"StartTime":56282.0,"Objects":[{"StartTime":56282.0,"Position":352.0,"HyperDash":false}]},{"StartTime":56472.0,"Objects":[{"StartTime":56472.0,"Position":264.0,"HyperDash":false},{"StartTime":56538.0,"Position":236.1824,"HyperDash":false},{"StartTime":56605.0,"Position":201.573,"HyperDash":false},{"StartTime":56671.0,"Position":187.143539,"HyperDash":false},{"StartTime":56738.0,"Position":159.746231,"HyperDash":false},{"StartTime":56805.0,"Position":123.075737,"HyperDash":false},{"StartTime":56871.0,"Position":78.82073,"HyperDash":false},{"StartTime":56938.0,"Position":49.782032,"HyperDash":false},{"StartTime":57041.0,"Position":18.8103733,"HyperDash":false}]},{"StartTime":57421.0,"Objects":[{"StartTime":57421.0,"Position":160.0,"HyperDash":false}]},{"StartTime":57611.0,"Objects":[{"StartTime":57611.0,"Position":32.0,"HyperDash":false}]},{"StartTime":57801.0,"Objects":[{"StartTime":57801.0,"Position":160.0,"HyperDash":false}]},{"StartTime":57991.0,"Objects":[{"StartTime":57991.0,"Position":248.0,"HyperDash":false},{"StartTime":58057.0,"Position":279.8176,"HyperDash":false},{"StartTime":58124.0,"Position":314.427,"HyperDash":false},{"StartTime":58190.0,"Position":336.856445,"HyperDash":false},{"StartTime":58257.0,"Position":373.253754,"HyperDash":false},{"StartTime":58324.0,"Position":394.924255,"HyperDash":false},{"StartTime":58390.0,"Position":404.17926,"HyperDash":false},{"StartTime":58457.0,"Position":435.217957,"HyperDash":false},{"StartTime":58560.0,"Position":493.189636,"HyperDash":false}]},{"StartTime":58940.0,"Objects":[{"StartTime":58940.0,"Position":360.0,"HyperDash":false}]},{"StartTime":59130.0,"Objects":[{"StartTime":59130.0,"Position":256.0,"HyperDash":false}]},{"StartTime":59320.0,"Objects":[{"StartTime":59320.0,"Position":152.0,"HyperDash":false}]},{"StartTime":59510.0,"Objects":[{"StartTime":59510.0,"Position":168.0,"HyperDash":false},{"StartTime":59604.0,"Position":213.268082,"HyperDash":false},{"StartTime":59699.0,"Position":263.009766,"HyperDash":false},{"StartTime":59794.0,"Position":292.74585,"HyperDash":false},{"StartTime":59889.0,"Position":343.451233,"HyperDash":false},{"StartTime":59984.0,"Position":303.3675,"HyperDash":false},{"StartTime":60079.0,"Position":253.641876,"HyperDash":false},{"StartTime":60174.0,"Position":227.887085,"HyperDash":false},{"StartTime":60269.0,"Position":168.0,"HyperDash":false},{"StartTime":60355.0,"Position":189.407471,"HyperDash":false},{"StartTime":60441.0,"Position":257.7932,"HyperDash":false},{"StartTime":60527.0,"Position":268.439819,"HyperDash":false},{"StartTime":60649.0,"Position":343.451233,"HyperDash":false}]},{"StartTime":60839.0,"Objects":[{"StartTime":60839.0,"Position":408.0,"HyperDash":false}]},{"StartTime":60934.0,"Objects":[{"StartTime":60934.0,"Position":408.0,"HyperDash":false}]},{"StartTime":61029.0,"Objects":[{"StartTime":61029.0,"Position":408.0,"HyperDash":false},{"StartTime":61114.0,"Position":391.84967,"HyperDash":false},{"StartTime":61200.0,"Position":372.427,"HyperDash":false},{"StartTime":61286.0,"Position":329.0043,"HyperDash":false},{"StartTime":61408.0,"Position":304.776764,"HyperDash":false}]},{"StartTime":61599.0,"Objects":[{"StartTime":61599.0,"Position":304.0,"HyperDash":false}]},{"StartTime":61788.0,"Objects":[{"StartTime":61788.0,"Position":216.0,"HyperDash":false},{"StartTime":61873.0,"Position":231.980789,"HyperDash":false},{"StartTime":61959.0,"Position":262.282928,"HyperDash":false},{"StartTime":62045.0,"Position":291.630219,"HyperDash":false},{"StartTime":62167.0,"Position":318.820038,"HyperDash":false}]},{"StartTime":62358.0,"Objects":[{"StartTime":62358.0,"Position":319.0,"HyperDash":false}]},{"StartTime":62548.0,"Objects":[{"StartTime":62548.0,"Position":240.0,"HyperDash":false},{"StartTime":62642.0,"Position":251.786285,"HyperDash":false},{"StartTime":62737.0,"Position":294.0,"HyperDash":false},{"StartTime":62814.0,"Position":280.200531,"HyperDash":false},{"StartTime":62927.0,"Position":240.0,"HyperDash":false}]},{"StartTime":63118.0,"Objects":[{"StartTime":63118.0,"Position":192.0,"HyperDash":false},{"StartTime":63203.0,"Position":179.84967,"HyperDash":false},{"StartTime":63289.0,"Position":137.426987,"HyperDash":false},{"StartTime":63375.0,"Position":126.0043,"HyperDash":false},{"StartTime":63497.0,"Position":88.77678,"HyperDash":false}]},{"StartTime":63687.0,"Objects":[{"StartTime":63687.0,"Position":176.0,"HyperDash":false}]},{"StartTime":63877.0,"Objects":[{"StartTime":63877.0,"Position":264.0,"HyperDash":false}]},{"StartTime":64067.0,"Objects":[{"StartTime":64067.0,"Position":352.0,"HyperDash":false},{"StartTime":64152.0,"Position":394.99942,"HyperDash":false},{"StartTime":64238.0,"Position":401.898163,"HyperDash":false},{"StartTime":64324.0,"Position":421.559357,"HyperDash":false},{"StartTime":64446.0,"Position":422.749817,"HyperDash":false}]},{"StartTime":64637.0,"Objects":[{"StartTime":64637.0,"Position":352.0,"HyperDash":false}]},{"StartTime":64826.0,"Objects":[{"StartTime":64826.0,"Position":272.0,"HyperDash":false},{"StartTime":64902.0,"Position":280.7143,"HyperDash":false},{"StartTime":65015.0,"Position":326.0,"HyperDash":false}]},{"StartTime":65206.0,"Objects":[{"StartTime":65206.0,"Position":326.0,"HyperDash":false}]},{"StartTime":65396.0,"Objects":[{"StartTime":65396.0,"Position":272.0,"HyperDash":false},{"StartTime":65481.0,"Position":237.667328,"HyperDash":false},{"StartTime":65567.0,"Position":221.486267,"HyperDash":false},{"StartTime":65653.0,"Position":177.3163,"HyperDash":false},{"StartTime":65775.0,"Position":167.831314,"HyperDash":false}]},{"StartTime":65966.0,"Objects":[{"StartTime":65966.0,"Position":104.0,"HyperDash":false}]},{"StartTime":66156.0,"Objects":[{"StartTime":66156.0,"Position":48.0,"HyperDash":false}]},{"StartTime":66345.0,"Objects":[{"StartTime":66345.0,"Position":104.0,"HyperDash":false}]},{"StartTime":66535.0,"Objects":[{"StartTime":66535.0,"Position":56.0,"HyperDash":false}]},{"StartTime":66630.0,"Objects":[{"StartTime":66630.0,"Position":80.0,"HyperDash":false}]},{"StartTime":66725.0,"Objects":[{"StartTime":66725.0,"Position":104.0,"HyperDash":false}]},{"StartTime":66915.0,"Objects":[{"StartTime":66915.0,"Position":192.0,"HyperDash":false}]},{"StartTime":67105.0,"Objects":[{"StartTime":67105.0,"Position":280.0,"HyperDash":false},{"StartTime":67190.0,"Position":314.967377,"HyperDash":false},{"StartTime":67276.0,"Position":332.346161,"HyperDash":false},{"StartTime":67362.0,"Position":371.7249,"HyperDash":false},{"StartTime":67484.0,"Position":435.9134,"HyperDash":false}]},{"StartTime":67675.0,"Objects":[{"StartTime":67675.0,"Position":448.0,"HyperDash":false}]},{"StartTime":67864.0,"Objects":[{"StartTime":67864.0,"Position":456.0,"HyperDash":false},{"StartTime":67949.0,"Position":419.88092,"HyperDash":false},{"StartTime":68035.0,"Position":402.3487,"HyperDash":false},{"StartTime":68121.0,"Position":361.816467,"HyperDash":false},{"StartTime":68243.0,"Position":299.410278,"HyperDash":false}]},{"StartTime":68434.0,"Objects":[{"StartTime":68434.0,"Position":288.0,"HyperDash":false}]},{"StartTime":68624.0,"Objects":[{"StartTime":68624.0,"Position":208.0,"HyperDash":false}]},{"StartTime":68814.0,"Objects":[{"StartTime":68814.0,"Position":128.0,"HyperDash":false}]},{"StartTime":69004.0,"Objects":[{"StartTime":69004.0,"Position":48.0,"HyperDash":false}]},{"StartTime":69194.0,"Objects":[{"StartTime":69194.0,"Position":128.0,"HyperDash":false},{"StartTime":69279.0,"Position":167.7291,"HyperDash":false},{"StartTime":69365.0,"Position":176.461563,"HyperDash":false},{"StartTime":69451.0,"Position":208.0288,"HyperDash":false},{"StartTime":69573.0,"Position":193.863922,"HyperDash":false}]},{"StartTime":69763.0,"Objects":[{"StartTime":69763.0,"Position":256.0,"HyperDash":false}]},{"StartTime":69953.0,"Objects":[{"StartTime":69953.0,"Position":256.0,"HyperDash":false}]},{"StartTime":70143.0,"Objects":[{"StartTime":70143.0,"Position":318.0,"HyperDash":false},{"StartTime":70228.0,"Position":307.361053,"HyperDash":false},{"StartTime":70314.0,"Position":339.541077,"HyperDash":false},{"StartTime":70400.0,"Position":329.926361,"HyperDash":false},{"StartTime":70522.0,"Position":382.366,"HyperDash":false}]},{"StartTime":70902.0,"Objects":[{"StartTime":70902.0,"Position":256.0,"HyperDash":false},{"StartTime":70973.0,"Position":237.22084,"HyperDash":false},{"StartTime":71044.0,"Position":261.5726,"HyperDash":false},{"StartTime":71115.0,"Position":256.317383,"HyperDash":false},{"StartTime":71186.0,"Position":259.616821,"HyperDash":false},{"StartTime":71248.0,"Position":284.489624,"HyperDash":false},{"StartTime":71310.0,"Position":249.320618,"HyperDash":false},{"StartTime":71372.0,"Position":249.26532,"HyperDash":false},{"StartTime":71471.0,"Position":256.0,"HyperDash":false}]},{"StartTime":71662.0,"Objects":[{"StartTime":71662.0,"Position":256.0,"HyperDash":false}]},{"StartTime":72042.0,"Objects":[{"StartTime":72042.0,"Position":256.0,"HyperDash":false}]},{"StartTime":72421.0,"Objects":[{"StartTime":72421.0,"Position":160.0,"HyperDash":false}]},{"StartTime":72611.0,"Objects":[{"StartTime":72611.0,"Position":224.0,"HyperDash":false}]},{"StartTime":72801.0,"Objects":[{"StartTime":72801.0,"Position":288.0,"HyperDash":false}]},{"StartTime":72991.0,"Objects":[{"StartTime":72991.0,"Position":352.0,"HyperDash":false}]},{"StartTime":73181.0,"Objects":[{"StartTime":73181.0,"Position":408.0,"HyperDash":false}]},{"StartTime":73371.0,"Objects":[{"StartTime":73371.0,"Position":304.0,"HyperDash":false}]},{"StartTime":73561.0,"Objects":[{"StartTime":73561.0,"Position":208.0,"HyperDash":false}]},{"StartTime":73751.0,"Objects":[{"StartTime":73751.0,"Position":112.0,"HyperDash":false}]},{"StartTime":73940.0,"Objects":[{"StartTime":73940.0,"Position":160.0,"HyperDash":false}]},{"StartTime":74130.0,"Objects":[{"StartTime":74130.0,"Position":224.0,"HyperDash":false}]},{"StartTime":74225.0,"Objects":[{"StartTime":74225.0,"Position":248.0,"HyperDash":false}]},{"StartTime":74320.0,"Objects":[{"StartTime":74320.0,"Position":272.0,"HyperDash":false}]},{"StartTime":74415.0,"Objects":[{"StartTime":74415.0,"Position":296.0,"HyperDash":false}]},{"StartTime":74510.0,"Objects":[{"StartTime":74510.0,"Position":320.0,"HyperDash":false}]},{"StartTime":74605.0,"Objects":[{"StartTime":74605.0,"Position":344.0,"HyperDash":false}]},{"StartTime":74700.0,"Objects":[{"StartTime":74700.0,"Position":368.0,"HyperDash":false},{"StartTime":74785.0,"Position":391.4436,"HyperDash":false},{"StartTime":74871.0,"Position":429.4646,"HyperDash":false},{"StartTime":74957.0,"Position":450.2139,"HyperDash":false},{"StartTime":75079.0,"Position":476.639343,"HyperDash":false}]},{"StartTime":75270.0,"Objects":[{"StartTime":75270.0,"Position":368.0,"HyperDash":false}]},{"StartTime":75459.0,"Objects":[{"StartTime":75459.0,"Position":296.0,"HyperDash":false},{"StartTime":75535.0,"Position":252.914215,"HyperDash":false},{"StartTime":75648.0,"Position":210.849869,"HyperDash":false}]},{"StartTime":75839.0,"Objects":[{"StartTime":75839.0,"Position":144.0,"HyperDash":false}]},{"StartTime":76029.0,"Objects":[{"StartTime":76029.0,"Position":168.0,"HyperDash":false},{"StartTime":76114.0,"Position":202.25589,"HyperDash":false},{"StartTime":76200.0,"Position":242.877075,"HyperDash":false},{"StartTime":76286.0,"Position":302.62854,"HyperDash":false},{"StartTime":76408.0,"Position":345.765717,"HyperDash":false}]},{"StartTime":76599.0,"Objects":[{"StartTime":76599.0,"Position":344.0,"HyperDash":false},{"StartTime":76684.0,"Position":307.766968,"HyperDash":false},{"StartTime":76770.0,"Position":252.272888,"HyperDash":false},{"StartTime":76856.0,"Position":211.514786,"HyperDash":false},{"StartTime":76978.0,"Position":167.090546,"HyperDash":false}]},{"StartTime":77168.0,"Objects":[{"StartTime":77168.0,"Position":256.0,"HyperDash":false}]},{"StartTime":77358.0,"Objects":[{"StartTime":77358.0,"Position":256.0,"HyperDash":false}]},{"StartTime":77548.0,"Objects":[{"StartTime":77548.0,"Position":424.0,"HyperDash":false},{"StartTime":77633.0,"Position":417.615356,"HyperDash":false},{"StartTime":77719.0,"Position":433.1576,"HyperDash":false},{"StartTime":77805.0,"Position":439.338928,"HyperDash":false},{"StartTime":77927.0,"Position":425.7557,"HyperDash":false}]},{"StartTime":78118.0,"Objects":[{"StartTime":78118.0,"Position":296.0,"HyperDash":false},{"StartTime":78194.0,"Position":289.17218,"HyperDash":false},{"StartTime":78307.0,"Position":326.270264,"HyperDash":false}]},{"StartTime":78497.0,"Objects":[{"StartTime":78497.0,"Position":240.0,"HyperDash":false},{"StartTime":78573.0,"Position":252.172165,"HyperDash":false},{"StartTime":78686.0,"Position":270.270264,"HyperDash":false}]},{"StartTime":78877.0,"Objects":[{"StartTime":78877.0,"Position":168.0,"HyperDash":false},{"StartTime":78953.0,"Position":193.367844,"HyperDash":false},{"StartTime":79066.0,"Position":198.756882,"HyperDash":false}]},{"StartTime":79257.0,"Objects":[{"StartTime":79257.0,"Position":104.0,"HyperDash":false},{"StartTime":79333.0,"Position":113.367844,"HyperDash":false},{"StartTime":79446.0,"Position":134.756882,"HyperDash":false}]},{"StartTime":79637.0,"Objects":[{"StartTime":79637.0,"Position":48.0,"HyperDash":false},{"StartTime":79731.0,"Position":47.97381,"HyperDash":false},{"StartTime":79826.0,"Position":15.6918831,"HyperDash":false},{"StartTime":79903.0,"Position":19.7344723,"HyperDash":false},{"StartTime":80016.0,"Position":48.0,"HyperDash":false}]},{"StartTime":80206.0,"Objects":[{"StartTime":80206.0,"Position":48.0,"HyperDash":false}]},{"StartTime":80396.0,"Objects":[{"StartTime":80396.0,"Position":48.0,"HyperDash":false}]},{"StartTime":80586.0,"Objects":[{"StartTime":80586.0,"Position":48.0,"HyperDash":false},{"StartTime":80671.0,"Position":50.6653442,"HyperDash":false},{"StartTime":80757.0,"Position":72.3398361,"HyperDash":false},{"StartTime":80843.0,"Position":138.798065,"HyperDash":false},{"StartTime":80965.0,"Position":177.234756,"HyperDash":false}]},{"StartTime":81156.0,"Objects":[{"StartTime":81156.0,"Position":334.0,"HyperDash":false},{"StartTime":81241.0,"Position":384.0748,"HyperDash":false},{"StartTime":81327.0,"Position":430.608582,"HyperDash":false},{"StartTime":81413.0,"Position":423.721344,"HyperDash":false},{"StartTime":81535.0,"Position":463.472778,"HyperDash":false}]},{"StartTime":81725.0,"Objects":[{"StartTime":81725.0,"Position":256.0,"HyperDash":false}]},{"StartTime":81915.0,"Objects":[{"StartTime":81915.0,"Position":256.0,"HyperDash":false}]},{"StartTime":82105.0,"Objects":[{"StartTime":82105.0,"Position":48.0,"HyperDash":false},{"StartTime":82190.0,"Position":55.6653442,"HyperDash":false},{"StartTime":82276.0,"Position":92.3398361,"HyperDash":false},{"StartTime":82362.0,"Position":132.798065,"HyperDash":false},{"StartTime":82484.0,"Position":177.234756,"HyperDash":false}]},{"StartTime":82675.0,"Objects":[{"StartTime":82675.0,"Position":334.0,"HyperDash":false},{"StartTime":82760.0,"Position":361.0748,"HyperDash":false},{"StartTime":82846.0,"Position":397.608582,"HyperDash":false},{"StartTime":82932.0,"Position":421.721344,"HyperDash":false},{"StartTime":83054.0,"Position":463.472778,"HyperDash":false}]},{"StartTime":83244.0,"Objects":[{"StartTime":83244.0,"Position":256.0,"HyperDash":false}]},{"StartTime":83434.0,"Objects":[{"StartTime":83434.0,"Position":256.0,"HyperDash":false}]},{"StartTime":83624.0,"Objects":[{"StartTime":83624.0,"Position":177.0,"HyperDash":false},{"StartTime":83709.0,"Position":139.9757,"HyperDash":false},{"StartTime":83795.0,"Position":85.66393,"HyperDash":false},{"StartTime":83881.0,"Position":76.88606,"HyperDash":false},{"StartTime":84003.0,"Position":48.41881,"HyperDash":false}]},{"StartTime":84194.0,"Objects":[{"StartTime":84194.0,"Position":240.0,"HyperDash":false},{"StartTime":84270.0,"Position":217.612869,"HyperDash":false},{"StartTime":84383.0,"Position":151.997787,"HyperDash":false}]},{"StartTime":84573.0,"Objects":[{"StartTime":84573.0,"Position":40.0,"HyperDash":false},{"StartTime":84649.0,"Position":65.48768,"HyperDash":false},{"StartTime":84762.0,"Position":128.252258,"HyperDash":false}]},{"StartTime":84953.0,"Objects":[{"StartTime":84953.0,"Position":280.0,"HyperDash":false},{"StartTime":85029.0,"Position":237.890076,"HyperDash":false},{"StartTime":85142.0,"Position":192.68718,"HyperDash":false}]},{"StartTime":85333.0,"Objects":[{"StartTime":85333.0,"Position":392.0,"HyperDash":false},{"StartTime":85392.0,"Position":335.0,"HyperDash":false},{"StartTime":85451.0,"Position":193.0,"HyperDash":false},{"StartTime":85510.0,"Position":478.0,"HyperDash":false},{"StartTime":85570.0,"Position":255.0,"HyperDash":false},{"StartTime":85629.0,"Position":175.0,"HyperDash":false},{"StartTime":85688.0,"Position":274.0,"HyperDash":false},{"StartTime":85748.0,"Position":442.0,"HyperDash":false},{"StartTime":85807.0,"Position":295.0,"HyperDash":false},{"StartTime":85866.0,"Position":311.0,"HyperDash":false},{"StartTime":85926.0,"Position":17.0,"HyperDash":false},{"StartTime":85985.0,"Position":467.0,"HyperDash":false},{"StartTime":86044.0,"Position":30.0,"HyperDash":false},{"StartTime":86104.0,"Position":218.0,"HyperDash":false},{"StartTime":86163.0,"Position":26.0,"HyperDash":false},{"StartTime":86222.0,"Position":16.0,"HyperDash":false},{"StartTime":86282.0,"Position":248.0,"HyperDash":false}]},{"StartTime":86472.0,"Objects":[{"StartTime":86472.0,"Position":256.0,"HyperDash":false}]},{"StartTime":86662.0,"Objects":[{"StartTime":86662.0,"Position":128.0,"HyperDash":false}]},{"StartTime":86757.0,"Objects":[{"StartTime":86757.0,"Position":152.0,"HyperDash":false}]},{"StartTime":86852.0,"Objects":[{"StartTime":86852.0,"Position":176.0,"HyperDash":false},{"StartTime":86928.0,"Position":199.190475,"HyperDash":false},{"StartTime":87041.0,"Position":266.0,"HyperDash":false}]},{"StartTime":87232.0,"Objects":[{"StartTime":87232.0,"Position":360.0,"HyperDash":false},{"StartTime":87317.0,"Position":331.134338,"HyperDash":false},{"StartTime":87403.0,"Position":283.893768,"HyperDash":false},{"StartTime":87489.0,"Position":250.155975,"HyperDash":false},{"StartTime":87611.0,"Position":199.214035,"HyperDash":false}]},{"StartTime":87801.0,"Objects":[{"StartTime":87801.0,"Position":136.0,"HyperDash":false},{"StartTime":87877.0,"Position":153.190475,"HyperDash":false},{"StartTime":87990.0,"Position":226.0,"HyperDash":false}]},{"StartTime":88181.0,"Objects":[{"StartTime":88181.0,"Position":440.0,"HyperDash":false},{"StartTime":88266.0,"Position":401.6306,"HyperDash":false},{"StartTime":88352.0,"Position":361.417969,"HyperDash":false},{"StartTime":88438.0,"Position":315.9722,"HyperDash":false},{"StartTime":88560.0,"Position":286.761566,"HyperDash":false}]},{"StartTime":88751.0,"Objects":[{"StartTime":88751.0,"Position":72.0,"HyperDash":false},{"StartTime":88836.0,"Position":97.36939,"HyperDash":false},{"StartTime":88922.0,"Position":151.554047,"HyperDash":false},{"StartTime":89008.0,"Position":175.23497,"HyperDash":false},{"StartTime":89130.0,"Position":225.445587,"HyperDash":false}]},{"StartTime":89320.0,"Objects":[{"StartTime":89320.0,"Position":256.0,"HyperDash":false},{"StartTime":89414.0,"Position":256.0,"HyperDash":false},{"StartTime":89509.0,"Position":256.0,"HyperDash":false}]},{"StartTime":89700.0,"Objects":[{"StartTime":89700.0,"Position":488.0,"HyperDash":false},{"StartTime":89785.0,"Position":441.7927,"HyperDash":false},{"StartTime":89871.0,"Position":394.103729,"HyperDash":false},{"StartTime":89957.0,"Position":358.440735,"HyperDash":false},{"StartTime":90079.0,"Position":314.813538,"HyperDash":false}]},{"StartTime":90270.0,"Objects":[{"StartTime":90270.0,"Position":256.0,"HyperDash":false}]},{"StartTime":90459.0,"Objects":[{"StartTime":90459.0,"Position":160.0,"HyperDash":false}]},{"StartTime":90649.0,"Objects":[{"StartTime":90649.0,"Position":64.0,"HyperDash":false}]},{"StartTime":90839.0,"Objects":[{"StartTime":90839.0,"Position":160.0,"HyperDash":false}]},{"StartTime":91029.0,"Objects":[{"StartTime":91029.0,"Position":256.0,"HyperDash":false}]},{"StartTime":91219.0,"Objects":[{"StartTime":91219.0,"Position":352.0,"HyperDash":false}]},{"StartTime":91409.0,"Objects":[{"StartTime":91409.0,"Position":448.0,"HyperDash":false}]},{"StartTime":91599.0,"Objects":[{"StartTime":91599.0,"Position":352.0,"HyperDash":false}]},{"StartTime":91788.0,"Objects":[{"StartTime":91788.0,"Position":256.0,"HyperDash":false}]},{"StartTime":91978.0,"Objects":[{"StartTime":91978.0,"Position":256.0,"HyperDash":false}]},{"StartTime":92168.0,"Objects":[{"StartTime":92168.0,"Position":256.0,"HyperDash":false}]},{"StartTime":92358.0,"Objects":[{"StartTime":92358.0,"Position":256.0,"HyperDash":false},{"StartTime":92434.0,"Position":250.0,"HyperDash":false},{"StartTime":92547.0,"Position":256.0,"HyperDash":false}]},{"StartTime":92738.0,"Objects":[{"StartTime":92738.0,"Position":32.0,"HyperDash":false},{"StartTime":92823.0,"Position":61.3693924,"HyperDash":false},{"StartTime":92909.0,"Position":113.213722,"HyperDash":false},{"StartTime":92995.0,"Position":137.112122,"HyperDash":false},{"StartTime":93117.0,"Position":192.083252,"HyperDash":false}]},{"StartTime":93307.0,"Objects":[{"StartTime":93307.0,"Position":64.0,"HyperDash":false},{"StartTime":93383.0,"Position":90.59053,"HyperDash":false},{"StartTime":93496.0,"Position":127.639618,"HyperDash":false}]},{"StartTime":93687.0,"Objects":[{"StartTime":93687.0,"Position":256.0,"HyperDash":false},{"StartTime":93763.0,"Position":296.590546,"HyperDash":false},{"StartTime":93876.0,"Position":319.639618,"HyperDash":false}]},{"StartTime":94067.0,"Objects":[{"StartTime":94067.0,"Position":424.0,"HyperDash":false}]},{"StartTime":94257.0,"Objects":[{"StartTime":94257.0,"Position":256.0,"HyperDash":false},{"StartTime":94342.0,"Position":210.766815,"HyperDash":false},{"StartTime":94428.0,"Position":186.0,"HyperDash":false},{"StartTime":94514.0,"Position":209.0,"HyperDash":false},{"StartTime":94636.0,"Position":192.0,"HyperDash":false}]},{"StartTime":94826.0,"Objects":[{"StartTime":94826.0,"Position":328.0,"HyperDash":false},{"StartTime":94920.0,"Position":353.6438,"HyperDash":false},{"StartTime":95015.0,"Position":418.0,"HyperDash":false},{"StartTime":95092.0,"Position":396.667542,"HyperDash":false},{"StartTime":95205.0,"Position":328.0,"HyperDash":false}]},{"StartTime":95396.0,"Objects":[{"StartTime":95396.0,"Position":328.0,"HyperDash":false}]},{"StartTime":95586.0,"Objects":[{"StartTime":95586.0,"Position":328.0,"HyperDash":false}]},{"StartTime":95776.0,"Objects":[{"StartTime":95776.0,"Position":192.0,"HyperDash":false},{"StartTime":95861.0,"Position":170.3153,"HyperDash":false},{"StartTime":95947.0,"Position":94.6082153,"HyperDash":false},{"StartTime":96033.0,"Position":54.801857,"HyperDash":false},{"StartTime":96155.0,"Position":13.5809069,"HyperDash":false}]},{"StartTime":96345.0,"Objects":[{"StartTime":96345.0,"Position":56.0,"HyperDash":false},{"StartTime":96421.0,"Position":104.190475,"HyperDash":false},{"StartTime":96534.0,"Position":146.0,"HyperDash":false}]},{"StartTime":96725.0,"Objects":[{"StartTime":96725.0,"Position":232.0,"HyperDash":false}]},{"StartTime":96915.0,"Objects":[{"StartTime":96915.0,"Position":280.0,"HyperDash":false}]},{"StartTime":97105.0,"Objects":[{"StartTime":97105.0,"Position":360.0,"HyperDash":false},{"StartTime":97181.0,"Position":408.1905,"HyperDash":false},{"StartTime":97294.0,"Position":450.0,"HyperDash":false}]},{"StartTime":97485.0,"Objects":[{"StartTime":97485.0,"Position":458.0,"HyperDash":false},{"StartTime":97579.0,"Position":425.0,"HyperDash":false},{"StartTime":97674.0,"Position":466.0,"HyperDash":false},{"StartTime":97769.0,"Position":56.0,"HyperDash":false},{"StartTime":97864.0,"Position":109.0,"HyperDash":false},{"StartTime":97959.0,"Position":482.0,"HyperDash":false},{"StartTime":98054.0,"Position":147.0,"HyperDash":false},{"StartTime":98149.0,"Position":285.0,"HyperDash":false},{"StartTime":98244.0,"Position":452.0,"HyperDash":false},{"StartTime":98339.0,"Position":419.0,"HyperDash":false},{"StartTime":98434.0,"Position":269.0,"HyperDash":false},{"StartTime":98529.0,"Position":249.0,"HyperDash":false},{"StartTime":98624.0,"Position":233.0,"HyperDash":false},{"StartTime":98719.0,"Position":449.0,"HyperDash":false},{"StartTime":98814.0,"Position":411.0,"HyperDash":false},{"StartTime":98909.0,"Position":75.0,"HyperDash":false},{"StartTime":99004.0,"Position":474.0,"HyperDash":false}]},{"StartTime":111156.0,"Objects":[{"StartTime":111156.0,"Position":256.0,"HyperDash":false}]},{"StartTime":111915.0,"Objects":[{"StartTime":111915.0,"Position":256.0,"HyperDash":false}]},{"StartTime":112105.0,"Objects":[{"StartTime":112105.0,"Position":256.0,"HyperDash":false}]},{"StartTime":112295.0,"Objects":[{"StartTime":112295.0,"Position":256.0,"HyperDash":false}]},{"StartTime":112485.0,"Objects":[{"StartTime":112485.0,"Position":256.0,"HyperDash":false}]},{"StartTime":112675.0,"Objects":[{"StartTime":112675.0,"Position":328.0,"HyperDash":false},{"StartTime":112760.0,"Position":361.17868,"HyperDash":false},{"StartTime":112846.0,"Position":407.7525,"HyperDash":false},{"StartTime":112932.0,"Position":421.257233,"HyperDash":false},{"StartTime":113054.0,"Position":455.379,"HyperDash":false}]},{"StartTime":113244.0,"Objects":[{"StartTime":113244.0,"Position":456.0,"HyperDash":false}]},{"StartTime":113434.0,"Objects":[{"StartTime":113434.0,"Position":456.0,"HyperDash":false}]},{"StartTime":113624.0,"Objects":[{"StartTime":113624.0,"Position":368.0,"HyperDash":false},{"StartTime":113718.0,"Position":305.349274,"HyperDash":false},{"StartTime":113813.0,"Position":287.706543,"HyperDash":false},{"StartTime":113890.0,"Position":296.162018,"HyperDash":false},{"StartTime":114003.0,"Position":368.0,"HyperDash":false}]},{"StartTime":114194.0,"Objects":[{"StartTime":114194.0,"Position":456.0,"HyperDash":false},{"StartTime":114279.0,"Position":435.479034,"HyperDash":false},{"StartTime":114365.0,"Position":402.81424,"HyperDash":false},{"StartTime":114451.0,"Position":376.903717,"HyperDash":false},{"StartTime":114573.0,"Position":310.688843,"HyperDash":false}]},{"StartTime":114763.0,"Objects":[{"StartTime":114763.0,"Position":256.0,"HyperDash":false},{"StartTime":114839.0,"Position":203.173843,"HyperDash":false},{"StartTime":114952.0,"Position":176.330536,"HyperDash":false}]},{"StartTime":115143.0,"Objects":[{"StartTime":115143.0,"Position":112.0,"HyperDash":false}]},{"StartTime":115333.0,"Objects":[{"StartTime":115333.0,"Position":176.0,"HyperDash":false}]},{"StartTime":115523.0,"Objects":[{"StartTime":115523.0,"Position":240.0,"HyperDash":false}]},{"StartTime":115713.0,"Objects":[{"StartTime":115713.0,"Position":176.0,"HyperDash":false},{"StartTime":115798.0,"Position":197.9177,"HyperDash":false},{"StartTime":115884.0,"Position":227.720581,"HyperDash":false},{"StartTime":115970.0,"Position":273.524536,"HyperDash":false},{"StartTime":116092.0,"Position":328.682556,"HyperDash":false}]},{"StartTime":116282.0,"Objects":[{"StartTime":116282.0,"Position":296.0,"HyperDash":false}]},{"StartTime":116472.0,"Objects":[{"StartTime":116472.0,"Position":360.0,"HyperDash":false}]},{"StartTime":116662.0,"Objects":[{"StartTime":116662.0,"Position":448.0,"HyperDash":false},{"StartTime":116738.0,"Position":439.409454,"HyperDash":false},{"StartTime":116851.0,"Position":384.360382,"HyperDash":false}]},{"StartTime":117042.0,"Objects":[{"StartTime":117042.0,"Position":384.0,"HyperDash":false},{"StartTime":117127.0,"Position":354.734955,"HyperDash":false},{"StartTime":117213.0,"Position":299.665924,"HyperDash":false},{"StartTime":117299.0,"Position":257.5697,"HyperDash":false},{"StartTime":117421.0,"Position":234.549561,"HyperDash":false}]},{"StartTime":117611.0,"Objects":[{"StartTime":117611.0,"Position":280.0,"HyperDash":false},{"StartTime":117687.0,"Position":309.4148,"HyperDash":false},{"StartTime":117800.0,"Position":286.3127,"HyperDash":false}]},{"StartTime":117991.0,"Objects":[{"StartTime":117991.0,"Position":192.0,"HyperDash":false},{"StartTime":118067.0,"Position":177.6565,"HyperDash":false},{"StartTime":118180.0,"Position":196.931625,"HyperDash":false}]},{"StartTime":118561.0,"Objects":[{"StartTime":118561.0,"Position":248.0,"HyperDash":false}]},{"StartTime":118940.0,"Objects":[{"StartTime":118940.0,"Position":248.0,"HyperDash":false}]},{"StartTime":119320.0,"Objects":[{"StartTime":119320.0,"Position":248.0,"HyperDash":false}]},{"StartTime":119700.0,"Objects":[{"StartTime":119700.0,"Position":448.0,"HyperDash":false}]},{"StartTime":119890.0,"Objects":[{"StartTime":119890.0,"Position":384.0,"HyperDash":false}]},{"StartTime":120080.0,"Objects":[{"StartTime":120080.0,"Position":320.0,"HyperDash":false}]},{"StartTime":120270.0,"Objects":[{"StartTime":120270.0,"Position":256.0,"HyperDash":false},{"StartTime":120355.0,"Position":213.1814,"HyperDash":false},{"StartTime":120441.0,"Position":169.527481,"HyperDash":false},{"StartTime":120527.0,"Position":146.788452,"HyperDash":false},{"StartTime":120649.0,"Position":78.92116,"HyperDash":false}]},{"StartTime":120839.0,"Objects":[{"StartTime":120839.0,"Position":80.0,"HyperDash":false}]},{"StartTime":121219.0,"Objects":[{"StartTime":121219.0,"Position":32.0,"HyperDash":false}]},{"StartTime":121409.0,"Objects":[{"StartTime":121409.0,"Position":120.0,"HyperDash":false}]},{"StartTime":121599.0,"Objects":[{"StartTime":121599.0,"Position":208.0,"HyperDash":false}]},{"StartTime":121788.0,"Objects":[{"StartTime":121788.0,"Position":296.0,"HyperDash":false},{"StartTime":121873.0,"Position":324.8186,"HyperDash":false},{"StartTime":121959.0,"Position":394.4725,"HyperDash":false},{"StartTime":122045.0,"Position":403.211548,"HyperDash":false},{"StartTime":122167.0,"Position":473.078827,"HyperDash":false}]},{"StartTime":122358.0,"Objects":[{"StartTime":122358.0,"Position":472.0,"HyperDash":false}]},{"StartTime":122738.0,"Objects":[{"StartTime":122738.0,"Position":208.0,"HyperDash":false}]},{"StartTime":122928.0,"Objects":[{"StartTime":122928.0,"Position":256.0,"HyperDash":false}]},{"StartTime":123117.0,"Objects":[{"StartTime":123117.0,"Position":304.0,"HyperDash":false}]},{"StartTime":123307.0,"Objects":[{"StartTime":123307.0,"Position":256.0,"HyperDash":false}]},{"StartTime":123687.0,"Objects":[{"StartTime":123687.0,"Position":256.0,"HyperDash":false}]},{"StartTime":124067.0,"Objects":[{"StartTime":124067.0,"Position":256.0,"HyperDash":false}]},{"StartTime":124257.0,"Objects":[{"StartTime":124257.0,"Position":256.0,"HyperDash":false}]},{"StartTime":124447.0,"Objects":[{"StartTime":124447.0,"Position":256.0,"HyperDash":false}]},{"StartTime":124637.0,"Objects":[{"StartTime":124637.0,"Position":256.0,"HyperDash":false}]},{"StartTime":124732.0,"Objects":[{"StartTime":124732.0,"Position":256.0,"HyperDash":false}]},{"StartTime":124826.0,"Objects":[{"StartTime":124826.0,"Position":256.0,"HyperDash":false},{"StartTime":124911.0,"Position":294.15155,"HyperDash":false},{"StartTime":124997.0,"Position":348.2999,"HyperDash":false},{"StartTime":125083.0,"Position":367.688416,"HyperDash":false},{"StartTime":125205.0,"Position":375.982025,"HyperDash":false}]},{"StartTime":125396.0,"Objects":[{"StartTime":125396.0,"Position":456.0,"HyperDash":false}]},{"StartTime":125586.0,"Objects":[{"StartTime":125586.0,"Position":392.0,"HyperDash":false}]},{"StartTime":125776.0,"Objects":[{"StartTime":125776.0,"Position":304.0,"HyperDash":false},{"StartTime":125852.0,"Position":263.17807,"HyperDash":false},{"StartTime":125965.0,"Position":227.350739,"HyperDash":false}]},{"StartTime":126156.0,"Objects":[{"StartTime":126156.0,"Position":192.0,"HyperDash":false}]},{"StartTime":126345.0,"Objects":[{"StartTime":126345.0,"Position":160.0,"HyperDash":false},{"StartTime":126430.0,"Position":126.124176,"HyperDash":false},{"StartTime":126516.0,"Position":85.81763,"HyperDash":false},{"StartTime":126602.0,"Position":37.2244949,"HyperDash":false},{"StartTime":126724.0,"Position":32.57615,"HyperDash":false}]},{"StartTime":126915.0,"Objects":[{"StartTime":126915.0,"Position":120.0,"HyperDash":false},{"StartTime":126991.0,"Position":102.400024,"HyperDash":false},{"StartTime":127104.0,"Position":68.7711,"HyperDash":false}]},{"StartTime":127295.0,"Objects":[{"StartTime":127295.0,"Position":136.0,"HyperDash":false},{"StartTime":127389.0,"Position":114.741783,"HyperDash":false},{"StartTime":127484.0,"Position":83.06455,"HyperDash":false},{"StartTime":127561.0,"Position":120.434265,"HyperDash":false},{"StartTime":127674.0,"Position":136.0,"HyperDash":false}]},{"StartTime":127864.0,"Objects":[{"StartTime":127864.0,"Position":184.0,"HyperDash":false},{"StartTime":127949.0,"Position":200.744141,"HyperDash":false},{"StartTime":128035.0,"Position":221.767609,"HyperDash":false},{"StartTime":128121.0,"Position":262.7911,"HyperDash":false},{"StartTime":128243.0,"Position":289.8709,"HyperDash":false}]},{"StartTime":128434.0,"Objects":[{"StartTime":128434.0,"Position":384.0,"HyperDash":false}]},{"StartTime":128624.0,"Objects":[{"StartTime":128624.0,"Position":448.0,"HyperDash":false}]},{"StartTime":128814.0,"Objects":[{"StartTime":128814.0,"Position":448.0,"HyperDash":false},{"StartTime":128890.0,"Position":398.135345,"HyperDash":false},{"StartTime":129003.0,"Position":368.7576,"HyperDash":false}]},{"StartTime":129194.0,"Objects":[{"StartTime":129194.0,"Position":440.0,"HyperDash":false},{"StartTime":129279.0,"Position":389.377838,"HyperDash":false},{"StartTime":129365.0,"Position":370.438324,"HyperDash":false},{"StartTime":129451.0,"Position":329.820526,"HyperDash":false},{"StartTime":129573.0,"Position":267.138855,"HyperDash":false}]},{"StartTime":129763.0,"Objects":[{"StartTime":129763.0,"Position":208.0,"HyperDash":false}]},{"StartTime":129953.0,"Objects":[{"StartTime":129953.0,"Position":128.0,"HyperDash":false}]},{"StartTime":130143.0,"Objects":[{"StartTime":130143.0,"Position":208.0,"HyperDash":false}]},{"StartTime":130333.0,"Objects":[{"StartTime":130333.0,"Position":288.0,"HyperDash":false},{"StartTime":130409.0,"Position":333.1905,"HyperDash":false},{"StartTime":130522.0,"Position":378.0,"HyperDash":false}]},{"StartTime":130713.0,"Objects":[{"StartTime":130713.0,"Position":448.0,"HyperDash":false},{"StartTime":130789.0,"Position":411.8095,"HyperDash":false},{"StartTime":130902.0,"Position":358.0,"HyperDash":false}]},{"StartTime":131282.0,"Objects":[{"StartTime":131282.0,"Position":176.0,"HyperDash":false}]},{"StartTime":131662.0,"Objects":[{"StartTime":131662.0,"Position":360.0,"HyperDash":false}]},{"StartTime":131852.0,"Objects":[{"StartTime":131852.0,"Position":288.0,"HyperDash":false}]},{"StartTime":132042.0,"Objects":[{"StartTime":132042.0,"Position":200.0,"HyperDash":false}]},{"StartTime":132232.0,"Objects":[{"StartTime":132232.0,"Position":112.0,"HyperDash":false}]},{"StartTime":132421.0,"Objects":[{"StartTime":132421.0,"Position":96.0,"HyperDash":false},{"StartTime":132487.0,"Position":49.5101624,"HyperDash":false},{"StartTime":132554.0,"Position":57.3071823,"HyperDash":false},{"StartTime":132620.0,"Position":26.5927753,"HyperDash":false},{"StartTime":132687.0,"Position":15.30433,"HyperDash":false},{"StartTime":132754.0,"Position":15.7045517,"HyperDash":false},{"StartTime":132820.0,"Position":49.43814,"HyperDash":false},{"StartTime":132887.0,"Position":66.86148,"HyperDash":false},{"StartTime":132990.0,"Position":96.71054,"HyperDash":false}]},{"StartTime":133371.0,"Objects":[{"StartTime":133371.0,"Position":224.0,"HyperDash":false}]},{"StartTime":133561.0,"Objects":[{"StartTime":133561.0,"Position":312.0,"HyperDash":false}]},{"StartTime":133751.0,"Objects":[{"StartTime":133751.0,"Position":400.0,"HyperDash":false}]},{"StartTime":133940.0,"Objects":[{"StartTime":133940.0,"Position":416.0,"HyperDash":false},{"StartTime":134006.0,"Position":452.489838,"HyperDash":false},{"StartTime":134073.0,"Position":482.6928,"HyperDash":false},{"StartTime":134139.0,"Position":473.407227,"HyperDash":false},{"StartTime":134206.0,"Position":502.695679,"HyperDash":false},{"StartTime":134273.0,"Position":505.295471,"HyperDash":false},{"StartTime":134339.0,"Position":462.561859,"HyperDash":false},{"StartTime":134406.0,"Position":475.138519,"HyperDash":false},{"StartTime":134509.0,"Position":415.289459,"HyperDash":false}]},{"StartTime":134890.0,"Objects":[{"StartTime":134890.0,"Position":80.0,"HyperDash":false}]},{"StartTime":135080.0,"Objects":[{"StartTime":135080.0,"Position":160.0,"HyperDash":false}]},{"StartTime":135270.0,"Objects":[{"StartTime":135270.0,"Position":200.0,"HyperDash":false}]},{"StartTime":135459.0,"Objects":[{"StartTime":135459.0,"Position":280.0,"HyperDash":false}]},{"StartTime":135839.0,"Objects":[{"StartTime":135839.0,"Position":464.0,"HyperDash":false}]},{"StartTime":136029.0,"Objects":[{"StartTime":136029.0,"Position":376.0,"HyperDash":false}]},{"StartTime":136219.0,"Objects":[{"StartTime":136219.0,"Position":376.0,"HyperDash":false}]},{"StartTime":136409.0,"Objects":[{"StartTime":136409.0,"Position":280.0,"HyperDash":false}]},{"StartTime":136599.0,"Objects":[{"StartTime":136599.0,"Position":280.0,"HyperDash":false}]},{"StartTime":136978.0,"Objects":[{"StartTime":136978.0,"Position":56.0,"HyperDash":false},{"StartTime":137063.0,"Position":98.33999,"HyperDash":false},{"StartTime":137149.0,"Position":121.429718,"HyperDash":false},{"StartTime":137235.0,"Position":184.982086,"HyperDash":false},{"StartTime":137357.0,"Position":227.214722,"HyperDash":false}]},{"StartTime":137738.0,"Objects":[{"StartTime":137738.0,"Position":456.0,"HyperDash":false},{"StartTime":137823.0,"Position":411.66,"HyperDash":false},{"StartTime":137909.0,"Position":389.570282,"HyperDash":false},{"StartTime":137995.0,"Position":336.0179,"HyperDash":false},{"StartTime":138117.0,"Position":284.785278,"HyperDash":false}]},{"StartTime":138497.0,"Objects":[{"StartTime":138497.0,"Position":256.0,"HyperDash":false}]},{"StartTime":138687.0,"Objects":[{"StartTime":138687.0,"Position":200.0,"HyperDash":false}]},{"StartTime":138877.0,"Objects":[{"StartTime":138877.0,"Position":256.0,"HyperDash":false}]},{"StartTime":139067.0,"Objects":[{"StartTime":139067.0,"Position":312.0,"HyperDash":false},{"StartTime":139143.0,"Position":331.1905,"HyperDash":false},{"StartTime":139256.0,"Position":402.0,"HyperDash":false}]},{"StartTime":139447.0,"Objects":[{"StartTime":139447.0,"Position":400.0,"HyperDash":false},{"StartTime":139541.0,"Position":424.6438,"HyperDash":false},{"StartTime":139636.0,"Position":490.0,"HyperDash":false},{"StartTime":139713.0,"Position":436.667542,"HyperDash":false},{"StartTime":139826.0,"Position":400.0,"HyperDash":false}]},{"StartTime":140016.0,"Objects":[{"StartTime":140016.0,"Position":400.0,"HyperDash":false},{"StartTime":140101.0,"Position":405.018951,"HyperDash":false},{"StartTime":140187.0,"Position":337.8755,"HyperDash":false},{"StartTime":140273.0,"Position":336.351257,"HyperDash":false},{"StartTime":140395.0,"Position":259.5054,"HyperDash":false}]},{"StartTime":140586.0,"Objects":[{"StartTime":140586.0,"Position":224.0,"HyperDash":false}]},{"StartTime":140776.0,"Objects":[{"StartTime":140776.0,"Position":296.0,"HyperDash":false}]},{"StartTime":140966.0,"Objects":[{"StartTime":140966.0,"Position":224.0,"HyperDash":false}]},{"StartTime":141156.0,"Objects":[{"StartTime":141156.0,"Position":296.0,"HyperDash":false}]},{"StartTime":141345.0,"Objects":[{"StartTime":141345.0,"Position":256.0,"HyperDash":false},{"StartTime":141430.0,"Position":196.648087,"HyperDash":false},{"StartTime":141516.0,"Position":175.249878,"HyperDash":false},{"StartTime":141602.0,"Position":133.184525,"HyperDash":false},{"StartTime":141724.0,"Position":114.597687,"HyperDash":false}]},{"StartTime":141915.0,"Objects":[{"StartTime":141915.0,"Position":112.0,"HyperDash":false},{"StartTime":142009.0,"Position":98.0,"HyperDash":false},{"StartTime":142104.0,"Position":112.0,"HyperDash":false},{"StartTime":142181.0,"Position":98.0,"HyperDash":false},{"StartTime":142294.0,"Position":112.0,"HyperDash":false}]},{"StartTime":142485.0,"Objects":[{"StartTime":142485.0,"Position":112.0,"HyperDash":false}]},{"StartTime":142580.0,"Objects":[{"StartTime":142580.0,"Position":112.0,"HyperDash":false}]},{"StartTime":142675.0,"Objects":[{"StartTime":142675.0,"Position":112.0,"HyperDash":false}]},{"StartTime":142864.0,"Objects":[{"StartTime":142864.0,"Position":112.0,"HyperDash":false}]},{"StartTime":143054.0,"Objects":[{"StartTime":143054.0,"Position":232.0,"HyperDash":false},{"StartTime":143139.0,"Position":225.714432,"HyperDash":false},{"StartTime":143225.0,"Position":180.464615,"HyperDash":false},{"StartTime":143311.0,"Position":216.858948,"HyperDash":false},{"StartTime":143433.0,"Position":221.927963,"HyperDash":false}]},{"StartTime":143814.0,"Objects":[{"StartTime":143814.0,"Position":280.0,"HyperDash":false},{"StartTime":143899.0,"Position":293.285583,"HyperDash":false},{"StartTime":143985.0,"Position":317.53537,"HyperDash":false},{"StartTime":144071.0,"Position":329.141052,"HyperDash":false},{"StartTime":144193.0,"Position":290.072052,"HyperDash":false}]},{"StartTime":144573.0,"Objects":[{"StartTime":144573.0,"Position":256.0,"HyperDash":false}]},{"StartTime":144763.0,"Objects":[{"StartTime":144763.0,"Position":344.0,"HyperDash":false}]},{"StartTime":144953.0,"Objects":[{"StartTime":144953.0,"Position":416.0,"HyperDash":false}]},{"StartTime":145143.0,"Objects":[{"StartTime":145143.0,"Position":416.0,"HyperDash":false},{"StartTime":145228.0,"Position":392.6306,"HyperDash":false},{"StartTime":145314.0,"Position":338.7863,"HyperDash":false},{"StartTime":145400.0,"Position":289.941956,"HyperDash":false},{"StartTime":145522.0,"Position":236.0,"HyperDash":false}]},{"StartTime":145713.0,"Objects":[{"StartTime":145713.0,"Position":144.0,"HyperDash":false}]},{"StartTime":145902.0,"Objects":[{"StartTime":145902.0,"Position":80.0,"HyperDash":false}]},{"StartTime":146092.0,"Objects":[{"StartTime":146092.0,"Position":16.0,"HyperDash":false}]},{"StartTime":146472.0,"Objects":[{"StartTime":146472.0,"Position":256.0,"HyperDash":false}]},{"StartTime":146852.0,"Objects":[{"StartTime":146852.0,"Position":496.0,"HyperDash":false}]},{"StartTime":147137.0,"Objects":[{"StartTime":147137.0,"Position":352.0,"HyperDash":false}]},{"StartTime":147421.0,"Objects":[{"StartTime":147421.0,"Position":160.0,"HyperDash":false}]},{"StartTime":147611.0,"Objects":[{"StartTime":147611.0,"Position":256.0,"HyperDash":false}]},{"StartTime":147991.0,"Objects":[{"StartTime":147991.0,"Position":256.0,"HyperDash":false}]},{"StartTime":148371.0,"Objects":[{"StartTime":148371.0,"Position":256.0,"HyperDash":false}]},{"StartTime":148561.0,"Objects":[{"StartTime":148561.0,"Position":368.0,"HyperDash":false}]},{"StartTime":148751.0,"Objects":[{"StartTime":148751.0,"Position":256.0,"HyperDash":false}]},{"StartTime":148940.0,"Objects":[{"StartTime":148940.0,"Position":144.0,"HyperDash":false}]},{"StartTime":149130.0,"Objects":[{"StartTime":149130.0,"Position":288.0,"HyperDash":false}]},{"StartTime":149225.0,"Objects":[{"StartTime":149225.0,"Position":312.0,"HyperDash":false}]},{"StartTime":149320.0,"Objects":[{"StartTime":149320.0,"Position":336.0,"HyperDash":false}]},{"StartTime":149415.0,"Objects":[{"StartTime":149415.0,"Position":312.0,"HyperDash":false}]},{"StartTime":149510.0,"Objects":[{"StartTime":149510.0,"Position":288.0,"HyperDash":false}]},{"StartTime":149700.0,"Objects":[{"StartTime":149700.0,"Position":224.0,"HyperDash":false}]},{"StartTime":149795.0,"Objects":[{"StartTime":149795.0,"Position":200.0,"HyperDash":false}]},{"StartTime":149890.0,"Objects":[{"StartTime":149890.0,"Position":176.0,"HyperDash":false}]},{"StartTime":149985.0,"Objects":[{"StartTime":149985.0,"Position":200.0,"HyperDash":false}]},{"StartTime":150080.0,"Objects":[{"StartTime":150080.0,"Position":224.0,"HyperDash":false}]},{"StartTime":150175.0,"Objects":[{"StartTime":150175.0,"Position":256.0,"HyperDash":false}]},{"StartTime":150270.0,"Objects":[{"StartTime":150270.0,"Position":256.0,"HyperDash":false}]},{"StartTime":150649.0,"Objects":[{"StartTime":150649.0,"Position":168.0,"HyperDash":false},{"StartTime":150725.0,"Position":142.229309,"HyperDash":false},{"StartTime":150838.0,"Position":168.0,"HyperDash":false}]},{"StartTime":151029.0,"Objects":[{"StartTime":151029.0,"Position":344.0,"HyperDash":false},{"StartTime":151105.0,"Position":368.7707,"HyperDash":false},{"StartTime":151218.0,"Position":344.0,"HyperDash":false}]},{"StartTime":151409.0,"Objects":[{"StartTime":151409.0,"Position":256.0,"HyperDash":false}]},{"StartTime":151599.0,"Objects":[{"StartTime":151599.0,"Position":256.0,"HyperDash":false}]},{"StartTime":151694.0,"Objects":[{"StartTime":151694.0,"Position":256.0,"HyperDash":false}]},{"StartTime":151788.0,"Objects":[{"StartTime":151788.0,"Position":256.0,"HyperDash":false}]},{"StartTime":151978.0,"Objects":[{"StartTime":151978.0,"Position":464.0,"HyperDash":false},{"StartTime":152063.0,"Position":422.517944,"HyperDash":false},{"StartTime":152149.0,"Position":426.162018,"HyperDash":false},{"StartTime":152235.0,"Position":393.104584,"HyperDash":false},{"StartTime":152357.0,"Position":346.162628,"HyperDash":true}]},{"StartTime":152548.0,"Objects":[{"StartTime":152548.0,"Position":48.0,"HyperDash":false},{"StartTime":152633.0,"Position":100.48204,"HyperDash":false},{"StartTime":152719.0,"Position":77.83798,"HyperDash":false},{"StartTime":152805.0,"Position":114.895416,"HyperDash":false},{"StartTime":152927.0,"Position":165.837372,"HyperDash":false}]},{"StartTime":153118.0,"Objects":[{"StartTime":153118.0,"Position":256.0,"HyperDash":false}]},{"StartTime":153213.0,"Objects":[{"StartTime":153213.0,"Position":256.0,"HyperDash":false}]},{"StartTime":153307.0,"Objects":[{"StartTime":153307.0,"Position":256.0,"HyperDash":false}]},{"StartTime":153497.0,"Objects":[{"StartTime":153497.0,"Position":168.0,"HyperDash":false},{"StartTime":153582.0,"Position":217.34,"HyperDash":false},{"StartTime":153668.0,"Position":235.429718,"HyperDash":false},{"StartTime":153754.0,"Position":295.9821,"HyperDash":false},{"StartTime":153876.0,"Position":339.214722,"HyperDash":false}]},{"StartTime":154067.0,"Objects":[{"StartTime":154067.0,"Position":168.0,"HyperDash":false},{"StartTime":154143.0,"Position":134.40947,"HyperDash":false},{"StartTime":154256.0,"Position":104.3604,"HyperDash":false}]},{"StartTime":154447.0,"Objects":[{"StartTime":154447.0,"Position":344.0,"HyperDash":false},{"StartTime":154523.0,"Position":362.5905,"HyperDash":false},{"StartTime":154636.0,"Position":407.6396,"HyperDash":true}]},{"StartTime":154826.0,"Objects":[{"StartTime":154826.0,"Position":168.0,"HyperDash":false},{"StartTime":154902.0,"Position":150.40947,"HyperDash":false},{"StartTime":155015.0,"Position":104.3604,"HyperDash":false}]},{"StartTime":155206.0,"Objects":[{"StartTime":155206.0,"Position":344.0,"HyperDash":false},{"StartTime":155282.0,"Position":365.5905,"HyperDash":false},{"StartTime":155395.0,"Position":407.6396,"HyperDash":false}]},{"StartTime":155586.0,"Objects":[{"StartTime":155586.0,"Position":256.0,"HyperDash":false},{"StartTime":155680.0,"Position":270.830933,"HyperDash":false},{"StartTime":155775.0,"Position":254.810913,"HyperDash":false},{"StartTime":155852.0,"Position":238.329559,"HyperDash":false},{"StartTime":155965.0,"Position":256.0,"HyperDash":false}]},{"StartTime":156156.0,"Objects":[{"StartTime":156156.0,"Position":256.0,"HyperDash":false}]},{"StartTime":156345.0,"Objects":[{"StartTime":156345.0,"Position":256.0,"HyperDash":false}]},{"StartTime":156535.0,"Objects":[{"StartTime":156535.0,"Position":96.0,"HyperDash":false},{"StartTime":156620.0,"Position":138.369385,"HyperDash":false},{"StartTime":156706.0,"Position":196.213715,"HyperDash":false},{"StartTime":156792.0,"Position":213.399918,"HyperDash":false},{"StartTime":156914.0,"Position":244.507538,"HyperDash":false}]},{"StartTime":157105.0,"Objects":[{"StartTime":157105.0,"Position":152.0,"HyperDash":false},{"StartTime":157181.0,"Position":158.0,"HyperDash":false},{"StartTime":157294.0,"Position":122.301514,"HyperDash":false}]},{"StartTime":157485.0,"Objects":[{"StartTime":157485.0,"Position":32.0,"HyperDash":false},{"StartTime":157561.0,"Position":15.0,"HyperDash":false},{"StartTime":157674.0,"Position":61.6984863,"HyperDash":false}]},{"StartTime":157864.0,"Objects":[{"StartTime":157864.0,"Position":152.0,"HyperDash":true}]},{"StartTime":158054.0,"Objects":[{"StartTime":158054.0,"Position":416.0,"HyperDash":false},{"StartTime":158139.0,"Position":368.6306,"HyperDash":false},{"StartTime":158225.0,"Position":342.7863,"HyperDash":false},{"StartTime":158311.0,"Position":278.600067,"HyperDash":false},{"StartTime":158433.0,"Position":267.492462,"HyperDash":false}]},{"StartTime":158624.0,"Objects":[{"StartTime":158624.0,"Position":360.0,"HyperDash":false},{"StartTime":158700.0,"Position":345.0,"HyperDash":false},{"StartTime":158813.0,"Position":389.6985,"HyperDash":false}]},{"StartTime":159004.0,"Objects":[{"StartTime":159004.0,"Position":480.0,"HyperDash":false},{"StartTime":159080.0,"Position":483.0,"HyperDash":false},{"StartTime":159193.0,"Position":450.3015,"HyperDash":false}]},{"StartTime":159383.0,"Objects":[{"StartTime":159383.0,"Position":360.0,"HyperDash":false}]},{"StartTime":159573.0,"Objects":[{"StartTime":159573.0,"Position":255.0,"HyperDash":false},{"StartTime":159658.0,"Position":267.0,"HyperDash":false},{"StartTime":159744.0,"Position":265.0,"HyperDash":false},{"StartTime":159830.0,"Position":261.0,"HyperDash":false},{"StartTime":159952.0,"Position":255.0,"HyperDash":false}]},{"StartTime":160143.0,"Objects":[{"StartTime":160143.0,"Position":256.0,"HyperDash":false}]},{"StartTime":160333.0,"Objects":[{"StartTime":160333.0,"Position":376.0,"HyperDash":false}]},{"StartTime":160523.0,"Objects":[{"StartTime":160523.0,"Position":376.0,"HyperDash":false}]},{"StartTime":160713.0,"Objects":[{"StartTime":160713.0,"Position":256.0,"HyperDash":false}]},{"StartTime":160902.0,"Objects":[{"StartTime":160902.0,"Position":136.0,"HyperDash":false}]},{"StartTime":161092.0,"Objects":[{"StartTime":161092.0,"Position":136.0,"HyperDash":false}]},{"StartTime":161282.0,"Objects":[{"StartTime":161282.0,"Position":199.0,"HyperDash":false},{"StartTime":161341.0,"Position":494.0,"HyperDash":false},{"StartTime":161400.0,"Position":293.0,"HyperDash":false},{"StartTime":161460.0,"Position":115.0,"HyperDash":false},{"StartTime":161519.0,"Position":412.0,"HyperDash":false},{"StartTime":161578.0,"Position":506.0,"HyperDash":false},{"StartTime":161638.0,"Position":293.0,"HyperDash":false},{"StartTime":161697.0,"Position":346.0,"HyperDash":false},{"StartTime":161757.0,"Position":117.0,"HyperDash":false},{"StartTime":161816.0,"Position":285.0,"HyperDash":false},{"StartTime":161875.0,"Position":17.0,"HyperDash":false},{"StartTime":161935.0,"Position":238.0,"HyperDash":false},{"StartTime":161994.0,"Position":222.0,"HyperDash":false},{"StartTime":162053.0,"Position":450.0,"HyperDash":false},{"StartTime":162113.0,"Position":67.0,"HyperDash":false},{"StartTime":162172.0,"Position":219.0,"HyperDash":false},{"StartTime":162232.0,"Position":307.0,"HyperDash":false}]},{"StartTime":162421.0,"Objects":[{"StartTime":162421.0,"Position":256.0,"HyperDash":false}]},{"StartTime":162611.0,"Objects":[{"StartTime":162611.0,"Position":168.0,"HyperDash":false}]},{"StartTime":162706.0,"Objects":[{"StartTime":162706.0,"Position":152.0,"HyperDash":false}]},{"StartTime":162801.0,"Objects":[{"StartTime":162801.0,"Position":136.0,"HyperDash":false},{"StartTime":162886.0,"Position":184.369385,"HyperDash":false},{"StartTime":162972.0,"Position":235.213715,"HyperDash":false},{"StartTime":163058.0,"Position":243.058044,"HyperDash":false},{"StartTime":163180.0,"Position":306.314148,"HyperDash":false}]},{"StartTime":163371.0,"Objects":[{"StartTime":163371.0,"Position":392.0,"HyperDash":false},{"StartTime":163447.0,"Position":387.0,"HyperDash":false},{"StartTime":163560.0,"Position":392.0,"HyperDash":false}]},{"StartTime":163751.0,"Objects":[{"StartTime":163751.0,"Position":440.0,"HyperDash":false}]},{"StartTime":163940.0,"Objects":[{"StartTime":163940.0,"Position":344.0,"HyperDash":false}]},{"StartTime":164130.0,"Objects":[{"StartTime":164130.0,"Position":120.0,"HyperDash":false},{"StartTime":164215.0,"Position":96.0444,"HyperDash":false},{"StartTime":164301.0,"Position":55.6488266,"HyperDash":false},{"StartTime":164387.0,"Position":88.2046661,"HyperDash":false},{"StartTime":164509.0,"Position":93.82585,"HyperDash":false}]},{"StartTime":164700.0,"Objects":[{"StartTime":164700.0,"Position":232.0,"HyperDash":false},{"StartTime":164785.0,"Position":275.9556,"HyperDash":false},{"StartTime":164871.0,"Position":299.351166,"HyperDash":false},{"StartTime":164957.0,"Position":295.795349,"HyperDash":false},{"StartTime":165079.0,"Position":258.174164,"HyperDash":false}]},{"StartTime":165270.0,"Objects":[{"StartTime":165270.0,"Position":160.0,"HyperDash":false}]},{"StartTime":165459.0,"Objects":[{"StartTime":165459.0,"Position":160.0,"HyperDash":false}]},{"StartTime":165649.0,"Objects":[{"StartTime":165649.0,"Position":304.0,"HyperDash":false},{"StartTime":165734.0,"Position":324.3694,"HyperDash":false},{"StartTime":165820.0,"Position":364.7582,"HyperDash":false},{"StartTime":165906.0,"Position":401.273468,"HyperDash":false},{"StartTime":166028.0,"Position":446.4695,"HyperDash":false}]},{"StartTime":166219.0,"Objects":[{"StartTime":166219.0,"Position":320.0,"HyperDash":false},{"StartTime":166295.0,"Position":331.608,"HyperDash":false},{"StartTime":166408.0,"Position":376.222565,"HyperDash":false}]},{"StartTime":166599.0,"Objects":[{"StartTime":166599.0,"Position":456.0,"HyperDash":false},{"StartTime":166693.0,"Position":485.888763,"HyperDash":false},{"StartTime":166788.0,"Position":512.0,"HyperDash":false},{"StartTime":166865.0,"Position":508.525848,"HyperDash":false},{"StartTime":166978.0,"Position":456.0,"HyperDash":false}]},{"StartTime":167168.0,"Objects":[{"StartTime":167168.0,"Position":376.0,"HyperDash":false}]},{"StartTime":167358.0,"Objects":[{"StartTime":167358.0,"Position":376.0,"HyperDash":false},{"StartTime":167434.0,"Position":359.082825,"HyperDash":false},{"StartTime":167547.0,"Position":319.0086,"HyperDash":false}]},{"StartTime":167738.0,"Objects":[{"StartTime":167738.0,"Position":240.0,"HyperDash":false},{"StartTime":167814.0,"Position":227.391983,"HyperDash":false},{"StartTime":167927.0,"Position":183.777435,"HyperDash":false}]},{"StartTime":168118.0,"Objects":[{"StartTime":168118.0,"Position":112.0,"HyperDash":false},{"StartTime":168203.0,"Position":82.78144,"HyperDash":false},{"StartTime":168289.0,"Position":79.26619,"HyperDash":false},{"StartTime":168375.0,"Position":41.750946,"HyperDash":false},{"StartTime":168497.0,"Position":0.0,"HyperDash":true}]},{"StartTime":168687.0,"Objects":[{"StartTime":168687.0,"Position":256.0,"HyperDash":false},{"StartTime":168772.0,"Position":272.0,"HyperDash":false},{"StartTime":168858.0,"Position":270.0,"HyperDash":false},{"StartTime":168944.0,"Position":274.0,"HyperDash":false},{"StartTime":169066.0,"Position":256.0,"HyperDash":false}]},{"StartTime":169257.0,"Objects":[{"StartTime":169257.0,"Position":328.0,"HyperDash":false}]},{"StartTime":169447.0,"Objects":[{"StartTime":169447.0,"Position":256.0,"HyperDash":false}]},{"StartTime":169637.0,"Objects":[{"StartTime":169637.0,"Position":184.0,"HyperDash":false}]},{"StartTime":169827.0,"Objects":[{"StartTime":169827.0,"Position":256.0,"HyperDash":false}]},{"StartTime":170016.0,"Objects":[{"StartTime":170016.0,"Position":328.0,"HyperDash":true}]},{"StartTime":170206.0,"Objects":[{"StartTime":170206.0,"Position":32.0,"HyperDash":false},{"StartTime":170291.0,"Position":69.44879,"HyperDash":false},{"StartTime":170377.0,"Position":93.3499146,"HyperDash":false},{"StartTime":170463.0,"Position":153.251038,"HyperDash":false},{"StartTime":170585.0,"Position":203.43634,"HyperDash":true}]},{"StartTime":170776.0,"Objects":[{"StartTime":170776.0,"Position":480.0,"HyperDash":false},{"StartTime":170861.0,"Position":437.5512,"HyperDash":false},{"StartTime":170947.0,"Position":400.6501,"HyperDash":false},{"StartTime":171033.0,"Position":369.748962,"HyperDash":false},{"StartTime":171155.0,"Position":308.56366,"HyperDash":false}]},{"StartTime":171345.0,"Objects":[{"StartTime":171345.0,"Position":328.0,"HyperDash":false}]},{"StartTime":171535.0,"Objects":[{"StartTime":171535.0,"Position":184.0,"HyperDash":true}]},{"StartTime":171725.0,"Objects":[{"StartTime":171725.0,"Position":440.0,"HyperDash":false},{"StartTime":171810.0,"Position":393.6306,"HyperDash":false},{"StartTime":171896.0,"Position":358.7863,"HyperDash":false},{"StartTime":171982.0,"Position":322.941956,"HyperDash":false},{"StartTime":172104.0,"Position":260.0,"HyperDash":false}]},{"StartTime":172295.0,"Objects":[{"StartTime":172295.0,"Position":152.0,"HyperDash":false}]},{"StartTime":172485.0,"Objects":[{"StartTime":172485.0,"Position":192.0,"HyperDash":false}]},{"StartTime":172675.0,"Objects":[{"StartTime":172675.0,"Position":320.0,"HyperDash":false}]},{"StartTime":172864.0,"Objects":[{"StartTime":172864.0,"Position":360.0,"HyperDash":false}]},{"StartTime":173054.0,"Objects":[{"StartTime":173054.0,"Position":320.0,"HyperDash":false}]},{"StartTime":173244.0,"Objects":[{"StartTime":173244.0,"Position":192.0,"HyperDash":false}]},{"StartTime":173434.0,"Objects":[{"StartTime":173434.0,"Position":487.0,"HyperDash":false},{"StartTime":173528.0,"Position":53.0,"HyperDash":false},{"StartTime":173623.0,"Position":40.0,"HyperDash":false},{"StartTime":173718.0,"Position":153.0,"HyperDash":false},{"StartTime":173813.0,"Position":79.0,"HyperDash":false},{"StartTime":173908.0,"Position":488.0,"HyperDash":false},{"StartTime":174003.0,"Position":396.0,"HyperDash":false},{"StartTime":174098.0,"Position":428.0,"HyperDash":false},{"StartTime":174193.0,"Position":59.0,"HyperDash":false},{"StartTime":174288.0,"Position":255.0,"HyperDash":false},{"StartTime":174383.0,"Position":294.0,"HyperDash":false},{"StartTime":174478.0,"Position":354.0,"HyperDash":false},{"StartTime":174573.0,"Position":270.0,"HyperDash":false},{"StartTime":174668.0,"Position":362.0,"HyperDash":false},{"StartTime":174763.0,"Position":255.0,"HyperDash":false},{"StartTime":174858.0,"Position":203.0,"HyperDash":false},{"StartTime":174953.0,"Position":67.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/42587.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/42587.osu new file mode 100644 index 0000000000..41366eab43 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/42587.osu @@ -0,0 +1,528 @@ +osu file format v6 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:8 +CircleSize:5 +OverallDifficulty:8 +SliderMultiplier:1.8 +SliderTickRate:0.5 + +[Events] +//Break Periods +2,99204,110406 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,0,0,0 + +[TimingPoints] +270,379.746835443038,4,2,1,85,1,0 +48782,-100,4,2,0,50,0,0 +48972,-100,4,2,0,85,0,0 +60744,-100,4,2,1,85,0,0 +60982,-100,4,2,1,65,0,0 +61171,-100,4,2,1,85,0,0 +71092,-100,4,2,1,40,0,0 +71282,-100,4,2,1,85,0,0 +71567,-100,4,2,0,60,0,0 +71852,-100,4,2,1,85,0,0 +72232,-100,4,2,1,85,0,0 +73086,-100,4,1,0,60,0,0 +74035,-100,4,1,1,50,0,0 +74890,-100,4,2,1,85,0,0 +81061,-100,4,2,1,60,0,0 +81251,-100,4,2,1,85,0,0 +82580,-100,4,2,1,60,0,0 +82770,-100,4,2,1,85,0,0 +86804,-100,4,2,1,60,0,0 +86947,-100,4,2,1,85,0,0 +87137,-100,4,2,1,60,0,0 +87326,-100,4,2,1,85,0,0 +88656,-100,4,2,1,60,0,0 +88845,-100,4,2,1,85,0,0 +92643,-100,4,2,1,60,0,0 +92833,-100,4,2,1,85,0,0 +93592,-100,4,2,1,60,0,0 +93782,-100,4,2,1,86,0,0 +94162,-100,4,2,1,60,0,0 +94352,-100,4,2,1,85,0,0 +95111,-100,4,2,1,60,0,0 +95301,-100,4,2,1,85,0,0 +98624,-100,4,1,1,70,0,0 +110966,-100,4,1,0,60,0,0 +112390,-100,4,2,1,85,0,0 +118371,-100,4,2,1,75,0,0 +118751,-100,4,2,1,65,0,0 +119130,-100,4,2,1,55,0,0 +119510,-100,4,2,1,85,0,0 +135934,-100,4,1,1,80,0,0 +136314,-100,4,2,1,85,0,0 +136883,-100,4,2,1,65,0,0 +137073,-100,4,2,1,85,0,0 +147516,-100,4,2,0,60,0,0 +147801,-100,4,2,1,85,0,0 +149035,-100,4,1,1,65,0,0 +149605,-100,4,1,1,75,0,0 +150459,-100,4,2,1,70,0,0 +150744,-100,4,2,1,85,0,0 +150934,-100,4,2,1,70,0,0 +151124,-100,4,2,1,85,0,0 +157010,-100,4,2,1,70,0,0 +157200,-100,4,2,1,85,0,0 +158529,-100,4,2,1,70,0,0 +158719,-100,4,2,1,85,0,0 +162754,-100,4,2,1,70,0,0 +162896,-100,4,2,1,85,0,0 +163466,-100,4,2,1,70,0,0 +163656,-100,4,2,1,85,0,0 +164035,-100,4,2,1,70,0,0 +164225,-100,4,2,1,85,0,0 +164605,-100,4,2,1,70,0,0 +164795,-100,4,2,1,85,0,0 +168592,-100,4,2,1,70,0,0 +168782,-100,4,2,1,85,0,0 +169542,-100,4,2,1,70,0,0 +169732,-100,4,2,1,85,0,0 +170111,-100,4,2,1,70,0,0 +170301,-100,4,2,1,85,0,0 +170681,-100,4,2,1,70,0,0 +170871,-100,4,2,1,85,0,0 +173339,-100,4,1,0,51,0,0 + +[HitObjects] +376,288,24383,1,0 +392,264,24478,1,0 +408,240,24573,1,8 +448,160,24763,2,0,B|344:160,1,90,0|0 +280,120,25143,1,0 +232,200,25333,1,0 +152,160,25523,2,0,B|56:160,1,90,0|0 +32,248,25902,1,0 +96,312,26092,6,0,L|144:376|264:376,2,180 +96,224,27042,1,0 +176,264,27232,2,0,B|288:264,1,90 +448,264,27801,1,0 +360,264,27991,1,0 +192,192,28371,1,0 +280,192,28561,1,0 +368,192,28751,1,0 +456,192,28940,1,0 +456,192,29130,6,0,L|408:128|288:128,2,180 +456,280,30080,1,0 +376,240,30270,2,0,B|256:240,1,90 +112,280,30839,1,0 +176,216,31029,1,0 +112,152,31219,1,0 +112,152,31314,1,0 +112,152,31409,1,0 +176,88,31599,1,0 +240,152,31788,1,0 +176,216,31978,1,0 +240,280,32168,6,0,L|296:328|416:328,2,180 +240,192,33118,1,0 +328,192,33307,2,0,L|328:152|288:96,1,90 +136,32,33877,1,0 +80,104,34067,1,0 +24,176,34257,1,0 +24,200,34352,1,0 +24,224,34447,1,0 +40,240,34542,1,0 +56,248,34637,1,0 +144,248,34826,1,0 +232,248,35016,1,0 +376,248,35206,6,0,L|408:248|464:288,2,90,0|0|0 +232,248,35776,2,0,L|200:248|144:288,1,90 +304,352,36156,1,0 +304,248,36345,2,0,B|304:152,1,90 +112,80,36725,6,0,B|16:128,1,90,8|0 +112,160,37105,2,0,B|16:208,1,90 +112,240,37485,1,0 +112,328,37675,6,0,B|24:376,2,90 +112,240,38244,1,0 +32,200,38434,1,0 +112,160,38624,1,0 +32,120,38814,1,0 +112,80,39004,1,0 +200,80,39194,6,0,B|304:80,1,90 +384,168,39573,2,0,B|272:168,1,90 +200,256,39953,2,0,B|296:256,3,90 +408,200,40713,5,0 +360,112,40902,1,0 +280,56,41092,2,0,B|192:24|88:64,1,180 +168,128,41662,2,0,B|120:136|64:176,2,90 +264,128,42232,2,0,B|384:128|448:232,1,180 +320,224,42801,2,0,B|280:312,1,90,0|0 +184,336,43181,2,0,B|232:248,1,90 +227,256,43561,1,0 +192,176,43751,6,0,B|144:256,2,90,0|0|0 +128,112,44320,2,0,B|184:32|304:40,1,180 +376,40,44890,1,0 +440,208,45270,5,0 +384,280,45459,1,0 +304,312,45649,1,0 +216,328,45839,2,0,B|141:308|112:292|56:216,1,180 +56,144,46409,1,0 +216,64,46788,5,0 +296,96,46978,1,0 +216,144,47168,1,0 +296,176,47358,1,0 +136,232,47738,1,0 +376,296,48118,1,0 +136,360,48497,1,0 +376,184,48877,6,0,B|256:184,1,90,4|0 +192,184,49257,1,0 +128,120,49447,1,0 +216,120,49637,2,0,B|328:120,3,90 +400,120,50396,2,0,B|472:200|400:304,1,180 +336,232,50966,2,0,B|264:320,1,90 +208,360,51345,5,0 +168,280,51535,1,0 +120,360,51725,1,0 +72,280,51915,2,0,B|48:176|136:112,1,180 +216,88,52485,1,0 +304,112,52675,1,0 +232,168,52864,5,0 +312,200,53054,1,0 +288,288,53244,2,0,B|368:320|456:232,1,180 +392,176,53814,2,0,B|336:72,1,90 +280,152,54194,2,0,B|184:80,1,90,0|0 +176,192,54573,1,0 +104,136,54763,2,0,B|48:248|104:336|208:344,1,270 +216,256,55523,1,0 +264,184,55713,1,0 +352,184,55902,5,0 +440,136,56092,1,0 +352,88,56282,1,0 +264,88,56472,2,0,B|144:-16|8:96,1,270 +160,152,57421,5,0 +32,216,57611,1,0 +160,280,57801,1,0 +248,312,57991,2,0,B|368:416|504:304,1,270 +360,192,58940,5,0 +256,192,59130,1,0 +152,192,59320,1,0 +168,96,59510,2,0,B|256:56|368:104,3,180 +408,136,60839,1,4 +408,136,60934,1,4 +408,136,61029,6,0,B|352:216|296:296,1,180,8|4 +304,283,61599,1,0 +216,280,61788,2,0,B|263:212|319:132,1,180,0|4 +319,132,62358,1,0 +240,96,62548,6,0,B|312:0,2,90,0|0|4 +192,168,63118,2,0,B|136:248|80:328,1,180,0|0 +176,312,63687,1,4 +264,312,63877,1,0 +352,312,64067,6,0,B|448:248|416:120,1,180,0|4 +352,208,64637,1,0 +272,168,64826,2,0,B|344:72,1,90 +326,96,65206,1,4 +272,24,65396,2,0,B|160:56|168:184,1,180 +104,96,65966,1,4 +48,168,66156,1,0 +104,232,66345,1,0 +56,312,66535,1,0 +80,328,66630,1,0 +104,344,66725,1,4 +192,312,66915,1,0 +280,344,67105,6,0,B|436:254,1,180,0|4 +448,168,67675,1,0 +456,80,67864,2,0,B|299:169,1,180,0|4 +288,256,68434,1,0 +208,296,68624,5,0 +128,256,68814,1,0 +48,296,69004,1,4 +128,256,69194,2,0,B|208:192|192:80,1,180 +256,32,69763,1,4 +256,32,69953,1,0 +318,96,70143,6,0,B|304:192|384:256,1,180,0|4 +256,120,70902,2,0,B|224:184|304:200|248:264,2,135 +256,32,71662,5,4 +256,32,72042,1,4 +160,144,72421,5,2 +224,144,72611,1,2 +288,144,72801,1,2 +352,144,72991,1,2 +408,216,73181,5,0 +304,216,73371,1,0 +208,216,73561,1,0 +112,216,73751,1,0 +160,288,73940,5,0 +224,288,74130,1,8 +248,288,74225,1,8 +272,288,74320,1,8 +296,288,74415,1,0 +320,288,74510,1,0 +344,288,74605,1,4 +368,288,74700,6,0,B|464:256|480:136,1,180,4|4 +368,64,75270,1,0 +296,176,75459,2,0,B|240:208|184:152,1,90 +144,64,75839,1,4 +168,328,76029,6,0,B|224:344|262:347|352:328,1,180,0|0 +344,192,76599,2,0,B|282:175|232:167|144:200,1,180,4|0 +256,256,77168,1,0 +256,256,77358,1,4 +424,256,77548,6,0,B|444:180|440:128|424:72,1,180 +296,32,78118,2,0,B|336:144,1,90,4|0 +240,264,78497,2,0,B|280:152,1,90 +168,32,78877,2,0,B|200:120,1,90,4|0 +104,264,79257,2,0,B|136:176,1,90 +48,120,79637,2,0,B|8:16,2,90,4|0|0 +48,120,80206,1,4 +48,120,80396,1,4 +48,256,80586,6,0,B|72:360|192:360,1,180,0|0 +334,359,81156,2,0,B|440:360|464:256,1,180,12|0 +256,192,81725,1,0 +256,192,81915,1,4 +48,128,82105,6,0,B|72:24|192:24,1,180,0|0 +334,25,82675,2,0,B|440:24|464:128,1,180,12|0 +256,192,83244,1,0 +256,192,83434,1,4 +177,24,83624,6,0,B|72:24|48:128,1,180 +240,96,84194,2,0,B|128:120,1,90,4|0 +40,208,84573,2,0,B|160:184,1,90,0|0 +280,216,84953,2,0,B|184:240,1,90,4|0 +256,208,85333,12,4,86282 +256,192,86472,5,4 +128,80,86662,5,4 +152,64,86757,1,4 +176,48,86852,2,0,B|288:48,1,90,12|0 +360,56,87232,2,0,L|288:112|176:112,1,180,12|0 +136,176,87801,2,0,B|240:176,1,90,0|4 +440,352,88181,6,0,L|389:352|344:312|272:360,1,180,0|0 +72,352,88751,2,0,L|122:352|168:312|240:360,1,180,12|0 +256,192,89320,2,0,B|256:240,2,45,0|0|4 +488,48,89700,6,0,B|389:33|304:88,1,180 +256,192,90270,1,4 +160,280,90459,1,0 +64,192,90649,1,0 +160,104,90839,1,0 +256,192,91029,1,4 +352,280,91219,1,0 +448,192,91409,1,0 +352,104,91599,1,0 +256,192,91788,1,4 +256,64,91978,1,0 +256,192,92168,1,0 +256,192,92358,2,0,B|256:304,1,90,4|4 +32,32,92738,6,0,L|144:32|200:88,1,180,8|0 +64,128,93307,2,0,B|127:191,1,90,4|0 +256,152,93687,2,0,B|319:215,1,90,8|0 +424,304,94067,1,4 +256,368,94257,6,0,L|192:328|192:216,1,180,8|0 +328,224,94826,2,0,B|440:224,2,90,4|0|8 +328,88,95396,1,0 +328,88,95586,1,4 +192,88,95776,6,0,B|104:67|12:88,1,180,0|2 +56,192,96345,2,0,B|176:192,1,90,6|2 +232,232,96725,1,2 +280,152,96915,1,2 +360,192,97105,2,12,B|472:192,1,90,6|2 +256,208,97485,12,4,99004 +256,352,111156,5,4 +256,192,111915,1,0 +256,192,112105,1,0 +256,192,112295,1,0 +256,104,112485,1,0 +328,48,112675,6,0,B|416:48|456:88|456:160,1,180 +456,232,113244,1,0 +456,320,113434,1,0 +368,336,113624,2,0,B|304:336|272:392,2,90 +456,320,114194,2,0,B|416:256|376:232|288:224,1,180 +256,296,114763,2,0,B|200:288|160:240,1,90 +112,192,115143,5,0 +176,256,115333,1,0 +240,192,115523,1,0 +176,128,115713,2,0,B|224:48|344:48,1,180 +296,128,116282,1,0 +360,192,116472,1,0 +448,192,116662,6,0,B|368:272,1,90 +384,352,117042,2,0,B|264:360|216:232,1,180 +280,192,117611,2,0,B|323:159|280:104,1,90 +192,112,117991,2,0,B|155:158|198:191,1,90 +248,360,118561,1,0 +248,296,118940,1,0 +248,232,119320,1,0 +448,240,119700,5,0 +384,304,119890,1,0 +320,240,120080,1,0 +256,304,120270,2,0,B|176:336|48:296,1,180 +80,304,120839,1,0 +32,32,121219,5,0 +120,136,121409,1,0 +208,32,121599,1,0 +296,136,121788,2,0,B|376:104|504:144,1,180 +472,136,122358,1,0 +208,192,122738,5,0 +256,112,122928,1,0 +304,192,123117,1,0 +256,272,123307,1,0 +256,48,123687,1,0 +256,336,124067,1,0 +256,248,124257,5,4 +256,160,124447,1,4 +256,72,124637,1,4 +256,72,124732,1,4 +256,72,124826,2,4,B|376:72|376:176,1,180,0|4 +456,224,125396,1,0 +392,288,125586,1,0 +304,288,125776,6,0,B|200:352,1,90,0|4 +192,248,126156,1,0 +160,336,126345,2,0,B|48:336|24:192,1,180,0|4 +120,224,126915,2,0,B|48:120,1,90 +136,96,127295,2,0,B|72:8,2,90,0|4|0 +184,168,127864,2,0,B|312:344,1,180,0|4 +384,312,128434,1,0 +448,256,128624,1,0 +448,168,128814,6,0,B|344:112,1,90,0|4 +440,72,129194,2,0,B|368:40|328:40|248:80,1,180 +208,136,129763,1,4 +128,184,129953,1,0 +208,232,130143,1,0 +288,184,130333,2,0,B|400:184,1,90,0|4 +448,248,130713,2,0,B|352:248,1,90 +176,248,131282,1,4 +360,248,131662,1,0 +288,192,131852,5,0 +200,192,132042,1,4 +112,192,132232,1,0 +96,288,132421,2,0,B|0:256|-32:144|112:88,1,270 +224,192,133371,5,0 +312,192,133561,1,4 +400,192,133751,1,0 +416,288,133940,2,0,B|512:256|544:144|400:88,1,270 +80,192,134890,5,0 +160,152,135080,1,4 +200,232,135270,1,0 +280,192,135459,1,0 +464,192,135839,1,4 +376,192,136029,1,0 +376,192,136219,1,0 +280,192,136409,1,4 +280,192,136599,1,4 +56,216,136978,6,0,B|144:272|256:200,1,180,8|4 +456,168,137738,2,0,B|368:112|256:184,1,180,0|4 +256,32,138497,5,0 +200,104,138687,1,0 +256,176,138877,1,4 +312,104,139067,2,0,B|424:104,1,90 +400,192,139447,2,0,B|504:192,2,90,0|4|0 +400,280,140016,6,0,B|400:368|232:352,1,180,0|4 +224,272,140586,1,0 +296,216,140776,1,0 +224,168,140966,1,0 +296,112,141156,1,4 +256,32,141345,2,0,B|107:25|115:113,1,180,0|0 +112,200,141915,2,0,B|112:312,2,90,4|0|0 +112,112,142485,1,0 +112,112,142580,1,0 +112,112,142675,1,4 +112,24,142864,1,0 +232,8,143054,6,0,B|152:96|248:208,1,180,0|4 +280,376,143814,2,0,B|360:288|264:176,1,180,0|4 +256,32,144573,5,0 +344,32,144763,1,0 +416,88,144953,1,4 +416,176,145143,2,0,B|232:176,1,180 +144,176,145713,1,4 +80,112,145902,1,0 +16,176,146092,5,0 +256,304,146472,1,4 +496,176,146852,1,0 +352,32,147137,1,0 +160,32,147421,1,0 +256,160,147611,5,4 +256,224,147991,1,4 +256,96,148371,5,2 +368,192,148561,1,2 +256,288,148751,1,2 +144,192,148940,1,2 +288,144,149130,5,0 +312,168,149225,1,8 +336,192,149320,1,0 +312,216,149415,1,8 +288,240,149510,1,0 +224,144,149700,5,0 +200,168,149795,1,8 +176,192,149890,1,0 +200,216,149985,1,8 +224,240,150080,1,0 +256,256,150175,1,8 +256,288,150270,1,0 +168,24,150649,6,4,L|152:56|168:80|168:128,1,90,8|0 +344,24,151029,2,0,L|360:56|344:80|344:128,1,90,12|0 +256,264,151409,1,8 +256,80,151599,1,0 +256,80,151694,1,0 +256,80,151788,1,4 +464,224,151978,6,0,L|424:240|424:240|440:280|328:280,1,180,0|0 +48,280,152548,2,0,L|88:264|88:264|72:224|184:224,1,180,4|0 +256,80,153118,1,0 +256,80,153213,1,0 +256,80,153307,1,4 +168,312,153497,6,0,B|256:368|368:296,1,180 +168,248,154067,2,0,B|96:176,1,90,4|0 +344,248,154447,2,0,B|416:176,1,90 +168,160,154826,2,0,B|96:88,1,90,4|0 +344,160,155206,2,0,B|416:88,1,90 +256,352,155586,2,0,B|280:312|216:296|272:248,2,90,4|0|0 +256,352,156156,1,4 +256,352,156345,1,4 +96,32,156535,6,0,L|208:32|264:120,1,180,0|0 +152,96,157105,2,0,L|152:144|112:184,1,90,12|0 +32,176,157485,2,0,L|32:224|64:256,1,90 +152,256,157864,1,4 +416,352,158054,6,0,L|304:352|248:264,1,180 +360,288,158624,2,0,L|360:240|400:200,1,90,12|0 +480,208,159004,2,0,L|480:160|448:128,1,90 +360,128,159383,1,4 +255,236,159573,6,0,B|255:52,1,180 +256,56,160143,1,4 +376,120,160333,1,0 +376,264,160523,1,0 +256,328,160713,1,0 +136,264,160902,1,4 +136,120,161092,1,0 +256,208,161282,12,4,162232 +256,192,162421,5,4 +168,320,162611,5,4 +152,336,162706,1,4 +136,352,162801,2,0,L|264:352|320:312,1,180,12|4 +392,352,163371,2,0,B|392:248,1,90,0|8 +440,184,163751,1,0 +344,184,163940,1,4 +120,32,164130,6,0,B|8:64|120:216,1,180,8|0 +232,136,164700,2,0,B|344:168|232:320,1,180,8|0 +160,360,165270,1,0 +160,360,165459,1,4 +304,360,165649,6,0,L|384:360|448:280,1,180 +320,288,166219,2,0,B|384:208,1,90,4|0 +456,120,166599,2,0,B|512:50,2,90,0|0|4 +376,216,167168,1,0 +376,88,167358,2,0,B|304:176,1,90 +240,120,167738,2,0,B|176:200,1,90,4|0 +112,144,168118,2,0,B|-16:304,1,180,0|4 +256,360,168687,6,0,B|256:168,1,180,8|0 +328,96,169257,1,4 +256,16,169447,1,0 +184,96,169637,1,8 +256,176,169827,1,0 +328,96,170016,1,4 +32,304,170206,6,0,B|232:240,1,180,8|0 +480,80,170776,2,0,B|280:144,1,180,8|0 +328,280,171345,1,0 +184,104,171535,1,4 +440,192,171725,6,4,B|248:192,1,180,0|2 +152,192,172295,1,2 +192,72,172485,1,2 +320,72,172675,1,2 +360,192,172864,1,2 +320,312,173054,1,2 +192,312,173244,1,2 +256,208,173434,12,4,174953 diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/50859-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/50859-expected-conversion.json new file mode 100644 index 0000000000..ee89090492 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/50859-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":179.0,"Objects":[{"StartTime":179.0,"Position":120.0,"HyperDash":false}]},{"StartTime":786.0,"Objects":[{"StartTime":786.0,"Position":311.0,"HyperDash":false},{"StartTime":852.0,"Position":322.1386,"HyperDash":false},{"StartTime":919.0,"Position":352.673279,"HyperDash":false},{"StartTime":986.0,"Position":387.207916,"HyperDash":false},{"StartTime":1089.0,"Position":431.0,"HyperDash":false}]},{"StartTime":1392.0,"Objects":[{"StartTime":1392.0,"Position":431.0,"HyperDash":false},{"StartTime":1458.0,"Position":419.8614,"HyperDash":false},{"StartTime":1525.0,"Position":395.326721,"HyperDash":false},{"StartTime":1592.0,"Position":352.792084,"HyperDash":false},{"StartTime":1695.0,"Position":311.0,"HyperDash":false}]},{"StartTime":1998.0,"Objects":[{"StartTime":1998.0,"Position":215.0,"HyperDash":false}]},{"StartTime":2301.0,"Objects":[{"StartTime":2301.0,"Position":119.0,"HyperDash":false},{"StartTime":2376.0,"Position":147.702972,"HyperDash":false},{"StartTime":2452.0,"Position":163.801971,"HyperDash":false},{"StartTime":2528.0,"Position":200.900986,"HyperDash":false},{"StartTime":2604.0,"Position":239.0,"HyperDash":false},{"StartTime":2679.0,"Position":264.702972,"HyperDash":false},{"StartTime":2755.0,"Position":312.801971,"HyperDash":false},{"StartTime":2831.0,"Position":332.901,"HyperDash":false},{"StartTime":2907.0,"Position":359.0,"HyperDash":false},{"StartTime":2973.0,"Position":390.1386,"HyperDash":false},{"StartTime":3040.0,"Position":392.673279,"HyperDash":false},{"StartTime":3107.0,"Position":429.207916,"HyperDash":false},{"StartTime":3210.0,"Position":479.0,"HyperDash":false}]},{"StartTime":3513.0,"Objects":[{"StartTime":3513.0,"Position":478.0,"HyperDash":false}]},{"StartTime":3816.0,"Objects":[{"StartTime":3816.0,"Position":382.0,"HyperDash":false},{"StartTime":3882.0,"Position":373.8614,"HyperDash":false},{"StartTime":3949.0,"Position":346.326721,"HyperDash":false},{"StartTime":4016.0,"Position":315.792084,"HyperDash":false},{"StartTime":4119.0,"Position":262.0,"HyperDash":false}]},{"StartTime":4422.0,"Objects":[{"StartTime":4422.0,"Position":166.0,"HyperDash":false},{"StartTime":4488.0,"Position":158.0,"HyperDash":false},{"StartTime":4555.0,"Position":149.0,"HyperDash":false},{"StartTime":4622.0,"Position":179.0,"HyperDash":false},{"StartTime":4725.0,"Position":166.0,"HyperDash":false}]},{"StartTime":5331.0,"Objects":[{"StartTime":5331.0,"Position":166.0,"HyperDash":false}]},{"StartTime":5634.0,"Objects":[{"StartTime":5634.0,"Position":261.0,"HyperDash":false},{"StartTime":5691.0,"Position":278.649017,"HyperDash":false},{"StartTime":5785.0,"Position":321.0,"HyperDash":false}]},{"StartTime":6089.0,"Objects":[{"StartTime":6089.0,"Position":321.0,"HyperDash":false},{"StartTime":6146.0,"Position":325.0,"HyperDash":false},{"StartTime":6240.0,"Position":321.0,"HyperDash":false}]},{"StartTime":6543.0,"Objects":[{"StartTime":6543.0,"Position":321.0,"HyperDash":false}]},{"StartTime":6998.0,"Objects":[{"StartTime":6998.0,"Position":465.0,"HyperDash":false},{"StartTime":7055.0,"Position":450.0,"HyperDash":false},{"StartTime":7149.0,"Position":465.0,"HyperDash":false}]},{"StartTime":7452.0,"Objects":[{"StartTime":7452.0,"Position":369.0,"HyperDash":false},{"StartTime":7518.0,"Position":365.0,"HyperDash":false},{"StartTime":7585.0,"Position":368.0,"HyperDash":false},{"StartTime":7652.0,"Position":383.0,"HyperDash":false},{"StartTime":7755.0,"Position":369.0,"HyperDash":false}]},{"StartTime":8058.0,"Objects":[{"StartTime":8058.0,"Position":464.0,"HyperDash":false}]},{"StartTime":8361.0,"Objects":[{"StartTime":8361.0,"Position":464.0,"HyperDash":false},{"StartTime":8427.0,"Position":422.8614,"HyperDash":false},{"StartTime":8494.0,"Position":392.326721,"HyperDash":false},{"StartTime":8561.0,"Position":368.792084,"HyperDash":false},{"StartTime":8664.0,"Position":344.0,"HyperDash":false}]},{"StartTime":8967.0,"Objects":[{"StartTime":8967.0,"Position":248.0,"HyperDash":false}]},{"StartTime":9270.0,"Objects":[{"StartTime":9270.0,"Position":200.0,"HyperDash":false}]},{"StartTime":9573.0,"Objects":[{"StartTime":9573.0,"Position":296.0,"HyperDash":false}]},{"StartTime":10180.0,"Objects":[{"StartTime":10180.0,"Position":275.0,"HyperDash":false}]},{"StartTime":10483.0,"Objects":[{"StartTime":10483.0,"Position":179.0,"HyperDash":false}]},{"StartTime":10786.0,"Objects":[{"StartTime":10786.0,"Position":179.0,"HyperDash":false},{"StartTime":10852.0,"Position":218.138611,"HyperDash":false},{"StartTime":10919.0,"Position":248.673264,"HyperDash":false},{"StartTime":10986.0,"Position":257.207916,"HyperDash":false},{"StartTime":11089.0,"Position":299.0,"HyperDash":false}]},{"StartTime":11392.0,"Objects":[{"StartTime":11392.0,"Position":299.0,"HyperDash":false}]},{"StartTime":11695.0,"Objects":[{"StartTime":11695.0,"Position":203.0,"HyperDash":false},{"StartTime":11752.0,"Position":173.351,"HyperDash":false},{"StartTime":11846.0,"Position":143.0,"HyperDash":false}]},{"StartTime":11998.0,"Objects":[{"StartTime":11998.0,"Position":94.0,"HyperDash":false}]},{"StartTime":12301.0,"Objects":[{"StartTime":12301.0,"Position":94.0,"HyperDash":false}]},{"StartTime":12604.0,"Objects":[{"StartTime":12604.0,"Position":189.0,"HyperDash":false}]},{"StartTime":13513.0,"Objects":[{"StartTime":13513.0,"Position":476.0,"HyperDash":false}]},{"StartTime":13816.0,"Objects":[{"StartTime":13816.0,"Position":380.0,"HyperDash":false}]},{"StartTime":14725.0,"Objects":[{"StartTime":14725.0,"Position":272.0,"HyperDash":false},{"StartTime":14782.0,"Position":248.351,"HyperDash":false},{"StartTime":14876.0,"Position":212.0,"HyperDash":false}]},{"StartTime":15028.0,"Objects":[{"StartTime":15028.0,"Position":177.0,"HyperDash":false},{"StartTime":15085.0,"Position":196.0,"HyperDash":false},{"StartTime":15179.0,"Position":177.0,"HyperDash":false}]},{"StartTime":15331.0,"Objects":[{"StartTime":15331.0,"Position":225.0,"HyperDash":false}]},{"StartTime":15483.0,"Objects":[{"StartTime":15483.0,"Position":273.0,"HyperDash":false}]},{"StartTime":15786.0,"Objects":[{"StartTime":15786.0,"Position":273.0,"HyperDash":false}]},{"StartTime":16089.0,"Objects":[{"StartTime":16089.0,"Position":273.0,"HyperDash":false}]},{"StartTime":16846.0,"Objects":[{"StartTime":16846.0,"Position":33.0,"HyperDash":false},{"StartTime":16903.0,"Position":27.0,"HyperDash":false},{"StartTime":16997.0,"Position":33.0,"HyperDash":false}]},{"StartTime":17149.0,"Objects":[{"StartTime":17149.0,"Position":33.0,"HyperDash":false}]},{"StartTime":17755.0,"Objects":[{"StartTime":17755.0,"Position":224.0,"HyperDash":false}]},{"StartTime":18967.0,"Objects":[{"StartTime":18967.0,"Position":277.0,"HyperDash":false}]},{"StartTime":19119.0,"Objects":[{"StartTime":19119.0,"Position":228.0,"HyperDash":false}]},{"StartTime":19270.0,"Objects":[{"StartTime":19270.0,"Position":181.0,"HyperDash":false}]},{"StartTime":19573.0,"Objects":[{"StartTime":19573.0,"Position":181.0,"HyperDash":false}]},{"StartTime":19876.0,"Objects":[{"StartTime":19876.0,"Position":181.0,"HyperDash":false}]},{"StartTime":20786.0,"Objects":[{"StartTime":20786.0,"Position":469.0,"HyperDash":false}]},{"StartTime":21089.0,"Objects":[{"StartTime":21089.0,"Position":373.0,"HyperDash":false}]},{"StartTime":21392.0,"Objects":[{"StartTime":21392.0,"Position":277.0,"HyperDash":false}]},{"StartTime":21998.0,"Objects":[{"StartTime":21998.0,"Position":243.0,"HyperDash":false}]},{"StartTime":22149.0,"Objects":[{"StartTime":22149.0,"Position":243.0,"HyperDash":false}]},{"StartTime":22301.0,"Objects":[{"StartTime":22301.0,"Position":243.0,"HyperDash":false}]},{"StartTime":22452.0,"Objects":[{"StartTime":22452.0,"Position":290.0,"HyperDash":false},{"StartTime":22509.0,"Position":295.0,"HyperDash":false},{"StartTime":22603.0,"Position":290.0,"HyperDash":false}]},{"StartTime":22755.0,"Objects":[{"StartTime":22755.0,"Position":290.0,"HyperDash":false}]},{"StartTime":23058.0,"Objects":[{"StartTime":23058.0,"Position":385.0,"HyperDash":false}]},{"StartTime":23361.0,"Objects":[{"StartTime":23361.0,"Position":385.0,"HyperDash":false}]},{"StartTime":24119.0,"Objects":[{"StartTime":24119.0,"Position":213.0,"HyperDash":false},{"StartTime":24176.0,"Position":203.351,"HyperDash":false},{"StartTime":24270.0,"Position":153.0,"HyperDash":false}]},{"StartTime":24422.0,"Objects":[{"StartTime":24422.0,"Position":104.0,"HyperDash":false}]},{"StartTime":25028.0,"Objects":[{"StartTime":25028.0,"Position":295.0,"HyperDash":false}]},{"StartTime":26240.0,"Objects":[{"StartTime":26240.0,"Position":56.0,"HyperDash":false}]},{"StartTime":26392.0,"Objects":[{"StartTime":26392.0,"Position":56.0,"HyperDash":false}]},{"StartTime":26543.0,"Objects":[{"StartTime":26543.0,"Position":56.0,"HyperDash":false}]},{"StartTime":26846.0,"Objects":[{"StartTime":26846.0,"Position":56.0,"HyperDash":false}]},{"StartTime":27149.0,"Objects":[{"StartTime":27149.0,"Position":151.0,"HyperDash":false}]},{"StartTime":28058.0,"Objects":[{"StartTime":28058.0,"Position":438.0,"HyperDash":false},{"StartTime":28124.0,"Position":455.0,"HyperDash":false},{"StartTime":28191.0,"Position":455.0,"HyperDash":false},{"StartTime":28258.0,"Position":453.0,"HyperDash":false},{"StartTime":28361.0,"Position":438.0,"HyperDash":false}]},{"StartTime":29270.0,"Objects":[{"StartTime":29270.0,"Position":184.0,"HyperDash":false},{"StartTime":29336.0,"Position":227.138611,"HyperDash":false},{"StartTime":29403.0,"Position":245.673264,"HyperDash":false},{"StartTime":29470.0,"Position":246.207916,"HyperDash":false},{"StartTime":29573.0,"Position":304.0,"HyperDash":false}]},{"StartTime":29876.0,"Objects":[{"StartTime":29876.0,"Position":399.0,"HyperDash":false}]},{"StartTime":30180.0,"Objects":[{"StartTime":30180.0,"Position":399.0,"HyperDash":false}]},{"StartTime":30483.0,"Objects":[{"StartTime":30483.0,"Position":303.0,"HyperDash":false},{"StartTime":30549.0,"Position":281.8614,"HyperDash":false},{"StartTime":30616.0,"Position":238.326736,"HyperDash":false},{"StartTime":30683.0,"Position":208.792084,"HyperDash":false},{"StartTime":30786.0,"Position":183.0,"HyperDash":false}]},{"StartTime":31089.0,"Objects":[{"StartTime":31089.0,"Position":115.0,"HyperDash":false},{"StartTime":31155.0,"Position":159.138611,"HyperDash":false},{"StartTime":31222.0,"Position":159.673264,"HyperDash":false},{"StartTime":31289.0,"Position":210.207916,"HyperDash":false},{"StartTime":31392.0,"Position":235.0,"HyperDash":false}]},{"StartTime":31695.0,"Objects":[{"StartTime":31695.0,"Position":330.0,"HyperDash":false}]},{"StartTime":31998.0,"Objects":[{"StartTime":31998.0,"Position":425.0,"HyperDash":false}]},{"StartTime":32301.0,"Objects":[{"StartTime":32301.0,"Position":425.0,"HyperDash":false},{"StartTime":32367.0,"Position":401.8614,"HyperDash":false},{"StartTime":32434.0,"Position":362.326721,"HyperDash":false},{"StartTime":32501.0,"Position":346.792084,"HyperDash":false},{"StartTime":32604.0,"Position":305.0,"HyperDash":false}]},{"StartTime":32907.0,"Objects":[{"StartTime":32907.0,"Position":209.0,"HyperDash":false},{"StartTime":32973.0,"Position":172.861389,"HyperDash":false},{"StartTime":33040.0,"Position":156.326736,"HyperDash":false},{"StartTime":33107.0,"Position":111.792084,"HyperDash":false},{"StartTime":33210.0,"Position":89.0,"HyperDash":false}]},{"StartTime":33513.0,"Objects":[{"StartTime":33513.0,"Position":89.0,"HyperDash":false}]},{"StartTime":33816.0,"Objects":[{"StartTime":33816.0,"Position":184.0,"HyperDash":false}]},{"StartTime":34119.0,"Objects":[{"StartTime":34119.0,"Position":279.0,"HyperDash":false}]},{"StartTime":34422.0,"Objects":[{"StartTime":34422.0,"Position":374.0,"HyperDash":false}]},{"StartTime":34725.0,"Objects":[{"StartTime":34725.0,"Position":469.0,"HyperDash":false},{"StartTime":34791.0,"Position":453.0,"HyperDash":false},{"StartTime":34858.0,"Position":477.0,"HyperDash":false},{"StartTime":34925.0,"Position":456.0,"HyperDash":false},{"StartTime":35028.0,"Position":469.0,"HyperDash":false}]},{"StartTime":35331.0,"Objects":[{"StartTime":35331.0,"Position":373.0,"HyperDash":false},{"StartTime":35397.0,"Position":326.8614,"HyperDash":false},{"StartTime":35464.0,"Position":315.326721,"HyperDash":false},{"StartTime":35531.0,"Position":282.792084,"HyperDash":false},{"StartTime":35634.0,"Position":253.0,"HyperDash":false}]},{"StartTime":35937.0,"Objects":[{"StartTime":35937.0,"Position":157.0,"HyperDash":false}]},{"StartTime":36240.0,"Objects":[{"StartTime":36240.0,"Position":157.0,"HyperDash":false}]},{"StartTime":36392.0,"Objects":[{"StartTime":36392.0,"Position":157.0,"HyperDash":false}]},{"StartTime":36543.0,"Objects":[{"StartTime":36543.0,"Position":204.0,"HyperDash":false},{"StartTime":36618.0,"Position":241.702972,"HyperDash":false},{"StartTime":36694.0,"Position":264.0,"HyperDash":false},{"StartTime":36752.0,"Position":239.227722,"HyperDash":false},{"StartTime":36846.0,"Position":204.0,"HyperDash":false}]},{"StartTime":36998.0,"Objects":[{"StartTime":36998.0,"Position":204.0,"HyperDash":false},{"StartTime":37055.0,"Position":221.0,"HyperDash":false},{"StartTime":37149.0,"Position":204.0,"HyperDash":false}]},{"StartTime":37301.0,"Objects":[{"StartTime":37301.0,"Position":205.0,"HyperDash":false}]},{"StartTime":37604.0,"Objects":[{"StartTime":37604.0,"Position":300.0,"HyperDash":false}]},{"StartTime":37907.0,"Objects":[{"StartTime":37907.0,"Position":300.0,"HyperDash":false}]},{"StartTime":38967.0,"Objects":[{"StartTime":38967.0,"Position":32.0,"HyperDash":false}]},{"StartTime":39573.0,"Objects":[{"StartTime":39573.0,"Position":32.0,"HyperDash":false}]},{"StartTime":40786.0,"Objects":[{"StartTime":40786.0,"Position":416.0,"HyperDash":false}]},{"StartTime":40937.0,"Objects":[{"StartTime":40937.0,"Position":416.0,"HyperDash":false}]},{"StartTime":41089.0,"Objects":[{"StartTime":41089.0,"Position":416.0,"HyperDash":false}]},{"StartTime":41392.0,"Objects":[{"StartTime":41392.0,"Position":320.0,"HyperDash":false}]},{"StartTime":41695.0,"Objects":[{"StartTime":41695.0,"Position":320.0,"HyperDash":false}]},{"StartTime":42604.0,"Objects":[{"StartTime":42604.0,"Position":48.0,"HyperDash":false},{"StartTime":42670.0,"Position":57.13861,"HyperDash":false},{"StartTime":42737.0,"Position":105.673264,"HyperDash":false},{"StartTime":42804.0,"Position":146.207916,"HyperDash":false},{"StartTime":42907.0,"Position":168.0,"HyperDash":false}]},{"StartTime":43210.0,"Objects":[{"StartTime":43210.0,"Position":263.0,"HyperDash":false}]},{"StartTime":43816.0,"Objects":[{"StartTime":43816.0,"Position":376.0,"HyperDash":false},{"StartTime":43891.0,"Position":326.594055,"HyperDash":false},{"StartTime":43967.0,"Position":256.396027,"HyperDash":false},{"StartTime":44043.0,"Position":200.198029,"HyperDash":false},{"StartTime":44119.0,"Position":136.0,"HyperDash":false},{"StartTime":44194.0,"Position":202.405945,"HyperDash":false},{"StartTime":44270.0,"Position":255.603943,"HyperDash":false},{"StartTime":44346.0,"Position":300.802,"HyperDash":false},{"StartTime":44422.0,"Position":376.0,"HyperDash":false},{"StartTime":44497.0,"Position":313.594055,"HyperDash":false},{"StartTime":44573.0,"Position":256.396027,"HyperDash":false},{"StartTime":44649.0,"Position":201.198044,"HyperDash":false},{"StartTime":44725.0,"Position":136.0,"HyperDash":false},{"StartTime":44800.0,"Position":204.405945,"HyperDash":false},{"StartTime":44876.0,"Position":255.603973,"HyperDash":false},{"StartTime":44952.0,"Position":305.801971,"HyperDash":false},{"StartTime":45028.0,"Position":376.0,"HyperDash":false},{"StartTime":45103.0,"Position":306.594055,"HyperDash":false},{"StartTime":45179.0,"Position":256.3961,"HyperDash":false},{"StartTime":45237.0,"Position":224.45549,"HyperDash":false},{"StartTime":45331.0,"Position":136.0,"HyperDash":false}]},{"StartTime":45634.0,"Objects":[{"StartTime":45634.0,"Position":376.0,"HyperDash":false},{"StartTime":45709.0,"Position":323.594055,"HyperDash":false},{"StartTime":45785.0,"Position":256.396027,"HyperDash":false},{"StartTime":45861.0,"Position":198.198029,"HyperDash":false},{"StartTime":45937.0,"Position":136.0,"HyperDash":false},{"StartTime":46012.0,"Position":176.405945,"HyperDash":false},{"StartTime":46088.0,"Position":255.603943,"HyperDash":false},{"StartTime":46164.0,"Position":318.802,"HyperDash":false},{"StartTime":46240.0,"Position":376.0,"HyperDash":false},{"StartTime":46315.0,"Position":324.594055,"HyperDash":false},{"StartTime":46391.0,"Position":256.396027,"HyperDash":false},{"StartTime":46467.0,"Position":199.198044,"HyperDash":false},{"StartTime":46543.0,"Position":136.0,"HyperDash":false},{"StartTime":46618.0,"Position":193.405945,"HyperDash":false},{"StartTime":46694.0,"Position":255.603973,"HyperDash":false},{"StartTime":46770.0,"Position":298.801971,"HyperDash":false},{"StartTime":46846.0,"Position":376.0,"HyperDash":false},{"StartTime":46921.0,"Position":327.594055,"HyperDash":false},{"StartTime":46997.0,"Position":256.3961,"HyperDash":false},{"StartTime":47055.0,"Position":217.45549,"HyperDash":false},{"StartTime":47149.0,"Position":136.0,"HyperDash":false}]},{"StartTime":47452.0,"Objects":[{"StartTime":47452.0,"Position":376.0,"HyperDash":false},{"StartTime":47527.0,"Position":327.594055,"HyperDash":false},{"StartTime":47603.0,"Position":256.396027,"HyperDash":false},{"StartTime":47679.0,"Position":189.198029,"HyperDash":false},{"StartTime":47755.0,"Position":136.0,"HyperDash":false},{"StartTime":47830.0,"Position":195.405945,"HyperDash":false},{"StartTime":47906.0,"Position":255.603943,"HyperDash":false},{"StartTime":47982.0,"Position":300.802,"HyperDash":false},{"StartTime":48058.0,"Position":376.0,"HyperDash":false},{"StartTime":48133.0,"Position":324.594055,"HyperDash":false},{"StartTime":48209.0,"Position":256.396027,"HyperDash":false},{"StartTime":48285.0,"Position":188.198044,"HyperDash":false},{"StartTime":48361.0,"Position":136.0,"HyperDash":false},{"StartTime":48436.0,"Position":199.405945,"HyperDash":false},{"StartTime":48512.0,"Position":255.603973,"HyperDash":false},{"StartTime":48588.0,"Position":310.801971,"HyperDash":false},{"StartTime":48664.0,"Position":376.0,"HyperDash":false},{"StartTime":48739.0,"Position":317.594055,"HyperDash":false},{"StartTime":48815.0,"Position":256.3961,"HyperDash":false},{"StartTime":48873.0,"Position":213.45549,"HyperDash":false},{"StartTime":48967.0,"Position":136.0,"HyperDash":false}]},{"StartTime":49270.0,"Objects":[{"StartTime":49270.0,"Position":376.0,"HyperDash":false},{"StartTime":49345.0,"Position":335.594482,"HyperDash":false},{"StartTime":49421.0,"Position":256.396881,"HyperDash":false},{"StartTime":49479.0,"Position":210.456619,"HyperDash":false},{"StartTime":49573.0,"Position":136.001678,"HyperDash":false}]},{"StartTime":49876.0,"Objects":[{"StartTime":49876.0,"Position":136.0,"HyperDash":false}]},{"StartTime":50180.0,"Objects":[{"StartTime":50180.0,"Position":328.0,"HyperDash":false}]},{"StartTime":50483.0,"Objects":[{"StartTime":50483.0,"Position":329.0,"HyperDash":false}]},{"StartTime":50786.0,"Objects":[{"StartTime":50786.0,"Position":136.0,"HyperDash":false}]},{"StartTime":50937.0,"Objects":[{"StartTime":50937.0,"Position":138.0,"HyperDash":false}]},{"StartTime":51089.0,"Objects":[{"StartTime":51089.0,"Position":138.0,"HyperDash":false},{"StartTime":51146.0,"Position":142.649,"HyperDash":false},{"StartTime":51240.0,"Position":198.0,"HyperDash":false}]},{"StartTime":51392.0,"Objects":[{"StartTime":51392.0,"Position":198.0,"HyperDash":false}]},{"StartTime":51543.0,"Objects":[{"StartTime":51543.0,"Position":246.0,"HyperDash":false}]},{"StartTime":51695.0,"Objects":[{"StartTime":51695.0,"Position":295.0,"HyperDash":false},{"StartTime":51752.0,"Position":303.649017,"HyperDash":false},{"StartTime":51846.0,"Position":355.0,"HyperDash":false}]},{"StartTime":52149.0,"Objects":[{"StartTime":52149.0,"Position":355.0,"HyperDash":false}]},{"StartTime":52452.0,"Objects":[{"StartTime":52452.0,"Position":260.0,"HyperDash":false}]},{"StartTime":53513.0,"Objects":[{"StartTime":53513.0,"Position":40.0,"HyperDash":false},{"StartTime":53588.0,"Position":68.70297,"HyperDash":false},{"StartTime":53664.0,"Position":116.801987,"HyperDash":false},{"StartTime":53740.0,"Position":141.900986,"HyperDash":false},{"StartTime":53816.0,"Position":160.0,"HyperDash":false},{"StartTime":53882.0,"Position":184.138626,"HyperDash":false},{"StartTime":53949.0,"Position":229.673264,"HyperDash":false},{"StartTime":54016.0,"Position":230.207916,"HyperDash":false},{"StartTime":54119.0,"Position":280.0,"HyperDash":false}]},{"StartTime":55331.0,"Objects":[{"StartTime":55331.0,"Position":40.0,"HyperDash":false},{"StartTime":55406.0,"Position":84.70297,"HyperDash":false},{"StartTime":55482.0,"Position":100.0,"HyperDash":false},{"StartTime":55540.0,"Position":64.22772,"HyperDash":false},{"StartTime":55634.0,"Position":40.0,"HyperDash":false}]},{"StartTime":55937.0,"Objects":[{"StartTime":55937.0,"Position":40.0,"HyperDash":false},{"StartTime":56003.0,"Position":29.0,"HyperDash":false},{"StartTime":56070.0,"Position":44.0,"HyperDash":false},{"StartTime":56137.0,"Position":28.0,"HyperDash":false},{"StartTime":56240.0,"Position":40.0,"HyperDash":false}]},{"StartTime":57149.0,"Objects":[{"StartTime":57149.0,"Position":300.0,"HyperDash":false},{"StartTime":57215.0,"Position":316.0,"HyperDash":false},{"StartTime":57282.0,"Position":288.0,"HyperDash":false},{"StartTime":57349.0,"Position":299.0,"HyperDash":false},{"StartTime":57452.0,"Position":300.0,"HyperDash":false}]},{"StartTime":58361.0,"Objects":[{"StartTime":58361.0,"Position":256.0,"HyperDash":false},{"StartTime":58418.0,"Position":265.649017,"HyperDash":false},{"StartTime":58512.0,"Position":316.0,"HyperDash":false}]},{"StartTime":58664.0,"Objects":[{"StartTime":58664.0,"Position":364.0,"HyperDash":false},{"StartTime":58721.0,"Position":358.0,"HyperDash":false},{"StartTime":58815.0,"Position":364.0,"HyperDash":false}]},{"StartTime":58967.0,"Objects":[{"StartTime":58967.0,"Position":329.0,"HyperDash":false}]},{"StartTime":59119.0,"Objects":[{"StartTime":59119.0,"Position":280.0,"HyperDash":false},{"StartTime":59176.0,"Position":249.350983,"HyperDash":false},{"StartTime":59270.0,"Position":220.0,"HyperDash":false}]},{"StartTime":59422.0,"Objects":[{"StartTime":59422.0,"Position":185.0,"HyperDash":false},{"StartTime":59479.0,"Position":176.0,"HyperDash":false},{"StartTime":59573.0,"Position":185.0,"HyperDash":false}]},{"StartTime":59876.0,"Objects":[{"StartTime":59876.0,"Position":185.0,"HyperDash":false}]},{"StartTime":60180.0,"Objects":[{"StartTime":60180.0,"Position":253.0,"HyperDash":false}]},{"StartTime":60331.0,"Objects":[{"StartTime":60331.0,"Position":253.0,"HyperDash":false}]},{"StartTime":60483.0,"Objects":[{"StartTime":60483.0,"Position":253.0,"HyperDash":false}]},{"StartTime":60634.0,"Objects":[{"StartTime":60634.0,"Position":253.0,"HyperDash":false}]},{"StartTime":60786.0,"Objects":[{"StartTime":60786.0,"Position":253.0,"HyperDash":false},{"StartTime":60861.0,"Position":218.297028,"HyperDash":false},{"StartTime":60937.0,"Position":193.0,"HyperDash":false},{"StartTime":61013.0,"Position":217.900986,"HyperDash":false},{"StartTime":61089.0,"Position":253.0,"HyperDash":false},{"StartTime":61164.0,"Position":237.297028,"HyperDash":false},{"StartTime":61240.0,"Position":193.0,"HyperDash":false},{"StartTime":61298.0,"Position":218.772278,"HyperDash":false},{"StartTime":61392.0,"Position":253.0,"HyperDash":false}]},{"StartTime":61695.0,"Objects":[{"StartTime":61695.0,"Position":253.0,"HyperDash":false}]},{"StartTime":61998.0,"Objects":[{"StartTime":61998.0,"Position":348.0,"HyperDash":false},{"StartTime":62073.0,"Position":336.0,"HyperDash":false},{"StartTime":62149.0,"Position":348.0,"HyperDash":false},{"StartTime":62225.0,"Position":336.0,"HyperDash":false},{"StartTime":62301.0,"Position":348.0,"HyperDash":false},{"StartTime":62376.0,"Position":333.0,"HyperDash":false},{"StartTime":62452.0,"Position":348.0,"HyperDash":false},{"StartTime":62510.0,"Position":344.0,"HyperDash":false},{"StartTime":62604.0,"Position":348.0,"HyperDash":false}]},{"StartTime":62755.0,"Objects":[{"StartTime":62755.0,"Position":348.0,"HyperDash":false}]},{"StartTime":62907.0,"Objects":[{"StartTime":62907.0,"Position":348.0,"HyperDash":false}]},{"StartTime":63058.0,"Objects":[{"StartTime":63058.0,"Position":348.0,"HyperDash":false}]},{"StartTime":63210.0,"Objects":[{"StartTime":63210.0,"Position":348.0,"HyperDash":false}]},{"StartTime":63513.0,"Objects":[{"StartTime":63513.0,"Position":252.0,"HyperDash":false}]},{"StartTime":63816.0,"Objects":[{"StartTime":63816.0,"Position":252.0,"HyperDash":false}]},{"StartTime":63967.0,"Objects":[{"StartTime":63967.0,"Position":252.0,"HyperDash":false},{"StartTime":64042.0,"Position":225.264313,"HyperDash":false},{"StartTime":64118.0,"Position":192.0,"HyperDash":false},{"StartTime":64194.0,"Position":203.0,"HyperDash":false},{"StartTime":64270.0,"Position":252.0,"HyperDash":false},{"StartTime":64327.0,"Position":229.268738,"HyperDash":false},{"StartTime":64421.0,"Position":192.0,"HyperDash":false}]},{"StartTime":64725.0,"Objects":[{"StartTime":64725.0,"Position":288.0,"HyperDash":false}]},{"StartTime":65028.0,"Objects":[{"StartTime":65028.0,"Position":383.0,"HyperDash":false}]},{"StartTime":65331.0,"Objects":[{"StartTime":65331.0,"Position":383.0,"HyperDash":false}]},{"StartTime":65634.0,"Objects":[{"StartTime":65634.0,"Position":287.0,"HyperDash":false},{"StartTime":65691.0,"Position":277.350983,"HyperDash":false},{"StartTime":65785.0,"Position":227.0,"HyperDash":false}]},{"StartTime":65937.0,"Objects":[{"StartTime":65937.0,"Position":178.0,"HyperDash":false}]},{"StartTime":66089.0,"Objects":[{"StartTime":66089.0,"Position":129.0,"HyperDash":false}]},{"StartTime":66240.0,"Objects":[{"StartTime":66240.0,"Position":81.0,"HyperDash":false}]},{"StartTime":66392.0,"Objects":[{"StartTime":66392.0,"Position":81.0,"HyperDash":false}]},{"StartTime":66543.0,"Objects":[{"StartTime":66543.0,"Position":81.0,"HyperDash":false},{"StartTime":66600.0,"Position":100.64901,"HyperDash":false},{"StartTime":66694.0,"Position":141.0,"HyperDash":false}]},{"StartTime":66846.0,"Objects":[{"StartTime":66846.0,"Position":189.0,"HyperDash":false},{"StartTime":66903.0,"Position":227.649,"HyperDash":false},{"StartTime":66997.0,"Position":249.0,"HyperDash":false}]},{"StartTime":67755.0,"Objects":[{"StartTime":67755.0,"Position":192.0,"HyperDash":false},{"StartTime":67812.0,"Position":205.649,"HyperDash":false},{"StartTime":67906.0,"Position":252.0,"HyperDash":false}]},{"StartTime":68058.0,"Objects":[{"StartTime":68058.0,"Position":300.0,"HyperDash":false}]},{"StartTime":68664.0,"Objects":[{"StartTime":68664.0,"Position":300.0,"HyperDash":false}]},{"StartTime":68967.0,"Objects":[{"StartTime":68967.0,"Position":300.0,"HyperDash":false}]},{"StartTime":69270.0,"Objects":[{"StartTime":69270.0,"Position":204.0,"HyperDash":false}]},{"StartTime":69876.0,"Objects":[{"StartTime":69876.0,"Position":395.0,"HyperDash":false},{"StartTime":69933.0,"Position":384.272858,"HyperDash":false},{"StartTime":70027.0,"Position":395.722839,"HyperDash":false}]},{"StartTime":70180.0,"Objects":[{"StartTime":70180.0,"Position":395.0,"HyperDash":false}]},{"StartTime":70483.0,"Objects":[{"StartTime":70483.0,"Position":296.0,"HyperDash":false}]},{"StartTime":70786.0,"Objects":[{"StartTime":70786.0,"Position":200.0,"HyperDash":false}]},{"StartTime":71695.0,"Objects":[{"StartTime":71695.0,"Position":200.0,"HyperDash":false}]},{"StartTime":71998.0,"Objects":[{"StartTime":71998.0,"Position":295.0,"HyperDash":false}]},{"StartTime":72907.0,"Objects":[{"StartTime":72907.0,"Position":91.0,"HyperDash":false}]},{"StartTime":73058.0,"Objects":[{"StartTime":73058.0,"Position":138.0,"HyperDash":false}]},{"StartTime":73210.0,"Objects":[{"StartTime":73210.0,"Position":186.0,"HyperDash":false}]},{"StartTime":73361.0,"Objects":[{"StartTime":73361.0,"Position":186.0,"HyperDash":false},{"StartTime":73418.0,"Position":194.649,"HyperDash":false},{"StartTime":73512.0,"Position":246.0,"HyperDash":false}]},{"StartTime":73664.0,"Objects":[{"StartTime":73664.0,"Position":294.0,"HyperDash":false},{"StartTime":73721.0,"Position":334.649017,"HyperDash":false},{"StartTime":73815.0,"Position":354.0,"HyperDash":false}]},{"StartTime":73967.0,"Objects":[{"StartTime":73967.0,"Position":354.0,"HyperDash":false}]},{"StartTime":74270.0,"Objects":[{"StartTime":74270.0,"Position":354.0,"HyperDash":false}]},{"StartTime":75331.0,"Objects":[{"StartTime":75331.0,"Position":40.0,"HyperDash":false}]},{"StartTime":75937.0,"Objects":[{"StartTime":75937.0,"Position":160.0,"HyperDash":false}]},{"StartTime":76089.0,"Objects":[{"StartTime":76089.0,"Position":160.0,"HyperDash":false}]},{"StartTime":76543.0,"Objects":[{"StartTime":76543.0,"Position":303.0,"HyperDash":false}]},{"StartTime":77149.0,"Objects":[{"StartTime":77149.0,"Position":160.0,"HyperDash":false},{"StartTime":77206.0,"Position":192.649,"HyperDash":false},{"StartTime":77300.0,"Position":220.0,"HyperDash":false}]},{"StartTime":77452.0,"Objects":[{"StartTime":77452.0,"Position":268.0,"HyperDash":false}]},{"StartTime":77755.0,"Objects":[{"StartTime":77755.0,"Position":268.0,"HyperDash":false}]},{"StartTime":78058.0,"Objects":[{"StartTime":78058.0,"Position":268.0,"HyperDash":false}]},{"StartTime":78361.0,"Objects":[{"StartTime":78361.0,"Position":363.0,"HyperDash":false},{"StartTime":78418.0,"Position":382.0,"HyperDash":false},{"StartTime":78512.0,"Position":363.0,"HyperDash":false}]},{"StartTime":78967.0,"Objects":[{"StartTime":78967.0,"Position":363.0,"HyperDash":false}]},{"StartTime":79270.0,"Objects":[{"StartTime":79270.0,"Position":267.0,"HyperDash":false},{"StartTime":79336.0,"Position":223.861389,"HyperDash":false},{"StartTime":79403.0,"Position":208.326736,"HyperDash":false},{"StartTime":79470.0,"Position":193.792084,"HyperDash":false},{"StartTime":79573.0,"Position":147.0,"HyperDash":false}]},{"StartTime":80180.0,"Objects":[{"StartTime":80180.0,"Position":96.0,"HyperDash":false},{"StartTime":80255.0,"Position":108.0,"HyperDash":false},{"StartTime":80331.0,"Position":83.0,"HyperDash":false},{"StartTime":80407.0,"Position":82.0,"HyperDash":false},{"StartTime":80483.0,"Position":96.0,"HyperDash":false},{"StartTime":80558.0,"Position":99.0,"HyperDash":false},{"StartTime":80634.0,"Position":82.0,"HyperDash":false},{"StartTime":80710.0,"Position":102.0,"HyperDash":false},{"StartTime":80786.0,"Position":96.0,"HyperDash":false},{"StartTime":80861.0,"Position":86.0,"HyperDash":false},{"StartTime":80937.0,"Position":89.0,"HyperDash":false},{"StartTime":81013.0,"Position":123.90097,"HyperDash":false},{"StartTime":81089.0,"Position":144.0,"HyperDash":false},{"StartTime":81155.0,"Position":177.138611,"HyperDash":false},{"StartTime":81222.0,"Position":194.673279,"HyperDash":false},{"StartTime":81289.0,"Position":229.207916,"HyperDash":false},{"StartTime":81392.0,"Position":264.0,"HyperDash":false}]},{"StartTime":81695.0,"Objects":[{"StartTime":81695.0,"Position":360.0,"HyperDash":false}]},{"StartTime":81998.0,"Objects":[{"StartTime":81998.0,"Position":455.0,"HyperDash":false},{"StartTime":82073.0,"Position":449.0,"HyperDash":false},{"StartTime":82149.0,"Position":448.0,"HyperDash":false},{"StartTime":82225.0,"Position":474.0,"HyperDash":false},{"StartTime":82301.0,"Position":455.0,"HyperDash":false},{"StartTime":82376.0,"Position":455.0,"HyperDash":false},{"StartTime":82452.0,"Position":470.0,"HyperDash":false},{"StartTime":82528.0,"Position":439.0,"HyperDash":false},{"StartTime":82604.0,"Position":455.0,"HyperDash":false},{"StartTime":82679.0,"Position":458.0,"HyperDash":false},{"StartTime":82755.0,"Position":451.0,"HyperDash":false},{"StartTime":82831.0,"Position":445.09903,"HyperDash":false},{"StartTime":82907.0,"Position":407.0,"HyperDash":false},{"StartTime":82982.0,"Position":392.297028,"HyperDash":false},{"StartTime":83058.0,"Position":335.198029,"HyperDash":false},{"StartTime":83134.0,"Position":301.09903,"HyperDash":false},{"StartTime":83210.0,"Position":287.0,"HyperDash":false},{"StartTime":83276.0,"Position":242.861389,"HyperDash":false},{"StartTime":83343.0,"Position":230.326721,"HyperDash":false},{"StartTime":83410.0,"Position":212.792053,"HyperDash":false},{"StartTime":83513.0,"Position":167.0,"HyperDash":false}]},{"StartTime":83816.0,"Objects":[{"StartTime":83816.0,"Position":124.0,"HyperDash":false},{"StartTime":83891.0,"Position":154.702972,"HyperDash":false},{"StartTime":83967.0,"Position":181.801987,"HyperDash":false},{"StartTime":84043.0,"Position":229.900986,"HyperDash":false},{"StartTime":84119.0,"Position":244.0,"HyperDash":false},{"StartTime":84194.0,"Position":287.702972,"HyperDash":false},{"StartTime":84270.0,"Position":290.801971,"HyperDash":false},{"StartTime":84346.0,"Position":332.901,"HyperDash":false},{"StartTime":84422.0,"Position":364.0,"HyperDash":false},{"StartTime":84497.0,"Position":367.0,"HyperDash":false},{"StartTime":84573.0,"Position":374.0,"HyperDash":false},{"StartTime":84649.0,"Position":360.0,"HyperDash":false},{"StartTime":84725.0,"Position":364.0,"HyperDash":false},{"StartTime":84791.0,"Position":368.0,"HyperDash":false},{"StartTime":84858.0,"Position":369.0,"HyperDash":false},{"StartTime":84925.0,"Position":364.0,"HyperDash":false},{"StartTime":85028.0,"Position":364.0,"HyperDash":false}]},{"StartTime":85331.0,"Objects":[{"StartTime":85331.0,"Position":268.0,"HyperDash":false}]},{"StartTime":85634.0,"Objects":[{"StartTime":85634.0,"Position":172.0,"HyperDash":false},{"StartTime":85709.0,"Position":124.288116,"HyperDash":false},{"StartTime":85785.0,"Position":93.18007,"HyperDash":false},{"StartTime":85861.0,"Position":71.07203,"HyperDash":false},{"StartTime":85937.0,"Position":52.0,"HyperDash":false},{"StartTime":86012.0,"Position":45.0,"HyperDash":false},{"StartTime":86088.0,"Position":68.0,"HyperDash":false},{"StartTime":86164.0,"Position":66.0,"HyperDash":false},{"StartTime":86240.0,"Position":52.0,"HyperDash":false},{"StartTime":86315.0,"Position":33.0,"HyperDash":false},{"StartTime":86391.0,"Position":34.0,"HyperDash":false},{"StartTime":86467.0,"Position":66.0,"HyperDash":false},{"StartTime":86543.0,"Position":76.10803,"HyperDash":false},{"StartTime":86618.0,"Position":109.819916,"HyperDash":false},{"StartTime":86694.0,"Position":132.927948,"HyperDash":false},{"StartTime":86770.0,"Position":153.036011,"HyperDash":false},{"StartTime":86846.0,"Position":196.144073,"HyperDash":false},{"StartTime":86921.0,"Position":235.855927,"HyperDash":false},{"StartTime":86997.0,"Position":237.963989,"HyperDash":false},{"StartTime":87073.0,"Position":282.072021,"HyperDash":false},{"StartTime":87149.0,"Position":316.0,"HyperDash":false},{"StartTime":87206.0,"Position":327.0,"HyperDash":false},{"StartTime":87300.0,"Position":316.0,"HyperDash":false}]},{"StartTime":87452.0,"Objects":[{"StartTime":87452.0,"Position":316.0,"HyperDash":false},{"StartTime":87518.0,"Position":297.0,"HyperDash":false},{"StartTime":87585.0,"Position":333.0,"HyperDash":false},{"StartTime":87652.0,"Position":325.0,"HyperDash":false},{"StartTime":87755.0,"Position":316.0,"HyperDash":false}]},{"StartTime":88058.0,"Objects":[{"StartTime":88058.0,"Position":411.0,"HyperDash":false},{"StartTime":88133.0,"Position":411.0,"HyperDash":false},{"StartTime":88209.0,"Position":410.0,"HyperDash":false},{"StartTime":88285.0,"Position":423.0,"HyperDash":false},{"StartTime":88361.0,"Position":411.0,"HyperDash":false},{"StartTime":88436.0,"Position":412.0,"HyperDash":false},{"StartTime":88512.0,"Position":398.0,"HyperDash":false},{"StartTime":88588.0,"Position":414.0,"HyperDash":false},{"StartTime":88664.0,"Position":411.0,"HyperDash":false},{"StartTime":88739.0,"Position":382.297028,"HyperDash":false},{"StartTime":88815.0,"Position":340.198,"HyperDash":false},{"StartTime":88891.0,"Position":331.09903,"HyperDash":false},{"StartTime":88967.0,"Position":299.0,"HyperDash":false},{"StartTime":89033.0,"Position":253.861389,"HyperDash":false},{"StartTime":89100.0,"Position":231.326721,"HyperDash":false},{"StartTime":89167.0,"Position":225.792084,"HyperDash":false},{"StartTime":89270.0,"Position":179.0,"HyperDash":false}]},{"StartTime":89876.0,"Objects":[{"StartTime":89876.0,"Position":176.0,"HyperDash":false},{"StartTime":89951.0,"Position":144.297028,"HyperDash":false},{"StartTime":90027.0,"Position":110.198013,"HyperDash":false},{"StartTime":90103.0,"Position":73.0990143,"HyperDash":false},{"StartTime":90179.0,"Position":56.0,"HyperDash":false},{"StartTime":90245.0,"Position":34.0,"HyperDash":false},{"StartTime":90312.0,"Position":29.0,"HyperDash":false},{"StartTime":90379.0,"Position":40.0,"HyperDash":false},{"StartTime":90482.0,"Position":40.0,"HyperDash":false}]},{"StartTime":91089.0,"Objects":[{"StartTime":91089.0,"Position":232.0,"HyperDash":false}]},{"StartTime":91695.0,"Objects":[{"StartTime":91695.0,"Position":423.0,"HyperDash":false},{"StartTime":91770.0,"Position":409.0,"HyperDash":false},{"StartTime":91846.0,"Position":424.0,"HyperDash":false},{"StartTime":91922.0,"Position":438.0,"HyperDash":false},{"StartTime":91998.0,"Position":423.0,"HyperDash":false},{"StartTime":92073.0,"Position":417.0,"HyperDash":false},{"StartTime":92149.0,"Position":420.198029,"HyperDash":false},{"StartTime":92225.0,"Position":354.099,"HyperDash":false},{"StartTime":92301.0,"Position":343.0,"HyperDash":false},{"StartTime":92376.0,"Position":331.297028,"HyperDash":false},{"StartTime":92452.0,"Position":266.198,"HyperDash":false},{"StartTime":92528.0,"Position":237.09903,"HyperDash":false},{"StartTime":92604.0,"Position":223.0,"HyperDash":false},{"StartTime":92670.0,"Position":208.861389,"HyperDash":false},{"StartTime":92737.0,"Position":185.326721,"HyperDash":false},{"StartTime":92804.0,"Position":135.792084,"HyperDash":false},{"StartTime":92907.0,"Position":103.0,"HyperDash":false}]},{"StartTime":93513.0,"Objects":[{"StartTime":93513.0,"Position":112.0,"HyperDash":false}]},{"StartTime":94119.0,"Objects":[{"StartTime":94119.0,"Position":303.0,"HyperDash":false}]},{"StartTime":94725.0,"Objects":[{"StartTime":94725.0,"Position":440.0,"HyperDash":false},{"StartTime":94800.0,"Position":426.0,"HyperDash":false},{"StartTime":94876.0,"Position":436.0,"HyperDash":false},{"StartTime":94952.0,"Position":453.0,"HyperDash":false},{"StartTime":95028.0,"Position":440.0,"HyperDash":false},{"StartTime":95103.0,"Position":440.0,"HyperDash":false},{"StartTime":95179.0,"Position":433.0,"HyperDash":false},{"StartTime":95255.0,"Position":456.0,"HyperDash":false},{"StartTime":95331.0,"Position":440.0,"HyperDash":false},{"StartTime":95406.0,"Position":449.0,"HyperDash":false},{"StartTime":95482.0,"Position":433.0,"HyperDash":false},{"StartTime":95558.0,"Position":456.0,"HyperDash":false},{"StartTime":95634.0,"Position":440.0,"HyperDash":false},{"StartTime":95700.0,"Position":439.0,"HyperDash":false},{"StartTime":95767.0,"Position":423.0,"HyperDash":false},{"StartTime":95834.0,"Position":428.0,"HyperDash":false},{"StartTime":95937.0,"Position":440.0,"HyperDash":false}]},{"StartTime":96543.0,"Objects":[{"StartTime":96543.0,"Position":216.0,"HyperDash":false},{"StartTime":96618.0,"Position":204.0,"HyperDash":false},{"StartTime":96694.0,"Position":208.0,"HyperDash":false},{"StartTime":96770.0,"Position":218.0,"HyperDash":false},{"StartTime":96846.0,"Position":216.0,"HyperDash":false},{"StartTime":96912.0,"Position":233.0,"HyperDash":false},{"StartTime":96979.0,"Position":225.0,"HyperDash":false},{"StartTime":97046.0,"Position":206.0,"HyperDash":false},{"StartTime":97149.0,"Position":216.0,"HyperDash":false}]},{"StartTime":97755.0,"Objects":[{"StartTime":97755.0,"Position":48.0,"HyperDash":false}]},{"StartTime":98361.0,"Objects":[{"StartTime":98361.0,"Position":216.0,"HyperDash":false}]},{"StartTime":98967.0,"Objects":[{"StartTime":98967.0,"Position":216.0,"HyperDash":false},{"StartTime":99042.0,"Position":231.0,"HyperDash":false},{"StartTime":99118.0,"Position":207.0,"HyperDash":false},{"StartTime":99194.0,"Position":205.0,"HyperDash":false},{"StartTime":99270.0,"Position":216.0,"HyperDash":false},{"StartTime":99345.0,"Position":206.0,"HyperDash":false},{"StartTime":99421.0,"Position":218.0,"HyperDash":false},{"StartTime":99497.0,"Position":208.0,"HyperDash":false},{"StartTime":99573.0,"Position":216.0,"HyperDash":false},{"StartTime":99648.0,"Position":234.0,"HyperDash":false},{"StartTime":99724.0,"Position":222.0,"HyperDash":false},{"StartTime":99800.0,"Position":231.0,"HyperDash":false},{"StartTime":99876.0,"Position":216.0,"HyperDash":false},{"StartTime":99942.0,"Position":200.0,"HyperDash":false},{"StartTime":100009.0,"Position":199.0,"HyperDash":false},{"StartTime":100076.0,"Position":228.0,"HyperDash":false},{"StartTime":100179.0,"Position":216.0,"HyperDash":false}]},{"StartTime":100786.0,"Objects":[{"StartTime":100786.0,"Position":216.0,"HyperDash":false}]},{"StartTime":101392.0,"Objects":[{"StartTime":101392.0,"Position":216.0,"HyperDash":false}]},{"StartTime":101998.0,"Objects":[{"StartTime":101998.0,"Position":356.0,"HyperDash":false},{"StartTime":102054.0,"Position":362.0,"HyperDash":false},{"StartTime":102111.0,"Position":347.0,"HyperDash":false},{"StartTime":102168.0,"Position":252.0,"HyperDash":false},{"StartTime":102225.0,"Position":477.0,"HyperDash":false},{"StartTime":102282.0,"Position":358.0,"HyperDash":false},{"StartTime":102338.0,"Position":17.0,"HyperDash":false},{"StartTime":102395.0,"Position":399.0,"HyperDash":false},{"StartTime":102452.0,"Position":280.0,"HyperDash":false},{"StartTime":102509.0,"Position":304.0,"HyperDash":false},{"StartTime":102566.0,"Position":221.0,"HyperDash":false},{"StartTime":102622.0,"Position":407.0,"HyperDash":false},{"StartTime":102679.0,"Position":287.0,"HyperDash":false},{"StartTime":102736.0,"Position":135.0,"HyperDash":false},{"StartTime":102793.0,"Position":437.0,"HyperDash":false},{"StartTime":102850.0,"Position":289.0,"HyperDash":false},{"StartTime":102907.0,"Position":464.0,"HyperDash":false},{"StartTime":102963.0,"Position":36.0,"HyperDash":false},{"StartTime":103020.0,"Position":378.0,"HyperDash":false},{"StartTime":103077.0,"Position":297.0,"HyperDash":false},{"StartTime":103134.0,"Position":418.0,"HyperDash":false},{"StartTime":103191.0,"Position":329.0,"HyperDash":false},{"StartTime":103247.0,"Position":338.0,"HyperDash":false},{"StartTime":103304.0,"Position":394.0,"HyperDash":false},{"StartTime":103361.0,"Position":40.0,"HyperDash":false},{"StartTime":103418.0,"Position":13.0,"HyperDash":false},{"StartTime":103475.0,"Position":80.0,"HyperDash":false},{"StartTime":103531.0,"Position":138.0,"HyperDash":false},{"StartTime":103588.0,"Position":311.0,"HyperDash":false},{"StartTime":103645.0,"Position":216.0,"HyperDash":false},{"StartTime":103702.0,"Position":310.0,"HyperDash":false},{"StartTime":103759.0,"Position":397.0,"HyperDash":false},{"StartTime":103816.0,"Position":214.0,"HyperDash":false},{"StartTime":103872.0,"Position":505.0,"HyperDash":false},{"StartTime":103929.0,"Position":173.0,"HyperDash":false},{"StartTime":103986.0,"Position":295.0,"HyperDash":false},{"StartTime":104043.0,"Position":199.0,"HyperDash":false},{"StartTime":104100.0,"Position":494.0,"HyperDash":false},{"StartTime":104156.0,"Position":293.0,"HyperDash":false},{"StartTime":104213.0,"Position":115.0,"HyperDash":false},{"StartTime":104270.0,"Position":412.0,"HyperDash":false},{"StartTime":104327.0,"Position":506.0,"HyperDash":false},{"StartTime":104384.0,"Position":293.0,"HyperDash":false},{"StartTime":104440.0,"Position":346.0,"HyperDash":false},{"StartTime":104497.0,"Position":117.0,"HyperDash":false},{"StartTime":104554.0,"Position":285.0,"HyperDash":false},{"StartTime":104611.0,"Position":17.0,"HyperDash":false},{"StartTime":104668.0,"Position":238.0,"HyperDash":false},{"StartTime":104725.0,"Position":222.0,"HyperDash":false},{"StartTime":104781.0,"Position":450.0,"HyperDash":false},{"StartTime":104838.0,"Position":67.0,"HyperDash":false},{"StartTime":104895.0,"Position":219.0,"HyperDash":false},{"StartTime":104952.0,"Position":307.0,"HyperDash":false},{"StartTime":105009.0,"Position":367.0,"HyperDash":false},{"StartTime":105065.0,"Position":412.0,"HyperDash":false},{"StartTime":105122.0,"Position":413.0,"HyperDash":false},{"StartTime":105179.0,"Position":143.0,"HyperDash":false},{"StartTime":105236.0,"Position":339.0,"HyperDash":false},{"StartTime":105293.0,"Position":342.0,"HyperDash":false},{"StartTime":105349.0,"Position":249.0,"HyperDash":false},{"StartTime":105406.0,"Position":235.0,"HyperDash":false},{"StartTime":105463.0,"Position":323.0,"HyperDash":false},{"StartTime":105520.0,"Position":365.0,"HyperDash":false},{"StartTime":105577.0,"Position":74.0,"HyperDash":false},{"StartTime":105634.0,"Position":281.0,"HyperDash":false},{"StartTime":105690.0,"Position":398.0,"HyperDash":false},{"StartTime":105747.0,"Position":335.0,"HyperDash":false},{"StartTime":105804.0,"Position":388.0,"HyperDash":false},{"StartTime":105861.0,"Position":228.0,"HyperDash":false},{"StartTime":105918.0,"Position":323.0,"HyperDash":false},{"StartTime":105974.0,"Position":441.0,"HyperDash":false},{"StartTime":106031.0,"Position":442.0,"HyperDash":false},{"StartTime":106088.0,"Position":278.0,"HyperDash":false},{"StartTime":106145.0,"Position":90.0,"HyperDash":false},{"StartTime":106202.0,"Position":409.0,"HyperDash":false},{"StartTime":106258.0,"Position":377.0,"HyperDash":false},{"StartTime":106315.0,"Position":457.0,"HyperDash":false},{"StartTime":106372.0,"Position":409.0,"HyperDash":false},{"StartTime":106429.0,"Position":43.0,"HyperDash":false},{"StartTime":106486.0,"Position":162.0,"HyperDash":false},{"StartTime":106543.0,"Position":341.0,"HyperDash":false},{"StartTime":106599.0,"Position":72.0,"HyperDash":false},{"StartTime":106656.0,"Position":135.0,"HyperDash":false},{"StartTime":106713.0,"Position":252.0,"HyperDash":false},{"StartTime":106770.0,"Position":446.0,"HyperDash":false},{"StartTime":106827.0,"Position":284.0,"HyperDash":false},{"StartTime":106883.0,"Position":70.0,"HyperDash":false},{"StartTime":106940.0,"Position":494.0,"HyperDash":false},{"StartTime":106997.0,"Position":463.0,"HyperDash":false},{"StartTime":107054.0,"Position":277.0,"HyperDash":false},{"StartTime":107111.0,"Position":425.0,"HyperDash":false},{"StartTime":107167.0,"Position":281.0,"HyperDash":false},{"StartTime":107224.0,"Position":3.0,"HyperDash":false},{"StartTime":107281.0,"Position":346.0,"HyperDash":false},{"StartTime":107338.0,"Position":350.0,"HyperDash":false},{"StartTime":107395.0,"Position":217.0,"HyperDash":false},{"StartTime":107452.0,"Position":455.0,"HyperDash":false},{"StartTime":107508.0,"Position":229.0,"HyperDash":false},{"StartTime":107565.0,"Position":51.0,"HyperDash":false},{"StartTime":107622.0,"Position":199.0,"HyperDash":false},{"StartTime":107679.0,"Position":208.0,"HyperDash":false},{"StartTime":107736.0,"Position":173.0,"HyperDash":false},{"StartTime":107792.0,"Position":367.0,"HyperDash":false},{"StartTime":107849.0,"Position":193.0,"HyperDash":false},{"StartTime":107906.0,"Position":488.0,"HyperDash":false},{"StartTime":107963.0,"Position":314.0,"HyperDash":false},{"StartTime":108020.0,"Position":135.0,"HyperDash":false},{"StartTime":108076.0,"Position":399.0,"HyperDash":false},{"StartTime":108133.0,"Position":404.0,"HyperDash":false},{"StartTime":108190.0,"Position":152.0,"HyperDash":false},{"StartTime":108247.0,"Position":353.0,"HyperDash":false},{"StartTime":108304.0,"Position":358.0,"HyperDash":false},{"StartTime":108361.0,"Position":447.0,"HyperDash":false},{"StartTime":108417.0,"Position":222.0,"HyperDash":false},{"StartTime":108474.0,"Position":382.0,"HyperDash":false},{"StartTime":108531.0,"Position":433.0,"HyperDash":false},{"StartTime":108588.0,"Position":450.0,"HyperDash":false},{"StartTime":108645.0,"Position":326.0,"HyperDash":false},{"StartTime":108701.0,"Position":414.0,"HyperDash":false},{"StartTime":108758.0,"Position":285.0,"HyperDash":false},{"StartTime":108815.0,"Position":336.0,"HyperDash":false},{"StartTime":108872.0,"Position":509.0,"HyperDash":false},{"StartTime":108929.0,"Position":334.0,"HyperDash":false},{"StartTime":108985.0,"Position":72.0,"HyperDash":false},{"StartTime":109042.0,"Position":425.0,"HyperDash":false},{"StartTime":109099.0,"Position":451.0,"HyperDash":false},{"StartTime":109156.0,"Position":220.0,"HyperDash":false},{"StartTime":109213.0,"Position":25.0,"HyperDash":false},{"StartTime":109270.0,"Position":77.0,"HyperDash":false}]},{"StartTime":111392.0,"Objects":[{"StartTime":111392.0,"Position":48.0,"HyperDash":false},{"StartTime":111449.0,"Position":89.64901,"HyperDash":false},{"StartTime":111543.0,"Position":108.0,"HyperDash":false}]},{"StartTime":111695.0,"Objects":[{"StartTime":111695.0,"Position":156.0,"HyperDash":false}]},{"StartTime":112301.0,"Objects":[{"StartTime":112301.0,"Position":347.0,"HyperDash":false},{"StartTime":112358.0,"Position":344.0,"HyperDash":false},{"StartTime":112452.0,"Position":347.0,"HyperDash":false}]},{"StartTime":112604.0,"Objects":[{"StartTime":112604.0,"Position":347.0,"HyperDash":false},{"StartTime":112661.0,"Position":343.0,"HyperDash":false},{"StartTime":112755.0,"Position":347.0,"HyperDash":false}]},{"StartTime":112907.0,"Objects":[{"StartTime":112907.0,"Position":347.0,"HyperDash":false}]},{"StartTime":113513.0,"Objects":[{"StartTime":113513.0,"Position":155.0,"HyperDash":false}]},{"StartTime":113664.0,"Objects":[{"StartTime":113664.0,"Position":155.0,"HyperDash":false}]},{"StartTime":113816.0,"Objects":[{"StartTime":113816.0,"Position":155.0,"HyperDash":false},{"StartTime":113891.0,"Position":169.702972,"HyperDash":false},{"StartTime":113967.0,"Position":201.801987,"HyperDash":false},{"StartTime":114043.0,"Position":248.900986,"HyperDash":false},{"StartTime":114119.0,"Position":275.0,"HyperDash":false},{"StartTime":114185.0,"Position":240.861389,"HyperDash":false},{"StartTime":114252.0,"Position":220.326736,"HyperDash":false},{"StartTime":114319.0,"Position":184.792084,"HyperDash":false},{"StartTime":114422.0,"Position":155.0,"HyperDash":false}]},{"StartTime":114725.0,"Objects":[{"StartTime":114725.0,"Position":155.0,"HyperDash":false},{"StartTime":114782.0,"Position":174.649,"HyperDash":false},{"StartTime":114876.0,"Position":215.0,"HyperDash":false}]},{"StartTime":115331.0,"Objects":[{"StartTime":115331.0,"Position":359.0,"HyperDash":false}]},{"StartTime":115634.0,"Objects":[{"StartTime":115634.0,"Position":359.0,"HyperDash":false},{"StartTime":115700.0,"Position":376.0,"HyperDash":false},{"StartTime":115767.0,"Position":358.0,"HyperDash":false},{"StartTime":115834.0,"Position":343.0,"HyperDash":false},{"StartTime":115937.0,"Position":359.0,"HyperDash":false}]},{"StartTime":116543.0,"Objects":[{"StartTime":116543.0,"Position":167.0,"HyperDash":false},{"StartTime":116600.0,"Position":186.0,"HyperDash":false},{"StartTime":116694.0,"Position":167.0,"HyperDash":false}]},{"StartTime":116846.0,"Objects":[{"StartTime":116846.0,"Position":167.0,"HyperDash":false}]},{"StartTime":116998.0,"Objects":[{"StartTime":116998.0,"Position":215.0,"HyperDash":false},{"StartTime":117055.0,"Position":232.649,"HyperDash":false},{"StartTime":117149.0,"Position":275.0,"HyperDash":false}]},{"StartTime":117301.0,"Objects":[{"StartTime":117301.0,"Position":323.0,"HyperDash":false}]},{"StartTime":117604.0,"Objects":[{"StartTime":117604.0,"Position":323.0,"HyperDash":false}]},{"StartTime":117907.0,"Objects":[{"StartTime":117907.0,"Position":227.0,"HyperDash":false}]},{"StartTime":118967.0,"Objects":[{"StartTime":118967.0,"Position":40.0,"HyperDash":false}]},{"StartTime":119573.0,"Objects":[{"StartTime":119573.0,"Position":231.0,"HyperDash":false}]},{"StartTime":120180.0,"Objects":[{"StartTime":120180.0,"Position":422.0,"HyperDash":false},{"StartTime":120255.0,"Position":413.0,"HyperDash":false},{"StartTime":120331.0,"Position":402.0,"HyperDash":false},{"StartTime":120407.0,"Position":413.0,"HyperDash":false},{"StartTime":120483.0,"Position":422.0,"HyperDash":false},{"StartTime":120549.0,"Position":440.0,"HyperDash":false},{"StartTime":120616.0,"Position":418.0,"HyperDash":false},{"StartTime":120683.0,"Position":433.0,"HyperDash":false},{"StartTime":120786.0,"Position":422.0,"HyperDash":false}]},{"StartTime":120937.0,"Objects":[{"StartTime":120937.0,"Position":373.0,"HyperDash":false}]},{"StartTime":121089.0,"Objects":[{"StartTime":121089.0,"Position":324.0,"HyperDash":false},{"StartTime":121155.0,"Position":293.8614,"HyperDash":false},{"StartTime":121222.0,"Position":274.326721,"HyperDash":false},{"StartTime":121289.0,"Position":262.792084,"HyperDash":false},{"StartTime":121392.0,"Position":204.0,"HyperDash":false}]},{"StartTime":121695.0,"Objects":[{"StartTime":121695.0,"Position":204.0,"HyperDash":false}]},{"StartTime":122604.0,"Objects":[{"StartTime":122604.0,"Position":40.0,"HyperDash":false}]},{"StartTime":122907.0,"Objects":[{"StartTime":122907.0,"Position":256.0,"HyperDash":false}]},{"StartTime":123210.0,"Objects":[{"StartTime":123210.0,"Position":472.0,"HyperDash":false}]},{"StartTime":123816.0,"Objects":[{"StartTime":123816.0,"Position":472.0,"HyperDash":false},{"StartTime":123891.0,"Position":427.297028,"HyperDash":false},{"StartTime":123967.0,"Position":429.198029,"HyperDash":false},{"StartTime":124043.0,"Position":387.099,"HyperDash":false},{"StartTime":124119.0,"Position":352.0,"HyperDash":false},{"StartTime":124194.0,"Position":317.297028,"HyperDash":false},{"StartTime":124270.0,"Position":277.198029,"HyperDash":false},{"StartTime":124346.0,"Position":258.099,"HyperDash":false},{"StartTime":124422.0,"Position":232.0,"HyperDash":false},{"StartTime":124497.0,"Position":217.297028,"HyperDash":false},{"StartTime":124573.0,"Position":174.198029,"HyperDash":false},{"StartTime":124649.0,"Position":134.09903,"HyperDash":false},{"StartTime":124725.0,"Position":112.0,"HyperDash":false},{"StartTime":124800.0,"Position":74.29706,"HyperDash":false},{"StartTime":124876.0,"Position":66.19803,"HyperDash":false},{"StartTime":124952.0,"Position":49.0,"HyperDash":false},{"StartTime":125028.0,"Position":32.0,"HyperDash":false},{"StartTime":125103.0,"Position":44.0,"HyperDash":false},{"StartTime":125179.0,"Position":49.0,"HyperDash":false},{"StartTime":125255.0,"Position":39.901,"HyperDash":false},{"StartTime":125331.0,"Position":88.0,"HyperDash":false},{"StartTime":125397.0,"Position":106.138611,"HyperDash":false},{"StartTime":125464.0,"Position":129.673279,"HyperDash":false},{"StartTime":125531.0,"Position":176.207947,"HyperDash":false},{"StartTime":125634.0,"Position":208.0,"HyperDash":false}]},{"StartTime":126240.0,"Objects":[{"StartTime":126240.0,"Position":399.0,"HyperDash":false}]},{"StartTime":126846.0,"Objects":[{"StartTime":126846.0,"Position":399.0,"HyperDash":false}]},{"StartTime":127452.0,"Objects":[{"StartTime":127452.0,"Position":315.0,"HyperDash":false},{"StartTime":127508.0,"Position":35.0,"HyperDash":false},{"StartTime":127565.0,"Position":208.0,"HyperDash":false},{"StartTime":127622.0,"Position":504.0,"HyperDash":false},{"StartTime":127679.0,"Position":296.0,"HyperDash":false},{"StartTime":127736.0,"Position":105.0,"HyperDash":false},{"StartTime":127792.0,"Position":488.0,"HyperDash":false},{"StartTime":127849.0,"Position":230.0,"HyperDash":false},{"StartTime":127906.0,"Position":446.0,"HyperDash":false},{"StartTime":127963.0,"Position":241.0,"HyperDash":false},{"StartTime":128020.0,"Position":413.0,"HyperDash":false},{"StartTime":128076.0,"Position":357.0,"HyperDash":false},{"StartTime":128133.0,"Position":256.0,"HyperDash":false},{"StartTime":128190.0,"Position":192.0,"HyperDash":false},{"StartTime":128247.0,"Position":116.0,"HyperDash":false},{"StartTime":128304.0,"Position":397.0,"HyperDash":false},{"StartTime":128361.0,"Position":422.0,"HyperDash":false},{"StartTime":128417.0,"Position":230.0,"HyperDash":false},{"StartTime":128474.0,"Position":479.0,"HyperDash":false},{"StartTime":128531.0,"Position":276.0,"HyperDash":false},{"StartTime":128588.0,"Position":423.0,"HyperDash":false},{"StartTime":128645.0,"Position":450.0,"HyperDash":false},{"StartTime":128701.0,"Position":336.0,"HyperDash":false},{"StartTime":128758.0,"Position":145.0,"HyperDash":false},{"StartTime":128815.0,"Position":30.0,"HyperDash":false},{"StartTime":128872.0,"Position":426.0,"HyperDash":false},{"StartTime":128929.0,"Position":394.0,"HyperDash":false},{"StartTime":128985.0,"Position":274.0,"HyperDash":false},{"StartTime":129042.0,"Position":44.0,"HyperDash":false},{"StartTime":129099.0,"Position":32.0,"HyperDash":false},{"StartTime":129156.0,"Position":10.0,"HyperDash":false},{"StartTime":129213.0,"Position":505.0,"HyperDash":false},{"StartTime":129270.0,"Position":321.0,"HyperDash":false}]},{"StartTime":129876.0,"Objects":[{"StartTime":129876.0,"Position":48.0,"HyperDash":false}]},{"StartTime":130483.0,"Objects":[{"StartTime":130483.0,"Position":144.0,"HyperDash":false}]},{"StartTime":131089.0,"Objects":[{"StartTime":131089.0,"Position":240.0,"HyperDash":false},{"StartTime":131164.0,"Position":239.851486,"HyperDash":false},{"StartTime":131240.0,"Position":286.901,"HyperDash":false},{"StartTime":131316.0,"Position":289.9505,"HyperDash":false},{"StartTime":131392.0,"Position":282.0,"HyperDash":false},{"StartTime":131467.0,"Position":312.8515,"HyperDash":false},{"StartTime":131543.0,"Position":328.901,"HyperDash":false},{"StartTime":131619.0,"Position":352.9505,"HyperDash":false},{"StartTime":131695.0,"Position":360.0,"HyperDash":false},{"StartTime":131770.0,"Position":349.0,"HyperDash":false},{"StartTime":131846.0,"Position":359.0,"HyperDash":false},{"StartTime":131922.0,"Position":362.0,"HyperDash":false},{"StartTime":131998.0,"Position":352.0,"HyperDash":false},{"StartTime":132073.0,"Position":356.0,"HyperDash":false},{"StartTime":132149.0,"Position":371.0,"HyperDash":false},{"StartTime":132225.0,"Position":346.0,"HyperDash":false},{"StartTime":132301.0,"Position":360.0,"HyperDash":false},{"StartTime":132372.0,"Position":328.9406,"HyperDash":false},{"StartTime":132443.0,"Position":335.8812,"HyperDash":false},{"StartTime":132514.0,"Position":328.821777,"HyperDash":false},{"StartTime":132586.0,"Position":302.564362,"HyperDash":false},{"StartTime":132657.0,"Position":285.504944,"HyperDash":false},{"StartTime":132728.0,"Position":274.445557,"HyperDash":false},{"StartTime":132799.0,"Position":246.386139,"HyperDash":false},{"StartTime":132907.0,"Position":240.0,"HyperDash":false}]},{"StartTime":133513.0,"Objects":[{"StartTime":133513.0,"Position":144.0,"HyperDash":false}]},{"StartTime":134119.0,"Objects":[{"StartTime":134119.0,"Position":144.0,"HyperDash":false}]},{"StartTime":134725.0,"Objects":[{"StartTime":134725.0,"Position":423.0,"HyperDash":false},{"StartTime":134781.0,"Position":367.0,"HyperDash":false},{"StartTime":134838.0,"Position":146.0,"HyperDash":false},{"StartTime":134895.0,"Position":322.0,"HyperDash":false},{"StartTime":134952.0,"Position":169.0,"HyperDash":false},{"StartTime":135009.0,"Position":159.0,"HyperDash":false},{"StartTime":135065.0,"Position":388.0,"HyperDash":false},{"StartTime":135122.0,"Position":67.0,"HyperDash":false},{"StartTime":135179.0,"Position":176.0,"HyperDash":false},{"StartTime":135236.0,"Position":371.0,"HyperDash":false},{"StartTime":135293.0,"Position":365.0,"HyperDash":false},{"StartTime":135349.0,"Position":104.0,"HyperDash":false},{"StartTime":135406.0,"Position":363.0,"HyperDash":false},{"StartTime":135463.0,"Position":75.0,"HyperDash":false},{"StartTime":135520.0,"Position":158.0,"HyperDash":false},{"StartTime":135577.0,"Position":98.0,"HyperDash":false},{"StartTime":135634.0,"Position":30.0,"HyperDash":false},{"StartTime":135690.0,"Position":164.0,"HyperDash":false},{"StartTime":135747.0,"Position":341.0,"HyperDash":false},{"StartTime":135804.0,"Position":18.0,"HyperDash":false},{"StartTime":135861.0,"Position":210.0,"HyperDash":false},{"StartTime":135918.0,"Position":420.0,"HyperDash":false},{"StartTime":135974.0,"Position":447.0,"HyperDash":false},{"StartTime":136031.0,"Position":78.0,"HyperDash":false},{"StartTime":136088.0,"Position":177.0,"HyperDash":false},{"StartTime":136145.0,"Position":305.0,"HyperDash":false},{"StartTime":136202.0,"Position":400.0,"HyperDash":false},{"StartTime":136258.0,"Position":462.0,"HyperDash":false},{"StartTime":136315.0,"Position":64.0,"HyperDash":false},{"StartTime":136372.0,"Position":458.0,"HyperDash":false},{"StartTime":136429.0,"Position":380.0,"HyperDash":false},{"StartTime":136486.0,"Position":65.0,"HyperDash":false},{"StartTime":136543.0,"Position":23.0,"HyperDash":false},{"StartTime":136599.0,"Position":379.0,"HyperDash":false},{"StartTime":136656.0,"Position":44.0,"HyperDash":false},{"StartTime":136713.0,"Position":485.0,"HyperDash":false},{"StartTime":136770.0,"Position":269.0,"HyperDash":false},{"StartTime":136827.0,"Position":155.0,"HyperDash":false},{"StartTime":136883.0,"Position":324.0,"HyperDash":false},{"StartTime":136940.0,"Position":149.0,"HyperDash":false},{"StartTime":136997.0,"Position":351.0,"HyperDash":false},{"StartTime":137054.0,"Position":385.0,"HyperDash":false},{"StartTime":137111.0,"Position":338.0,"HyperDash":false},{"StartTime":137167.0,"Position":322.0,"HyperDash":false},{"StartTime":137224.0,"Position":84.0,"HyperDash":false},{"StartTime":137281.0,"Position":342.0,"HyperDash":false},{"StartTime":137338.0,"Position":395.0,"HyperDash":false},{"StartTime":137395.0,"Position":72.0,"HyperDash":false},{"StartTime":137452.0,"Position":324.0,"HyperDash":false},{"StartTime":137508.0,"Position":67.0,"HyperDash":false},{"StartTime":137565.0,"Position":371.0,"HyperDash":false},{"StartTime":137622.0,"Position":446.0,"HyperDash":false},{"StartTime":137679.0,"Position":29.0,"HyperDash":false},{"StartTime":137736.0,"Position":22.0,"HyperDash":false},{"StartTime":137792.0,"Position":432.0,"HyperDash":false},{"StartTime":137849.0,"Position":12.0,"HyperDash":false},{"StartTime":137906.0,"Position":330.0,"HyperDash":false},{"StartTime":137963.0,"Position":419.0,"HyperDash":false},{"StartTime":138020.0,"Position":278.0,"HyperDash":false},{"StartTime":138076.0,"Position":202.0,"HyperDash":false},{"StartTime":138133.0,"Position":208.0,"HyperDash":false},{"StartTime":138190.0,"Position":21.0,"HyperDash":false},{"StartTime":138247.0,"Position":437.0,"HyperDash":false},{"StartTime":138304.0,"Position":312.0,"HyperDash":false},{"StartTime":138361.0,"Position":508.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/50859.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/50859.osu new file mode 100644 index 0000000000..8272b8b1db --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/50859.osu @@ -0,0 +1,290 @@ +osu file format v7 + +[General] +StackLeniency: 0.5 +Mode: 0 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +SliderMultiplier:2.4 +SliderTickRate:2 + +[Events] +//Break Periods +2,109470,110492 +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +180,606.060606060606,3,2,1,20,1,0 +11528,-100,4,2,1,50,0,0 +28952,-100,4,2,1,20,0,0 +36452,-100,4,2,1,50,0,0 +43523,-50,4,2,2,20,0,0 +50921,-100,4,2,1,40,0,0 +51073,-100,4,2,1,60,0,0 +53371,-100,4,2,2,60,0,0 +54280,-100,4,2,1,60,0,0 +58195,-100,4,2,1,40,0,0 +65468,-100,4,2,1,60,0,0 +68801,-100,4,1,0,30,0,0 +69129,-100,4,1,1,30,0,0 +69407,-100,4,2,1,60,0,0 +75644,-100,4,1,0,35,0,0 +76680,-100,4,2,1,60,0,0 +78195,-100,4,2,1,40,0,0 +78649,-100,4,2,1,60,0,0 +87386,-100,4,1,2,30,0,0 +101856,-100,4,1,1,40,0,0 +109583,-100,4,2,2,60,0,0 +112008,-100,4,1,2,20,0,0 +113068,-100,4,2,2,60,0,0 +114583,-100,4,2,2,40,0,0 +115038,-100,4,2,2,60,0,0 +123523,-100,4,1,2,30,0,0 +124280,-100,4,1,0,30,0,0 +124583,-100,4,1,2,30,0,0 +124886,-100,4,1,0,30,0,0 +125189,-100,4,1,2,30,0,0 +125644,-100,4,1,1,30,0,0 +125947,-100,4,1,2,20,0,0 +127159,-100,4,1,1,60,0,0 +129583,-200,4,1,0,20,0,0 +134583,-200,4,1,1,0,0,0 + +[HitObjects] +120,72,179,1,0 +311,72,786,2,0,B|448:72,1,120,2|0 +431,167,1392,2,0,B|303:167,1,120,2|2 +215,167,1998,1,0 +119,167,2301,6,0,B|487:167,1,360,2|2 +478,261,3513,1,0 +382,261,3816,6,0,B|254:261,1,120 +166,261,4422,2,0,B|166:138,1,120,2|2 +166,332,5331,1,0 +261,332,5634,2,0,L|327:332,1,60,0|2 +321,235,6089,2,0,L|321:175,1,60,0|2 +321,79,6543,1,0 +465,79,6998,2,0,L|465:143,1,60,0|2 +369,139,7452,6,0,B|369:278,1,120 +464,259,8058,1,2 +464,163,8361,2,0,B|288:163,1,120,0|2 +248,163,8967,1,0 +200,243,9270,1,0 +296,243,9573,1,2 +275,37,10180,5,0 +179,37,10483,1,2 +179,132,10786,2,0,B|307:132,1,120,2|0 +299,227,11392,1,0 +203,227,11695,6,0,L|142:227,1,60 +94,227,11998,1,2 +94,131,12301,1,2 +189,131,12604,1,0 +476,131,13513,1,0 +380,131,13816,1,2 +272,23,14725,6,0,L|208:23,1,60,2|0 +177,57,15028,2,0,L|177:129,1,60 +225,117,15331,1,0 +273,117,15483,1,2 +273,211,15786,1,2 +273,306,16089,1,2 +33,306,16846,6,0,L|33:242,1,60 +33,197,17149,1,2 +224,197,17755,1,0 +277,50,18967,5,0 +228,50,19119,1,0 +181,50,19270,1,2 +181,145,19573,1,2 +181,240,19876,1,0 +469,240,20786,5,0 +373,240,21089,1,2 +277,240,21392,1,0 +243,350,21998,5,2 +243,302,22149,1,0 +243,254,22301,1,2 +290,254,22452,2,0,L|290:193,1,60 +290,146,22755,1,2 +385,146,23058,1,2 +385,241,23361,1,2 +213,68,24119,6,0,L|149:68,1,60,0|0 +104,68,24422,1,2 +295,68,25028,1,0 +56,64,26240,5,0 +56,64,26392,1,0 +56,64,26543,1,2 +56,159,26846,1,2 +151,159,27149,1,0 +438,159,28058,6,0,B|438:303,1,120,0|2 +184,192,29270,6,0,B|312:192,1,120,6|0 +399,192,29876,1,2 +399,95,30180,1,0 +303,95,30483,2,0,B|129:95,1,120,2|0 +115,162,31089,6,0,B|243:162,1,120,2|0 +330,162,31695,1,2 +425,162,31998,1,0 +425,257,32301,2,0,B|265:257,1,120,2|0 +209,257,32907,6,0,B|65:257,1,120,6|0 +89,160,33513,1,2 +184,160,33816,1,0 +279,160,34119,1,2 +374,160,34422,1,0 +469,160,34725,6,0,B|469:304,1,120,2|0 +373,280,35331,2,0,B|216:280,1,120,2|0 +157,280,35937,1,2 +157,184,36240,1,0 +157,135,36392,1,0 +204,135,36543,6,0,B|268:135,2,60,2|0|2 +204,183,36998,2,0,B|204:255,1,60 +205,291,37301,1,2 +300,291,37604,1,2 +300,195,37907,1,2 +32,32,38967,5,2 +32,223,39573,1,0 +416,223,40786,5,0 +416,176,40937,1,0 +416,128,41089,1,2 +320,128,41392,1,2 +320,224,41695,1,0 +48,128,42604,6,0,B|192:128,1,120,0|2 +263,128,43210,1,0 +376,192,43816,6,0,B|136:192,5,240,6|0|2|0|2|0 +376,248,45634,6,0,B|136:248,5,240,2|0|2|0|2|0 +376,184,47452,6,0,B|136:184,5,240,6|0|2|0|2|0 +376,248,49270,6,0,B|109:247,1,240,2|0 +136,136,49876,1,2 +328,136,50180,1,0 +329,326,50483,1,2 +136,328,50786,1,0 +138,278,50937,1,0 +138,229,51089,6,0,B|255:229,1,60,6|0 +198,180,51392,1,2 +246,180,51543,1,0 +295,180,51695,2,0,B|365:180,1,60,0|2 +355,84,52149,1,2 +260,84,52452,1,2 +40,344,53513,6,0,L|280:344,1,240,2|0 +40,40,55331,6,0,L|120:40,2,60,0|0|2 +40,135,55937,2,0,L|40:262,1,120,2|0 +300,132,57149,6,0,L|300:272,1,120,0|2 +256,192,58361,6,0,L|336:192,1,60,2|0 +364,192,58664,2,0,L|364:256,1,60,2|0 +329,286,58967,1,0 +280,286,59119,2,0,L|208:286,1,60,2|0 +185,251,59422,2,0,L|185:179,1,60,2|0 +185,95,59876,1,4 +253,163,60180,5,2 +253,163,60331,1,2 +253,163,60483,1,2 +253,163,60634,1,0 +253,211,60786,2,0,L|192:211,4,60,2|0|2|0|2 +253,115,61695,1,4 +348,115,61998,6,0,L|348:51,4,60,2|0|2|0|2 +348,162,62755,1,2 +348,210,62907,1,2 +348,257,63058,1,0 +348,257,63210,1,2 +252,257,63513,1,4 +252,161,63816,5,0 +252,113,63967,2,0,L|169:113,3,60,2|0|2|0 +288,113,64725,1,4 +383,113,65028,1,4 +383,208,65331,1,0 +287,208,65634,6,0,L|195:208,1,60,2|0 +178,208,65937,1,2 +129,208,66089,1,0 +81,208,66240,1,0 +81,256,66392,1,2 +81,303,66543,2,0,L|145:303,1,60,0|2 +189,303,66846,2,0,L|253:303,1,60,0|2 +192,48,67755,6,0,L|304:48,1,60 +300,48,68058,1,2 +300,239,68664,1,0 +300,143,68967,5,0 +204,143,69270,1,4 +395,143,69876,6,0,L|396:226,1,60,0|0 +395,251,70180,1,2 +296,248,70483,1,2 +200,248,70786,1,0 +200,40,71695,1,0 +295,40,71998,1,2 +91,243,72907,5,2 +138,243,73058,1,0 +186,243,73210,1,0 +186,290,73361,2,0,L|254:290,1,60,2|0 +294,290,73664,2,0,L|371:290,1,60,2|0 +354,241,73967,1,2 +354,145,74270,1,2 +40,40,75331,5,2 +160,208,75937,1,0 +160,208,76089,1,0 +303,208,76543,1,4 +160,80,77149,6,0,L|232:80,1,60,0|0 +268,80,77452,1,2 +268,175,77755,1,2 +268,270,78058,1,0 +363,270,78361,6,4,L|363:187,1,60 +363,65,78967,5,0 +267,65,79270,2,0,L|126:65,1,120,2|0 +96,32,80180,6,0,L|96:344|296:344,1,480 +360,344,81695,1,0 +455,344,81998,2,0,L|455:32|159:32,1,600,2|0 +124,99,83816,6,0,L|364:99|364:347,1,480 +268,339,85331,1,4 +172,339,85634,2,0,L|52:339|52:235|52:123|156:123|316:123|316:219,1,660 +316,231,87452,6,0,L|316:354,1,120 +411,351,88058,2,0,L|411:103|147:103,1,480,4|0 +176,296,89876,2,0,L|40:296|40:152,1,240 +232,191,91089,5,4 +423,191,91695,2,0,L|423:351|71:351,1,480,4|4 +112,167,93513,1,0 +303,167,94119,1,0 +440,280,94725,6,0,L|440:35,2,240,4|4|0 +216,280,96543,2,0,L|216:32,1,240,4|0 +48,160,97755,1,0 +216,40,98361,5,4 +216,352,98967,2,0,L|216:104,2,240,4|0|4 +216,32,100786,1,4 +216,352,101392,1,0 +256,192,101998,12,0,109270 +48,48,111392,6,0,L|128:48,1,60 +156,48,111695,1,2 +347,48,112301,2,0,L|347:112,1,60 +347,156,112604,2,0,L|347:220,1,60 +347,264,112907,1,4 +155,264,113513,5,0 +155,216,113664,1,0 +155,167,113816,2,0,L|275:167,2,120,2|2|0 +155,71,114725,6,4,L|217:71,1,60 +359,71,115331,5,0 +359,166,115634,2,0,L|359:296,1,120,2|0 +167,286,116543,6,0,L|167:205,1,60,2|0 +167,177,116846,1,0 +215,177,116998,2,0,L|281:177,1,60,2|0 +323,177,117301,5,2 +323,81,117604,1,2 +227,81,117907,1,2 +40,344,118967,5,2 +231,344,119573,1,0 +422,344,120180,6,0,L|422:50,1,240 +373,104,120937,1,0 +324,104,121089,2,0,L|204:104,1,120,2|2 +204,199,121695,1,0 +40,40,122604,5,0 +256,40,122907,1,2 +472,40,123210,1,0 +472,232,123816,6,2,L|32:232|32:336|240:336,1,720,2|2 +399,336,126240,1,8 +399,144,126846,1,8 +256,192,127452,12,0,129270 +48,192,129876,5,8 +144,192,130483,1,8 +240,192,131089,2,2,L|360:192|360:72|240:72,1,360,2|2 +144,72,133513,1,8 +144,167,134119,1,8 +256,192,134725,12,0,138361 diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/75858-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/75858-expected-conversion.json new file mode 100644 index 0000000000..d5db48bc8c --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/75858-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":1173.0,"Objects":[{"StartTime":1173.0,"Position":94.0,"HyperDash":false},{"StartTime":1251.0,"Position":94.284874,"HyperDash":false},{"StartTime":1330.0,"Position":115.604385,"HyperDash":false},{"StartTime":1409.0,"Position":141.8706,"HyperDash":false},{"StartTime":1488.0,"Position":178.597519,"HyperDash":false},{"StartTime":1566.0,"Position":199.289474,"HyperDash":false},{"StartTime":1645.0,"Position":202.258377,"HyperDash":false},{"StartTime":1724.0,"Position":219.14473,"HyperDash":false},{"StartTime":1839.0,"Position":247.271439,"HyperDash":false}]},{"StartTime":2506.0,"Objects":[{"StartTime":2506.0,"Position":398.0,"HyperDash":false}]},{"StartTime":3172.0,"Objects":[{"StartTime":3172.0,"Position":471.0,"HyperDash":false}]},{"StartTime":3839.0,"Objects":[{"StartTime":3839.0,"Position":320.0,"HyperDash":false},{"StartTime":3917.0,"Position":287.205841,"HyperDash":false},{"StartTime":3996.0,"Position":275.6191,"HyperDash":false},{"StartTime":4075.0,"Position":252.736725,"HyperDash":false},{"StartTime":4154.0,"Position":241.768585,"HyperDash":false},{"StartTime":4232.0,"Position":241.149734,"HyperDash":false},{"StartTime":4311.0,"Position":212.634354,"HyperDash":false},{"StartTime":4390.0,"Position":181.770065,"HyperDash":false},{"StartTime":4505.0,"Position":166.756821,"HyperDash":false}]},{"StartTime":5173.0,"Objects":[{"StartTime":5173.0,"Position":65.0,"HyperDash":false}]},{"StartTime":5839.0,"Objects":[{"StartTime":5839.0,"Position":233.0,"HyperDash":false}]},{"StartTime":6506.0,"Objects":[{"StartTime":6506.0,"Position":239.0,"HyperDash":false},{"StartTime":6584.0,"Position":262.183746,"HyperDash":false},{"StartTime":6663.0,"Position":257.084351,"HyperDash":false},{"StartTime":6742.0,"Position":286.044983,"HyperDash":false},{"StartTime":6821.0,"Position":331.921021,"HyperDash":false},{"StartTime":6899.0,"Position":349.51355,"HyperDash":false},{"StartTime":6978.0,"Position":364.384766,"HyperDash":false},{"StartTime":7057.0,"Position":362.333984,"HyperDash":false},{"StartTime":7172.0,"Position":397.8736,"HyperDash":false}]},{"StartTime":7839.0,"Objects":[{"StartTime":7839.0,"Position":493.0,"HyperDash":false}]},{"StartTime":8506.0,"Objects":[{"StartTime":8506.0,"Position":175.0,"HyperDash":false}]},{"StartTime":9173.0,"Objects":[{"StartTime":9173.0,"Position":223.0,"HyperDash":false}]},{"StartTime":9839.0,"Objects":[{"StartTime":9839.0,"Position":119.0,"HyperDash":false}]},{"StartTime":10506.0,"Objects":[{"StartTime":10506.0,"Position":423.0,"HyperDash":false}]},{"StartTime":10839.0,"Objects":[{"StartTime":10839.0,"Position":199.0,"HyperDash":false},{"StartTime":10917.0,"Position":173.819885,"HyperDash":false},{"StartTime":10996.0,"Position":195.248886,"HyperDash":false},{"StartTime":11075.0,"Position":168.416779,"HyperDash":false},{"StartTime":11154.0,"Position":168.359177,"HyperDash":false},{"StartTime":11232.0,"Position":140.23172,"HyperDash":false},{"StartTime":11311.0,"Position":141.862854,"HyperDash":false},{"StartTime":11390.0,"Position":135.414291,"HyperDash":false},{"StartTime":11505.0,"Position":122.606651,"HyperDash":false}]},{"StartTime":11839.0,"Objects":[{"StartTime":11839.0,"Position":42.0,"HyperDash":false}]},{"StartTime":12506.0,"Objects":[{"StartTime":12506.0,"Position":178.0,"HyperDash":false}]},{"StartTime":13172.0,"Objects":[{"StartTime":13172.0,"Position":263.0,"HyperDash":false},{"StartTime":13250.0,"Position":294.723358,"HyperDash":false},{"StartTime":13329.0,"Position":284.270325,"HyperDash":false},{"StartTime":13408.0,"Position":299.0951,"HyperDash":false},{"StartTime":13487.0,"Position":321.0376,"HyperDash":false},{"StartTime":13565.0,"Position":368.773682,"HyperDash":false},{"StartTime":13644.0,"Position":391.718231,"HyperDash":false},{"StartTime":13723.0,"Position":392.566528,"HyperDash":false},{"StartTime":13838.0,"Position":420.607758,"HyperDash":false}]},{"StartTime":14506.0,"Objects":[{"StartTime":14506.0,"Position":293.0,"HyperDash":false},{"StartTime":14584.0,"Position":277.467133,"HyperDash":false},{"StartTime":14663.0,"Position":271.341,"HyperDash":false},{"StartTime":14742.0,"Position":276.740753,"HyperDash":false},{"StartTime":14821.0,"Position":235.4871,"HyperDash":false},{"StartTime":14899.0,"Position":229.366821,"HyperDash":false},{"StartTime":14978.0,"Position":219.329987,"HyperDash":false},{"StartTime":15057.0,"Position":204.814072,"HyperDash":false},{"StartTime":15172.0,"Position":160.38443,"HyperDash":false}]},{"StartTime":15839.0,"Objects":[{"StartTime":15839.0,"Position":282.0,"HyperDash":false},{"StartTime":15917.0,"Position":307.532867,"HyperDash":false},{"StartTime":15996.0,"Position":317.659,"HyperDash":false},{"StartTime":16075.0,"Position":334.259247,"HyperDash":false},{"StartTime":16154.0,"Position":342.512878,"HyperDash":false},{"StartTime":16232.0,"Position":333.633179,"HyperDash":false},{"StartTime":16311.0,"Position":373.67,"HyperDash":false},{"StartTime":16390.0,"Position":375.1859,"HyperDash":false},{"StartTime":16505.0,"Position":414.61557,"HyperDash":false}]},{"StartTime":17172.0,"Objects":[{"StartTime":17172.0,"Position":416.0,"HyperDash":false}]},{"StartTime":17839.0,"Objects":[{"StartTime":17839.0,"Position":256.0,"HyperDash":false},{"StartTime":17920.0,"Position":221.750565,"HyperDash":false},{"StartTime":18001.0,"Position":235.501129,"HyperDash":false},{"StartTime":18082.0,"Position":190.251709,"HyperDash":false},{"StartTime":18163.0,"Position":195.002274,"HyperDash":false},{"StartTime":18244.0,"Position":183.643,"HyperDash":false},{"StartTime":18325.0,"Position":181.956619,"HyperDash":false},{"StartTime":18406.0,"Position":200.650589,"HyperDash":false},{"StartTime":18487.0,"Position":193.194916,"HyperDash":false},{"StartTime":18568.0,"Position":202.48082,"HyperDash":false},{"StartTime":18649.0,"Position":179.827667,"HyperDash":false},{"StartTime":18730.0,"Position":173.703339,"HyperDash":false},{"StartTime":18811.0,"Position":186.446991,"HyperDash":false},{"StartTime":18892.0,"Position":151.2917,"HyperDash":false},{"StartTime":18973.0,"Position":126.879227,"HyperDash":false},{"StartTime":19054.0,"Position":122.770569,"HyperDash":false},{"StartTime":19172.0,"Position":99.93324,"HyperDash":false}]},{"StartTime":19839.0,"Objects":[{"StartTime":19839.0,"Position":256.0,"HyperDash":false}]},{"StartTime":20173.0,"Objects":[{"StartTime":20173.0,"Position":123.0,"HyperDash":false},{"StartTime":20245.0,"Position":42.0,"HyperDash":false},{"StartTime":20318.0,"Position":393.0,"HyperDash":false},{"StartTime":20391.0,"Position":75.0,"HyperDash":false},{"StartTime":20464.0,"Position":377.0,"HyperDash":false},{"StartTime":20537.0,"Position":354.0,"HyperDash":false},{"StartTime":20610.0,"Position":287.0,"HyperDash":false},{"StartTime":20683.0,"Position":361.0,"HyperDash":false},{"StartTime":20756.0,"Position":479.0,"HyperDash":false},{"StartTime":20829.0,"Position":346.0,"HyperDash":false},{"StartTime":20902.0,"Position":266.0,"HyperDash":false},{"StartTime":20974.0,"Position":400.0,"HyperDash":false},{"StartTime":21047.0,"Position":202.0,"HyperDash":false},{"StartTime":21120.0,"Position":500.0,"HyperDash":false},{"StartTime":21193.0,"Position":80.0,"HyperDash":false},{"StartTime":21266.0,"Position":399.0,"HyperDash":false},{"StartTime":21339.0,"Position":455.0,"HyperDash":false},{"StartTime":21412.0,"Position":105.0,"HyperDash":false},{"StartTime":21485.0,"Position":100.0,"HyperDash":false},{"StartTime":21558.0,"Position":195.0,"HyperDash":false},{"StartTime":21631.0,"Position":106.0,"HyperDash":false},{"StartTime":21704.0,"Position":305.0,"HyperDash":false},{"StartTime":21776.0,"Position":225.0,"HyperDash":false},{"StartTime":21849.0,"Position":79.0,"HyperDash":false},{"StartTime":21922.0,"Position":38.0,"HyperDash":false},{"StartTime":21995.0,"Position":99.0,"HyperDash":false},{"StartTime":22068.0,"Position":79.0,"HyperDash":false},{"StartTime":22141.0,"Position":169.0,"HyperDash":false},{"StartTime":22214.0,"Position":238.0,"HyperDash":false},{"StartTime":22287.0,"Position":511.0,"HyperDash":false},{"StartTime":22360.0,"Position":58.0,"HyperDash":false},{"StartTime":22433.0,"Position":368.0,"HyperDash":false},{"StartTime":22506.0,"Position":52.0,"HyperDash":false}]},{"StartTime":22961.0,"Objects":[{"StartTime":22961.0,"Position":256.0,"HyperDash":false}]},{"StartTime":23415.0,"Objects":[{"StartTime":23415.0,"Position":236.0,"HyperDash":false}]},{"StartTime":23870.0,"Objects":[{"StartTime":23870.0,"Position":104.0,"HyperDash":false},{"StartTime":23926.0,"Position":115.299927,"HyperDash":false},{"StartTime":23983.0,"Position":150.004791,"HyperDash":false},{"StartTime":24040.0,"Position":149.768875,"HyperDash":false},{"StartTime":24097.0,"Position":172.51532,"HyperDash":false},{"StartTime":24192.0,"Position":154.09137,"HyperDash":false},{"StartTime":24324.0,"Position":104.0,"HyperDash":false}]},{"StartTime":24779.0,"Objects":[{"StartTime":24779.0,"Position":256.0,"HyperDash":false},{"StartTime":24835.0,"Position":262.6648,"HyperDash":false},{"StartTime":24892.0,"Position":294.163452,"HyperDash":false},{"StartTime":24949.0,"Position":301.429565,"HyperDash":false},{"StartTime":25006.0,"Position":329.7457,"HyperDash":false},{"StartTime":25101.0,"Position":304.427277,"HyperDash":false},{"StartTime":25233.0,"Position":256.0,"HyperDash":false}]},{"StartTime":25688.0,"Objects":[{"StartTime":25688.0,"Position":118.0,"HyperDash":false},{"StartTime":25783.0,"Position":160.2344,"HyperDash":false},{"StartTime":25915.0,"Position":196.5579,"HyperDash":false}]},{"StartTime":26142.0,"Objects":[{"StartTime":26142.0,"Position":321.0,"HyperDash":false}]},{"StartTime":26597.0,"Objects":[{"StartTime":26597.0,"Position":419.0,"HyperDash":false},{"StartTime":26692.0,"Position":383.782776,"HyperDash":false},{"StartTime":26824.0,"Position":341.8768,"HyperDash":false}]},{"StartTime":27052.0,"Objects":[{"StartTime":27052.0,"Position":185.0,"HyperDash":false}]},{"StartTime":27506.0,"Objects":[{"StartTime":27506.0,"Position":71.0,"HyperDash":false}]},{"StartTime":27733.0,"Objects":[{"StartTime":27733.0,"Position":97.0,"HyperDash":false},{"StartTime":27828.0,"Position":69.73373,"HyperDash":false},{"StartTime":27960.0,"Position":95.43024,"HyperDash":false}]},{"StartTime":28415.0,"Objects":[{"StartTime":28415.0,"Position":376.0,"HyperDash":false}]},{"StartTime":28642.0,"Objects":[{"StartTime":28642.0,"Position":313.0,"HyperDash":false},{"StartTime":28737.0,"Position":349.615631,"HyperDash":false},{"StartTime":28869.0,"Position":392.036163,"HyperDash":false}]},{"StartTime":29324.0,"Objects":[{"StartTime":29324.0,"Position":501.0,"HyperDash":false}]},{"StartTime":29552.0,"Objects":[{"StartTime":29552.0,"Position":411.0,"HyperDash":false}]},{"StartTime":29779.0,"Objects":[{"StartTime":29779.0,"Position":501.0,"HyperDash":false}]},{"StartTime":30233.0,"Objects":[{"StartTime":30233.0,"Position":311.0,"HyperDash":false}]},{"StartTime":30461.0,"Objects":[{"StartTime":30461.0,"Position":231.0,"HyperDash":false}]},{"StartTime":30688.0,"Objects":[{"StartTime":30688.0,"Position":151.0,"HyperDash":false},{"StartTime":30744.0,"Position":136.485382,"HyperDash":false},{"StartTime":30801.0,"Position":111.448036,"HyperDash":false},{"StartTime":30915.0,"Position":151.0,"HyperDash":false}]},{"StartTime":31142.0,"Objects":[{"StartTime":31142.0,"Position":364.0,"HyperDash":false}]},{"StartTime":31370.0,"Objects":[{"StartTime":31370.0,"Position":202.0,"HyperDash":false}]},{"StartTime":31597.0,"Objects":[{"StartTime":31597.0,"Position":194.0,"HyperDash":false},{"StartTime":31649.0,"Position":193.329712,"HyperDash":false},{"StartTime":31701.0,"Position":177.29129,"HyperDash":false},{"StartTime":31753.0,"Position":180.897339,"HyperDash":false},{"StartTime":31806.0,"Position":196.245209,"HyperDash":false},{"StartTime":31858.0,"Position":225.942978,"HyperDash":false},{"StartTime":31910.0,"Position":221.896729,"HyperDash":false},{"StartTime":31962.0,"Position":258.838379,"HyperDash":false},{"StartTime":32051.0,"Position":270.298431,"HyperDash":false}]},{"StartTime":32279.0,"Objects":[{"StartTime":32279.0,"Position":316.0,"HyperDash":false}]},{"StartTime":32506.0,"Objects":[{"StartTime":32506.0,"Position":273.0,"HyperDash":false},{"StartTime":32558.0,"Position":268.772461,"HyperDash":false},{"StartTime":32610.0,"Position":229.84964,"HyperDash":false},{"StartTime":32662.0,"Position":200.402817,"HyperDash":false},{"StartTime":32715.0,"Position":184.266861,"HyperDash":false},{"StartTime":32767.0,"Position":200.3539,"HyperDash":false},{"StartTime":32819.0,"Position":172.707291,"HyperDash":false},{"StartTime":32871.0,"Position":162.9561,"HyperDash":false},{"StartTime":32960.0,"Position":144.968948,"HyperDash":false}]},{"StartTime":33188.0,"Objects":[{"StartTime":33188.0,"Position":294.0,"HyperDash":false}]},{"StartTime":33415.0,"Objects":[{"StartTime":33415.0,"Position":295.0,"HyperDash":false},{"StartTime":33467.0,"Position":281.203522,"HyperDash":false},{"StartTime":33519.0,"Position":263.38385,"HyperDash":false},{"StartTime":33571.0,"Position":270.625458,"HyperDash":false},{"StartTime":33624.0,"Position":279.906525,"HyperDash":false},{"StartTime":33676.0,"Position":253.1824,"HyperDash":false},{"StartTime":33728.0,"Position":271.372864,"HyperDash":false},{"StartTime":33780.0,"Position":265.406738,"HyperDash":false},{"StartTime":33869.0,"Position":300.795166,"HyperDash":false}]},{"StartTime":34097.0,"Objects":[{"StartTime":34097.0,"Position":406.0,"HyperDash":false}]},{"StartTime":34324.0,"Objects":[{"StartTime":34324.0,"Position":372.0,"HyperDash":false},{"StartTime":34376.0,"Position":366.75238,"HyperDash":false},{"StartTime":34428.0,"Position":319.542755,"HyperDash":false},{"StartTime":34480.0,"Position":337.707428,"HyperDash":false},{"StartTime":34533.0,"Position":311.1586,"HyperDash":false},{"StartTime":34585.0,"Position":283.844269,"HyperDash":false},{"StartTime":34637.0,"Position":264.676025,"HyperDash":false},{"StartTime":34689.0,"Position":259.952332,"HyperDash":false},{"StartTime":34778.0,"Position":219.572815,"HyperDash":false}]},{"StartTime":35006.0,"Objects":[{"StartTime":35006.0,"Position":117.0,"HyperDash":false}]},{"StartTime":35233.0,"Objects":[{"StartTime":35233.0,"Position":107.0,"HyperDash":false},{"StartTime":35285.0,"Position":123.10994,"HyperDash":false},{"StartTime":35337.0,"Position":138.9144,"HyperDash":false},{"StartTime":35389.0,"Position":142.755829,"HyperDash":false},{"StartTime":35442.0,"Position":177.3442,"HyperDash":false},{"StartTime":35494.0,"Position":180.653748,"HyperDash":false},{"StartTime":35546.0,"Position":203.782745,"HyperDash":false},{"StartTime":35598.0,"Position":209.428528,"HyperDash":false},{"StartTime":35687.0,"Position":255.847168,"HyperDash":false}]},{"StartTime":35915.0,"Objects":[{"StartTime":35915.0,"Position":370.0,"HyperDash":false}]},{"StartTime":36142.0,"Objects":[{"StartTime":36142.0,"Position":330.0,"HyperDash":false}]},{"StartTime":36597.0,"Objects":[{"StartTime":36597.0,"Position":370.0,"HyperDash":false}]},{"StartTime":36824.0,"Objects":[{"StartTime":36824.0,"Position":416.0,"HyperDash":false}]},{"StartTime":37051.0,"Objects":[{"StartTime":37051.0,"Position":406.0,"HyperDash":false},{"StartTime":37103.0,"Position":403.974335,"HyperDash":false},{"StartTime":37155.0,"Position":389.16626,"HyperDash":false},{"StartTime":37207.0,"Position":364.729828,"HyperDash":false},{"StartTime":37260.0,"Position":356.0597,"HyperDash":false},{"StartTime":37312.0,"Position":363.056549,"HyperDash":false},{"StartTime":37364.0,"Position":339.779724,"HyperDash":false},{"StartTime":37416.0,"Position":319.443939,"HyperDash":false},{"StartTime":37505.0,"Position":295.632843,"HyperDash":false}]},{"StartTime":37733.0,"Objects":[{"StartTime":37733.0,"Position":161.0,"HyperDash":false}]},{"StartTime":37961.0,"Objects":[{"StartTime":37961.0,"Position":147.0,"HyperDash":false}]},{"StartTime":38074.0,"Objects":[{"StartTime":38074.0,"Position":161.0,"HyperDash":false}]},{"StartTime":38188.0,"Objects":[{"StartTime":38188.0,"Position":147.0,"HyperDash":false}]},{"StartTime":46142.0,"Objects":[{"StartTime":46142.0,"Position":105.0,"HyperDash":false},{"StartTime":46194.0,"Position":101.3565,"HyperDash":false},{"StartTime":46246.0,"Position":117.818565,"HyperDash":false},{"StartTime":46298.0,"Position":135.426117,"HyperDash":false},{"StartTime":46351.0,"Position":146.825043,"HyperDash":false},{"StartTime":46403.0,"Position":174.897232,"HyperDash":false},{"StartTime":46455.0,"Position":178.608673,"HyperDash":false},{"StartTime":46507.0,"Position":211.715851,"HyperDash":false},{"StartTime":46596.0,"Position":242.038391,"HyperDash":false}]},{"StartTime":47051.0,"Objects":[{"StartTime":47051.0,"Position":399.0,"HyperDash":false},{"StartTime":47107.0,"Position":433.4483,"HyperDash":false},{"StartTime":47164.0,"Position":427.2428,"HyperDash":false},{"StartTime":47221.0,"Position":452.0353,"HyperDash":false},{"StartTime":47278.0,"Position":477.822449,"HyperDash":false},{"StartTime":47373.0,"Position":461.8406,"HyperDash":false},{"StartTime":47505.0,"Position":399.0,"HyperDash":false}]},{"StartTime":47961.0,"Objects":[{"StartTime":47961.0,"Position":422.0,"HyperDash":false},{"StartTime":48013.0,"Position":393.6435,"HyperDash":false},{"StartTime":48065.0,"Position":415.181427,"HyperDash":false},{"StartTime":48117.0,"Position":403.573883,"HyperDash":false},{"StartTime":48170.0,"Position":352.174957,"HyperDash":false},{"StartTime":48222.0,"Position":336.102783,"HyperDash":false},{"StartTime":48274.0,"Position":346.391327,"HyperDash":false},{"StartTime":48326.0,"Position":328.284149,"HyperDash":false},{"StartTime":48415.0,"Position":284.9616,"HyperDash":false}]},{"StartTime":48870.0,"Objects":[{"StartTime":48870.0,"Position":128.0,"HyperDash":false},{"StartTime":48926.0,"Position":123.551682,"HyperDash":false},{"StartTime":48983.0,"Position":80.7571945,"HyperDash":false},{"StartTime":49040.0,"Position":54.96469,"HyperDash":false},{"StartTime":49097.0,"Position":49.17756,"HyperDash":false},{"StartTime":49192.0,"Position":78.1594,"HyperDash":false},{"StartTime":49324.0,"Position":128.0,"HyperDash":false}]},{"StartTime":49779.0,"Objects":[{"StartTime":49779.0,"Position":252.0,"HyperDash":false},{"StartTime":49831.0,"Position":281.5043,"HyperDash":false},{"StartTime":49883.0,"Position":284.787231,"HyperDash":false},{"StartTime":49935.0,"Position":303.602631,"HyperDash":false},{"StartTime":49988.0,"Position":315.098541,"HyperDash":false},{"StartTime":50040.0,"Position":356.3944,"HyperDash":false},{"StartTime":50092.0,"Position":367.7095,"HyperDash":false},{"StartTime":50144.0,"Position":369.952545,"HyperDash":false},{"StartTime":50233.0,"Position":407.8509,"HyperDash":false}]},{"StartTime":50688.0,"Objects":[{"StartTime":50688.0,"Position":248.0,"HyperDash":false}]},{"StartTime":50915.0,"Objects":[{"StartTime":50915.0,"Position":377.0,"HyperDash":false},{"StartTime":51010.0,"Position":359.866516,"HyperDash":false},{"StartTime":51142.0,"Position":298.613647,"HyperDash":false}]},{"StartTime":51370.0,"Objects":[{"StartTime":51370.0,"Position":161.0,"HyperDash":false}]},{"StartTime":51597.0,"Objects":[{"StartTime":51597.0,"Position":159.0,"HyperDash":false},{"StartTime":51692.0,"Position":111.3809,"HyperDash":false},{"StartTime":51824.0,"Position":81.1563339,"HyperDash":false}]},{"StartTime":52051.0,"Objects":[{"StartTime":52051.0,"Position":107.0,"HyperDash":false},{"StartTime":52146.0,"Position":72.66428,"HyperDash":false},{"StartTime":52278.0,"Position":28.8123531,"HyperDash":false}]},{"StartTime":52506.0,"Objects":[{"StartTime":52506.0,"Position":75.0,"HyperDash":false},{"StartTime":52558.0,"Position":76.23376,"HyperDash":false},{"StartTime":52610.0,"Position":99.55078,"HyperDash":false},{"StartTime":52662.0,"Position":117.824188,"HyperDash":false},{"StartTime":52715.0,"Position":140.248856,"HyperDash":false},{"StartTime":52767.0,"Position":167.9607,"HyperDash":false},{"StartTime":52819.0,"Position":172.073,"HyperDash":false},{"StartTime":52871.0,"Position":216.350311,"HyperDash":false},{"StartTime":52960.0,"Position":224.446579,"HyperDash":false}]},{"StartTime":53415.0,"Objects":[{"StartTime":53415.0,"Position":413.0,"HyperDash":false}]},{"StartTime":53642.0,"Objects":[{"StartTime":53642.0,"Position":321.0,"HyperDash":false}]},{"StartTime":53870.0,"Objects":[{"StartTime":53870.0,"Position":321.0,"HyperDash":false},{"StartTime":53922.0,"Position":347.217651,"HyperDash":false},{"StartTime":53974.0,"Position":342.931427,"HyperDash":false},{"StartTime":54026.0,"Position":379.435669,"HyperDash":false},{"StartTime":54079.0,"Position":363.721619,"HyperDash":false},{"StartTime":54131.0,"Position":366.5289,"HyperDash":false},{"StartTime":54183.0,"Position":366.0941,"HyperDash":false},{"StartTime":54235.0,"Position":367.430542,"HyperDash":false},{"StartTime":54324.0,"Position":367.2075,"HyperDash":false}]},{"StartTime":54551.0,"Objects":[{"StartTime":54551.0,"Position":310.0,"HyperDash":false}]},{"StartTime":54779.0,"Objects":[{"StartTime":54779.0,"Position":222.0,"HyperDash":false}]},{"StartTime":55233.0,"Objects":[{"StartTime":55233.0,"Position":310.0,"HyperDash":false}]},{"StartTime":55461.0,"Objects":[{"StartTime":55461.0,"Position":222.0,"HyperDash":false}]},{"StartTime":55688.0,"Objects":[{"StartTime":55688.0,"Position":266.0,"HyperDash":false},{"StartTime":55740.0,"Position":250.312454,"HyperDash":false},{"StartTime":55792.0,"Position":222.151321,"HyperDash":false},{"StartTime":55844.0,"Position":229.8381,"HyperDash":false},{"StartTime":55897.0,"Position":199.311859,"HyperDash":false},{"StartTime":55949.0,"Position":190.573822,"HyperDash":false},{"StartTime":56001.0,"Position":163.5982,"HyperDash":false},{"StartTime":56053.0,"Position":126.797623,"HyperDash":false},{"StartTime":56142.0,"Position":119.985596,"HyperDash":false}]},{"StartTime":56370.0,"Objects":[{"StartTime":56370.0,"Position":70.0,"HyperDash":false}]},{"StartTime":56597.0,"Objects":[{"StartTime":56597.0,"Position":128.0,"HyperDash":false}]},{"StartTime":57051.0,"Objects":[{"StartTime":57051.0,"Position":70.0,"HyperDash":false}]},{"StartTime":57279.0,"Objects":[{"StartTime":57279.0,"Position":128.0,"HyperDash":false}]},{"StartTime":57506.0,"Objects":[{"StartTime":57506.0,"Position":99.0,"HyperDash":false},{"StartTime":57558.0,"Position":95.98298,"HyperDash":false},{"StartTime":57610.0,"Position":141.1198,"HyperDash":false},{"StartTime":57662.0,"Position":153.374634,"HyperDash":false},{"StartTime":57715.0,"Position":146.589783,"HyperDash":false},{"StartTime":57767.0,"Position":186.773819,"HyperDash":false},{"StartTime":57819.0,"Position":202.087418,"HyperDash":false},{"StartTime":57871.0,"Position":227.361313,"HyperDash":false},{"StartTime":57960.0,"Position":249.971191,"HyperDash":false}]},{"StartTime":58188.0,"Objects":[{"StartTime":58188.0,"Position":398.0,"HyperDash":false}]},{"StartTime":58415.0,"Objects":[{"StartTime":58415.0,"Position":366.0,"HyperDash":false}]},{"StartTime":58642.0,"Objects":[{"StartTime":58642.0,"Position":401.0,"HyperDash":false},{"StartTime":58737.0,"Position":372.52832,"HyperDash":false},{"StartTime":58869.0,"Position":348.93866,"HyperDash":false}]},{"StartTime":59097.0,"Objects":[{"StartTime":59097.0,"Position":203.0,"HyperDash":false}]},{"StartTime":59324.0,"Objects":[{"StartTime":59324.0,"Position":337.0,"HyperDash":false},{"StartTime":59419.0,"Position":354.740051,"HyperDash":false},{"StartTime":59551.0,"Position":364.726837,"HyperDash":false}]},{"StartTime":59779.0,"Objects":[{"StartTime":59779.0,"Position":284.0,"HyperDash":false},{"StartTime":59831.0,"Position":267.281219,"HyperDash":false},{"StartTime":59883.0,"Position":257.357849,"HyperDash":false},{"StartTime":59935.0,"Position":220.054123,"HyperDash":false},{"StartTime":59988.0,"Position":220.521576,"HyperDash":false},{"StartTime":60040.0,"Position":193.393219,"HyperDash":false},{"StartTime":60092.0,"Position":176.168411,"HyperDash":false},{"StartTime":60144.0,"Position":151.876328,"HyperDash":false},{"StartTime":60233.0,"Position":130.344528,"HyperDash":false}]},{"StartTime":60688.0,"Objects":[{"StartTime":60688.0,"Position":41.0,"HyperDash":false}]},{"StartTime":61142.0,"Objects":[{"StartTime":61142.0,"Position":191.0,"HyperDash":false},{"StartTime":61237.0,"Position":220.210571,"HyperDash":false},{"StartTime":61369.0,"Position":265.576843,"HyperDash":false}]},{"StartTime":61597.0,"Objects":[{"StartTime":61597.0,"Position":254.0,"HyperDash":false},{"StartTime":61692.0,"Position":300.210571,"HyperDash":false},{"StartTime":61824.0,"Position":328.576843,"HyperDash":false}]},{"StartTime":62051.0,"Objects":[{"StartTime":62051.0,"Position":299.0,"HyperDash":false}]},{"StartTime":62279.0,"Objects":[{"StartTime":62279.0,"Position":319.0,"HyperDash":false},{"StartTime":62374.0,"Position":304.789429,"HyperDash":false},{"StartTime":62506.0,"Position":244.423172,"HyperDash":false}]},{"StartTime":62733.0,"Objects":[{"StartTime":62733.0,"Position":102.0,"HyperDash":false}]},{"StartTime":62961.0,"Objects":[{"StartTime":62961.0,"Position":80.0,"HyperDash":false}]},{"StartTime":63188.0,"Objects":[{"StartTime":63188.0,"Position":31.0,"HyperDash":false}]},{"StartTime":63415.0,"Objects":[{"StartTime":63415.0,"Position":31.0,"HyperDash":false},{"StartTime":63471.0,"Position":15.0,"HyperDash":false},{"StartTime":63528.0,"Position":13.0,"HyperDash":false},{"StartTime":63585.0,"Position":43.0,"HyperDash":false},{"StartTime":63642.0,"Position":31.0,"HyperDash":false},{"StartTime":63737.0,"Position":38.0,"HyperDash":false},{"StartTime":63869.0,"Position":31.0,"HyperDash":false}]},{"StartTime":64324.0,"Objects":[{"StartTime":64324.0,"Position":331.0,"HyperDash":false}]},{"StartTime":64779.0,"Objects":[{"StartTime":64779.0,"Position":335.0,"HyperDash":false},{"StartTime":64874.0,"Position":315.0,"HyperDash":false},{"StartTime":65006.0,"Position":335.0,"HyperDash":false}]},{"StartTime":65233.0,"Objects":[{"StartTime":65233.0,"Position":405.0,"HyperDash":false},{"StartTime":65328.0,"Position":404.0,"HyperDash":false},{"StartTime":65460.0,"Position":405.0,"HyperDash":false}]},{"StartTime":65688.0,"Objects":[{"StartTime":65688.0,"Position":475.0,"HyperDash":false}]},{"StartTime":65915.0,"Objects":[{"StartTime":65915.0,"Position":475.0,"HyperDash":false},{"StartTime":66010.0,"Position":460.0,"HyperDash":false},{"StartTime":66142.0,"Position":475.0,"HyperDash":false}]},{"StartTime":66370.0,"Objects":[{"StartTime":66370.0,"Position":335.0,"HyperDash":false}]},{"StartTime":66597.0,"Objects":[{"StartTime":66597.0,"Position":315.0,"HyperDash":false}]},{"StartTime":66824.0,"Objects":[{"StartTime":66824.0,"Position":189.0,"HyperDash":false}]},{"StartTime":67051.0,"Objects":[{"StartTime":67051.0,"Position":219.0,"HyperDash":false}]},{"StartTime":67279.0,"Objects":[{"StartTime":67279.0,"Position":159.0,"HyperDash":false}]},{"StartTime":67506.0,"Objects":[{"StartTime":67506.0,"Position":245.0,"HyperDash":false}]},{"StartTime":67733.0,"Objects":[{"StartTime":67733.0,"Position":255.0,"HyperDash":false}]},{"StartTime":67961.0,"Objects":[{"StartTime":67961.0,"Position":329.0,"HyperDash":false},{"StartTime":68056.0,"Position":343.033264,"HyperDash":false},{"StartTime":68188.0,"Position":407.932129,"HyperDash":false}]},{"StartTime":68415.0,"Objects":[{"StartTime":68415.0,"Position":427.0,"HyperDash":false},{"StartTime":68510.0,"Position":397.966736,"HyperDash":false},{"StartTime":68642.0,"Position":348.067871,"HyperDash":false}]},{"StartTime":68870.0,"Objects":[{"StartTime":68870.0,"Position":303.0,"HyperDash":false},{"StartTime":68965.0,"Position":338.033264,"HyperDash":false},{"StartTime":69097.0,"Position":381.932129,"HyperDash":false}]},{"StartTime":69324.0,"Objects":[{"StartTime":69324.0,"Position":401.0,"HyperDash":false},{"StartTime":69419.0,"Position":384.966736,"HyperDash":false},{"StartTime":69551.0,"Position":322.067871,"HyperDash":false}]},{"StartTime":69779.0,"Objects":[{"StartTime":69779.0,"Position":186.0,"HyperDash":false}]},{"StartTime":70006.0,"Objects":[{"StartTime":70006.0,"Position":298.0,"HyperDash":false}]},{"StartTime":70233.0,"Objects":[{"StartTime":70233.0,"Position":163.0,"HyperDash":false}]},{"StartTime":70461.0,"Objects":[{"StartTime":70461.0,"Position":143.0,"HyperDash":false}]},{"StartTime":70688.0,"Objects":[{"StartTime":70688.0,"Position":84.0,"HyperDash":false},{"StartTime":70744.0,"Position":78.72694,"HyperDash":false},{"StartTime":70801.0,"Position":82.25869,"HyperDash":false},{"StartTime":70858.0,"Position":70.21074,"HyperDash":false},{"StartTime":70915.0,"Position":84.71703,"HyperDash":false},{"StartTime":70971.0,"Position":78.31016,"HyperDash":false},{"StartTime":71028.0,"Position":58.2654724,"HyperDash":false},{"StartTime":71085.0,"Position":57.63716,"HyperDash":false},{"StartTime":71142.0,"Position":84.0,"HyperDash":false},{"StartTime":71237.0,"Position":54.4390259,"HyperDash":false},{"StartTime":71369.0,"Position":84.71703,"HyperDash":false}]},{"StartTime":71597.0,"Objects":[{"StartTime":71597.0,"Position":148.0,"HyperDash":false},{"StartTime":71649.0,"Position":165.786362,"HyperDash":false},{"StartTime":71701.0,"Position":179.3862,"HyperDash":false},{"StartTime":71753.0,"Position":206.911774,"HyperDash":false},{"StartTime":71806.0,"Position":197.271149,"HyperDash":false},{"StartTime":71858.0,"Position":230.514236,"HyperDash":false},{"StartTime":71910.0,"Position":245.834518,"HyperDash":false},{"StartTime":71962.0,"Position":272.100525,"HyperDash":false},{"StartTime":72051.0,"Position":300.802856,"HyperDash":false}]},{"StartTime":72506.0,"Objects":[{"StartTime":72506.0,"Position":374.0,"HyperDash":false},{"StartTime":72558.0,"Position":376.213623,"HyperDash":false},{"StartTime":72610.0,"Position":353.6138,"HyperDash":false},{"StartTime":72662.0,"Position":320.088226,"HyperDash":false},{"StartTime":72715.0,"Position":293.728851,"HyperDash":false},{"StartTime":72767.0,"Position":297.485779,"HyperDash":false},{"StartTime":72819.0,"Position":272.165466,"HyperDash":false},{"StartTime":72871.0,"Position":247.899475,"HyperDash":false},{"StartTime":72960.0,"Position":221.197159,"HyperDash":false}]},{"StartTime":73188.0,"Objects":[{"StartTime":73188.0,"Position":77.0,"HyperDash":false}]},{"StartTime":73415.0,"Objects":[{"StartTime":73415.0,"Position":213.0,"HyperDash":false},{"StartTime":73510.0,"Position":233.974548,"HyperDash":false},{"StartTime":73642.0,"Position":279.844421,"HyperDash":false}]},{"StartTime":73870.0,"Objects":[{"StartTime":73870.0,"Position":346.0,"HyperDash":false},{"StartTime":73965.0,"Position":336.709564,"HyperDash":false},{"StartTime":74097.0,"Position":297.516541,"HyperDash":false}]},{"StartTime":74324.0,"Objects":[{"StartTime":74324.0,"Position":222.0,"HyperDash":false}]},{"StartTime":74551.0,"Objects":[{"StartTime":74551.0,"Position":282.0,"HyperDash":false}]},{"StartTime":74779.0,"Objects":[{"StartTime":74779.0,"Position":252.0,"HyperDash":false},{"StartTime":74835.0,"Position":222.93634,"HyperDash":false},{"StartTime":74892.0,"Position":231.971436,"HyperDash":false},{"StartTime":74949.0,"Position":179.985031,"HyperDash":false},{"StartTime":75006.0,"Position":173.674133,"HyperDash":false},{"StartTime":75101.0,"Position":204.278076,"HyperDash":false},{"StartTime":75233.0,"Position":252.0,"HyperDash":false}]},{"StartTime":75688.0,"Objects":[{"StartTime":75688.0,"Position":194.0,"HyperDash":false},{"StartTime":75744.0,"Position":188.93634,"HyperDash":false},{"StartTime":75801.0,"Position":159.971436,"HyperDash":false},{"StartTime":75858.0,"Position":124.985031,"HyperDash":false},{"StartTime":75915.0,"Position":115.674141,"HyperDash":false},{"StartTime":76010.0,"Position":154.278076,"HyperDash":false},{"StartTime":76142.0,"Position":194.0,"HyperDash":false}]},{"StartTime":76597.0,"Objects":[{"StartTime":76597.0,"Position":347.0,"HyperDash":false}]},{"StartTime":76824.0,"Objects":[{"StartTime":76824.0,"Position":327.0,"HyperDash":false}]},{"StartTime":77051.0,"Objects":[{"StartTime":77051.0,"Position":351.0,"HyperDash":false}]},{"StartTime":77506.0,"Objects":[{"StartTime":77506.0,"Position":448.0,"HyperDash":false}]},{"StartTime":77733.0,"Objects":[{"StartTime":77733.0,"Position":368.0,"HyperDash":false}]},{"StartTime":77961.0,"Objects":[{"StartTime":77961.0,"Position":242.0,"HyperDash":false}]},{"StartTime":78415.0,"Objects":[{"StartTime":78415.0,"Position":50.0,"HyperDash":false}]},{"StartTime":78642.0,"Objects":[{"StartTime":78642.0,"Position":118.0,"HyperDash":false},{"StartTime":78737.0,"Position":102.772095,"HyperDash":false},{"StartTime":78869.0,"Position":62.0753326,"HyperDash":false}]},{"StartTime":79324.0,"Objects":[{"StartTime":79324.0,"Position":218.0,"HyperDash":false},{"StartTime":79419.0,"Position":253.274826,"HyperDash":false},{"StartTime":79551.0,"Position":294.3989,"HyperDash":false}]},{"StartTime":79779.0,"Objects":[{"StartTime":79779.0,"Position":443.0,"HyperDash":false}]},{"StartTime":80233.0,"Objects":[{"StartTime":80233.0,"Position":286.0,"HyperDash":false},{"StartTime":80289.0,"Position":301.1139,"HyperDash":false},{"StartTime":80346.0,"Position":277.211975,"HyperDash":false},{"StartTime":80403.0,"Position":273.310028,"HyperDash":false},{"StartTime":80460.0,"Position":282.4081,"HyperDash":false},{"StartTime":80555.0,"Position":290.911316,"HyperDash":false},{"StartTime":80687.0,"Position":286.0,"HyperDash":false}]},{"StartTime":81142.0,"Objects":[{"StartTime":81142.0,"Position":427.0,"HyperDash":false}]},{"StartTime":81370.0,"Objects":[{"StartTime":81370.0,"Position":423.0,"HyperDash":false}]},{"StartTime":81597.0,"Objects":[{"StartTime":81597.0,"Position":427.0,"HyperDash":false},{"StartTime":81653.0,"Position":415.357849,"HyperDash":false},{"StartTime":81710.0,"Position":429.752075,"HyperDash":false},{"StartTime":81824.0,"Position":427.0,"HyperDash":false}]},{"StartTime":82051.0,"Objects":[{"StartTime":82051.0,"Position":411.0,"HyperDash":false}]},{"StartTime":82279.0,"Objects":[{"StartTime":82279.0,"Position":301.0,"HyperDash":false}]},{"StartTime":82506.0,"Objects":[{"StartTime":82506.0,"Position":285.0,"HyperDash":false},{"StartTime":82558.0,"Position":275.960876,"HyperDash":false},{"StartTime":82610.0,"Position":258.1504,"HyperDash":false},{"StartTime":82662.0,"Position":240.960434,"HyperDash":false},{"StartTime":82715.0,"Position":213.29361,"HyperDash":false},{"StartTime":82767.0,"Position":177.039841,"HyperDash":false},{"StartTime":82819.0,"Position":192.027191,"HyperDash":false},{"StartTime":82871.0,"Position":162.507034,"HyperDash":false},{"StartTime":82960.0,"Position":132.24411,"HyperDash":false}]},{"StartTime":83188.0,"Objects":[{"StartTime":83188.0,"Position":246.0,"HyperDash":false}]},{"StartTime":83415.0,"Objects":[{"StartTime":83415.0,"Position":267.0,"HyperDash":false},{"StartTime":83467.0,"Position":277.3109,"HyperDash":false},{"StartTime":83519.0,"Position":280.6682,"HyperDash":false},{"StartTime":83571.0,"Position":302.941681,"HyperDash":false},{"StartTime":83624.0,"Position":298.1128,"HyperDash":false},{"StartTime":83676.0,"Position":291.2743,"HyperDash":false},{"StartTime":83728.0,"Position":290.638519,"HyperDash":false},{"StartTime":83780.0,"Position":254.4002,"HyperDash":false},{"StartTime":83869.0,"Position":250.128326,"HyperDash":false}]},{"StartTime":84097.0,"Objects":[{"StartTime":84097.0,"Position":161.0,"HyperDash":false}]},{"StartTime":84324.0,"Objects":[{"StartTime":84324.0,"Position":188.0,"HyperDash":false},{"StartTime":84376.0,"Position":211.223328,"HyperDash":false},{"StartTime":84428.0,"Position":235.527512,"HyperDash":false},{"StartTime":84480.0,"Position":225.64093,"HyperDash":false},{"StartTime":84533.0,"Position":278.681366,"HyperDash":false},{"StartTime":84585.0,"Position":283.741333,"HyperDash":false},{"StartTime":84637.0,"Position":282.889923,"HyperDash":false},{"StartTime":84689.0,"Position":320.765961,"HyperDash":false},{"StartTime":84778.0,"Position":329.839569,"HyperDash":false}]},{"StartTime":85006.0,"Objects":[{"StartTime":85006.0,"Position":177.0,"HyperDash":false}]},{"StartTime":85233.0,"Objects":[{"StartTime":85233.0,"Position":177.0,"HyperDash":false},{"StartTime":85285.0,"Position":165.201,"HyperDash":false},{"StartTime":85337.0,"Position":182.838409,"HyperDash":false},{"StartTime":85389.0,"Position":172.951111,"HyperDash":false},{"StartTime":85442.0,"Position":165.6638,"HyperDash":false},{"StartTime":85494.0,"Position":172.542755,"HyperDash":false},{"StartTime":85546.0,"Position":188.28334,"HyperDash":false},{"StartTime":85598.0,"Position":236.294235,"HyperDash":false},{"StartTime":85687.0,"Position":249.329514,"HyperDash":false}]},{"StartTime":85915.0,"Objects":[{"StartTime":85915.0,"Position":368.0,"HyperDash":false}]},{"StartTime":86142.0,"Objects":[{"StartTime":86142.0,"Position":404.0,"HyperDash":false},{"StartTime":86194.0,"Position":401.8277,"HyperDash":false},{"StartTime":86246.0,"Position":454.150146,"HyperDash":false},{"StartTime":86298.0,"Position":426.349945,"HyperDash":false},{"StartTime":86351.0,"Position":450.306671,"HyperDash":false},{"StartTime":86403.0,"Position":444.439728,"HyperDash":false},{"StartTime":86455.0,"Position":429.120422,"HyperDash":false},{"StartTime":86507.0,"Position":418.0135,"HyperDash":false},{"StartTime":86596.0,"Position":405.457642,"HyperDash":false}]},{"StartTime":86824.0,"Objects":[{"StartTime":86824.0,"Position":272.0,"HyperDash":false}]},{"StartTime":87051.0,"Objects":[{"StartTime":87051.0,"Position":220.0,"HyperDash":false}]},{"StartTime":87506.0,"Objects":[{"StartTime":87506.0,"Position":272.0,"HyperDash":false}]},{"StartTime":87733.0,"Objects":[{"StartTime":87733.0,"Position":192.0,"HyperDash":false}]},{"StartTime":87961.0,"Objects":[{"StartTime":87961.0,"Position":168.0,"HyperDash":false},{"StartTime":88013.0,"Position":180.07048,"HyperDash":false},{"StartTime":88065.0,"Position":145.3697,"HyperDash":false},{"StartTime":88117.0,"Position":181.001678,"HyperDash":false},{"StartTime":88170.0,"Position":158.001877,"HyperDash":false},{"StartTime":88222.0,"Position":177.868271,"HyperDash":false},{"StartTime":88274.0,"Position":202.302979,"HyperDash":false},{"StartTime":88326.0,"Position":215.876221,"HyperDash":false},{"StartTime":88415.0,"Position":224.187881,"HyperDash":false}]},{"StartTime":88642.0,"Objects":[{"StartTime":88642.0,"Position":363.0,"HyperDash":false}]},{"StartTime":88870.0,"Objects":[{"StartTime":88870.0,"Position":393.0,"HyperDash":false}]},{"StartTime":88983.0,"Objects":[{"StartTime":88983.0,"Position":363.0,"HyperDash":false}]},{"StartTime":89097.0,"Objects":[{"StartTime":89097.0,"Position":393.0,"HyperDash":false}]},{"StartTime":93415.0,"Objects":[{"StartTime":93415.0,"Position":330.0,"HyperDash":false},{"StartTime":93500.0,"Position":362.93335,"HyperDash":false},{"StartTime":93585.0,"Position":384.5453,"HyperDash":false},{"StartTime":93670.0,"Position":408.46228,"HyperDash":false},{"StartTime":93755.0,"Position":448.525055,"HyperDash":false},{"StartTime":93831.0,"Position":430.9859,"HyperDash":false},{"StartTime":93907.0,"Position":391.21936,"HyperDash":false},{"StartTime":93983.0,"Position":356.6329,"HyperDash":false},{"StartTime":94096.0,"Position":330.0,"HyperDash":false}]},{"StartTime":94324.0,"Objects":[{"StartTime":94324.0,"Position":55.0,"HyperDash":false}]},{"StartTime":94552.0,"Objects":[{"StartTime":94552.0,"Position":181.0,"HyperDash":false},{"StartTime":94665.0,"Position":145.222916,"HyperDash":false}]},{"StartTime":95233.0,"Objects":[{"StartTime":95233.0,"Position":181.0,"HyperDash":false},{"StartTime":95318.0,"Position":141.066635,"HyperDash":false},{"StartTime":95403.0,"Position":137.4547,"HyperDash":false},{"StartTime":95488.0,"Position":96.53772,"HyperDash":false},{"StartTime":95573.0,"Position":62.47494,"HyperDash":false},{"StartTime":95649.0,"Position":96.0141144,"HyperDash":false},{"StartTime":95725.0,"Position":128.78064,"HyperDash":false},{"StartTime":95801.0,"Position":133.367081,"HyperDash":false},{"StartTime":95914.0,"Position":181.0,"HyperDash":false}]},{"StartTime":96142.0,"Objects":[{"StartTime":96142.0,"Position":456.0,"HyperDash":false}]},{"StartTime":96370.0,"Objects":[{"StartTime":96370.0,"Position":330.0,"HyperDash":false},{"StartTime":96483.0,"Position":365.7771,"HyperDash":false}]},{"StartTime":97052.0,"Objects":[{"StartTime":97052.0,"Position":330.0,"HyperDash":false},{"StartTime":97137.0,"Position":369.93335,"HyperDash":false},{"StartTime":97222.0,"Position":380.5453,"HyperDash":false},{"StartTime":97307.0,"Position":411.46228,"HyperDash":false},{"StartTime":97392.0,"Position":448.525055,"HyperDash":false},{"StartTime":97468.0,"Position":429.9859,"HyperDash":false},{"StartTime":97544.0,"Position":391.21936,"HyperDash":false},{"StartTime":97620.0,"Position":384.6329,"HyperDash":false},{"StartTime":97733.0,"Position":330.0,"HyperDash":false}]},{"StartTime":97961.0,"Objects":[{"StartTime":97961.0,"Position":55.0,"HyperDash":false}]},{"StartTime":98188.0,"Objects":[{"StartTime":98188.0,"Position":181.0,"HyperDash":false},{"StartTime":98301.0,"Position":145.222916,"HyperDash":false}]},{"StartTime":98870.0,"Objects":[{"StartTime":98870.0,"Position":181.0,"HyperDash":false},{"StartTime":98955.0,"Position":139.066635,"HyperDash":false},{"StartTime":99040.0,"Position":124.4547,"HyperDash":false},{"StartTime":99125.0,"Position":111.53772,"HyperDash":false},{"StartTime":99210.0,"Position":62.47494,"HyperDash":false},{"StartTime":99286.0,"Position":89.0141144,"HyperDash":false},{"StartTime":99362.0,"Position":121.780647,"HyperDash":false},{"StartTime":99438.0,"Position":125.367081,"HyperDash":false},{"StartTime":99551.0,"Position":181.0,"HyperDash":false}]},{"StartTime":99779.0,"Objects":[{"StartTime":99779.0,"Position":456.0,"HyperDash":false}]},{"StartTime":100006.0,"Objects":[{"StartTime":100006.0,"Position":330.0,"HyperDash":false},{"StartTime":100119.0,"Position":365.7771,"HyperDash":false}]},{"StartTime":100688.0,"Objects":[{"StartTime":100688.0,"Position":454.0,"HyperDash":false},{"StartTime":100801.0,"Position":414.608643,"HyperDash":false}]},{"StartTime":101029.0,"Objects":[{"StartTime":101029.0,"Position":335.0,"HyperDash":false},{"StartTime":101142.0,"Position":295.465118,"HyperDash":false}]},{"StartTime":101370.0,"Objects":[{"StartTime":101370.0,"Position":162.0,"HyperDash":false}]},{"StartTime":101597.0,"Objects":[{"StartTime":101597.0,"Position":137.0,"HyperDash":false},{"StartTime":101692.0,"Position":96.96697,"HyperDash":false},{"StartTime":101824.0,"Position":57.5450439,"HyperDash":false}]},{"StartTime":101938.0,"Objects":[{"StartTime":101938.0,"Position":84.0,"HyperDash":false}]},{"StartTime":102506.0,"Objects":[{"StartTime":102506.0,"Position":57.0,"HyperDash":false},{"StartTime":102619.0,"Position":96.39134,"HyperDash":false}]},{"StartTime":102847.0,"Objects":[{"StartTime":102847.0,"Position":176.0,"HyperDash":false},{"StartTime":102960.0,"Position":215.520477,"HyperDash":false}]},{"StartTime":103188.0,"Objects":[{"StartTime":103188.0,"Position":350.0,"HyperDash":false}]},{"StartTime":103415.0,"Objects":[{"StartTime":103415.0,"Position":374.0,"HyperDash":false},{"StartTime":103510.0,"Position":415.03302,"HyperDash":false},{"StartTime":103642.0,"Position":453.454956,"HyperDash":false}]},{"StartTime":103756.0,"Objects":[{"StartTime":103756.0,"Position":427.0,"HyperDash":false}]},{"StartTime":104324.0,"Objects":[{"StartTime":104324.0,"Position":454.0,"HyperDash":false},{"StartTime":104437.0,"Position":414.608643,"HyperDash":false}]},{"StartTime":104665.0,"Objects":[{"StartTime":104665.0,"Position":335.0,"HyperDash":false},{"StartTime":104778.0,"Position":295.465118,"HyperDash":false}]},{"StartTime":105006.0,"Objects":[{"StartTime":105006.0,"Position":162.0,"HyperDash":false}]},{"StartTime":105120.0,"Objects":[{"StartTime":105120.0,"Position":190.0,"HyperDash":false}]},{"StartTime":105233.0,"Objects":[{"StartTime":105233.0,"Position":137.0,"HyperDash":false},{"StartTime":105328.0,"Position":83.96697,"HyperDash":false},{"StartTime":105460.0,"Position":57.5450439,"HyperDash":false}]},{"StartTime":105574.0,"Objects":[{"StartTime":105574.0,"Position":84.0,"HyperDash":false}]},{"StartTime":106142.0,"Objects":[{"StartTime":106142.0,"Position":57.0,"HyperDash":false},{"StartTime":106255.0,"Position":96.39134,"HyperDash":false}]},{"StartTime":106483.0,"Objects":[{"StartTime":106483.0,"Position":176.0,"HyperDash":false},{"StartTime":106596.0,"Position":215.520477,"HyperDash":false}]},{"StartTime":106824.0,"Objects":[{"StartTime":106824.0,"Position":295.0,"HyperDash":false},{"StartTime":106904.0,"Position":306.2746,"HyperDash":false},{"StartTime":106985.0,"Position":352.66098,"HyperDash":false},{"StartTime":107065.0,"Position":389.650146,"HyperDash":false},{"StartTime":107146.0,"Position":414.777618,"HyperDash":false},{"StartTime":107227.0,"Position":392.217163,"HyperDash":false},{"StartTime":107307.0,"Position":354.003235,"HyperDash":false},{"StartTime":107388.0,"Position":321.594,"HyperDash":false},{"StartTime":107505.0,"Position":294.390747,"HyperDash":false}]},{"StartTime":115233.0,"Objects":[{"StartTime":115233.0,"Position":114.0,"HyperDash":false},{"StartTime":115285.0,"Position":143.939957,"HyperDash":false},{"StartTime":115337.0,"Position":150.324554,"HyperDash":false},{"StartTime":115389.0,"Position":183.259644,"HyperDash":false},{"StartTime":115442.0,"Position":188.794647,"HyperDash":false},{"StartTime":115494.0,"Position":195.08873,"HyperDash":false},{"StartTime":115546.0,"Position":237.411819,"HyperDash":false},{"StartTime":115598.0,"Position":240.698227,"HyperDash":false},{"StartTime":115687.0,"Position":269.692047,"HyperDash":false}]},{"StartTime":115915.0,"Objects":[{"StartTime":115915.0,"Position":413.0,"HyperDash":false}]},{"StartTime":116142.0,"Objects":[{"StartTime":116142.0,"Position":419.0,"HyperDash":false},{"StartTime":116198.0,"Position":419.6598,"HyperDash":false},{"StartTime":116255.0,"Position":457.3915,"HyperDash":false},{"StartTime":116312.0,"Position":466.4778,"HyperDash":false},{"StartTime":116369.0,"Position":449.986969,"HyperDash":false},{"StartTime":116464.0,"Position":432.831818,"HyperDash":false},{"StartTime":116596.0,"Position":419.0,"HyperDash":false}]},{"StartTime":117052.0,"Objects":[{"StartTime":117052.0,"Position":366.0,"HyperDash":false},{"StartTime":117147.0,"Position":351.721741,"HyperDash":false},{"StartTime":117279.0,"Position":295.245026,"HyperDash":false}]},{"StartTime":117506.0,"Objects":[{"StartTime":117506.0,"Position":157.0,"HyperDash":false}]},{"StartTime":117733.0,"Objects":[{"StartTime":117733.0,"Position":141.0,"HyperDash":false}]},{"StartTime":117961.0,"Objects":[{"StartTime":117961.0,"Position":84.0,"HyperDash":false},{"StartTime":118017.0,"Position":70.0,"HyperDash":false},{"StartTime":118074.0,"Position":100.0,"HyperDash":false},{"StartTime":118131.0,"Position":96.0,"HyperDash":false},{"StartTime":118188.0,"Position":84.0,"HyperDash":false},{"StartTime":118244.0,"Position":72.0,"HyperDash":false},{"StartTime":118301.0,"Position":95.0,"HyperDash":false},{"StartTime":118358.0,"Position":100.0,"HyperDash":false},{"StartTime":118415.0,"Position":84.0,"HyperDash":false},{"StartTime":118510.0,"Position":103.0,"HyperDash":false},{"StartTime":118642.0,"Position":84.0,"HyperDash":false}]},{"StartTime":118870.0,"Objects":[{"StartTime":118870.0,"Position":86.0,"HyperDash":false}]},{"StartTime":119097.0,"Objects":[{"StartTime":119097.0,"Position":224.0,"HyperDash":false}]},{"StartTime":119324.0,"Objects":[{"StartTime":119324.0,"Position":226.0,"HyperDash":false}]},{"StartTime":119552.0,"Objects":[{"StartTime":119552.0,"Position":366.0,"HyperDash":false}]},{"StartTime":119779.0,"Objects":[{"StartTime":119779.0,"Position":368.0,"HyperDash":false},{"StartTime":119835.0,"Position":397.255524,"HyperDash":false},{"StartTime":119892.0,"Position":406.27597,"HyperDash":false},{"StartTime":119949.0,"Position":410.2929,"HyperDash":false},{"StartTime":120006.0,"Position":446.9768,"HyperDash":false},{"StartTime":120101.0,"Position":415.96817,"HyperDash":false},{"StartTime":120233.0,"Position":368.0,"HyperDash":false}]},{"StartTime":120688.0,"Objects":[{"StartTime":120688.0,"Position":407.0,"HyperDash":false}]},{"StartTime":120915.0,"Objects":[{"StartTime":120915.0,"Position":321.0,"HyperDash":false}]},{"StartTime":121142.0,"Objects":[{"StartTime":121142.0,"Position":286.0,"HyperDash":false},{"StartTime":121194.0,"Position":262.810974,"HyperDash":false},{"StartTime":121246.0,"Position":235.4965,"HyperDash":false},{"StartTime":121298.0,"Position":223.241028,"HyperDash":false},{"StartTime":121351.0,"Position":219.863861,"HyperDash":false},{"StartTime":121403.0,"Position":209.2498,"HyperDash":false},{"StartTime":121455.0,"Position":171.249588,"HyperDash":false},{"StartTime":121507.0,"Position":177.110733,"HyperDash":false},{"StartTime":121596.0,"Position":137.418732,"HyperDash":false}]},{"StartTime":121824.0,"Objects":[{"StartTime":121824.0,"Position":78.0,"HyperDash":false}]},{"StartTime":122052.0,"Objects":[{"StartTime":122052.0,"Position":102.0,"HyperDash":false},{"StartTime":122147.0,"Position":99.41432,"HyperDash":false},{"StartTime":122279.0,"Position":141.1235,"HyperDash":false}]},{"StartTime":122506.0,"Objects":[{"StartTime":122506.0,"Position":187.0,"HyperDash":false},{"StartTime":122558.0,"Position":192.496933,"HyperDash":false},{"StartTime":122610.0,"Position":237.792938,"HyperDash":false},{"StartTime":122662.0,"Position":249.932373,"HyperDash":false},{"StartTime":122715.0,"Position":261.228668,"HyperDash":false},{"StartTime":122767.0,"Position":286.120331,"HyperDash":false},{"StartTime":122819.0,"Position":293.076569,"HyperDash":false},{"StartTime":122871.0,"Position":298.186584,"HyperDash":false},{"StartTime":122960.0,"Position":344.480072,"HyperDash":false}]},{"StartTime":123188.0,"Objects":[{"StartTime":123188.0,"Position":450.0,"HyperDash":false}]},{"StartTime":123415.0,"Objects":[{"StartTime":123415.0,"Position":342.0,"HyperDash":false},{"StartTime":123467.0,"Position":304.888275,"HyperDash":false},{"StartTime":123519.0,"Position":292.63266,"HyperDash":false},{"StartTime":123571.0,"Position":276.625946,"HyperDash":false},{"StartTime":123624.0,"Position":263.464172,"HyperDash":false},{"StartTime":123676.0,"Position":249.683212,"HyperDash":false},{"StartTime":123728.0,"Position":225.779449,"HyperDash":false},{"StartTime":123780.0,"Position":234.624741,"HyperDash":false},{"StartTime":123869.0,"Position":184.843155,"HyperDash":false}]},{"StartTime":124097.0,"Objects":[{"StartTime":124097.0,"Position":52.0,"HyperDash":false}]},{"StartTime":124324.0,"Objects":[{"StartTime":124324.0,"Position":184.0,"HyperDash":false},{"StartTime":124376.0,"Position":195.443817,"HyperDash":false},{"StartTime":124428.0,"Position":216.739166,"HyperDash":false},{"StartTime":124480.0,"Position":252.924118,"HyperDash":false},{"StartTime":124533.0,"Position":262.274628,"HyperDash":false},{"StartTime":124585.0,"Position":290.18573,"HyperDash":false},{"StartTime":124637.0,"Position":307.118835,"HyperDash":false},{"StartTime":124689.0,"Position":304.171661,"HyperDash":false},{"StartTime":124778.0,"Position":341.434662,"HyperDash":false}]},{"StartTime":125006.0,"Objects":[{"StartTime":125006.0,"Position":437.0,"HyperDash":false}]},{"StartTime":125233.0,"Objects":[{"StartTime":125233.0,"Position":474.0,"HyperDash":false},{"StartTime":125328.0,"Position":482.109436,"HyperDash":false},{"StartTime":125460.0,"Position":475.3147,"HyperDash":false}]},{"StartTime":125688.0,"Objects":[{"StartTime":125688.0,"Position":437.0,"HyperDash":false},{"StartTime":125783.0,"Position":440.578949,"HyperDash":false},{"StartTime":125915.0,"Position":435.0608,"HyperDash":false}]},{"StartTime":126142.0,"Objects":[{"StartTime":126142.0,"Position":506.0,"HyperDash":false},{"StartTime":126194.0,"Position":472.674347,"HyperDash":false},{"StartTime":126246.0,"Position":456.3487,"HyperDash":false},{"StartTime":126298.0,"Position":448.023041,"HyperDash":false},{"StartTime":126351.0,"Position":433.344971,"HyperDash":false},{"StartTime":126403.0,"Position":411.019348,"HyperDash":false},{"StartTime":126455.0,"Position":390.693665,"HyperDash":false},{"StartTime":126507.0,"Position":380.368042,"HyperDash":false},{"StartTime":126596.0,"Position":346.003,"HyperDash":false}]},{"StartTime":127052.0,"Objects":[{"StartTime":127052.0,"Position":28.0,"HyperDash":false},{"StartTime":127104.0,"Position":56.3256531,"HyperDash":false},{"StartTime":127156.0,"Position":67.6513062,"HyperDash":false},{"StartTime":127208.0,"Position":71.97695,"HyperDash":false},{"StartTime":127261.0,"Position":111.655014,"HyperDash":false},{"StartTime":127313.0,"Position":136.980667,"HyperDash":false},{"StartTime":127365.0,"Position":146.30632,"HyperDash":false},{"StartTime":127417.0,"Position":174.631958,"HyperDash":false},{"StartTime":127506.0,"Position":187.997025,"HyperDash":false}]},{"StartTime":127733.0,"Objects":[{"StartTime":127733.0,"Position":342.0,"HyperDash":false}]},{"StartTime":127961.0,"Objects":[{"StartTime":127961.0,"Position":226.0,"HyperDash":false},{"StartTime":128017.0,"Position":203.38623,"HyperDash":false},{"StartTime":128074.0,"Position":210.367325,"HyperDash":false},{"StartTime":128131.0,"Position":229.320847,"HyperDash":false},{"StartTime":128188.0,"Position":223.423782,"HyperDash":false},{"StartTime":128283.0,"Position":206.484131,"HyperDash":false},{"StartTime":128415.0,"Position":226.0,"HyperDash":false}]},{"StartTime":128642.0,"Objects":[{"StartTime":128642.0,"Position":302.0,"HyperDash":false}]},{"StartTime":128870.0,"Objects":[{"StartTime":128870.0,"Position":314.0,"HyperDash":false}]},{"StartTime":129097.0,"Objects":[{"StartTime":129097.0,"Position":302.0,"HyperDash":false}]},{"StartTime":129324.0,"Objects":[{"StartTime":129324.0,"Position":314.0,"HyperDash":false}]},{"StartTime":129779.0,"Objects":[{"StartTime":129779.0,"Position":308.0,"HyperDash":false},{"StartTime":129835.0,"Position":334.61377,"HyperDash":false},{"StartTime":129892.0,"Position":326.6327,"HyperDash":false},{"StartTime":129949.0,"Position":328.679138,"HyperDash":false},{"StartTime":130006.0,"Position":310.576233,"HyperDash":false},{"StartTime":130101.0,"Position":331.515869,"HyperDash":false},{"StartTime":130233.0,"Position":308.0,"HyperDash":false}]},{"StartTime":130461.0,"Objects":[{"StartTime":130461.0,"Position":232.0,"HyperDash":false}]},{"StartTime":130688.0,"Objects":[{"StartTime":130688.0,"Position":220.0,"HyperDash":false}]},{"StartTime":130915.0,"Objects":[{"StartTime":130915.0,"Position":232.0,"HyperDash":false}]},{"StartTime":131142.0,"Objects":[{"StartTime":131142.0,"Position":220.0,"HyperDash":false}]},{"StartTime":131597.0,"Objects":[{"StartTime":131597.0,"Position":18.0,"HyperDash":false}]},{"StartTime":132052.0,"Objects":[{"StartTime":132052.0,"Position":278.0,"HyperDash":false}]},{"StartTime":132506.0,"Objects":[{"StartTime":132506.0,"Position":326.0,"HyperDash":false}]},{"StartTime":132961.0,"Objects":[{"StartTime":132961.0,"Position":430.0,"HyperDash":false}]},{"StartTime":133415.0,"Objects":[{"StartTime":133415.0,"Position":358.0,"HyperDash":false}]},{"StartTime":133870.0,"Objects":[{"StartTime":133870.0,"Position":122.0,"HyperDash":false}]},{"StartTime":134324.0,"Objects":[{"StartTime":134324.0,"Position":119.0,"HyperDash":false},{"StartTime":134419.0,"Position":68.98176,"HyperDash":false},{"StartTime":134551.0,"Position":42.8232956,"HyperDash":false}]},{"StartTime":134779.0,"Objects":[{"StartTime":134779.0,"Position":113.0,"HyperDash":false}]},{"StartTime":135233.0,"Objects":[{"StartTime":135233.0,"Position":243.0,"HyperDash":false}]},{"StartTime":135688.0,"Objects":[{"StartTime":135688.0,"Position":251.0,"HyperDash":false}]},{"StartTime":136142.0,"Objects":[{"StartTime":136142.0,"Position":406.0,"HyperDash":false}]},{"StartTime":136597.0,"Objects":[{"StartTime":136597.0,"Position":484.0,"HyperDash":false}]},{"StartTime":137052.0,"Objects":[{"StartTime":137052.0,"Position":352.0,"HyperDash":false}]},{"StartTime":137506.0,"Objects":[{"StartTime":137506.0,"Position":164.0,"HyperDash":false}]},{"StartTime":137961.0,"Objects":[{"StartTime":137961.0,"Position":178.0,"HyperDash":false},{"StartTime":138056.0,"Position":131.390686,"HyperDash":false},{"StartTime":138188.0,"Position":107.408012,"HyperDash":false}]},{"StartTime":138415.0,"Objects":[{"StartTime":138415.0,"Position":129.0,"HyperDash":false}]},{"StartTime":138870.0,"Objects":[{"StartTime":138870.0,"Position":247.0,"HyperDash":false},{"StartTime":138965.0,"Position":268.5533,"HyperDash":false},{"StartTime":139097.0,"Position":323.543732,"HyperDash":false}]},{"StartTime":139324.0,"Objects":[{"StartTime":139324.0,"Position":469.0,"HyperDash":false}]},{"StartTime":139779.0,"Objects":[{"StartTime":139779.0,"Position":309.0,"HyperDash":false},{"StartTime":139874.0,"Position":267.4467,"HyperDash":false},{"StartTime":140006.0,"Position":232.456268,"HyperDash":false}]},{"StartTime":140233.0,"Objects":[{"StartTime":140233.0,"Position":87.0,"HyperDash":false}]},{"StartTime":140688.0,"Objects":[{"StartTime":140688.0,"Position":109.0,"HyperDash":false}]},{"StartTime":140915.0,"Objects":[{"StartTime":140915.0,"Position":241.0,"HyperDash":false}]},{"StartTime":141142.0,"Objects":[{"StartTime":141142.0,"Position":243.0,"HyperDash":false}]},{"StartTime":141370.0,"Objects":[{"StartTime":141370.0,"Position":305.0,"HyperDash":false}]},{"StartTime":141597.0,"Objects":[{"StartTime":141597.0,"Position":349.0,"HyperDash":false}]},{"StartTime":141824.0,"Objects":[{"StartTime":141824.0,"Position":449.0,"HyperDash":false}]},{"StartTime":142052.0,"Objects":[{"StartTime":142052.0,"Position":493.0,"HyperDash":false}]},{"StartTime":142506.0,"Objects":[{"StartTime":142506.0,"Position":401.0,"HyperDash":false},{"StartTime":142562.0,"Position":403.0,"HyperDash":false},{"StartTime":142619.0,"Position":420.0,"HyperDash":false},{"StartTime":142676.0,"Position":407.0,"HyperDash":false},{"StartTime":142733.0,"Position":401.0,"HyperDash":false},{"StartTime":142828.0,"Position":411.0,"HyperDash":false},{"StartTime":142960.0,"Position":401.0,"HyperDash":false}]},{"StartTime":143415.0,"Objects":[{"StartTime":143415.0,"Position":246.0,"HyperDash":false},{"StartTime":143471.0,"Position":242.0,"HyperDash":false},{"StartTime":143528.0,"Position":264.0,"HyperDash":false},{"StartTime":143585.0,"Position":252.0,"HyperDash":false},{"StartTime":143642.0,"Position":246.0,"HyperDash":false},{"StartTime":143737.0,"Position":262.0,"HyperDash":false},{"StartTime":143869.0,"Position":246.0,"HyperDash":false}]},{"StartTime":144324.0,"Objects":[{"StartTime":144324.0,"Position":91.0,"HyperDash":false}]},{"StartTime":144552.0,"Objects":[{"StartTime":144552.0,"Position":45.0,"HyperDash":false}]},{"StartTime":144779.0,"Objects":[{"StartTime":144779.0,"Position":135.0,"HyperDash":false}]},{"StartTime":145006.0,"Objects":[{"StartTime":145006.0,"Position":45.0,"HyperDash":false}]},{"StartTime":145233.0,"Objects":[{"StartTime":145233.0,"Position":133.0,"HyperDash":false}]},{"StartTime":145688.0,"Objects":[{"StartTime":145688.0,"Position":337.0,"HyperDash":false}]},{"StartTime":145915.0,"Objects":[{"StartTime":145915.0,"Position":277.0,"HyperDash":false}]},{"StartTime":146142.0,"Objects":[{"StartTime":146142.0,"Position":386.0,"HyperDash":false}]},{"StartTime":146597.0,"Objects":[{"StartTime":146597.0,"Position":406.0,"HyperDash":false}]},{"StartTime":146824.0,"Objects":[{"StartTime":146824.0,"Position":320.0,"HyperDash":false}]},{"StartTime":147051.0,"Objects":[{"StartTime":147051.0,"Position":378.0,"HyperDash":false}]},{"StartTime":147506.0,"Objects":[{"StartTime":147506.0,"Position":320.0,"HyperDash":false}]},{"StartTime":147733.0,"Objects":[{"StartTime":147733.0,"Position":282.0,"HyperDash":false},{"StartTime":147828.0,"Position":269.560242,"HyperDash":false},{"StartTime":147960.0,"Position":205.662415,"HyperDash":false}]},{"StartTime":148415.0,"Objects":[{"StartTime":148415.0,"Position":234.0,"HyperDash":false},{"StartTime":148510.0,"Position":236.789261,"HyperDash":false},{"StartTime":148642.0,"Position":226.947067,"HyperDash":false}]},{"StartTime":148870.0,"Objects":[{"StartTime":148870.0,"Position":194.0,"HyperDash":false}]},{"StartTime":149324.0,"Objects":[{"StartTime":149324.0,"Position":88.0,"HyperDash":false},{"StartTime":149380.0,"Position":61.61062,"HyperDash":false},{"StartTime":149437.0,"Position":75.7172852,"HyperDash":false},{"StartTime":149494.0,"Position":64.72825,"HyperDash":false},{"StartTime":149551.0,"Position":71.9050446,"HyperDash":false},{"StartTime":149646.0,"Position":67.47814,"HyperDash":false},{"StartTime":149778.0,"Position":88.0,"HyperDash":false}]},{"StartTime":150233.0,"Objects":[{"StartTime":150233.0,"Position":120.0,"HyperDash":false},{"StartTime":150289.0,"Position":137.763626,"HyperDash":false},{"StartTime":150346.0,"Position":141.788849,"HyperDash":false},{"StartTime":150403.0,"Position":166.251251,"HyperDash":false},{"StartTime":150460.0,"Position":185.8204,"HyperDash":false},{"StartTime":150555.0,"Position":158.755432,"HyperDash":false},{"StartTime":150687.0,"Position":120.0,"HyperDash":false}]},{"StartTime":151142.0,"Objects":[{"StartTime":151142.0,"Position":276.0,"HyperDash":false},{"StartTime":151198.0,"Position":313.273468,"HyperDash":false},{"StartTime":151255.0,"Position":314.899536,"HyperDash":false},{"StartTime":151312.0,"Position":331.123352,"HyperDash":false},{"StartTime":151369.0,"Position":346.809448,"HyperDash":false},{"StartTime":151464.0,"Position":327.8075,"HyperDash":false},{"StartTime":151596.0,"Position":276.0,"HyperDash":false}]},{"StartTime":152051.0,"Objects":[{"StartTime":152051.0,"Position":384.0,"HyperDash":false},{"StartTime":152146.0,"Position":373.33017,"HyperDash":false},{"StartTime":152278.0,"Position":375.168457,"HyperDash":false}]},{"StartTime":152506.0,"Objects":[{"StartTime":152506.0,"Position":256.0,"HyperDash":false}]},{"StartTime":152733.0,"Objects":[{"StartTime":152733.0,"Position":218.0,"HyperDash":false}]},{"StartTime":152961.0,"Objects":[{"StartTime":152961.0,"Position":100.0,"HyperDash":false}]},{"StartTime":153188.0,"Objects":[{"StartTime":153188.0,"Position":104.0,"HyperDash":false}]},{"StartTime":153415.0,"Objects":[{"StartTime":153415.0,"Position":60.0,"HyperDash":false}]},{"StartTime":153870.0,"Objects":[{"StartTime":153870.0,"Position":241.0,"HyperDash":false},{"StartTime":153965.0,"Position":262.296783,"HyperDash":false},{"StartTime":154097.0,"Position":297.158661,"HyperDash":false}]},{"StartTime":154324.0,"Objects":[{"StartTime":154324.0,"Position":311.0,"HyperDash":false}]},{"StartTime":154779.0,"Objects":[{"StartTime":154779.0,"Position":365.0,"HyperDash":false},{"StartTime":154835.0,"Position":380.953857,"HyperDash":false},{"StartTime":154892.0,"Position":377.488251,"HyperDash":false},{"StartTime":154949.0,"Position":393.8036,"HyperDash":false},{"StartTime":155006.0,"Position":430.5609,"HyperDash":false},{"StartTime":155101.0,"Position":417.3473,"HyperDash":false},{"StartTime":155233.0,"Position":365.0,"HyperDash":false}]},{"StartTime":155688.0,"Objects":[{"StartTime":155688.0,"Position":179.0,"HyperDash":false}]},{"StartTime":155915.0,"Objects":[{"StartTime":155915.0,"Position":285.0,"HyperDash":false}]},{"StartTime":156142.0,"Objects":[{"StartTime":156142.0,"Position":154.0,"HyperDash":false}]},{"StartTime":156597.0,"Objects":[{"StartTime":156597.0,"Position":26.0,"HyperDash":false}]},{"StartTime":156824.0,"Objects":[{"StartTime":156824.0,"Position":166.0,"HyperDash":false},{"StartTime":156919.0,"Position":196.995117,"HyperDash":false},{"StartTime":157051.0,"Position":244.69249,"HyperDash":false}]},{"StartTime":157506.0,"Objects":[{"StartTime":157506.0,"Position":305.0,"HyperDash":false},{"StartTime":157601.0,"Position":339.2251,"HyperDash":false},{"StartTime":157733.0,"Position":383.441528,"HyperDash":false}]},{"StartTime":157961.0,"Objects":[{"StartTime":157961.0,"Position":461.0,"HyperDash":false}]},{"StartTime":158415.0,"Objects":[{"StartTime":158415.0,"Position":279.0,"HyperDash":false}]},{"StartTime":158642.0,"Objects":[{"StartTime":158642.0,"Position":370.0,"HyperDash":false}]},{"StartTime":158870.0,"Objects":[{"StartTime":158870.0,"Position":353.0,"HyperDash":false}]},{"StartTime":159324.0,"Objects":[{"StartTime":159324.0,"Position":140.0,"HyperDash":false}]},{"StartTime":159551.0,"Objects":[{"StartTime":159551.0,"Position":320.0,"HyperDash":false}]},{"StartTime":159779.0,"Objects":[{"StartTime":159779.0,"Position":399.0,"HyperDash":false}]},{"StartTime":160006.0,"Objects":[{"StartTime":160006.0,"Position":320.0,"HyperDash":false}]},{"StartTime":160233.0,"Objects":[{"StartTime":160233.0,"Position":255.0,"HyperDash":false},{"StartTime":160328.0,"Position":225.620453,"HyperDash":false},{"StartTime":160460.0,"Position":209.024933,"HyperDash":false}]},{"StartTime":160688.0,"Objects":[{"StartTime":160688.0,"Position":187.0,"HyperDash":false}]},{"StartTime":161142.0,"Objects":[{"StartTime":161142.0,"Position":354.0,"HyperDash":false},{"StartTime":161237.0,"Position":355.953247,"HyperDash":false},{"StartTime":161369.0,"Position":320.988251,"HyperDash":false}]},{"StartTime":161597.0,"Objects":[{"StartTime":161597.0,"Position":207.0,"HyperDash":false}]},{"StartTime":162051.0,"Objects":[{"StartTime":162051.0,"Position":43.0,"HyperDash":false}]},{"StartTime":162279.0,"Objects":[{"StartTime":162279.0,"Position":119.0,"HyperDash":false},{"StartTime":162374.0,"Position":150.19606,"HyperDash":false},{"StartTime":162506.0,"Position":180.9159,"HyperDash":false}]},{"StartTime":162961.0,"Objects":[{"StartTime":162961.0,"Position":195.0,"HyperDash":false},{"StartTime":163056.0,"Position":148.134537,"HyperDash":false},{"StartTime":163188.0,"Position":125.699371,"HyperDash":false}]},{"StartTime":163415.0,"Objects":[{"StartTime":163415.0,"Position":266.0,"HyperDash":false}]},{"StartTime":163870.0,"Objects":[{"StartTime":163870.0,"Position":337.0,"HyperDash":false},{"StartTime":163926.0,"Position":340.576416,"HyperDash":false},{"StartTime":163983.0,"Position":358.8032,"HyperDash":false},{"StartTime":164040.0,"Position":399.717,"HyperDash":false},{"StartTime":164097.0,"Position":413.786346,"HyperDash":false},{"StartTime":164192.0,"Position":398.392517,"HyperDash":false},{"StartTime":164324.0,"Position":337.0,"HyperDash":false}]},{"StartTime":164779.0,"Objects":[{"StartTime":164779.0,"Position":365.0,"HyperDash":false},{"StartTime":164874.0,"Position":341.216339,"HyperDash":false},{"StartTime":165006.0,"Position":289.0602,"HyperDash":false}]},{"StartTime":165233.0,"Objects":[{"StartTime":165233.0,"Position":164.0,"HyperDash":false}]},{"StartTime":165688.0,"Objects":[{"StartTime":165688.0,"Position":420.0,"HyperDash":false}]},{"StartTime":165915.0,"Objects":[{"StartTime":165915.0,"Position":347.0,"HyperDash":false},{"StartTime":166010.0,"Position":378.42804,"HyperDash":false},{"StartTime":166142.0,"Position":365.11972,"HyperDash":false}]},{"StartTime":166597.0,"Objects":[{"StartTime":166597.0,"Position":86.0,"HyperDash":false}]},{"StartTime":166824.0,"Objects":[{"StartTime":166824.0,"Position":212.0,"HyperDash":false}]},{"StartTime":167051.0,"Objects":[{"StartTime":167051.0,"Position":74.0,"HyperDash":false},{"StartTime":167107.0,"Position":65.55724,"HyperDash":false},{"StartTime":167164.0,"Position":36.62049,"HyperDash":false},{"StartTime":167278.0,"Position":74.0,"HyperDash":false}]},{"StartTime":167506.0,"Objects":[{"StartTime":167506.0,"Position":244.0,"HyperDash":false}]},{"StartTime":167733.0,"Objects":[{"StartTime":167733.0,"Position":166.0,"HyperDash":false}]},{"StartTime":167961.0,"Objects":[{"StartTime":167961.0,"Position":274.0,"HyperDash":false},{"StartTime":168013.0,"Position":301.951935,"HyperDash":false},{"StartTime":168065.0,"Position":319.251465,"HyperDash":false},{"StartTime":168117.0,"Position":329.4265,"HyperDash":false},{"StartTime":168170.0,"Position":343.4541,"HyperDash":false},{"StartTime":168222.0,"Position":376.318848,"HyperDash":false},{"StartTime":168274.0,"Position":385.979645,"HyperDash":false},{"StartTime":168326.0,"Position":398.919922,"HyperDash":false},{"StartTime":168415.0,"Position":410.559265,"HyperDash":false}]},{"StartTime":168642.0,"Objects":[{"StartTime":168642.0,"Position":266.0,"HyperDash":false}]},{"StartTime":168870.0,"Objects":[{"StartTime":168870.0,"Position":262.0,"HyperDash":false},{"StartTime":168922.0,"Position":264.549957,"HyperDash":false},{"StartTime":168974.0,"Position":244.610046,"HyperDash":false},{"StartTime":169026.0,"Position":261.6293,"HyperDash":false},{"StartTime":169079.0,"Position":278.3733,"HyperDash":false},{"StartTime":169131.0,"Position":294.3729,"HyperDash":false},{"StartTime":169183.0,"Position":290.6487,"HyperDash":false},{"StartTime":169235.0,"Position":313.168427,"HyperDash":false},{"StartTime":169324.0,"Position":333.1015,"HyperDash":false}]},{"StartTime":169551.0,"Objects":[{"StartTime":169551.0,"Position":391.0,"HyperDash":false}]},{"StartTime":169779.0,"Objects":[{"StartTime":169779.0,"Position":340.0,"HyperDash":false},{"StartTime":169831.0,"Position":319.4877,"HyperDash":false},{"StartTime":169883.0,"Position":321.416565,"HyperDash":false},{"StartTime":169935.0,"Position":292.492767,"HyperDash":false},{"StartTime":169988.0,"Position":257.8616,"HyperDash":false},{"StartTime":170040.0,"Position":232.613708,"HyperDash":false},{"StartTime":170092.0,"Position":235.74971,"HyperDash":false},{"StartTime":170144.0,"Position":224.646179,"HyperDash":false},{"StartTime":170233.0,"Position":191.472458,"HyperDash":false}]},{"StartTime":170461.0,"Objects":[{"StartTime":170461.0,"Position":300.0,"HyperDash":false}]},{"StartTime":170688.0,"Objects":[{"StartTime":170688.0,"Position":319.0,"HyperDash":false},{"StartTime":170740.0,"Position":317.817749,"HyperDash":false},{"StartTime":170792.0,"Position":324.806122,"HyperDash":false},{"StartTime":170844.0,"Position":327.30014,"HyperDash":false},{"StartTime":170897.0,"Position":341.6977,"HyperDash":false},{"StartTime":170949.0,"Position":331.214264,"HyperDash":false},{"StartTime":171001.0,"Position":340.934967,"HyperDash":false},{"StartTime":171053.0,"Position":335.773926,"HyperDash":false},{"StartTime":171142.0,"Position":303.6745,"HyperDash":false}]},{"StartTime":171370.0,"Objects":[{"StartTime":171370.0,"Position":157.0,"HyperDash":false}]},{"StartTime":171597.0,"Objects":[{"StartTime":171597.0,"Position":184.0,"HyperDash":false},{"StartTime":171649.0,"Position":177.816864,"HyperDash":false},{"StartTime":171701.0,"Position":167.695633,"HyperDash":false},{"StartTime":171753.0,"Position":168.327789,"HyperDash":false},{"StartTime":171806.0,"Position":156.99791,"HyperDash":false},{"StartTime":171858.0,"Position":147.82634,"HyperDash":false},{"StartTime":171910.0,"Position":166.402451,"HyperDash":false},{"StartTime":171962.0,"Position":147.244476,"HyperDash":false},{"StartTime":172051.0,"Position":180.821411,"HyperDash":false}]},{"StartTime":172279.0,"Objects":[{"StartTime":172279.0,"Position":296.0,"HyperDash":false}]},{"StartTime":172506.0,"Objects":[{"StartTime":172506.0,"Position":366.0,"HyperDash":false}]},{"StartTime":172961.0,"Objects":[{"StartTime":172961.0,"Position":296.0,"HyperDash":false}]},{"StartTime":173188.0,"Objects":[{"StartTime":173188.0,"Position":272.0,"HyperDash":false}]},{"StartTime":173415.0,"Objects":[{"StartTime":173415.0,"Position":216.0,"HyperDash":false},{"StartTime":173467.0,"Position":213.8114,"HyperDash":false},{"StartTime":173519.0,"Position":176.041458,"HyperDash":false},{"StartTime":173571.0,"Position":162.964,"HyperDash":false},{"StartTime":173624.0,"Position":126.355881,"HyperDash":false},{"StartTime":173676.0,"Position":137.035782,"HyperDash":false},{"StartTime":173728.0,"Position":92.75827,"HyperDash":false},{"StartTime":173780.0,"Position":98.66459,"HyperDash":false},{"StartTime":173869.0,"Position":60.0903053,"HyperDash":false}]},{"StartTime":174097.0,"Objects":[{"StartTime":174097.0,"Position":156.0,"HyperDash":false}]},{"StartTime":174324.0,"Objects":[{"StartTime":174324.0,"Position":150.0,"HyperDash":false}]},{"StartTime":174438.0,"Objects":[{"StartTime":174438.0,"Position":156.0,"HyperDash":false}]},{"StartTime":174551.0,"Objects":[{"StartTime":174551.0,"Position":150.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/75858.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/75858.osu new file mode 100644 index 0000000000..637273efad --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/75858.osu @@ -0,0 +1,417 @@ +osu file format v8 + +[General] +StackLeniency: 0.6 +Mode: 0 + +[Difficulty] +HPDrainRate:5 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:7 +SliderMultiplier:1.6 +SliderTickRate:0.5 + +[Events] +//Background and Video events +//Break Periods +2,38388,45242 +2,89297,92515 +2,107705,114333 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +1173,666.666666666667,4,2,1,60,1,0 +2173,-100,4,2,1,50,0,0 +2839,-100,4,2,1,60,0,0 +4839,-100,4,2,1,50,0,0 +5506,-100,4,2,1,60,0,0 +7506,-100,4,2,1,50,0,0 +8173,-100,4,2,1,60,0,0 +10673,-200,4,2,1,60,0,0 +11173,-200,4,2,1,10,0,0 +11673,-100,4,2,1,60,0,0 +12839,-100,4,2,1,50,0,0 +13506,-100,4,2,1,60,0,0 +15506,-100,4,2,1,50,0,0 +16173,-100,4,2,1,60,0,0 +16839,-100,4,2,1,50,0,0 +17506,-100,4,2,1,60,0,0 +19506,-100,4,2,1,70,0,0 +20006,-100,4,2,1,30,0,0 +22052,454.545454545455,4,2,1,40,1,0 +23642,-100,4,2,2,70,0,0 +23870,-100,4,2,2,70,0,1 +38415,-100,4,2,2,70,0,0 +45915,-100,4,2,2,60,0,0 +52733,-100,4,2,2,25,0,0 +53188,-100,4,2,2,60,0,0 +60006,-100,4,2,2,25,0,0 +60460,-100,4,2,1,45,0,0 +67620,-100,4,2,1,50,0,0 +71483,-100,4,2,1,55,0,0 +74267,-100,4,2,1,70,0,0 +74665,-100,4,2,2,80,0,0 +74779,-100,4,2,2,80,0,1 +89324,-100,4,2,2,80,0,0 +92961,-100,4,2,2,65,0,0 +107279,-100,4,2,1,40,0,0 +107733,-100,4,2,1,45,0,0 +126029,-100,4,2,1,50,0,0 +128813,-100,4,2,1,60,0,0 +129211,-100,4,2,1,70,0,0 +129438,-100,4,2,1,55,0,0 +130631,-100,4,2,1,65,0,0 +131029,-100,4,2,1,75,0,0 +131370,-100,4,2,2,65,0,0 +145461,-100,4,2,2,75,0,0 +145688,-100,4,2,2,75,0,1 +160120,-100,4,2,2,80,0,0 +160233,-100,4,2,2,80,0,1 +174779,-100,4,2,2,80,0,0 + +[HitObjects] +94,279,1173,2,0,B|125:307|190:315|253:298,1,160,8|2 +398,247,2506,1,0 +471,104,3172,1,2 +320,51,3839,6,0,B|275:33|209:34|165:67,1,160,8|2 +65,190,5173,1,0 +149,325,5839,1,2 +239,192,6506,6,0,B|295:173|352:207|417:188,1,160,4|2 +493,320,7839,1,0 +334,340,8506,1,2 +199,253,9173,5,0 +171,95,9839,1,2 +271,219,10506,1,0 +199,253,10839,2,0,B|161:276|115:272,1,80,2|0 +42,266,11839,5,4 +110,121,12506,1,2 +263,168,13172,2,0,B|305:186|378:185|423:172,1,160,0|2 +293,75,14506,6,0,B|276:121|216:147|156:149,1,160,8|2 +282,251,15839,2,0,B|299:297|359:323|419:325,1,160,0|2 +416,164,17172,1,4 +256,148,17839,2,0,B|172:136|172:136|230:189|198:266|154:289|154:289|85:276,1,320,2|2 +256,148,19839,1,4 +256,192,20173,12,8,22506 +256,192,22961,5,8 +256,192,23415,1,8 +104,245,23870,6,0,B|132:284|196:282,2,80,2|0|2 +256,192,24779,2,0,B|314:192|348:238,2,80,2|0|2 +118,111,25688,2,0,B|165:91|229:112,1,80,0|2 +275,113,26142,1,0 +419,185,26597,2,0,B|383:204|338:204|337:204,1,80,2|0 +261,196,27052,1,2 +128,285,27506,5,0 +97,211,27733,2,0,B|82:168|96:131,1,80,2|0 +236,56,28415,1,0 +313,77,28642,2,0,B|356:91|411:84,1,80,0|2 +456,232,29324,1,0 +456,232,29552,1,2 +456,232,29779,1,0 +311,299,30233,1,0 +231,312,30461,1,2 +151,300,30688,2,0,B|98:292,2,40,0|0|0 +231,312,31142,5,2 +202,236,31370,1,0 +194,156,31597,2,0,B|188:101|218:47|274:27,1,160,2|2 +295,104,32279,1,0 +273,181,32506,2,0,B|218:179|150:144|144:96,1,160,2|0 +219,72,33188,1,2 +295,104,33415,2,0,B|252:152|265:239|328:269,1,160 +367,205,34097,1,0 +372,125,34324,2,0,B|323:95|242:92|191:148,1,160,2|0 +154,170,35006,1,2 +107,234,35233,6,0,B|134:267|226:268|262:230,1,160,0|2 +316,183,35915,1,0 +350,111,36142,1,2 +350,111,36597,1,0 +393,178,36824,1,2 +406,257,37051,2,0,B|402:303|344:360|271:363,1,160,0|2 +216,350,37733,1,0 +154,298,37961,5,2 +154,298,38074,1,2 +154,298,38188,1,2 +105,136,46142,6,0,B|125:91|191:64|257:74,1,160,0|2 +399,102,47051,2,0,B|486:117|485:117,2,80,0|2|0 +422,260,47961,2,0,B|402:305|336:332|270:322,1,160,0|2 +128,294,48870,2,0,B|41:279|42:279,2,80,0|2|0 +252,193,49779,6,0,B|297:168|358:163|436:186,1,160,0|2 +342,324,50688,1,0 +377,252,50915,2,0,B|335:241|293:260,1,80,2|0 +227,293,51370,1,0 +159,335,51597,2,0,B|118:354|78:347,1,80,2|0 +107,271,52051,2,0,B|56:280|16:255,1,80,2|0 +75,196,52506,2,0,B|132:204|191:190|229:151,1,160,4|0 +321,27,53415,5,2 +321,27,53642,1,0 +321,27,53870,2,0,B|376:37|403:124|352:180,1,160 +331,230,54551,1,2 +266,276,54779,1,0 +266,276,55233,5,2 +266,276,55461,1,0 +266,276,55688,2,0,B|208:296|133:275|108:219,1,160 +89,164,56370,1,2 +99,84,56597,1,0 +99,84,57051,5,2 +99,84,57279,1,0 +99,84,57506,2,0,B|128:116|201:127|254:108,1,160 +326,84,58188,1,2 +382,27,58415,1,0 +401,104,58642,2,0,B|392:148|345:160,1,80,0|2 +274,188,59097,1,0 +337,236,59324,2,0,B|374:265|364:310,1,80,2|0 +284,298,59779,2,0,B|243:334|169:279|128:318,1,160,4|0 +41,182,60688,5,0 +191,127,61142,2,0,B|276:94,1,80,2|0 +254,177,61597,2,0,B|339:144,1,80 +319,227,62051,1,2 +319,227,62279,2,0,B|234:260,1,80,0|8 +168,281,62733,1,0 +91,305,62961,1,2 +31,252,63188,1,0 +31,172,63415,2,0,B|31:88,2,80,0|2|2 +181,116,64324,5,8 +335,74,64779,2,0,B|335:162,1,80,2|0 +405,116,65233,2,0,B|405:198,1,80 +475,157,65688,1,2 +475,157,65915,2,0,B|475:69,1,80,0|8 +405,37,66370,1,0 +325,26,66597,1,2 +252,60,66824,1,8 +204,124,67051,1,0 +189,202,67279,1,2 +202,280,67506,1,10 +250,343,67733,1,0 +329,332,67961,6,0,B|432:315,1,80,0|8 +427,241,68415,2,0,B|324:258,1,80,2|8 +303,187,68870,2,0,B|406:170,1,80,0|8 +401,96,69324,2,0,B|298:113,1,80,2|8 +242,122,69779,5,0 +242,122,70006,1,8 +163,135,70233,1,2 +163,135,70461,1,8 +84,150,70688,2,0,B|60:195|95:243,3,80,0|2|2|0 +148,275,71597,6,0,B|180:305|252:312|305:295,1,160,4|10 +374,86,72506,2,0,B|342:56|270:49|217:66,1,160,4|10 +147,97,73188,1,0 +213,141,73415,2,0,B|286:189,1,80,8|2 +346,229,73870,2,0,B|282:313,1,80,8|2 +252,358,74324,1,10 +252,358,74551,1,10 +252,358,74779,6,0,B|208:373|169:356,2,80,0|0|2 +194,208,75688,2,0,B|150:193|111:210,2,80,2|0|2 +347,252,76597,1,0 +347,252,76824,1,2 +347,252,77051,1,0 +448,128,77506,1,0 +368,117,77733,1,0 +305,67,77961,1,2 +146,87,78415,5,0 +118,161,78642,2,0,B|99:205|41:224,1,80,2|0 +218,249,79324,2,0,B|252:272|301:266,1,80 +372,247,79779,1,2 +286,112,80233,2,0,B|282:23,2,80,0|2|0 +427,186,81142,1,0 +427,186,81370,1,2 +427,186,81597,2,0,B|431:244,2,40 +421,105,82051,5,0 +356,152,82279,1,0 +285,188,82506,2,0,B|236:212|160:202|130:174,1,160,2|2 +188,119,83188,1,0 +267,110,83415,2,0,B|303:160|289:236|225:276,1,160,2|0 +193,198,84097,1,2 +188,119,84324,2,0,B|240:128|312:104|337:51,1,160 +257,29,85006,1,0 +177,39,85233,2,0,B|160:93|191:163|284:166,1,160,2|0 +326,183,85915,1,2 +404,197,86142,6,0,B|455:212|468:261|448:314|380:320,1,160 +326,330,86824,1,0 +246,322,87051,1,2 +246,322,87506,1,0 +192,262,87733,1,2 +168,185,87961,2,0,B|148:132|174:73|235:44,1,160,0|2 +299,23,88642,1,0 +378,36,88870,5,2 +378,36,88983,1,2 +378,36,89097,1,2 +330,47,93415,6,0,B|388:28|453:36,2,120,2|0|2 +254,74,94324,1,0 +181,108,94552,2,0,B|129:134,1,40 +181,107,95233,6,0,B|123:88|58:96,2,120,2|0|2 +257,134,96142,1,0 +330,168,96370,2,0,B|382:194,1,40 +330,168,97052,6,0,B|388:149|453:157,2,120,2|0|2 +254,195,97961,1,0 +181,229,98188,2,0,B|129:255,1,40 +181,228,98870,6,0,B|123:209|58:217,2,120,2|0|2 +257,255,99779,1,0 +330,289,100006,2,0,B|382:315,1,40 +454,74,100688,6,0,B|403:83,1,40,2|0 +335,95,101029,2,0,B|270:105,1,40,2|0 +216,114,101370,1,0 +137,127,101597,2,0,B|89:139|30:126,1,80,2|0 +57,130,101938,1,0 +57,130,102506,6,0,B|108:139,1,40,2|0 +176,151,102847,2,0,B|240:161,1,40,2|0 +295,170,103188,1,0 +374,183,103415,2,0,B|422:195|481:182,1,80,2|0 +454,187,103756,1,0 +454,187,104324,6,0,B|403:196,1,40,2|0 +335,208,104665,2,0,B|270:218,1,40,2|0 +216,227,105006,1,0 +176,234,105120,1,0 +137,240,105233,2,0,B|89:252|30:239,1,80,2|0 +57,244,105574,1,0 +57,244,106142,6,0,B|108:253,1,40,2|0 +176,265,106483,2,0,B|240:275,1,40,2|0 +295,284,106824,2,0,B|320:300|372:301|408:283|408:283|371:256|318:256|291:287,1,240,0|12 +114,269,115233,6,0,B|145:293|228:297|281:282,1,160,0|2 +347,264,115915,1,0 +419,230,116142,2,0,B|452:197|450:147,2,80,0|0|2 +366,78,117052,2,0,B|330:116|275:110,1,80,8|0 +216,99,117506,1,2 +149,54,117733,1,0 +84,102,117961,2,0,B|84:216,3,80,0|2|2|0 +85,262,118870,5,8 +155,299,119097,1,0 +225,261,119324,1,2 +296,297,119552,1,0 +368,263,119779,2,0,B|411:250|461:267,2,80,0|0|2 +434,117,120688,1,0 +364,77,120915,1,8 +286,58,121142,2,0,B|229:48|161:67|126:113,1,160,2|0 +102,172,121824,1,10 +102,252,122052,2,0,B|104:301|150:325|152:324,1,80,2|0 +187,253,122506,6,0,B|228:231|312:284|368:259,1,160,8|2 +409,217,123188,1,0 +342,172,123415,2,0,B|297:185|225:140|184:159,1,160,0|2 +118,114,124097,1,0 +184,70,124324,2,0,B|226:47|319:101|365:76,1,160,8|2 +401,29,125006,1,0 +474,59,125233,2,0,B|496:100|472:142,1,80,0|2 +437,206,125688,2,0,B|415:251|442:297,1,80,2|0 +506,246,126142,6,0,B|342:247,1,160,0|10 +28,229,127052,2,0,B|192:228,1,160,0|10 +267,228,127733,1,0 +226,297,127961,2,0,B|202:340|232:391,2,80,8|0|10 +267,228,128642,1,0 +308,159,128870,1,8 +308,159,129097,1,10 +308,159,129324,1,4 +308,159,129779,6,0,B|332:202|302:253,2,80,10|0|10 +267,90,130461,1,0 +226,21,130688,1,10 +226,21,130915,1,10 +226,21,131142,1,6 +119,140,131597,5,2 +148,297,132052,1,0 +302,338,132506,1,2 +430,242,132961,1,0 +394,86,133415,1,2 +240,40,133870,1,0 +119,140,134324,2,0,B|81:168|17:153,1,80,2|0 +65,80,134779,1,0 +178,192,135233,5,2 +247,336,135688,1,0 +406,343,136142,1,2 +484,203,136597,1,0 +418,57,137052,1,2 +258,52,137506,1,0 +178,192,137961,2,0,B|141:228|91:227,1,80,2|2 +110,146,138415,1,0 +247,228,138870,6,0,B|282:250|337:247,1,80,2|0 +403,246,139324,1,0 +309,115,139779,2,0,B|274:93|219:96,1,80,2|0 +153,97,140233,1,0 +98,247,140688,1,2 +175,265,140915,1,0 +242,221,141142,1,2 +274,147,141370,1,0 +327,87,141597,1,2 +399,52,141824,1,0 +471,86,142052,1,0 +401,230,142506,6,0,B|401:323,2,80,2|0|0 +246,272,143415,2,0,B|246:365,2,80,2|0|0 +91,314,144324,1,2 +45,247,144552,1,0 +90,181,144779,1,2 +45,114,145006,1,0 +89,47,145233,1,2 +235,112,145688,5,0 +307,146,145915,1,0 +386,139,146142,1,2 +386,139,146597,1,2 +353,211,146824,1,0 +349,291,147051,1,2 +349,291,147506,1,0 +282,246,147733,2,0,B|245:222|179:226,1,80,2|0 +234,70,148415,2,0,B|247:122|216:167,1,80,2|0 +205,225,148870,1,2 +88,116,149324,6,0,B|56:159|77:205,2,80,0|2|0 +120,272,150233,2,0,B|139:307|193:313,2,80,2|0|2 +276,304,151142,2,0,B|324:298|364:252,2,80,0|2|0 +384,185,152051,2,0,B|399:140|372:104,1,80,0|2 +314,56,152506,1,0 +237,34,152733,1,0 +159,54,152961,5,2 +102,110,153188,1,0 +82,187,153415,1,2 +241,172,153870,2,0,B|296:192|303:250,1,80 +307,304,154324,1,2 +365,155,154779,2,0,B|389:116|435:115,2,80,0|2|0 +307,304,155688,1,2 +232,334,155915,1,0 +154,315,156142,1,2 +90,167,156597,5,0 +166,189,156824,2,0,B|211:202|257:182,1,80,2|0 +305,38,157506,2,0,B|345:21|392:34,1,80,2|0 +461,50,157961,1,2 +370,181,158415,1,0 +370,181,158642,1,2 +370,181,158870,1,0 +255,292,159324,1,0 +320,337,159551,1,2 +399,341,159779,5,2 +320,337,160006,1,0 +255,292,160233,2,0,B|209:264|205:203,1,80,2|0 +196,149,160688,1,2 +354,171,161142,2,0,B|352:219|305:256,1,80 +256,290,161597,1,2 +125,197,162051,1,0 +119,117,162279,2,0,B|138:78|187:70,1,80,2|0 +195,230,162961,2,0,B|143:232|114:179,1,80,2|0 +190,150,163415,1,2 +337,86,163870,6,0,B|372:64|421:70,2,80,0|2|0 +365,243,164779,2,0,B|328:272|260:256,1,80,2|0 +212,239,165233,1,2 +292,111,165688,1,0 +347,168,165915,2,0,B|377:201|362:257,1,80,2|0 +224,320,166597,1,0 +149,292,166824,1,2 +74,261,167051,2,0,B|32:245,2,40 +138,213,167506,5,2 +205,169,167733,1,0 +274,129,167961,2,0,B|328:113|400:144|414:196,1,160,2|0 +340,224,168642,1,0 +262,204,168870,2,0,B|249:152|288:80|343:74,1,160,2|0 +367,148,169551,1,2 +340,224,169779,2,0,B|298:191|219:196|180:244,1,160,0|2 +240,295,170461,1,0 +319,301,170688,2,0,B|355:264|345:184|301:156,1,160,2|0 +229,127,171370,1,2 +184,60,171597,6,0,B|131:94|134:176|208:218,1,160 +252,234,172279,1,0 +331,241,172506,1,2 +331,241,172961,1,0 +284,306,173188,1,2 +216,348,173415,2,0,B|171:368|94:370|56:347,1,160,0|2 +106,283,174097,1,0 +153,218,174324,5,2 +153,218,174438,1,2 +153,218,174551,1,2 diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/871815-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/871815-expected-conversion.json new file mode 100644 index 0000000000..c8ebf04ca4 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/871815-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":1459.0,"Objects":[{"StartTime":1459.0,"Position":150.0,"HyperDash":false}]},{"StartTime":1809.0,"Objects":[{"StartTime":1809.0,"Position":150.0,"HyperDash":false},{"StartTime":1896.0,"Position":172.185562,"HyperDash":false},{"StartTime":1984.0,"Position":224.798538,"HyperDash":false},{"StartTime":2072.0,"Position":262.41153,"HyperDash":false}]},{"StartTime":2160.0,"Objects":[{"StartTime":2160.0,"Position":281.0,"HyperDash":false},{"StartTime":2229.0,"Position":285.0371,"HyperDash":false},{"StartTime":2335.0,"Position":285.094574,"HyperDash":false}]},{"StartTime":2511.0,"Objects":[{"StartTime":2511.0,"Position":272.0,"HyperDash":false}]},{"StartTime":2687.0,"Objects":[{"StartTime":2687.0,"Position":367.0,"HyperDash":false},{"StartTime":2774.0,"Position":395.071045,"HyperDash":false},{"StartTime":2862.0,"Position":385.323761,"HyperDash":false},{"StartTime":2931.0,"Position":384.634644,"HyperDash":false},{"StartTime":3037.0,"Position":371.162445,"HyperDash":false}]},{"StartTime":3213.0,"Objects":[{"StartTime":3213.0,"Position":278.0,"HyperDash":false}]},{"StartTime":3388.0,"Objects":[{"StartTime":3388.0,"Position":113.0,"HyperDash":false}]},{"StartTime":3476.0,"Objects":[{"StartTime":3476.0,"Position":116.0,"HyperDash":false}]},{"StartTime":3564.0,"Objects":[{"StartTime":3564.0,"Position":121.0,"HyperDash":false},{"StartTime":3607.0,"Position":121.720619,"HyperDash":false},{"StartTime":3651.0,"Position":121.0,"HyperDash":false},{"StartTime":3695.0,"Position":121.720619,"HyperDash":false},{"StartTime":3739.0,"Position":121.0,"HyperDash":false},{"StartTime":3783.0,"Position":121.720619,"HyperDash":false},{"StartTime":3827.0,"Position":121.0,"HyperDash":false},{"StartTime":3871.0,"Position":121.720619,"HyperDash":false},{"StartTime":3914.0,"Position":121.0,"HyperDash":false}]},{"StartTime":4266.0,"Objects":[{"StartTime":4266.0,"Position":368.0,"HyperDash":false},{"StartTime":4353.0,"Position":394.044952,"HyperDash":false},{"StartTime":4441.0,"Position":430.602478,"HyperDash":false},{"StartTime":4510.0,"Position":451.933838,"HyperDash":false},{"StartTime":4616.0,"Position":498.848267,"HyperDash":false}]},{"StartTime":4792.0,"Objects":[{"StartTime":4792.0,"Position":440.0,"HyperDash":false}]},{"StartTime":4967.0,"Objects":[{"StartTime":4967.0,"Position":289.0,"HyperDash":false},{"StartTime":5036.0,"Position":249.229553,"HyperDash":false},{"StartTime":5142.0,"Position":216.522751,"HyperDash":false}]},{"StartTime":5318.0,"Objects":[{"StartTime":5318.0,"Position":105.0,"HyperDash":false}]},{"StartTime":5494.0,"Objects":[{"StartTime":5494.0,"Position":119.0,"HyperDash":false},{"StartTime":5581.0,"Position":96.97381,"HyperDash":false},{"StartTime":5669.0,"Position":97.10305,"HyperDash":false},{"StartTime":5738.0,"Position":108.834061,"HyperDash":false},{"StartTime":5844.0,"Position":132.852325,"HyperDash":false}]},{"StartTime":6020.0,"Objects":[{"StartTime":6020.0,"Position":192.0,"HyperDash":true}]},{"StartTime":6195.0,"Objects":[{"StartTime":6195.0,"Position":451.0,"HyperDash":false},{"StartTime":6282.0,"Position":417.845947,"HyperDash":false},{"StartTime":6370.0,"Position":392.282257,"HyperDash":false},{"StartTime":6439.0,"Position":343.915466,"HyperDash":false},{"StartTime":6545.0,"Position":323.918671,"HyperDash":false}]},{"StartTime":6722.0,"Objects":[{"StartTime":6722.0,"Position":380.0,"HyperDash":false}]},{"StartTime":6897.0,"Objects":[{"StartTime":6897.0,"Position":334.0,"HyperDash":false}]},{"StartTime":6985.0,"Objects":[{"StartTime":6985.0,"Position":334.0,"HyperDash":false}]},{"StartTime":7073.0,"Objects":[{"StartTime":7073.0,"Position":334.0,"HyperDash":false},{"StartTime":7160.0,"Position":336.334045,"HyperDash":false},{"StartTime":7248.0,"Position":347.94342,"HyperDash":false},{"StartTime":7317.0,"Position":360.6226,"HyperDash":false},{"StartTime":7423.0,"Position":326.399445,"HyperDash":false}]},{"StartTime":7599.0,"Objects":[{"StartTime":7599.0,"Position":281.0,"HyperDash":false}]},{"StartTime":7774.0,"Objects":[{"StartTime":7774.0,"Position":140.0,"HyperDash":false}]},{"StartTime":7950.0,"Objects":[{"StartTime":7950.0,"Position":274.0,"HyperDash":false}]},{"StartTime":8125.0,"Objects":[{"StartTime":8125.0,"Position":138.0,"HyperDash":false}]},{"StartTime":8301.0,"Objects":[{"StartTime":8301.0,"Position":266.0,"HyperDash":false},{"StartTime":8388.0,"Position":316.25592,"HyperDash":false},{"StartTime":8476.0,"Position":340.940063,"HyperDash":false},{"StartTime":8545.0,"Position":353.487854,"HyperDash":false},{"StartTime":8651.0,"Position":415.880127,"HyperDash":false}]},{"StartTime":8827.0,"Objects":[{"StartTime":8827.0,"Position":512.0,"HyperDash":false}]},{"StartTime":9002.0,"Objects":[{"StartTime":9002.0,"Position":490.0,"HyperDash":false},{"StartTime":9089.0,"Position":482.986267,"HyperDash":false},{"StartTime":9177.0,"Position":439.110077,"HyperDash":false},{"StartTime":9246.0,"Position":414.503052,"HyperDash":false},{"StartTime":9352.0,"Position":366.139923,"HyperDash":false}]},{"StartTime":9529.0,"Objects":[{"StartTime":9529.0,"Position":260.0,"HyperDash":false},{"StartTime":9598.0,"Position":239.881287,"HyperDash":false},{"StartTime":9704.0,"Position":261.6383,"HyperDash":false}]},{"StartTime":9792.0,"Objects":[{"StartTime":9792.0,"Position":267.0,"HyperDash":false}]},{"StartTime":9880.0,"Objects":[{"StartTime":9880.0,"Position":267.0,"HyperDash":false},{"StartTime":9967.0,"Position":234.350662,"HyperDash":false},{"StartTime":10055.0,"Position":203.079025,"HyperDash":false},{"StartTime":10124.0,"Position":187.624725,"HyperDash":false},{"StartTime":10230.0,"Position":130.615417,"HyperDash":false}]},{"StartTime":10406.0,"Objects":[{"StartTime":10406.0,"Position":185.0,"HyperDash":false}]},{"StartTime":10581.0,"Objects":[{"StartTime":10581.0,"Position":177.0,"HyperDash":false},{"StartTime":10650.0,"Position":191.354156,"HyperDash":false},{"StartTime":10756.0,"Position":249.516769,"HyperDash":false}]},{"StartTime":10932.0,"Objects":[{"StartTime":10932.0,"Position":352.0,"HyperDash":false}]},{"StartTime":11108.0,"Objects":[{"StartTime":11108.0,"Position":436.0,"HyperDash":false},{"StartTime":11177.0,"Position":446.0463,"HyperDash":false},{"StartTime":11283.0,"Position":509.668152,"HyperDash":false}]},{"StartTime":11458.0,"Objects":[{"StartTime":11458.0,"Position":368.0,"HyperDash":false},{"StartTime":11527.0,"Position":322.9537,"HyperDash":false},{"StartTime":11633.0,"Position":294.331848,"HyperDash":false}]},{"StartTime":11809.0,"Objects":[{"StartTime":11809.0,"Position":181.0,"HyperDash":false},{"StartTime":11878.0,"Position":187.9666,"HyperDash":false},{"StartTime":11984.0,"Position":184.937943,"HyperDash":false}]},{"StartTime":12160.0,"Objects":[{"StartTime":12160.0,"Position":221.0,"HyperDash":false}]},{"StartTime":12248.0,"Objects":[{"StartTime":12248.0,"Position":221.0,"HyperDash":false}]},{"StartTime":12336.0,"Objects":[{"StartTime":12336.0,"Position":221.0,"HyperDash":false},{"StartTime":12405.0,"Position":266.95636,"HyperDash":false},{"StartTime":12511.0,"Position":293.24704,"HyperDash":false}]},{"StartTime":12687.0,"Objects":[{"StartTime":12687.0,"Position":440.0,"HyperDash":false},{"StartTime":12774.0,"Position":402.903931,"HyperDash":false},{"StartTime":12862.0,"Position":366.3639,"HyperDash":false},{"StartTime":12931.0,"Position":335.8872,"HyperDash":false},{"StartTime":13037.0,"Position":292.814026,"HyperDash":false}]},{"StartTime":13213.0,"Objects":[{"StartTime":13213.0,"Position":330.0,"HyperDash":false}]},{"StartTime":13301.0,"Objects":[{"StartTime":13301.0,"Position":330.0,"HyperDash":false}]},{"StartTime":13388.0,"Objects":[{"StartTime":13388.0,"Position":330.0,"HyperDash":false},{"StartTime":13457.0,"Position":378.510529,"HyperDash":false},{"StartTime":13563.0,"Position":404.689636,"HyperDash":false}]},{"StartTime":13739.0,"Objects":[{"StartTime":13739.0,"Position":494.0,"HyperDash":false}]},{"StartTime":13915.0,"Objects":[{"StartTime":13915.0,"Position":321.0,"HyperDash":false}]},{"StartTime":14002.0,"Objects":[{"StartTime":14002.0,"Position":321.0,"HyperDash":false}]},{"StartTime":14090.0,"Objects":[{"StartTime":14090.0,"Position":321.0,"HyperDash":false},{"StartTime":14159.0,"Position":343.727631,"HyperDash":false},{"StartTime":14265.0,"Position":391.072754,"HyperDash":false}]},{"StartTime":14441.0,"Objects":[{"StartTime":14441.0,"Position":231.0,"HyperDash":false}]},{"StartTime":14616.0,"Objects":[{"StartTime":14616.0,"Position":188.0,"HyperDash":false},{"StartTime":14703.0,"Position":182.992142,"HyperDash":false},{"StartTime":14791.0,"Position":176.795868,"HyperDash":false},{"StartTime":14860.0,"Position":189.954117,"HyperDash":false},{"StartTime":14966.0,"Position":188.0,"HyperDash":false}]},{"StartTime":15143.0,"Objects":[{"StartTime":15143.0,"Position":125.0,"HyperDash":false},{"StartTime":15230.0,"Position":105.420952,"HyperDash":false},{"StartTime":15318.0,"Position":59.72222,"HyperDash":false},{"StartTime":15406.0,"Position":22.9492321,"HyperDash":false}]},{"StartTime":15494.0,"Objects":[{"StartTime":15494.0,"Position":17.0,"HyperDash":false},{"StartTime":15563.0,"Position":33.37393,"HyperDash":false},{"StartTime":15669.0,"Position":20.4846058,"HyperDash":false}]},{"StartTime":15844.0,"Objects":[{"StartTime":15844.0,"Position":29.0,"HyperDash":false}]},{"StartTime":16020.0,"Objects":[{"StartTime":16020.0,"Position":130.0,"HyperDash":false}]},{"StartTime":16108.0,"Objects":[{"StartTime":16108.0,"Position":130.0,"HyperDash":false}]},{"StartTime":16195.0,"Objects":[{"StartTime":16195.0,"Position":130.0,"HyperDash":false},{"StartTime":16264.0,"Position":176.33783,"HyperDash":false},{"StartTime":16370.0,"Position":203.709747,"HyperDash":false}]},{"StartTime":16546.0,"Objects":[{"StartTime":16546.0,"Position":287.0,"HyperDash":false}]},{"StartTime":16722.0,"Objects":[{"StartTime":16722.0,"Position":402.0,"HyperDash":false},{"StartTime":16791.0,"Position":440.382324,"HyperDash":false},{"StartTime":16897.0,"Position":476.5204,"HyperDash":false}]},{"StartTime":17073.0,"Objects":[{"StartTime":17073.0,"Position":326.0,"HyperDash":false},{"StartTime":17142.0,"Position":279.617676,"HyperDash":false},{"StartTime":17248.0,"Position":251.479614,"HyperDash":false}]},{"StartTime":17423.0,"Objects":[{"StartTime":17423.0,"Position":125.0,"HyperDash":false},{"StartTime":17492.0,"Position":122.322762,"HyperDash":false},{"StartTime":17598.0,"Position":119.049225,"HyperDash":false}]},{"StartTime":17774.0,"Objects":[{"StartTime":17774.0,"Position":125.0,"HyperDash":false}]},{"StartTime":17862.0,"Objects":[{"StartTime":17862.0,"Position":125.0,"HyperDash":false}]},{"StartTime":17950.0,"Objects":[{"StartTime":17950.0,"Position":125.0,"HyperDash":false},{"StartTime":18019.0,"Position":142.158081,"HyperDash":false},{"StartTime":18125.0,"Position":198.3747,"HyperDash":false}]},{"StartTime":18301.0,"Objects":[{"StartTime":18301.0,"Position":245.0,"HyperDash":false},{"StartTime":18388.0,"Position":193.484589,"HyperDash":false},{"StartTime":18476.0,"Position":170.83812,"HyperDash":false},{"StartTime":18545.0,"Position":133.486023,"HyperDash":false},{"StartTime":18651.0,"Position":97.91507,"HyperDash":false}]},{"StartTime":18827.0,"Objects":[{"StartTime":18827.0,"Position":15.0,"HyperDash":false}]},{"StartTime":18915.0,"Objects":[{"StartTime":18915.0,"Position":15.0,"HyperDash":false}]},{"StartTime":19002.0,"Objects":[{"StartTime":19002.0,"Position":15.0,"HyperDash":false},{"StartTime":19071.0,"Position":21.7103615,"HyperDash":false},{"StartTime":19177.0,"Position":4.26349068,"HyperDash":false}]},{"StartTime":19353.0,"Objects":[{"StartTime":19353.0,"Position":0.0,"HyperDash":false}]},{"StartTime":19529.0,"Objects":[{"StartTime":19529.0,"Position":137.0,"HyperDash":false},{"StartTime":19598.0,"Position":169.483047,"HyperDash":false},{"StartTime":19704.0,"Position":210.398544,"HyperDash":false}]},{"StartTime":19880.0,"Objects":[{"StartTime":19880.0,"Position":328.0,"HyperDash":false},{"StartTime":19949.0,"Position":319.217133,"HyperDash":false},{"StartTime":20055.0,"Position":318.546051,"HyperDash":false}]},{"StartTime":20230.0,"Objects":[{"StartTime":20230.0,"Position":264.0,"HyperDash":false}]},{"StartTime":20318.0,"Objects":[{"StartTime":20318.0,"Position":264.0,"HyperDash":false}]},{"StartTime":20406.0,"Objects":[{"StartTime":20406.0,"Position":264.0,"HyperDash":false},{"StartTime":20493.0,"Position":295.147522,"HyperDash":false},{"StartTime":20581.0,"Position":330.866455,"HyperDash":false},{"StartTime":20650.0,"Position":359.710419,"HyperDash":false},{"StartTime":20756.0,"Position":396.14447,"HyperDash":false}]},{"StartTime":21108.0,"Objects":[{"StartTime":21108.0,"Position":412.0,"HyperDash":false},{"StartTime":21195.0,"Position":395.0836,"HyperDash":false},{"StartTime":21283.0,"Position":414.179626,"HyperDash":false},{"StartTime":21370.0,"Position":423.2632,"HyperDash":false},{"StartTime":21458.0,"Position":416.359283,"HyperDash":false},{"StartTime":21528.0,"Position":397.23114,"HyperDash":false},{"StartTime":21634.0,"Position":418.551361,"HyperDash":false}]},{"StartTime":21809.0,"Objects":[{"StartTime":21809.0,"Position":496.0,"HyperDash":false},{"StartTime":21896.0,"Position":491.214264,"HyperDash":false},{"StartTime":21984.0,"Position":496.431,"HyperDash":false},{"StartTime":22053.0,"Position":504.600922,"HyperDash":false},{"StartTime":22159.0,"Position":496.862,"HyperDash":false}]},{"StartTime":22336.0,"Objects":[{"StartTime":22336.0,"Position":499.0,"HyperDash":false}]},{"StartTime":22511.0,"Objects":[{"StartTime":22511.0,"Position":379.0,"HyperDash":false},{"StartTime":22598.0,"Position":360.8092,"HyperDash":false},{"StartTime":22686.0,"Position":345.0908,"HyperDash":false},{"StartTime":22773.0,"Position":309.7649,"HyperDash":false},{"StartTime":22861.0,"Position":307.9739,"HyperDash":false},{"StartTime":22931.0,"Position":312.241852,"HyperDash":false},{"StartTime":23037.0,"Position":271.985718,"HyperDash":false}]},{"StartTime":23213.0,"Objects":[{"StartTime":23213.0,"Position":322.0,"HyperDash":false},{"StartTime":23300.0,"Position":336.858,"HyperDash":false},{"StartTime":23388.0,"Position":327.7828,"HyperDash":false},{"StartTime":23457.0,"Position":329.661743,"HyperDash":false},{"StartTime":23563.0,"Position":317.734131,"HyperDash":false}]},{"StartTime":23739.0,"Objects":[{"StartTime":23739.0,"Position":240.0,"HyperDash":false}]},{"StartTime":23915.0,"Objects":[{"StartTime":23915.0,"Position":345.0,"HyperDash":false},{"StartTime":23984.0,"Position":381.55426,"HyperDash":false},{"StartTime":24090.0,"Position":419.956451,"HyperDash":false}]},{"StartTime":24266.0,"Objects":[{"StartTime":24266.0,"Position":283.0,"HyperDash":false}]},{"StartTime":24441.0,"Objects":[{"StartTime":24441.0,"Position":111.0,"HyperDash":false},{"StartTime":24510.0,"Position":97.44574,"HyperDash":false},{"StartTime":24616.0,"Position":36.04355,"HyperDash":false}]},{"StartTime":24792.0,"Objects":[{"StartTime":24792.0,"Position":173.0,"HyperDash":false}]},{"StartTime":24967.0,"Objects":[{"StartTime":24967.0,"Position":263.0,"HyperDash":false}]},{"StartTime":25055.0,"Objects":[{"StartTime":25055.0,"Position":280.0,"HyperDash":false}]},{"StartTime":25143.0,"Objects":[{"StartTime":25143.0,"Position":297.0,"HyperDash":false}]},{"StartTime":25230.0,"Objects":[{"StartTime":25230.0,"Position":314.0,"HyperDash":false}]},{"StartTime":25318.0,"Objects":[{"StartTime":25318.0,"Position":337.0,"HyperDash":false},{"StartTime":25376.0,"Position":334.666473,"HyperDash":false},{"StartTime":25434.0,"Position":337.0,"HyperDash":false},{"StartTime":25493.0,"Position":334.666473,"HyperDash":false},{"StartTime":25551.0,"Position":337.0,"HyperDash":false},{"StartTime":25610.0,"Position":334.666473,"HyperDash":false},{"StartTime":25668.0,"Position":337.0,"HyperDash":false}]},{"StartTime":25844.0,"Objects":[{"StartTime":25844.0,"Position":447.0,"HyperDash":false}]},{"StartTime":26020.0,"Objects":[{"StartTime":26020.0,"Position":436.0,"HyperDash":false}]},{"StartTime":26195.0,"Objects":[{"StartTime":26195.0,"Position":297.0,"HyperDash":false}]},{"StartTime":26546.0,"Objects":[{"StartTime":26546.0,"Position":297.0,"HyperDash":false},{"StartTime":26633.0,"Position":249.353119,"HyperDash":false},{"StartTime":26721.0,"Position":227.527557,"HyperDash":false},{"StartTime":26790.0,"Position":188.1133,"HyperDash":false},{"StartTime":26896.0,"Position":156.074387,"HyperDash":false}]},{"StartTime":27072.0,"Objects":[{"StartTime":27072.0,"Position":51.0,"HyperDash":false}]},{"StartTime":27247.0,"Objects":[{"StartTime":27247.0,"Position":185.0,"HyperDash":false},{"StartTime":27316.0,"Position":218.59346,"HyperDash":false},{"StartTime":27422.0,"Position":258.538177,"HyperDash":false}]},{"StartTime":27598.0,"Objects":[{"StartTime":27598.0,"Position":436.0,"HyperDash":false},{"StartTime":27667.0,"Position":416.406555,"HyperDash":false},{"StartTime":27773.0,"Position":362.461823,"HyperDash":false}]},{"StartTime":27949.0,"Objects":[{"StartTime":27949.0,"Position":151.0,"HyperDash":false},{"StartTime":28036.0,"Position":189.972488,"HyperDash":false},{"StartTime":28124.0,"Position":223.203812,"HyperDash":false},{"StartTime":28193.0,"Position":242.7229,"HyperDash":false},{"StartTime":28299.0,"Position":296.7707,"HyperDash":false}]},{"StartTime":28475.0,"Objects":[{"StartTime":28475.0,"Position":223.0,"HyperDash":false}]},{"StartTime":28651.0,"Objects":[{"StartTime":28651.0,"Position":296.0,"HyperDash":false},{"StartTime":28738.0,"Position":337.803925,"HyperDash":false},{"StartTime":28826.0,"Position":368.138336,"HyperDash":false},{"StartTime":28895.0,"Position":404.540863,"HyperDash":false},{"StartTime":29001.0,"Position":440.327179,"HyperDash":false}]},{"StartTime":29177.0,"Objects":[{"StartTime":29177.0,"Position":486.0,"HyperDash":false}]},{"StartTime":29353.0,"Objects":[{"StartTime":29353.0,"Position":366.0,"HyperDash":false},{"StartTime":29422.0,"Position":350.499329,"HyperDash":false},{"StartTime":29528.0,"Position":293.446533,"HyperDash":false}]},{"StartTime":29703.0,"Objects":[{"StartTime":29703.0,"Position":169.0,"HyperDash":false}]},{"StartTime":29879.0,"Objects":[{"StartTime":29879.0,"Position":245.0,"HyperDash":false}]},{"StartTime":30054.0,"Objects":[{"StartTime":30054.0,"Position":126.0,"HyperDash":false},{"StartTime":30123.0,"Position":155.500671,"HyperDash":false},{"StartTime":30229.0,"Position":198.553482,"HyperDash":false}]},{"StartTime":30404.0,"Objects":[{"StartTime":30404.0,"Position":323.0,"HyperDash":false}]},{"StartTime":30580.0,"Objects":[{"StartTime":30580.0,"Position":247.0,"HyperDash":false}]},{"StartTime":30756.0,"Objects":[{"StartTime":30756.0,"Position":349.0,"HyperDash":false},{"StartTime":30843.0,"Position":365.629761,"HyperDash":false},{"StartTime":30931.0,"Position":422.0551,"HyperDash":false},{"StartTime":31000.0,"Position":454.551147,"HyperDash":false},{"StartTime":31106.0,"Position":495.5697,"HyperDash":false}]},{"StartTime":31282.0,"Objects":[{"StartTime":31282.0,"Position":423.0,"HyperDash":false}]},{"StartTime":31458.0,"Objects":[{"StartTime":31458.0,"Position":323.0,"HyperDash":false},{"StartTime":31545.0,"Position":295.370239,"HyperDash":false},{"StartTime":31633.0,"Position":249.944885,"HyperDash":false},{"StartTime":31702.0,"Position":223.448853,"HyperDash":false},{"StartTime":31808.0,"Position":176.4303,"HyperDash":false}]},{"StartTime":31984.0,"Objects":[{"StartTime":31984.0,"Position":247.0,"HyperDash":false}]},{"StartTime":32160.0,"Objects":[{"StartTime":32160.0,"Position":99.0,"HyperDash":false},{"StartTime":32247.0,"Position":84.41518,"HyperDash":false},{"StartTime":32335.0,"Position":83.6537247,"HyperDash":false},{"StartTime":32404.0,"Position":71.69304,"HyperDash":false},{"StartTime":32510.0,"Position":108.235535,"HyperDash":false}]},{"StartTime":32686.0,"Objects":[{"StartTime":32686.0,"Position":164.0,"HyperDash":false}]},{"StartTime":32861.0,"Objects":[{"StartTime":32861.0,"Position":323.0,"HyperDash":false},{"StartTime":32930.0,"Position":362.799,"HyperDash":false},{"StartTime":33036.0,"Position":396.638153,"HyperDash":false}]},{"StartTime":33212.0,"Objects":[{"StartTime":33212.0,"Position":164.0,"HyperDash":false},{"StartTime":33281.0,"Position":116.200989,"HyperDash":false},{"StartTime":33387.0,"Position":90.36186,"HyperDash":false}]},{"StartTime":33563.0,"Objects":[{"StartTime":33563.0,"Position":323.0,"HyperDash":false},{"StartTime":33632.0,"Position":336.507568,"HyperDash":false},{"StartTime":33738.0,"Position":323.911469,"HyperDash":true}]},{"StartTime":33914.0,"Objects":[{"StartTime":33914.0,"Position":78.0,"HyperDash":false},{"StartTime":33983.0,"Position":82.492424,"HyperDash":false},{"StartTime":34089.0,"Position":77.08854,"HyperDash":false}]},{"StartTime":34265.0,"Objects":[{"StartTime":34265.0,"Position":234.0,"HyperDash":false},{"StartTime":34352.0,"Position":191.5233,"HyperDash":false},{"StartTime":34440.0,"Position":164.09671,"HyperDash":false},{"StartTime":34509.0,"Position":134.647873,"HyperDash":false},{"StartTime":34615.0,"Position":89.6628342,"HyperDash":false}]},{"StartTime":34791.0,"Objects":[{"StartTime":34791.0,"Position":148.0,"HyperDash":false}]},{"StartTime":34967.0,"Objects":[{"StartTime":34967.0,"Position":175.0,"HyperDash":false},{"StartTime":35054.0,"Position":199.913467,"HyperDash":false},{"StartTime":35142.0,"Position":201.876785,"HyperDash":false},{"StartTime":35211.0,"Position":207.491714,"HyperDash":false},{"StartTime":35317.0,"Position":181.816238,"HyperDash":false}]},{"StartTime":35493.0,"Objects":[{"StartTime":35493.0,"Position":94.0,"HyperDash":false}]},{"StartTime":35668.0,"Objects":[{"StartTime":35668.0,"Position":95.0,"HyperDash":false},{"StartTime":35755.0,"Position":137.9405,"HyperDash":false},{"StartTime":35843.0,"Position":163.627121,"HyperDash":false},{"StartTime":35912.0,"Position":197.017715,"HyperDash":false},{"StartTime":36018.0,"Position":234.539215,"HyperDash":false}]},{"StartTime":36195.0,"Objects":[{"StartTime":36195.0,"Position":319.0,"HyperDash":false}]},{"StartTime":36370.0,"Objects":[{"StartTime":36370.0,"Position":251.0,"HyperDash":false},{"StartTime":36457.0,"Position":231.0595,"HyperDash":false},{"StartTime":36545.0,"Position":182.372879,"HyperDash":false},{"StartTime":36614.0,"Position":153.982285,"HyperDash":false},{"StartTime":36720.0,"Position":111.460777,"HyperDash":false}]},{"StartTime":36896.0,"Objects":[{"StartTime":36896.0,"Position":175.0,"HyperDash":false}]},{"StartTime":37072.0,"Objects":[{"StartTime":37072.0,"Position":229.0,"HyperDash":false}]},{"StartTime":37160.0,"Objects":[{"StartTime":37160.0,"Position":245.0,"HyperDash":false}]},{"StartTime":37247.0,"Objects":[{"StartTime":37247.0,"Position":261.0,"HyperDash":false}]},{"StartTime":37335.0,"Objects":[{"StartTime":37335.0,"Position":277.0,"HyperDash":false}]},{"StartTime":37423.0,"Objects":[{"StartTime":37423.0,"Position":292.0,"HyperDash":false},{"StartTime":37492.0,"Position":308.471649,"HyperDash":false},{"StartTime":37598.0,"Position":366.746948,"HyperDash":false}]},{"StartTime":37774.0,"Objects":[{"StartTime":37774.0,"Position":491.0,"HyperDash":false}]},{"StartTime":38124.0,"Objects":[{"StartTime":38124.0,"Position":491.0,"HyperDash":false}]},{"StartTime":38300.0,"Objects":[{"StartTime":38300.0,"Position":422.0,"HyperDash":false}]},{"StartTime":38475.0,"Objects":[{"StartTime":38475.0,"Position":388.0,"HyperDash":false}]},{"StartTime":38826.0,"Objects":[{"StartTime":38826.0,"Position":388.0,"HyperDash":false}]},{"StartTime":39002.0,"Objects":[{"StartTime":39002.0,"Position":270.0,"HyperDash":false}]},{"StartTime":39177.0,"Objects":[{"StartTime":39177.0,"Position":305.0,"HyperDash":false},{"StartTime":39264.0,"Position":31.0,"HyperDash":false},{"StartTime":39352.0,"Position":421.0,"HyperDash":false},{"StartTime":39440.0,"Position":145.0,"HyperDash":false},{"StartTime":39528.0,"Position":318.0,"HyperDash":false},{"StartTime":39615.0,"Position":249.0,"HyperDash":false},{"StartTime":39703.0,"Position":147.0,"HyperDash":false},{"StartTime":39791.0,"Position":302.0,"HyperDash":false},{"StartTime":39879.0,"Position":212.0,"HyperDash":false},{"StartTime":39966.0,"Position":427.0,"HyperDash":false},{"StartTime":40054.0,"Position":116.0,"HyperDash":false},{"StartTime":40142.0,"Position":508.0,"HyperDash":false},{"StartTime":40230.0,"Position":417.0,"HyperDash":false},{"StartTime":40317.0,"Position":302.0,"HyperDash":false},{"StartTime":40405.0,"Position":132.0,"HyperDash":false},{"StartTime":40493.0,"Position":352.0,"HyperDash":false},{"StartTime":40581.0,"Position":174.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/871815.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/871815.osu new file mode 100644 index 0000000000..668c12fc0c --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/871815.osu @@ -0,0 +1,165 @@ +osu file format v14 + +[General] +StackLeniency: 0.3 +Mode: 0 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:6 +ApproachRate:8.3 +SliderMultiplier:1.5 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +1459,350.877192982456,4,2,1,45,1,0 +21108,-200,4,2,1,45,0,0 +23915,-100,4,2,1,45,0,0 +26546,350.877192982456,4,2,1,65,1,1 +40581,-100,4,2,1,45,0,0 + +[HitObjects] +150,114,1459,5,2,0:0:0:0: +150,114,1809,2,0,L|276:109,1,112.5,2|0,0:0|0:0,0:0:0:0: +281,109,2160,2,0,P|289:151|282:193,1,75,0|0,0:0|0:0,0:0:0:0: +272,298,2511,1,0,0:0:0:0: +367,70,2687,6,0,P|384:125|366:226,1,150,2|0,0:0|0:0,0:0:0:0: +278,289,3213,1,0,0:0:0:0: +113,221,3388,1,0,1:0:0:0: +116,235,3476,1,0,1:0:0:0: +121,248,3564,2,0,L|122:274,8,18.75,0|0|0|0|2|0|0|0|2,1:0|0:0|0:0|0:0|0:0|0:0|0:0|0:0|1:0,0:0:0:0: +368,70,4266,6,0,P|412:37|501:60,1,150,4|8,1:0|1:0,0:0:0:0: +440,119,4792,1,0,0:0:0:0: +289,84,4967,2,0,P|250:95|210:87,1,75,0|0,0:0|1:0,0:0:0:0: +105,24,5318,1,0,1:0:0:0: +119,191,5494,6,0,P|98:235|145:332,1,150,4|0,1:0|1:0,0:0:0:0: +192,253,6020,1,0,1:0:0:0: +451,314,6195,2,0,P|393:272|315:306,1,150,4|0,1:0|0:0,0:0:0:0: +380,360,6722,1,0,1:0:0:0: +334,189,6897,1,2,0:0:0:0: +334,189,6985,1,0,0:0:0:0: +334,189,7073,6,0,P|348:132|320:35,1,150,4|0,1:0|1:0,0:0:0:0: +281,256,7599,1,0,0:0:0:0: +140,171,7774,1,0,0:0:0:0: +274,290,7950,1,2,0:0:0:0: +138,135,8125,1,0,1:0:0:0: +266,321,8301,6,0,L|416:315,1,150,0|0,1:0|0:0,0:0:0:0: +512,307,8827,1,0,1:0:0:0: +490,150,9002,2,0,P|435:96|347:109,1,150,2|2,0:0|0:0,0:0:0:0: +260,59,9529,2,0,P|255:102|268:147,1,75,8|0,1:0|1:0,0:0:0:0: +267,164,9792,1,0,0:0:0:0: +267,164,9880,6,0,P|217:197|121:182,1,150,4|0,1:0|1:0,0:0:0:0: +185,106,10406,1,0,0:0:0:0: +177,283,10581,2,0,P|219:284|260:266,1,75,0|0,1:0|0:0,0:0:0:0: +352,225,10932,1,0,1:0:0:0: +436,132,11108,6,0,L|525:149,1,75,2|0,1:2|0:0,0:0:0:0: +368,30,11458,2,0,L|279:47,1,75,2|0,0:2|1:0,0:0:0:0: +181,124,11809,2,0,P|175:162|190:205,1,75,2|0,0:0|1:0,0:0:0:0: +221,325,12160,1,0,0:0:0:0: +221,325,12248,1,0,0:0:0:0: +221,325,12336,2,0,P|257:330|294:318,1,75,0|0,1:0|0:0,0:0:0:0: +440,318,12687,6,0,P|378:306|272:327,1,150,2|0,1:2|1:0,0:0:0:0: +330,209,13213,1,0,0:0:0:0: +330,209,13301,1,0,0:0:0:0: +330,209,13388,2,0,P|388:209|417:204,1,75,0|0,0:0|0:0,0:0:0:0: +494,149,13739,1,0,1:0:0:0: +321,99,13915,1,2,1:2:0:0: +321,99,14002,1,0,0:0:0:0: +321,99,14090,6,0,P|364:87|392:73,1,75,0|0,0:0|0:0,0:0:0:0: +231,160,14441,1,0,1:0:0:0: +188,259,14616,2,0,P|177:302|177:335,2,75,2|0|0,1:2|0:0|0:0,0:0:0:0: +125,87,15143,6,0,B|99:97|70:83|70:83|73:86|73:86|35:72|7:97,1,112.5,8|0,1:0|0:0,0:0:0:0: +17,99,15494,6,0,L|21:185,1,75,4|0,1:0|0:0,0:0:0:0: +29,282,15844,1,0,1:0:0:0: +130,334,16020,1,0,0:0:0:0: +130,334,16108,1,0,0:0:0:0: +130,334,16195,2,0,P|165:337|208:327,1,75,0|0,1:0|0:0,0:0:0:0: +287,251,16546,1,0,1:0:0:0: +402,165,16722,6,0,L|490:155,1,75,2|0,1:2|0:0,0:0:0:0: +326,67,17073,2,0,L|238:57,1,75,2|0,0:2|1:0,0:0:0:0: +125,41,17423,2,0,P|116:84|124:131,1,75,2|0,0:0|1:0,0:0:0:0: +125,238,17774,1,2,0:0:0:0: +125,238,17862,1,0,0:0:0:0: +125,238,17950,2,0,P|165:242|204:231,1,75,0|0,1:0|0:0,0:0:0:0: +245,344,18301,6,0,P|162:336|85:357,1,150,2|0,1:2|1:0,0:0:0:0: +15,271,18827,1,0,0:0:0:0: +15,271,18915,1,0,0:0:0:0: +15,271,19002,2,0,P|3:222|7:184,1,75,0|2,0:0|0:0,0:0:0:0: +0,85,19353,1,0,1:0:0:0: +137,68,19529,6,0,P|170:69|214:57,1,75,4|0,1:2|1:0,0:0:0:0: +328,191,19880,2,0,P|329:158|317:114,1,75,0|2,0:0|1:0,0:0:0:0: +264,261,20230,1,0,1:0:0:0: +264,261,20318,1,0,0:0:0:0: +264,261,20406,2,0,P|318:289|401:251,1,150,0|8,0:0|1:0,0:0:0:0: +412,245,21108,6,0,L|419:365,1,112.5,4|0,1:0|0:0,0:0:0:0: +496,259,21809,2,0,L|497:172,1,75,0|0,1:0|0:0,0:0:0:0: +499,82,22336,1,0,0:0:0:0: +379,42,22511,6,0,P|338:25|265:38,1,112.5,2|2,0:0|0:0,0:0:0:0: +322,179,23213,2,0,P|328:145|318:107,1,75,0|0,0:0|0:0,0:0:0:0: +240,150,23739,1,0,0:0:0:0: +345,271,23915,6,0,L|433:274,1,75,4|0,1:0|0:0,0:0:0:0: +283,331,24266,1,0,0:0:0:0: +111,275,24441,6,0,L|23:272,1,75,4|0,1:0|0:0,0:0:0:0: +173,215,24792,1,0,0:0:0:0: +263,127,24967,5,0,1:0:0:0: +280,119,25055,1,0,0:0:0:0: +297,112,25143,1,0,1:0:0:0: +314,105,25230,1,0,0:0:0:0: +337,95,25318,6,0,L|334:127,6,25,0|0|0|0|0|0|0,1:0|0:0|0:0|1:0|0:0|0:0|1:0,0:0:0:0: +447,46,25844,1,0,1:0:0:0: +436,197,26020,1,0,0:0:0:0: +297,263,26195,1,2,1:0:0:0: +297,263,26546,6,0,P|230:288|143:260,1,150,4|0,1:0|0:0,0:0:0:0: +51,182,27072,1,2,1:0:0:0: +185,111,27247,2,0,P|224:103|271:112,1,75,0|0,0:0|0:0,0:0:0:0: +436,197,27598,2,0,P|397:205|350:196,1,75,0|2,0:0|1:0,0:0:0:0: +151,269,27949,6,0,P|208:252|320:273,1,150,0|0,1:0|0:0,0:0:0:0: +223,342,28475,1,0,0:0:0:0: +296,262,28651,2,0,P|353:279|456:253,1,150,0|0,1:0|0:0,0:0:0:0: +486,133,29177,1,2,1:0:0:0: +366,52,29353,6,0,P|324:39|288:42,1,75,2|0,0:0|0:0,0:0:0:0: +169,61,29703,1,0,0:0:0:0: +245,149,29879,1,2,1:0:0:0: +126,258,30054,6,0,P|168:271|204:268,1,75,2|0,0:0|0:0,0:0:0:0: +323,249,30404,1,0,0:0:0:0: +247,161,30580,1,0,0:0:0:0: +349,54,30756,6,0,P|397:41|502:54,1,150,2|0,0:0|0:0,0:0:0:0: +423,138,31282,1,0,1:0:0:0: +323,249,31458,2,0,P|275:262|170:249,1,150,0|0,1:0|0:0,0:0:0:0: +247,161,31984,1,2,1:0:0:0: +99,42,32160,6,0,P|85:127|121:200,1,150,4|0,1:0|0:0,0:0:0:0: +164,309,32686,1,2,1:0:0:0: +323,249,32861,2,0,P|376:243|401:249,1,75,0|0,0:0|0:0,0:0:0:0: +164,309,33212,2,0,P|111:315|86:309,1,75,0|2,0:0|1:0,0:0:0:0: +323,249,33563,6,0,P|330:211|316:158,1,75,0|0,1:0|0:0,0:0:0:0: +78,57,33914,2,0,P|71:95|85:148,1,75,0|0,0:0|0:0,0:0:0:0: +234,300,34265,2,0,P|174:276|80:280,1,150,0|0,1:0|0:0,0:0:0:0: +148,364,34791,1,2,1:0:0:0: +175,186,34967,6,0,P|199:138|172:34,1,150,4|0,1:0|0:0,0:0:0:0: +94,115,35493,1,2,1:0:0:0: +95,260,35668,2,0,P|143:284|247:257,1,150,4|0,1:0|0:0,0:0:0:0: +319,199,36195,1,0,0:0:0:0: +251,89,36370,6,0,P|203:65|99:92,1,150,0|0,1:0|0:0,0:0:0:0: +175,186,36896,1,0,0:0:0:0: +229,329,37072,1,0,1:0:0:0: +245,337,37160,1,0,1:0:0:0: +261,345,37247,1,0,1:0:0:0: +277,353,37335,1,0,0:0:0:0: +292,361,37423,2,0,L|377:368,1,75,0|0,1:0|1:0,0:0:0:0: +491,315,37774,5,4,1:0:0:0: +491,315,38124,1,0,1:0:0:0: +422,209,38300,1,0,1:0:0:0: +388,68,38475,1,4,1:0:0:0: +388,68,38826,1,0,1:0:0:0: +270,153,39002,1,0,1:0:0:0: +256,192,39177,12,4,40581,1:0:0:0: diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic-hyperdash-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/basic-hyperdash-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic-hyperdash-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/basic-hyperdash-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic-hyperdash.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/basic-hyperdash.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic-hyperdash.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/basic-hyperdash.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/basic.osu similarity index 96% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/basic.osu index 40b4409760..abd2ff2ee6 100644 --- a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/basic.osu +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/basic.osu @@ -1,27 +1,27 @@ -osu file format v14 - -[Difficulty] -HPDrainRate:6 -CircleSize:4 -OverallDifficulty:7 -ApproachRate:8.3 -SliderMultiplier:1.6 -SliderTickRate:1 - -[TimingPoints] -500,500,4,2,1,50,1,0 -13426,-100,4,3,1,45,0,0 -14884,-100,4,2,1,50,0,0 - -[HitObjects] -96,192,500,6,0,L|416:192,2,320 -256,192,3000,12,0,4000,0:0:0:0: -256,192,4500,12,0,5500,0:0:0:0: -256,192,6000,12,0,6500,0:0:0:0: -256,128,7000,6,0,L|352:128,4,80 -32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 -256,192,11500,12,0,12000,0:0:0:0: -512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 -256,256,17000,6,0,L|160:256,4,80 -256,192,18500,12,0,19450,0:0:0:0: -216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 +osu file format v14 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +13426,-100,4,3,1,45,0,0 +14884,-100,4,2,1,50,0,0 + +[HitObjects] +96,192,500,6,0,L|416:192,2,320 +256,192,3000,12,0,4000,0:0:0:0: +256,192,4500,12,0,5500,0:0:0:0: +256,192,6000,12,0,6500,0:0:0:0: +256,128,7000,6,0,L|352:128,4,80 +32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 +256,192,11500,12,0,12000,0:0:0:0: +512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 +256,256,17000,6,0,L|160:256,4,80 +256,192,18500,12,0,19450,0:0:0:0: +216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/diffcalc-test.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/diffcalc-test.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/diffcalc-test.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-repeat-slider-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-repeat-slider.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-spinner-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-spinner.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-spinner.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-spinner.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-stream-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-stream.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/hardrock-stream.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/hardrock-stream.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/pixel-jump-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/pixel-jump-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/pixel-jump.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/pixel-jump.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/pixel-jump.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/right-bound-hr-offset-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/right-bound-hr-offset-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/right-bound-hr-offset-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/right-bound-hr-offset-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/right-bound-hr-offset.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/right-bound-hr-offset.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/right-bound-hr-offset.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/right-bound-hr-offset.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/slider-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/slider-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/slider.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/slider.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/slider.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-and-circles-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-and-circles-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-and-circles-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-and-circles-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-and-circles.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-and-circles.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-and-circles.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-and-circles.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-precision-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-precision-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-precision.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner-precision.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner-precision.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/spinner.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/spinner.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/tiny-ticks-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/tiny-ticks-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/tiny-ticks.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/tiny-ticks.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/tiny-ticks.osu diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/v8-tick-distance-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance-expected-conversion.json rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/v8-tick-distance-expected-conversion.json diff --git a/osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/v8-tick-distance.osu similarity index 100% rename from osu.Game.Rulesets.Catch/Resources/Testing/Beatmaps/v8-tick-distance.osu rename to osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/v8-tick-distance.osu From 40ff95d586aa0e45079e6aff38fb99a5a3e9b0f2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 6 Dec 2023 12:19:12 +0900 Subject: [PATCH 0395/1118] Fix diffcalc tests --- osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs index 880316f177..6a70173c4a 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchDifficultyCalculatorTest.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Catch.Tests { public class CatchDifficultyCalculatorTest : DifficultyCalculatorTest { - protected override string ResourceAssembly => "osu.Game.Rulesets.Catch"; + protected override string ResourceAssembly => "osu.Game.Rulesets.Catch.Tests"; [TestCase(4.0505463516206195d, 127, "diffcalc-test")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) From 0af16732b81a39ba6347bb6c036fe6a5b5a71800 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 13:38:46 +0900 Subject: [PATCH 0396/1118] Change default slider velocity to 1.4 This is the default in osu!stable and plays better than 1.0. --- osu.Game/Beatmaps/BeatmapDifficulty.cs | 2 +- osu.Game/Screens/Edit/Setup/DifficultySection.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 217f3b89a4..ac2267380d 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -18,7 +18,7 @@ namespace osu.Game.Beatmaps public float OverallDifficulty { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; public float ApproachRate { get; set; } = IBeatmapDifficultyInfo.DEFAULT_DIFFICULTY; - public double SliderMultiplier { get; set; } = 1; + public double SliderMultiplier { get; set; } = 1.4; public double SliderTickRate { get; set; } = 1; public BeatmapDifficulty() diff --git a/osu.Game/Screens/Edit/Setup/DifficultySection.cs b/osu.Game/Screens/Edit/Setup/DifficultySection.cs index 1915b0cfd1..8028df6c0f 100644 --- a/osu.Game/Screens/Edit/Setup/DifficultySection.cs +++ b/osu.Game/Screens/Edit/Setup/DifficultySection.cs @@ -88,7 +88,7 @@ namespace osu.Game.Screens.Edit.Setup Description = EditorSetupStrings.BaseVelocityDescription, Current = new BindableDouble(Beatmap.Difficulty.SliderMultiplier) { - Default = 1, + Default = 1.4, MinValue = 0.4, MaxValue = 3.6, Precision = 0.01f, From f9dd5bd828fc6f2df9677141649da5733eade565 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 13:39:59 +0900 Subject: [PATCH 0397/1118] Remove unused constant --- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 2551321ff2..b9d65d2631 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -31,11 +31,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps /// private const float osu_base_scoring_distance = 100; - /// - /// Drum roll distance that results in a duration of 1 speed-adjusted beat length. - /// - private const float taiko_base_distance = 100; - private readonly bool isForCurrentRuleset; public TaikoBeatmapConverter(IBeatmap beatmap, Ruleset ruleset) From 79826dee58b62c72f19352a03bd3da4db829f725 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 13:50:10 +0900 Subject: [PATCH 0398/1118] Fix tests which were relying on `SliderMultiplier==1` --- osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs | 6 +++++- osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs | 6 +++++- .../Gameplay/TestSceneDrawableScrollingRuleset.cs | 12 +++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs index 3c222662f5..8c179fe9a9 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneHyperDash.cs @@ -63,7 +63,11 @@ namespace osu.Game.Rulesets.Catch.Tests BeatmapInfo = { Ruleset = ruleset, - Difficulty = new BeatmapDifficulty { CircleSize = 3.6f } + Difficulty = new BeatmapDifficulty + { + CircleSize = 3.6f, + SliderMultiplier = 1, + }, } }; diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index d6a030ba64..716d2e0756 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -507,7 +507,11 @@ namespace osu.Game.Rulesets.Osu.Tests HitObjects = { slider }, BeatmapInfo = { - Difficulty = new BeatmapDifficulty { SliderTickRate = tickRate ?? 3 }, + Difficulty = new BeatmapDifficulty + { + SliderTickRate = tickRate ?? 3, + SliderMultiplier = 1, + }, Ruleset = new OsuRuleset().RulesetInfo, }, ControlPointInfo = cpi, diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 4c898feb48..697fb787e6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -251,7 +251,17 @@ namespace osu.Game.Tests.Visual.Gameplay /// The . private IBeatmap createBeatmap(Func createAction = null) { - var beatmap = new Beatmap { BeatmapInfo = { Ruleset = new OsuRuleset().RulesetInfo } }; + var beatmap = new Beatmap + { + BeatmapInfo = + { + Difficulty = new BeatmapDifficulty + { + SliderMultiplier = 1 + }, + Ruleset = new OsuRuleset().RulesetInfo + } + }; for (int i = 0; i < 10; i++) { From 160edcd2707d64aede30385eb9bf59ef4641fb7b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 6 Dec 2023 07:09:46 +0300 Subject: [PATCH 0399/1118] Move objects at a constant speed whenever possible --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 54 ++++++++++++++++++----- 1 file changed, 42 insertions(+), 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs index 16517a2f36..0defebc1c3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -27,8 +27,8 @@ namespace osu.Game.Rulesets.Osu.Mods public override double ScoreMultiplier => 1; public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModMagnetised), typeof(OsuModRepel), typeof(OsuModFreezeFrame), typeof(ModWithVisibilityAdjustment) }).ToArray(); - private static readonly Vector3 camera_position = new Vector3(OsuPlayfield.BASE_SIZE.X * 0.5f, OsuPlayfield.BASE_SIZE.Y * 0.5f, -100); - private readonly float minDepth = depthForScale(1.5f); + private static readonly Vector3 camera_position = new Vector3(OsuPlayfield.BASE_SIZE.X * 0.5f, OsuPlayfield.BASE_SIZE.Y * 0.5f, -200); + private readonly float sliderMinDepth = depthForScale(1.5f); // Depth at which slider's scale will be 1.5f [SettingSource("Maximum depth", "How far away objects appear.", 0)] public BindableFloat MaxDepth { get; } = new BindableFloat(100) @@ -80,30 +80,60 @@ namespace osu.Game.Rulesets.Osu.Mods switch (drawable) { case DrawableHitCircle circle: - processObject(time, circle, 0); + processHitObject(time, circle); break; case DrawableSlider slider: - processObject(time, slider, slider.HitObject.Duration); + processSlider(time, slider); break; } } } - private void processObject(double time, DrawableOsuHitObject drawable, double duration) + private void processHitObject(double time, DrawableOsuHitObject drawable) { var hitObject = drawable.HitObject; + // Circles are always moving at the constant speed. They'll fade out before reaching the camera even at extreme conditions (AR 11, max depth). + double speed = MaxDepth.Value / hitObject.TimePreempt; + double appearTime = hitObject.StartTime - hitObject.TimePreempt; + float z = MaxDepth.Value - (float)((Math.Max(time, appearTime) - appearTime) * speed); + + float scale = scaleForDepth(z); + drawable.Position = toPlayfieldPosition(scale, hitObject.Position); + drawable.Scale = new Vector2(scale); + } + + private void processSlider(double time, DrawableSlider drawableSlider) + { + var hitObject = drawableSlider.HitObject; + double baseSpeed = MaxDepth.Value / hitObject.TimePreempt; - double offsetAfterStartTime = duration + hitObject.MaximumJudgementOffset + 500; - double slowSpeed = Math.Min(-minDepth / offsetAfterStartTime, baseSpeed); + double appearTime = hitObject.StartTime - hitObject.TimePreempt; + + // Allow slider to move at a constant speed if its scale at the end time will be lower than 1.5f + float zEnd = MaxDepth.Value - (float)((Math.Max(hitObject.StartTime + hitObject.Duration, appearTime) - appearTime) * baseSpeed); + + if (zEnd > sliderMinDepth) + { + processHitObject(time, drawableSlider); + return; + } + + double offsetAfterStartTime = hitObject.Duration + 500; + double slowSpeed = Math.Min(-sliderMinDepth / offsetAfterStartTime, baseSpeed); double decelerationTime = hitObject.TimePreempt * 0.2; float decelerationDistance = (float)(decelerationTime * (baseSpeed + slowSpeed) * 0.5); float z; - if (time < hitObject.StartTime) + if (time < hitObject.StartTime - decelerationTime) + { + float fullDistance = decelerationDistance + (float)(baseSpeed * (hitObject.TimePreempt - decelerationTime)); + z = fullDistance - (float)((Math.Max(time, appearTime) - appearTime) * baseSpeed); + } + else if (time < hitObject.StartTime) { double timeOffset = time - (hitObject.StartTime - decelerationTime); double deceleration = (slowSpeed - baseSpeed) / decelerationTime; @@ -116,13 +146,13 @@ namespace osu.Game.Rulesets.Osu.Mods } float scale = scaleForDepth(z); - drawable.Position = toPlayfieldPosition(scale, hitObject.Position); - drawable.Scale = new Vector2(scale); + drawableSlider.Position = toPlayfieldPosition(scale, hitObject.Position); + drawableSlider.Scale = new Vector2(scale); } - private static float scaleForDepth(float depth) => 100 / (depth - camera_position.Z); + private static float scaleForDepth(float depth) => -camera_position.Z / Math.Max(1f, depth - camera_position.Z); - private static float depthForScale(float scale) => 100 / scale + camera_position.Z; + private static float depthForScale(float scale) => -camera_position.Z / scale + camera_position.Z; private static Vector2 toPlayfieldPosition(float scale, Vector2 positionAtZeroDepth) { From 394ea73055e5a7592daccde911bad0fd04f9093c Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 6 Dec 2023 14:50:03 +0900 Subject: [PATCH 0400/1118] Add some comments where truncations were added --- osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs | 2 ++ osu.Game.Rulesets.Catch/Objects/BananaShower.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 50e6fd9673..02d4cdbb94 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -247,6 +247,8 @@ namespace osu.Game.Rulesets.Catch.Beatmaps currentObject.DistanceToHyperDash = 0; int thisDirection = nextObject.EffectiveX > currentObject.EffectiveX ? 1 : -1; + + // Int truncation added to match osu!stable. double timeToNext = (int)nextObject.StartTime - (int)currentObject.StartTime - 1000f / 60f / 4; // 1/4th of a frame of grace time, taken from osu-stable double distanceToNext = Math.Abs(nextObject.EffectiveX - currentObject.EffectiveX) - (lastDirection == thisDirection ? lastExcess : halfCatcherWidth); float distanceToHyper = (float)(timeToNext * Catcher.BASE_DASH_SPEED - distanceToNext); diff --git a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs index abeb7fe61d..328cc2b52a 100644 --- a/osu.Game.Rulesets.Catch/Objects/BananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/BananaShower.cs @@ -23,6 +23,7 @@ namespace osu.Game.Rulesets.Catch.Objects private void createBananas(CancellationToken cancellationToken) { + // Int truncation added to match osu!stable. int startTime = (int)StartTime; int endTime = (int)EndTime; float spacing = (float)(EndTime - StartTime); From 43dc9082571901919914e43cbce34dd3db8ec89c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 15:59:29 +0900 Subject: [PATCH 0401/1118] Fix test value getting clobbered due to stupid stuff Don't even ask. Just smile and nod. --- .../Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs index 697fb787e6..e4d39bb6de 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneDrawableScrollingRuleset.cs @@ -198,7 +198,7 @@ namespace osu.Game.Tests.Visual.Gameplay { var beatmap = createBeatmap(); beatmap.ControlPointInfo.Add(0, new TimingControlPoint { BeatLength = time_range }); - beatmap.Difficulty.SliderMultiplier = 2; + beatmap.BeatmapInfo.Difficulty.SliderMultiplier = 2; createTest(beatmap); AddStep("adjust time range", () => drawableRuleset.TimeRange.Value = 2000); @@ -237,7 +237,7 @@ namespace osu.Game.Tests.Visual.Gameplay }); private void assertPosition(int index, float relativeY) => AddAssert($"hitobject {index} at {relativeY}", - () => Precision.AlmostEquals(getDrawableHitObject(index)?.DrawPosition.Y ?? -1, yScale * relativeY)); + () => getDrawableHitObject(index)?.DrawPosition.Y / yScale ?? -1, () => Is.EqualTo(relativeY).Within(Precision.FLOAT_EPSILON)); private void setTime(double time) { From b5bae566c23afa718762527a224f96f0c0eba219 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 14:22:50 +0900 Subject: [PATCH 0402/1118] Fix incorrect slider velocity being written on export for osu!taiko beatmaps --- osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index b375a6f7ff..78c663195a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -149,11 +149,7 @@ namespace osu.Game.Beatmaps.Formats writer.WriteLine(FormattableString.Invariant($"OverallDifficulty: {beatmap.Difficulty.OverallDifficulty}")); writer.WriteLine(FormattableString.Invariant($"ApproachRate: {beatmap.Difficulty.ApproachRate}")); - // Taiko adjusts the slider multiplier (see: LEGACY_TAIKO_VELOCITY_MULTIPLIER) - writer.WriteLine(onlineRulesetID == 1 - ? FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.SliderMultiplier / LEGACY_TAIKO_VELOCITY_MULTIPLIER}") - : FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.SliderMultiplier}")); - + writer.WriteLine(FormattableString.Invariant($"SliderMultiplier: {beatmap.Difficulty.SliderMultiplier}")); writer.WriteLine(FormattableString.Invariant($"SliderTickRate: {beatmap.Difficulty.SliderTickRate}")); } From 1cb3c710ba316a8d6642f25b9a4472fc37c75a8d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 14:30:35 +0900 Subject: [PATCH 0403/1118] Remove complex implementation of taiko SV multiplier --- .../Editor/TestSceneTaikoEditorSaving.cs | 7 +-- .../Beatmaps/TaikoBeatmapConverter.cs | 43 ------------------- 2 files changed, 1 insertion(+), 49 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs index af7db2251b..1d5efb01e4 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs @@ -3,7 +3,6 @@ using NUnit.Framework; using osu.Framework.Utils; -using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Taiko.Tests.Editor @@ -27,11 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor bool assertTaikoSliderMulitplier() { - // we can only assert value correctness on TaikoMultiplierAppliedDifficulty, because that is the final difficulty converted taiko beatmaps use. - // therefore, ensure that we have that difficulty type by calling .CopyFrom(), which is a no-op if the type is already correct. - var taikoDifficulty = new TaikoBeatmapConverter.TaikoMultiplierAppliedDifficulty(); - taikoDifficulty.CopyFrom(EditorBeatmap.Difficulty); - return Precision.AlmostEquals(taikoDifficulty.SliderMultiplier, 2); + return Precision.AlmostEquals(EditorBeatmap.Difficulty.SliderMultiplier, 2); } } } diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index b9d65d2631..5229d3ff23 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -10,7 +10,6 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Utils; using System.Threading; -using JetBrains.Annotations; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Beatmaps.Formats; @@ -43,12 +42,6 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) { - if (!(original.Difficulty is TaikoMultiplierAppliedDifficulty)) - { - // Rewrite the beatmap info to add the slider velocity multiplier - original.Difficulty = new TaikoMultiplierAppliedDifficulty(original.Difficulty); - } - Beatmap converted = base.ConvertBeatmap(original, cancellationToken); if (original.BeatmapInfo.Ruleset.OnlineID == 0) @@ -218,41 +211,5 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps } protected override Beatmap CreateBeatmap() => new TaikoBeatmap(); - - // Important to note that this is subclassing a realm object. - // Realm doesn't allow this, but for now this can work since we aren't (in theory?) persisting this to the database. - // It is only used during beatmap conversion and processing. - internal class TaikoMultiplierAppliedDifficulty : BeatmapDifficulty - { - public TaikoMultiplierAppliedDifficulty(IBeatmapDifficultyInfo difficulty) - { - CopyFrom(difficulty); - } - - [UsedImplicitly] - public TaikoMultiplierAppliedDifficulty() - { - } - - #region Overrides of BeatmapDifficulty - - public override BeatmapDifficulty Clone() => new TaikoMultiplierAppliedDifficulty(this); - - public override void CopyTo(BeatmapDifficulty other) - { - base.CopyTo(other); - if (!(other is TaikoMultiplierAppliedDifficulty)) - other.SliderMultiplier /= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; - } - - public override void CopyFrom(IBeatmapDifficultyInfo other) - { - base.CopyFrom(other); - if (!(other is TaikoMultiplierAppliedDifficulty)) - SliderMultiplier *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; - } - - #endregion - } } } From 8a0d152bcf74b3166019288f7860966624d25fe2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 14:57:52 +0900 Subject: [PATCH 0404/1118] Reapply legacy taiko velocity multiplier in all relevant places --- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 2 +- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 2 +- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 5229d3ff23..e63a65cd80 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -185,7 +185,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps else beatLength = timingPoint.BeatLength; - double sliderScoringPointDistance = osu_base_scoring_distance * beatmap.Difficulty.SliderMultiplier / beatmap.Difficulty.SliderTickRate; + double sliderScoringPointDistance = osu_base_scoring_distance * (beatmap.Difficulty.SliderMultiplier * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER) / beatmap.Difficulty.SliderTickRate; // The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll. double taikoVelocity = sliderScoringPointDistance * beatmap.Difficulty.SliderTickRate; diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 2a76782a08..6845ae2efa 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.Objects TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime); - double scoringDistance = base_distance * difficulty.SliderMultiplier * effectPoint.ScrollSpeed; + double scoringDistance = base_distance * (difficulty.SliderMultiplier * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER) * effectPoint.ScrollSpeed; Velocity = scoringDistance / timingPoint.BeatLength; TickRate = difficulty.SliderTickRate == 3 ? 3 : 4; diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 2af4c0c2e8..24391d544f 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -10,6 +10,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Beatmaps; +using osu.Game.Beatmaps.Formats; using osu.Game.Configuration; using osu.Game.Input.Handlers; using osu.Game.Replays; @@ -70,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected virtual double ComputeTimeRange() { // Taiko scrolls at a constant 100px per 1000ms. More notes become visible as the playfield is lengthened. - const float scroll_rate = 10; + const float scroll_rate = 10 / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; // Since the time range will depend on a positional value, it is referenced to the x480 pixel space. // Width is used because it defines how many notes fit on the playfield. From 1b50d1011ab7dcac5ff2135e13a10199d975a894 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 15:26:32 +0900 Subject: [PATCH 0405/1118] Move constant local to taiko --- .../Beatmaps/TaikoBeatmapConverter.cs | 16 +++++++++++++--- osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs | 8 ++++---- .../UI/DrawableTaikoRuleset.cs | 4 ++-- .../Beatmaps/Formats/LegacyBeatmapEncoder.cs | 6 ------ 4 files changed, 19 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index e63a65cd80..5975458f16 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -12,13 +12,23 @@ using osu.Framework.Utils; using System.Threading; using osu.Game.Audio; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Formats; using osu.Game.Rulesets.Objects.Legacy; namespace osu.Game.Rulesets.Taiko.Beatmaps { internal class TaikoBeatmapConverter : BeatmapConverter { + /// + /// A speed multiplier applied globally to osu!taiko. + /// + /// + /// osu! is generally slower than taiko, so a factor was historically added to increase speed for converts. + /// This must be used everywhere slider length or beat length is used in taiko. + /// + /// Of note, this has never been exposed to the end user, and is considered a hidden internal multiplier. + /// + public const float VELOCITY_MULTIPLIER = 1.4f; + /// /// Because swells are easier in taiko than spinners are in osu!, /// legacy taiko multiplies a factor when converting the number of required hits. @@ -173,7 +183,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps double distance = pathData.Path.ExpectedDistance.Value ?? 0; // Do not combine the following two lines! - distance *= LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + distance *= VELOCITY_MULTIPLIER; distance *= spans; TimingControlPoint timingPoint = beatmap.ControlPointInfo.TimingPointAt(obj.StartTime); @@ -185,7 +195,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps else beatLength = timingPoint.BeatLength; - double sliderScoringPointDistance = osu_base_scoring_distance * (beatmap.Difficulty.SliderMultiplier * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER) / beatmap.Difficulty.SliderTickRate; + double sliderScoringPointDistance = osu_base_scoring_distance * (beatmap.Difficulty.SliderMultiplier * TaikoBeatmapConverter.VELOCITY_MULTIPLIER) / beatmap.Difficulty.SliderTickRate; // The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll. double taikoVelocity = sliderScoringPointDistance * beatmap.Difficulty.SliderTickRate; diff --git a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs index 6845ae2efa..f3143de345 100644 --- a/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs +++ b/osu.Game.Rulesets.Taiko/Objects/DrumRoll.cs @@ -1,14 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Game.Rulesets.Objects.Types; using System.Threading; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Beatmaps.Formats; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Beatmaps; using osuTK; namespace osu.Game.Rulesets.Taiko.Objects @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Taiko.Objects TimingControlPoint timingPoint = controlPointInfo.TimingPointAt(StartTime); EffectControlPoint effectPoint = controlPointInfo.EffectPointAt(StartTime); - double scoringDistance = base_distance * (difficulty.SliderMultiplier * LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER) * effectPoint.ScrollSpeed; + double scoringDistance = base_distance * (difficulty.SliderMultiplier * TaikoBeatmapConverter.VELOCITY_MULTIPLIER) * effectPoint.ScrollSpeed; Velocity = scoringDistance / timingPoint.BeatLength; TickRate = difficulty.SliderTickRate == 3 ? 3 : 4; @@ -116,7 +116,7 @@ namespace osu.Game.Rulesets.Taiko.Objects double IHasDistance.Distance => Duration * Velocity; SliderPath IHasPath.Path - => new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER); + => new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(1) }, ((IHasDistance)this).Distance / TaikoBeatmapConverter.VELOCITY_MULTIPLIER); #endregion } diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 24391d544f..88085dfe97 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -10,13 +10,13 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Input; using osu.Game.Beatmaps; -using osu.Game.Beatmaps.Formats; using osu.Game.Configuration; using osu.Game.Input.Handlers; using osu.Game.Replays; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Taiko.Beatmaps; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Rulesets.Taiko.Replays; using osu.Game.Rulesets.Timing; @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Taiko.UI protected virtual double ComputeTimeRange() { // Taiko scrolls at a constant 100px per 1000ms. More notes become visible as the playfield is lengthened. - const float scroll_rate = 10 / LegacyBeatmapEncoder.LEGACY_TAIKO_VELOCITY_MULTIPLIER; + const float scroll_rate = 10 / TaikoBeatmapConverter.VELOCITY_MULTIPLIER; // Since the time range will depend on a positional value, it is referenced to the x480 pixel space. // Width is used because it defines how many notes fit on the playfield. diff --git a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs index 78c663195a..290d29090a 100644 --- a/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs +++ b/osu.Game/Beatmaps/Formats/LegacyBeatmapEncoder.cs @@ -23,12 +23,6 @@ namespace osu.Game.Beatmaps.Formats { public const int FIRST_LAZER_VERSION = 128; - /// - /// osu! is generally slower than taiko, so a factor is added to increase - /// speed. This must be used everywhere slider length or beat length is used. - /// - public const float LEGACY_TAIKO_VELOCITY_MULTIPLIER = 1.4f; - private readonly IBeatmap beatmap; private readonly ISkin? skin; From 51f9377e3de5181ad3a31697495e3e64deaa0c12 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 16:03:32 +0900 Subject: [PATCH 0406/1118] Remove pointless test --- .../Editor/TestSceneTaikoEditorSaving.cs | 33 ------------------- 1 file changed, 33 deletions(-) delete mode 100644 osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs deleted file mode 100644 index 1d5efb01e4..0000000000 --- a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using NUnit.Framework; -using osu.Framework.Utils; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Taiko.Tests.Editor -{ - public partial class TestSceneTaikoEditorSaving : EditorSavingTestScene - { - protected override Ruleset CreateRuleset() => new TaikoRuleset(); - - [Test] - public void TestTaikoSliderMultiplier() - { - AddStep("Set slider multiplier", () => EditorBeatmap.Difficulty.SliderMultiplier = 2); - - SaveEditor(); - - AddAssert("Beatmap has correct slider multiplier", assertTaikoSliderMulitplier); - - ReloadEditorToSameBeatmap(); - - AddAssert("Beatmap still has correct slider multiplier", assertTaikoSliderMulitplier); - - bool assertTaikoSliderMulitplier() - { - return Precision.AlmostEquals(EditorBeatmap.Difficulty.SliderMultiplier, 2); - } - } - } -} From b0878e36cf39baeb92da9b646e2a47c62acf7891 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Wed, 6 Dec 2023 10:30:21 +0300 Subject: [PATCH 0407/1118] Fix stacks having incorrect position --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs index 0defebc1c3..f71acf95b8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -100,7 +100,7 @@ namespace osu.Game.Rulesets.Osu.Mods float z = MaxDepth.Value - (float)((Math.Max(time, appearTime) - appearTime) * speed); float scale = scaleForDepth(z); - drawable.Position = toPlayfieldPosition(scale, hitObject.Position); + drawable.Position = toPlayfieldPosition(scale, hitObject.StackedPosition); drawable.Scale = new Vector2(scale); } @@ -146,7 +146,7 @@ namespace osu.Game.Rulesets.Osu.Mods } float scale = scaleForDepth(z); - drawableSlider.Position = toPlayfieldPosition(scale, hitObject.Position); + drawableSlider.Position = toPlayfieldPosition(scale, hitObject.StackedPosition); drawableSlider.Scale = new Vector2(scale); } From 01c614935b2edc68a1ee37de654e22406890021d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 17:09:12 +0900 Subject: [PATCH 0408/1118] Revert "Remove pointless test" This reverts commit 51f9377e3de5181ad3a31697495e3e64deaa0c12. --- .../Editor/TestSceneTaikoEditorSaving.cs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs new file mode 100644 index 0000000000..1d5efb01e4 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs @@ -0,0 +1,33 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Utils; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Taiko.Tests.Editor +{ + public partial class TestSceneTaikoEditorSaving : EditorSavingTestScene + { + protected override Ruleset CreateRuleset() => new TaikoRuleset(); + + [Test] + public void TestTaikoSliderMultiplier() + { + AddStep("Set slider multiplier", () => EditorBeatmap.Difficulty.SliderMultiplier = 2); + + SaveEditor(); + + AddAssert("Beatmap has correct slider multiplier", assertTaikoSliderMulitplier); + + ReloadEditorToSameBeatmap(); + + AddAssert("Beatmap still has correct slider multiplier", assertTaikoSliderMulitplier); + + bool assertTaikoSliderMulitplier() + { + return Precision.AlmostEquals(EditorBeatmap.Difficulty.SliderMultiplier, 2); + } + } + } +} From 853d67f9cc587bb0f86cf751cd96d013eefd3488 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 17:11:28 +0900 Subject: [PATCH 0409/1118] Add test coverage of correct multiplier written to `.osu` file --- .../Editor/TestSceneTaikoEditorSaving.cs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs index 1d5efb01e4..64ce97da7b 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs @@ -1,9 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Globalization; +using System.IO; +using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Utils; using osu.Game.Tests.Visual; +using SharpCompress.Archives.Zip; namespace osu.Game.Rulesets.Taiko.Tests.Editor { @@ -11,6 +17,40 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor { protected override Ruleset CreateRuleset() => new TaikoRuleset(); + [Test] + public void TestTaikoSliderMultiplierInExport() + { + AddStep("Set slider multiplier", () => EditorBeatmap.Difficulty.SliderMultiplier = 2); + + SaveEditor(); + AddStep("export beatmap", () => Game.BeatmapManager.Export(EditorBeatmap.BeatmapInfo.BeatmapSet!).WaitSafely()); + + AddAssert("check slider multiplier correct in file", () => + { + string export = LocalStorage.GetFiles("exports").First(); + + using (var stream = LocalStorage.GetStream(export)) + using (var zip = ZipArchive.Open(stream)) + { + using (var osuStream = zip.Entries.First().OpenEntryStream()) + using (var reader = new StreamReader(osuStream)) + { + string? line; + + while ((line = reader.ReadLine()) != null) + { + if (line.StartsWith("SliderMultiplier", StringComparison.Ordinal)) + { + return float.Parse(line.Split(':', StringSplitOptions.TrimEntries).Last(), provider: CultureInfo.InvariantCulture); + } + } + } + } + + return 0; + }, () => Is.EqualTo(2)); + } + [Test] public void TestTaikoSliderMultiplier() { From 44beecb840fae9db881b862caad12e2d339d0956 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 17:16:20 +0900 Subject: [PATCH 0410/1118] Test multiple values, including default --- .../Editor/TestSceneTaikoEditorSaving.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs index 64ce97da7b..fb05502158 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Editor/TestSceneTaikoEditorSaving.cs @@ -8,6 +8,7 @@ using System.Linq; using NUnit.Framework; using osu.Framework.Extensions; using osu.Framework.Utils; +using osu.Game.Beatmaps; using osu.Game.Tests.Visual; using SharpCompress.Archives.Zip; @@ -17,10 +18,14 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor { protected override Ruleset CreateRuleset() => new TaikoRuleset(); - [Test] - public void TestTaikoSliderMultiplierInExport() + [TestCase(null)] + [TestCase(1f)] + [TestCase(2f)] + [TestCase(2.4f)] + public void TestTaikoSliderMultiplierInExport(float? multiplier) { - AddStep("Set slider multiplier", () => EditorBeatmap.Difficulty.SliderMultiplier = 2); + if (multiplier.HasValue) + AddStep("Set slider multiplier", () => EditorBeatmap.Difficulty.SliderMultiplier = multiplier.Value); SaveEditor(); AddStep("export beatmap", () => Game.BeatmapManager.Export(EditorBeatmap.BeatmapInfo.BeatmapSet!).WaitSafely()); @@ -48,7 +53,7 @@ namespace osu.Game.Rulesets.Taiko.Tests.Editor } return 0; - }, () => Is.EqualTo(2)); + }, () => Is.EqualTo(multiplier ?? new BeatmapDifficulty().SliderMultiplier).Within(Precision.FLOAT_EPSILON)); } [Test] From ca991f1f546f0a20333d49265513466056661426 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 17:18:35 +0900 Subject: [PATCH 0411/1118] Move flags local to `EndlessPlayer` --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 4 ++++ osu.Game/Screens/Play/Player.cs | 4 ---- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index be7ddd115b..262ce263bd 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -290,6 +290,10 @@ namespace osu.Game.Overlays.SkinEditor { protected override UserActivity? InitialActivity => null; + public override bool DisallowExternalBeatmapRulesetChanges => true; + + public override bool? AllowGlobalTrackControl => false; + public EndlessPlayer(Func, Score> createScore) : base(createScore, new PlayerConfiguration { diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 48411e9c87..1c97efcff7 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -59,10 +59,6 @@ namespace osu.Game.Screens.Play protected override bool PlayExitSound => !isRestarting; - public override bool DisallowExternalBeatmapRulesetChanges => true; - - public override bool? AllowGlobalTrackControl => false; - protected override UserActivity InitialActivity => new UserActivity.InSoloGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); public override float BackgroundParallaxAmount => 0.1f; From faf60cec919637b0c8b04b3c69b15b909cfd90f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Dec 2023 10:11:41 +0100 Subject: [PATCH 0412/1118] Extend test coverage of skin editor open to fail better --- .../Visual/Navigation/TestSceneSkinEditorNavigation.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs index fa85c8c9f8..57f1b2fbe9 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneSkinEditorNavigation.cs @@ -283,15 +283,17 @@ namespace osu.Game.Tests.Visual.Navigation openSkinEditor(); } - [Test] - public void TestOpenSkinEditorGameplaySceneWhenDifferentRulesetActive() + [TestCase(1)] + [TestCase(2)] + [TestCase(3)] + public void TestOpenSkinEditorGameplaySceneWhenDifferentRulesetActive(int rulesetId) { BeatmapSetInfo beatmapSet = null!; AddStep("import beatmap", () => beatmapSet = BeatmapImportHelper.LoadQuickOszIntoOsu(Game).GetResultSafely()); - AddStep("select mania difficulty", () => + AddStep($"select difficulty for ruleset w/ ID {rulesetId}", () => { - var beatmap = beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == 3); + var beatmap = beatmapSet.Beatmaps.First(b => b.Ruleset.OnlineID == rulesetId); Game.Beatmap.Value = Game.BeatmapManager.GetWorkingBeatmap(beatmap); }); From f239d03d75aaeb02118f5a316c6febf135b291dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Dec 2023 10:12:25 +0100 Subject: [PATCH 0413/1118] Forcibly change ruleset to correct one before entering gameplay from main menu Closes #25663 (again). As it turns out, in some scenarios it can be the case that the current game-global `Beatmap` is not valid for the current game-global `Ruleset`. The validity of one and the other in conjunction is only really validated by the song select screen; elsewhere there is no guarantee that the global beatmap is playable using the global ruleset. However, this only comes up in very specific circumstances, namely one: when trying to autoplay a catch beatmap with osu! ruleset globally active via the skin editor flow. `Player` is responsible for retrieving the beatmap to be played. It does so by invoking the appropriate beatmap converter and asking it if the beatmap can be converted: https://github.com/ppy/osu/blob/6d64538d7a3130df63574eb75a8ebe044154c799/osu.Game/Beatmaps/WorkingBeatmap.cs#L262-L266 If the code above throws, `Player` actually silently covers for this, by trying the beatmap's default ruleset instead: https://github.com/ppy/osu/blob/6d64538d7a3130df63574eb75a8ebe044154c799/osu.Game/Screens/Play/Player.cs#L529-L536 However, for the pairing of osu! ruleset and catch beatmap, this fails, as `OsuBeatmapConverter`'s condition necessary for permitting conversion is that the objects have a defined position: https://github.com/ppy/osu/blob/6d64538d7a3130df63574eb75a8ebe044154c799/osu.Game.Rulesets.Osu/Beatmaps/OsuBeatmapConverter.cs#L25 which they will do, due to the fact that all catch beatmaps are really just osu! beatmaps but with conversion steps applied, and thus `Player` succeeds to load the catch beatmap in osu! ruleset. In the skin editor scenario, this would lead to the secondary failure of the skin editor trying to apply `CatchModAutoplay` on top of all of that, which would fail at the hard-cast of the beatmap to `CatchBeatmap`. --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index 262ce263bd..bedaf12c9b 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -17,6 +17,7 @@ using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Input.Bindings; +using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens; @@ -58,6 +59,9 @@ namespace osu.Game.Overlays.SkinEditor [Resolved] private Bindable> mods { get; set; } = null!; + [Resolved] + private Bindable ruleset { get; set; } = null!; + [Resolved] private IBindable beatmap { get; set; } = null!; @@ -153,7 +157,11 @@ namespace osu.Game.Overlays.SkinEditor if (screen is Player) return; - var replayGeneratingMod = beatmap.Value.BeatmapInfo.Ruleset.CreateInstance().GetAutoplayMod(); + // the validity of the current game-wide beatmap + ruleset combination is enforced by song select. + // if we're anywhere else, the state is unknown and may not make sense, so forcibly set something that does. + if (screen is not PlaySongSelect) + ruleset.Value = beatmap.Value.BeatmapInfo.Ruleset; + var replayGeneratingMod = ruleset.Value.CreateInstance().GetAutoplayMod(); IReadOnlyList usableMods = mods.Value; From b8694aba98631438d64d20820c05dd6351cee14f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 6 Dec 2023 22:00:35 +0900 Subject: [PATCH 0414/1118] Remove unnecessary prefix MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs index 5975458f16..010b1f0a7a 100644 --- a/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs +++ b/osu.Game.Rulesets.Taiko/Beatmaps/TaikoBeatmapConverter.cs @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Taiko.Beatmaps else beatLength = timingPoint.BeatLength; - double sliderScoringPointDistance = osu_base_scoring_distance * (beatmap.Difficulty.SliderMultiplier * TaikoBeatmapConverter.VELOCITY_MULTIPLIER) / beatmap.Difficulty.SliderTickRate; + double sliderScoringPointDistance = osu_base_scoring_distance * (beatmap.Difficulty.SliderMultiplier * VELOCITY_MULTIPLIER) / beatmap.Difficulty.SliderTickRate; // The velocity and duration of the taiko hit object - calculated as the velocity of a drum roll. double taikoVelocity = sliderScoringPointDistance * beatmap.Difficulty.SliderTickRate; From a8f3a0533ac72da3a7abd1696770c27a21b4c692 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Wed, 6 Dec 2023 16:35:59 +0100 Subject: [PATCH 0415/1118] Use 4th order BSpline by default --- .../Editor/TestSceneSliderPlacementBlueprint.cs | 8 ++++---- .../Sliders/Components/PathControlPointVisualiser.cs | 2 +- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs index f889999fe3..bbded55732 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneSliderPlacementBlueprint.cs @@ -312,7 +312,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertLength(808, tolerance: 10); assertControlPointCount(5); - assertControlPointType(0, PathType.BSpline(3)); + assertControlPointType(0, PathType.BSpline(4)); assertControlPointType(1, null); assertControlPointType(2, null); assertControlPointType(3, null); @@ -337,9 +337,9 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor assertPlaced(true); assertLength(600, tolerance: 10); assertControlPointCount(4); - assertControlPointType(0, PathType.BSpline(3)); - assertControlPointType(1, PathType.BSpline(3)); - assertControlPointType(2, PathType.BSpline(3)); + assertControlPointType(0, PathType.BSpline(4)); + assertControlPointType(1, PathType.BSpline(4)); + assertControlPointType(2, PathType.BSpline(4)); assertControlPointType(3, null); } diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 3add95b2b2..24e2210b45 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -373,7 +373,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components curveTypeItems.Add(createMenuItemForPathType(PathType.LINEAR)); curveTypeItems.Add(createMenuItemForPathType(PathType.PERFECT_CURVE)); curveTypeItems.Add(createMenuItemForPathType(PathType.BEZIER)); - curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(3))); + curveTypeItems.Add(createMenuItemForPathType(PathType.BSpline(4))); if (selectedPieces.Any(piece => piece.ControlPoint.Type?.Type == SplineType.Catmull)) curveTypeItems.Add(createMenuItemForPathType(PathType.CATMULL)); diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index e13a46d0cd..672ac43f2c 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -49,7 +49,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders [Resolved(CanBeNull = true)] private FreehandSliderToolboxGroup freehandToolboxGroup { get; set; } - private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder(); + private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder { Degree = 4 }; protected override bool IsValidForPlacement => HitObject.Path.HasValidLength; @@ -239,7 +239,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders { if (state == SliderPlacementState.Drawing) { - segmentStart.Type = PathType.BSpline(3); + segmentStart.Type = PathType.BSpline(4); return; } @@ -335,7 +335,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (segment.Count == 0) continue; - HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[0], PathType.BSpline(3))); + HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[0], PathType.BSpline(4))); for (int j = 1; j < segment.Count - 1; j++) HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[j])); From 22287f3a7f3acc256f13d5759665e2dbf4a27983 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Wed, 6 Dec 2023 16:36:24 +0100 Subject: [PATCH 0416/1118] decrease max tolerance --- osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs index 87dd8636c9..7d1eb4e270 100644 --- a/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Edit public BindableFloat Tolerance { get; } = new BindableFloat(2f) { MinValue = 0.05f, - MaxValue = 3f, + MaxValue = 2.0f, Precision = 0.01f }; @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Edit }; // We map internal ranges to a more standard range of values for display to the user. - private readonly BindableInt displayTolerance = new BindableInt(66) + private readonly BindableInt displayTolerance = new BindableInt(100) { MinValue = 5, MaxValue = 100 @@ -90,8 +90,8 @@ namespace osu.Game.Rulesets.Osu.Edit displayCornerThreshold.Value = internalToDisplayCornerThreshold(threshold.NewValue) ); - float displayToInternalTolerance(float v) => v / 33f; - int internalToDisplayTolerance(float v) => (int)Math.Round(v * 33f); + float displayToInternalTolerance(float v) => v / 50f; + int internalToDisplayTolerance(float v) => (int)Math.Round(v * 50f); float displayToInternalCornerThreshold(float v) => v / 100f; int internalToDisplayCornerThreshold(float v) => (int)Math.Round(v * 100f); From cb823f367f92fdeaf4db1a7ea3f75777f5a787bd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Dec 2023 18:16:45 +0100 Subject: [PATCH 0417/1118] Simplify `UserActivity` for serialisability over the wire Up until now, the `UserActivity` class hierarchy contained things like beatmap info, room info, full replay info, etc. While this was convenient, it is soon going to be less so, as the data is sent over the wire to the spectator server so that the user's activity can be broadcast to other clients. To counteract this without creating a second separate and slimmed-down class hierarchy, slim down the `UserActivity` structure to contain the bare minimum amounts of data such that the structures aren't overly large and complex to serialise, but also contain enough data that they can be used by receiving clients directly without having to do beatmap or score lookups. --- osu.Desktop/DiscordRichPresence.cs | 36 +-- osu.Game/Online/Chat/NowPlayingCommand.cs | 21 +- osu.Game/Online/SignalRWorkaroundTypes.cs | 16 ++ osu.Game/Users/UserActivity.cs | 276 +++++++++++++++------- 4 files changed, 227 insertions(+), 122 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index caf0a1d9fd..c66725e3e3 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -9,7 +9,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Logging; -using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Extensions; using osu.Game.Online.API; @@ -95,17 +94,18 @@ namespace osu.Desktop if (status.Value is UserStatusOnline && activity.Value != null) { - presence.State = truncate(activity.Value.GetStatus(privacyMode.Value == DiscordRichPresenceMode.Limited)); - presence.Details = truncate(getDetails(activity.Value)); + bool hideIdentifiableInformation = privacyMode.Value == DiscordRichPresenceMode.Limited; + presence.State = truncate(activity.Value.GetStatus(hideIdentifiableInformation)); + presence.Details = truncate(activity.Value.GetDetails(hideIdentifiableInformation) ?? string.Empty); - if (getBeatmap(activity.Value) is IBeatmapInfo beatmap && beatmap.OnlineID > 0) + if (getBeatmapID(activity.Value) is int beatmapId && beatmapId > 0) { presence.Buttons = new[] { new Button { Label = "View beatmap", - Url = $@"{api.WebsiteRootUrl}/beatmapsets/{beatmap.BeatmapSet?.OnlineID}#{ruleset.Value.ShortName}/{beatmap.OnlineID}" + Url = $@"{api.WebsiteRootUrl}/beatmaps/{beatmapId}?mode={ruleset.Value.ShortName}" } }; } @@ -159,40 +159,20 @@ namespace osu.Desktop }); } - private IBeatmapInfo? getBeatmap(UserActivity activity) + private int? getBeatmapID(UserActivity activity) { switch (activity) { case UserActivity.InGame game: - return game.BeatmapInfo; + return game.BeatmapID; case UserActivity.EditingBeatmap edit: - return edit.BeatmapInfo; + return edit.BeatmapID; } return null; } - private string getDetails(UserActivity activity) - { - switch (activity) - { - case UserActivity.InGame game: - return game.BeatmapInfo.ToString() ?? string.Empty; - - case UserActivity.EditingBeatmap edit: - return edit.BeatmapInfo.ToString() ?? string.Empty; - - case UserActivity.WatchingReplay watching: - return watching.BeatmapInfo?.ToString() ?? string.Empty; - - case UserActivity.InLobby lobby: - return privacyMode.Value == DiscordRichPresenceMode.Limited ? string.Empty : lobby.Room.Name.Value; - } - - return string.Empty; - } - protected override void Dispose(bool isDisposing) { client.Dispose(); diff --git a/osu.Game/Online/Chat/NowPlayingCommand.cs b/osu.Game/Online/Chat/NowPlayingCommand.cs index e7018d6993..0e6f6f0bf6 100644 --- a/osu.Game/Online/Chat/NowPlayingCommand.cs +++ b/osu.Game/Online/Chat/NowPlayingCommand.cs @@ -7,7 +7,6 @@ using System.Text; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Framework.Localisation; using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Rulesets; @@ -33,9 +32,6 @@ namespace osu.Game.Online.Chat [Resolved] private IBindable currentRuleset { get; set; } = null!; - [Resolved] - private LocalisationManager localisation { get; set; } = null!; - private readonly Channel? target; /// @@ -52,23 +48,28 @@ namespace osu.Game.Online.Chat base.LoadComplete(); string verb; - IBeatmapInfo beatmapInfo; + + int beatmapOnlineID; + string beatmapDisplayTitle; switch (api.Activity.Value) { case UserActivity.InGame game: verb = "playing"; - beatmapInfo = game.BeatmapInfo; + beatmapOnlineID = game.BeatmapID; + beatmapDisplayTitle = game.BeatmapDisplayTitle; break; case UserActivity.EditingBeatmap edit: verb = "editing"; - beatmapInfo = edit.BeatmapInfo; + beatmapOnlineID = edit.BeatmapID; + beatmapDisplayTitle = edit.BeatmapDisplayTitle; break; default: verb = "listening to"; - beatmapInfo = currentBeatmap.Value.BeatmapInfo; + beatmapOnlineID = currentBeatmap.Value.BeatmapInfo.OnlineID; + beatmapDisplayTitle = currentBeatmap.Value.BeatmapInfo.GetDisplayTitle(); break; } @@ -86,9 +87,7 @@ namespace osu.Game.Online.Chat string getBeatmapPart() { - string beatmapInfoString = localisation.GetLocalisedBindableString(beatmapInfo.GetDisplayTitleRomanisable()).Value; - - return beatmapInfo.OnlineID > 0 ? $"[{api.WebsiteRootUrl}/b/{beatmapInfo.OnlineID} {beatmapInfoString}]" : beatmapInfoString; + return beatmapOnlineID > 0 ? $"[{api.WebsiteRootUrl}/b/{beatmapOnlineID} {beatmapDisplayTitle}]" : beatmapDisplayTitle; } string getRulesetPart() diff --git a/osu.Game/Online/SignalRWorkaroundTypes.cs b/osu.Game/Online/SignalRWorkaroundTypes.cs index 0e3eb0aab0..59a12b3bf1 100644 --- a/osu.Game/Online/SignalRWorkaroundTypes.cs +++ b/osu.Game/Online/SignalRWorkaroundTypes.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using osu.Game.Online.Multiplayer; using osu.Game.Online.Multiplayer.Countdown; using osu.Game.Online.Multiplayer.MatchTypes.TeamVersus; +using osu.Game.Users; namespace osu.Game.Online { @@ -18,6 +19,7 @@ namespace osu.Game.Online { internal static readonly IReadOnlyList<(Type derivedType, Type baseType)> BASE_TYPE_MAPPING = new[] { + // multiplayer (typeof(ChangeTeamRequest), typeof(MatchUserRequest)), (typeof(StartMatchCountdownRequest), typeof(MatchUserRequest)), (typeof(StopCountdownRequest), typeof(MatchUserRequest)), @@ -28,6 +30,20 @@ namespace osu.Game.Online (typeof(MatchStartCountdown), typeof(MultiplayerCountdown)), (typeof(ForceGameplayStartCountdown), typeof(MultiplayerCountdown)), (typeof(ServerShuttingDownCountdown), typeof(MultiplayerCountdown)), + + // metadata + (typeof(UserActivity.ChoosingBeatmap), typeof(UserActivity)), + (typeof(UserActivity.InSoloGame), typeof(UserActivity)), + (typeof(UserActivity.WatchingReplay), typeof(UserActivity)), + (typeof(UserActivity.SpectatingUser), typeof(UserActivity)), + (typeof(UserActivity.SearchingForLobby), typeof(UserActivity)), + (typeof(UserActivity.InLobby), typeof(UserActivity)), + (typeof(UserActivity.InMultiplayerGame), typeof(UserActivity)), + (typeof(UserActivity.SpectatingMultiplayerGame), typeof(UserActivity)), + (typeof(UserActivity.InPlaylistGame), typeof(UserActivity)), + (typeof(UserActivity.EditingBeatmap), typeof(UserActivity)), + (typeof(UserActivity.ModdingBeatmap), typeof(UserActivity)), + (typeof(UserActivity.TestingBeatmap), typeof(UserActivity)), }; } } diff --git a/osu.Game/Users/UserActivity.cs b/osu.Game/Users/UserActivity.cs index c82f642fdc..1b09666df6 100644 --- a/osu.Game/Users/UserActivity.cs +++ b/osu.Game/Users/UserActivity.cs @@ -1,8 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using MessagePack; using osu.Game.Beatmaps; using osu.Game.Graphics; +using osu.Game.Online; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Scoring; @@ -10,43 +13,84 @@ using osuTK.Graphics; namespace osu.Game.Users { + /// + /// Base class for all structures describing the user's current activity. + /// + /// + /// Warning: keep specs consistent with + /// . + /// + [Serializable] + [MessagePackObject] + [Union(11, typeof(ChoosingBeatmap))] + [Union(12, typeof(InSoloGame))] + [Union(13, typeof(WatchingReplay))] + [Union(14, typeof(SpectatingUser))] + [Union(21, typeof(SearchingForLobby))] + [Union(22, typeof(InLobby))] + [Union(23, typeof(InMultiplayerGame))] + [Union(24, typeof(SpectatingMultiplayerGame))] + [Union(31, typeof(InPlaylistGame))] + [Union(41, typeof(EditingBeatmap))] + [Union(42, typeof(ModdingBeatmap))] + [Union(43, typeof(TestingBeatmap))] public abstract class UserActivity { public abstract string GetStatus(bool hideIdentifiableInformation = false); + public virtual string? GetDetails(bool hideIdentifiableInformation = false) => null; public virtual Color4 GetAppropriateColour(OsuColour colours) => colours.GreenDarker; - public class ModdingBeatmap : EditingBeatmap - { - public override string GetStatus(bool hideIdentifiableInformation = false) => "Modding a beatmap"; - public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark; - - public ModdingBeatmap(IBeatmapInfo info) - : base(info) - { - } - } - + [MessagePackObject] public class ChoosingBeatmap : UserActivity { public override string GetStatus(bool hideIdentifiableInformation = false) => "Choosing a beatmap"; } + [MessagePackObject] public abstract class InGame : UserActivity { - public IBeatmapInfo BeatmapInfo { get; } + [Key(0)] + public int BeatmapID { get; set; } - public IRulesetInfo Ruleset { get; } + [Key(1)] + public string BeatmapDisplayTitle { get; set; } = string.Empty; + + [Key(2)] + public int RulesetID { get; set; } + + [Key(3)] + public string RulesetPlayingVerb { get; set; } = string.Empty; // TODO: i'm going with this for now, but this is wasteful protected InGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset) { - BeatmapInfo = beatmapInfo; - Ruleset = ruleset; + BeatmapID = beatmapInfo.OnlineID; + BeatmapDisplayTitle = beatmapInfo.GetDisplayTitle(); + + RulesetID = ruleset.OnlineID; + RulesetPlayingVerb = ruleset.CreateInstance().PlayingVerb; } - public override string GetStatus(bool hideIdentifiableInformation = false) => Ruleset.CreateInstance().PlayingVerb; + [SerializationConstructor] + protected InGame() { } + + public override string GetStatus(bool hideIdentifiableInformation = false) => RulesetPlayingVerb; + public override string GetDetails(bool hideIdentifiableInformation = false) => BeatmapDisplayTitle; } + [MessagePackObject] + public class InSoloGame : InGame + { + public InSoloGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset) + : base(beatmapInfo, ruleset) + { + } + + [SerializationConstructor] + public InSoloGame() { } + } + + [MessagePackObject] public class InMultiplayerGame : InGame { public InMultiplayerGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset) @@ -54,9 +98,122 @@ namespace osu.Game.Users { } + [SerializationConstructor] + public InMultiplayerGame() + { + } + public override string GetStatus(bool hideIdentifiableInformation = false) => $@"{base.GetStatus(hideIdentifiableInformation)} with others"; } + [MessagePackObject] + public class InPlaylistGame : InGame + { + public InPlaylistGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset) + : base(beatmapInfo, ruleset) + { + } + + [SerializationConstructor] + public InPlaylistGame() { } + } + + [MessagePackObject] + public class TestingBeatmap : InGame + { + public TestingBeatmap(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset) + : base(beatmapInfo, ruleset) + { + } + + [SerializationConstructor] + public TestingBeatmap() { } + + public override string GetStatus(bool hideIdentifiableInformation = false) => "Testing a beatmap"; + } + + [MessagePackObject] + public class EditingBeatmap : UserActivity + { + [Key(0)] + public int BeatmapID { get; set; } + + [Key(1)] + public string BeatmapDisplayTitle { get; set; } = string.Empty; + + public EditingBeatmap(IBeatmapInfo info) + { + BeatmapID = info.OnlineID; + BeatmapDisplayTitle = info.GetDisplayTitle(); + } + + [SerializationConstructor] + public EditingBeatmap() { } + + public override string GetStatus(bool hideIdentifiableInformation = false) => @"Editing a beatmap"; + public override string GetDetails(bool hideIdentifiableInformation = false) => BeatmapDisplayTitle; + } + + [MessagePackObject] + public class ModdingBeatmap : EditingBeatmap + { + public ModdingBeatmap(IBeatmapInfo info) + : base(info) + { + } + + [SerializationConstructor] + public ModdingBeatmap() { } + + public override string GetStatus(bool hideIdentifiableInformation = false) => "Modding a beatmap"; + public override Color4 GetAppropriateColour(OsuColour colours) => colours.PurpleDark; + } + + [MessagePackObject] + public class WatchingReplay : UserActivity + { + [Key(0)] + public long ScoreID { get; set; } + + [Key(1)] + public string PlayerName { get; set; } = string.Empty; + + [Key(2)] + public int BeatmapID { get; set; } + + [Key(3)] + public string? BeatmapDisplayTitle { get; set; } + + public WatchingReplay(ScoreInfo score) + { + ScoreID = score.OnlineID; + PlayerName = score.User.Username; + BeatmapID = score.BeatmapInfo?.OnlineID ?? -1; + BeatmapDisplayTitle = score.BeatmapInfo?.GetDisplayTitle(); + } + + [SerializationConstructor] + public WatchingReplay() { } + + public override string GetStatus(bool hideIdentifiableInformation = false) => hideIdentifiableInformation ? @"Watching a replay" : $@"Watching {PlayerName}'s replay"; + public override string? GetDetails(bool hideIdentifiableInformation = false) => BeatmapDisplayTitle; + } + + [MessagePackObject] + public class SpectatingUser : WatchingReplay + { + public SpectatingUser(ScoreInfo score) + : base(score) + { + } + + [SerializationConstructor] + public SpectatingUser() { } + + public override string GetStatus(bool hideIdentifiableInformation = false) => hideIdentifiableInformation ? @"Spectating a user" : $@"Spectating {PlayerName}"; + } + + [MessagePackObject] public class SpectatingMultiplayerGame : InGame { public SpectatingMultiplayerGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset) @@ -64,88 +221,41 @@ namespace osu.Game.Users { } + [SerializationConstructor] + public SpectatingMultiplayerGame() { } + public override string GetStatus(bool hideIdentifiableInformation = false) => $"Watching others {base.GetStatus(hideIdentifiableInformation).ToLowerInvariant()}"; } - public class InPlaylistGame : InGame - { - public InPlaylistGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset) - : base(beatmapInfo, ruleset) - { - } - } - - public class InSoloGame : InGame - { - public InSoloGame(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset) - : base(beatmapInfo, ruleset) - { - } - } - - public class TestingBeatmap : InGame - { - public override string GetStatus(bool hideIdentifiableInformation = false) => "Testing a beatmap"; - - public TestingBeatmap(IBeatmapInfo beatmapInfo, IRulesetInfo ruleset) - : base(beatmapInfo, ruleset) - { - } - } - - public class EditingBeatmap : UserActivity - { - public IBeatmapInfo BeatmapInfo { get; } - - public EditingBeatmap(IBeatmapInfo info) - { - BeatmapInfo = info; - } - - public override string GetStatus(bool hideIdentifiableInformation = false) => @"Editing a beatmap"; - } - - public class WatchingReplay : UserActivity - { - private readonly ScoreInfo score; - - protected string Username => score.User.Username; - - public BeatmapInfo? BeatmapInfo => score.BeatmapInfo; - - public WatchingReplay(ScoreInfo score) - { - this.score = score; - } - - public override string GetStatus(bool hideIdentifiableInformation = false) => hideIdentifiableInformation ? @"Watching a replay" : $@"Watching {Username}'s replay"; - } - - public class SpectatingUser : WatchingReplay - { - public override string GetStatus(bool hideIdentifiableInformation = false) => hideIdentifiableInformation ? @"Spectating a user" : $@"Spectating {Username}"; - - public SpectatingUser(ScoreInfo score) - : base(score) - { - } - } - + [MessagePackObject] public class SearchingForLobby : UserActivity { public override string GetStatus(bool hideIdentifiableInformation = false) => @"Looking for a lobby"; } + [MessagePackObject] public class InLobby : UserActivity { - public override string GetStatus(bool hideIdentifiableInformation = false) => @"In a lobby"; + [Key(0)] + public long RoomID { get; set; } - public readonly Room Room; + [Key(1)] + public string RoomName { get; set; } = string.Empty; public InLobby(Room room) { - Room = room; + RoomID = room.RoomID.Value ?? -1; + RoomName = room.Name.Value; } + + [SerializationConstructor] + public InLobby() { } + + public override string GetStatus(bool hideIdentifiableInformation = false) => @"In a lobby"; + + public override string? GetDetails(bool hideIdentifiableInformation = false) => hideIdentifiableInformation + ? null + : RoomName; } } } From d66fa093205320b3a09a20ded2632b68dbf3fe21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Dec 2023 18:21:44 +0100 Subject: [PATCH 0418/1118] Simplify `UserStatus` to be an enumeration type There were absolutely no gains from having it be a reference type / class, only complications, especially when coming from the serialisation angle. --- osu.Desktop/DiscordRichPresence.cs | 6 +-- .../Online/TestSceneUserClickableAvatar.cs | 2 +- .../Visual/Online/TestSceneUserPanel.cs | 18 ++++---- osu.Game/Online/API/APIAccess.cs | 2 +- .../Online/API/Requests/Responses/APIUser.cs | 2 +- osu.Game/Overlays/Login/LoginPanel.cs | 6 +-- osu.Game/Users/ExtendedUserPanel.cs | 17 +++---- osu.Game/Users/UserStatus.cs | 46 +++++++++++-------- 8 files changed, 53 insertions(+), 46 deletions(-) diff --git a/osu.Desktop/DiscordRichPresence.cs b/osu.Desktop/DiscordRichPresence.cs index c66725e3e3..f990fd55fc 100644 --- a/osu.Desktop/DiscordRichPresence.cs +++ b/osu.Desktop/DiscordRichPresence.cs @@ -33,7 +33,7 @@ namespace osu.Desktop [Resolved] private IAPIProvider api { get; set; } = null!; - private readonly IBindable status = new Bindable(); + private readonly IBindable status = new Bindable(); private readonly IBindable activity = new Bindable(); private readonly Bindable privacyMode = new Bindable(); @@ -86,13 +86,13 @@ namespace osu.Desktop if (!client.IsInitialized) return; - if (status.Value is UserStatusOffline || privacyMode.Value == DiscordRichPresenceMode.Off) + if (status.Value == UserStatus.Offline || privacyMode.Value == DiscordRichPresenceMode.Off) { client.ClearPresence(); return; } - if (status.Value is UserStatusOnline && activity.Value != null) + if (status.Value == UserStatus.Online && activity.Value != null) { bool hideIdentifiableInformation = privacyMode.Value == DiscordRichPresenceMode.Limited; presence.State = truncate(activity.Value.GetStatus(hideIdentifiableInformation)); diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs index 9edaa841b2..4539eae25f 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserClickableAvatar.cs @@ -64,7 +64,7 @@ namespace osu.Game.Tests.Visual.Online Colour = color ?? "000000", Status = { - Value = new UserStatusOnline() + Value = UserStatus.Online }, }; diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs index c61b572d8c..b3b8fd78d3 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Online public partial class TestSceneUserPanel : OsuTestScene { private readonly Bindable activity = new Bindable(); - private readonly Bindable status = new Bindable(); + private readonly Bindable status = new Bindable(); private UserGridPanel boundPanel1; private TestUserListPanel boundPanel2; @@ -66,7 +66,7 @@ namespace osu.Game.Tests.Visual.Online Id = 3103765, CountryCode = CountryCode.JP, CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", - Status = { Value = new UserStatusOnline() } + Status = { Value = UserStatus.Online } }) { Width = 300 }, boundPanel1 = new UserGridPanel(new APIUser { @@ -99,16 +99,16 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestUserStatus() { - AddStep("online", () => status.Value = new UserStatusOnline()); - AddStep("do not disturb", () => status.Value = new UserStatusDoNotDisturb()); - AddStep("offline", () => status.Value = new UserStatusOffline()); + AddStep("online", () => status.Value = UserStatus.Online); + AddStep("do not disturb", () => status.Value = UserStatus.DoNotDisturb); + AddStep("offline", () => status.Value = UserStatus.Offline); AddStep("null status", () => status.Value = null); } [Test] public void TestUserActivity() { - AddStep("set online status", () => status.Value = new UserStatusOnline()); + AddStep("set online status", () => status.Value = UserStatus.Online); AddStep("idle", () => activity.Value = null); AddStep("watching replay", () => activity.Value = new UserActivity.WatchingReplay(createScore(@"nats"))); @@ -127,12 +127,12 @@ namespace osu.Game.Tests.Visual.Online public void TestUserActivityChange() { AddAssert("visit message is visible", () => boundPanel2.LastVisitMessage.IsPresent); - AddStep("set online status", () => status.Value = new UserStatusOnline()); + AddStep("set online status", () => status.Value = UserStatus.Online); AddAssert("visit message is not visible", () => !boundPanel2.LastVisitMessage.IsPresent); AddStep("set choosing activity", () => activity.Value = new UserActivity.ChoosingBeatmap()); - AddStep("set offline status", () => status.Value = new UserStatusOffline()); + AddStep("set offline status", () => status.Value = UserStatus.Offline); AddAssert("visit message is visible", () => boundPanel2.LastVisitMessage.IsPresent); - AddStep("set online status", () => status.Value = new UserStatusOnline()); + AddStep("set online status", () => status.Value = UserStatus.Online); AddAssert("visit message is not visible", () => !boundPanel2.LastVisitMessage.IsPresent); } diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 4f586c8fff..21107d61fc 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -247,7 +247,7 @@ namespace osu.Game.Online.API userReq.Success += user => { // todo: save/pull from settings - user.Status.Value = new UserStatusOnline(); + user.Status.Value = UserStatus.Online; setLocalUser(user); diff --git a/osu.Game/Online/API/Requests/Responses/APIUser.cs b/osu.Game/Online/API/Requests/Responses/APIUser.cs index 2ee66453cf..56eec19fa1 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUser.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUser.cs @@ -43,7 +43,7 @@ namespace osu.Game.Online.API.Requests.Responses set => countryCodeString = value.ToString(); } - public readonly Bindable Status = new Bindable(); + public readonly Bindable Status = new Bindable(); public readonly Bindable Activity = new Bindable(); diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs index 71ecf2e75a..19af95459f 100644 --- a/osu.Game/Overlays/Login/LoginPanel.cs +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -148,17 +148,17 @@ namespace osu.Game.Overlays.Login switch (action.NewValue) { case UserAction.Online: - api.LocalUser.Value.Status.Value = new UserStatusOnline(); + api.LocalUser.Value.Status.Value = UserStatus.Online; dropdown.StatusColour = colours.Green; break; case UserAction.DoNotDisturb: - api.LocalUser.Value.Status.Value = new UserStatusDoNotDisturb(); + api.LocalUser.Value.Status.Value = UserStatus.DoNotDisturb; dropdown.StatusColour = colours.Red; break; case UserAction.AppearOffline: - api.LocalUser.Value.Status.Value = new UserStatusOffline(); + api.LocalUser.Value.Status.Value = UserStatus.Offline; dropdown.StatusColour = colours.Gray7; break; diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index 3c1b68f9ef..18fe852556 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -6,6 +6,7 @@ using osuTK; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Graphics; @@ -18,7 +19,7 @@ namespace osu.Game.Users { public abstract partial class ExtendedUserPanel : UserPanel { - public readonly Bindable Status = new Bindable(); + public readonly Bindable Status = new Bindable(); public readonly IBindable Activity = new Bindable(); @@ -97,14 +98,14 @@ namespace osu.Game.Users return statusContainer; } - private void displayStatus(UserStatus status, UserActivity activity = null) + private void displayStatus(UserStatus? status, UserActivity activity = null) { if (status != null) { - LastVisitMessage.FadeTo(status is UserStatusOffline && User.LastVisit.HasValue ? 1 : 0); + LastVisitMessage.FadeTo(status == UserStatus.Offline && User.LastVisit.HasValue ? 1 : 0); // Set status message based on activity (if we have one) and status is not offline - if (activity != null && !(status is UserStatusOffline)) + if (activity != null && status != UserStatus.Offline) { statusMessage.Text = activity.GetStatus(); statusIcon.FadeColour(activity.GetAppropriateColour(Colours), 500, Easing.OutQuint); @@ -112,8 +113,8 @@ namespace osu.Game.Users } // Otherwise use only status - statusMessage.Text = status.Message; - statusIcon.FadeColour(status.GetAppropriateColour(Colours), 500, Easing.OutQuint); + statusMessage.Text = status.GetLocalisableDescription(); + statusIcon.FadeColour(status.Value.GetAppropriateColour(Colours), 500, Easing.OutQuint); return; } @@ -121,11 +122,11 @@ namespace osu.Game.Users // Fallback to web status if local one is null if (User.IsOnline) { - Status.Value = new UserStatusOnline(); + Status.Value = UserStatus.Online; return; } - Status.Value = new UserStatusOffline(); + Status.Value = UserStatus.Offline; } protected override bool OnHover(HoverEvent e) diff --git a/osu.Game/Users/UserStatus.cs b/osu.Game/Users/UserStatus.cs index ffd86b78c7..cd25add4d1 100644 --- a/osu.Game/Users/UserStatus.cs +++ b/osu.Game/Users/UserStatus.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.ComponentModel; using osu.Framework.Localisation; using osuTK.Graphics; using osu.Game.Graphics; @@ -8,32 +10,36 @@ using osu.Game.Resources.Localisation.Web; namespace osu.Game.Users { - public abstract class UserStatus + public enum UserStatus { - public abstract LocalisableString Message { get; } - public abstract Color4 GetAppropriateColour(OsuColour colours); + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOffline))] + Offline, + + [Description("Do not disturb")] + DoNotDisturb, + + [LocalisableDescription(typeof(UsersStrings), nameof(UsersStrings.StatusOnline))] + Online, } - public class UserStatusOnline : UserStatus + public static class UserStatusExtensions { - public override LocalisableString Message => UsersStrings.StatusOnline; - public override Color4 GetAppropriateColour(OsuColour colours) => colours.GreenLight; - } + public static Color4 GetAppropriateColour(this UserStatus userStatus, OsuColour colours) + { + switch (userStatus) + { + case UserStatus.Offline: + return Color4.Black; - public abstract class UserStatusBusy : UserStatusOnline - { - public override Color4 GetAppropriateColour(OsuColour colours) => colours.YellowDark; - } + case UserStatus.DoNotDisturb: + return colours.RedDark; - public class UserStatusOffline : UserStatus - { - public override LocalisableString Message => UsersStrings.StatusOffline; - public override Color4 GetAppropriateColour(OsuColour colours) => Color4.Black; - } + case UserStatus.Online: + return colours.GreenDark; - public class UserStatusDoNotDisturb : UserStatus - { - public override LocalisableString Message => "Do not disturb"; - public override Color4 GetAppropriateColour(OsuColour colours) => colours.RedDark; + default: + throw new ArgumentOutOfRangeException(nameof(userStatus), userStatus, "Unsupported user status"); + } + } } } From 602550b9c233280d81cb65f8b39d1167a2ccaeca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Dec 2023 19:31:42 +0100 Subject: [PATCH 0419/1118] Fix test failures --- osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs index fb36580a42..1e9b0317fb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneNowPlayingCommand.cs @@ -11,8 +11,8 @@ using osu.Game.Beatmaps; using osu.Game.Online.API; using osu.Game.Online.Chat; using osu.Game.Online.Rooms; -using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online @@ -53,7 +53,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestPlayActivity() { - AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new RulesetInfo())); + AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo)); AddStep("Run command", () => Add(new NowPlayingCommand(new Channel()))); @@ -82,7 +82,7 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestModPresence() { - AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new RulesetInfo())); + AddStep("Set activity", () => api.Activity.Value = new UserActivity.InSoloGame(new BeatmapInfo(), new OsuRuleset().RulesetInfo)); AddStep("Add Hidden mod", () => SelectedMods.Value = new[] { Ruleset.Value.CreateInstance().CreateMod() }); From 41c33f74f27822889290aea330ade6654eb6f5a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Dec 2023 18:24:31 +0100 Subject: [PATCH 0420/1118] Extend metadata client with user presence-observing capabilities --- osu.Game/Online/Metadata/IMetadataClient.cs | 14 +- osu.Game/Online/Metadata/IMetadataServer.cs | 21 +++ osu.Game/Online/Metadata/MetadataClient.cs | 53 +++++++- .../Online/Metadata/OnlineMetadataClient.cs | 120 +++++++++++++++++- osu.Game/Online/OnlineStatusNotifier.cs | 8 ++ osu.Game/Users/UserPresence.cs | 28 ++++ 6 files changed, 239 insertions(+), 5 deletions(-) create mode 100644 osu.Game/Users/UserPresence.cs diff --git a/osu.Game/Online/Metadata/IMetadataClient.cs b/osu.Game/Online/Metadata/IMetadataClient.cs index ad1e7ebbaf..7102554ae9 100644 --- a/osu.Game/Online/Metadata/IMetadataClient.cs +++ b/osu.Game/Online/Metadata/IMetadataClient.cs @@ -2,11 +2,23 @@ // See the LICENCE file in the repository root for full licence text. using System.Threading.Tasks; +using osu.Game.Users; namespace osu.Game.Online.Metadata { - public interface IMetadataClient + /// + /// Interface for metadata-related remote procedure calls to be executed on the client side. + /// + public interface IMetadataClient : IStatefulUserHubClient { + /// + /// Delivers the set of requested to the client. + /// Task BeatmapSetsUpdated(BeatmapUpdates updates); + + /// + /// Delivers an update of the of the user with the supplied . + /// + Task UserPresenceUpdated(int userId, UserPresence? status); } } diff --git a/osu.Game/Online/Metadata/IMetadataServer.cs b/osu.Game/Online/Metadata/IMetadataServer.cs index 994f60f877..9780045333 100644 --- a/osu.Game/Online/Metadata/IMetadataServer.cs +++ b/osu.Game/Online/Metadata/IMetadataServer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Threading.Tasks; +using osu.Game.Users; namespace osu.Game.Online.Metadata { @@ -17,5 +18,25 @@ namespace osu.Game.Online.Metadata /// The last processed queue ID. /// Task GetChangesSince(int queueId); + + /// + /// Signals to the server that the current user's has changed. + /// + Task UpdateActivity(UserActivity? activity); + + /// + /// Signals to the server that the current user's has changed. + /// + Task UpdateStatus(UserStatus? status); + + /// + /// Signals to the server that the current user would like to begin receiving updates on other users' online presence. + /// + Task BeginWatchingUserPresence(); + + /// + /// Signals to the server that the current user would like to stop receiving updates on other users' online presence. + /// + Task EndWatchingUserPresence(); } } diff --git a/osu.Game/Online/Metadata/MetadataClient.cs b/osu.Game/Online/Metadata/MetadataClient.cs index d4e7540fe7..8e99a9b2cb 100644 --- a/osu.Game/Online/Metadata/MetadataClient.cs +++ b/osu.Game/Online/Metadata/MetadataClient.cs @@ -4,22 +4,71 @@ using System; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Game.Users; namespace osu.Game.Online.Metadata { public abstract partial class MetadataClient : Component, IMetadataClient, IMetadataServer { - public abstract Task BeatmapSetsUpdated(BeatmapUpdates updates); + public abstract IBindable IsConnected { get; } + + #region Beatmap metadata updates public abstract Task GetChangesSince(int queueId); - public Action? ChangedBeatmapSetsArrived; + public abstract Task BeatmapSetsUpdated(BeatmapUpdates updates); + + public event Action? ChangedBeatmapSetsArrived; protected Task ProcessChanges(int[] beatmapSetIDs) { ChangedBeatmapSetsArrived?.Invoke(beatmapSetIDs.Distinct().ToArray()); return Task.CompletedTask; } + + #endregion + + #region User presence updates + + /// + /// Whether the client is currently receiving user presence updates from the server. + /// + public abstract IBindable IsWatchingUserPresence { get; } + + /// + /// Dictionary keyed by user ID containing all of the information about currently online users received from the server. + /// + public abstract IBindableDictionary UserStates { get; } + + /// + public abstract Task UpdateActivity(UserActivity? activity); + + /// + public abstract Task UpdateStatus(UserStatus? status); + + /// + public abstract Task BeginWatchingUserPresence(); + + /// + public abstract Task EndWatchingUserPresence(); + + /// + public abstract Task UserPresenceUpdated(int userId, UserPresence? presence); + + #endregion + + #region Disconnection handling + + public event Action? Disconnecting; + + public virtual Task DisconnectRequested() + { + Schedule(() => Disconnecting?.Invoke()); + return Task.CompletedTask; + } + + #endregion } } diff --git a/osu.Game/Online/Metadata/OnlineMetadataClient.cs b/osu.Game/Online/Metadata/OnlineMetadataClient.cs index 57311419f7..27093d7961 100644 --- a/osu.Game/Online/Metadata/OnlineMetadataClient.cs +++ b/osu.Game/Online/Metadata/OnlineMetadataClient.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.SignalR.Client; using osu.Framework.Allocation; @@ -10,17 +11,32 @@ using osu.Framework.Bindables; using osu.Framework.Logging; using osu.Game.Configuration; using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Users; namespace osu.Game.Online.Metadata { public partial class OnlineMetadataClient : MetadataClient { + public override IBindable IsConnected { get; } = new Bindable(); + + public override IBindable IsWatchingUserPresence => isWatchingUserPresence; + private readonly BindableBool isWatchingUserPresence = new BindableBool(); + + // ReSharper disable once InconsistentlySynchronizedField + public override IBindableDictionary UserStates => userStates; + private readonly BindableDictionary userStates = new BindableDictionary(); + private readonly string endpoint; private IHubClientConnector? connector; private Bindable lastQueueId = null!; + private IBindable localUser = null!; + private IBindable userActivity = null!; + private IBindable? userStatus; + private HubConnection? connection => connector?.CurrentConnection; public OnlineMetadataClient(EndpointConfiguration endpoints) @@ -33,7 +49,7 @@ namespace osu.Game.Online.Metadata { // Importantly, we are intentionally not using MessagePack here to correctly support derived class serialization. // More information on the limitations / reasoning can be found in osu-server-spectator's initialisation code. - connector = api.GetHubConnector(nameof(OnlineMetadataClient), endpoint); + connector = api.GetHubConnector(nameof(OnlineMetadataClient), endpoint, false); if (connector != null) { @@ -42,12 +58,37 @@ namespace osu.Game.Online.Metadata // this is kind of SILLY // https://github.com/dotnet/aspnetcore/issues/15198 connection.On(nameof(IMetadataClient.BeatmapSetsUpdated), ((IMetadataClient)this).BeatmapSetsUpdated); + connection.On(nameof(IMetadataClient.UserPresenceUpdated), ((IMetadataClient)this).UserPresenceUpdated); }; - connector.IsConnected.BindValueChanged(isConnectedChanged, true); + IsConnected.BindTo(connector.IsConnected); + IsConnected.BindValueChanged(isConnectedChanged, true); } lastQueueId = config.GetBindable(OsuSetting.LastProcessedMetadataId); + + localUser = api.LocalUser.GetBoundCopy(); + userActivity = api.Activity.GetBoundCopy()!; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + localUser.BindValueChanged(_ => + { + if (localUser.Value is not GuestUser) + { + userStatus = localUser.Value.Status.GetBoundCopy(); + userStatus.BindValueChanged(status => UpdateStatus(status.NewValue), true); + } + else + userStatus = null; + }, true); + userActivity.BindValueChanged(activity => + { + if (localUser.Value is not GuestUser) + UpdateActivity(activity.NewValue); + }, true); } private bool catchingUp; @@ -55,7 +96,17 @@ namespace osu.Game.Online.Metadata private void isConnectedChanged(ValueChangedEvent connected) { if (!connected.NewValue) + { + isWatchingUserPresence.Value = false; + userStates.Clear(); return; + } + + if (localUser.Value is not GuestUser) + { + UpdateActivity(userActivity.Value); + UpdateStatus(userStatus?.Value); + } if (lastQueueId.Value >= 0) { @@ -116,6 +167,71 @@ namespace osu.Game.Online.Metadata return connection.InvokeAsync(nameof(IMetadataServer.GetChangesSince), queueId); } + public override Task UpdateActivity(UserActivity? activity) + { + if (connector?.IsConnected.Value != true) + return Task.FromCanceled(new CancellationToken(true)); + + Debug.Assert(connection != null); + return connection.InvokeAsync(nameof(IMetadataServer.UpdateActivity), activity); + } + + public override Task UpdateStatus(UserStatus? status) + { + if (connector?.IsConnected.Value != true) + return Task.FromCanceled(new CancellationToken(true)); + + Debug.Assert(connection != null); + return connection.InvokeAsync(nameof(IMetadataServer.UpdateStatus), status); + } + + public override Task UserPresenceUpdated(int userId, UserPresence? presence) + { + lock (userStates) + { + if (presence != null) + userStates[userId] = presence.Value; + else + userStates.Remove(userId); + } + + return Task.CompletedTask; + } + + public override async Task BeginWatchingUserPresence() + { + if (connector?.IsConnected.Value != true) + throw new OperationCanceledException(); + + Debug.Assert(connection != null); + await connection.InvokeAsync(nameof(IMetadataServer.BeginWatchingUserPresence)).ConfigureAwait(false); + isWatchingUserPresence.Value = true; + } + + public override async Task EndWatchingUserPresence() + { + try + { + if (connector?.IsConnected.Value != true) + throw new OperationCanceledException(); + + // must happen synchronously before any remote calls to avoid misordering. + userStates.Clear(); + Debug.Assert(connection != null); + await connection.InvokeAsync(nameof(IMetadataServer.EndWatchingUserPresence)).ConfigureAwait(false); + } + finally + { + isWatchingUserPresence.Value = false; + } + } + + public override async Task DisconnectRequested() + { + await base.DisconnectRequested().ConfigureAwait(false); + await EndWatchingUserPresence().ConfigureAwait(false); + } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); diff --git a/osu.Game/Online/OnlineStatusNotifier.cs b/osu.Game/Online/OnlineStatusNotifier.cs index 0d846f7d27..c36e4ab894 100644 --- a/osu.Game/Online/OnlineStatusNotifier.cs +++ b/osu.Game/Online/OnlineStatusNotifier.cs @@ -9,6 +9,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; using osu.Game.Online.API; +using osu.Game.Online.Metadata; using osu.Game.Online.Multiplayer; using osu.Game.Online.Spectator; using osu.Game.Overlays; @@ -30,6 +31,9 @@ namespace osu.Game.Online [Resolved] private SpectatorClient spectatorClient { get; set; } = null!; + [Resolved] + private MetadataClient metadataClient { get; set; } = null!; + [Resolved] private INotificationOverlay? notificationOverlay { get; set; } @@ -56,6 +60,7 @@ namespace osu.Game.Online multiplayerClient.Disconnecting += notifyAboutForcedDisconnection; spectatorClient.Disconnecting += notifyAboutForcedDisconnection; + metadataClient.Disconnecting += notifyAboutForcedDisconnection; } protected override void LoadComplete() @@ -131,6 +136,9 @@ namespace osu.Game.Online if (multiplayerClient.IsNotNull()) multiplayerClient.Disconnecting -= notifyAboutForcedDisconnection; + + if (metadataClient.IsNotNull()) + metadataClient.Disconnecting -= notifyAboutForcedDisconnection; } } } diff --git a/osu.Game/Users/UserPresence.cs b/osu.Game/Users/UserPresence.cs new file mode 100644 index 0000000000..dff40a9889 --- /dev/null +++ b/osu.Game/Users/UserPresence.cs @@ -0,0 +1,28 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using MessagePack; + +namespace osu.Game.Users +{ + /// + /// Structure containing all relevant information about a user's online presence. + /// + [Serializable] + [MessagePackObject] + public struct UserPresence + { + /// + /// The user's current activity. + /// + [Key(0)] + public UserActivity? Activity { get; set; } + + /// + /// The user's current status. + /// + [Key(1)] + public UserStatus? Status { get; set; } + } +} From 54f3a622beb982ce82542db39b17e1917b6a6bc7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Dec 2023 18:25:16 +0100 Subject: [PATCH 0421/1118] Retrofit user presence watching into dashboard overlay --- ...ngDisplay.cs => CurrentlyOnlineDisplay.cs} | 113 ++++++++++++++---- .../Dashboard/DashboardOverlayHeader.cs | 2 +- osu.Game/Overlays/DashboardOverlay.cs | 33 ++++- 3 files changed, 124 insertions(+), 24 deletions(-) rename osu.Game/Overlays/Dashboard/{CurrentlyPlayingDisplay.cs => CurrentlyOnlineDisplay.cs} (63%) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs similarity index 63% rename from osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs rename to osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs index 6967a61204..fe3151398f 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyPlayingDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs @@ -6,7 +6,6 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -20,6 +19,7 @@ using osu.Game.Database; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Metadata; using osu.Game.Online.Spectator; using osu.Game.Resources.Localisation.Web; using osu.Game.Screens; @@ -30,19 +30,27 @@ using osuTK; namespace osu.Game.Overlays.Dashboard { - internal partial class CurrentlyPlayingDisplay : CompositeDrawable + internal partial class CurrentlyOnlineDisplay : CompositeDrawable { private const float search_textbox_height = 40; private const float padding = 10; private readonly IBindableList playingUsers = new BindableList(); + private readonly IBindableDictionary onlineUsers = new BindableDictionary(); + private readonly Dictionary userPanels = new Dictionary(); - private SearchContainer userFlow; + private SearchContainer userFlow; private BasicSearchTextBox searchTextBox; + [Resolved] + private IAPIProvider api { get; set; } + [Resolved] private SpectatorClient spectatorClient { get; set; } + [Resolved] + private MetadataClient metadataClient { get; set; } + [BackgroundDependencyLoader] private void load(OverlayColourProvider colourProvider) { @@ -72,7 +80,7 @@ namespace osu.Game.Overlays.Dashboard PlaceholderText = HomeStrings.SearchPlaceholder, }, }, - userFlow = new SearchContainer + userFlow = new SearchContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, @@ -97,6 +105,9 @@ namespace osu.Game.Overlays.Dashboard { base.LoadComplete(); + onlineUsers.BindTo(metadataClient.UserStates); + onlineUsers.BindCollectionChanged(onUserUpdated, true); + playingUsers.BindTo(spectatorClient.PlayingUsers); playingUsers.BindCollectionChanged(onPlayingUsersChanged, true); } @@ -108,15 +119,20 @@ namespace osu.Game.Overlays.Dashboard searchTextBox.TakeFocus(); } - private void onPlayingUsersChanged(object sender, NotifyCollectionChangedEventArgs e) => Schedule(() => + private void onUserUpdated(object sender, NotifyDictionaryChangedEventArgs e) => Schedule(() => { switch (e.Action) { - case NotifyCollectionChangedAction.Add: + case NotifyDictionaryChangedAction.Add: Debug.Assert(e.NewItems != null); - foreach (int userId in e.NewItems) + foreach (var kvp in e.NewItems) { + int userId = kvp.Key; + + if (userId == api.LocalUser.Value.Id) + continue; + users.GetUserAsync(userId).ContinueWith(task => { APIUser user = task.GetResultSafely(); @@ -126,40 +142,90 @@ namespace osu.Game.Overlays.Dashboard Schedule(() => { - // user may no longer be playing. - if (!playingUsers.Contains(user.Id)) - return; + // explicitly refetch the user's status. + // things may have changed in between the time of scheduling and the time of actual execution. + if (onlineUsers.TryGetValue(userId, out var updatedStatus)) + { + user.Activity.Value = updatedStatus.Activity; + user.Status.Value = updatedStatus.Status; + } - // TODO: remove this once online state is being updated more correctly. - user.IsOnline = true; - - userFlow.Add(createUserPanel(user)); + userFlow.Add(userPanels[userId] = createUserPanel(user)); }); }); } break; + case NotifyDictionaryChangedAction.Replace: + Debug.Assert(e.NewItems != null); + + foreach (var kvp in e.NewItems) + { + if (userPanels.TryGetValue(kvp.Key, out var panel)) + { + panel.User.Activity.Value = kvp.Value.Activity; + panel.User.Status.Value = kvp.Value.Status; + } + } + + break; + + case NotifyDictionaryChangedAction.Remove: + Debug.Assert(e.OldItems != null); + + foreach (var kvp in e.OldItems) + { + int userId = kvp.Key; + if (userPanels.Remove(userId, out var userPanel)) + userPanel.Expire(); + } + + break; + } + }); + + private void onPlayingUsersChanged(object sender, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + Debug.Assert(e.NewItems != null); + + foreach (int userId in e.NewItems) + { + if (userPanels.TryGetValue(userId, out var panel)) + panel.CanSpectate.Value = userId != api.LocalUser.Value.Id; + } + + break; + case NotifyCollectionChangedAction.Remove: Debug.Assert(e.OldItems != null); foreach (int userId in e.OldItems) - userFlow.FirstOrDefault(card => card.User.Id == userId)?.Expire(); + { + if (userPanels.TryGetValue(userId, out var panel)) + panel.CanSpectate.Value = false; + } + break; } - }); + } - private PlayingUserPanel createUserPanel(APIUser user) => - new PlayingUserPanel(user).With(panel => + private OnlineUserPanel createUserPanel(APIUser user) => + new OnlineUserPanel(user).With(panel => { panel.Anchor = Anchor.TopCentre; panel.Origin = Anchor.TopCentre; }); - public partial class PlayingUserPanel : CompositeDrawable, IFilterable + public partial class OnlineUserPanel : CompositeDrawable, IFilterable { public readonly APIUser User; + public BindableBool CanSpectate { get; } = new BindableBool(); + public IEnumerable FilterTerms { get; } [Resolved(canBeNull: true)] @@ -178,7 +244,7 @@ namespace osu.Game.Overlays.Dashboard } } - public PlayingUserPanel(APIUser user) + public OnlineUserPanel(APIUser user) { User = user; @@ -188,7 +254,7 @@ namespace osu.Game.Overlays.Dashboard } [BackgroundDependencyLoader] - private void load(IAPIProvider api) + private void load() { InternalChildren = new Drawable[] { @@ -205,6 +271,9 @@ namespace osu.Game.Overlays.Dashboard RelativeSizeAxes = Axes.X, Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, + // this is SHOCKING + Activity = { BindTarget = User.Activity }, + Status = { BindTarget = User.Status }, }, new PurpleRoundedButton { @@ -213,7 +282,7 @@ namespace osu.Game.Overlays.Dashboard Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, Action = () => performer?.PerformFromScreen(s => s.Push(new SoloSpectatorScreen(User))), - Enabled = { Value = User.Id != api.LocalUser.Value.Id } + Enabled = { BindTarget = CanSpectate } } } }, diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index b9d869c2ec..104f0943dc 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays.Dashboard [LocalisableDescription(typeof(FriendsStrings), nameof(FriendsStrings.TitleCompact))] Friends, - [Description("Currently Playing")] + [Description("Currently online")] CurrentlyPlaying } } diff --git a/osu.Game/Overlays/DashboardOverlay.cs b/osu.Game/Overlays/DashboardOverlay.cs index 2f96421531..1861f892bd 100644 --- a/osu.Game/Overlays/DashboardOverlay.cs +++ b/osu.Game/Overlays/DashboardOverlay.cs @@ -2,6 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics.Containers; +using osu.Game.Online.Metadata; +using osu.Game.Online.Multiplayer; using osu.Game.Overlays.Dashboard; using osu.Game.Overlays.Dashboard.Friends; @@ -9,6 +14,11 @@ namespace osu.Game.Overlays { public partial class DashboardOverlay : TabbableOnlineOverlay { + [Resolved] + private MetadataClient metadataClient { get; set; } = null!; + + private IBindable metadataConnected = null!; + public DashboardOverlay() : base(OverlayColourScheme.Purple) { @@ -27,12 +37,33 @@ namespace osu.Game.Overlays break; case DashboardOverlayTabs.CurrentlyPlaying: - LoadDisplay(new CurrentlyPlayingDisplay()); + LoadDisplay(new CurrentlyOnlineDisplay()); break; default: throw new NotImplementedException($"Display for {tab} tab is not implemented"); } } + + protected override void LoadComplete() + { + base.LoadComplete(); + + metadataConnected = metadataClient.IsConnected.GetBoundCopy(); + metadataConnected.BindValueChanged(_ => updateUserPresenceState()); + State.BindValueChanged(_ => updateUserPresenceState()); + updateUserPresenceState(); + } + + private void updateUserPresenceState() + { + if (!metadataConnected.Value) + return; + + if (State.Value == Visibility.Visible) + metadataClient.BeginWatchingUserPresence().FireAndForget(); + else + metadataClient.EndWatchingUserPresence().FireAndForget(); + } } } From 86e003aec1cfc97014aebe00c71fe11e999dfae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Dec 2023 18:25:30 +0100 Subject: [PATCH 0422/1118] Update currently online display tests --- ....cs => TestSceneCurrentlyOnlineDisplay.cs} | 33 ++++++-- .../Visual/Metadata/TestMetadataClient.cs | 81 +++++++++++++++++++ 2 files changed, 106 insertions(+), 8 deletions(-) rename osu.Game.Tests/Visual/Online/{TestSceneCurrentlyPlayingDisplay.cs => TestSceneCurrentlyOnlineDisplay.cs} (60%) create mode 100644 osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyOnlineDisplay.cs similarity index 60% rename from osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs rename to osu.Game.Tests/Visual/Online/TestSceneCurrentlyOnlineDisplay.cs index 5237238f63..7687cd195d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyPlayingDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyOnlineDisplay.cs @@ -10,43 +10,50 @@ using osu.Framework.Graphics; using osu.Framework.Testing; using osu.Game.Database; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Metadata; using osu.Game.Online.Spectator; using osu.Game.Overlays; using osu.Game.Overlays.Dashboard; +using osu.Game.Screens.OnlinePlay.Match.Components; +using osu.Game.Tests.Visual.Metadata; using osu.Game.Tests.Visual.Spectator; using osu.Game.Users; namespace osu.Game.Tests.Visual.Online { - public partial class TestSceneCurrentlyPlayingDisplay : OsuTestScene + public partial class TestSceneCurrentlyOnlineDisplay : OsuTestScene { private readonly APIUser streamingUser = new APIUser { Id = 2, Username = "Test user" }; private TestSpectatorClient spectatorClient = null!; - private CurrentlyPlayingDisplay currentlyPlaying = null!; + private TestMetadataClient metadataClient = null!; + private CurrentlyOnlineDisplay currentlyOnline = null!; [SetUpSteps] public void SetUpSteps() { - AddStep("add streaming client", () => + AddStep("set up components", () => { spectatorClient = new TestSpectatorClient(); + metadataClient = new TestMetadataClient(); var lookupCache = new TestUserLookupCache(); Children = new Drawable[] { lookupCache, spectatorClient, + metadataClient, new DependencyProvidingContainer { RelativeSizeAxes = Axes.Both, CachedDependencies = new (Type, object)[] { (typeof(SpectatorClient), spectatorClient), + (typeof(MetadataClient), metadataClient), (typeof(UserLookupCache), lookupCache), (typeof(OverlayColourProvider), new OverlayColourProvider(OverlayColourScheme.Purple)), }, - Child = currentlyPlaying = new CurrentlyPlayingDisplay + Child = currentlyOnline = new CurrentlyOnlineDisplay { RelativeSizeAxes = Axes.Both, } @@ -58,10 +65,20 @@ namespace osu.Game.Tests.Visual.Online [Test] public void TestBasicDisplay() { - AddStep("Add playing user", () => spectatorClient.SendStartPlay(streamingUser.Id, 0)); - AddUntilStep("Panel loaded", () => currentlyPlaying.ChildrenOfType().FirstOrDefault()?.User.Id == 2); - AddStep("Remove playing user", () => spectatorClient.SendEndPlay(streamingUser.Id)); - AddUntilStep("Panel no longer present", () => !currentlyPlaying.ChildrenOfType().Any()); + AddStep("Begin watching user presence", () => metadataClient.BeginWatchingUserPresence()); + AddStep("Add online user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, new UserPresence { Status = UserStatus.Online, Activity = new UserActivity.ChoosingBeatmap() })); + AddUntilStep("Panel loaded", () => currentlyOnline.ChildrenOfType().FirstOrDefault()?.User.Id == 2); + AddAssert("Spectate button disabled", () => currentlyOnline.ChildrenOfType().First().Enabled.Value, () => Is.False); + + AddStep("User began playing", () => spectatorClient.SendStartPlay(streamingUser.Id, 0)); + AddAssert("Spectate button enabled", () => currentlyOnline.ChildrenOfType().First().Enabled.Value, () => Is.True); + + AddStep("User finished playing", () => spectatorClient.SendEndPlay(streamingUser.Id)); + AddAssert("Spectate button disabled", () => currentlyOnline.ChildrenOfType().First().Enabled.Value, () => Is.False); + + AddStep("Remove playing user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, null)); + AddUntilStep("Panel no longer present", () => !currentlyOnline.ChildrenOfType().Any()); + AddStep("End watching user presence", () => metadataClient.EndWatchingUserPresence()); } internal partial class TestUserLookupCache : UserLookupCache diff --git a/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs b/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs new file mode 100644 index 0000000000..16cbf879df --- /dev/null +++ b/osu.Game/Tests/Visual/Metadata/TestMetadataClient.cs @@ -0,0 +1,81 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Threading.Tasks; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Game.Online.API; +using osu.Game.Online.Metadata; +using osu.Game.Users; + +namespace osu.Game.Tests.Visual.Metadata +{ + public partial class TestMetadataClient : MetadataClient + { + public override IBindable IsConnected => new BindableBool(true); + + public override IBindable IsWatchingUserPresence => isWatchingUserPresence; + private readonly BindableBool isWatchingUserPresence = new BindableBool(); + + public override IBindableDictionary UserStates => userStates; + private readonly BindableDictionary userStates = new BindableDictionary(); + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + public override Task BeginWatchingUserPresence() + { + isWatchingUserPresence.Value = true; + return Task.CompletedTask; + } + + public override Task EndWatchingUserPresence() + { + isWatchingUserPresence.Value = false; + return Task.CompletedTask; + } + + public override Task UpdateActivity(UserActivity? activity) + { + if (isWatchingUserPresence.Value) + { + userStates.TryGetValue(api.LocalUser.Value.Id, out var localUserPresence); + localUserPresence = localUserPresence with { Activity = activity }; + userStates[api.LocalUser.Value.Id] = localUserPresence; + } + + return Task.CompletedTask; + } + + public override Task UpdateStatus(UserStatus? status) + { + if (isWatchingUserPresence.Value) + { + userStates.TryGetValue(api.LocalUser.Value.Id, out var localUserPresence); + localUserPresence = localUserPresence with { Status = status }; + userStates[api.LocalUser.Value.Id] = localUserPresence; + } + + return Task.CompletedTask; + } + + public override Task UserPresenceUpdated(int userId, UserPresence? presence) + { + if (isWatchingUserPresence.Value) + { + if (presence.HasValue) + userStates[userId] = presence.Value; + else + userStates.Remove(userId); + } + + return Task.CompletedTask; + } + + public override Task GetChangesSince(int queueId) + => Task.FromResult(new BeatmapUpdates(Array.Empty(), queueId)); + + public override Task BeatmapSetsUpdated(BeatmapUpdates updates) => Task.CompletedTask; + } +} From 37049d41b4850da8c15f5dedc0c7f06a6807be4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 6 Dec 2023 18:25:50 +0100 Subject: [PATCH 0423/1118] Show user's status as tooltip on the extended user panel --- osu.Game/Users/ExtendedUserPanel.cs | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index 18fe852556..1359f5d792 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -9,10 +9,12 @@ using osu.Framework.Bindables; using osu.Framework.Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Cursor; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Users.Drawables; using osu.Framework.Input.Events; +using osu.Framework.Localisation; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Users @@ -26,7 +28,7 @@ namespace osu.Game.Users protected TextFlowContainer LastVisitMessage { get; private set; } private StatusIcon statusIcon; - private OsuSpriteText statusMessage; + private StatusText statusMessage; protected ExtendedUserPanel(APIUser user) : base(user) @@ -88,7 +90,7 @@ namespace osu.Game.Users } })); - statusContainer.Add(statusMessage = new OsuSpriteText + statusContainer.Add(statusMessage = new StatusText { Anchor = alignment, Origin = alignment, @@ -108,12 +110,14 @@ namespace osu.Game.Users if (activity != null && status != UserStatus.Offline) { statusMessage.Text = activity.GetStatus(); + statusMessage.TooltipText = activity.GetDetails(); statusIcon.FadeColour(activity.GetAppropriateColour(Colours), 500, Easing.OutQuint); return; } // Otherwise use only status statusMessage.Text = status.GetLocalisableDescription(); + statusMessage.TooltipText = string.Empty; statusIcon.FadeColour(status.Value.GetAppropriateColour(Colours), 500, Easing.OutQuint); return; @@ -140,5 +144,10 @@ namespace osu.Game.Users BorderThickness = 0; base.OnHoverLost(e); } + + private partial class StatusText : OsuSpriteText, IHasTooltip + { + public LocalisableString TooltipText { get; set; } + } } } From 193047619210ce42a2a54e87dcce2bffb5ddab70 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Thu, 7 Dec 2023 00:26:13 +0100 Subject: [PATCH 0424/1118] Add circle arc segments --- .../Sliders/SliderPlacementBlueprint.cs | 77 ++++++++++++++++++- 1 file changed, 74 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 672ac43f2c..f5d0d82c2a 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; @@ -335,15 +336,85 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders if (segment.Count == 0) continue; - HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[0], PathType.BSpline(4))); - for (int j = 1; j < segment.Count - 1; j++) - HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[j])); + // Replace this segment with a circular arc if it is a reasonable substitute. + var circleArcSegment = tryCircleArc(segment); + + if (circleArcSegment is not null) + { + HitObject.Path.ControlPoints.Add(new PathControlPoint(circleArcSegment[0], PathType.PERFECT_CURVE)); + HitObject.Path.ControlPoints.Add(new PathControlPoint(circleArcSegment[1])); + } + else + { + HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[0], PathType.BSpline(4))); + for (int j = 1; j < segment.Count - 1; j++) + HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[j])); + } if (isLastSegment) HitObject.Path.ControlPoints.Add(new PathControlPoint(segment[^1])); } } + private Vector2[] tryCircleArc(List segment) + { + if (segment.Count < 3) return null; + + // Assume the segment creates a reasonable circular arc and then check if it reasonable + var points = PathApproximator.BSplineToPiecewiseLinear(segment.ToArray(), bSplineBuilder.Degree); + var circleArcControlPoints = new[] { points[0], points[points.Count / 2], points[^1] }; + var circleArc = new CircularArcProperties(circleArcControlPoints); + + if (!circleArc.IsValid) return null; + + double length = circleArc.ThetaRange * circleArc.Radius; + + if (length > 1000) return null; + + double loss = 0; + Vector2? lastPoint = null; + Vector2? lastVec = null; + int? lastDir = null; + double totalWinding = 0; + + // Loop through the points and check if they are not too far away from the circular arc. + // Also make sure it curves monotonically in one direction and at most one loop is done. + foreach (var point in points) + { + loss += Math.Pow((Vector2.Distance(point, circleArc.Centre) - circleArc.Radius) / length, 2); + + if (lastPoint.HasValue) + { + var vec = point - lastPoint.Value; + + if (lastVec.HasValue) + { + double dot = Vector2.Dot(vec, lastVec.Value); + double det = lastVec.Value.X * vec.Y - lastVec.Value.Y * vec.X; + double angle = Math.Atan2(det, dot); + int dir = Math.Sign(angle); + + if (dir == 0) + continue; + + if (lastDir.HasValue && dir != lastDir) + return null; // Curvature changed, like in an S-shape + + totalWinding += Math.Abs(angle); + lastDir = dir; + } + + lastVec = vec; + } + + lastPoint = point; + } + + loss /= points.Count; + + return loss > 0.002 || totalWinding > MathHelper.TwoPi ? null : circleArcControlPoints; + } + private enum SliderPlacementState { Initial, From 89859b85b7e8523171fe6e7d686bf4e6ecb99d3c Mon Sep 17 00:00:00 2001 From: OliBomby Date: Thu, 7 Dec 2023 00:43:34 +0100 Subject: [PATCH 0425/1118] add controllable leniency --- .../Sliders/SliderPlacementBlueprint.cs | 9 ++++-- .../Edit/FreehandSliderToolboxGroup.cs | 32 +++++++++++++++++++ 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index f5d0d82c2a..5878c0e11b 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -95,6 +95,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders bSplineBuilder.CornerThreshold = e.NewValue; Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); }, true); + + freehandToolboxGroup.CircleThreshold.BindValueChanged(e => + { + Scheduler.AddOnce(updateSliderPathFromBSplineBuilder); + }, true); } } @@ -358,7 +363,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private Vector2[] tryCircleArc(List segment) { - if (segment.Count < 3) return null; + if (segment.Count < 3 || freehandToolboxGroup.CircleThreshold.Value == 0) return null; // Assume the segment creates a reasonable circular arc and then check if it reasonable var points = PathApproximator.BSplineToPiecewiseLinear(segment.ToArray(), bSplineBuilder.Degree); @@ -412,7 +417,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders loss /= points.Count; - return loss > 0.002 || totalWinding > MathHelper.TwoPi ? null : circleArcControlPoints; + return loss > freehandToolboxGroup.CircleThreshold.Value || totalWinding > MathHelper.TwoPi ? null : circleArcControlPoints; } private enum SliderPlacementState diff --git a/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs index 7d1eb4e270..c574280267 100644 --- a/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs @@ -31,6 +31,13 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; + public BindableFloat CircleThreshold { get; } = new BindableFloat(0.002f) + { + MinValue = 0f, + MaxValue = 0.005f, + Precision = 0.0001f + }; + // We map internal ranges to a more standard range of values for display to the user. private readonly BindableInt displayTolerance = new BindableInt(100) { @@ -44,8 +51,15 @@ namespace osu.Game.Rulesets.Osu.Edit MaxValue = 100 }; + private readonly BindableInt displayCircleThreshold = new BindableInt(40) + { + MinValue = 0, + MaxValue = 100 + }; + private ExpandableSlider toleranceSlider = null!; private ExpandableSlider cornerThresholdSlider = null!; + private ExpandableSlider circleThresholdSlider = null!; [BackgroundDependencyLoader] private void load() @@ -59,6 +73,10 @@ namespace osu.Game.Rulesets.Osu.Edit cornerThresholdSlider = new ExpandableSlider { Current = displayCornerThreshold + }, + circleThresholdSlider = new ExpandableSlider + { + Current = displayCircleThreshold } }; } @@ -83,18 +101,32 @@ namespace osu.Game.Rulesets.Osu.Edit CornerThreshold.Value = displayToInternalCornerThreshold(threshold.NewValue); }, true); + displayCircleThreshold.BindValueChanged(threshold => + { + circleThresholdSlider.ContractedLabelText = $"P. C. T.: {threshold.NewValue:N0}"; + circleThresholdSlider.ExpandedLabelText = $"Perfect Curve Threshold: {threshold.NewValue:N0}"; + + CircleThreshold.Value = displayToInternalCircleThreshold(threshold.NewValue); + }, true); + Tolerance.BindValueChanged(tolerance => displayTolerance.Value = internalToDisplayTolerance(tolerance.NewValue) ); CornerThreshold.BindValueChanged(threshold => displayCornerThreshold.Value = internalToDisplayCornerThreshold(threshold.NewValue) ); + CircleThreshold.BindValueChanged(threshold => + displayCircleThreshold.Value = internalToDisplayCircleThreshold(threshold.NewValue) + ); float displayToInternalTolerance(float v) => v / 50f; int internalToDisplayTolerance(float v) => (int)Math.Round(v * 50f); float displayToInternalCornerThreshold(float v) => v / 100f; int internalToDisplayCornerThreshold(float v) => (int)Math.Round(v * 100f); + + float displayToInternalCircleThreshold(float v) => v / 20000f; + int internalToDisplayCircleThreshold(float v) => (int)Math.Round(v * 20000f); } } } From a2ec75d824b4bc80198a8937944ed84d7f904158 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Thu, 7 Dec 2023 00:57:29 +0100 Subject: [PATCH 0426/1118] Fix illegal circle arc with center out of polygon --- .../Sliders/SliderPlacementBlueprint.cs | 39 ++++++++++++++----- 1 file changed, 29 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 5878c0e11b..e5dada19bb 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -379,37 +379,56 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders double loss = 0; Vector2? lastPoint = null; Vector2? lastVec = null; + Vector2? lastVec2 = null; int? lastDir = null; + int? lastDir2 = null; double totalWinding = 0; // Loop through the points and check if they are not too far away from the circular arc. // Also make sure it curves monotonically in one direction and at most one loop is done. foreach (var point in points) { - loss += Math.Pow((Vector2.Distance(point, circleArc.Centre) - circleArc.Radius) / length, 2); + var vec = point - circleArc.Centre; + loss += Math.Pow((vec.Length - circleArc.Radius) / length, 2); + + if (lastVec.HasValue) + { + double det = lastVec.Value.X * vec.Y - lastVec.Value.Y * vec.X; + int dir = Math.Sign(det); + + if (dir == 0) + continue; + + if (lastDir.HasValue && dir != lastDir) + return null; // Circle center is not inside the polygon + + lastDir = dir; + } + + lastVec = vec; if (lastPoint.HasValue) { - var vec = point - lastPoint.Value; + var vec2 = point - lastPoint.Value; - if (lastVec.HasValue) + if (lastVec2.HasValue) { - double dot = Vector2.Dot(vec, lastVec.Value); - double det = lastVec.Value.X * vec.Y - lastVec.Value.Y * vec.X; + double dot = Vector2.Dot(vec2, lastVec2.Value); + double det = lastVec2.Value.X * vec2.Y - lastVec2.Value.Y * vec2.X; double angle = Math.Atan2(det, dot); - int dir = Math.Sign(angle); + int dir2 = Math.Sign(angle); - if (dir == 0) + if (dir2 == 0) continue; - if (lastDir.HasValue && dir != lastDir) + if (lastDir2.HasValue && dir2 != lastDir2) return null; // Curvature changed, like in an S-shape totalWinding += Math.Abs(angle); - lastDir = dir; + lastDir2 = dir2; } - lastVec = vec; + lastVec2 = vec2; } lastPoint = point; From 7b49db05d134676605efd0859016c78f9900ebee Mon Sep 17 00:00:00 2001 From: OliBomby Date: Thu, 7 Dec 2023 01:15:42 +0100 Subject: [PATCH 0427/1118] Update default parameters to be slightly better --- osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs index c574280267..f17118ba34 100644 --- a/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs +++ b/osu.Game.Rulesets.Osu/Edit/FreehandSliderToolboxGroup.cs @@ -17,7 +17,7 @@ namespace osu.Game.Rulesets.Osu.Edit { } - public BindableFloat Tolerance { get; } = new BindableFloat(2f) + public BindableFloat Tolerance { get; } = new BindableFloat(1.8f) { MinValue = 0.05f, MaxValue = 2.0f, @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Edit Precision = 0.01f }; - public BindableFloat CircleThreshold { get; } = new BindableFloat(0.002f) + public BindableFloat CircleThreshold { get; } = new BindableFloat(0.0015f) { MinValue = 0f, MaxValue = 0.005f, @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Osu.Edit }; // We map internal ranges to a more standard range of values for display to the user. - private readonly BindableInt displayTolerance = new BindableInt(100) + private readonly BindableInt displayTolerance = new BindableInt(90) { MinValue = 5, MaxValue = 100 @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Edit MaxValue = 100 }; - private readonly BindableInt displayCircleThreshold = new BindableInt(40) + private readonly BindableInt displayCircleThreshold = new BindableInt(30) { MinValue = 0, MaxValue = 100 From d6cb8b70bb653ae2bdf4a3a483818dc06bc1f7a3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 7 Dec 2023 12:25:23 +0900 Subject: [PATCH 0428/1118] Fix FP precision issue when converting mania beatmaps --- .../ManiaBeatmapConversionTest.cs | 4 +- .../ManiaBeatmapSampleConversionTest.cs | 2 +- .../ManiaDifficultyCalculatorTest.cs | 2 +- .../Beatmaps/100374-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/100374.osu | 449 ++++++++++++++++++ .../Beatmaps/20544-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/20544.osu | 126 +++++ .../Beatmaps/basic-expected-conversion.json | 0 .../Resources/Testing/Beatmaps/basic.osu | 54 +-- .../convert-samples-expected-conversion.json | 0 .../Testing/Beatmaps/convert-samples.osu | 0 .../Testing/Beatmaps/diffcalc-test.osu | 0 .../mania-samples-expected-conversion.json | 0 .../Testing/Beatmaps/mania-samples.osu | 0 ...r-convert-samples-expected-conversion.json | 0 .../Beatmaps/slider-convert-samples.osu | 0 ...ero-length-slider-expected-conversion.json | 0 .../Testing/Beatmaps/zero-length-slider.osu | 0 .../Patterns/Legacy/PatternGenerator.cs | 8 +- 19 files changed, 615 insertions(+), 32 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374-expected-conversion.json create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374.osu create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544-expected-conversion.json create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544.osu rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/basic-expected-conversion.json (100%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/basic.osu (96%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json (100%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/convert-samples.osu (100%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/diffcalc-test.osu (100%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json (100%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/mania-samples.osu (100%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/slider-convert-samples-expected-conversion.json (100%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/slider-convert-samples.osu (100%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json (100%) rename {osu.Game.Rulesets.Mania => osu.Game.Rulesets.Mania.Tests}/Resources/Testing/Beatmaps/zero-length-slider.osu (100%) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index ef6dca620a..435d5e737e 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -18,10 +18,12 @@ namespace osu.Game.Rulesets.Mania.Tests [TestFixture] public class ManiaBeatmapConversionTest : BeatmapConversionTest { - protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + protected override string ResourceAssembly => "osu.Game.Rulesets.Mania.Tests"; [TestCase("basic")] [TestCase("zero-length-slider")] + [TestCase("20544")] + [TestCase("100374")] public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs index 51f35d3c3d..99598557a6 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapSampleConversionTest.cs @@ -18,7 +18,7 @@ namespace osu.Game.Rulesets.Mania.Tests [TestFixture] public class ManiaBeatmapSampleConversionTest : BeatmapConversionTest, SampleConvertValue> { - protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + protected override string ResourceAssembly => "osu.Game.Rulesets.Mania.Tests"; [TestCase("convert-samples")] [TestCase("mania-samples")] diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs index 7b0171a9ee..229df4b67b 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaDifficultyCalculatorTest.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mania.Tests { public class ManiaDifficultyCalculatorTest : DifficultyCalculatorTest { - protected override string ResourceAssembly => "osu.Game.Rulesets.Mania"; + protected override string ResourceAssembly => "osu.Game.Rulesets.Mania.Tests"; [TestCase(2.3493769750220914d, 242, "diffcalc-test")] public void Test(double expectedStarRating, int expectedMaxCombo, string name) diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374-expected-conversion.json new file mode 100644 index 0000000000..59f73f7ad4 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"RandomW":273084013,"RandomX":842502087,"RandomY":3579807591,"RandomZ":273326509,"StartTime":15562.0,"Objects":[{"StartTime":15562.0,"EndTime":17155.0,"Column":0}]},{"RandomW":2659258901,"RandomX":3579807591,"RandomY":273326509,"RandomZ":273084013,"StartTime":17686.0,"Objects":[{"StartTime":17686.0,"EndTime":17686.0,"Column":0},{"StartTime":17686.0,"EndTime":17686.0,"Column":1}]},{"RandomW":3083655709,"RandomX":273326509,"RandomY":273084013,"RandomZ":2659258901,"StartTime":17951.0,"Objects":[{"StartTime":17951.0,"EndTime":17951.0,"Column":1}]},{"RandomW":3588026162,"RandomX":2659258901,"RandomY":3083655709,"RandomZ":4073603712,"StartTime":18217.0,"Objects":[{"StartTime":18217.0,"EndTime":18217.0,"Column":2},{"StartTime":18217.0,"EndTime":18217.0,"Column":4}]},{"RandomW":1130061350,"RandomX":3083655709,"RandomY":4073603712,"RandomZ":3588026162,"StartTime":18482.0,"Objects":[{"StartTime":18482.0,"EndTime":18482.0,"Column":2}]},{"RandomW":315421426,"RandomX":3588026162,"RandomY":1130061350,"RandomZ":2459334754,"StartTime":18748.0,"Objects":[{"StartTime":18748.0,"EndTime":19013.0,"Column":0}]},{"RandomW":3110660773,"RandomX":2459334754,"RandomY":315421426,"RandomZ":542845670,"StartTime":19279.0,"Objects":[{"StartTime":19279.0,"EndTime":19809.0,"Column":3},{"StartTime":19544.0,"EndTime":19544.0,"Column":1},{"StartTime":19809.0,"EndTime":19809.0,"Column":1}]},{"RandomW":3110660773,"RandomX":2459334754,"RandomY":315421426,"RandomZ":542845670,"StartTime":20075.0,"Objects":[{"StartTime":20075.0,"EndTime":20075.0,"Column":4},{"StartTime":20075.0,"EndTime":20075.0,"Column":2}]},{"RandomW":2552021122,"RandomX":315421426,"RandomY":542845670,"RandomZ":3110660773,"StartTime":20341.0,"Objects":[{"StartTime":20341.0,"EndTime":20341.0,"Column":3}]},{"RandomW":3979536913,"RandomX":542845670,"RandomY":3110660773,"RandomZ":2552021122,"StartTime":20606.0,"Objects":[{"StartTime":20606.0,"EndTime":20606.0,"Column":2},{"StartTime":20606.0,"EndTime":20606.0,"Column":3}]},{"RandomW":3926138036,"RandomX":2552021122,"RandomY":3979536913,"RandomZ":348643659,"StartTime":20871.0,"Objects":[{"StartTime":20871.0,"EndTime":21401.0,"Column":4}]},{"RandomW":4001028953,"RandomX":348643659,"RandomY":3926138036,"RandomZ":2489502118,"StartTime":21933.0,"Objects":[{"StartTime":21933.0,"EndTime":22198.0,"Column":5}]},{"RandomW":263714783,"RandomX":2489502118,"RandomY":4001028953,"RandomZ":3315380836,"StartTime":22464.0,"Objects":[{"StartTime":22464.0,"EndTime":22729.0,"Column":0}]},{"RandomW":3045229215,"RandomX":3315380836,"RandomY":263714783,"RandomZ":2367299702,"StartTime":22995.0,"Objects":[{"StartTime":22995.0,"EndTime":23791.0,"Column":2}]},{"RandomW":622075324,"RandomX":2367299702,"RandomY":3045229215,"RandomZ":2511145433,"StartTime":24057.0,"Objects":[{"StartTime":24057.0,"EndTime":24322.0,"Column":1}]},{"RandomW":1428674661,"RandomX":3630592823,"RandomY":628640291,"RandomZ":2684635853,"StartTime":24588.0,"Objects":[{"StartTime":24588.0,"EndTime":24853.0,"Column":4},{"StartTime":24588.0,"EndTime":24853.0,"Column":3}]},{"RandomW":2963472042,"RandomX":3191072317,"RandomY":1509788298,"RandomZ":3677221210,"StartTime":25119.0,"Objects":[{"StartTime":25119.0,"EndTime":25649.0,"Column":2}]},{"RandomW":2441208973,"RandomX":1509788298,"RandomY":3677221210,"RandomZ":2963472042,"StartTime":26181.0,"Objects":[{"StartTime":26181.0,"EndTime":26181.0,"Column":2},{"StartTime":26181.0,"EndTime":26181.0,"Column":3}]},{"RandomW":614303213,"RandomX":3677221210,"RandomY":2963472042,"RandomZ":2441208973,"StartTime":26447.0,"Objects":[{"StartTime":26447.0,"EndTime":26447.0,"Column":3}]},{"RandomW":931064848,"RandomX":2441208973,"RandomY":614303213,"RandomZ":2425227013,"StartTime":26712.0,"Objects":[{"StartTime":26712.0,"EndTime":26977.0,"Column":2}]},{"RandomW":1631554006,"RandomX":2425227013,"RandomY":931064848,"RandomZ":2839921662,"StartTime":27243.0,"Objects":[{"StartTime":27243.0,"EndTime":27508.0,"Column":4}]},{"RandomW":1102544522,"RandomX":2839921662,"RandomY":1631554006,"RandomZ":2171149531,"StartTime":27774.0,"Objects":[{"StartTime":27774.0,"EndTime":28039.0,"Column":3}]},{"RandomW":1535528787,"RandomX":2171149531,"RandomY":1102544522,"RandomZ":3328843633,"StartTime":28305.0,"Objects":[{"StartTime":28305.0,"EndTime":28835.0,"Column":4},{"StartTime":28305.0,"EndTime":28305.0,"Column":3},{"StartTime":28570.0,"EndTime":28570.0,"Column":3},{"StartTime":28835.0,"EndTime":28835.0,"Column":3}]},{"RandomW":2462060348,"RandomX":1102544522,"RandomY":3328843633,"RandomZ":1535528787,"StartTime":29102.0,"Objects":[{"StartTime":29102.0,"EndTime":29102.0,"Column":3}]},{"RandomW":2548780898,"RandomX":2462060348,"RandomY":1752789184,"RandomZ":4269701929,"StartTime":29367.0,"Objects":[{"StartTime":29367.0,"EndTime":29897.0,"Column":5},{"StartTime":29367.0,"EndTime":29897.0,"Column":1}]},{"RandomW":2872444045,"RandomX":2548780898,"RandomY":96471884,"RandomZ":2795275332,"StartTime":30429.0,"Objects":[{"StartTime":30429.0,"EndTime":30694.0,"Column":2}]},{"RandomW":554186146,"RandomX":2872444045,"RandomY":1718345430,"RandomZ":1676944188,"StartTime":30960.0,"Objects":[{"StartTime":30960.0,"EndTime":31225.0,"Column":4},{"StartTime":30960.0,"EndTime":31225.0,"Column":1}]},{"RandomW":44350362,"RandomX":1676944188,"RandomY":554186146,"RandomZ":973164386,"StartTime":31491.0,"Objects":[{"StartTime":31491.0,"EndTime":32287.0,"Column":0}]},{"RandomW":2689469863,"RandomX":973164386,"RandomY":44350362,"RandomZ":3230373169,"StartTime":32553.0,"Objects":[{"StartTime":32553.0,"EndTime":32818.0,"Column":1}]},{"RandomW":3076210018,"RandomX":3230373169,"RandomY":2689469863,"RandomZ":2416196755,"StartTime":33084.0,"Objects":[{"StartTime":33084.0,"EndTime":33349.0,"Column":2}]},{"RandomW":4212524875,"RandomX":2416196755,"RandomY":3076210018,"RandomZ":736433317,"StartTime":33615.0,"Objects":[{"StartTime":33615.0,"EndTime":34145.0,"Column":5}]},{"RandomW":668643347,"RandomX":4212524875,"RandomY":1246190622,"RandomZ":614058009,"StartTime":34677.0,"Objects":[{"StartTime":34677.0,"EndTime":34677.0,"Column":0},{"StartTime":34677.0,"EndTime":34677.0,"Column":5}]},{"RandomW":4133034829,"RandomX":668643347,"RandomY":1824376828,"RandomZ":476758489,"StartTime":34942.0,"Objects":[{"StartTime":34942.0,"EndTime":34942.0,"Column":1},{"StartTime":34942.0,"EndTime":34942.0,"Column":5}]},{"RandomW":82933693,"RandomX":1824376828,"RandomY":476758489,"RandomZ":4133034829,"StartTime":35208.0,"Objects":[{"StartTime":35208.0,"EndTime":35208.0,"Column":0},{"StartTime":35208.0,"EndTime":35208.0,"Column":1}]},{"RandomW":2263995128,"RandomX":476758489,"RandomY":4133034829,"RandomZ":82933693,"StartTime":35473.0,"Objects":[{"StartTime":35473.0,"EndTime":35473.0,"Column":1}]},{"RandomW":3437211638,"RandomX":4133034829,"RandomY":82933693,"RandomZ":2263995128,"StartTime":35739.0,"Objects":[{"StartTime":35739.0,"EndTime":35739.0,"Column":2}]},{"RandomW":2107738941,"RandomX":2263995128,"RandomY":3437211638,"RandomZ":4066526803,"StartTime":36004.0,"Objects":[{"StartTime":36004.0,"EndTime":36004.0,"Column":2},{"StartTime":36004.0,"EndTime":36004.0,"Column":5}]},{"RandomW":1976561763,"RandomX":3437211638,"RandomY":4066526803,"RandomZ":2107738941,"StartTime":36270.0,"Objects":[{"StartTime":36270.0,"EndTime":36270.0,"Column":3},{"StartTime":36270.0,"EndTime":36270.0,"Column":4}]},{"RandomW":1147027763,"RandomX":4066526803,"RandomY":2107738941,"RandomZ":1976561763,"StartTime":36535.0,"Objects":[{"StartTime":36535.0,"EndTime":36535.0,"Column":3}]},{"RandomW":3580315894,"RandomX":1976561763,"RandomY":1147027763,"RandomZ":2767111989,"StartTime":36801.0,"Objects":[{"StartTime":36801.0,"EndTime":37331.0,"Column":4}]},{"RandomW":3743545041,"RandomX":1147027763,"RandomY":2767111989,"RandomZ":3580315894,"StartTime":37597.0,"Objects":[{"StartTime":37597.0,"EndTime":37597.0,"Column":1}]},{"RandomW":1409948107,"RandomX":3743545041,"RandomY":1774216159,"RandomZ":3150304957,"StartTime":37863.0,"Objects":[{"StartTime":37863.0,"EndTime":38393.0,"Column":2},{"StartTime":37863.0,"EndTime":38393.0,"Column":3}]},{"RandomW":4009340712,"RandomX":3150304957,"RandomY":1409948107,"RandomZ":2219703013,"StartTime":38925.0,"Objects":[{"StartTime":38925.0,"EndTime":39190.0,"Column":5}]},{"RandomW":3071167491,"RandomX":2065497204,"RandomY":2145154717,"RandomZ":2494378321,"StartTime":39456.0,"Objects":[{"StartTime":39456.0,"EndTime":39721.0,"Column":0},{"StartTime":39456.0,"EndTime":39721.0,"Column":2}]},{"RandomW":1245938367,"RandomX":3071167491,"RandomY":728627658,"RandomZ":3080260260,"StartTime":39987.0,"Objects":[{"StartTime":39987.0,"EndTime":40783.0,"Column":3}]},{"RandomW":3032241617,"RandomX":1245938367,"RandomY":2414391712,"RandomZ":3406801470,"StartTime":41048.0,"Objects":[{"StartTime":41048.0,"EndTime":41313.0,"Column":2}]},{"RandomW":3367991920,"RandomX":3804000131,"RandomY":672376773,"RandomZ":2667292323,"StartTime":41579.0,"Objects":[{"StartTime":41579.0,"EndTime":41844.0,"Column":1},{"StartTime":41579.0,"EndTime":41844.0,"Column":3}]},{"RandomW":2095476726,"RandomX":2667292323,"RandomY":3367991920,"RandomZ":3380532371,"StartTime":42110.0,"Objects":[{"StartTime":42110.0,"EndTime":42640.0,"Column":5}]},{"RandomW":869340745,"RandomX":2095476726,"RandomY":1063981175,"RandomZ":204767504,"StartTime":43172.0,"Objects":[{"StartTime":43172.0,"EndTime":43172.0,"Column":1},{"StartTime":43172.0,"EndTime":43172.0,"Column":4}]},{"RandomW":461904197,"RandomX":204767504,"RandomY":869340745,"RandomZ":2080855578,"StartTime":43438.0,"Objects":[{"StartTime":43438.0,"EndTime":43438.0,"Column":2},{"StartTime":43438.0,"EndTime":43438.0,"Column":1}]},{"RandomW":3004966693,"RandomX":869340745,"RandomY":2080855578,"RandomZ":461904197,"StartTime":43703.0,"Objects":[{"StartTime":43703.0,"EndTime":43703.0,"Column":3},{"StartTime":43703.0,"EndTime":43703.0,"Column":4}]},{"RandomW":147065937,"RandomX":2080855578,"RandomY":461904197,"RandomZ":3004966693,"StartTime":43969.0,"Objects":[{"StartTime":43969.0,"EndTime":43969.0,"Column":4}]},{"RandomW":1312111829,"RandomX":461904197,"RandomY":3004966693,"RandomZ":147065937,"StartTime":44234.0,"Objects":[{"StartTime":44234.0,"EndTime":44234.0,"Column":4}]},{"RandomW":355223143,"RandomX":3004966693,"RandomY":147065937,"RandomZ":1312111829,"StartTime":44500.0,"Objects":[{"StartTime":44500.0,"EndTime":44500.0,"Column":3}]},{"RandomW":1197174504,"RandomX":147065937,"RandomY":1312111829,"RandomZ":355223143,"StartTime":44765.0,"Objects":[{"StartTime":44765.0,"EndTime":44765.0,"Column":2},{"StartTime":44765.0,"EndTime":44765.0,"Column":3}]},{"RandomW":2296450669,"RandomX":355223143,"RandomY":1197174504,"RandomZ":1876247766,"StartTime":45031.0,"Objects":[{"StartTime":45031.0,"EndTime":45031.0,"Column":1},{"StartTime":45031.0,"EndTime":45031.0,"Column":0}]},{"RandomW":1664705375,"RandomX":1876247766,"RandomY":2296450669,"RandomZ":4287200872,"StartTime":45296.0,"Objects":[{"StartTime":45296.0,"EndTime":45296.0,"Column":0},{"StartTime":45296.0,"EndTime":45296.0,"Column":4}]},{"RandomW":2786027546,"RandomX":2296450669,"RandomY":4287200872,"RandomZ":1664705375,"StartTime":45562.0,"Objects":[{"StartTime":45562.0,"EndTime":45562.0,"Column":1}]},{"RandomW":639469776,"RandomX":4287200872,"RandomY":1664705375,"RandomZ":2786027546,"StartTime":45827.0,"Objects":[{"StartTime":45827.0,"EndTime":45827.0,"Column":3},{"StartTime":45827.0,"EndTime":45827.0,"Column":4}]},{"RandomW":2463352901,"RandomX":1664705375,"RandomY":2786027546,"RandomZ":639469776,"StartTime":46093.0,"Objects":[{"StartTime":46093.0,"EndTime":46093.0,"Column":4}]},{"RandomW":760995091,"RandomX":2463352901,"RandomY":978871003,"RandomZ":3888812594,"StartTime":46358.0,"Objects":[{"StartTime":46358.0,"EndTime":46888.0,"Column":2}]},{"RandomW":3631307076,"RandomX":3888812594,"RandomY":760995091,"RandomZ":566667549,"StartTime":47420.0,"Objects":[{"StartTime":47420.0,"EndTime":47685.0,"Column":4}]},{"RandomW":2353216536,"RandomX":3631307076,"RandomY":1805196154,"RandomZ":2564415583,"StartTime":47951.0,"Objects":[{"StartTime":47951.0,"EndTime":48216.0,"Column":1},{"StartTime":47951.0,"EndTime":48216.0,"Column":0}]},{"RandomW":717730087,"RandomX":2353216536,"RandomY":3735744429,"RandomZ":2102099401,"StartTime":48482.0,"Objects":[{"StartTime":48482.0,"EndTime":49278.0,"Column":5},{"StartTime":48482.0,"EndTime":49278.0,"Column":2}]},{"RandomW":271333990,"RandomX":717730087,"RandomY":3220302747,"RandomZ":917482575,"StartTime":49544.0,"Objects":[{"StartTime":49544.0,"EndTime":49809.0,"Column":0}]},{"RandomW":937976203,"RandomX":917482575,"RandomY":271333990,"RandomZ":125173709,"StartTime":50075.0,"Objects":[{"StartTime":50075.0,"EndTime":50340.0,"Column":2}]},{"RandomW":2781059562,"RandomX":937976203,"RandomY":2087616237,"RandomZ":232817676,"StartTime":50606.0,"Objects":[{"StartTime":50606.0,"EndTime":51667.0,"Column":0},{"StartTime":50606.0,"EndTime":51667.0,"Column":1}]},{"RandomW":3511898336,"RandomX":2087616237,"RandomY":232817676,"RandomZ":2781059562,"StartTime":52730.0,"Objects":[{"StartTime":52730.0,"EndTime":52730.0,"Column":4}]},{"RandomW":623291556,"RandomX":3737503025,"RandomY":3607951873,"RandomZ":1857627587,"StartTime":53792.0,"Objects":[{"StartTime":53792.0,"EndTime":54322.0,"Column":5},{"StartTime":53792.0,"EndTime":54322.0,"Column":1}]},{"RandomW":3577350524,"RandomX":3607951873,"RandomY":1857627587,"RandomZ":623291556,"StartTime":54588.0,"Objects":[{"StartTime":54588.0,"EndTime":54588.0,"Column":2}]},{"RandomW":3611414219,"RandomX":1700150568,"RandomY":3261504380,"RandomZ":3526708248,"StartTime":54854.0,"Objects":[{"StartTime":54854.0,"EndTime":55384.0,"Column":3},{"StartTime":54854.0,"EndTime":55384.0,"Column":4}]},{"RandomW":4116828180,"RandomX":3526708248,"RandomY":3611414219,"RandomZ":53089910,"StartTime":55916.0,"Objects":[{"StartTime":55916.0,"EndTime":56446.0,"Column":5}]},{"RandomW":1419945944,"RandomX":53089910,"RandomY":4116828180,"RandomZ":2370574124,"StartTime":56978.0,"Objects":[{"StartTime":56978.0,"EndTime":57549.0,"Column":3}]},{"RandomW":4235330325,"RandomX":2370574124,"RandomY":1419945944,"RandomZ":124293788,"StartTime":58120.0,"Objects":[{"StartTime":58120.0,"EndTime":58405.0,"Column":5}]},{"RandomW":1354196818,"RandomX":124293788,"RandomY":4235330325,"RandomZ":292200128,"StartTime":58692.0,"Objects":[{"StartTime":58692.0,"EndTime":58973.0,"Column":3}]},{"RandomW":2131632245,"RandomX":292200128,"RandomY":1354196818,"RandomZ":319349674,"StartTime":59325.0,"Objects":[{"StartTime":59325.0,"EndTime":60170.0,"Column":5}]},{"RandomW":987180490,"RandomX":1354196818,"RandomY":319349674,"RandomZ":2131632245,"StartTime":60513.0,"Objects":[{"StartTime":60513.0,"EndTime":60513.0,"Column":3}]},{"RandomW":2247158810,"RandomX":2131632245,"RandomY":987180490,"RandomZ":3518058549,"StartTime":60778.0,"Objects":[{"StartTime":60778.0,"EndTime":61043.0,"Column":0}]},{"RandomW":2347989337,"RandomX":987180490,"RandomY":3518058549,"RandomZ":2247158810,"StartTime":61309.0,"Objects":[{"StartTime":61309.0,"EndTime":61309.0,"Column":3}]},{"RandomW":82954311,"RandomX":1403151684,"RandomY":1362150166,"RandomZ":1092174296,"StartTime":61840.0,"Objects":[{"StartTime":61840.0,"EndTime":62105.0,"Column":0}]},{"RandomW":408605211,"RandomX":82954311,"RandomY":1144587736,"RandomZ":2479248954,"StartTime":62371.0,"Objects":[{"StartTime":62371.0,"EndTime":62901.0,"Column":1}]},{"RandomW":2455999143,"RandomX":1144587736,"RandomY":2479248954,"RandomZ":408605211,"StartTime":63168.0,"Objects":[{"StartTime":63168.0,"EndTime":63168.0,"Column":2}]},{"RandomW":1898608481,"RandomX":2455999143,"RandomY":519590646,"RandomZ":3207504021,"StartTime":63433.0,"Objects":[{"StartTime":63433.0,"EndTime":63963.0,"Column":5}]},{"RandomW":601995191,"RandomX":3207504021,"RandomY":1898608481,"RandomZ":4283573577,"StartTime":64230.0,"Objects":[{"StartTime":64230.0,"EndTime":64230.0,"Column":5},{"StartTime":64230.0,"EndTime":64230.0,"Column":1}]},{"RandomW":3909194070,"RandomX":1898608481,"RandomY":4283573577,"RandomZ":601995191,"StartTime":64495.0,"Objects":[{"StartTime":64495.0,"EndTime":64495.0,"Column":3},{"StartTime":64495.0,"EndTime":64495.0,"Column":4}]},{"RandomW":3417465448,"RandomX":4283573577,"RandomY":601995191,"RandomZ":3909194070,"StartTime":64761.0,"Objects":[{"StartTime":64761.0,"EndTime":64761.0,"Column":4}]},{"RandomW":2779016762,"RandomX":601995191,"RandomY":3909194070,"RandomZ":3417465448,"StartTime":65026.0,"Objects":[{"StartTime":65026.0,"EndTime":65026.0,"Column":4},{"StartTime":65026.0,"EndTime":65026.0,"Column":5}]},{"RandomW":2346068278,"RandomX":3909194070,"RandomY":3417465448,"RandomZ":2779016762,"StartTime":65292.0,"Objects":[{"StartTime":65292.0,"EndTime":65292.0,"Column":3}]},{"RandomW":1857589819,"RandomX":3417465448,"RandomY":2779016762,"RandomZ":2346068278,"StartTime":65557.0,"Objects":[{"StartTime":65557.0,"EndTime":65557.0,"Column":4},{"StartTime":65557.0,"EndTime":65557.0,"Column":5}]},{"RandomW":910236838,"RandomX":2779016762,"RandomY":2346068278,"RandomZ":1857589819,"StartTime":66088.0,"Objects":[{"StartTime":66088.0,"EndTime":66088.0,"Column":2},{"StartTime":66088.0,"EndTime":66088.0,"Column":3}]},{"RandomW":910236838,"RandomX":2779016762,"RandomY":2346068278,"RandomZ":1857589819,"StartTime":66354.0,"Objects":[{"StartTime":66354.0,"EndTime":66354.0,"Column":3},{"StartTime":66354.0,"EndTime":66354.0,"Column":2}]},{"RandomW":2327273799,"RandomX":1857589819,"RandomY":910236838,"RandomZ":2953998826,"StartTime":66619.0,"Objects":[{"StartTime":66619.0,"EndTime":67149.0,"Column":0}]},{"RandomW":540283744,"RandomX":910236838,"RandomY":2953998826,"RandomZ":2327273799,"StartTime":67416.0,"Objects":[{"StartTime":67416.0,"EndTime":67416.0,"Column":0}]},{"RandomW":1024467186,"RandomX":2327273799,"RandomY":540283744,"RandomZ":514760684,"StartTime":67681.0,"Objects":[{"StartTime":67681.0,"EndTime":68211.0,"Column":2}]},{"RandomW":211600206,"RandomX":540283744,"RandomY":514760684,"RandomZ":1024467186,"StartTime":68478.0,"Objects":[{"StartTime":68478.0,"EndTime":68478.0,"Column":2}]},{"RandomW":2360573614,"RandomX":514760684,"RandomY":1024467186,"RandomZ":211600206,"StartTime":68743.0,"Objects":[{"StartTime":68743.0,"EndTime":68743.0,"Column":4},{"StartTime":68743.0,"EndTime":68743.0,"Column":5}]},{"RandomW":3867722027,"RandomX":1024467186,"RandomY":211600206,"RandomZ":2360573614,"StartTime":69009.0,"Objects":[{"StartTime":69009.0,"EndTime":69009.0,"Column":3}]},{"RandomW":1512274616,"RandomX":211600206,"RandomY":2360573614,"RandomZ":3867722027,"StartTime":69274.0,"Objects":[{"StartTime":69274.0,"EndTime":69274.0,"Column":4},{"StartTime":69274.0,"EndTime":69274.0,"Column":5}]},{"RandomW":2957984769,"RandomX":2360573614,"RandomY":3867722027,"RandomZ":1512274616,"StartTime":69540.0,"Objects":[{"StartTime":69540.0,"EndTime":69540.0,"Column":3}]},{"RandomW":2803767976,"RandomX":3867722027,"RandomY":1512274616,"RandomZ":2957984769,"StartTime":69805.0,"Objects":[{"StartTime":69805.0,"EndTime":69805.0,"Column":4},{"StartTime":69805.0,"EndTime":69805.0,"Column":5}]},{"RandomW":1183341084,"RandomX":2957984769,"RandomY":2803767976,"RandomZ":121575161,"StartTime":70336.0,"Objects":[{"StartTime":70336.0,"EndTime":70601.0,"Column":3}]},{"RandomW":3685872119,"RandomX":121575161,"RandomY":1183341084,"RandomZ":2351788416,"StartTime":70867.0,"Objects":[{"StartTime":70867.0,"EndTime":71397.0,"Column":4}]},{"RandomW":617004198,"RandomX":1183341084,"RandomY":2351788416,"RandomZ":3685872119,"StartTime":71663.0,"Objects":[{"StartTime":71663.0,"EndTime":71663.0,"Column":3}]},{"RandomW":2478235967,"RandomX":617004198,"RandomY":546986648,"RandomZ":3353120378,"StartTime":71929.0,"Objects":[{"StartTime":71929.0,"EndTime":72459.0,"Column":0}]},{"RandomW":2189712483,"RandomX":546986648,"RandomY":3353120378,"RandomZ":2478235967,"StartTime":72725.0,"Objects":[{"StartTime":72725.0,"EndTime":72725.0,"Column":2}]},{"RandomW":1882757169,"RandomX":3353120378,"RandomY":2478235967,"RandomZ":2189712483,"StartTime":72991.0,"Objects":[{"StartTime":72991.0,"EndTime":72991.0,"Column":3},{"StartTime":72991.0,"EndTime":72991.0,"Column":4}]},{"RandomW":1404331794,"RandomX":2478235967,"RandomY":2189712483,"RandomZ":1882757169,"StartTime":73256.0,"Objects":[{"StartTime":73256.0,"EndTime":73256.0,"Column":1}]},{"RandomW":1999620930,"RandomX":2189712483,"RandomY":1882757169,"RandomZ":1404331794,"StartTime":73522.0,"Objects":[{"StartTime":73522.0,"EndTime":73522.0,"Column":3},{"StartTime":73522.0,"EndTime":73522.0,"Column":4}]},{"RandomW":3622364800,"RandomX":1882757169,"RandomY":1404331794,"RandomZ":1999620930,"StartTime":73787.0,"Objects":[{"StartTime":73787.0,"EndTime":73787.0,"Column":2}]},{"RandomW":1671763292,"RandomX":1404331794,"RandomY":1999620930,"RandomZ":3622364800,"StartTime":74053.0,"Objects":[{"StartTime":74053.0,"EndTime":74053.0,"Column":3},{"StartTime":74053.0,"EndTime":74053.0,"Column":4}]},{"RandomW":2594561583,"RandomX":3622364800,"RandomY":1671763292,"RandomZ":2480497357,"StartTime":74584.0,"Objects":[{"StartTime":74584.0,"EndTime":74849.0,"Column":1}]},{"RandomW":1101860073,"RandomX":2480497357,"RandomY":2594561583,"RandomZ":183105309,"StartTime":75115.0,"Objects":[{"StartTime":75115.0,"EndTime":75645.0,"Column":3}]},{"RandomW":423280923,"RandomX":2594561583,"RandomY":183105309,"RandomZ":1101860073,"StartTime":75911.0,"Objects":[{"StartTime":75911.0,"EndTime":75911.0,"Column":2}]},{"RandomW":3905841932,"RandomX":1101860073,"RandomY":423280923,"RandomZ":2916757685,"StartTime":76177.0,"Objects":[{"StartTime":76177.0,"EndTime":76707.0,"Column":4}]},{"RandomW":3241015480,"RandomX":423280923,"RandomY":2916757685,"RandomZ":3905841932,"StartTime":76973.0,"Objects":[{"StartTime":76973.0,"EndTime":76973.0,"Column":3}]},{"RandomW":1928531304,"RandomX":3905841932,"RandomY":3241015480,"RandomZ":248564639,"StartTime":77239.0,"Objects":[{"StartTime":77239.0,"EndTime":77504.0,"Column":5}]},{"RandomW":634267655,"RandomX":3925777969,"RandomY":1203262350,"RandomZ":3485263061,"StartTime":77770.0,"Objects":[{"StartTime":77770.0,"EndTime":78035.0,"Column":3},{"StartTime":77770.0,"EndTime":78035.0,"Column":1}]},{"RandomW":953955737,"RandomX":1203262350,"RandomY":3485263061,"RandomZ":634267655,"StartTime":78301.0,"Objects":[{"StartTime":78301.0,"EndTime":78301.0,"Column":3}]},{"RandomW":3179099439,"RandomX":3485263061,"RandomY":634267655,"RandomZ":953955737,"StartTime":78566.0,"Objects":[{"StartTime":78566.0,"EndTime":78566.0,"Column":2},{"StartTime":78566.0,"EndTime":78566.0,"Column":3}]},{"RandomW":2513433625,"RandomX":634267655,"RandomY":953955737,"RandomZ":3179099439,"StartTime":78832.0,"Objects":[{"StartTime":78832.0,"EndTime":78832.0,"Column":3},{"StartTime":78832.0,"EndTime":78832.0,"Column":4}]},{"RandomW":3239409847,"RandomX":953955737,"RandomY":3179099439,"RandomZ":2513433625,"StartTime":79097.0,"Objects":[{"StartTime":79097.0,"EndTime":79097.0,"Column":5},{"StartTime":79097.0,"EndTime":79097.0,"Column":0}]},{"RandomW":1279031172,"RandomX":2513433625,"RandomY":3239409847,"RandomZ":415034865,"StartTime":79363.0,"Objects":[{"StartTime":79363.0,"EndTime":79893.0,"Column":3}]},{"RandomW":2797153574,"RandomX":3239409847,"RandomY":415034865,"RandomZ":1279031172,"StartTime":80159.0,"Objects":[{"StartTime":80159.0,"EndTime":80159.0,"Column":3}]},{"RandomW":858752658,"RandomX":1279031172,"RandomY":2797153574,"RandomZ":3422759302,"StartTime":80424.0,"Objects":[{"StartTime":80424.0,"EndTime":80954.0,"Column":2}]},{"RandomW":2617268004,"RandomX":2797153574,"RandomY":3422759302,"RandomZ":858752658,"StartTime":81221.0,"Objects":[{"StartTime":81221.0,"EndTime":81221.0,"Column":4}]},{"RandomW":4089416095,"RandomX":3422759302,"RandomY":858752658,"RandomZ":2617268004,"StartTime":81486.0,"Objects":[{"StartTime":81486.0,"EndTime":81486.0,"Column":4},{"StartTime":81486.0,"EndTime":81486.0,"Column":5}]},{"RandomW":640008567,"RandomX":858752658,"RandomY":2617268004,"RandomZ":4089416095,"StartTime":81752.0,"Objects":[{"StartTime":81752.0,"EndTime":81752.0,"Column":4}]},{"RandomW":1769064503,"RandomX":2617268004,"RandomY":4089416095,"RandomZ":640008567,"StartTime":82017.0,"Objects":[{"StartTime":82017.0,"EndTime":82017.0,"Column":5},{"StartTime":82017.0,"EndTime":82017.0,"Column":0}]},{"RandomW":4171929422,"RandomX":640008567,"RandomY":1769064503,"RandomZ":4149611338,"StartTime":82283.0,"Objects":[{"StartTime":82283.0,"EndTime":82283.0,"Column":3},{"StartTime":82283.0,"EndTime":82283.0,"Column":5}]},{"RandomW":4035764053,"RandomX":1769064503,"RandomY":4149611338,"RandomZ":4171929422,"StartTime":82548.0,"Objects":[{"StartTime":82548.0,"EndTime":82548.0,"Column":5},{"StartTime":82548.0,"EndTime":82548.0,"Column":0}]},{"RandomW":391872771,"RandomX":4149611338,"RandomY":4171929422,"RandomZ":4035764053,"StartTime":83079.0,"Objects":[{"StartTime":83079.0,"EndTime":83079.0,"Column":3},{"StartTime":83079.0,"EndTime":83079.0,"Column":4}]},{"RandomW":391872771,"RandomX":4149611338,"RandomY":4171929422,"RandomZ":4035764053,"StartTime":83345.0,"Objects":[{"StartTime":83345.0,"EndTime":83345.0,"Column":2},{"StartTime":83345.0,"EndTime":83345.0,"Column":1}]},{"RandomW":4239141202,"RandomX":4035764053,"RandomY":391872771,"RandomZ":1343280377,"StartTime":83610.0,"Objects":[{"StartTime":83610.0,"EndTime":84140.0,"Column":5}]},{"RandomW":2008371177,"RandomX":4239141202,"RandomY":1783379941,"RandomZ":2715086902,"StartTime":84407.0,"Objects":[{"StartTime":84407.0,"EndTime":84407.0,"Column":1},{"StartTime":84407.0,"EndTime":84407.0,"Column":5}]},{"RandomW":980563717,"RandomX":3939376884,"RandomY":3778473815,"RandomZ":3882214919,"StartTime":84672.0,"Objects":[{"StartTime":84672.0,"EndTime":85202.0,"Column":4},{"StartTime":84672.0,"EndTime":85202.0,"Column":2}]},{"RandomW":2698098433,"RandomX":3778473815,"RandomY":3882214919,"RandomZ":980563717,"StartTime":85469.0,"Objects":[{"StartTime":85469.0,"EndTime":85469.0,"Column":1}]},{"RandomW":4140546075,"RandomX":3882214919,"RandomY":980563717,"RandomZ":2698098433,"StartTime":85734.0,"Objects":[{"StartTime":85734.0,"EndTime":85734.0,"Column":3},{"StartTime":85734.0,"EndTime":85734.0,"Column":4}]},{"RandomW":1045835035,"RandomX":980563717,"RandomY":2698098433,"RandomZ":4140546075,"StartTime":86000.0,"Objects":[{"StartTime":86000.0,"EndTime":86000.0,"Column":1}]},{"RandomW":2503475147,"RandomX":2698098433,"RandomY":4140546075,"RandomZ":1045835035,"StartTime":86265.0,"Objects":[{"StartTime":86265.0,"EndTime":86265.0,"Column":1},{"StartTime":86265.0,"EndTime":86265.0,"Column":2}]},{"RandomW":3094559699,"RandomX":4140546075,"RandomY":1045835035,"RandomZ":2503475147,"StartTime":86531.0,"Objects":[{"StartTime":86531.0,"EndTime":86531.0,"Column":3}]},{"RandomW":332613542,"RandomX":1045835035,"RandomY":2503475147,"RandomZ":3094559699,"StartTime":86796.0,"Objects":[{"StartTime":86796.0,"EndTime":86796.0,"Column":2},{"StartTime":86796.0,"EndTime":86796.0,"Column":3}]},{"RandomW":2534271858,"RandomX":332613542,"RandomY":2623704626,"RandomZ":3061969874,"StartTime":87327.0,"Objects":[{"StartTime":87327.0,"EndTime":87592.0,"Column":1}]},{"RandomW":794230988,"RandomX":2534271858,"RandomY":510287938,"RandomZ":2532404899,"StartTime":87858.0,"Objects":[{"StartTime":87858.0,"EndTime":88388.0,"Column":2}]},{"RandomW":3623430191,"RandomX":510287938,"RandomY":2532404899,"RandomZ":794230988,"StartTime":88655.0,"Objects":[{"StartTime":88655.0,"EndTime":88655.0,"Column":2}]},{"RandomW":2269498220,"RandomX":794230988,"RandomY":3623430191,"RandomZ":2598120162,"StartTime":88920.0,"Objects":[{"StartTime":88920.0,"EndTime":89450.0,"Column":0}]},{"RandomW":277080616,"RandomX":3623430191,"RandomY":2598120162,"RandomZ":2269498220,"StartTime":89717.0,"Objects":[{"StartTime":89717.0,"EndTime":89717.0,"Column":2}]},{"RandomW":237305927,"RandomX":2598120162,"RandomY":2269498220,"RandomZ":277080616,"StartTime":89982.0,"Objects":[{"StartTime":89982.0,"EndTime":89982.0,"Column":1},{"StartTime":89982.0,"EndTime":89982.0,"Column":2}]},{"RandomW":3697412902,"RandomX":277080616,"RandomY":237305927,"RandomZ":1976938587,"StartTime":90247.0,"Objects":[{"StartTime":90247.0,"EndTime":90247.0,"Column":1},{"StartTime":90247.0,"EndTime":90247.0,"Column":4}]},{"RandomW":3552536616,"RandomX":237305927,"RandomY":1976938587,"RandomZ":3697412902,"StartTime":90513.0,"Objects":[{"StartTime":90513.0,"EndTime":90513.0,"Column":2},{"StartTime":90513.0,"EndTime":90513.0,"Column":3}]},{"RandomW":758205604,"RandomX":3697412902,"RandomY":3552536616,"RandomZ":4122897696,"StartTime":90778.0,"Objects":[{"StartTime":90778.0,"EndTime":90778.0,"Column":1},{"StartTime":90778.0,"EndTime":90778.0,"Column":2}]},{"RandomW":3787868447,"RandomX":3552536616,"RandomY":4122897696,"RandomZ":758205604,"StartTime":91044.0,"Objects":[{"StartTime":91044.0,"EndTime":91044.0,"Column":2},{"StartTime":91044.0,"EndTime":91044.0,"Column":3}]},{"RandomW":1748107640,"RandomX":3787868447,"RandomY":3373302567,"RandomZ":3485540424,"StartTime":91575.0,"Objects":[{"StartTime":91575.0,"EndTime":91840.0,"Column":4}]},{"RandomW":4130051617,"RandomX":3485540424,"RandomY":1748107640,"RandomZ":3144627152,"StartTime":92106.0,"Objects":[{"StartTime":92106.0,"EndTime":92636.0,"Column":5}]},{"RandomW":808332236,"RandomX":1748107640,"RandomY":3144627152,"RandomZ":4130051617,"StartTime":92902.0,"Objects":[{"StartTime":92902.0,"EndTime":92902.0,"Column":3}]},{"RandomW":182226446,"RandomX":4130051617,"RandomY":808332236,"RandomZ":3371160944,"StartTime":93168.0,"Objects":[{"StartTime":93168.0,"EndTime":93698.0,"Column":0}]},{"RandomW":2699856874,"RandomX":808332236,"RandomY":3371160944,"RandomZ":182226446,"StartTime":93964.0,"Objects":[{"StartTime":93964.0,"EndTime":93964.0,"Column":1}]},{"RandomW":3110990203,"RandomX":2699856874,"RandomY":3789399152,"RandomZ":1462741358,"StartTime":94230.0,"Objects":[{"StartTime":94230.0,"EndTime":94495.0,"Column":4},{"StartTime":94230.0,"EndTime":94495.0,"Column":2}]},{"RandomW":2375429180,"RandomX":2098892391,"RandomY":1911053200,"RandomZ":1537665050,"StartTime":94761.0,"Objects":[{"StartTime":94761.0,"EndTime":95026.0,"Column":5},{"StartTime":94761.0,"EndTime":95026.0,"Column":0}]},{"RandomW":391186846,"RandomX":1537665050,"RandomY":2375429180,"RandomZ":609673823,"StartTime":95292.0,"Objects":[{"StartTime":95292.0,"EndTime":96353.0,"Column":1}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":96486.0,"Objects":[{"StartTime":96486.0,"EndTime":98478.0,"Column":5}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":113345.0,"Objects":[{"StartTime":113345.0,"EndTime":113345.0,"Column":4}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":113876.0,"Objects":[{"StartTime":113876.0,"EndTime":113876.0,"Column":1}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":114407.0,"Objects":[{"StartTime":114407.0,"EndTime":114407.0,"Column":4}]},{"RandomW":1192288733,"RandomX":609673823,"RandomY":391186846,"RandomZ":2078004566,"StartTime":114672.0,"Objects":[{"StartTime":114672.0,"EndTime":114672.0,"Column":2},{"StartTime":114672.0,"EndTime":114672.0,"Column":3}]},{"RandomW":3569858426,"RandomX":391186846,"RandomY":2078004566,"RandomZ":1192288733,"StartTime":114938.0,"Objects":[{"StartTime":114938.0,"EndTime":114938.0,"Column":2}]},{"RandomW":1262832005,"RandomX":2078004566,"RandomY":1192288733,"RandomZ":3569858426,"StartTime":115203.0,"Objects":[{"StartTime":115203.0,"EndTime":115203.0,"Column":3},{"StartTime":115203.0,"EndTime":115203.0,"Column":4}]},{"RandomW":4002501854,"RandomX":1192288733,"RandomY":3569858426,"RandomZ":1262832005,"StartTime":115469.0,"Objects":[{"StartTime":115469.0,"EndTime":115469.0,"Column":3},{"StartTime":115469.0,"EndTime":115469.0,"Column":4}]},{"RandomW":776953560,"RandomX":3569858426,"RandomY":1262832005,"RandomZ":4002501854,"StartTime":116000.0,"Objects":[{"StartTime":116000.0,"EndTime":116000.0,"Column":3},{"StartTime":116000.0,"EndTime":116000.0,"Column":4}]},{"RandomW":776953560,"RandomX":3569858426,"RandomY":1262832005,"RandomZ":4002501854,"StartTime":116531.0,"Objects":[{"StartTime":116531.0,"EndTime":116531.0,"Column":2},{"StartTime":116531.0,"EndTime":116531.0,"Column":1}]},{"RandomW":3352969228,"RandomX":1262832005,"RandomY":4002501854,"RandomZ":776953560,"StartTime":117062.0,"Objects":[{"StartTime":117062.0,"EndTime":117062.0,"Column":3},{"StartTime":117062.0,"EndTime":117062.0,"Column":4}]},{"RandomW":2796695571,"RandomX":4002501854,"RandomY":776953560,"RandomZ":3352969228,"StartTime":117327.0,"Objects":[{"StartTime":117327.0,"EndTime":117327.0,"Column":2}]},{"RandomW":3269572543,"RandomX":776953560,"RandomY":3352969228,"RandomZ":2796695571,"StartTime":117593.0,"Objects":[{"StartTime":117593.0,"EndTime":117593.0,"Column":4},{"StartTime":117593.0,"EndTime":117593.0,"Column":5}]},{"RandomW":3269572543,"RandomX":776953560,"RandomY":3352969228,"RandomZ":2796695571,"StartTime":118124.0,"Objects":[{"StartTime":118124.0,"EndTime":118124.0,"Column":1},{"StartTime":118124.0,"EndTime":118124.0,"Column":0}]},{"RandomW":3269572543,"RandomX":776953560,"RandomY":3352969228,"RandomZ":2796695571,"StartTime":118655.0,"Objects":[{"StartTime":118655.0,"EndTime":118655.0,"Column":5},{"StartTime":118655.0,"EndTime":118655.0,"Column":4}]},{"RandomW":2517403813,"RandomX":3352969228,"RandomY":2796695571,"RandomZ":3269572543,"StartTime":118920.0,"Objects":[{"StartTime":118920.0,"EndTime":118920.0,"Column":2},{"StartTime":118920.0,"EndTime":118920.0,"Column":3}]},{"RandomW":2210619464,"RandomX":2796695571,"RandomY":3269572543,"RandomZ":2517403813,"StartTime":119186.0,"Objects":[{"StartTime":119186.0,"EndTime":119186.0,"Column":4}]},{"RandomW":3032935051,"RandomX":3269572543,"RandomY":2517403813,"RandomZ":2210619464,"StartTime":119451.0,"Objects":[{"StartTime":119451.0,"EndTime":119451.0,"Column":5},{"StartTime":119451.0,"EndTime":119451.0,"Column":0}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":119717.0,"Objects":[{"StartTime":119717.0,"EndTime":119717.0,"Column":4},{"StartTime":119717.0,"EndTime":119717.0,"Column":5}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":120247.0,"Objects":[{"StartTime":120247.0,"EndTime":120247.0,"Column":1},{"StartTime":120247.0,"EndTime":120247.0,"Column":0}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":120778.0,"Objects":[{"StartTime":120778.0,"EndTime":120778.0,"Column":5},{"StartTime":120778.0,"EndTime":120778.0,"Column":4}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":121309.0,"Objects":[{"StartTime":121309.0,"EndTime":121309.0,"Column":1},{"StartTime":121309.0,"EndTime":121309.0,"Column":0}]},{"RandomW":2314078604,"RandomX":2210619464,"RandomY":3032935051,"RandomZ":2069229539,"StartTime":121575.0,"Objects":[{"StartTime":121575.0,"EndTime":121575.0,"Column":3}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":121840.0,"Objects":[{"StartTime":121840.0,"EndTime":121840.0,"Column":2},{"StartTime":121840.0,"EndTime":121840.0,"Column":3}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":122371.0,"Objects":[{"StartTime":122371.0,"EndTime":122371.0,"Column":3},{"StartTime":122371.0,"EndTime":122371.0,"Column":2}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":122902.0,"Objects":[{"StartTime":122902.0,"EndTime":122902.0,"Column":3},{"StartTime":122902.0,"EndTime":122902.0,"Column":2}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":123433.0,"Objects":[{"StartTime":123433.0,"EndTime":123433.0,"Column":3},{"StartTime":123433.0,"EndTime":123433.0,"Column":2}]},{"RandomW":2460408790,"RandomX":2069229539,"RandomY":2314078604,"RandomZ":297269721,"StartTime":123699.0,"Objects":[{"StartTime":123699.0,"EndTime":123699.0,"Column":1}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":123964.0,"Objects":[{"StartTime":123964.0,"EndTime":123964.0,"Column":3},{"StartTime":123964.0,"EndTime":123964.0,"Column":4}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":124495.0,"Objects":[{"StartTime":124495.0,"EndTime":124495.0,"Column":2},{"StartTime":124495.0,"EndTime":124495.0,"Column":1}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":125026.0,"Objects":[{"StartTime":125026.0,"EndTime":125026.0,"Column":4},{"StartTime":125026.0,"EndTime":125026.0,"Column":3}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":125557.0,"Objects":[{"StartTime":125557.0,"EndTime":125557.0,"Column":2},{"StartTime":125557.0,"EndTime":125557.0,"Column":1}]},{"RandomW":3204700088,"RandomX":297269721,"RandomY":2460408790,"RandomZ":1180177558,"StartTime":125823.0,"Objects":[{"StartTime":125823.0,"EndTime":125823.0,"Column":2}]},{"RandomW":299141296,"RandomX":2460408790,"RandomY":1180177558,"RandomZ":3204700088,"StartTime":126088.0,"Objects":[{"StartTime":126088.0,"EndTime":126088.0,"Column":3},{"StartTime":126088.0,"EndTime":126088.0,"Column":4}]},{"RandomW":299141296,"RandomX":2460408790,"RandomY":1180177558,"RandomZ":3204700088,"StartTime":126619.0,"Objects":[{"StartTime":126619.0,"EndTime":126619.0,"Column":2},{"StartTime":126619.0,"EndTime":126619.0,"Column":1}]},{"RandomW":299141296,"RandomX":2460408790,"RandomY":1180177558,"RandomZ":3204700088,"StartTime":127150.0,"Objects":[{"StartTime":127150.0,"EndTime":127150.0,"Column":4},{"StartTime":127150.0,"EndTime":127150.0,"Column":3}]},{"RandomW":3037239607,"RandomX":1180177558,"RandomY":3204700088,"RandomZ":299141296,"StartTime":127416.0,"Objects":[{"StartTime":127416.0,"EndTime":127416.0,"Column":4},{"StartTime":127416.0,"EndTime":127416.0,"Column":5}]},{"RandomW":863164324,"RandomX":3204700088,"RandomY":299141296,"RandomZ":3037239607,"StartTime":127681.0,"Objects":[{"StartTime":127681.0,"EndTime":127681.0,"Column":5}]},{"RandomW":2456647781,"RandomX":299141296,"RandomY":3037239607,"RandomZ":863164324,"StartTime":127947.0,"Objects":[{"StartTime":127947.0,"EndTime":127947.0,"Column":4},{"StartTime":127947.0,"EndTime":127947.0,"Column":5}]},{"RandomW":659157904,"RandomX":3037239607,"RandomY":863164324,"RandomZ":2456647781,"StartTime":128212.0,"Objects":[{"StartTime":128212.0,"EndTime":128212.0,"Column":3},{"StartTime":128212.0,"EndTime":128212.0,"Column":4}]},{"RandomW":659157904,"RandomX":3037239607,"RandomY":863164324,"RandomZ":2456647781,"StartTime":128743.0,"Objects":[{"StartTime":128743.0,"EndTime":128743.0,"Column":2},{"StartTime":128743.0,"EndTime":128743.0,"Column":1}]},{"RandomW":659157904,"RandomX":3037239607,"RandomY":863164324,"RandomZ":2456647781,"StartTime":129274.0,"Objects":[{"StartTime":129274.0,"EndTime":129274.0,"Column":4},{"StartTime":129274.0,"EndTime":129274.0,"Column":3}]},{"RandomW":3598260079,"RandomX":863164324,"RandomY":2456647781,"RandomZ":659157904,"StartTime":129540.0,"Objects":[{"StartTime":129540.0,"EndTime":129540.0,"Column":3},{"StartTime":129540.0,"EndTime":129540.0,"Column":4}]},{"RandomW":1930638835,"RandomX":2456647781,"RandomY":659157904,"RandomZ":3598260079,"StartTime":129805.0,"Objects":[{"StartTime":129805.0,"EndTime":129805.0,"Column":1},{"StartTime":129805.0,"EndTime":129805.0,"Column":2}]},{"RandomW":4230333264,"RandomX":1930638835,"RandomY":2319762852,"RandomZ":3807998479,"StartTime":130071.0,"Objects":[{"StartTime":130071.0,"EndTime":130071.0,"Column":2},{"StartTime":130071.0,"EndTime":130071.0,"Column":3}]},{"RandomW":2482386774,"RandomX":4230333264,"RandomY":376688010,"RandomZ":3132506885,"StartTime":132460.0,"Objects":[{"StartTime":132460.0,"EndTime":132990.0,"Column":0}]},{"RandomW":3381449487,"RandomX":3132506885,"RandomY":2482386774,"RandomZ":1092311355,"StartTime":133522.0,"Objects":[{"StartTime":133522.0,"EndTime":134052.0,"Column":3}]},{"RandomW":3812940964,"RandomX":1092311355,"RandomY":3381449487,"RandomZ":3240759120,"StartTime":134318.0,"Objects":[{"StartTime":134318.0,"EndTime":134848.0,"Column":4}]},{"RandomW":2199106412,"RandomX":2014155638,"RandomY":3619038163,"RandomZ":1182263034,"StartTime":135115.0,"Objects":[{"StartTime":135115.0,"EndTime":135380.0,"Column":3},{"StartTime":135115.0,"EndTime":135380.0,"Column":0}]},{"RandomW":4049541057,"RandomX":1182263034,"RandomY":2199106412,"RandomZ":2542868059,"StartTime":135646.0,"Objects":[{"StartTime":135646.0,"EndTime":136176.0,"Column":5}]},{"RandomW":376448389,"RandomX":2542868059,"RandomY":4049541057,"RandomZ":149323558,"StartTime":136708.0,"Objects":[{"StartTime":136708.0,"EndTime":136973.0,"Column":1}]},{"RandomW":10761513,"RandomX":149323558,"RandomY":376448389,"RandomZ":156027614,"StartTime":137239.0,"Objects":[{"StartTime":137239.0,"EndTime":137504.0,"Column":0}]},{"RandomW":2890609580,"RandomX":156027614,"RandomY":10761513,"RandomZ":998270292,"StartTime":137770.0,"Objects":[{"StartTime":137770.0,"EndTime":138566.0,"Column":2}]},{"RandomW":3792858866,"RandomX":998270292,"RandomY":2890609580,"RandomZ":3275622081,"StartTime":138832.0,"Objects":[{"StartTime":138832.0,"EndTime":139097.0,"Column":4}]},{"RandomW":479756469,"RandomX":3792858866,"RandomY":3665829153,"RandomZ":799245198,"StartTime":139363.0,"Objects":[{"StartTime":139363.0,"EndTime":139628.0,"Column":2},{"StartTime":139363.0,"EndTime":139628.0,"Column":1}]},{"RandomW":1559664190,"RandomX":1837897770,"RandomY":3074386351,"RandomZ":2226336565,"StartTime":139894.0,"Objects":[{"StartTime":139894.0,"EndTime":140690.0,"Column":0},{"StartTime":139894.0,"EndTime":140690.0,"Column":4}]},{"RandomW":1370921154,"RandomX":3074386351,"RandomY":2226336565,"RandomZ":1559664190,"StartTime":140955.0,"Objects":[{"StartTime":140955.0,"EndTime":140955.0,"Column":4}]},{"RandomW":12534613,"RandomX":1559664190,"RandomY":1370921154,"RandomZ":495513930,"StartTime":141221.0,"Objects":[{"StartTime":141221.0,"EndTime":141751.0,"Column":3},{"StartTime":141486.0,"EndTime":141486.0,"Column":1},{"StartTime":141751.0,"EndTime":141751.0,"Column":1}]},{"RandomW":1474110729,"RandomX":12534613,"RandomY":3893387802,"RandomZ":226854738,"StartTime":142017.0,"Objects":[{"StartTime":142017.0,"EndTime":142017.0,"Column":2},{"StartTime":142017.0,"EndTime":142017.0,"Column":3}]},{"RandomW":3883366092,"RandomX":1474110729,"RandomY":2911002956,"RandomZ":3337209428,"StartTime":142283.0,"Objects":[{"StartTime":142283.0,"EndTime":142548.0,"Column":4}]},{"RandomW":1868157439,"RandomX":3883366092,"RandomY":1497166406,"RandomZ":3876220972,"StartTime":142814.0,"Objects":[{"StartTime":142814.0,"EndTime":143079.0,"Column":5}]},{"RandomW":868486094,"RandomX":1497166406,"RandomY":3876220972,"RandomZ":1868157439,"StartTime":143345.0,"Objects":[{"StartTime":143345.0,"EndTime":143345.0,"Column":2}]},{"RandomW":2379505970,"RandomX":3876220972,"RandomY":1868157439,"RandomZ":868486094,"StartTime":143610.0,"Objects":[{"StartTime":143610.0,"EndTime":143610.0,"Column":2}]},{"RandomW":971762612,"RandomX":1868157439,"RandomY":868486094,"RandomZ":2379505970,"StartTime":143876.0,"Objects":[{"StartTime":143876.0,"EndTime":143876.0,"Column":4}]},{"RandomW":2333467129,"RandomX":2379505970,"RandomY":971762612,"RandomZ":2560365407,"StartTime":144141.0,"Objects":[{"StartTime":144141.0,"EndTime":144671.0,"Column":0}]},{"RandomW":3275109659,"RandomX":2560365407,"RandomY":2333467129,"RandomZ":2783370328,"StartTime":145203.0,"Objects":[{"StartTime":145203.0,"EndTime":145468.0,"Column":3}]},{"RandomW":2675369072,"RandomX":2783370328,"RandomY":3275109659,"RandomZ":3142107337,"StartTime":145734.0,"Objects":[{"StartTime":145734.0,"EndTime":145999.0,"Column":1}]},{"RandomW":2114821552,"RandomX":3142107337,"RandomY":2675369072,"RandomZ":216133594,"StartTime":146265.0,"Objects":[{"StartTime":146265.0,"EndTime":146795.0,"Column":5}]},{"RandomW":2210288688,"RandomX":2675369072,"RandomY":216133594,"RandomZ":2114821552,"StartTime":147062.0,"Objects":[{"StartTime":147062.0,"EndTime":147062.0,"Column":3}]},{"RandomW":2824847566,"RandomX":2114821552,"RandomY":2210288688,"RandomZ":2881713491,"StartTime":147327.0,"Objects":[{"StartTime":147327.0,"EndTime":147592.0,"Column":1}]},{"RandomW":3418617049,"RandomX":2881713491,"RandomY":2824847566,"RandomZ":3131910248,"StartTime":147858.0,"Objects":[{"StartTime":147858.0,"EndTime":148123.0,"Column":3}]},{"RandomW":4264037536,"RandomX":3418617049,"RandomY":2065328415,"RandomZ":756387586,"StartTime":148389.0,"Objects":[{"StartTime":148389.0,"EndTime":149450.0,"Column":2},{"StartTime":148389.0,"EndTime":149450.0,"Column":5}]},{"RandomW":714689152,"RandomX":2065328415,"RandomY":756387586,"RandomZ":4264037536,"StartTime":149717.0,"Objects":[{"StartTime":149717.0,"EndTime":149717.0,"Column":2}]},{"RandomW":2187562077,"RandomX":756387586,"RandomY":4264037536,"RandomZ":714689152,"StartTime":149982.0,"Objects":[{"StartTime":149982.0,"EndTime":149982.0,"Column":1},{"StartTime":149982.0,"EndTime":149982.0,"Column":2}]},{"RandomW":59731596,"RandomX":4264037536,"RandomY":714689152,"RandomZ":2187562077,"StartTime":150247.0,"Objects":[{"StartTime":150247.0,"EndTime":150247.0,"Column":0}]},{"RandomW":3179032401,"RandomX":714689152,"RandomY":2187562077,"RandomZ":59731596,"StartTime":150513.0,"Objects":[{"StartTime":150513.0,"EndTime":150513.0,"Column":1}]},{"RandomW":1565638452,"RandomX":2187562077,"RandomY":59731596,"RandomZ":3179032401,"StartTime":150778.0,"Objects":[{"StartTime":150778.0,"EndTime":150778.0,"Column":2}]},{"RandomW":3285111207,"RandomX":59731596,"RandomY":3179032401,"RandomZ":1565638452,"StartTime":151044.0,"Objects":[{"StartTime":151044.0,"EndTime":151044.0,"Column":3},{"StartTime":151044.0,"EndTime":151044.0,"Column":4}]},{"RandomW":3142401116,"RandomX":3179032401,"RandomY":1565638452,"RandomZ":3285111207,"StartTime":151309.0,"Objects":[{"StartTime":151309.0,"EndTime":151309.0,"Column":4}]},{"RandomW":2191101353,"RandomX":3142401116,"RandomY":3877079747,"RandomZ":930029834,"StartTime":151575.0,"Objects":[{"StartTime":151575.0,"EndTime":152105.0,"Column":2},{"StartTime":151575.0,"EndTime":152105.0,"Column":0}]},{"RandomW":1171726387,"RandomX":2191101353,"RandomY":1357180538,"RandomZ":201209655,"StartTime":152637.0,"Objects":[{"StartTime":152637.0,"EndTime":152902.0,"Column":3}]},{"RandomW":2089660876,"RandomX":201209655,"RandomY":1171726387,"RandomZ":191699429,"StartTime":153168.0,"Objects":[{"StartTime":153168.0,"EndTime":153698.0,"Column":5}]},{"RandomW":2251323109,"RandomX":1171726387,"RandomY":191699429,"RandomZ":2089660876,"StartTime":153964.0,"Objects":[{"StartTime":153964.0,"EndTime":153964.0,"Column":2}]},{"RandomW":147408153,"RandomX":2251323109,"RandomY":2048526504,"RandomZ":433820735,"StartTime":154230.0,"Objects":[{"StartTime":154230.0,"EndTime":154230.0,"Column":0},{"StartTime":154230.0,"EndTime":154230.0,"Column":5}]},{"RandomW":223059387,"RandomX":2048526504,"RandomY":433820735,"RandomZ":147408153,"StartTime":154495.0,"Objects":[{"StartTime":154495.0,"EndTime":154495.0,"Column":3}]},{"RandomW":1644267862,"RandomX":147408153,"RandomY":223059387,"RandomZ":2814282738,"StartTime":154761.0,"Objects":[{"StartTime":154761.0,"EndTime":155026.0,"Column":4}]},{"RandomW":585628331,"RandomX":1644267862,"RandomY":547547522,"RandomZ":1901399656,"StartTime":155292.0,"Objects":[{"StartTime":155292.0,"EndTime":155292.0,"Column":0},{"StartTime":155292.0,"EndTime":155292.0,"Column":5}]},{"RandomW":1287818392,"RandomX":547547522,"RandomY":1901399656,"RandomZ":585628331,"StartTime":155557.0,"Objects":[{"StartTime":155557.0,"EndTime":155557.0,"Column":1}]},{"RandomW":3879046214,"RandomX":2065404539,"RandomY":2732913982,"RandomZ":3217781099,"StartTime":155823.0,"Objects":[{"StartTime":155823.0,"EndTime":156088.0,"Column":2},{"StartTime":155823.0,"EndTime":156088.0,"Column":4}]},{"RandomW":3318878889,"RandomX":3217781099,"RandomY":3879046214,"RandomZ":1075466897,"StartTime":156354.0,"Objects":[{"StartTime":156354.0,"EndTime":156619.0,"Column":3}]},{"RandomW":1785367685,"RandomX":1075466897,"RandomY":3318878889,"RandomZ":561406801,"StartTime":156885.0,"Objects":[{"StartTime":156885.0,"EndTime":157415.0,"Column":4}]},{"RandomW":2909067134,"RandomX":561406801,"RandomY":1785367685,"RandomZ":4168537475,"StartTime":157947.0,"Objects":[{"StartTime":157947.0,"EndTime":157947.0,"Column":5},{"StartTime":157947.0,"EndTime":157947.0,"Column":2}]},{"RandomW":1067074920,"RandomX":1785367685,"RandomY":4168537475,"RandomZ":2909067134,"StartTime":158212.0,"Objects":[{"StartTime":158212.0,"EndTime":158212.0,"Column":4}]},{"RandomW":27977914,"RandomX":4168537475,"RandomY":2909067134,"RandomZ":1067074920,"StartTime":158478.0,"Objects":[{"StartTime":158478.0,"EndTime":158478.0,"Column":5},{"StartTime":158478.0,"EndTime":158478.0,"Column":0}]},{"RandomW":1329528769,"RandomX":2909067134,"RandomY":1067074920,"RandomZ":27977914,"StartTime":158743.0,"Objects":[{"StartTime":158743.0,"EndTime":158743.0,"Column":4}]},{"RandomW":3295284863,"RandomX":1067074920,"RandomY":27977914,"RandomZ":1329528769,"StartTime":159009.0,"Objects":[{"StartTime":159009.0,"EndTime":159009.0,"Column":5}]},{"RandomW":691446431,"RandomX":27977914,"RandomY":1329528769,"RandomZ":3295284863,"StartTime":159540.0,"Objects":[{"StartTime":159540.0,"EndTime":159540.0,"Column":3},{"StartTime":159540.0,"EndTime":159540.0,"Column":4}]},{"RandomW":3354872060,"RandomX":3295284863,"RandomY":691446431,"RandomZ":2140106811,"StartTime":159805.0,"Objects":[{"StartTime":159805.0,"EndTime":159805.0,"Column":2},{"StartTime":159805.0,"EndTime":159805.0,"Column":3}]},{"RandomW":1400553355,"RandomX":691446431,"RandomY":2140106811,"RandomZ":3354872060,"StartTime":160071.0,"Objects":[{"StartTime":160071.0,"EndTime":160071.0,"Column":2}]},{"RandomW":1400553355,"RandomX":691446431,"RandomY":2140106811,"RandomZ":3354872060,"StartTime":160601.0,"Objects":[{"StartTime":160601.0,"EndTime":160601.0,"Column":3}]},{"RandomW":3485781281,"RandomX":2140106811,"RandomY":3354872060,"RandomZ":1400553355,"StartTime":160867.0,"Objects":[{"StartTime":160867.0,"EndTime":160867.0,"Column":3}]},{"RandomW":3053679463,"RandomX":1400553355,"RandomY":3485781281,"RandomZ":3419304522,"StartTime":161132.0,"Objects":[{"StartTime":161132.0,"EndTime":161397.0,"Column":2}]},{"RandomW":3645336111,"RandomX":3419304522,"RandomY":3053679463,"RandomZ":805504203,"StartTime":161663.0,"Objects":[{"StartTime":161663.0,"EndTime":162193.0,"Column":4}]},{"RandomW":1638076271,"RandomX":3053679463,"RandomY":805504203,"RandomZ":3645336111,"StartTime":162460.0,"Objects":[{"StartTime":162460.0,"EndTime":162460.0,"Column":3}]},{"RandomW":107981020,"RandomX":1638076271,"RandomY":3432435831,"RandomZ":3835408498,"StartTime":162725.0,"Objects":[{"StartTime":162725.0,"EndTime":162725.0,"Column":0},{"StartTime":162725.0,"EndTime":162725.0,"Column":5}]},{"RandomW":94467567,"RandomX":3835408498,"RandomY":107981020,"RandomZ":2144208649,"StartTime":163256.0,"Objects":[{"StartTime":163256.0,"EndTime":163256.0,"Column":4},{"StartTime":163256.0,"EndTime":163256.0,"Column":0}]},{"RandomW":1015041289,"RandomX":107981020,"RandomY":2144208649,"RandomZ":94467567,"StartTime":163522.0,"Objects":[{"StartTime":163522.0,"EndTime":163522.0,"Column":3}]},{"RandomW":2029876639,"RandomX":1204955917,"RandomY":1210817201,"RandomZ":1177260118,"StartTime":163787.0,"Objects":[{"StartTime":163787.0,"EndTime":164052.0,"Column":5}]},{"RandomW":3125496505,"RandomX":1177260118,"RandomY":2029876639,"RandomZ":2929832910,"StartTime":164318.0,"Objects":[{"StartTime":164318.0,"EndTime":164583.0,"Column":2}]},{"RandomW":2426857185,"RandomX":3125496505,"RandomY":2700661894,"RandomZ":859446411,"StartTime":164849.0,"Objects":[{"StartTime":164849.0,"EndTime":165114.0,"Column":0}]},{"RandomW":4116661924,"RandomX":2426857185,"RandomY":1884842190,"RandomZ":375578279,"StartTime":165380.0,"Objects":[{"StartTime":165380.0,"EndTime":165910.0,"Column":1},{"StartTime":165380.0,"EndTime":165910.0,"Column":5}]},{"RandomW":3787729819,"RandomX":375578279,"RandomY":4116661924,"RandomZ":1382622976,"StartTime":166442.0,"Objects":[{"StartTime":166442.0,"EndTime":166972.0,"Column":4}]},{"RandomW":3780331234,"RandomX":4116661924,"RandomY":1382622976,"RandomZ":3787729819,"StartTime":167239.0,"Objects":[{"StartTime":167239.0,"EndTime":167239.0,"Column":3}]},{"RandomW":891570220,"RandomX":3780331234,"RandomY":3996538378,"RandomZ":4118560235,"StartTime":167504.0,"Objects":[{"StartTime":167504.0,"EndTime":168034.0,"Column":5},{"StartTime":167504.0,"EndTime":168034.0,"Column":2}]},{"RandomW":1312521276,"RandomX":3996538378,"RandomY":4118560235,"RandomZ":891570220,"StartTime":168301.0,"Objects":[{"StartTime":168301.0,"EndTime":168301.0,"Column":0}]},{"RandomW":316798455,"RandomX":4118560235,"RandomY":891570220,"RandomZ":1312521276,"StartTime":168566.0,"Objects":[{"StartTime":168566.0,"EndTime":168566.0,"Column":2},{"StartTime":168566.0,"EndTime":168566.0,"Column":3}]},{"RandomW":107348261,"RandomX":891570220,"RandomY":1312521276,"RandomZ":316798455,"StartTime":168832.0,"Objects":[{"StartTime":168832.0,"EndTime":168832.0,"Column":1}]},{"RandomW":286543085,"RandomX":1312521276,"RandomY":316798455,"RandomZ":107348261,"StartTime":169097.0,"Objects":[{"StartTime":169097.0,"EndTime":169097.0,"Column":1},{"StartTime":169097.0,"EndTime":169097.0,"Column":2}]},{"RandomW":2220558447,"RandomX":316798455,"RandomY":107348261,"RandomZ":286543085,"StartTime":169363.0,"Objects":[{"StartTime":169363.0,"EndTime":169363.0,"Column":2}]},{"RandomW":2567445342,"RandomX":107348261,"RandomY":286543085,"RandomZ":2220558447,"StartTime":169628.0,"Objects":[{"StartTime":169628.0,"EndTime":169628.0,"Column":1},{"StartTime":169628.0,"EndTime":169628.0,"Column":2}]},{"RandomW":2941341299,"RandomX":286543085,"RandomY":2220558447,"RandomZ":2567445342,"StartTime":170159.0,"Objects":[{"StartTime":170159.0,"EndTime":170159.0,"Column":3},{"StartTime":170159.0,"EndTime":170159.0,"Column":4}]},{"RandomW":2941341299,"RandomX":286543085,"RandomY":2220558447,"RandomZ":2567445342,"StartTime":170424.0,"Objects":[{"StartTime":170424.0,"EndTime":170424.0,"Column":2},{"StartTime":170424.0,"EndTime":170424.0,"Column":1}]},{"RandomW":1087727581,"RandomX":2567445342,"RandomY":2941341299,"RandomZ":479267920,"StartTime":170690.0,"Objects":[{"StartTime":170690.0,"EndTime":171220.0,"Column":3}]},{"RandomW":2581485170,"RandomX":2941341299,"RandomY":479267920,"RandomZ":1087727581,"StartTime":171486.0,"Objects":[{"StartTime":171486.0,"EndTime":171486.0,"Column":5}]},{"RandomW":683596203,"RandomX":1087727581,"RandomY":2581485170,"RandomZ":3168383468,"StartTime":171752.0,"Objects":[{"StartTime":171752.0,"EndTime":172282.0,"Column":1}]},{"RandomW":3284056302,"RandomX":2581485170,"RandomY":3168383468,"RandomZ":683596203,"StartTime":172548.0,"Objects":[{"StartTime":172548.0,"EndTime":172548.0,"Column":2}]},{"RandomW":2830633773,"RandomX":3168383468,"RandomY":683596203,"RandomZ":3284056302,"StartTime":172814.0,"Objects":[{"StartTime":172814.0,"EndTime":172814.0,"Column":3},{"StartTime":172814.0,"EndTime":172814.0,"Column":4}]},{"RandomW":3651115271,"RandomX":683596203,"RandomY":3284056302,"RandomZ":2830633773,"StartTime":173079.0,"Objects":[{"StartTime":173079.0,"EndTime":173079.0,"Column":3}]},{"RandomW":120746014,"RandomX":3284056302,"RandomY":2830633773,"RandomZ":3651115271,"StartTime":173345.0,"Objects":[{"StartTime":173345.0,"EndTime":173345.0,"Column":3},{"StartTime":173345.0,"EndTime":173345.0,"Column":4}]},{"RandomW":830325214,"RandomX":2830633773,"RandomY":3651115271,"RandomZ":120746014,"StartTime":173610.0,"Objects":[{"StartTime":173610.0,"EndTime":173610.0,"Column":4}]},{"RandomW":1509180863,"RandomX":3651115271,"RandomY":120746014,"RandomZ":830325214,"StartTime":173876.0,"Objects":[{"StartTime":173876.0,"EndTime":173876.0,"Column":3},{"StartTime":173876.0,"EndTime":173876.0,"Column":4}]},{"RandomW":2233493011,"RandomX":3902833961,"RandomY":923589330,"RandomZ":3425613873,"StartTime":174407.0,"Objects":[{"StartTime":174407.0,"EndTime":174672.0,"Column":2},{"StartTime":174407.0,"EndTime":174672.0,"Column":0}]},{"RandomW":2517643905,"RandomX":1207989122,"RandomY":993303558,"RandomZ":3011821377,"StartTime":174938.0,"Objects":[{"StartTime":174938.0,"EndTime":175468.0,"Column":3},{"StartTime":174938.0,"EndTime":175468.0,"Column":1}]},{"RandomW":3720863650,"RandomX":993303558,"RandomY":3011821377,"RandomZ":2517643905,"StartTime":175734.0,"Objects":[{"StartTime":175734.0,"EndTime":175734.0,"Column":2}]},{"RandomW":3563355415,"RandomX":2517643905,"RandomY":3720863650,"RandomZ":1116519600,"StartTime":176000.0,"Objects":[{"StartTime":176000.0,"EndTime":176530.0,"Column":3}]},{"RandomW":3287800096,"RandomX":3720863650,"RandomY":1116519600,"RandomZ":3563355415,"StartTime":176796.0,"Objects":[{"StartTime":176796.0,"EndTime":176796.0,"Column":3}]},{"RandomW":539898931,"RandomX":1116519600,"RandomY":3563355415,"RandomZ":3287800096,"StartTime":177062.0,"Objects":[{"StartTime":177062.0,"EndTime":177062.0,"Column":2},{"StartTime":177062.0,"EndTime":177062.0,"Column":3}]},{"RandomW":123758010,"RandomX":3563355415,"RandomY":3287800096,"RandomZ":539898931,"StartTime":177327.0,"Objects":[{"StartTime":177327.0,"EndTime":177327.0,"Column":4}]},{"RandomW":4028312708,"RandomX":3287800096,"RandomY":539898931,"RandomZ":123758010,"StartTime":177593.0,"Objects":[{"StartTime":177593.0,"EndTime":177593.0,"Column":2},{"StartTime":177593.0,"EndTime":177593.0,"Column":3}]},{"RandomW":2371409278,"RandomX":539898931,"RandomY":123758010,"RandomZ":4028312708,"StartTime":177858.0,"Objects":[{"StartTime":177858.0,"EndTime":177858.0,"Column":3}]},{"RandomW":3699828554,"RandomX":123758010,"RandomY":4028312708,"RandomZ":2371409278,"StartTime":178124.0,"Objects":[{"StartTime":178124.0,"EndTime":178124.0,"Column":2},{"StartTime":178124.0,"EndTime":178124.0,"Column":3}]},{"RandomW":4053363780,"RandomX":2371409278,"RandomY":3699828554,"RandomZ":3637445845,"StartTime":178655.0,"Objects":[{"StartTime":178655.0,"EndTime":178920.0,"Column":5}]},{"RandomW":1366734997,"RandomX":3637445845,"RandomY":4053363780,"RandomZ":3122766892,"StartTime":179186.0,"Objects":[{"StartTime":179186.0,"EndTime":179716.0,"Column":3}]},{"RandomW":2085192570,"RandomX":1366734997,"RandomY":4047501250,"RandomZ":3422445293,"StartTime":179982.0,"Objects":[{"StartTime":179982.0,"EndTime":179982.0,"Column":3},{"StartTime":179982.0,"EndTime":179982.0,"Column":5}]},{"RandomW":2526042960,"RandomX":3422445293,"RandomY":2085192570,"RandomZ":2552180342,"StartTime":180247.0,"Objects":[{"StartTime":180247.0,"EndTime":180777.0,"Column":1}]},{"RandomW":2946528857,"RandomX":2085192570,"RandomY":2552180342,"RandomZ":2526042960,"StartTime":181044.0,"Objects":[{"StartTime":181044.0,"EndTime":181044.0,"Column":2}]},{"RandomW":4275012500,"RandomX":2526042960,"RandomY":2946528857,"RandomZ":2680316548,"StartTime":181309.0,"Objects":[{"StartTime":181309.0,"EndTime":181574.0,"Column":5}]},{"RandomW":716767862,"RandomX":1177533555,"RandomY":3396673648,"RandomZ":1210370441,"StartTime":181840.0,"Objects":[{"StartTime":181840.0,"EndTime":182105.0,"Column":3},{"StartTime":181840.0,"EndTime":182105.0,"Column":2}]},{"RandomW":1918581647,"RandomX":1210370441,"RandomY":716767862,"RandomZ":290385782,"StartTime":182371.0,"Objects":[{"StartTime":182371.0,"EndTime":182636.0,"Column":5}]},{"RandomW":2554770024,"RandomX":1918581647,"RandomY":475913420,"RandomZ":4262840195,"StartTime":182902.0,"Objects":[{"StartTime":182902.0,"EndTime":183432.0,"Column":1}]},{"RandomW":862610860,"RandomX":475913420,"RandomY":4262840195,"RandomZ":2554770024,"StartTime":183699.0,"Objects":[{"StartTime":183699.0,"EndTime":185557.0,"Column":2}]},{"RandomW":3240322225,"RandomX":4262840195,"RandomY":2554770024,"RandomZ":862610860,"StartTime":202017.0,"Objects":[{"StartTime":202017.0,"EndTime":202017.0,"Column":0}]},{"RandomW":2438630089,"RandomX":2554770024,"RandomY":862610860,"RandomZ":3240322225,"StartTime":202283.0,"Objects":[{"StartTime":202283.0,"EndTime":202283.0,"Column":1}]},{"RandomW":1543895637,"RandomX":3240322225,"RandomY":2438630089,"RandomZ":1008910200,"StartTime":202548.0,"Objects":[{"StartTime":202548.0,"EndTime":203078.0,"Column":4}]},{"RandomW":2262375304,"RandomX":2438630089,"RandomY":1008910200,"RandomZ":1543895637,"StartTime":203345.0,"Objects":[{"StartTime":203345.0,"EndTime":203345.0,"Column":2}]},{"RandomW":3932191533,"RandomX":1543895637,"RandomY":2262375304,"RandomZ":3281044824,"StartTime":203610.0,"Objects":[{"StartTime":203610.0,"EndTime":203875.0,"Column":4}]},{"RandomW":2456816417,"RandomX":3932191533,"RandomY":2579817318,"RandomZ":3616517773,"StartTime":204141.0,"Objects":[{"StartTime":204141.0,"EndTime":204406.0,"Column":0}]},{"RandomW":1863357795,"RandomX":2456816417,"RandomY":2065740625,"RandomZ":3309416576,"StartTime":204672.0,"Objects":[{"StartTime":204672.0,"EndTime":205202.0,"Column":3},{"StartTime":204672.0,"EndTime":205202.0,"Column":5}]},{"RandomW":66010220,"RandomX":3309416576,"RandomY":1863357795,"RandomZ":2100015779,"StartTime":205469.0,"Objects":[{"StartTime":205469.0,"EndTime":205469.0,"Column":4},{"StartTime":205469.0,"EndTime":205469.0,"Column":0}]},{"RandomW":548562611,"RandomX":2100015779,"RandomY":66010220,"RandomZ":3420604705,"StartTime":205734.0,"Objects":[{"StartTime":205734.0,"EndTime":205999.0,"Column":1}]},{"RandomW":2052728473,"RandomX":3420604705,"RandomY":548562611,"RandomZ":2913964,"StartTime":206265.0,"Objects":[{"StartTime":206265.0,"EndTime":206530.0,"Column":5}]},{"RandomW":1944462115,"RandomX":2052728473,"RandomY":2737357746,"RandomZ":270315162,"StartTime":206796.0,"Objects":[{"StartTime":206796.0,"EndTime":206796.0,"Column":2},{"StartTime":206796.0,"EndTime":206796.0,"Column":3}]},{"RandomW":3626216744,"RandomX":2737357746,"RandomY":270315162,"RandomZ":1944462115,"StartTime":207062.0,"Objects":[{"StartTime":207062.0,"EndTime":207062.0,"Column":5}]},{"RandomW":1039388877,"RandomX":270315162,"RandomY":1944462115,"RandomZ":3626216744,"StartTime":207327.0,"Objects":[{"StartTime":207327.0,"EndTime":207327.0,"Column":4}]},{"RandomW":3362701719,"RandomX":1944462115,"RandomY":3626216744,"RandomZ":1039388877,"StartTime":207593.0,"Objects":[{"StartTime":207593.0,"EndTime":207593.0,"Column":3}]},{"RandomW":3968495235,"RandomX":3362701719,"RandomY":2329091202,"RandomZ":1331472925,"StartTime":207858.0,"Objects":[{"StartTime":207858.0,"EndTime":208388.0,"Column":5}]},{"RandomW":1381394684,"RandomX":2329091202,"RandomY":1331472925,"RandomZ":3968495235,"StartTime":208655.0,"Objects":[{"StartTime":208655.0,"EndTime":208655.0,"Column":5}]},{"RandomW":1435798214,"RandomX":1381394684,"RandomY":1081301304,"RandomZ":3939835753,"StartTime":208920.0,"Objects":[{"StartTime":208920.0,"EndTime":209450.0,"Column":4}]},{"RandomW":3026458880,"RandomX":1081301304,"RandomY":3939835753,"RandomZ":1435798214,"StartTime":209717.0,"Objects":[{"StartTime":209717.0,"EndTime":209717.0,"Column":5}]},{"RandomW":3713738018,"RandomX":3026458880,"RandomY":1845767213,"RandomZ":745035987,"StartTime":209982.0,"Objects":[{"StartTime":209982.0,"EndTime":210512.0,"Column":2},{"StartTime":209982.0,"EndTime":210512.0,"Column":4}]},{"RandomW":1231260560,"RandomX":1845767213,"RandomY":745035987,"RandomZ":3713738018,"StartTime":210778.0,"Objects":[{"StartTime":210778.0,"EndTime":210778.0,"Column":4}]},{"RandomW":105489365,"RandomX":745035987,"RandomY":3713738018,"RandomZ":1231260560,"StartTime":211044.0,"Objects":[{"StartTime":211044.0,"EndTime":211044.0,"Column":4}]},{"RandomW":1753861391,"RandomX":3713738018,"RandomY":1231260560,"RandomZ":105489365,"StartTime":211309.0,"Objects":[{"StartTime":211309.0,"EndTime":211309.0,"Column":2}]},{"RandomW":966114829,"RandomX":105489365,"RandomY":1753861391,"RandomZ":1828685577,"StartTime":211575.0,"Objects":[{"StartTime":211575.0,"EndTime":211575.0,"Column":3},{"StartTime":211575.0,"EndTime":211575.0,"Column":2}]},{"RandomW":1431749195,"RandomX":1836275468,"RandomY":1290011463,"RandomZ":1159621643,"StartTime":211840.0,"Objects":[{"StartTime":211840.0,"EndTime":212370.0,"Column":5},{"StartTime":211840.0,"EndTime":212370.0,"Column":4}]},{"RandomW":3472418283,"RandomX":1159621643,"RandomY":1431749195,"RandomZ":2724869338,"StartTime":212637.0,"Objects":[{"StartTime":212637.0,"EndTime":212902.0,"Column":3}]},{"RandomW":1755864208,"RandomX":3472418283,"RandomY":2016458251,"RandomZ":2610391004,"StartTime":213168.0,"Objects":[{"StartTime":213168.0,"EndTime":213698.0,"Column":1},{"StartTime":213168.0,"EndTime":213698.0,"Column":4}]},{"RandomW":1635138515,"RandomX":2016458251,"RandomY":2610391004,"RandomZ":1755864208,"StartTime":213964.0,"Objects":[{"StartTime":213964.0,"EndTime":213964.0,"Column":3}]},{"RandomW":3162662082,"RandomX":1755864208,"RandomY":1635138515,"RandomZ":2617989400,"StartTime":214230.0,"Objects":[{"StartTime":214230.0,"EndTime":214495.0,"Column":2}]},{"RandomW":1184692914,"RandomX":2617989400,"RandomY":3162662082,"RandomZ":2531582750,"StartTime":214761.0,"Objects":[{"StartTime":214761.0,"EndTime":215026.0,"Column":3}]},{"RandomW":798124101,"RandomX":2531582750,"RandomY":1184692914,"RandomZ":2157553888,"StartTime":215292.0,"Objects":[{"StartTime":215292.0,"EndTime":215557.0,"Column":2}]},{"RandomW":1923400471,"RandomX":798124101,"RandomY":2665448122,"RandomZ":1060614841,"StartTime":215823.0,"Objects":[{"StartTime":215823.0,"EndTime":216088.0,"Column":5}]},{"RandomW":775950648,"RandomX":1923400471,"RandomY":3469237574,"RandomZ":2892029047,"StartTime":216354.0,"Objects":[{"StartTime":216354.0,"EndTime":216354.0,"Column":1},{"StartTime":216354.0,"EndTime":216354.0,"Column":4}]},{"RandomW":1321234603,"RandomX":4127626210,"RandomY":1546611249,"RandomZ":1925740893,"StartTime":216885.0,"Objects":[{"StartTime":216885.0,"EndTime":217150.0,"Column":5},{"StartTime":216885.0,"EndTime":217150.0,"Column":3}]},{"RandomW":2881678930,"RandomX":1925740893,"RandomY":1321234603,"RandomZ":2358993682,"StartTime":217416.0,"Objects":[{"StartTime":217416.0,"EndTime":217946.0,"Column":2}]},{"RandomW":2599512294,"RandomX":1321234603,"RandomY":2358993682,"RandomZ":2881678930,"StartTime":218212.0,"Objects":[{"StartTime":218212.0,"EndTime":218212.0,"Column":1}]},{"RandomW":2150464549,"RandomX":2881678930,"RandomY":2599512294,"RandomZ":3623425595,"StartTime":218478.0,"Objects":[{"StartTime":218478.0,"EndTime":219008.0,"Column":0}]},{"RandomW":763775798,"RandomX":3623425595,"RandomY":2150464549,"RandomZ":1008837132,"StartTime":219274.0,"Objects":[{"StartTime":219274.0,"EndTime":221132.0,"Column":2}]},{"RandomW":3656799832,"RandomX":1008837132,"RandomY":763775798,"RandomZ":852609139,"StartTime":221663.0,"Objects":[{"StartTime":221663.0,"EndTime":222193.0,"Column":4}]},{"RandomW":4147545979,"RandomX":852609139,"RandomY":3656799832,"RandomZ":3908484776,"StartTime":222460.0,"Objects":[{"StartTime":222460.0,"EndTime":222460.0,"Column":2},{"StartTime":222460.0,"EndTime":222460.0,"Column":5}]},{"RandomW":540508179,"RandomX":3908484776,"RandomY":4147545979,"RandomZ":1259887550,"StartTime":222725.0,"Objects":[{"StartTime":222725.0,"EndTime":223255.0,"Column":1}]},{"RandomW":1042752714,"RandomX":1259887550,"RandomY":540508179,"RandomZ":2104064323,"StartTime":223522.0,"Objects":[{"StartTime":223522.0,"EndTime":223522.0,"Column":5},{"StartTime":223522.0,"EndTime":223522.0,"Column":2}]},{"RandomW":3077262619,"RandomX":540508179,"RandomY":2104064323,"RandomZ":1042752714,"StartTime":223787.0,"Objects":[{"StartTime":223787.0,"EndTime":223787.0,"Column":3},{"StartTime":223787.0,"EndTime":223787.0,"Column":4}]},{"RandomW":734033149,"RandomX":2104064323,"RandomY":1042752714,"RandomZ":3077262619,"StartTime":224053.0,"Objects":[{"StartTime":224053.0,"EndTime":224053.0,"Column":4}]},{"RandomW":492155815,"RandomX":1042752714,"RandomY":3077262619,"RandomZ":734033149,"StartTime":224318.0,"Objects":[{"StartTime":224318.0,"EndTime":224318.0,"Column":4},{"StartTime":224318.0,"EndTime":224318.0,"Column":5}]},{"RandomW":441697715,"RandomX":3077262619,"RandomY":734033149,"RandomZ":492155815,"StartTime":224584.0,"Objects":[{"StartTime":224584.0,"EndTime":224584.0,"Column":3}]},{"RandomW":4156379255,"RandomX":734033149,"RandomY":492155815,"RandomZ":441697715,"StartTime":224849.0,"Objects":[{"StartTime":224849.0,"EndTime":224849.0,"Column":4},{"StartTime":224849.0,"EndTime":224849.0,"Column":5}]},{"RandomW":3757225441,"RandomX":492155815,"RandomY":441697715,"RandomZ":4156379255,"StartTime":225380.0,"Objects":[{"StartTime":225380.0,"EndTime":225380.0,"Column":2},{"StartTime":225380.0,"EndTime":225380.0,"Column":3}]},{"RandomW":3757225441,"RandomX":492155815,"RandomY":441697715,"RandomZ":4156379255,"StartTime":225646.0,"Objects":[{"StartTime":225646.0,"EndTime":225646.0,"Column":3},{"StartTime":225646.0,"EndTime":225646.0,"Column":2}]},{"RandomW":2225043333,"RandomX":3950035756,"RandomY":4132636893,"RandomZ":3158636107,"StartTime":225911.0,"Objects":[{"StartTime":225911.0,"EndTime":226441.0,"Column":5},{"StartTime":225911.0,"EndTime":226441.0,"Column":0}]},{"RandomW":479006094,"RandomX":2225043333,"RandomY":3919293849,"RandomZ":2279622039,"StartTime":226708.0,"Objects":[{"StartTime":226708.0,"EndTime":226708.0,"Column":0},{"StartTime":226708.0,"EndTime":226708.0,"Column":1}]},{"RandomW":3529234379,"RandomX":479006094,"RandomY":1674670789,"RandomZ":1460857923,"StartTime":226973.0,"Objects":[{"StartTime":226973.0,"EndTime":227503.0,"Column":4},{"StartTime":226973.0,"EndTime":227503.0,"Column":3}]},{"RandomW":2798539123,"RandomX":1674670789,"RandomY":1460857923,"RandomZ":3529234379,"StartTime":227770.0,"Objects":[{"StartTime":227770.0,"EndTime":227770.0,"Column":3}]},{"RandomW":1315002421,"RandomX":1460857923,"RandomY":3529234379,"RandomZ":2798539123,"StartTime":228035.0,"Objects":[{"StartTime":228035.0,"EndTime":228035.0,"Column":2},{"StartTime":228035.0,"EndTime":228035.0,"Column":3}]},{"RandomW":2396116302,"RandomX":3529234379,"RandomY":2798539123,"RandomZ":1315002421,"StartTime":228301.0,"Objects":[{"StartTime":228301.0,"EndTime":228301.0,"Column":1}]},{"RandomW":2184752848,"RandomX":2798539123,"RandomY":1315002421,"RandomZ":2396116302,"StartTime":228566.0,"Objects":[{"StartTime":228566.0,"EndTime":228566.0,"Column":2},{"StartTime":228566.0,"EndTime":228566.0,"Column":3}]},{"RandomW":1453929005,"RandomX":1315002421,"RandomY":2396116302,"RandomZ":2184752848,"StartTime":228832.0,"Objects":[{"StartTime":228832.0,"EndTime":228832.0,"Column":1}]},{"RandomW":307062845,"RandomX":2396116302,"RandomY":2184752848,"RandomZ":1453929005,"StartTime":229097.0,"Objects":[{"StartTime":229097.0,"EndTime":229097.0,"Column":2},{"StartTime":229097.0,"EndTime":229097.0,"Column":3}]},{"RandomW":2488853431,"RandomX":1430246951,"RandomY":1243135735,"RandomZ":862796553,"StartTime":229628.0,"Objects":[{"StartTime":229628.0,"EndTime":229893.0,"Column":0}]},{"RandomW":2954723307,"RandomX":862796553,"RandomY":2488853431,"RandomZ":1065193973,"StartTime":230159.0,"Objects":[{"StartTime":230159.0,"EndTime":230689.0,"Column":2}]},{"RandomW":3118771232,"RandomX":1065193973,"RandomY":2954723307,"RandomZ":3941773202,"StartTime":230955.0,"Objects":[{"StartTime":230955.0,"EndTime":230955.0,"Column":3},{"StartTime":230955.0,"EndTime":230955.0,"Column":2}]},{"RandomW":1630107201,"RandomX":3532926875,"RandomY":2476115689,"RandomZ":1207743047,"StartTime":231221.0,"Objects":[{"StartTime":231221.0,"EndTime":231751.0,"Column":0},{"StartTime":231221.0,"EndTime":231751.0,"Column":4}]},{"RandomW":313681160,"RandomX":2476115689,"RandomY":1207743047,"RandomZ":1630107201,"StartTime":232017.0,"Objects":[{"StartTime":232017.0,"EndTime":232017.0,"Column":2}]},{"RandomW":892602489,"RandomX":1207743047,"RandomY":1630107201,"RandomZ":313681160,"StartTime":232283.0,"Objects":[{"StartTime":232283.0,"EndTime":232283.0,"Column":3},{"StartTime":232283.0,"EndTime":232283.0,"Column":4}]},{"RandomW":2549672466,"RandomX":1630107201,"RandomY":313681160,"RandomZ":892602489,"StartTime":232548.0,"Objects":[{"StartTime":232548.0,"EndTime":232548.0,"Column":1}]},{"RandomW":3175685586,"RandomX":313681160,"RandomY":892602489,"RandomZ":2549672466,"StartTime":232814.0,"Objects":[{"StartTime":232814.0,"EndTime":232814.0,"Column":3},{"StartTime":232814.0,"EndTime":232814.0,"Column":4}]},{"RandomW":1012053334,"RandomX":892602489,"RandomY":2549672466,"RandomZ":3175685586,"StartTime":233079.0,"Objects":[{"StartTime":233079.0,"EndTime":233079.0,"Column":2}]},{"RandomW":2846885221,"RandomX":2549672466,"RandomY":3175685586,"RandomZ":1012053334,"StartTime":233345.0,"Objects":[{"StartTime":233345.0,"EndTime":233345.0,"Column":3},{"StartTime":233345.0,"EndTime":233345.0,"Column":4}]},{"RandomW":2773158813,"RandomX":2846885221,"RandomY":4182295099,"RandomZ":203093837,"StartTime":233876.0,"Objects":[{"StartTime":233876.0,"EndTime":234141.0,"Column":0},{"StartTime":233876.0,"EndTime":234141.0,"Column":1}]},{"RandomW":857734082,"RandomX":203093837,"RandomY":2773158813,"RandomZ":2365172092,"StartTime":234407.0,"Objects":[{"StartTime":234407.0,"EndTime":234937.0,"Column":2}]},{"RandomW":3898917491,"RandomX":2773158813,"RandomY":2365172092,"RandomZ":857734082,"StartTime":235203.0,"Objects":[{"StartTime":235203.0,"EndTime":235203.0,"Column":2}]},{"RandomW":1417532037,"RandomX":857734082,"RandomY":3898917491,"RandomZ":361638657,"StartTime":235469.0,"Objects":[{"StartTime":235469.0,"EndTime":235999.0,"Column":3}]},{"RandomW":2557538851,"RandomX":3898917491,"RandomY":361638657,"RandomZ":1417532037,"StartTime":236265.0,"Objects":[{"StartTime":236265.0,"EndTime":236265.0,"Column":3}]},{"RandomW":846935039,"RandomX":1417532037,"RandomY":2557538851,"RandomZ":1456065540,"StartTime":236531.0,"Objects":[{"StartTime":236531.0,"EndTime":236796.0,"Column":2}]},{"RandomW":2547399683,"RandomX":1456065540,"RandomY":846935039,"RandomZ":2284332751,"StartTime":237062.0,"Objects":[{"StartTime":237062.0,"EndTime":237327.0,"Column":1}]},{"RandomW":2405919505,"RandomX":846935039,"RandomY":2284332751,"RandomZ":2547399683,"StartTime":237593.0,"Objects":[{"StartTime":237593.0,"EndTime":237593.0,"Column":3},{"StartTime":237593.0,"EndTime":237593.0,"Column":4}]},{"RandomW":1684559305,"RandomX":2284332751,"RandomY":2547399683,"RandomZ":2405919505,"StartTime":237858.0,"Objects":[{"StartTime":237858.0,"EndTime":237858.0,"Column":5},{"StartTime":237858.0,"EndTime":237858.0,"Column":0}]},{"RandomW":2914982357,"RandomX":2547399683,"RandomY":2405919505,"RandomZ":1684559305,"StartTime":238124.0,"Objects":[{"StartTime":238124.0,"EndTime":238124.0,"Column":2},{"StartTime":238124.0,"EndTime":238124.0,"Column":3}]},{"RandomW":2343509573,"RandomX":2405919505,"RandomY":1684559305,"RandomZ":2914982357,"StartTime":238389.0,"Objects":[{"StartTime":238389.0,"EndTime":238389.0,"Column":5}]},{"RandomW":1059378114,"RandomX":1684559305,"RandomY":2914982357,"RandomZ":2343509573,"StartTime":238655.0,"Objects":[{"StartTime":238655.0,"EndTime":240778.0,"Column":2}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374.osu b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374.osu new file mode 100644 index 0000000000..50f943b9e6 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374.osu @@ -0,0 +1,449 @@ +osu file format v9 + +[General] +StackLeniency: 0.4 +Mode: 0 + +[Difficulty] +HPDrainRate:5 +CircleSize:4 +OverallDifficulty:5 +ApproachRate:6 +SliderMultiplier:1.7 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +2,98678,112295 +2,185757,200967 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +695,530.973451327434,4,2,1,20,1,0 +33457,-100,4,2,1,25,0,0 +33988,-100,4,2,1,30,0,0 +34386,-100,4,1,0,30,0,0 +38649,-100,4,1,1,30,0,0 +42897,-100,4,1,0,30,0,0 +47144,-100,4,1,1,30,0,0 +51530,-100,4,2,1,20,0,0 +56978,571.428571428571,4,2,1,20,1,0 +58692,845.070422535211,4,2,1,20,1,0 +60248,530.973451327434,4,2,1,20,1,0 +60740,-100,4,1,1,30,0,0 +61555,-66.6666666666667,4,1,1,30,0,0 +62219,-100,4,1,0,40,0,0 +78148,-100,4,1,0,30,0,0 +78413,-100,4,1,0,35,0,0 +78679,-100,4,1,0,40,0,0 +78944,-100,4,1,0,45,0,0 +79210,-100,4,1,0,40,0,0 +96466,-100,4,2,1,30,0,0 +132285,-100,4,2,1,20,0,0 +149453,-100,4,1,1,35,0,0 +153790,-100,4,2,1,40,0,0 +157639,-100,4,1,1,35,0,0 +162020,-100,4,2,1,40,0,0 +166158,-100,4,1,0,40,0,0 +201733,-100,4,2,1,20,0,0 +219099,-133.333333333333,4,2,1,20,0,0 +221024,-100,4,1,1,30,0,0 +221290,-100,4,1,0,30,0,0 + +[HitObjects] +256,192,15562,12,0,17155 +72,120,17686,5,8 +128,224,17951,1,0 +185,119,18217,1,0 +246,220,18482,1,0 +128,224,18748,2,0,B|161:262|208:264,1,85,4|0 +309,213,19279,2,0,B|297:169|325:120,2,85,0|0|8 +309,213,20075,5,0 +309,332,20341,1,0 +206,272,20606,1,8 +309,213,20871,2,0,B|336:117|261:56,1,170,4|0 +205,272,21933,6,0,B|183:307|125:328,1,85,8|0 +149,256,22464,2,0,B|114:281|45:280,1,85,0|0 +101,216,22995,2,0,B|16:264|-56:176|16:72|104:128,1,255,4|0 +149,136,24057,6,0,B|170:100|229:80,1,85,8|0 +205,149,24588,2,0,B|239:123|309:125,1,85,0|8 +253,189,25119,2,0,B|349:144|413:221,1,170,4|8 +240,336,26181,5,8 +288,264,26447,1,0 +344,328,26712,2,0,B|391:339|440:328,1,85,0|0 +488,270,27243,2,0,B|424:256|392:200,1,85,4|0 +329,230,27774,2,0,B|328:176|386:142,1,85,0|0 +363,69,28305,2,0,B|328:40|280:56,2,85,8|0|0 +312,136,29102,1,0 +224,120,29367,2,0,B|192:168|256:240|224:296,1,170,4|8 +96,240,30429,6,0,B|83:195|56:160,1,85,8|0 +96,88,30960,2,0,B|83:132|56:168,1,85,0|0 +59,164,31491,2,0,B|129:182|187:167|254:149|323:168,1,255,4|0 +312,165,32553,6,0,B|302:210|256:237,1,85,8|0 +312,166,33084,2,0,B|321:120|368:94,1,85,8|0 +312,166,33615,2,0,B|318:204|374:193|426:183|450:247,1,170,8|8 +200,232,34677,5,4 +119,169,34942,1,0 +57,248,35208,1,8 +137,311,35473,1,0 +200,232,35739,5,0 +248,302,36004,1,0 +318,254,36270,1,8 +270,183,36535,1,0 +200,232,36801,6,0,B|120:272|120:272|40:224,1,170,0|8 +130,183,37597,1,0 +200,232,37863,2,0,B|280:192|280:192|368:240,1,170,0|8 +167,111,38925,6,0,B|134:71|98:65,1,85,8|0 +167,112,39456,2,0,B|115:116|90:142,1,85,4|0 +167,112,39987,2,0,B|120:192|176:248|240:312|152:368,1,255,8|0 +173,351,41048,6,0,B|142:305|80:288,1,85,8|0 +173,351,41579,2,0,B|194:299|175:238,1,85,4|0 +173,351,42110,2,0,B|237:351|253:303|269:255|341:263,1,170,8|8 +128,144,43172,5,4 +208,176,43438,1,0 +288,144,43703,1,8 +368,176,43969,1,0 +408,272,44234,5,0 +312,312,44500,1,0 +216,272,44765,1,8 +120,312,45031,1,0 +48,240,45296,5,0 +160,272,45562,1,0 +272,240,45827,1,8 +384,280,46093,1,0 +496,240,46358,2,0,B|448:208|448:208|496:176|504:128|442:127,1,170,0|8 +152,128,47420,6,0,B|122:167|120:224,1,85,8|0 +88,128,47951,2,0,B|95:177|133:218,1,85,4|0 +121,204,48482,2,0,B|140:296|264:280|308:368,1,255,8|0 +308,368,49544,6,0,B|293:318|324:264,1,85,8|0 +368,348,50075,2,0,B|322:323|305:263,1,85,4|0 +324,200,50606,2,0,B|274:214|203:224|142:108|131:56|243:32|243:120|211:160|107:136,1,340,8|2 +369,216,52730,5,2 +176,312,53792,2,0,B|166:217|64:144,1,170,0|0 +179,150,54588,1,0 +120,88,54854,2,0,B|107:176|38:232,1,170,2|0 +464,320,55916,6,0,B|392:252|288:280,1,170,0|0 +280,104,56978,6,0,B|312:192|416:208,1,170,2|0 +192,160,58120,2,0,B|182:224|112:240,1,85,2|0 +24,240,58692,6,0,B|72:240|88:272,1,56.6666666666667,6|0 +224,296,59325,2,0,B|240:200|200:120,1,170 +316,136,60513,5,0 +400,156,60778,2,0,B|408:100|364:56,1,85,10|0 +320,16,61309,1,2 +160,112,61840,6,0,B|95:104|28:135,1,127.499996200204,8|0 +160,112,62371,6,0,B|80:168|96:296,1,170,4|8 +176,280,63168,1,0 +224,208,63433,2,0,B|280:288|392:264,1,170,0|8 +456,184,64230,1,0 +328,144,64495,1,8 +416,248,64761,1,0 +408,112,65026,1,8 +336,232,65292,1,0 +388,182,65557,1,8 +256,288,66088,5,8 +256,288,66354,1,0 +256,288,66619,2,0,B|200:360|72:368,1,170,0|8 +44,308,67416,1,0 +87,234,67681,2,0,B|163:279|207:386,1,170,0|8 +256,288,68478,1,0 +400,120,68743,5,8 +328,256,69009,1,0 +400,120,69274,1,8 +264,184,69540,1,0 +400,120,69805,1,8 +400,120,70336,6,0,B|395:173|368:200,1,85,8|0 +213,255,70867,2,0,B|279:198|383:198,1,170,4|8 +329,125,71663,1,0 +248,104,71929,2,0,B|184:168|80:152,1,170,0|8 +200,224,72725,1,0 +272,339,72991,5,8 +151,276,73256,1,0 +267,204,73522,1,8 +204,322,73787,1,0 +287,272,74053,1,8 +287,272,74584,6,0,B|336:256|368:208,1,85,8|0 +372,140,75115,2,0,B|323:206|324:308,1,170,0|8 +240,288,75911,1,0 +160,248,76177,2,0,B|216:176|320:216,1,170,0|8 +272,136,76973,1,0 +200,88,77239,6,0,B|216:136|192:176,1,85,8|0 +160,248,77770,2,0,B|160:296|208:320,1,85,8|0 +328,232,78301,5,0 +233,133,78566,1,8 +297,15,78832,1,8 +432,40,79097,1,8 +453,176,79363,6,0,B|448:240|384:272|328:232,1,170,4|8 +286,306,80159,1,0 +203,288,80424,2,0,B|208:224|272:192|328:232,1,170,0|8 +404,231,81221,1,0 +408,160,81486,5,8 +360,288,81752,1,0 +472,216,82017,1,8 +336,208,82283,1,0 +440,296,82548,1,8 +288,320,83079,5,8 +288,320,83345,1,0 +288,320,83610,2,0,B|200:314|128:248,1,170,0|8 +88,320,84407,1,0 +56,240,84672,2,0,B|133:287|176:392,1,170,0|8 +163,274,85469,1,0 +296,216,85734,5,8 +165,75,86000,1,0 +99,178,86265,1,8 +282,97,86531,1,0 +184,264,86796,1,8 +184,264,87327,6,0,B|159:295|110:299,1,85,8|0 +23,247,87858,2,0,B|91:300|192:261,1,170,4|8 +245,326,88655,1,0 +293,254,88920,2,0,B|213:198|109:246,1,170,0|8 +181,302,89717,1,0 +165,166,89982,5,8 +141,302,90247,1,0 +205,182,90513,1,8 +109,278,90778,1,0 +229,214,91044,1,8 +376,132,91575,6,0,B|424:140|464:100,1,85,8|0 +464,192,92106,2,0,B|456:280|352:320,1,170,0|8 +300,256,92902,1,0 +228,212,93168,2,0,B|268:116|164:60,1,170,0|8 +100,32,93964,1,0 +84,116,94230,2,0,B|116:156|108:212,1,85,8|0 +188,160,94761,2,0,B|188:208|232:244,1,85,8|0 +296,196,95292,2,0,B|320:236|349:239|399:242|379:198|379:198|334:185|358:245|368:276|440:260|480:316|416:356,1,340,8|4 +256,192,96486,12,8,98478 +264,192,113345,5,8 +264,192,113876,1,8 +264,192,114407,5,0 +172,236,114672,1,8 +184,336,114938,1,0 +284,356,115203,1,8 +340,268,115469,1,8 +304,100,116000,1,8 +304,100,116531,1,0 +272,336,117062,5,8 +248,200,117327,1,0 +376,152,117593,1,8 +376,152,118124,1,8 +376,152,118655,5,0 +240,128,118920,1,8 +376,192,119186,1,0 +496,152,119451,1,8 +376,224,119717,1,8 +376,224,120247,1,8 +376,224,120778,1,0 +376,224,121309,5,8 +264,296,121575,1,0 +256,160,121840,1,8 +256,160,122371,1,8 +256,160,122902,1,0 +256,160,123433,5,8 +168,264,123699,1,0 +312,280,123964,1,8 +312,280,124495,1,8 +312,280,125026,1,0 +312,280,125557,5,8 +200,200,125823,1,0 +312,280,126088,1,8 +312,280,126619,1,8 +312,280,127150,5,0 +416,200,127416,1,8 +432,336,127681,1,0 +416,200,127947,1,8 +312,280,128212,1,8 +312,280,128743,1,8 +312,280,129274,5,8 +264,152,129540,1,8 +136,192,129805,1,8 +184,320,130071,1,12 +88,120,132460,6,0,B|127:224|104:304,1,170,2|0 +424,264,133522,2,0,B|384:159|408:80,1,170 +448,168,134318,2,0,B|369:240|297:240,1,170,4|0 +301,158,135115,2,0,B|277:206|309:262,1,85 +395,295,135646,2,0,B|323:263|227:287,1,170,0|2 +176,88,136708,6,0,B|134:57|80:64,1,85 +176,88,137239,2,0,B|221:64|264:64,1,85,8|0 +176,88,137770,2,0,B|137:175|196:220|272:272|208:344,1,255,4|0 +136,328,138832,6,0,B|83:306|40:328,1,85 +136,328,139363,2,0,B|184:312|224:328,1,85,2|0 +300,296,139894,2,0,B|300:198|388:200|468:200|452:104,1,255,4|0 +372,100,140955,1,0 +292,72,141221,6,0,B|250:102|244:152,2,85,0|8|0 +332,148,142017,1,4 +388,212,142283,2,0,B|414:243|465:241,1,85 +440,148,142814,2,0,B|400:172|388:213,1,85 +236,232,143345,1,0 +204,84,143610,1,0 +356,64,143876,1,0 +388,212,144141,2,0,B|350:295|228:308,1,170,4|0 +96,304,145203,6,0,B|96:208,1,85 +144,203,145734,2,0,B|144:288,1,85,8|0 +192,272,146265,2,0,B|192:176|192:176|192:120|256:112,1,170,4|0 +312,56,147062,1,0 +392,120,147327,6,0,B|392:208,1,85 +336,221,147858,2,0,B|336:136,1,85,8|0 +280,152,148389,2,0,B|280:256|280:256|264:272|280:288|280:288|296:304|280:320|280:320|248:336|280:352|280:352|312:368|312:368|280:376|224:384,1,340,4|4 +172,322,149717,5,0 +136,248,149982,1,8 +64,208,150247,1,0 +147,112,150513,5,0 +224,80,150778,1,0 +304,112,151044,1,8 +384,88,151309,1,0 +336,192,151575,6,0,B|280:272|176:264,1,170,0|8 +408,216,152637,2,0,B|429:173|464:152,1,85,0|0 +360,80,153168,2,0,B|376:168|304:264,1,170,8|0 +256,288,153964,5,2 +192,240,154230,1,4 +272,208,154495,1,0 +229,134,154761,2,0,B|276:214,1,85,0|2 +160,248,155292,1,4 +120,136,155557,1,0 +229,134,155823,6,0,B|331:134,1,85,0|2 +408,208,156354,2,0,B|312:208,1,85,4|0 +216,256,156885,2,0,B|272:280|264:352|208:344|192:296|256:272|328:312,1,170,0|4 +456,224,157947,5,0 +400,136,158212,1,0 +456,224,158478,1,8 +392,304,158743,1,0 +456,224,159009,1,0 +288,232,159540,5,8 +200,283,159805,1,0 +176,184,160071,1,0 +176,184,160601,5,8 +278,184,160867,1,0 +176,184,161132,2,0,B|88:184,1,85 +24,88,161663,2,0,B|192:88,1,170,8|0 +280,88,162460,1,2 +240,168,162725,1,4 +360,48,163256,5,0 +280,88,163522,1,2 +240,168,163787,2,0,B|344:168,1,85,4|0 +376,240,164318,2,0,B|320:312,1,85,2|0 +248,304,164849,2,0,B|200:232,1,85,6|0 +288,240,165380,2,0,B|288:136|288:136|286:82|344:72,1,170,6|8 +480,104,166442,6,0,B|416:168|416:296,1,170,4|8 +336,280,167239,1,0 +288,208,167504,2,0,B|232:288|120:264,1,170,0|8 +56,184,168301,1,0 +184,144,168566,1,8 +96,248,168832,1,0 +104,112,169097,1,8 +176,232,169363,1,0 +124,182,169628,1,8 +272,256,170159,5,8 +272,256,170424,1,0 +272,256,170690,2,0,B|310:339|428:329,1,170,0|8 +487,259,171486,1,0 +423,179,171752,2,0,B|340:241|340:329,1,170,0|8 +251,346,172548,1,0 +260,193,172814,5,8 +340,321,173079,1,0 +260,193,173345,1,8 +404,249,173610,1,0 +260,193,173876,1,8 +112,120,174407,6,0,B|117:173|144:200,1,85,8|0 +309,191,174938,2,0,B|225:225|117:191,1,170,0|8 +184,128,175734,1,0 +264,104,176000,2,0,B|328:168|432:152,1,170,0|8 +312,224,176796,1,0 +240,339,177062,5,8 +361,276,177327,1,0 +245,204,177593,1,8 +308,322,177858,1,0 +225,270,178124,1,8 +225,270,178655,6,0,B|176:256|144:208,1,85,8|0 +32,256,179186,2,0,B|120:256|192:312,1,170,0|8 +272,288,179982,1,0 +352,248,180247,2,0,B|296:176|192:216,1,170,0|8 +240,136,181044,1,0 +325,129,181309,6,0,B|322:176|285:217,1,85,8|0 +167,291,181840,2,0,B|170:244|207:203,1,85,8|0 +327,289,182371,2,0,B|280:286|239:249,1,85,8|0 +160,120,182902,2,0,B|216:112|248:152|272:192|336:192,1,170,8|4 +256,192,183699,12,4,185557 +80,104,202017,5,2 +152,219,202283,1,0 +16,224,202548,2,0,B|88:208|158:111,1,170,8|0 +226,87,203345,1,0 +304,120,203610,2,0,B|352:120|400:104,1,85,2|0 +304,120,204141,2,0,B|336:88|344:32,1,85,0|0 +341,45,204672,6,0,B|429:77|450:203,1,170,8|0 +360,184,205469,1,0 +304,120,205734,2,0,B|264:96|240:48,1,85,2|0 +304,120,206265,2,0,B|311:76|344:32,1,85,0|0 +408,88,206796,5,4 +472,168,207062,1,0 +392,224,207327,1,0 +304,280,207593,1,0 +224,208,207858,2,0,B|309:237|393:224,1,170 +472,168,208655,1,0 +408,88,208920,6,0,B|368:166|402:252,1,170,8|0 +504,280,209717,1,0 +403,319,209982,2,0,B|459:276|475:151,1,170,4|0 +408,88,210778,1,0 +384,200,211044,5,2 +240,160,211309,1,0 +264,304,211575,1,0 +296,224,211840,2,0,B|336:137|464:136,1,170,2|0 +296,224,212637,6,0,B|243:220|208:161,1,85,2|0 +163,324,213168,2,0,B|244:308|308:204,1,170,8|0 +296,136,213964,1,0 +264,56,214230,2,0,B|232:96|192:136,1,85,4|0 +208,120,214761,2,0,B|200:72|168:32,1,85 +175,42,215292,2,0,B|155:86|98:112,1,85,2|0 +50,53,215823,2,0,B|98:69|122:109,1,85,0|0 +117,102,216354,1,4 +168,344,216885,6,0,B|167:287|131:246,1,85 +88,160,217416,2,0,B|48:248|96:328,1,170,8|0 +144,264,218212,1,0 +224,296,218478,2,0,B|328:312|368:216,1,170,6|0 +363,110,219274,2,0,B|259:246|139:206|147:94|275:70|355:198|130:268,1,446.249986700714,2|8 +160,112,221663,6,0,B|80:168|96:296,1,170,4|8 +176,280,222460,1,0 +224,208,222725,2,0,B|280:288|392:264,1,170,0|8 +456,184,223522,1,0 +328,144,223787,5,8 +416,248,224053,1,0 +408,112,224318,1,8 +336,232,224584,1,0 +388,182,224849,1,8 +240,256,225380,5,8 +240,256,225646,1,0 +240,256,225911,2,0,B|184:328|76:314,1,170,0|8 +3,315,226708,1,0 +89,315,226973,2,0,B|184:302|240:374,1,170,0|8 +314,332,227770,1,0 +252,194,228035,5,8 +116,130,228301,1,0 +252,194,228566,1,8 +140,298,228832,1,0 +252,194,229097,1,8 +400,120,229628,6,0,B|352:112|288:144,1,85,8|0 +203,191,230159,2,0,B|287:225|395:191,1,170,0|8 +330,124,230955,1,0 +248,104,231221,2,0,B|152:96|80:152,1,170,0|8 +200,224,232017,1,0 +272,339,232283,5,8 +151,276,232548,1,0 +267,204,232814,1,8 +204,322,233079,1,0 +287,270,233345,1,8 +287,270,233876,6,0,B|335:254|367:206,1,85,8|0 +464,288,234407,2,0,B|368:272|304:344,1,170,0|8 +226,317,235203,1,0 +165,256,235469,2,0,B|224:192|336:208,1,170,0|8 +272,136,236265,1,0 +199,63,236531,2,0,B|152:80|120:128,1,85,8|0 +203,184,237062,2,0,B|167:218|165:267,1,85,8|0 +312,264,237593,5,8 +440,264,237858,1,8 +256,144,238124,1,8 +496,144,238389,1,0 +256,192,238655,12,4,240778 diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544-expected-conversion.json new file mode 100644 index 0000000000..2289a7243f --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"RandomW":273523780,"RandomX":842502087,"RandomY":3579807591,"RandomZ":273326509,"StartTime":7693.0,"Objects":[{"StartTime":7693.0,"EndTime":7693.0,"Column":0}]},{"RandomW":2659866685,"RandomX":3579807591,"RandomY":273326509,"RandomZ":273523780,"StartTime":8043.0,"Objects":[{"StartTime":8043.0,"EndTime":8043.0,"Column":1}]},{"RandomW":3083309108,"RandomX":273326509,"RandomY":273523780,"RandomZ":2659866685,"StartTime":8393.0,"Objects":[{"StartTime":8393.0,"EndTime":8393.0,"Column":2}]},{"RandomW":2413296944,"RandomX":2659866685,"RandomY":3083309108,"RandomZ":4072999080,"StartTime":8626.0,"Objects":[{"StartTime":8626.0,"EndTime":8626.0,"Column":2},{"StartTime":8626.0,"EndTime":8626.0,"Column":0}]},{"RandomW":1129322311,"RandomX":3083309108,"RandomY":4072999080,"RandomZ":2413296944,"StartTime":8860.0,"Objects":[{"StartTime":8860.0,"EndTime":8860.0,"Column":2}]},{"RandomW":3365759273,"RandomX":4072999080,"RandomY":2413296944,"RandomZ":1129322311,"StartTime":9326.0,"Objects":[{"StartTime":9326.0,"EndTime":9326.0,"Column":3}]},{"RandomW":315078874,"RandomX":2413296944,"RandomY":1129322311,"RandomZ":3365759273,"StartTime":9560.0,"Objects":[{"StartTime":9560.0,"EndTime":9560.0,"Column":3}]},{"RandomW":583662031,"RandomX":1129322311,"RandomY":3365759273,"RandomZ":315078874,"StartTime":9793.0,"Objects":[{"StartTime":9793.0,"EndTime":9793.0,"Column":3}]},{"RandomW":3789568254,"RandomX":3365759273,"RandomY":315078874,"RandomZ":583662031,"StartTime":10260.0,"Objects":[{"StartTime":10260.0,"EndTime":10260.0,"Column":2}]},{"RandomW":3256340938,"RandomX":315078874,"RandomY":583662031,"RandomZ":3789568254,"StartTime":10493.0,"Objects":[{"StartTime":10493.0,"EndTime":10493.0,"Column":2}]},{"RandomW":2152938451,"RandomX":3789568254,"RandomY":3256340938,"RandomZ":3979976762,"StartTime":10727.0,"Objects":[{"StartTime":10727.0,"EndTime":10727.0,"Column":1},{"StartTime":10727.0,"EndTime":10727.0,"Column":0}]},{"RandomW":1620362479,"RandomX":3256340938,"RandomY":3979976762,"RandomZ":2152938451,"StartTime":11427.0,"Objects":[{"StartTime":11427.0,"EndTime":11427.0,"Column":1}]},{"RandomW":477221046,"RandomX":3979976762,"RandomY":2152938451,"RandomZ":1620362479,"StartTime":11777.0,"Objects":[{"StartTime":11777.0,"EndTime":11777.0,"Column":1}]},{"RandomW":1013554034,"RandomX":2152938451,"RandomY":1620362479,"RandomZ":477221046,"StartTime":12127.0,"Objects":[{"StartTime":12127.0,"EndTime":12127.0,"Column":2}]},{"RandomW":637383311,"RandomX":1620362479,"RandomY":477221046,"RandomZ":1013554034,"StartTime":12360.0,"Objects":[{"StartTime":12360.0,"EndTime":12360.0,"Column":2}]},{"RandomW":3817388387,"RandomX":477221046,"RandomY":1013554034,"RandomZ":637383311,"StartTime":12594.0,"Objects":[{"StartTime":12594.0,"EndTime":12594.0,"Column":3}]},{"RandomW":19695232,"RandomX":637383311,"RandomY":3817388387,"RandomZ":1911435716,"StartTime":13060.0,"Objects":[{"StartTime":13060.0,"EndTime":13060.0,"Column":3},{"StartTime":13060.0,"EndTime":13060.0,"Column":0}]},{"RandomW":3381470688,"RandomX":3817388387,"RandomY":1911435716,"RandomZ":19695232,"StartTime":13294.0,"Objects":[{"StartTime":13294.0,"EndTime":13294.0,"Column":3}]},{"RandomW":1862836779,"RandomX":19695232,"RandomY":3381470688,"RandomZ":1869143571,"StartTime":13527.0,"Objects":[{"StartTime":13527.0,"EndTime":13527.0,"Column":3},{"StartTime":13527.0,"EndTime":13527.0,"Column":5}]},{"RandomW":175452620,"RandomX":3381470688,"RandomY":1869143571,"RandomZ":1862836779,"StartTime":13994.0,"Objects":[{"StartTime":13994.0,"EndTime":13994.0,"Column":4}]},{"RandomW":2859972423,"RandomX":1869143571,"RandomY":1862836779,"RandomZ":175452620,"StartTime":14227.0,"Objects":[{"StartTime":14227.0,"EndTime":14227.0,"Column":4}]},{"RandomW":2210823260,"RandomX":1862836779,"RandomY":175452620,"RandomZ":2859972423,"StartTime":14461.0,"Objects":[{"StartTime":14461.0,"EndTime":14461.0,"Column":5}]},{"RandomW":2851442677,"RandomX":175452620,"RandomY":2859972423,"RandomZ":2210823260,"StartTime":14927.0,"Objects":[{"StartTime":14927.0,"EndTime":16561.0,"Column":1}]},{"RandomW":179122262,"RandomX":2859972423,"RandomY":2210823260,"RandomZ":2851442677,"StartTime":16794.0,"Objects":[{"StartTime":16794.0,"EndTime":18078.0,"Column":0}]},{"RandomW":2917386405,"RandomX":2851442677,"RandomY":179122262,"RandomZ":494367691,"StartTime":18661.0,"Objects":[{"StartTime":18661.0,"EndTime":19127.0,"Column":2}]},{"RandomW":3407923728,"RandomX":494367691,"RandomY":2917386405,"RandomZ":2825679051,"StartTime":19595.0,"Objects":[{"StartTime":19595.0,"EndTime":20061.0,"Column":3}]},{"RandomW":358318928,"RandomX":3407923728,"RandomY":1835995540,"RandomZ":3732560508,"StartTime":20528.0,"Objects":[{"StartTime":20528.0,"EndTime":20994.0,"Column":4},{"StartTime":20528.0,"EndTime":20994.0,"Column":1}]},{"RandomW":3440439960,"RandomX":3732560508,"RandomY":358318928,"RandomZ":3638999969,"StartTime":21462.0,"Objects":[{"StartTime":21462.0,"EndTime":21928.0,"Column":3}]},{"RandomW":3249928444,"RandomX":358318928,"RandomY":3638999969,"RandomZ":3440439960,"StartTime":22395.0,"Objects":[{"StartTime":22395.0,"EndTime":22395.0,"Column":1}]},{"RandomW":3857394572,"RandomX":3440439960,"RandomY":3249928444,"RandomZ":138257049,"StartTime":22628.0,"Objects":[{"StartTime":22628.0,"EndTime":24028.0,"Column":4}]},{"RandomW":2938470811,"RandomX":3249928444,"RandomY":138257049,"RandomZ":3857394572,"StartTime":24262.0,"Objects":[{"StartTime":24262.0,"EndTime":24262.0,"Column":3}]},{"RandomW":3241803419,"RandomX":138257049,"RandomY":3857394572,"RandomZ":2938470811,"StartTime":24495.0,"Objects":[{"StartTime":24495.0,"EndTime":24495.0,"Column":4}]},{"RandomW":620078415,"RandomX":3857394572,"RandomY":2938470811,"RandomZ":3241803419,"StartTime":25195.0,"Objects":[{"StartTime":25195.0,"EndTime":25195.0,"Column":4}]},{"RandomW":2566806806,"RandomX":2938470811,"RandomY":3241803419,"RandomZ":620078415,"StartTime":25429.0,"Objects":[{"StartTime":25429.0,"EndTime":25429.0,"Column":4}]},{"RandomW":458505931,"RandomX":3241803419,"RandomY":620078415,"RandomZ":2566806806,"StartTime":26129.0,"Objects":[{"StartTime":26129.0,"EndTime":26129.0,"Column":3}]},{"RandomW":2629948988,"RandomX":2566806806,"RandomY":458505931,"RandomZ":362272284,"StartTime":26362.0,"Objects":[{"StartTime":26362.0,"EndTime":27762.0,"Column":1}]},{"RandomW":1285940261,"RandomX":362272284,"RandomY":2629948988,"RandomZ":4139597407,"StartTime":27996.0,"Objects":[{"StartTime":27996.0,"EndTime":27996.0,"Column":1},{"StartTime":27996.0,"EndTime":27996.0,"Column":3}]},{"RandomW":3878288539,"RandomX":2629948988,"RandomY":4139597407,"RandomZ":1285940261,"StartTime":28229.0,"Objects":[{"StartTime":28229.0,"EndTime":28229.0,"Column":1}]},{"RandomW":1788551508,"RandomX":1285940261,"RandomY":3878288539,"RandomZ":1976280692,"StartTime":28929.0,"Objects":[{"StartTime":28929.0,"EndTime":28929.0,"Column":1},{"StartTime":28929.0,"EndTime":28929.0,"Column":4}]},{"RandomW":159147246,"RandomX":3878288539,"RandomY":1976280692,"RandomZ":1788551508,"StartTime":29163.0,"Objects":[{"StartTime":29163.0,"EndTime":29163.0,"Column":1}]},{"RandomW":2702806142,"RandomX":1976280692,"RandomY":1788551508,"RandomZ":159147246,"StartTime":29863.0,"Objects":[{"StartTime":29863.0,"EndTime":29863.0,"Column":2}]},{"RandomW":2311677487,"RandomX":1788551508,"RandomY":159147246,"RandomZ":2702806142,"StartTime":30213.0,"Objects":[{"StartTime":30213.0,"EndTime":30213.0,"Column":3}]},{"RandomW":3175953261,"RandomX":2311677487,"RandomY":988506051,"RandomZ":3495571300,"StartTime":30446.0,"Objects":[{"StartTime":30446.0,"EndTime":31146.0,"Column":2}]},{"RandomW":516122535,"RandomX":3495571300,"RandomY":3175953261,"RandomZ":2138555125,"StartTime":31730.0,"Objects":[{"StartTime":31730.0,"EndTime":31730.0,"Column":2},{"StartTime":31730.0,"EndTime":31730.0,"Column":1}]},{"RandomW":534989332,"RandomX":3175953261,"RandomY":2138555125,"RandomZ":516122535,"StartTime":32080.0,"Objects":[{"StartTime":32080.0,"EndTime":32080.0,"Column":2}]},{"RandomW":3420570846,"RandomX":2138555125,"RandomY":516122535,"RandomZ":534989332,"StartTime":32430.0,"Objects":[{"StartTime":32430.0,"EndTime":32430.0,"Column":2}]},{"RandomW":172021565,"RandomX":516122535,"RandomY":534989332,"RandomZ":3420570846,"StartTime":32663.0,"Objects":[{"StartTime":32663.0,"EndTime":32663.0,"Column":2}]},{"RandomW":168636292,"RandomX":3420570846,"RandomY":172021565,"RandomZ":263944077,"StartTime":32780.0,"Objects":[{"StartTime":32780.0,"EndTime":32780.0,"Column":0}]},{"RandomW":3473923375,"RandomX":172021565,"RandomY":263944077,"RandomZ":168636292,"StartTime":33597.0,"Objects":[{"StartTime":33597.0,"EndTime":33597.0,"Column":1}]},{"RandomW":3287941836,"RandomX":263944077,"RandomY":168636292,"RandomZ":3473923375,"StartTime":33947.0,"Objects":[{"StartTime":33947.0,"EndTime":33947.0,"Column":1}]},{"RandomW":1950056015,"RandomX":3473923375,"RandomY":3287941836,"RandomZ":388563489,"StartTime":34180.0,"Objects":[{"StartTime":34180.0,"EndTime":35230.0,"Column":5}]},{"RandomW":3600000321,"RandomX":388563489,"RandomY":1950056015,"RandomZ":3312202562,"StartTime":35464.0,"Objects":[{"StartTime":35464.0,"EndTime":36164.0,"Column":4}]},{"RandomW":647123919,"RandomX":3312202562,"RandomY":3600000321,"RandomZ":2314505656,"StartTime":36397.0,"Objects":[{"StartTime":36397.0,"EndTime":37097.0,"Column":1}]},{"RandomW":3375531720,"RandomX":2314505656,"RandomY":647123919,"RandomZ":2193654396,"StartTime":37564.0,"Objects":[{"StartTime":37564.0,"EndTime":37914.0,"Column":3}]},{"RandomW":2335314869,"RandomX":3834006299,"RandomY":1346269295,"RandomZ":3597388662,"StartTime":38264.0,"Objects":[{"StartTime":38264.0,"EndTime":38264.0,"Column":4},{"StartTime":38380.0,"EndTime":38380.0,"Column":3},{"StartTime":38496.0,"EndTime":38496.0,"Column":4}]},{"RandomW":1564102491,"RandomX":1346269295,"RandomY":3597388662,"RandomZ":2335314869,"StartTime":39197.0,"Objects":[{"StartTime":39197.0,"EndTime":39197.0,"Column":2}]},{"RandomW":1989977426,"RandomX":2335314869,"RandomY":1564102491,"RandomZ":4263834011,"StartTime":39431.0,"Objects":[{"StartTime":39431.0,"EndTime":39431.0,"Column":2},{"StartTime":39431.0,"EndTime":39431.0,"Column":5}]},{"RandomW":3806815718,"RandomX":4263834011,"RandomY":1989977426,"RandomZ":1831387023,"StartTime":39664.0,"Objects":[{"StartTime":39664.0,"EndTime":39664.0,"Column":1},{"StartTime":39664.0,"EndTime":39664.0,"Column":4}]},{"RandomW":999749640,"RandomX":1989977426,"RandomY":1831387023,"RandomZ":3806815718,"StartTime":39898.0,"Objects":[{"StartTime":39898.0,"EndTime":40831.0,"Column":1}]},{"RandomW":2830335005,"RandomX":1831387023,"RandomY":3806815718,"RandomZ":999749640,"StartTime":41298.0,"Objects":[{"StartTime":41298.0,"EndTime":41298.0,"Column":1}]},{"RandomW":2152692291,"RandomX":3806815718,"RandomY":999749640,"RandomZ":2830335005,"StartTime":41648.0,"Objects":[{"StartTime":41648.0,"EndTime":41648.0,"Column":1}]},{"RandomW":1499396089,"RandomX":999749640,"RandomY":2830335005,"RandomZ":2152692291,"StartTime":41998.0,"Objects":[{"StartTime":41998.0,"EndTime":41998.0,"Column":2}]},{"RandomW":3582202466,"RandomX":2830335005,"RandomY":2152692291,"RandomZ":1499396089,"StartTime":42231.0,"Objects":[{"StartTime":42231.0,"EndTime":42231.0,"Column":2}]},{"RandomW":3873754971,"RandomX":2152692291,"RandomY":1499396089,"RandomZ":3582202466,"StartTime":42931.0,"Objects":[{"StartTime":42931.0,"EndTime":42931.0,"Column":4}]},{"RandomW":495070374,"RandomX":1499396089,"RandomY":3582202466,"RandomZ":3873754971,"StartTime":43165.0,"Objects":[{"StartTime":43165.0,"EndTime":43165.0,"Column":4}]},{"RandomW":3016618448,"RandomX":3582202466,"RandomY":3873754971,"RandomZ":495070374,"StartTime":43398.0,"Objects":[{"StartTime":43398.0,"EndTime":43398.0,"Column":4}]},{"RandomW":1177547465,"RandomX":3873754971,"RandomY":495070374,"RandomZ":3016618448,"StartTime":43631.0,"Objects":[{"StartTime":43631.0,"EndTime":43631.0,"Column":3}]},{"RandomW":2255582016,"RandomX":495070374,"RandomY":3016618448,"RandomZ":1177547465,"StartTime":43865.0,"Objects":[{"StartTime":43865.0,"EndTime":43865.0,"Column":3}]},{"RandomW":2325387316,"RandomX":3016618448,"RandomY":1177547465,"RandomZ":2255582016,"StartTime":44098.0,"Objects":[{"StartTime":44098.0,"EndTime":44098.0,"Column":2}]},{"RandomW":1443216326,"RandomX":1177547465,"RandomY":2255582016,"RandomZ":2325387316,"StartTime":44332.0,"Objects":[{"StartTime":44332.0,"EndTime":44332.0,"Column":2}]},{"RandomW":1650665398,"RandomX":2325387316,"RandomY":1443216326,"RandomZ":1871032949,"StartTime":44565.0,"Objects":[{"StartTime":44565.0,"EndTime":44565.0,"Column":1},{"StartTime":44565.0,"EndTime":44565.0,"Column":4}]},{"RandomW":1204166455,"RandomX":1871032949,"RandomY":1650665398,"RandomZ":1013336310,"StartTime":44798.0,"Objects":[{"StartTime":44798.0,"EndTime":45498.0,"Column":3}]},{"RandomW":2125976115,"RandomX":1013336310,"RandomY":1204166455,"RandomZ":93461408,"StartTime":45732.0,"Objects":[{"StartTime":45732.0,"EndTime":46432.0,"Column":5}]},{"RandomW":1391245329,"RandomX":1889010923,"RandomY":131109480,"RandomZ":2450179625,"StartTime":46665.0,"Objects":[{"StartTime":46665.0,"EndTime":47365.0,"Column":0},{"StartTime":46665.0,"EndTime":47365.0,"Column":3}]},{"RandomW":1629740061,"RandomX":2450179625,"RandomY":1391245329,"RandomZ":3806548475,"StartTime":47599.0,"Objects":[{"StartTime":47599.0,"EndTime":47949.0,"Column":4}]},{"RandomW":2462543108,"RandomX":3806548475,"RandomY":1629740061,"RandomZ":2782684574,"StartTime":48532.0,"Objects":[{"StartTime":48532.0,"EndTime":49232.0,"Column":0}]},{"RandomW":1398343675,"RandomX":2462543108,"RandomY":1783863854,"RandomZ":368009293,"StartTime":49466.0,"Objects":[{"StartTime":49466.0,"EndTime":50166.0,"Column":1},{"StartTime":49466.0,"EndTime":50166.0,"Column":3}]},{"RandomW":1655209110,"RandomX":1398343675,"RandomY":4200591321,"RandomZ":204183638,"StartTime":50399.0,"Objects":[{"StartTime":50399.0,"EndTime":51099.0,"Column":0},{"StartTime":50399.0,"EndTime":51099.0,"Column":4}]},{"RandomW":2898792131,"RandomX":1655209110,"RandomY":4183149031,"RandomZ":4235317299,"StartTime":51333.0,"Objects":[{"StartTime":51333.0,"EndTime":52033.0,"Column":5},{"StartTime":51333.0,"EndTime":52033.0,"Column":2}]},{"RandomW":2376440576,"RandomX":4183149031,"RandomY":4235317299,"RandomZ":2898792131,"StartTime":52266.0,"Objects":[{"StartTime":52266.0,"EndTime":52266.0,"Column":0}]},{"RandomW":3672662434,"RandomX":4235317299,"RandomY":2898792131,"RandomZ":2376440576,"StartTime":52499.0,"Objects":[{"StartTime":52499.0,"EndTime":52499.0,"Column":1}]},{"RandomW":1144553308,"RandomX":2376440576,"RandomY":3672662434,"RandomZ":2825568900,"StartTime":52849.0,"Objects":[{"StartTime":52849.0,"EndTime":53199.0,"Column":3}]},{"RandomW":3856961856,"RandomX":3672662434,"RandomY":2825568900,"RandomZ":1144553308,"StartTime":54133.0,"Objects":[{"StartTime":54133.0,"EndTime":54133.0,"Column":3}]},{"RandomW":3856961856,"RandomX":3672662434,"RandomY":2825568900,"RandomZ":1144553308,"StartTime":54366.0,"Objects":[{"StartTime":54366.0,"EndTime":54366.0,"Column":2}]},{"RandomW":3856961856,"RandomX":3672662434,"RandomY":2825568900,"RandomZ":1144553308,"StartTime":54600.0,"Objects":[{"StartTime":54600.0,"EndTime":54600.0,"Column":3}]},{"RandomW":2182646490,"RandomX":1144553308,"RandomY":3856961856,"RandomZ":2090342703,"StartTime":55066.0,"Objects":[{"StartTime":55066.0,"EndTime":55066.0,"Column":2},{"StartTime":55066.0,"EndTime":55066.0,"Column":0}]},{"RandomW":2182646490,"RandomX":1144553308,"RandomY":3856961856,"RandomZ":2090342703,"StartTime":55300.0,"Objects":[{"StartTime":55300.0,"EndTime":55300.0,"Column":5},{"StartTime":55300.0,"EndTime":55300.0,"Column":3}]},{"RandomW":2182646490,"RandomX":1144553308,"RandomY":3856961856,"RandomZ":2090342703,"StartTime":55533.0,"Objects":[{"StartTime":55533.0,"EndTime":55533.0,"Column":2},{"StartTime":55533.0,"EndTime":55533.0,"Column":0}]},{"RandomW":3304208416,"RandomX":2090342703,"RandomY":2182646490,"RandomZ":90031962,"StartTime":56000.0,"Objects":[{"StartTime":56000.0,"EndTime":56233.0,"Column":3}]},{"RandomW":1041697651,"RandomX":90031962,"RandomY":3304208416,"RandomZ":2015301872,"StartTime":56583.0,"Objects":[{"StartTime":56583.0,"EndTime":56583.0,"Column":1},{"StartTime":56583.0,"EndTime":56583.0,"Column":2}]},{"RandomW":3818981880,"RandomX":15037736,"RandomY":2251270868,"RandomZ":2287819377,"StartTime":56700.0,"Objects":[{"StartTime":56700.0,"EndTime":56700.0,"Column":0},{"StartTime":56700.0,"EndTime":56700.0,"Column":4}]},{"RandomW":3368447121,"RandomX":2251270868,"RandomY":2287819377,"RandomZ":3818981880,"StartTime":56933.0,"Objects":[{"StartTime":56933.0,"EndTime":56933.0,"Column":1}]},{"RandomW":860096087,"RandomX":2287819377,"RandomY":3818981880,"RandomZ":3368447121,"StartTime":57867.0,"Objects":[{"StartTime":57867.0,"EndTime":57867.0,"Column":3}]},{"RandomW":860096087,"RandomX":2287819377,"RandomY":3818981880,"RandomZ":3368447121,"StartTime":58100.0,"Objects":[{"StartTime":58100.0,"EndTime":58100.0,"Column":2}]},{"RandomW":860096087,"RandomX":2287819377,"RandomY":3818981880,"RandomZ":3368447121,"StartTime":58334.0,"Objects":[{"StartTime":58334.0,"EndTime":58334.0,"Column":3}]},{"RandomW":1369988252,"RandomX":3818981880,"RandomY":3368447121,"RandomZ":860096087,"StartTime":58800.0,"Objects":[{"StartTime":58800.0,"EndTime":58800.0,"Column":4}]},{"RandomW":1369988252,"RandomX":3818981880,"RandomY":3368447121,"RandomZ":860096087,"StartTime":59034.0,"Objects":[{"StartTime":59034.0,"EndTime":59034.0,"Column":1}]},{"RandomW":1369988252,"RandomX":3818981880,"RandomY":3368447121,"RandomZ":860096087,"StartTime":59267.0,"Objects":[{"StartTime":59267.0,"EndTime":59267.0,"Column":4}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544.osu b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544.osu new file mode 100644 index 0000000000..237a13ecd2 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544.osu @@ -0,0 +1,126 @@ +osu file format v5 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:2 +CircleSize:5 +OverallDifficulty:2 +SliderMultiplier:1 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Failing) +//Storyboard Layer 2 (Passing) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +7460,466.735154027506,4,1,0,100 + +[HitObjects] +80,56,7693,1,0 +120,96,8043,1,0 +176,104,8393,1,0 +216,104,8626,1,0 +256,104,8860,1,0 +296,168,9326,5,0 +296,208,9560,1,0 +296,248,9793,1,0 +216,256,10260,1,0 +176,256,10493,1,0 +136,256,10727,1,0 +136,136,11427,5,0 +136,72,11777,1,0 +192,72,12127,1,0 +232,72,12360,1,0 +272,72,12594,1,0 +280,152,13060,5,0 +280,192,13294,1,0 +280,232,13527,1,0 +360,240,13994,1,0 +400,240,14227,1,0 +440,240,14461,1,0 +256,192,14927,12,0,16561 +256,192,16794,12,0,18078 +192,96,18661,6,0,B|312:96,1,100 +288,176,19595,2,0,B|168:176,1,100 +192,256,20528,2,0,B|312:256,1,100 +304,176,21462,2,0,B|240:176|248:88,1,100 +168,104,22395,5,0 +128,104,22628,2,0,B|296:368,1,300 +328,352,24262,5,0 +368,352,24495,1,0 +368,232,25195,1,0 +368,192,25429,1,0 +280,104,26129,5,0 +240,104,26362,2,0,B|40:352,1,300 +88,336,27996,5,0 +128,336,28229,1,0 +136,216,28929,1,0 +136,176,29163,1,0 +256,176,29863,5,0 +312,176,30213,1,0 +352,176,30446,2,0,B|360:264|360:280|360:272|272:272,1,150 +208,232,31730,5,0 +208,168,32080,1,0 +208,104,32430,1,0 +248,104,32663,1,0 +248,104,32780,1,0 +120,160,33597,5,0 +120,216,33947,1,0 +120,256,34180,2,0,B|352:256,1,225 +344,216,35464,6,0,B|200:128,1,150 +176,136,36397,2,0,B|176:288,1,150 +296,288,37564,6,0,B|296:208,1,75 +296,152,38264,2,0,B|296:104,2,25 +248,32,39197,1,0 +208,32,39431,1,0 +168,32,39664,1,0 +168,72,39898,2,0,B|168:136,4,50 +104,128,41298,5,0 +168,136,41648,1,0 +208,184,41998,1,0 +232,216,42231,1,0 +344,248,42931,5,0 +344,208,43165,1,0 +344,168,43398,1,0 +304,168,43631,1,0 +264,168,43865,1,0 +224,168,44098,1,0 +184,168,44332,1,0 +144,168,44565,1,0 +104,176,44798,6,0,B|32:240|160:272,1,150 +192,272,45732,2,0,B|280:272|320:200,1,150 +320,160,46665,2,0,B|248:96|176:136,1,150 +144,144,47599,2,0,B|48:168,1,75 +112,256,48532,6,0,B|256:336,1,150 +280,320,49466,2,0,B|416:240,1,150 +408,200,50399,2,0,B|256:136,1,150 +232,144,51333,2,0,B|80:208,1,150 +56,216,52266,5,0 +96,216,52499,1,0 +152,216,52849,2,0,B|248:216,1,75 +328,88,54133,5,0 +328,88,54366,1,0 +328,88,54600,1,0 +248,88,55066,5,0 +248,88,55300,1,0 +248,88,55533,1,0 +256,168,56000,6,0,B|184:168,1,50 +144,168,56583,1,0 +144,168,56700,1,0 +104,168,56933,1,0 +264,168,57867,5,0 +264,168,58100,1,0 +264,168,58334,1,0 +344,168,58800,5,0 +344,168,59034,1,0 +344,168,59267,1,0 diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic-expected-conversion.json rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic.osu b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/basic.osu similarity index 96% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic.osu rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/basic.osu index 40b4409760..abd2ff2ee6 100644 --- a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/basic.osu +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/basic.osu @@ -1,27 +1,27 @@ -osu file format v14 - -[Difficulty] -HPDrainRate:6 -CircleSize:4 -OverallDifficulty:7 -ApproachRate:8.3 -SliderMultiplier:1.6 -SliderTickRate:1 - -[TimingPoints] -500,500,4,2,1,50,1,0 -13426,-100,4,3,1,45,0,0 -14884,-100,4,2,1,50,0,0 - -[HitObjects] -96,192,500,6,0,L|416:192,2,320 -256,192,3000,12,0,4000,0:0:0:0: -256,192,4500,12,0,5500,0:0:0:0: -256,192,6000,12,0,6500,0:0:0:0: -256,128,7000,6,0,L|352:128,4,80 -32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 -256,192,11500,12,0,12000,0:0:0:0: -512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 -256,256,17000,6,0,L|160:256,4,80 -256,192,18500,12,0,19450,0:0:0:0: -216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 +osu file format v14 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +13426,-100,4,3,1,45,0,0 +14884,-100,4,2,1,50,0,0 + +[HitObjects] +96,192,500,6,0,L|416:192,2,320 +256,192,3000,12,0,4000,0:0:0:0: +256,192,4500,12,0,5500,0:0:0:0: +256,192,6000,12,0,6500,0:0:0:0: +256,128,7000,6,0,L|352:128,4,80 +32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 +256,192,11500,12,0,12000,0:0:0:0: +512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 +256,256,17000,6,0,L|160:256,4,80 +256,192,18500,12,0,19450,0:0:0:0: +216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/convert-samples-expected-conversion.json diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/convert-samples.osu similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/convert-samples.osu rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/convert-samples.osu diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/diffcalc-test.osu similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/diffcalc-test.osu rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/diffcalc-test.osu diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/mania-samples-expected-conversion.json diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/mania-samples.osu similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/mania-samples.osu rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/mania-samples.osu diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/slider-convert-samples-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/slider-convert-samples-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/slider-convert-samples-expected-conversion.json rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/slider-convert-samples-expected-conversion.json diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/slider-convert-samples.osu b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/slider-convert-samples.osu similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/slider-convert-samples.osu rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/slider-convert-samples.osu diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json diff --git a/osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider.osu b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/zero-length-slider.osu similarity index 100% rename from osu.Game.Rulesets.Mania/Resources/Testing/Beatmaps/zero-length-slider.osu rename to osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/zero-length-slider.osu diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index 77f93b4ef9..e04b44311e 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -52,14 +52,18 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// The column. protected int GetColumn(float position, bool allowSpecial = false) { + // Casts to doubles are present here because, although code is originally written as float division, + // the division actually appears to occur on doubles in osu!stable. This is likely a result of + // differences in optimisations between .NET versions due to the presence of the double parameter type of Math.Floor(). + if (allowSpecial && TotalColumns == 8) { const float local_x_divisor = 512f / 7; - return Math.Clamp((int)MathF.Floor(position / local_x_divisor), 0, 6) + 1; + return Math.Clamp((int)Math.Floor((double)position / local_x_divisor), 0, 6) + 1; } float localXDivisor = 512f / TotalColumns; - return Math.Clamp((int)MathF.Floor(position / localXDivisor), 0, TotalColumns - 1); + return Math.Clamp((int)Math.Floor((double)position / localXDivisor), 0, TotalColumns - 1); } /// From 0553de768caaaaa9938a462953cc5e6960e26fcb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2023 15:26:02 +0900 Subject: [PATCH 0429/1118] Enforce namespace body style --- osu.sln.DotSettings | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.sln.DotSettings b/osu.sln.DotSettings index 342bc8aa79..c8c5d6745c 100644 --- a/osu.sln.DotSettings +++ b/osu.sln.DotSettings @@ -15,6 +15,7 @@ HINT HINT WARNING + WARNING WARNING WARNING WARNING From 005fb789945497ea67cd12ef42ba735278fa0af0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 7 Dec 2023 15:38:15 +0900 Subject: [PATCH 0430/1118] Fix last tick handling in osu beatmap conversion tests --- .../OsuBeatmapConversionTest.cs | 19 +- .../OsuDifficultyCalculatorTest.cs | 2 +- .../Beatmaps/1124896-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/1124896.osu | 1122 +++++++++++++++++ .../Beatmaps/basic-expected-conversion.json | 0 .../Resources/Testing/Beatmaps/basic.osu | 54 +- ...ear-perfect-curve-expected-conversion.json | 0 .../Beatmaps/colinear-perfect-curve.osu | 0 .../Testing/Beatmaps/diffcalc-test.osu | 0 ...ti-segment-slider-expected-conversion.json | 0 .../Testing/Beatmaps/multi-segment-slider.osu | 0 .../nan-slider-expected-conversion.json | 0 .../Resources/Testing/Beatmaps/nan-slider.osu | 0 .../old-stacking-expected-conversion.json | 0 .../Testing/Beatmaps/old-stacking.osu | 0 .../repeat-slider-expected-conversion.json | 0 .../Testing/Beatmaps/repeat-slider.osu | 0 ...r-paths-edge-case-expected-conversion.json | 0 .../Beatmaps/slider-paths-edge-case.osu | 0 ...r-ticks-edge-case-expected-conversion.json | 22 +- .../Beatmaps/slider-ticks-edge-case.osu | 0 .../slider-ticks-expected-conversion.json | 0 .../Testing/Beatmaps/slider-ticks.osu | 0 ...ven-repeat-slider-expected-conversion.json | 579 +++++++++ .../Testing/Beatmaps/uneven-repeat-slider.osu | 0 .../Testing/Beatmaps/very-fast-slider.osu | 0 .../Testing/Beatmaps/zero-length-sliders.osu | 0 ...ven-repeat-slider-expected-conversion.json | 348 ----- 28 files changed, 1744 insertions(+), 403 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/1124896-expected-conversion.json create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/1124896.osu rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/basic-expected-conversion.json (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/basic.osu (96%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/colinear-perfect-curve.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/diffcalc-test.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/multi-segment-slider-expected-conversion.json (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/multi-segment-slider.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/nan-slider-expected-conversion.json (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/nan-slider.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/old-stacking.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/repeat-slider.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/slider-paths-edge-case-expected-conversion.json (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/slider-paths-edge-case.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/slider-ticks-edge-case-expected-conversion.json (99%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/slider-ticks-edge-case.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/slider-ticks-expected-conversion.json (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/slider-ticks.osu (100%) create mode 100644 osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/uneven-repeat-slider.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/very-fast-slider.osu (100%) rename {osu.Game.Rulesets.Osu => osu.Game.Rulesets.Osu.Tests}/Resources/Testing/Beatmaps/zero-length-sliders.osu (100%) delete mode 100644 osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 3e0a86d39c..4a217a19ea 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using NUnit.Framework; using osu.Framework.Utils; using osu.Game.Rulesets.Objects; @@ -15,7 +14,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class OsuBeatmapConversionTest : BeatmapConversionTest { - protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; + protected override string ResourceAssembly => "osu.Game.Rulesets.Osu.Tests"; [TestCase("basic")] [TestCase("colinear-perfect-curve")] @@ -27,6 +26,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestCase("old-stacking")] [TestCase("multi-segment-slider")] [TestCase("nan-slider")] + [TestCase("1124896")] public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) @@ -34,21 +34,8 @@ namespace osu.Game.Rulesets.Osu.Tests switch (hitObject) { case Slider slider: - var objects = new List(); - foreach (var nested in slider.NestedHitObjects) - objects.Add(createConvertValue((OsuHitObject)nested, slider)); - - // stable does slider tail leniency by offsetting the last tick 36ms back. - // based on player feedback, we're doing this a little different in lazer, - // and the lazer method does not require offsetting the last tick - // (see `DrawableSliderTail.CheckForResult()`). - // however, in conversion tests, just so the output matches, we're bringing - // the 36ms offset back locally. - // in particular, on some sliders, this may rearrange nested objects, - // so we sort them again by start time to prevent test failures. - foreach (var obj in objects.OrderBy(cv => cv.StartTime)) - yield return obj; + yield return createConvertValue((OsuHitObject)nested, slider); break; diff --git a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs index fa7454b435..e35cf10d95 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuDifficultyCalculatorTest.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Osu.Tests [TestFixture] public class OsuDifficultyCalculatorTest : DifficultyCalculatorTest { - protected override string ResourceAssembly => "osu.Game.Rulesets.Osu"; + protected override string ResourceAssembly => "osu.Game.Rulesets.Osu.Tests"; [TestCase(6.710442985146793d, 239, "diffcalc-test")] [TestCase(1.4386882251130073d, 54, "zero-length-sliders")] diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/1124896-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/1124896-expected-conversion.json new file mode 100644 index 0000000000..68551d5d10 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/1124896-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":633.0,"Objects":[{"StartTime":633.0,"EndTime":633.0,"X":84.5217361,"Y":88.5217361}]},{"StartTime":844.0,"Objects":[{"StartTime":844.0,"EndTime":844.0,"X":88.2608643,"Y":92.2608643}]},{"StartTime":1055.0,"Objects":[{"StartTime":1055.0,"EndTime":1055.0,"X":92.0,"Y":96.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":1230.0,"EndTime":1230.0,"X":76.53984,"Y":161.705658,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":1477.0,"Objects":[{"StartTime":1477.0,"EndTime":1477.0,"X":200.0,"Y":100.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":1652.0,"EndTime":1652.0,"X":184.097,"Y":34.400116,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":1900.0,"Objects":[{"StartTime":1900.0,"EndTime":1900.0,"X":164.0,"Y":228.0}]},{"StartTime":2111.0,"Objects":[{"StartTime":2111.0,"EndTime":2111.0,"X":256.0,"Y":240.0}]},{"StartTime":2322.0,"Objects":[{"StartTime":2322.0,"EndTime":2322.0,"X":340.0,"Y":192.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":2497.0,"EndTime":2497.0,"X":350.197235,"Y":127.18325,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":2745.0,"Objects":[{"StartTime":2745.0,"EndTime":2745.0,"X":440.0,"Y":200.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":2920.0,"EndTime":2920.0,"X":450.363068,"Y":264.618042,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":3167.0,"Objects":[{"StartTime":3167.0,"EndTime":3167.0,"X":324.521729,"Y":308.521729}]},{"StartTime":3378.0,"Objects":[{"StartTime":3378.0,"EndTime":3378.0,"X":328.260864,"Y":312.260864,"StackOffset":{"X":-3.73913574,"Y":-3.73913574}},{"StartTime":3764.0,"EndTime":3764.0,"X":241.358566,"Y":327.7687,"StackOffset":{"X":-3.73913574,"Y":-3.73913574}}]},{"StartTime":4012.0,"Objects":[{"StartTime":4012.0,"EndTime":4012.0,"X":332.0,"Y":316.0}]},{"StartTime":4224.0,"Objects":[{"StartTime":4224.0,"EndTime":4224.0,"X":312.0,"Y":224.0}]},{"StartTime":4435.0,"Objects":[{"StartTime":4435.0,"EndTime":4435.0,"X":284.0,"Y":132.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":4610.0,"EndTime":4610.0,"X":218.719162,"Y":130.832062,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":4857.0,"Objects":[{"StartTime":4857.0,"EndTime":4857.0,"X":400.0,"Y":192.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":5032.0,"EndTime":5032.0,"X":465.280823,"Y":193.167923,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":5280.0,"Objects":[{"StartTime":5280.0,"EndTime":5280.0,"X":312.0,"Y":224.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":5455.0,"EndTime":5455.0,"X":310.832062,"Y":289.280823,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":5702.0,"Objects":[{"StartTime":5702.0,"EndTime":5702.0,"X":372.260864,"Y":104.260864}]},{"StartTime":5914.0,"Objects":[{"StartTime":5914.0,"EndTime":5914.0,"X":376.0,"Y":108.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":6300.0,"EndTime":6300.0,"X":249.910217,"Y":112.133125,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":6547.0,"Objects":[{"StartTime":6547.0,"EndTime":6547.0,"X":154.0,"Y":122.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":6722.0,"EndTime":6722.0,"X":171.671921,"Y":58.8828773,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":6970.0,"Objects":[{"StartTime":6970.0,"EndTime":6970.0,"X":107.0,"Y":195.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":7181.0,"EndTime":7181.0,"X":68.5987,"Y":143.051712,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":7356.0,"EndTime":7356.0,"X":107.0,"Y":195.0,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":7604.0,"Objects":[{"StartTime":7604.0,"EndTime":7604.0,"X":216.0,"Y":232.0}]},{"StartTime":7815.0,"Objects":[{"StartTime":7815.0,"EndTime":7815.0,"X":116.0,"Y":280.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":7990.0,"EndTime":7990.0,"X":53.6959572,"Y":265.658173,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":8238.0,"Objects":[{"StartTime":8238.0,"EndTime":8238.0,"X":176.0,"Y":160.0}]},{"StartTime":8449.0,"Objects":[{"StartTime":8449.0,"EndTime":8449.0,"X":248.0,"Y":291.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":8729.0,"EndTime":8729.0,"X":333.029968,"Y":327.610535,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":8871.0,"Objects":[{"StartTime":8871.0,"EndTime":8871.0,"X":334.0,"Y":328.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":9257.0,"EndTime":9257.0,"X":318.562378,"Y":193.885574,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":9505.0,"Objects":[{"StartTime":9505.0,"EndTime":9505.0,"X":428.0,"Y":184.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":9680.0,"EndTime":9680.0,"X":436.122375,"Y":251.009521,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":9928.0,"Objects":[{"StartTime":9928.0,"EndTime":9928.0,"X":328.0,"Y":128.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":10103.0,"EndTime":10103.0,"X":318.879852,"Y":194.881042,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":10350.0,"Objects":[{"StartTime":10350.0,"EndTime":10350.0,"X":320.0,"Y":108.0}]},{"StartTime":10773.0,"Objects":[{"StartTime":10773.0,"EndTime":10773.0,"X":308.0,"Y":88.0}]},{"StartTime":11195.0,"Objects":[{"StartTime":11195.0,"EndTime":11195.0,"X":296.0,"Y":68.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":11370.0,"EndTime":11370.0,"X":228.5764,"Y":64.78935,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":11618.0,"Objects":[{"StartTime":11618.0,"EndTime":11618.0,"X":318.0,"Y":194.0}]},{"StartTime":11829.0,"Objects":[{"StartTime":11829.0,"EndTime":11829.0,"X":288.0,"Y":52.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":12004.0,"EndTime":12004.0,"X":220.5764,"Y":48.7893524,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":12252.0,"Objects":[{"StartTime":12252.0,"EndTime":12252.0,"X":236.0,"Y":248.0}]},{"StartTime":12463.0,"Objects":[{"StartTime":12463.0,"EndTime":12463.0,"X":299.0,"Y":170.0}]},{"StartTime":12674.0,"Objects":[{"StartTime":12674.0,"EndTime":12674.0,"X":300.0,"Y":300.0}]},{"StartTime":12885.0,"Objects":[{"StartTime":12885.0,"EndTime":12885.0,"X":168.0,"Y":204.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":13096.0,"EndTime":13096.0,"X":100.5764,"Y":200.789352,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":13271.0,"EndTime":13271.0,"X":168.0,"Y":204.0,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":13519.0,"Objects":[{"StartTime":13519.0,"EndTime":13519.0,"X":227.0,"Y":332.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":13694.0,"EndTime":13694.0,"X":159.619965,"Y":336.022675,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":13942.0,"Objects":[{"StartTime":13942.0,"EndTime":13942.0,"X":299.260864,"Y":362.260864}]},{"StartTime":14153.0,"Objects":[{"StartTime":14153.0,"EndTime":14153.0,"X":302.0,"Y":365.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":14328.0,"EndTime":14328.0,"X":299.3276,"Y":299.703552,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":14576.0,"Objects":[{"StartTime":14576.0,"EndTime":14576.0,"X":469.0,"Y":258.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":14751.0,"EndTime":14751.0,"X":452.420563,"Y":331.144531,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":14998.0,"Objects":[{"StartTime":14998.0,"EndTime":14998.0,"X":376.0,"Y":256.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":15173.0,"EndTime":15173.0,"X":359.2077,"Y":182.904053,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":15421.0,"Objects":[{"StartTime":15421.0,"EndTime":15421.0,"X":384.0,"Y":80.0}]},{"StartTime":15632.0,"Objects":[{"StartTime":15632.0,"EndTime":15632.0,"X":282.0,"Y":102.0}]},{"StartTime":15843.0,"Objects":[{"StartTime":15843.0,"EndTime":15843.0,"X":436.0,"Y":148.0}]},{"StartTime":16055.0,"Objects":[{"StartTime":16055.0,"EndTime":16055.0,"X":266.521729,"Y":178.521729}]},{"StartTime":16160.0,"Objects":[{"StartTime":16160.0,"EndTime":16160.0,"X":270.260864,"Y":182.260864}]},{"StartTime":16266.0,"Objects":[{"StartTime":16266.0,"EndTime":16266.0,"X":274.0,"Y":186.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":16441.0,"EndTime":16441.0,"X":257.420563,"Y":259.144531,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":16688.0,"Objects":[{"StartTime":16688.0,"EndTime":16688.0,"X":160.0,"Y":202.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":16863.0,"EndTime":16863.0,"X":143.207687,"Y":128.904053,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":17111.0,"Objects":[{"StartTime":17111.0,"EndTime":17111.0,"X":79.0,"Y":35.0}]},{"StartTime":17322.0,"Objects":[{"StartTime":17322.0,"EndTime":17322.0,"X":23.0,"Y":123.0}]},{"StartTime":17533.0,"Objects":[{"StartTime":17533.0,"EndTime":17533.0,"X":161.0,"Y":42.0}]},{"StartTime":17745.0,"Objects":[{"StartTime":17745.0,"EndTime":17745.0,"X":76.0,"Y":188.0}]},{"StartTime":17956.0,"Objects":[{"StartTime":17956.0,"EndTime":17956.0,"X":79.0,"Y":35.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":18131.0,"EndTime":18131.0,"X":99.60409,"Y":107.114296,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":18378.0,"Objects":[{"StartTime":18378.0,"EndTime":18378.0,"X":211.0,"Y":104.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":18553.0,"EndTime":18553.0,"X":231.60408,"Y":176.114288,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":18801.0,"Objects":[{"StartTime":18801.0,"EndTime":18801.0,"X":344.0,"Y":170.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":18976.0,"EndTime":18976.0,"X":364.6041,"Y":242.114288,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":19224.0,"Objects":[{"StartTime":19224.0,"EndTime":19224.0,"X":433.0,"Y":132.0}]},{"StartTime":19435.0,"Objects":[{"StartTime":19435.0,"EndTime":19435.0,"X":364.521729,"Y":241.521729}]},{"StartTime":19540.0,"Objects":[{"StartTime":19540.0,"EndTime":19540.0,"X":368.260864,"Y":245.260864}]},{"StartTime":19646.0,"Objects":[{"StartTime":19646.0,"EndTime":19646.0,"X":372.0,"Y":249.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":19821.0,"EndTime":19821.0,"X":444.6992,"Y":253.148651,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":20069.0,"Objects":[{"StartTime":20069.0,"EndTime":20069.0,"X":468.0,"Y":104.0}]},{"StartTime":20280.0,"Objects":[{"StartTime":20280.0,"EndTime":20280.0,"X":413.0,"Y":180.0}]},{"StartTime":20491.0,"Objects":[{"StartTime":20491.0,"EndTime":20491.0,"X":324.0,"Y":58.0}]},{"StartTime":20702.0,"Objects":[{"StartTime":20702.0,"EndTime":20702.0,"X":414.0,"Y":31.0}]},{"StartTime":20914.0,"Objects":[{"StartTime":20914.0,"EndTime":20914.0,"X":324.0,"Y":151.0}]},{"StartTime":21125.0,"Objects":[{"StartTime":21125.0,"EndTime":21125.0,"X":244.0,"Y":40.0}]},{"StartTime":21336.0,"Objects":[{"StartTime":21336.0,"EndTime":21336.0,"X":301.0,"Y":186.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":21616.0,"EndTime":21616.0,"X":197.183792,"Y":187.195663,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":21759.0,"Objects":[{"StartTime":21759.0,"EndTime":21759.0,"X":197.0,"Y":187.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":21934.0,"EndTime":21934.0,"X":197.444717,"Y":260.028961,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":22181.0,"Objects":[{"StartTime":22181.0,"EndTime":22181.0,"X":287.0,"Y":362.0}]},{"StartTime":22393.0,"Objects":[{"StartTime":22393.0,"EndTime":22393.0,"X":330.0,"Y":234.0}]},{"StartTime":22604.0,"Objects":[{"StartTime":22604.0,"EndTime":22604.0,"X":197.0,"Y":260.0}]},{"StartTime":22815.0,"Objects":[{"StartTime":22815.0,"EndTime":22815.0,"X":356.260864,"Y":315.260864}]},{"StartTime":23026.0,"Objects":[{"StartTime":23026.0,"EndTime":23026.0,"X":360.0,"Y":319.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":23306.0,"EndTime":23306.0,"X":465.503235,"Y":323.503082,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":23449.0,"Objects":[{"StartTime":23449.0,"EndTime":23449.0,"X":468.739136,"Y":326.739136}]},{"StartTime":23660.0,"Objects":[{"StartTime":23660.0,"EndTime":23660.0,"X":398.260864,"Y":176.260864}]},{"StartTime":23871.0,"Objects":[{"StartTime":23871.0,"EndTime":23871.0,"X":402.0,"Y":180.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":24046.0,"EndTime":24046.0,"X":415.0339,"Y":253.858765,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":24294.0,"Objects":[{"StartTime":24294.0,"EndTime":24294.0,"X":314.0,"Y":145.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":24469.0,"EndTime":24469.0,"X":326.976959,"Y":71.13121,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":24716.0,"Objects":[{"StartTime":24716.0,"EndTime":24716.0,"X":472.0,"Y":72.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":24891.0,"EndTime":24891.0,"X":485.1493,"Y":145.838318,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":25139.0,"Objects":[{"StartTime":25139.0,"EndTime":25139.0,"X":320.0,"Y":222.0}]},{"StartTime":25350.0,"Objects":[{"StartTime":25350.0,"EndTime":25350.0,"X":235.0,"Y":116.0}]},{"StartTime":25562.0,"Objects":[{"StartTime":25562.0,"EndTime":25562.0,"X":276.0,"Y":295.0}]},{"StartTime":25667.0,"Objects":[{"StartTime":25667.0,"EndTime":25667.0,"X":304.0,"Y":305.0}]},{"StartTime":25773.0,"Objects":[{"StartTime":25773.0,"EndTime":25773.0,"X":333.0,"Y":306.0}]},{"StartTime":25878.0,"Objects":[{"StartTime":25878.0,"EndTime":25878.0,"X":362.0,"Y":299.0}]},{"StartTime":25984.0,"Objects":[{"StartTime":25984.0,"EndTime":25984.0,"X":392.0,"Y":280.0}]},{"StartTime":26090.0,"Objects":[{"StartTime":26090.0,"EndTime":26090.0,"X":425.0,"Y":239.0}]},{"StartTime":26195.0,"Objects":[{"StartTime":26195.0,"EndTime":26195.0,"X":447.0,"Y":193.0}]},{"StartTime":26301.0,"Objects":[{"StartTime":26301.0,"EndTime":26301.0,"X":454.0,"Y":143.0}]},{"StartTime":26407.0,"Objects":[{"StartTime":26407.0,"EndTime":26407.0,"X":452.0,"Y":88.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":26829.0,"EndTime":26829.0,"X":419.177216,"Y":32.9294777,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":27216.0,"EndTime":27216.0,"X":378.111816,"Y":82.11954,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":27463.0,"Objects":[{"StartTime":27463.0,"EndTime":27463.0,"X":368.0,"Y":160.0}]},{"StartTime":27674.0,"Objects":[{"StartTime":27674.0,"EndTime":27674.0,"X":487.0,"Y":58.0}]},{"StartTime":28097.0,"Objects":[{"StartTime":28097.0,"EndTime":28097.0,"X":300.0,"Y":200.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":28272.0,"EndTime":28272.0,"X":296.528,"Y":128.962769,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":28519.0,"Objects":[{"StartTime":28519.0,"EndTime":28519.0,"X":377.0,"Y":238.0}]},{"StartTime":28731.0,"Objects":[{"StartTime":28731.0,"EndTime":28731.0,"X":222.0,"Y":217.0}]},{"StartTime":28942.0,"Objects":[{"StartTime":28942.0,"EndTime":28942.0,"X":369.0,"Y":92.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":29117.0,"EndTime":29117.0,"X":365.6939,"Y":163.550735,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":29364.0,"Objects":[{"StartTime":29364.0,"EndTime":29364.0,"X":223.0,"Y":136.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":29539.0,"EndTime":29539.0,"X":224.683,"Y":64.56601,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":29787.0,"Objects":[{"StartTime":29787.0,"EndTime":29787.0,"X":251.0,"Y":276.0}]},{"StartTime":29998.0,"Objects":[{"StartTime":29998.0,"EndTime":29998.0,"X":135.0,"Y":240.0}]},{"StartTime":30209.0,"Objects":[{"StartTime":30209.0,"EndTime":30209.0,"X":244.0,"Y":356.0}]},{"StartTime":30421.0,"Objects":[{"StartTime":30421.0,"EndTime":30421.0,"X":137.0,"Y":161.0}]},{"StartTime":30632.0,"Objects":[{"StartTime":30632.0,"EndTime":30632.0,"X":166.0,"Y":327.0}]},{"StartTime":30843.0,"Objects":[{"StartTime":30843.0,"EndTime":30843.0,"X":219.0,"Y":187.0}]},{"StartTime":31055.0,"Objects":[{"StartTime":31055.0,"EndTime":31055.0,"X":68.0,"Y":322.0}]},{"StartTime":31266.0,"Objects":[{"StartTime":31266.0,"EndTime":31266.0,"X":311.0,"Y":192.0}]},{"StartTime":31477.0,"Objects":[{"StartTime":31477.0,"EndTime":31477.0,"X":140.0,"Y":89.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":31652.0,"EndTime":31652.0,"X":136.569946,"Y":160.058075,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":31899.0,"Objects":[{"StartTime":31899.0,"EndTime":31899.0,"X":217.0,"Y":51.0}]},{"StartTime":32111.0,"Objects":[{"StartTime":32111.0,"EndTime":32111.0,"X":62.0,"Y":72.0}]},{"StartTime":32322.0,"Objects":[{"StartTime":32322.0,"EndTime":32322.0,"X":209.0,"Y":197.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":32497.0,"EndTime":32497.0,"X":206.163559,"Y":125.298256,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":32744.0,"Objects":[{"StartTime":32744.0,"EndTime":32744.0,"X":64.0,"Y":168.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":32919.0,"EndTime":32919.0,"X":66.155014,"Y":239.272888,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":33167.0,"Objects":[{"StartTime":33167.0,"EndTime":33167.0,"X":209.0,"Y":197.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":33342.0,"EndTime":33342.0,"X":137.56601,"Y":198.683,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":33589.0,"Objects":[{"StartTime":33589.0,"EndTime":33589.0,"X":136.0,"Y":340.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":33764.0,"EndTime":33764.0,"X":207.453568,"Y":342.3376,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":34012.0,"Objects":[{"StartTime":34012.0,"EndTime":34012.0,"X":285.0,"Y":167.0}]},{"StartTime":34224.0,"Objects":[{"StartTime":34224.0,"EndTime":34224.0,"X":308.0,"Y":326.0}]},{"StartTime":34435.0,"Objects":[{"StartTime":34435.0,"EndTime":34435.0,"X":176.0,"Y":276.0}]},{"StartTime":34646.0,"Objects":[{"StartTime":34646.0,"EndTime":34646.0,"X":362.0,"Y":263.0}]},{"StartTime":34857.0,"Objects":[{"StartTime":34857.0,"EndTime":34857.0,"X":184.0,"Y":201.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":35032.0,"EndTime":35032.0,"X":175.4032,"Y":275.505676,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":35280.0,"Objects":[{"StartTime":35280.0,"EndTime":35280.0,"X":118.0,"Y":138.0}]},{"StartTime":35491.0,"Objects":[{"StartTime":35491.0,"EndTime":35491.0,"X":272.0,"Y":162.0}]},{"StartTime":35702.0,"Objects":[{"StartTime":35702.0,"EndTime":35702.0,"X":120.0,"Y":57.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":35877.0,"EndTime":35877.0,"X":164.450928,"Y":3.121443,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":36125.0,"Objects":[{"StartTime":36125.0,"EndTime":36125.0,"X":294.0,"Y":133.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":36300.0,"EndTime":36300.0,"X":247.996475,"Y":185.8328,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":36547.0,"Objects":[{"StartTime":36547.0,"EndTime":36547.0,"X":243.0,"Y":11.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":36722.0,"EndTime":36722.0,"X":296.045258,"Y":56.4152451,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":36970.0,"Objects":[{"StartTime":36970.0,"EndTime":36970.0,"X":171.0,"Y":183.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":37145.0,"EndTime":37145.0,"X":117.339569,"Y":137.949753,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":37393.0,"Objects":[{"StartTime":37393.0,"EndTime":37393.0,"X":368.0,"Y":94.0}]},{"StartTime":37604.0,"Objects":[{"StartTime":37604.0,"EndTime":37604.0,"X":228.0,"Y":243.0}]},{"StartTime":37815.0,"Objects":[{"StartTime":37815.0,"EndTime":37815.0,"X":222.0,"Y":94.0}]},{"StartTime":38026.0,"Objects":[{"StartTime":38026.0,"EndTime":38026.0,"X":374.0,"Y":238.0}]},{"StartTime":38238.0,"Objects":[{"StartTime":38238.0,"EndTime":38238.0,"X":368.0,"Y":94.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":38413.0,"EndTime":38413.0,"X":441.399017,"Y":109.413795,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":38660.0,"Objects":[{"StartTime":38660.0,"EndTime":38660.0,"X":240.0,"Y":170.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":38835.0,"EndTime":38835.0,"X":313.399017,"Y":185.413788,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":39083.0,"Objects":[{"StartTime":39083.0,"EndTime":39083.0,"X":110.0,"Y":240.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":39258.0,"EndTime":39258.0,"X":183.399017,"Y":255.413788,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":39505.0,"Objects":[{"StartTime":39505.0,"EndTime":39505.0,"X":106.0,"Y":321.0}]},{"StartTime":39716.0,"Objects":[{"StartTime":39716.0,"EndTime":39716.0,"X":148.0,"Y":159.0}]},{"StartTime":39928.0,"Objects":[{"StartTime":39928.0,"EndTime":39928.0,"X":35.0,"Y":279.0}]},{"StartTime":40139.0,"Objects":[{"StartTime":40139.0,"EndTime":40139.0,"X":213.0,"Y":325.0}]},{"StartTime":40350.0,"Objects":[{"StartTime":40350.0,"EndTime":40350.0,"X":61.0,"Y":312.0}]},{"StartTime":40561.0,"Objects":[{"StartTime":40561.0,"EndTime":40561.0,"X":237.0,"Y":299.0}]},{"StartTime":40773.0,"Objects":[{"StartTime":40773.0,"EndTime":40773.0,"X":120.0,"Y":92.0}]},{"StartTime":40878.0,"Objects":[{"StartTime":40878.0,"EndTime":40878.0,"X":124.0,"Y":129.0}]},{"StartTime":40984.0,"Objects":[{"StartTime":40984.0,"EndTime":40984.0,"X":128.0,"Y":166.0}]},{"StartTime":41089.0,"Objects":[{"StartTime":41089.0,"EndTime":41089.0,"X":132.0,"Y":203.0}]},{"StartTime":41195.0,"Objects":[{"StartTime":41195.0,"EndTime":41195.0,"X":136.0,"Y":241.0}]},{"StartTime":41407.0,"Objects":[{"StartTime":41407.0,"EndTime":41407.0,"X":273.521729,"Y":106.521736}]},{"StartTime":41512.0,"Objects":[{"StartTime":41512.0,"EndTime":41512.0,"X":277.260864,"Y":110.260864}]},{"StartTime":41618.0,"Objects":[{"StartTime":41618.0,"EndTime":41618.0,"X":281.0,"Y":114.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":41793.0,"EndTime":41793.0,"X":355.8014,"Y":108.545731,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":42040.0,"Objects":[{"StartTime":42040.0,"EndTime":42040.0,"X":292.0,"Y":34.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":42215.0,"EndTime":42215.0,"X":366.8014,"Y":28.54573,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":42463.0,"Objects":[{"StartTime":42463.0,"EndTime":42463.0,"X":400.0,"Y":177.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":42638.0,"EndTime":42638.0,"X":405.454285,"Y":251.8014,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":42885.0,"Objects":[{"StartTime":42885.0,"EndTime":42885.0,"X":480.0,"Y":188.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":43060.0,"EndTime":43060.0,"X":485.454285,"Y":262.8014,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":43308.0,"Objects":[{"StartTime":43308.0,"EndTime":43308.0,"X":330.0,"Y":317.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":43483.0,"EndTime":43483.0,"X":255.1986,"Y":311.545715,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":43730.0,"Objects":[{"StartTime":43730.0,"EndTime":43730.0,"X":319.0,"Y":237.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":43905.0,"EndTime":43905.0,"X":244.1986,"Y":231.545731,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":44153.0,"Objects":[{"StartTime":44153.0,"EndTime":44153.0,"X":129.0,"Y":357.0}]},{"StartTime":44364.0,"Objects":[{"StartTime":44364.0,"EndTime":44364.0,"X":43.0,"Y":239.0}]},{"StartTime":44576.0,"Objects":[{"StartTime":44576.0,"EndTime":44576.0,"X":181.0,"Y":284.0}]},{"StartTime":44787.0,"Objects":[{"StartTime":44787.0,"EndTime":44787.0,"X":43.0,"Y":329.0}]},{"StartTime":44998.0,"Objects":[{"StartTime":44998.0,"EndTime":44998.0,"X":129.0,"Y":211.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":45173.0,"EndTime":45173.0,"X":134.815765,"Y":136.22583,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":45421.0,"Objects":[{"StartTime":45421.0,"EndTime":45421.0,"X":224.0,"Y":157.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":45596.0,"EndTime":45596.0,"X":218.184235,"Y":82.22582,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":45843.0,"Objects":[{"StartTime":45843.0,"EndTime":45843.0,"X":312.0,"Y":60.0}]},{"StartTime":46055.0,"Objects":[{"StartTime":46055.0,"EndTime":46055.0,"X":414.0,"Y":106.0}]},{"StartTime":46266.0,"Objects":[{"StartTime":46266.0,"EndTime":46266.0,"X":401.0,"Y":1.0}]},{"StartTime":46477.0,"Objects":[{"StartTime":46477.0,"EndTime":46477.0,"X":302.521729,"Y":134.521729}]},{"StartTime":46583.0,"Objects":[{"StartTime":46583.0,"EndTime":46583.0,"X":306.260864,"Y":138.260864}]},{"StartTime":46688.0,"Objects":[{"StartTime":46688.0,"EndTime":46688.0,"X":310.0,"Y":142.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":46863.0,"EndTime":46863.0,"X":315.815765,"Y":216.77417,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":47111.0,"Objects":[{"StartTime":47111.0,"EndTime":47111.0,"X":405.0,"Y":196.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":47286.0,"EndTime":47286.0,"X":399.184235,"Y":270.77417,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":47533.0,"Objects":[{"StartTime":47533.0,"EndTime":47533.0,"X":280.0,"Y":288.0}]},{"StartTime":47745.0,"Objects":[{"StartTime":47745.0,"EndTime":47745.0,"X":388.0,"Y":352.0}]},{"StartTime":47956.0,"Objects":[{"StartTime":47956.0,"EndTime":47956.0,"X":492.0,"Y":176.0}]},{"StartTime":48167.0,"Objects":[{"StartTime":48167.0,"EndTime":48167.0,"X":465.0,"Y":312.0}]},{"StartTime":48378.0,"Objects":[{"StartTime":48378.0,"EndTime":48378.0,"X":315.0,"Y":216.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":48553.0,"EndTime":48553.0,"X":243.195923,"Y":215.908646,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":48801.0,"Objects":[{"StartTime":48801.0,"EndTime":48801.0,"X":280.0,"Y":288.0}]},{"StartTime":49012.0,"Objects":[{"StartTime":49012.0,"EndTime":49012.0,"X":392.0,"Y":188.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":49187.0,"EndTime":49187.0,"X":341.5537,"Y":136.966446,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":49435.0,"Objects":[{"StartTime":49435.0,"EndTime":49435.0,"X":472.0,"Y":212.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":49610.0,"EndTime":49610.0,"X":458.927246,"Y":141.03653,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":49857.0,"Objects":[{"StartTime":49857.0,"EndTime":49857.0,"X":399.0,"Y":270.0}]},{"StartTime":50069.0,"Objects":[{"StartTime":50069.0,"EndTime":50069.0,"X":341.0,"Y":136.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":50244.0,"EndTime":50244.0,"X":352.818542,"Y":61.9370422,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":50491.0,"Objects":[{"StartTime":50491.0,"EndTime":50491.0,"X":430.0,"Y":31.0}]},{"StartTime":50702.0,"Objects":[{"StartTime":50702.0,"EndTime":50702.0,"X":274.0,"Y":83.0}]},{"StartTime":50914.0,"Objects":[{"StartTime":50914.0,"EndTime":50914.0,"X":423.0,"Y":111.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":51089.0,"EndTime":51089.0,"X":497.184875,"Y":122.027481,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":51336.0,"Objects":[{"StartTime":51336.0,"EndTime":51336.0,"X":338.0,"Y":215.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":51511.0,"EndTime":51511.0,"X":407.975128,"Y":188.0096,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":51759.0,"Objects":[{"StartTime":51759.0,"EndTime":51759.0,"X":282.0,"Y":268.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":51934.0,"EndTime":51934.0,"X":262.7776,"Y":198.471313,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":52181.0,"Objects":[{"StartTime":52181.0,"EndTime":52181.0,"X":358.0,"Y":289.0}]},{"StartTime":52393.0,"Objects":[{"StartTime":52393.0,"EndTime":52393.0,"X":184.0,"Y":202.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":52568.0,"EndTime":52568.0,"X":218.515137,"Y":138.736755,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":52815.0,"Objects":[{"StartTime":52815.0,"EndTime":52815.0,"X":190.0,"Y":281.0}]},{"StartTime":53026.0,"Objects":[{"StartTime":53026.0,"EndTime":53026.0,"X":119.0,"Y":158.0}]},{"StartTime":53238.0,"Objects":[{"StartTime":53238.0,"EndTime":53238.0,"X":262.0,"Y":200.0}]},{"StartTime":53449.0,"Objects":[{"StartTime":53449.0,"EndTime":53449.0,"X":99.0,"Y":230.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":53624.0,"EndTime":53624.0,"X":118.7338,"Y":157.642715,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":53871.0,"Objects":[{"StartTime":53871.0,"EndTime":53871.0,"X":31.0,"Y":295.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":54046.0,"EndTime":54046.0,"X":11.2661953,"Y":222.642715,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":54294.0,"Objects":[{"StartTime":54294.0,"EndTime":54294.0,"X":131.0,"Y":316.0}]},{"StartTime":54505.0,"Objects":[{"StartTime":54505.0,"EndTime":54505.0,"X":222.0,"Y":242.0}]},{"StartTime":54716.0,"Objects":[{"StartTime":54716.0,"EndTime":54716.0,"X":110.521736,"Y":149.521729}]},{"StartTime":54822.0,"Objects":[{"StartTime":54822.0,"EndTime":54822.0,"X":114.260864,"Y":153.260864}]},{"StartTime":54928.0,"Objects":[{"StartTime":54928.0,"EndTime":54928.0,"X":118.0,"Y":157.0}]},{"StartTime":55139.0,"Objects":[{"StartTime":55139.0,"EndTime":55139.0,"X":226.0,"Y":332.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":55419.0,"EndTime":55419.0,"X":332.02774,"Y":333.580322,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":55562.0,"Objects":[{"StartTime":55562.0,"EndTime":55562.0,"X":332.0,"Y":333.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":55737.0,"EndTime":55737.0,"X":347.450775,"Y":259.608765,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":55984.0,"Objects":[{"StartTime":55984.0,"EndTime":55984.0,"X":289.0,"Y":191.0}]},{"StartTime":56195.0,"Objects":[{"StartTime":56195.0,"EndTime":56195.0,"X":338.0,"Y":116.0}]},{"StartTime":56407.0,"Objects":[{"StartTime":56407.0,"EndTime":56407.0,"X":427.0,"Y":103.0}]},{"StartTime":56618.0,"Objects":[{"StartTime":56618.0,"EndTime":56618.0,"X":502.0,"Y":151.0}]},{"StartTime":56829.0,"Objects":[{"StartTime":56829.0,"EndTime":56829.0,"X":371.0,"Y":38.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":57109.0,"EndTime":57109.0,"X":264.9723,"Y":36.41969,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":57252.0,"Objects":[{"StartTime":57252.0,"EndTime":57252.0,"X":265.0,"Y":37.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":57427.0,"EndTime":57427.0,"X":249.54921,"Y":110.391235,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":57674.0,"Objects":[{"StartTime":57674.0,"EndTime":57674.0,"X":132.0,"Y":25.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":57990.0,"EndTime":57990.0,"X":155.7147,"Y":134.790283,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":58271.0,"EndTime":58271.0,"X":132.0,"Y":25.0,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":58519.0,"Objects":[{"StartTime":58519.0,"EndTime":58519.0,"X":79.0,"Y":150.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":58799.0,"EndTime":58799.0,"X":158.959457,"Y":212.030838,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":58942.0,"Objects":[{"StartTime":58942.0,"EndTime":58942.0,"X":158.0,"Y":212.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":59117.0,"EndTime":59117.0,"X":231.232117,"Y":195.811844,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":59364.0,"Objects":[{"StartTime":59364.0,"EndTime":59364.0,"X":249.0,"Y":110.0}]},{"StartTime":59575.0,"Objects":[{"StartTime":59575.0,"EndTime":59575.0,"X":324.0,"Y":159.0}]},{"StartTime":59787.0,"Objects":[{"StartTime":59787.0,"EndTime":59787.0,"X":337.0,"Y":248.0}]},{"StartTime":59998.0,"Objects":[{"StartTime":59998.0,"EndTime":59998.0,"X":289.0,"Y":323.0}]},{"StartTime":60209.0,"Objects":[{"StartTime":60209.0,"EndTime":60209.0,"X":406.0,"Y":192.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":60489.0,"EndTime":60489.0,"X":468.030823,"Y":271.959473,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":60632.0,"Objects":[{"StartTime":60632.0,"EndTime":60632.0,"X":469.0,"Y":272.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":60807.0,"EndTime":60807.0,"X":451.908661,"Y":345.0266,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":61055.0,"Objects":[{"StartTime":61055.0,"EndTime":61055.0,"X":337.0,"Y":248.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":61371.0,"EndTime":61371.0,"X":359.946777,"Y":357.953369,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":61652.0,"EndTime":61652.0,"X":337.0,"Y":248.0,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":61900.0,"Objects":[{"StartTime":61900.0,"EndTime":61900.0,"X":232.0,"Y":195.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":62075.0,"EndTime":62075.0,"X":214.908661,"Y":268.0266,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":62322.0,"Objects":[{"StartTime":62322.0,"EndTime":62322.0,"X":129.0,"Y":122.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":62497.0,"EndTime":62497.0,"X":145.792313,"Y":195.095947,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":62745.0,"Objects":[{"StartTime":62745.0,"EndTime":62745.0,"X":177.0,"Y":358.0}]},{"StartTime":62956.0,"Objects":[{"StartTime":62956.0,"EndTime":62956.0,"X":108.0,"Y":282.0}]},{"StartTime":63167.0,"Objects":[{"StartTime":63167.0,"EndTime":63167.0,"X":286.0,"Y":341.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":63342.0,"EndTime":63342.0,"X":359.260956,"Y":357.0572,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":63590.0,"Objects":[{"StartTime":63590.0,"EndTime":63590.0,"X":410.0,"Y":231.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":63765.0,"EndTime":63765.0,"X":336.693939,"Y":246.84996,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":64012.0,"Objects":[{"StartTime":64012.0,"EndTime":64012.0,"X":465.0,"Y":158.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":64187.0,"EndTime":64187.0,"X":391.904053,"Y":141.207687,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":64435.0,"Objects":[{"StartTime":64435.0,"EndTime":64435.0,"X":226.0,"Y":111.0}]},{"StartTime":64646.0,"Objects":[{"StartTime":64646.0,"EndTime":64646.0,"X":320.0,"Y":175.0}]},{"StartTime":64857.0,"Objects":[{"StartTime":64857.0,"EndTime":64857.0,"X":222.0,"Y":34.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":65032.0,"EndTime":65032.0,"X":162.249863,"Y":68.4071,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":65280.0,"Objects":[{"StartTime":65280.0,"EndTime":65280.0,"X":218.0,"Y":189.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":65455.0,"EndTime":65455.0,"X":158.249863,"Y":154.592911,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":65702.0,"Objects":[{"StartTime":65702.0,"EndTime":65702.0,"X":296.0,"Y":70.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":65877.0,"EndTime":65877.0,"X":276.006042,"Y":142.285828,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":66125.0,"Objects":[{"StartTime":66125.0,"EndTime":66125.0,"X":236.0,"Y":337.0}]},{"StartTime":66336.0,"Objects":[{"StartTime":66336.0,"EndTime":66336.0,"X":325.0,"Y":219.0}]},{"StartTime":66547.0,"Objects":[{"StartTime":66547.0,"EndTime":66547.0,"X":152.0,"Y":247.0}]},{"StartTime":66758.0,"Objects":[{"StartTime":66758.0,"EndTime":66758.0,"X":316.0,"Y":312.0}]},{"StartTime":66970.0,"Objects":[{"StartTime":66970.0,"EndTime":66970.0,"X":88.0,"Y":184.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":67145.0,"EndTime":67145.0,"X":28.2498646,"Y":218.4071,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":67392.0,"Objects":[{"StartTime":67392.0,"EndTime":67392.0,"X":172.0,"Y":320.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":67567.0,"EndTime":67567.0,"X":152.006042,"Y":247.714172,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":67815.0,"Objects":[{"StartTime":67815.0,"EndTime":67815.0,"X":194.0,"Y":118.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":67990.0,"EndTime":67990.0,"X":127.445862,"Y":99.08952,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":68238.0,"Objects":[{"StartTime":68238.0,"EndTime":68238.0,"X":297.0,"Y":315.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":68413.0,"EndTime":68413.0,"X":277.006042,"Y":242.714172,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":68660.0,"Objects":[{"StartTime":68660.0,"EndTime":68660.0,"X":300.0,"Y":75.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":68835.0,"EndTime":68835.0,"X":277.048523,"Y":162.0243,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":68977.0,"Objects":[{"StartTime":68977.0,"EndTime":68977.0,"X":337.0,"Y":56.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":69152.0,"EndTime":69152.0,"X":314.048523,"Y":143.0243,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":69294.0,"Objects":[{"StartTime":69294.0,"EndTime":69294.0,"X":374.0,"Y":43.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":69363.0,"EndTime":69363.0,"X":353.9267,"Y":115.263847,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":69505.0,"Objects":[{"StartTime":69505.0,"EndTime":69505.0,"X":385.0,"Y":192.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":69680.0,"EndTime":69680.0,"X":470.1033,"Y":203.038986,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":69822.0,"Objects":[{"StartTime":69822.0,"EndTime":69822.0,"X":360.0,"Y":235.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":69997.0,"EndTime":69997.0,"X":444.7288,"Y":245.275024,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":70139.0,"Objects":[{"StartTime":70139.0,"EndTime":70139.0,"X":341.0,"Y":274.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":70208.0,"EndTime":70208.0,"X":412.045074,"Y":278.015778,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":70350.0,"Objects":[{"StartTime":70350.0,"EndTime":70350.0,"X":245.0,"Y":332.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":70525.0,"EndTime":70525.0,"X":238.370941,"Y":249.928986,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":70667.0,"Objects":[{"StartTime":70667.0,"EndTime":70667.0,"X":185.0,"Y":311.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":70842.0,"EndTime":70842.0,"X":238.16449,"Y":248.16507,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":70984.0,"Objects":[{"StartTime":70984.0,"EndTime":70984.0,"X":169.0,"Y":248.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":71053.0,"EndTime":71053.0,"X":237.883636,"Y":247.620834,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":71195.0,"Objects":[{"StartTime":71195.0,"EndTime":71195.0,"X":78.0,"Y":207.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":71370.0,"EndTime":71370.0,"X":63.43404,"Y":122.660629,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":71512.0,"Objects":[{"StartTime":71512.0,"EndTime":71512.0,"X":108.0,"Y":176.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":71687.0,"EndTime":71687.0,"X":93.43404,"Y":91.66063,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":71829.0,"Objects":[{"StartTime":71829.0,"EndTime":71829.0,"X":143.0,"Y":143.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":71898.0,"EndTime":71898.0,"X":131.188721,"Y":73.56615,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":72040.0,"Objects":[{"StartTime":72040.0,"EndTime":72040.0,"X":307.0,"Y":58.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":72215.0,"EndTime":72215.0,"X":225.182,"Y":43.19644,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":72357.0,"Objects":[{"StartTime":72357.0,"EndTime":72357.0,"X":388.0,"Y":72.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":72532.0,"EndTime":72532.0,"X":306.182,"Y":57.1964378,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":72674.0,"Objects":[{"StartTime":72674.0,"EndTime":72674.0,"X":454.0,"Y":91.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":72743.0,"EndTime":72743.0,"X":387.1621,"Y":71.76814,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":72885.0,"Objects":[{"StartTime":72885.0,"EndTime":72885.0,"X":338.0,"Y":180.0}]},{"StartTime":73097.0,"Objects":[{"StartTime":73097.0,"EndTime":73097.0,"X":269.0,"Y":308.0}]},{"StartTime":73202.0,"Objects":[{"StartTime":73202.0,"EndTime":73202.0,"X":304.0,"Y":334.0}]},{"StartTime":73308.0,"Objects":[{"StartTime":73308.0,"EndTime":73308.0,"X":348.0,"Y":344.0}]},{"StartTime":73414.0,"Objects":[{"StartTime":73414.0,"EndTime":73414.0,"X":391.0,"Y":335.0}]},{"StartTime":73519.0,"Objects":[{"StartTime":73519.0,"EndTime":73519.0,"X":428.0,"Y":309.0}]},{"StartTime":73625.0,"Objects":[{"StartTime":73625.0,"EndTime":73625.0,"X":450.0,"Y":271.0}]},{"StartTime":73730.0,"Objects":[{"StartTime":73730.0,"EndTime":73730.0,"X":453.0,"Y":227.0}]},{"StartTime":74576.0,"Objects":[{"StartTime":74576.0,"EndTime":74576.0,"X":453.0,"Y":227.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":74611.0,"EndTime":74611.0,"X":475.4206,"Y":227.605957,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":74646.0,"EndTime":74646.0,"X":453.142365,"Y":227.003845,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":74681.0,"EndTime":74681.0,"X":475.278259,"Y":227.602112,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":74716.0,"EndTime":74716.0,"X":453.2847,"Y":227.00769,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":74752.0,"EndTime":74752.0,"X":475.2071,"Y":227.600189,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":74787.0,"EndTime":74787.0,"X":453.213531,"Y":227.005768,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":74822.0,"EndTime":74822.0,"X":475.349426,"Y":227.604034,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":74857.0,"EndTime":74857.0,"X":453.071167,"Y":227.001923,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":74856.0,"EndTime":74856.0,"X":475.4918,"Y":227.60788,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":74998.0,"Objects":[{"StartTime":74998.0,"EndTime":74998.0,"X":506.0,"Y":152.0}]},{"StartTime":75421.0,"Objects":[{"StartTime":75421.0,"EndTime":75421.0,"X":222.0,"Y":89.0}]},{"StartTime":75632.0,"Objects":[{"StartTime":75632.0,"EndTime":75632.0,"X":194.0,"Y":259.0}]},{"StartTime":75843.0,"Objects":[{"StartTime":75843.0,"EndTime":75843.0,"X":320.0,"Y":218.0}]},{"StartTime":76054.0,"Objects":[{"StartTime":76054.0,"EndTime":76054.0,"X":150.0,"Y":190.0}]},{"StartTime":76266.0,"Objects":[{"StartTime":76266.0,"EndTime":76266.0,"X":339.0,"Y":335.0}]},{"StartTime":76477.0,"Objects":[{"StartTime":76477.0,"EndTime":76477.0,"X":372.0,"Y":130.0}]},{"StartTime":76688.0,"Objects":[{"StartTime":76688.0,"EndTime":76688.0,"X":221.0,"Y":180.0}]},{"StartTime":76899.0,"Objects":[{"StartTime":76899.0,"EndTime":76899.0,"X":425.0,"Y":212.0}]},{"StartTime":77111.0,"Objects":[{"StartTime":77111.0,"EndTime":77111.0,"X":285.0,"Y":121.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":77286.0,"EndTime":77286.0,"X":371.8806,"Y":129.901413,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":77533.0,"Objects":[{"StartTime":77533.0,"EndTime":77533.0,"X":194.0,"Y":259.0}]},{"StartTime":77745.0,"Objects":[{"StartTime":77745.0,"EndTime":77745.0,"X":323.0,"Y":182.0}]},{"StartTime":77956.0,"Objects":[{"StartTime":77956.0,"EndTime":77956.0,"X":244.0,"Y":316.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":78131.0,"EndTime":78131.0,"X":154.157745,"Y":324.1849,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":78378.0,"Objects":[{"StartTime":78378.0,"EndTime":78378.0,"X":245.0,"Y":179.0}]},{"StartTime":78590.0,"Objects":[{"StartTime":78590.0,"EndTime":78590.0,"X":350.0,"Y":277.0}]},{"StartTime":78801.0,"Objects":[{"StartTime":78801.0,"EndTime":78801.0,"X":160.0,"Y":228.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":79081.0,"EndTime":79081.0,"X":163.6551,"Y":81.7956848,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":79224.0,"Objects":[{"StartTime":79224.0,"EndTime":79224.0,"X":194.0,"Y":90.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":79399.0,"EndTime":79399.0,"X":283.264221,"Y":89.8079147,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":79646.0,"Objects":[{"StartTime":79646.0,"EndTime":79646.0,"X":129.0,"Y":0.0}]},{"StartTime":79857.0,"Objects":[{"StartTime":79857.0,"EndTime":79857.0,"X":22.0,"Y":146.0}]},{"StartTime":80069.0,"Objects":[{"StartTime":80069.0,"EndTime":80069.0,"X":194.0,"Y":90.0}]},{"StartTime":80280.0,"Objects":[{"StartTime":80280.0,"EndTime":80280.0,"X":22.0,"Y":33.0}]},{"StartTime":80491.0,"Objects":[{"StartTime":80491.0,"EndTime":80491.0,"X":129.0,"Y":180.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":80666.0,"EndTime":80666.0,"X":219.221863,"Y":178.1168,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":80913.0,"Objects":[{"StartTime":80913.0,"EndTime":80913.0,"X":308.0,"Y":80.0}]},{"StartTime":81125.0,"Objects":[{"StartTime":81125.0,"EndTime":81125.0,"X":280.0,"Y":252.0}]},{"StartTime":81336.0,"Objects":[{"StartTime":81336.0,"EndTime":81336.0,"X":446.0,"Y":206.0}]},{"StartTime":81547.0,"Objects":[{"StartTime":81547.0,"EndTime":81547.0,"X":339.0,"Y":60.0}]},{"StartTime":81759.0,"Objects":[{"StartTime":81759.0,"EndTime":81759.0,"X":511.0,"Y":116.0}]},{"StartTime":81970.0,"Objects":[{"StartTime":81970.0,"EndTime":81970.0,"X":339.0,"Y":173.0}]},{"StartTime":82181.0,"Objects":[{"StartTime":82181.0,"EndTime":82181.0,"X":446.0,"Y":26.0}]},{"StartTime":82393.0,"Objects":[{"StartTime":82393.0,"EndTime":82393.0,"X":280.0,"Y":118.0}]},{"StartTime":82604.0,"Objects":[{"StartTime":82604.0,"EndTime":82604.0,"X":435.0,"Y":118.0}]},{"StartTime":82816.0,"Objects":[{"StartTime":82816.0,"EndTime":82816.0,"X":259.0,"Y":26.0}]},{"StartTime":83026.0,"Objects":[{"StartTime":83026.0,"EndTime":83026.0,"X":339.0,"Y":173.0}]},{"StartTime":83238.0,"Objects":[{"StartTime":83238.0,"EndTime":83238.0,"X":154.0,"Y":128.0}]},{"StartTime":83449.0,"Objects":[{"StartTime":83449.0,"EndTime":83449.0,"X":304.0,"Y":88.0}]},{"StartTime":83661.0,"Objects":[{"StartTime":83661.0,"EndTime":83661.0,"X":157.0,"Y":222.0}]},{"StartTime":83871.0,"Objects":[{"StartTime":83871.0,"EndTime":83871.0,"X":352.0,"Y":280.0}]},{"StartTime":84083.0,"Objects":[{"StartTime":84083.0,"EndTime":84083.0,"X":160.0,"Y":173.0}]},{"StartTime":84294.0,"Objects":[{"StartTime":84294.0,"EndTime":84294.0,"X":339.0,"Y":173.0}]},{"StartTime":84506.0,"Objects":[{"StartTime":84506.0,"EndTime":84506.0,"X":135.0,"Y":280.0}]},{"StartTime":84716.0,"Objects":[{"StartTime":84716.0,"EndTime":84716.0,"X":259.0,"Y":130.0}]},{"StartTime":84928.0,"Objects":[{"StartTime":84928.0,"EndTime":84928.0,"X":65.0,"Y":235.0}]},{"StartTime":85139.0,"Objects":[{"StartTime":85139.0,"EndTime":85139.0,"X":244.0,"Y":235.0}]},{"StartTime":85351.0,"Objects":[{"StartTime":85351.0,"EndTime":85351.0,"X":40.0,"Y":129.0}]},{"StartTime":85562.0,"Objects":[{"StartTime":85562.0,"EndTime":85562.0,"X":300.0,"Y":92.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":85737.0,"EndTime":85737.0,"X":277.179749,"Y":186.7918,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":85984.0,"Objects":[{"StartTime":85984.0,"EndTime":85984.0,"X":192.0,"Y":43.0}]},{"StartTime":86195.0,"Objects":[{"StartTime":86195.0,"EndTime":86195.0,"X":361.0,"Y":34.0}]},{"StartTime":86407.0,"Objects":[{"StartTime":86407.0,"EndTime":86407.0,"X":327.0,"Y":233.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":86582.0,"EndTime":86582.0,"X":232.2082,"Y":210.179749,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":86829.0,"Objects":[{"StartTime":86829.0,"EndTime":86829.0,"X":376.0,"Y":125.0}]},{"StartTime":87040.0,"Objects":[{"StartTime":87040.0,"EndTime":87040.0,"X":385.0,"Y":294.0}]},{"StartTime":87252.0,"Objects":[{"StartTime":87252.0,"EndTime":87252.0,"X":195.0,"Y":265.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":87427.0,"EndTime":87427.0,"X":217.820251,"Y":170.2082,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":87674.0,"Objects":[{"StartTime":87674.0,"EndTime":87674.0,"X":303.0,"Y":314.0}]},{"StartTime":87885.0,"Objects":[{"StartTime":87885.0,"EndTime":87885.0,"X":134.0,"Y":323.0}]},{"StartTime":88097.0,"Objects":[{"StartTime":88097.0,"EndTime":88097.0,"X":177.0,"Y":108.0}]},{"StartTime":88202.0,"Objects":[{"StartTime":88202.0,"EndTime":88202.0,"X":223.0,"Y":95.0}]},{"StartTime":88308.0,"Objects":[{"StartTime":88308.0,"EndTime":88308.0,"X":267.0,"Y":114.0}]},{"StartTime":88413.0,"Objects":[{"StartTime":88413.0,"EndTime":88413.0,"X":291.0,"Y":155.0}]},{"StartTime":88519.0,"Objects":[{"StartTime":88519.0,"EndTime":88519.0,"X":284.0,"Y":203.0}]},{"StartTime":88731.0,"Objects":[{"StartTime":88731.0,"EndTime":88731.0,"X":102.0,"Y":204.0}]},{"StartTime":88942.0,"Objects":[{"StartTime":88942.0,"EndTime":88942.0,"X":224.0,"Y":16.0}]},{"StartTime":89153.0,"Objects":[{"StartTime":89153.0,"EndTime":89153.0,"X":207.0,"Y":200.0}]},{"StartTime":89364.0,"Objects":[{"StartTime":89364.0,"EndTime":89364.0,"X":96.0,"Y":112.0}]},{"StartTime":89575.0,"Objects":[{"StartTime":89575.0,"EndTime":89575.0,"X":113.0,"Y":296.0}]},{"StartTime":89787.0,"Objects":[{"StartTime":89787.0,"EndTime":89787.0,"X":0.0,"Y":152.0}]},{"StartTime":89998.0,"Objects":[{"StartTime":89998.0,"EndTime":89998.0,"X":184.0,"Y":169.0}]},{"StartTime":90209.0,"Objects":[{"StartTime":90209.0,"EndTime":90209.0,"X":16.0,"Y":296.0}]},{"StartTime":90420.0,"Objects":[{"StartTime":90420.0,"EndTime":90420.0,"X":211.0,"Y":242.0}]},{"StartTime":90632.0,"Objects":[{"StartTime":90632.0,"EndTime":90632.0,"X":88.0,"Y":52.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":90807.0,"EndTime":90807.0,"X":78.2983856,"Y":149.016129,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":91055.0,"Objects":[{"StartTime":91055.0,"EndTime":91055.0,"X":231.0,"Y":2.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":91230.0,"EndTime":91230.0,"X":173.4124,"Y":80.6760254,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":91477.0,"Objects":[{"StartTime":91477.0,"EndTime":91477.0,"X":383.0,"Y":22.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":91652.0,"EndTime":91652.0,"X":293.9368,"Y":61.67361,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":91900.0,"Objects":[{"StartTime":91900.0,"EndTime":91900.0,"X":491.0,"Y":110.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":92075.0,"EndTime":92075.0,"X":393.715942,"Y":103.5144,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":92322.0,"Objects":[{"StartTime":92322.0,"EndTime":92322.0,"X":436.0,"Y":284.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":92497.0,"EndTime":92497.0,"X":441.562347,"Y":186.658783,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":92745.0,"Objects":[{"StartTime":92745.0,"EndTime":92745.0,"X":300.260864,"Y":155.260864}]},{"StartTime":92956.0,"Objects":[{"StartTime":92956.0,"EndTime":92956.0,"X":304.0,"Y":159.0}]},{"StartTime":93167.0,"Objects":[{"StartTime":93167.0,"EndTime":93167.0,"X":412.0,"Y":328.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":93342.0,"EndTime":93342.0,"X":417.562347,"Y":230.658783,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":93590.0,"Objects":[{"StartTime":93590.0,"EndTime":93590.0,"X":288.260864,"Y":172.260864}]},{"StartTime":93801.0,"Objects":[{"StartTime":93801.0,"EndTime":93801.0,"X":292.0,"Y":176.0}]},{"StartTime":94012.0,"Objects":[{"StartTime":94012.0,"EndTime":94012.0,"X":392.0,"Y":364.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":94187.0,"EndTime":94187.0,"X":397.562347,"Y":266.658783,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":94435.0,"Objects":[{"StartTime":94435.0,"EndTime":94435.0,"X":276.260864,"Y":192.260864}]},{"StartTime":94646.0,"Objects":[{"StartTime":94646.0,"EndTime":94646.0,"X":280.0,"Y":196.0}]},{"StartTime":94857.0,"Objects":[{"StartTime":94857.0,"EndTime":94857.0,"X":160.0,"Y":155.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":95032.0,"EndTime":95032.0,"X":167.9152,"Y":243.954712,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":95280.0,"Objects":[{"StartTime":95280.0,"EndTime":95280.0,"X":424.0,"Y":112.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":95455.0,"EndTime":95455.0,"X":416.084778,"Y":23.0452919,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":95702.0,"Objects":[{"StartTime":95702.0,"EndTime":95702.0,"X":224.0,"Y":192.0}]},{"StartTime":95913.0,"Objects":[{"StartTime":95913.0,"EndTime":95913.0,"X":421.0,"Y":192.0}]},{"StartTime":96125.0,"Objects":[{"StartTime":96125.0,"EndTime":96125.0,"X":280.0,"Y":56.0}]},{"StartTime":96336.0,"Objects":[{"StartTime":96336.0,"EndTime":96336.0,"X":280.0,"Y":253.0}]},{"StartTime":96547.0,"Objects":[{"StartTime":96547.0,"EndTime":96547.0,"X":431.0,"Y":112.0}]},{"StartTime":96758.0,"Objects":[{"StartTime":96758.0,"EndTime":96758.0,"X":195.0,"Y":112.0}]},{"StartTime":96970.0,"Objects":[{"StartTime":96970.0,"EndTime":96970.0,"X":364.0,"Y":268.0}]},{"StartTime":97181.0,"Objects":[{"StartTime":97181.0,"EndTime":97181.0,"X":364.0,"Y":32.0}]},{"StartTime":97393.0,"Objects":[{"StartTime":97393.0,"EndTime":97393.0,"X":176.0,"Y":264.0}]},{"StartTime":97604.0,"Objects":[{"StartTime":97604.0,"EndTime":97604.0,"X":426.0,"Y":108.0}]},{"StartTime":97815.0,"Objects":[{"StartTime":97815.0,"EndTime":97815.0,"X":200.0,"Y":184.0}]},{"StartTime":98026.0,"Objects":[{"StartTime":98026.0,"EndTime":98026.0,"X":459.0,"Y":264.0}]},{"StartTime":98238.0,"Objects":[{"StartTime":98238.0,"EndTime":98238.0,"X":200.0,"Y":108.0}]},{"StartTime":98449.0,"Objects":[{"StartTime":98449.0,"EndTime":98449.0,"X":426.0,"Y":184.0}]},{"StartTime":98660.0,"Objects":[{"StartTime":98660.0,"EndTime":98660.0,"X":164.0,"Y":32.0}]},{"StartTime":98871.0,"Objects":[{"StartTime":98871.0,"EndTime":98871.0,"X":447.0,"Y":32.0}]},{"StartTime":99083.0,"Objects":[{"StartTime":99083.0,"EndTime":99083.0,"X":312.0,"Y":264.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":99258.0,"EndTime":99258.0,"X":305.2918,"Y":166.731049,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":99505.0,"Objects":[{"StartTime":99505.0,"EndTime":99505.0,"X":412.0,"Y":236.0}]},{"StartTime":99716.0,"Objects":[{"StartTime":99716.0,"EndTime":99716.0,"X":224.0,"Y":224.0}]},{"StartTime":99928.0,"Objects":[{"StartTime":99928.0,"EndTime":99928.0,"X":420.0,"Y":144.0}]},{"StartTime":100139.0,"Objects":[{"StartTime":100139.0,"EndTime":100139.0,"X":408.0,"Y":332.0}]},{"StartTime":100350.0,"Objects":[{"StartTime":100350.0,"EndTime":100350.0,"X":252.0,"Y":136.0}]},{"StartTime":100561.0,"Objects":[{"StartTime":100561.0,"EndTime":100561.0,"X":191.0,"Y":314.0}]},{"StartTime":100773.0,"Objects":[{"StartTime":100773.0,"EndTime":100773.0,"X":412.0,"Y":236.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":100948.0,"EndTime":100948.0,"X":487.0,"Y":236.0,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":101195.0,"Objects":[{"StartTime":101195.0,"EndTime":101195.0,"X":348.0,"Y":288.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":101370.0,"EndTime":101370.0,"X":273.0,"Y":288.0,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":101618.0,"Objects":[{"StartTime":101618.0,"EndTime":101618.0,"X":415.0,"Y":339.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":101898.0,"EndTime":101898.0,"X":411.2817,"Y":235.5634,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":102040.0,"Objects":[{"StartTime":102040.0,"EndTime":102040.0,"X":414.739136,"Y":238.739136}]},{"StartTime":102252.0,"Objects":[{"StartTime":102252.0,"EndTime":102252.0,"X":339.521729,"Y":119.521736}]},{"StartTime":102357.0,"Objects":[{"StartTime":102357.0,"EndTime":102357.0,"X":343.260864,"Y":123.260864}]},{"StartTime":102463.0,"Objects":[{"StartTime":102463.0,"EndTime":102463.0,"X":347.0,"Y":127.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":102638.0,"EndTime":102638.0,"X":432.363373,"Y":134.772491,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":102885.0,"Objects":[{"StartTime":102885.0,"EndTime":102885.0,"X":444.0,"Y":20.0}]},{"StartTime":103097.0,"Objects":[{"StartTime":103097.0,"EndTime":103097.0,"X":280.0,"Y":60.0}]},{"StartTime":103308.0,"Objects":[{"StartTime":103308.0,"EndTime":103308.0,"X":433.0,"Y":135.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":103483.0,"EndTime":103483.0,"X":423.061157,"Y":224.449539,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":103731.0,"Objects":[{"StartTime":103731.0,"EndTime":103731.0,"X":232.0,"Y":120.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":103906.0,"EndTime":103906.0,"X":222.061157,"Y":30.55046,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":104153.0,"Objects":[{"StartTime":104153.0,"EndTime":104153.0,"X":92.0,"Y":254.0}]},{"StartTime":104364.0,"Objects":[{"StartTime":104364.0,"EndTime":104364.0,"X":139.0,"Y":123.0}]},{"StartTime":104575.0,"Objects":[{"StartTime":104575.0,"EndTime":104575.0,"X":0.0,"Y":157.0}]},{"StartTime":104787.0,"Objects":[{"StartTime":104787.0,"EndTime":104787.0,"X":158.0,"Y":201.0}]},{"StartTime":104998.0,"Objects":[{"StartTime":104998.0,"EndTime":104998.0,"X":204.0,"Y":26.0}]},{"StartTime":105209.0,"Objects":[{"StartTime":105209.0,"EndTime":105209.0,"X":34.0,"Y":71.0}]},{"StartTime":105421.0,"Objects":[{"StartTime":105421.0,"EndTime":105421.0,"X":267.0,"Y":106.0}]},{"StartTime":105632.0,"Objects":[{"StartTime":105632.0,"EndTime":105632.0,"X":30.0,"Y":179.0}]},{"StartTime":105843.0,"Objects":[{"StartTime":105843.0,"EndTime":105843.0,"X":163.0,"Y":290.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":106018.0,"EndTime":106018.0,"X":157.2056,"Y":200.186722,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":106266.0,"Objects":[{"StartTime":106266.0,"EndTime":106266.0,"X":273.0,"Y":144.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":106441.0,"EndTime":106441.0,"X":354.2163,"Y":157.9499,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":106688.0,"Objects":[{"StartTime":106688.0,"EndTime":106688.0,"X":512.0,"Y":116.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":106863.0,"EndTime":106863.0,"X":430.2963,"Y":129.688965,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":107111.0,"Objects":[{"StartTime":107111.0,"EndTime":107111.0,"X":384.0,"Y":4.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":107286.0,"EndTime":107286.0,"X":368.694946,"Y":84.79979,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":107533.0,"Objects":[{"StartTime":107533.0,"EndTime":107533.0,"X":396.0,"Y":288.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":107708.0,"EndTime":107708.0,"X":410.385376,"Y":206.609482,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":107956.0,"Objects":[{"StartTime":107956.0,"EndTime":107956.0,"X":408.0,"Y":368.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":108131.0,"EndTime":108131.0,"X":475.4191,"Y":320.030762,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":108378.0,"Objects":[{"StartTime":108378.0,"EndTime":108378.0,"X":332.0,"Y":336.0}]},{"StartTime":108590.0,"Objects":[{"StartTime":108590.0,"EndTime":108590.0,"X":480.0,"Y":244.0}]},{"StartTime":108801.0,"Objects":[{"StartTime":108801.0,"EndTime":108801.0,"X":332.0,"Y":336.0}]},{"StartTime":109013.0,"Objects":[{"StartTime":109013.0,"EndTime":109013.0,"X":372.0,"Y":168.0}]},{"StartTime":109224.0,"Objects":[{"StartTime":109224.0,"EndTime":109224.0,"X":247.0,"Y":313.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":109399.0,"EndTime":109399.0,"X":267.7445,"Y":230.566544,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":109646.0,"Objects":[{"StartTime":109646.0,"EndTime":109646.0,"X":96.0,"Y":136.0}]},{"StartTime":109858.0,"Objects":[{"StartTime":109858.0,"EndTime":109858.0,"X":196.0,"Y":252.0}]},{"StartTime":110069.0,"Objects":[{"StartTime":110069.0,"EndTime":110069.0,"X":260.0,"Y":120.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":110244.0,"EndTime":110244.0,"X":170.550461,"Y":129.938843,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":110491.0,"Objects":[{"StartTime":110491.0,"EndTime":110491.0,"X":28.0,"Y":236.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":110666.0,"EndTime":110666.0,"X":117.449539,"Y":245.938843,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":110914.0,"Objects":[{"StartTime":110914.0,"EndTime":110914.0,"X":86.0,"Y":46.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":111089.0,"EndTime":111089.0,"X":95.05495,"Y":135.543335,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":111337.0,"Objects":[{"StartTime":111337.0,"EndTime":111337.0,"X":186.0,"Y":341.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":111512.0,"EndTime":111512.0,"X":195.938843,"Y":251.550461,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":111759.0,"Objects":[{"StartTime":111759.0,"EndTime":111759.0,"X":216.0,"Y":88.0}]},{"StartTime":111970.0,"Objects":[{"StartTime":111970.0,"EndTime":111970.0,"X":95.0,"Y":135.0}]},{"StartTime":112181.0,"Objects":[{"StartTime":112181.0,"EndTime":112181.0,"X":264.0,"Y":168.0}]},{"StartTime":112393.0,"Objects":[{"StartTime":112393.0,"EndTime":112393.0,"X":191.0,"Y":8.0}]},{"StartTime":112604.0,"Objects":[{"StartTime":112604.0,"EndTime":112604.0,"X":142.0,"Y":221.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":112779.0,"EndTime":112779.0,"X":132.061157,"Y":310.449524,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":113026.0,"Objects":[{"StartTime":113026.0,"EndTime":113026.0,"X":264.0,"Y":168.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":113201.0,"EndTime":113201.0,"X":254.061157,"Y":257.449524,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":113449.0,"Objects":[{"StartTime":113449.0,"EndTime":113449.0,"X":396.0,"Y":112.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":113624.0,"EndTime":113624.0,"X":386.061157,"Y":201.449539,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":113871.0,"Objects":[{"StartTime":113871.0,"EndTime":113871.0,"X":312.0,"Y":104.0}]},{"StartTime":114083.0,"Objects":[{"StartTime":114083.0,"EndTime":114083.0,"X":456.0,"Y":240.0}]},{"StartTime":114294.0,"Objects":[{"StartTime":114294.0,"EndTime":114294.0,"X":442.0,"Y":48.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":114469.0,"EndTime":114469.0,"X":360.0754,"Y":43.94542,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":114716.0,"Objects":[{"StartTime":114716.0,"EndTime":114716.0,"X":303.0,"Y":196.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":114891.0,"EndTime":114891.0,"X":386.2208,"Y":200.863846,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":115139.0,"Objects":[{"StartTime":115139.0,"EndTime":115139.0,"X":208.0,"Y":80.0}]},{"StartTime":115244.0,"Objects":[{"StartTime":115244.0,"EndTime":115244.0,"X":213.0,"Y":124.0}]},{"StartTime":115350.0,"Objects":[{"StartTime":115350.0,"EndTime":115350.0,"X":218.0,"Y":169.0}]},{"StartTime":115455.0,"Objects":[{"StartTime":115455.0,"EndTime":115455.0,"X":224.0,"Y":214.0}]},{"StartTime":115561.0,"Objects":[{"StartTime":115561.0,"EndTime":115561.0,"X":229.0,"Y":258.0}]},{"StartTime":115773.0,"Objects":[{"StartTime":115773.0,"EndTime":115773.0,"X":128.521729,"Y":184.521729}]},{"StartTime":115878.0,"Objects":[{"StartTime":115878.0,"EndTime":115878.0,"X":132.260864,"Y":188.260864}]},{"StartTime":115984.0,"Objects":[{"StartTime":115984.0,"EndTime":115984.0,"X":136.0,"Y":192.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":116159.0,"EndTime":116159.0,"X":61.1985931,"Y":186.545731,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":116407.0,"Objects":[{"StartTime":116407.0,"EndTime":116407.0,"X":60.0,"Y":104.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":116582.0,"EndTime":116582.0,"X":134.853943,"Y":108.678375,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":116829.0,"Objects":[{"StartTime":116829.0,"EndTime":116829.0,"X":202.0,"Y":5.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":117004.0,"EndTime":117004.0,"X":207.454269,"Y":79.80141,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":117251.0,"Objects":[{"StartTime":117251.0,"EndTime":117251.0,"X":288.0,"Y":104.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":117426.0,"EndTime":117426.0,"X":292.988922,"Y":29.1661148,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":117674.0,"Objects":[{"StartTime":117674.0,"EndTime":117674.0,"X":336.0,"Y":184.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":117849.0,"EndTime":117849.0,"X":261.1986,"Y":178.545731,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":118096.0,"Objects":[{"StartTime":118096.0,"EndTime":118096.0,"X":340.0,"Y":264.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":118271.0,"EndTime":118271.0,"X":414.754669,"Y":257.9388,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":118519.0,"Objects":[{"StartTime":118519.0,"EndTime":118519.0,"X":414.0,"Y":112.0}]},{"StartTime":118730.0,"Objects":[{"StartTime":118730.0,"EndTime":118730.0,"X":500.0,"Y":230.0}]},{"StartTime":118942.0,"Objects":[{"StartTime":118942.0,"EndTime":118942.0,"X":362.0,"Y":185.0}]},{"StartTime":119153.0,"Objects":[{"StartTime":119153.0,"EndTime":119153.0,"X":500.0,"Y":140.0}]},{"StartTime":119364.0,"Objects":[{"StartTime":119364.0,"EndTime":119364.0,"X":414.0,"Y":258.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":119539.0,"EndTime":119539.0,"X":339.245331,"Y":264.0612,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":119787.0,"Objects":[{"StartTime":119787.0,"EndTime":119787.0,"X":186.0,"Y":173.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":119962.0,"EndTime":119962.0,"X":260.829376,"Y":178.056046,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":120209.0,"Objects":[{"StartTime":120209.0,"EndTime":120209.0,"X":260.0,"Y":292.0}]},{"StartTime":120421.0,"Objects":[{"StartTime":120421.0,"EndTime":120421.0,"X":169.0,"Y":344.0}]},{"StartTime":120632.0,"Objects":[{"StartTime":120632.0,"EndTime":120632.0,"X":182.0,"Y":239.0}]},{"StartTime":120843.0,"Objects":[{"StartTime":120843.0,"EndTime":120843.0,"X":244.0,"Y":372.0}]},{"StartTime":121054.0,"Objects":[{"StartTime":121054.0,"EndTime":121054.0,"X":104.0,"Y":296.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":121229.0,"EndTime":121229.0,"X":29.2258224,"Y":301.815765,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":121477.0,"Objects":[{"StartTime":121477.0,"EndTime":121477.0,"X":186.0,"Y":173.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":121652.0,"EndTime":121652.0,"X":260.829376,"Y":178.056046,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":121899.0,"Objects":[{"StartTime":121899.0,"EndTime":121899.0,"X":104.0,"Y":208.0}]},{"StartTime":122111.0,"Objects":[{"StartTime":122111.0,"EndTime":122111.0,"X":78.0,"Y":106.0}]},{"StartTime":122322.0,"Objects":[{"StartTime":122322.0,"EndTime":122322.0,"X":104.0,"Y":248.0}]},{"StartTime":122534.0,"Objects":[{"StartTime":122534.0,"EndTime":122534.0,"X":177.0,"Y":144.0}]},{"StartTime":122744.0,"Objects":[{"StartTime":122744.0,"EndTime":122744.0,"X":288.0,"Y":256.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":122919.0,"EndTime":122919.0,"X":216.195923,"Y":256.09137,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":123167.0,"Objects":[{"StartTime":123167.0,"EndTime":123167.0,"X":216.0,"Y":144.0}]},{"StartTime":123378.0,"Objects":[{"StartTime":123378.0,"EndTime":123378.0,"X":367.0,"Y":280.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":123553.0,"EndTime":123553.0,"X":316.5537,"Y":331.033569,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":123801.0,"Objects":[{"StartTime":123801.0,"EndTime":123801.0,"X":450.0,"Y":260.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":123976.0,"EndTime":123976.0,"X":431.362823,"Y":329.464874,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":124223.0,"Objects":[{"StartTime":124223.0,"EndTime":124223.0,"X":277.0,"Y":260.0}]},{"StartTime":124435.0,"Objects":[{"StartTime":124435.0,"EndTime":124435.0,"X":332.0,"Y":128.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":124610.0,"EndTime":124610.0,"X":402.4845,"Y":153.630737,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":124857.0,"Objects":[{"StartTime":124857.0,"EndTime":124857.0,"X":367.0,"Y":280.0}]},{"StartTime":125069.0,"Objects":[{"StartTime":125069.0,"EndTime":125069.0,"X":272.0,"Y":180.0}]},{"StartTime":125280.0,"Objects":[{"StartTime":125280.0,"EndTime":125280.0,"X":470.0,"Y":129.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":125455.0,"EndTime":125455.0,"X":460.233978,"Y":199.678162,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":125702.0,"Objects":[{"StartTime":125702.0,"EndTime":125702.0,"X":356.0,"Y":52.0}]},{"StartTime":125914.0,"Objects":[{"StartTime":125914.0,"EndTime":125914.0,"X":402.0,"Y":153.0}]},{"StartTime":126125.0,"Objects":[{"StartTime":126125.0,"EndTime":126125.0,"X":232.0,"Y":72.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":126300.0,"EndTime":126300.0,"X":212.777573,"Y":141.528687,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":126547.0,"Objects":[{"StartTime":126547.0,"EndTime":126547.0,"X":288.0,"Y":124.0}]},{"StartTime":126759.0,"Objects":[{"StartTime":126759.0,"EndTime":126759.0,"X":134.0,"Y":138.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":126934.0,"EndTime":126934.0,"X":168.515137,"Y":201.263245,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":127181.0,"Objects":[{"StartTime":127181.0,"EndTime":127181.0,"X":335.0,"Y":212.0}]},{"StartTime":127393.0,"Objects":[{"StartTime":127393.0,"EndTime":127393.0,"X":212.0,"Y":141.0}]},{"StartTime":127604.0,"Objects":[{"StartTime":127604.0,"EndTime":127604.0,"X":254.0,"Y":284.0}]},{"StartTime":127815.0,"Objects":[{"StartTime":127815.0,"EndTime":127815.0,"X":286.0,"Y":130.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":127990.0,"EndTime":127990.0,"X":211.678345,"Y":140.064392,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":128237.0,"Objects":[{"StartTime":128237.0,"EndTime":128237.0,"X":384.0,"Y":51.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":128412.0,"EndTime":128412.0,"X":311.6427,"Y":31.2661953,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":128660.0,"Objects":[{"StartTime":128660.0,"EndTime":128660.0,"X":480.0,"Y":108.0}]},{"StartTime":128871.0,"Objects":[{"StartTime":128871.0,"EndTime":128871.0,"X":396.0,"Y":232.0}]},{"StartTime":129082.0,"Objects":[{"StartTime":129082.0,"EndTime":129082.0,"X":233.521729,"Y":217.521729}]},{"StartTime":129188.0,"Objects":[{"StartTime":129188.0,"EndTime":129188.0,"X":237.260864,"Y":221.260864}]},{"StartTime":129294.0,"Objects":[{"StartTime":129294.0,"EndTime":129294.0,"X":241.0,"Y":225.0}]},{"StartTime":129505.0,"Objects":[{"StartTime":129505.0,"EndTime":129505.0,"X":295.0,"Y":288.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":129785.0,"EndTime":129785.0,"X":191.701752,"Y":291.7883,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":129928.0,"Objects":[{"StartTime":129928.0,"EndTime":129928.0,"X":192.0,"Y":292.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":130103.0,"EndTime":130103.0,"X":175.94281,"Y":365.260956,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":130350.0,"Objects":[{"StartTime":130350.0,"EndTime":130350.0,"X":148.0,"Y":220.0}]},{"StartTime":130561.0,"Objects":[{"StartTime":130561.0,"EndTime":130561.0,"X":68.0,"Y":187.0}]},{"StartTime":130772.0,"Objects":[{"StartTime":130772.0,"EndTime":130772.0,"X":36.0,"Y":267.0}]},{"StartTime":130983.0,"Objects":[{"StartTime":130983.0,"EndTime":130983.0,"X":115.0,"Y":300.0}]},{"StartTime":131195.0,"Objects":[{"StartTime":131195.0,"EndTime":131195.0,"X":16.0,"Y":127.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":131475.0,"EndTime":131475.0,"X":119.044754,"Y":123.706215,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":131618.0,"Objects":[{"StartTime":131618.0,"EndTime":131618.0,"X":119.0,"Y":124.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":131793.0,"EndTime":131793.0,"X":192.260956,"Y":107.94281,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":132040.0,"Objects":[{"StartTime":132040.0,"EndTime":132040.0,"X":280.0,"Y":44.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":132356.0,"EndTime":132356.0,"X":170.209717,"Y":20.2853,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":132637.0,"EndTime":132637.0,"X":280.0,"Y":44.0,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":132885.0,"Objects":[{"StartTime":132885.0,"EndTime":132885.0,"X":96.0,"Y":56.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":133165.0,"EndTime":133165.0,"X":90.74685,"Y":156.698685,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":133308.0,"Objects":[{"StartTime":133308.0,"EndTime":133308.0,"X":91.0,"Y":157.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":133483.0,"EndTime":133483.0,"X":164.045471,"Y":139.98941,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":133731.0,"Objects":[{"StartTime":133731.0,"EndTime":133731.0,"X":44.0,"Y":216.0}]},{"StartTime":133942.0,"Objects":[{"StartTime":133942.0,"EndTime":133942.0,"X":123.0,"Y":249.0}]},{"StartTime":134153.0,"Objects":[{"StartTime":134153.0,"EndTime":134153.0,"X":91.0,"Y":329.0}]},{"StartTime":134364.0,"Objects":[{"StartTime":134364.0,"EndTime":134364.0,"X":11.0,"Y":296.0}]},{"StartTime":134576.0,"Objects":[{"StartTime":134576.0,"EndTime":134576.0,"X":200.0,"Y":268.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":134856.0,"EndTime":134856.0,"X":304.8808,"Y":260.356873,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":134998.0,"Objects":[{"StartTime":134998.0,"EndTime":134998.0,"X":304.0,"Y":260.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":135173.0,"EndTime":135173.0,"X":286.908661,"Y":333.0266,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":135421.0,"Objects":[{"StartTime":135421.0,"EndTime":135421.0,"X":436.0,"Y":348.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":135737.0,"EndTime":135737.0,"X":413.2101,"Y":238.014038,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":136018.0,"EndTime":136018.0,"X":436.0,"Y":348.0,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":136266.0,"Objects":[{"StartTime":136266.0,"EndTime":136266.0,"X":448.0,"Y":168.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":136441.0,"EndTime":136441.0,"X":377.865,"Y":166.693008,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":136688.0,"Objects":[{"StartTime":136688.0,"EndTime":136688.0,"X":232.0,"Y":260.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":136863.0,"EndTime":136863.0,"X":302.135,"Y":261.306976,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":137111.0,"Objects":[{"StartTime":137111.0,"EndTime":137111.0,"X":340.0,"Y":100.0}]},{"StartTime":137322.0,"Objects":[{"StartTime":137322.0,"EndTime":137322.0,"X":268.0,"Y":196.0}]},{"StartTime":137533.0,"Objects":[{"StartTime":137533.0,"EndTime":137533.0,"X":240.0,"Y":48.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":137708.0,"EndTime":137708.0,"X":250.133484,"Y":122.312263,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":137956.0,"Objects":[{"StartTime":137956.0,"EndTime":137956.0,"X":92.0,"Y":44.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":138131.0,"EndTime":138131.0,"X":163.568558,"Y":39.28212,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":138378.0,"Objects":[{"StartTime":138378.0,"EndTime":138378.0,"X":168.0,"Y":180.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":138553.0,"EndTime":138553.0,"X":98.2096,"Y":180.324524,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":138801.0,"Objects":[{"StartTime":138801.0,"EndTime":138801.0,"X":12.0,"Y":56.0}]},{"StartTime":139012.0,"Objects":[{"StartTime":139012.0,"EndTime":139012.0,"X":132.0,"Y":112.0}]},{"StartTime":139223.0,"Objects":[{"StartTime":139223.0,"EndTime":139223.0,"X":44.0,"Y":236.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":139398.0,"EndTime":139398.0,"X":19.9848156,"Y":171.056885,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":139646.0,"Objects":[{"StartTime":139646.0,"EndTime":139646.0,"X":244.0,"Y":172.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":139821.0,"EndTime":139821.0,"X":219.45665,"Y":236.357651,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":140069.0,"Objects":[{"StartTime":140069.0,"EndTime":140069.0,"X":216.0,"Y":104.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":140244.0,"EndTime":140244.0,"X":238.580536,"Y":39.2729034,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":140491.0,"Objects":[{"StartTime":140491.0,"EndTime":140491.0,"X":436.0,"Y":68.0}]},{"StartTime":140702.0,"Objects":[{"StartTime":140702.0,"EndTime":140702.0,"X":289.0,"Y":88.0}]},{"StartTime":140913.0,"Objects":[{"StartTime":140913.0,"EndTime":140913.0,"X":459.0,"Y":156.0}]},{"StartTime":141124.0,"Objects":[{"StartTime":141124.0,"EndTime":141124.0,"X":317.0,"Y":50.0}]},{"StartTime":141336.0,"Objects":[{"StartTime":141336.0,"EndTime":141336.0,"X":336.0,"Y":232.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":141511.0,"EndTime":141511.0,"X":325.956146,"Y":306.324432,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":141759.0,"Objects":[{"StartTime":141759.0,"EndTime":141759.0,"X":468.0,"Y":230.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":141934.0,"EndTime":141934.0,"X":458.0877,"Y":155.6579,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":142181.0,"Objects":[{"StartTime":142181.0,"EndTime":142181.0,"X":436.0,"Y":324.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":142356.0,"EndTime":142356.0,"X":510.4514,"Y":333.0549,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":142604.0,"Objects":[{"StartTime":142604.0,"EndTime":142604.0,"X":336.0,"Y":124.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":142779.0,"EndTime":142779.0,"X":261.534241,"Y":132.9359,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":143026.0,"Objects":[{"StartTime":143026.0,"EndTime":143026.0,"X":210.0,"Y":89.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":143201.0,"EndTime":143201.0,"X":184.922729,"Y":169.4724,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":143343.0,"Objects":[{"StartTime":143343.0,"EndTime":143343.0,"X":261.0,"Y":132.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":143518.0,"EndTime":143518.0,"X":185.715179,"Y":170.3263,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":143660.0,"Objects":[{"StartTime":143660.0,"EndTime":143660.0,"X":256.0,"Y":184.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":143729.0,"EndTime":143729.0,"X":184.960236,"Y":170.093552,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":143871.0,"Objects":[{"StartTime":143871.0,"EndTime":143871.0,"X":124.0,"Y":70.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":144046.0,"EndTime":144046.0,"X":110.185104,"Y":158.9334,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":144188.0,"Objects":[{"StartTime":144188.0,"EndTime":144188.0,"X":96.0,"Y":247.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":144363.0,"EndTime":144363.0,"X":109.814896,"Y":158.0666,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":144505.0,"Objects":[{"StartTime":144505.0,"EndTime":144505.0,"X":184.0,"Y":170.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":144574.0,"EndTime":144574.0,"X":109.964081,"Y":158.013229,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":144716.0,"Objects":[{"StartTime":144716.0,"EndTime":144716.0,"X":261.0,"Y":132.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":144891.0,"EndTime":144891.0,"X":349.75293,"Y":146.9304,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":145033.0,"Objects":[{"StartTime":145033.0,"EndTime":145033.0,"X":336.0,"Y":84.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":145208.0,"EndTime":145208.0,"X":387.835815,"Y":157.573425,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":145350.0,"Objects":[{"StartTime":145350.0,"EndTime":145350.0,"X":428.0,"Y":96.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":145419.0,"EndTime":145419.0,"X":415.2836,"Y":169.9141,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":145562.0,"Objects":[{"StartTime":145562.0,"EndTime":145562.0,"X":411.0,"Y":278.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":145737.0,"EndTime":145737.0,"X":491.462463,"Y":247.365463,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":145878.0,"Objects":[{"StartTime":145878.0,"EndTime":145878.0,"X":324.0,"Y":276.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":146053.0,"EndTime":146053.0,"X":409.8932,"Y":277.2359,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":146195.0,"Objects":[{"StartTime":146195.0,"EndTime":146195.0,"X":252.0,"Y":272.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":146264.0,"EndTime":146264.0,"X":324.1942,"Y":274.656555,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":146407.0,"Objects":[{"StartTime":146407.0,"EndTime":146407.0,"X":317.0,"Y":119.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":146582.0,"EndTime":146582.0,"X":292.912048,"Y":205.716614,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":146724.0,"Objects":[{"StartTime":146724.0,"EndTime":146724.0,"X":240.0,"Y":74.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":146899.0,"EndTime":146899.0,"X":262.5866,"Y":161.11972,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":147040.0,"Objects":[{"StartTime":147040.0,"EndTime":147040.0,"X":166.0,"Y":90.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":147109.0,"EndTime":147109.0,"X":219.407776,"Y":142.655563,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":147252.0,"Objects":[{"StartTime":147252.0,"EndTime":147252.0,"X":170.0,"Y":152.0}]},{"StartTime":147464.0,"Objects":[{"StartTime":147464.0,"EndTime":147464.0,"X":38.0,"Y":120.0}]},{"StartTime":147569.0,"Objects":[{"StartTime":147569.0,"EndTime":147569.0,"X":12.0,"Y":155.0}]},{"StartTime":147675.0,"Objects":[{"StartTime":147675.0,"EndTime":147675.0,"X":2.0,"Y":199.0}]},{"StartTime":147781.0,"Objects":[{"StartTime":147781.0,"EndTime":147781.0,"X":11.0,"Y":242.0}]},{"StartTime":147886.0,"Objects":[{"StartTime":147886.0,"EndTime":147886.0,"X":37.0,"Y":279.0}]},{"StartTime":147992.0,"Objects":[{"StartTime":147992.0,"EndTime":147992.0,"X":75.0,"Y":301.0}]},{"StartTime":148097.0,"Objects":[{"StartTime":148097.0,"EndTime":148097.0,"X":119.0,"Y":304.0}]},{"StartTime":148942.0,"Objects":[{"StartTime":148942.0,"EndTime":148942.0,"X":245.0,"Y":208.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":148977.0,"EndTime":148977.0,"X":264.88504,"Y":197.6252,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":149012.0,"EndTime":149012.0,"X":245.126251,"Y":207.934128,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":149047.0,"EndTime":149047.0,"X":264.7588,"Y":197.691071,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":149082.0,"EndTime":149082.0,"X":245.2525,"Y":207.868256,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":149118.0,"EndTime":149118.0,"X":264.695648,"Y":197.724014,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":149153.0,"EndTime":149153.0,"X":245.189377,"Y":207.901184,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":149188.0,"EndTime":149188.0,"X":264.8219,"Y":197.658142,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":149223.0,"EndTime":149223.0,"X":245.063126,"Y":207.967072,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":149222.0,"EndTime":149222.0,"X":264.948151,"Y":197.59227,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":149364.0,"Objects":[{"StartTime":149364.0,"EndTime":149364.0,"X":232.0,"Y":288.0}]},{"StartTime":149787.0,"Objects":[{"StartTime":149787.0,"EndTime":149787.0,"X":217.0,"Y":38.0}]},{"StartTime":149998.0,"Objects":[{"StartTime":149998.0,"EndTime":149998.0,"X":56.0,"Y":98.0}]},{"StartTime":150209.0,"Objects":[{"StartTime":150209.0,"EndTime":150209.0,"X":155.0,"Y":187.0}]},{"StartTime":150420.0,"Objects":[{"StartTime":150420.0,"EndTime":150420.0,"X":94.0,"Y":26.0}]},{"StartTime":150632.0,"Objects":[{"StartTime":150632.0,"EndTime":150632.0,"X":63.0,"Y":262.0}]},{"StartTime":150843.0,"Objects":[{"StartTime":150843.0,"EndTime":150843.0,"X":257.0,"Y":188.0}]},{"StartTime":151054.0,"Objects":[{"StartTime":151054.0,"EndTime":151054.0,"X":138.0,"Y":82.0}]},{"StartTime":151265.0,"Objects":[{"StartTime":151265.0,"EndTime":151265.0,"X":212.0,"Y":275.0}]},{"StartTime":151477.0,"Objects":[{"StartTime":151477.0,"EndTime":151477.0,"X":288.0,"Y":60.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":151652.0,"EndTime":151652.0,"X":266.524567,"Y":155.1055,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":151899.0,"Objects":[{"StartTime":151899.0,"EndTime":151899.0,"X":204.0,"Y":48.0}]},{"StartTime":152111.0,"Objects":[{"StartTime":152111.0,"EndTime":152111.0,"X":346.0,"Y":175.0}]},{"StartTime":152322.0,"Objects":[{"StartTime":152322.0,"EndTime":152322.0,"X":130.0,"Y":263.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":152497.0,"EndTime":152497.0,"X":151.311874,"Y":167.857727,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":152744.0,"Objects":[{"StartTime":152744.0,"EndTime":152744.0,"X":232.0,"Y":244.0}]},{"StartTime":152956.0,"Objects":[{"StartTime":152956.0,"EndTime":152956.0,"X":56.0,"Y":170.0}]},{"StartTime":153167.0,"Objects":[{"StartTime":153167.0,"EndTime":153167.0,"X":64.0,"Y":352.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":153447.0,"EndTime":153447.0,"X":194.861862,"Y":335.192657,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":153590.0,"Objects":[{"StartTime":153590.0,"EndTime":153590.0,"X":224.0,"Y":348.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":153765.0,"EndTime":153765.0,"X":313.264221,"Y":347.8079,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":154012.0,"Objects":[{"StartTime":154012.0,"EndTime":154012.0,"X":376.0,"Y":140.0}]},{"StartTime":154223.0,"Objects":[{"StartTime":154223.0,"EndTime":154223.0,"X":269.0,"Y":286.0}]},{"StartTime":154435.0,"Objects":[{"StartTime":154435.0,"EndTime":154435.0,"X":441.0,"Y":230.0}]},{"StartTime":154646.0,"Objects":[{"StartTime":154646.0,"EndTime":154646.0,"X":269.0,"Y":173.0}]},{"StartTime":154857.0,"Objects":[{"StartTime":154857.0,"EndTime":154857.0,"X":376.0,"Y":320.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":155032.0,"EndTime":155032.0,"X":465.264221,"Y":319.8079,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":155280.0,"Objects":[{"StartTime":155280.0,"EndTime":155280.0,"X":496.0,"Y":136.0}]},{"StartTime":155491.0,"Objects":[{"StartTime":155491.0,"EndTime":155491.0,"X":420.0,"Y":256.0}]},{"StartTime":155702.0,"Objects":[{"StartTime":155702.0,"EndTime":155702.0,"X":330.0,"Y":80.0}]},{"StartTime":155913.0,"Objects":[{"StartTime":155913.0,"EndTime":155913.0,"X":223.0,"Y":226.0}]},{"StartTime":156125.0,"Objects":[{"StartTime":156125.0,"EndTime":156125.0,"X":395.0,"Y":170.0}]},{"StartTime":156336.0,"Objects":[{"StartTime":156336.0,"EndTime":156336.0,"X":223.0,"Y":113.0}]},{"StartTime":156547.0,"Objects":[{"StartTime":156547.0,"EndTime":156547.0,"X":330.0,"Y":260.0}]},{"StartTime":156759.0,"Objects":[{"StartTime":156759.0,"EndTime":156759.0,"X":408.0,"Y":92.0}]},{"StartTime":156970.0,"Objects":[{"StartTime":156970.0,"EndTime":156970.0,"X":168.0,"Y":168.0}]},{"StartTime":157182.0,"Objects":[{"StartTime":157182.0,"EndTime":157182.0,"X":408.0,"Y":244.0}]},{"StartTime":157392.0,"Objects":[{"StartTime":157392.0,"EndTime":157392.0,"X":256.0,"Y":44.0}]},{"StartTime":157604.0,"Objects":[{"StartTime":157604.0,"EndTime":157604.0,"X":264.0,"Y":296.0}]},{"StartTime":157815.0,"Objects":[{"StartTime":157815.0,"EndTime":157815.0,"X":436.0,"Y":168.0}]},{"StartTime":158027.0,"Objects":[{"StartTime":158027.0,"EndTime":158027.0,"X":188.0,"Y":92.0}]},{"StartTime":158238.0,"Objects":[{"StartTime":158238.0,"EndTime":158238.0,"X":212.0,"Y":336.0}]},{"StartTime":158450.0,"Objects":[{"StartTime":158450.0,"EndTime":158450.0,"X":290.0,"Y":168.0}]},{"StartTime":158661.0,"Objects":[{"StartTime":158661.0,"EndTime":158661.0,"X":50.0,"Y":244.0}]},{"StartTime":158871.0,"Objects":[{"StartTime":158871.0,"EndTime":158871.0,"X":290.0,"Y":320.0}]},{"StartTime":159083.0,"Objects":[{"StartTime":159083.0,"EndTime":159083.0,"X":138.0,"Y":120.0}]},{"StartTime":159295.0,"Objects":[{"StartTime":159295.0,"EndTime":159295.0,"X":146.0,"Y":372.0}]},{"StartTime":159506.0,"Objects":[{"StartTime":159506.0,"EndTime":159506.0,"X":318.0,"Y":244.0}]},{"StartTime":159716.0,"Objects":[{"StartTime":159716.0,"EndTime":159716.0,"X":70.0,"Y":168.0}]},{"StartTime":159928.0,"Objects":[{"StartTime":159928.0,"EndTime":159928.0,"X":324.0,"Y":164.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":160103.0,"EndTime":160103.0,"X":396.4909,"Y":220.798523,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":160350.0,"Objects":[{"StartTime":160350.0,"EndTime":160350.0,"X":291.0,"Y":354.0}]},{"StartTime":160562.0,"Objects":[{"StartTime":160562.0,"EndTime":160562.0,"X":209.0,"Y":190.0}]},{"StartTime":160773.0,"Objects":[{"StartTime":160773.0,"EndTime":160773.0,"X":377.0,"Y":321.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":160948.0,"EndTime":160948.0,"X":290.7343,"Y":353.17215,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":161195.0,"Objects":[{"StartTime":161195.0,"EndTime":161195.0,"X":209.0,"Y":190.0}]},{"StartTime":161407.0,"Objects":[{"StartTime":161407.0,"EndTime":161407.0,"X":396.0,"Y":220.0}]},{"StartTime":161618.0,"Objects":[{"StartTime":161618.0,"EndTime":161618.0,"X":200.0,"Y":283.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":161793.0,"EndTime":161793.0,"X":209.6018,"Y":190.27742,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":162040.0,"Objects":[{"StartTime":162040.0,"EndTime":162040.0,"X":396.0,"Y":221.0}]},{"StartTime":162251.0,"Objects":[{"StartTime":162251.0,"EndTime":162251.0,"X":290.0,"Y":353.0}]},{"StartTime":162463.0,"Objects":[{"StartTime":162463.0,"EndTime":162463.0,"X":264.0,"Y":56.0}]},{"StartTime":162568.0,"Objects":[{"StartTime":162568.0,"EndTime":162568.0,"X":277.0,"Y":102.0}]},{"StartTime":162674.0,"Objects":[{"StartTime":162674.0,"EndTime":162674.0,"X":290.0,"Y":149.0}]},{"StartTime":162779.0,"Objects":[{"StartTime":162779.0,"EndTime":162779.0,"X":304.0,"Y":196.0}]},{"StartTime":162885.0,"Objects":[{"StartTime":162885.0,"EndTime":162885.0,"X":317.0,"Y":243.0}]},{"StartTime":163097.0,"Objects":[{"StartTime":163097.0,"EndTime":163097.0,"X":172.0,"Y":164.0}]},{"StartTime":163308.0,"Objects":[{"StartTime":163308.0,"EndTime":163308.0,"X":416.0,"Y":108.0}]},{"StartTime":163519.0,"Objects":[{"StartTime":163519.0,"EndTime":163519.0,"X":232.0,"Y":91.0}]},{"StartTime":163730.0,"Objects":[{"StartTime":163730.0,"EndTime":163730.0,"X":400.0,"Y":12.0}]},{"StartTime":163941.0,"Objects":[{"StartTime":163941.0,"EndTime":163941.0,"X":383.0,"Y":196.0}]},{"StartTime":164153.0,"Objects":[{"StartTime":164153.0,"EndTime":164153.0,"X":217.0,"Y":0.0}]},{"StartTime":164364.0,"Objects":[{"StartTime":164364.0,"EndTime":164364.0,"X":200.0,"Y":184.0}]},{"StartTime":164575.0,"Objects":[{"StartTime":164575.0,"EndTime":164575.0,"X":313.0,"Y":16.0}]},{"StartTime":164786.0,"Objects":[{"StartTime":164786.0,"EndTime":164786.0,"X":112.0,"Y":32.0}]},{"StartTime":164998.0,"Objects":[{"StartTime":164998.0,"EndTime":164998.0,"X":200.0,"Y":184.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":165173.0,"EndTime":165173.0,"X":205.788208,"Y":91.45287,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":165421.0,"Objects":[{"StartTime":165421.0,"EndTime":165421.0,"X":112.0,"Y":256.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":165596.0,"EndTime":165596.0,"X":106.211784,"Y":348.547119,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":165843.0,"Objects":[{"StartTime":165843.0,"EndTime":165843.0,"X":116.0,"Y":176.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":166018.0,"EndTime":166018.0,"X":23.4528751,"Y":170.211777,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":166266.0,"Objects":[{"StartTime":166266.0,"EndTime":166266.0,"X":196.0,"Y":264.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":166441.0,"EndTime":166441.0,"X":288.547119,"Y":269.7882,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":166688.0,"Objects":[{"StartTime":166688.0,"EndTime":166688.0,"X":248.0,"Y":60.0}]},{"StartTime":166899.0,"Objects":[{"StartTime":166899.0,"EndTime":166899.0,"X":248.0,"Y":201.0}]},{"StartTime":167111.0,"Objects":[{"StartTime":167111.0,"EndTime":167111.0,"X":333.0,"Y":55.0}]},{"StartTime":167322.0,"Objects":[{"StartTime":167322.0,"EndTime":167322.0,"X":248.0,"Y":201.0}]},{"StartTime":167533.0,"Objects":[{"StartTime":167533.0,"EndTime":167533.0,"X":424.0,"Y":101.0}]},{"StartTime":167744.0,"Objects":[{"StartTime":167744.0,"EndTime":167744.0,"X":248.0,"Y":201.0}]},{"StartTime":167956.0,"Objects":[{"StartTime":167956.0,"EndTime":167956.0,"X":468.0,"Y":224.0}]},{"StartTime":168167.0,"Objects":[{"StartTime":168167.0,"EndTime":168167.0,"X":292.0,"Y":124.0}]},{"StartTime":168378.0,"Objects":[{"StartTime":168378.0,"EndTime":168378.0,"X":364.0,"Y":328.0}]},{"StartTime":168589.0,"Objects":[{"StartTime":168589.0,"EndTime":168589.0,"X":364.0,"Y":158.0}]},{"StartTime":168801.0,"Objects":[{"StartTime":168801.0,"EndTime":168801.0,"X":244.0,"Y":304.0}]},{"StartTime":169013.0,"Objects":[{"StartTime":169013.0,"EndTime":169013.0,"X":464.0,"Y":327.0}]},{"StartTime":169224.0,"Objects":[{"StartTime":169224.0,"EndTime":169224.0,"X":192.0,"Y":248.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":169399.0,"EndTime":169399.0,"X":184.99115,"Y":345.247742,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":169646.0,"Objects":[{"StartTime":169646.0,"EndTime":169646.0,"X":508.0,"Y":272.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":169821.0,"EndTime":169821.0,"X":500.99115,"Y":174.752258,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":170068.0,"Objects":[{"StartTime":170068.0,"EndTime":170068.0,"X":268.0,"Y":60.0}]},{"StartTime":170279.0,"Objects":[{"StartTime":170279.0,"EndTime":170279.0,"X":268.0,"Y":257.0}]},{"StartTime":170491.0,"Objects":[{"StartTime":170491.0,"EndTime":170491.0,"X":404.0,"Y":116.0}]},{"StartTime":170702.0,"Objects":[{"StartTime":170702.0,"EndTime":170702.0,"X":207.0,"Y":116.0}]},{"StartTime":170913.0,"Objects":[{"StartTime":170913.0,"EndTime":170913.0,"X":348.0,"Y":267.0}]},{"StartTime":171124.0,"Objects":[{"StartTime":171124.0,"EndTime":171124.0,"X":348.0,"Y":31.0}]},{"StartTime":171336.0,"Objects":[{"StartTime":171336.0,"EndTime":171336.0,"X":192.0,"Y":200.0}]},{"StartTime":171547.0,"Objects":[{"StartTime":171547.0,"EndTime":171547.0,"X":428.0,"Y":200.0}]},{"StartTime":171759.0,"Objects":[{"StartTime":171759.0,"EndTime":171759.0,"X":268.0,"Y":60.0}]},{"StartTime":171970.0,"Objects":[{"StartTime":171970.0,"EndTime":171970.0,"X":386.0,"Y":236.0}]},{"StartTime":172181.0,"Objects":[{"StartTime":172181.0,"EndTime":172181.0,"X":386.0,"Y":11.0}]},{"StartTime":172393.0,"Objects":[{"StartTime":172393.0,"EndTime":172393.0,"X":268.0,"Y":187.0}]},{"StartTime":172604.0,"Objects":[{"StartTime":172604.0,"EndTime":172604.0,"X":149.0,"Y":55.0}]},{"StartTime":172815.0,"Objects":[{"StartTime":172815.0,"EndTime":172815.0,"X":30.0,"Y":231.0}]},{"StartTime":173026.0,"Objects":[{"StartTime":173026.0,"EndTime":173026.0,"X":30.0,"Y":7.0}]},{"StartTime":173238.0,"Objects":[{"StartTime":173238.0,"EndTime":173238.0,"X":149.0,"Y":183.0}]},{"StartTime":173449.0,"Objects":[{"StartTime":173449.0,"EndTime":173449.0,"X":30.0,"Y":7.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":173624.0,"EndTime":173624.0,"X":52.15489,"Y":101.949524,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":173871.0,"Objects":[{"StartTime":173871.0,"EndTime":173871.0,"X":240.0,"Y":64.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":174046.0,"EndTime":174046.0,"X":146.743469,"Y":35.54885,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":174294.0,"Objects":[{"StartTime":174294.0,"EndTime":174294.0,"X":80.0,"Y":216.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":174469.0,"EndTime":174469.0,"X":150.509186,"Y":148.659775,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":174716.0,"Objects":[{"StartTime":174716.0,"EndTime":174716.0,"X":124.0,"Y":280.0}]},{"StartTime":174928.0,"Objects":[{"StartTime":174928.0,"EndTime":174928.0,"X":56.0,"Y":128.0}]},{"StartTime":175139.0,"Objects":[{"StartTime":175139.0,"EndTime":175139.0,"X":216.0,"Y":212.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":175314.0,"EndTime":175314.0,"X":204.150711,"Y":286.058044,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":175562.0,"Objects":[{"StartTime":175562.0,"EndTime":175562.0,"X":296.0,"Y":216.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":175737.0,"EndTime":175737.0,"X":280.708374,"Y":304.6914,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":175984.0,"Objects":[{"StartTime":175984.0,"EndTime":175984.0,"X":376.0,"Y":208.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":176264.0,"EndTime":176264.0,"X":353.806122,"Y":341.1632,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":176406.0,"Objects":[{"StartTime":176406.0,"EndTime":176406.0,"X":356.739136,"Y":344.739136}]},{"StartTime":176618.0,"Objects":[{"StartTime":176618.0,"EndTime":176618.0,"X":320.521729,"Y":136.521729}]},{"StartTime":176723.0,"Objects":[{"StartTime":176723.0,"EndTime":176723.0,"X":324.260864,"Y":140.260864}]},{"StartTime":176829.0,"Objects":[{"StartTime":176829.0,"EndTime":176829.0,"X":328.0,"Y":144.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":177004.0,"EndTime":177004.0,"X":411.899,"Y":139.8616,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":177252.0,"Objects":[{"StartTime":177252.0,"EndTime":177252.0,"X":248.0,"Y":152.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":177427.0,"EndTime":177427.0,"X":164.101013,"Y":156.138382,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":177674.0,"Objects":[{"StartTime":177674.0,"EndTime":177674.0,"X":344.0,"Y":120.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":177849.0,"EndTime":177849.0,"X":427.899,"Y":115.8616,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":178097.0,"Objects":[{"StartTime":178097.0,"EndTime":178097.0,"X":236.0,"Y":168.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":178272.0,"EndTime":178272.0,"X":152.101013,"Y":172.138382,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":178519.0,"Objects":[{"StartTime":178519.0,"EndTime":178519.0,"X":192.0,"Y":272.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":178694.0,"EndTime":178694.0,"X":196.1384,"Y":355.899,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":178942.0,"Objects":[{"StartTime":178942.0,"EndTime":178942.0,"X":152.0,"Y":172.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":179117.0,"EndTime":179117.0,"X":147.8616,"Y":88.10101,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":179364.0,"Objects":[{"StartTime":179364.0,"EndTime":179364.0,"X":228.0,"Y":284.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":179539.0,"EndTime":179539.0,"X":232.1384,"Y":367.899,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":179787.0,"Objects":[{"StartTime":179787.0,"EndTime":179787.0,"X":116.0,"Y":152.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":179962.0,"EndTime":179962.0,"X":111.86161,"Y":68.10102,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":180209.0,"Objects":[{"StartTime":180209.0,"EndTime":180209.0,"X":100.0,"Y":256.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":180384.0,"EndTime":180384.0,"X":16.1010227,"Y":260.1384,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":180632.0,"Objects":[{"StartTime":180632.0,"EndTime":180632.0,"X":240.0,"Y":184.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":180807.0,"EndTime":180807.0,"X":323.899,"Y":179.8616,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":181055.0,"Objects":[{"StartTime":181055.0,"EndTime":181055.0,"X":288.0,"Y":336.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":181230.0,"EndTime":181230.0,"X":284.541016,"Y":246.0665,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":181477.0,"Objects":[{"StartTime":181477.0,"EndTime":181477.0,"X":432.0,"Y":84.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":181652.0,"EndTime":181652.0,"X":423.044678,"Y":173.553345,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":181900.0,"Objects":[{"StartTime":181900.0,"EndTime":181900.0,"X":368.0,"Y":352.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":182075.0,"EndTime":182075.0,"X":364.541016,"Y":262.0665,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":182322.0,"Objects":[{"StartTime":182322.0,"EndTime":182322.0,"X":512.0,"Y":100.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":182497.0,"EndTime":182497.0,"X":503.044678,"Y":189.553345,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":182745.0,"Objects":[{"StartTime":182745.0,"EndTime":182745.0,"X":272.0,"Y":104.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":182920.0,"EndTime":182920.0,"X":361.553345,"Y":112.955338,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":183062.0,"Objects":[{"StartTime":183062.0,"EndTime":183062.0,"X":356.0,"Y":132.0}]},{"StartTime":183167.0,"Objects":[{"StartTime":183167.0,"EndTime":183167.0,"X":352.0,"Y":156.0}]},{"StartTime":183378.0,"Objects":[{"StartTime":183378.0,"EndTime":183378.0,"X":276.0,"Y":20.0}]},{"StartTime":183590.0,"Objects":[{"StartTime":183590.0,"EndTime":183590.0,"X":304.0,"Y":240.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":183765.0,"EndTime":183765.0,"X":220.5027,"Y":243.341385,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":184012.0,"Objects":[{"StartTime":184012.0,"EndTime":184012.0,"X":392.0,"Y":272.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":184187.0,"EndTime":184187.0,"X":436.5039,"Y":342.962158,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":184435.0,"Objects":[{"StartTime":184435.0,"EndTime":184435.0,"X":376.0,"Y":184.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":184610.0,"EndTime":184610.0,"X":413.9991,"Y":109.324722,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":184857.0,"Objects":[{"StartTime":184857.0,"EndTime":184857.0,"X":320.0,"Y":336.0}]},{"StartTime":185069.0,"Objects":[{"StartTime":185069.0,"EndTime":185069.0,"X":260.0,"Y":180.0}]},{"StartTime":185280.0,"Objects":[{"StartTime":185280.0,"EndTime":185280.0,"X":176.0,"Y":304.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":185455.0,"EndTime":185455.0,"X":146.285233,"Y":347.999146,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":185702.0,"Objects":[{"StartTime":185702.0,"EndTime":185702.0,"X":207.0,"Y":176.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":185877.0,"EndTime":185877.0,"X":258.989227,"Y":179.51886,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":186125.0,"Objects":[{"StartTime":186125.0,"EndTime":186125.0,"X":84.0,"Y":224.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":186300.0,"EndTime":186300.0,"X":60.46429,"Y":176.0,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":186547.0,"Objects":[{"StartTime":186547.0,"EndTime":186547.0,"X":244.0,"Y":260.0}]},{"StartTime":186759.0,"Objects":[{"StartTime":186759.0,"EndTime":186759.0,"X":88.0,"Y":300.0}]},{"StartTime":186970.0,"Objects":[{"StartTime":186970.0,"EndTime":186970.0,"X":128.0,"Y":44.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":187145.0,"EndTime":187145.0,"X":133.824356,"Y":148.838348,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":187393.0,"Objects":[{"StartTime":187393.0,"EndTime":187393.0,"X":340.0,"Y":208.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":187568.0,"EndTime":187568.0,"X":345.824341,"Y":103.161659,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":187815.0,"Objects":[{"StartTime":187815.0,"EndTime":187815.0,"X":244.0,"Y":260.0}]},{"StartTime":188026.0,"Objects":[{"StartTime":188026.0,"EndTime":188026.0,"X":424.0,"Y":240.0}]},{"StartTime":188238.0,"Objects":[{"StartTime":188238.0,"EndTime":188238.0,"X":211.0,"Y":244.0}]},{"StartTime":188449.0,"Objects":[{"StartTime":188449.0,"EndTime":188449.0,"X":377.0,"Y":317.0}]},{"StartTime":188660.0,"Objects":[{"StartTime":188660.0,"EndTime":188660.0,"X":196.0,"Y":336.0}]},{"StartTime":188871.0,"Objects":[{"StartTime":188871.0,"EndTime":188871.0,"X":224.0,"Y":154.0}]},{"StartTime":189083.0,"Objects":[{"StartTime":189083.0,"EndTime":189083.0,"X":367.0,"Y":270.0}]},{"StartTime":189294.0,"Objects":[{"StartTime":189294.0,"EndTime":189294.0,"X":132.0,"Y":216.0}]},{"StartTime":189505.0,"Objects":[{"StartTime":189505.0,"EndTime":189505.0,"X":338.0,"Y":135.0}]},{"StartTime":189610.0,"Objects":[{"StartTime":189610.0,"EndTime":189610.0,"X":330.0,"Y":186.0}]},{"StartTime":189716.0,"Objects":[{"StartTime":189716.0,"EndTime":189716.0,"X":322.0,"Y":238.0}]},{"StartTime":189821.0,"Objects":[{"StartTime":189821.0,"EndTime":189821.0,"X":314.0,"Y":290.0}]},{"StartTime":189927.0,"Objects":[{"StartTime":189927.0,"EndTime":189927.0,"X":306.0,"Y":342.0}]},{"StartTime":190139.0,"Objects":[{"StartTime":190139.0,"EndTime":190139.0,"X":228.0,"Y":252.0}]},{"StartTime":190350.0,"Objects":[{"StartTime":190350.0,"EndTime":190350.0,"X":420.0,"Y":216.0}]},{"StartTime":190562.0,"Objects":[{"StartTime":190562.0,"EndTime":190562.0,"X":247.0,"Y":160.0}]},{"StartTime":190773.0,"Objects":[{"StartTime":190773.0,"EndTime":190773.0,"X":406.0,"Y":252.0}]},{"StartTime":190985.0,"Objects":[{"StartTime":190985.0,"EndTime":190985.0,"X":368.0,"Y":74.0}]},{"StartTime":191195.0,"Objects":[{"StartTime":191195.0,"EndTime":191195.0,"X":373.0,"Y":269.0}]},{"StartTime":191407.0,"Objects":[{"StartTime":191407.0,"EndTime":191407.0,"X":507.0,"Y":146.0}]},{"StartTime":191618.0,"Objects":[{"StartTime":191618.0,"EndTime":191618.0,"X":335.0,"Y":271.0}]},{"StartTime":191830.0,"Objects":[{"StartTime":191830.0,"EndTime":191830.0,"X":508.0,"Y":325.0}]},{"StartTime":192040.0,"Objects":[{"StartTime":192040.0,"EndTime":192040.0,"X":219.0,"Y":271.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":192215.0,"EndTime":192215.0,"X":205.632385,"Y":186.8185,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":192463.0,"Objects":[{"StartTime":192463.0,"EndTime":192463.0,"X":279.0,"Y":327.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":192638.0,"EndTime":192638.0,"X":197.296051,"Y":348.666077,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":192885.0,"Objects":[{"StartTime":192885.0,"EndTime":192885.0,"X":335.0,"Y":271.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":193060.0,"EndTime":193060.0,"X":356.590668,"Y":352.7418,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":193308.0,"Objects":[{"StartTime":193308.0,"EndTime":193308.0,"X":279.0,"Y":219.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":193483.0,"EndTime":193483.0,"X":360.7418,"Y":197.409332,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":193731.0,"Objects":[{"StartTime":193731.0,"EndTime":193731.0,"X":108.0,"Y":296.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":194011.0,"EndTime":194011.0,"X":111.138687,"Y":161.0365,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":194153.0,"Objects":[{"StartTime":194153.0,"EndTime":194153.0,"X":72.0,"Y":100.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":194328.0,"EndTime":194328.0,"X":155.1787,"Y":102.726517,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":194576.0,"Objects":[{"StartTime":194576.0,"EndTime":194576.0,"X":24.0,"Y":24.0}]},{"StartTime":194787.0,"Objects":[{"StartTime":194787.0,"EndTime":194787.0,"X":36.0,"Y":168.0}]},{"StartTime":194998.0,"Objects":[{"StartTime":194998.0,"EndTime":194998.0,"X":116.0,"Y":40.0}]},{"StartTime":195209.0,"Objects":[{"StartTime":195209.0,"EndTime":195209.0,"X":184.0,"Y":184.0}]},{"StartTime":195421.0,"Objects":[{"StartTime":195421.0,"EndTime":195421.0,"X":256.0,"Y":56.0}]},{"StartTime":195632.0,"Objects":[{"StartTime":195632.0,"EndTime":195632.0,"X":112.0,"Y":155.0}]},{"StartTime":195843.0,"Objects":[{"StartTime":195843.0,"EndTime":195843.0,"X":276.0,"Y":224.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":196018.0,"EndTime":196018.0,"X":268.203339,"Y":134.338348,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":196266.0,"Objects":[{"StartTime":196266.0,"EndTime":196266.0,"X":160.0,"Y":72.0}]},{"StartTime":196477.0,"Objects":[{"StartTime":196477.0,"EndTime":196477.0,"X":16.0,"Y":171.0}]},{"StartTime":196688.0,"Objects":[{"StartTime":196688.0,"EndTime":196688.0,"X":180.0,"Y":240.0}]},{"StartTime":196899.0,"Objects":[{"StartTime":196899.0,"EndTime":196899.0,"X":72.0,"Y":108.0}]},{"StartTime":197111.0,"Objects":[{"StartTime":197111.0,"EndTime":197111.0,"X":76.0,"Y":328.0}]},{"StartTime":197323.0,"Objects":[{"StartTime":197323.0,"EndTime":197323.0,"X":249.0,"Y":274.0}]},{"StartTime":197534.0,"Objects":[{"StartTime":197534.0,"EndTime":197534.0,"X":83.0,"Y":171.0}]},{"StartTime":197745.0,"Objects":[{"StartTime":197745.0,"EndTime":197745.0,"X":217.0,"Y":295.0}]},{"StartTime":197956.0,"Objects":[{"StartTime":197956.0,"EndTime":197956.0,"X":218.0,"Y":119.0}]},{"StartTime":198168.0,"Objects":[{"StartTime":198168.0,"EndTime":198168.0,"X":179.0,"Y":297.0}]},{"StartTime":198379.0,"Objects":[{"StartTime":198379.0,"EndTime":198379.0,"X":317.0,"Y":223.0}]},{"StartTime":198591.0,"Objects":[{"StartTime":198591.0,"EndTime":198591.0,"X":144.0,"Y":279.0}]},{"StartTime":198801.0,"Objects":[{"StartTime":198801.0,"EndTime":198801.0,"X":295.0,"Y":284.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":198976.0,"EndTime":198976.0,"X":277.349548,"Y":195.747742,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":199224.0,"Objects":[{"StartTime":199224.0,"EndTime":199224.0,"X":489.0,"Y":254.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":199399.0,"EndTime":199399.0,"X":471.349548,"Y":342.252258,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":199646.0,"Objects":[{"StartTime":199646.0,"EndTime":199646.0,"X":277.0,"Y":195.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":199821.0,"EndTime":199821.0,"X":259.349548,"Y":106.747734,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":200069.0,"Objects":[{"StartTime":200069.0,"EndTime":200069.0,"X":506.0,"Y":165.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":200244.0,"EndTime":200244.0,"X":488.349548,"Y":253.252258,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":200491.0,"Objects":[{"StartTime":200491.0,"EndTime":200491.0,"X":301.0,"Y":42.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":200771.0,"EndTime":200771.0,"X":419.8098,"Y":32.4704971,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":200914.0,"Objects":[{"StartTime":200914.0,"EndTime":200914.0,"X":432.0,"Y":52.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":201089.0,"EndTime":201089.0,"X":422.412018,"Y":141.487823,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":201336.0,"Objects":[{"StartTime":201336.0,"EndTime":201336.0,"X":262.0,"Y":226.0}]},{"StartTime":201547.0,"Objects":[{"StartTime":201547.0,"EndTime":201547.0,"X":352.0,"Y":103.0}]},{"StartTime":201759.0,"Objects":[{"StartTime":201759.0,"EndTime":201759.0,"X":352.0,"Y":256.0}]},{"StartTime":201970.0,"Objects":[{"StartTime":201970.0,"EndTime":201970.0,"X":262.0,"Y":132.0}]},{"StartTime":202181.0,"Objects":[{"StartTime":202181.0,"EndTime":202181.0,"X":407.0,"Y":179.0}]},{"StartTime":202393.0,"Objects":[{"StartTime":202393.0,"EndTime":202393.0,"X":240.0,"Y":253.0}]},{"StartTime":202604.0,"Objects":[{"StartTime":202604.0,"EndTime":202604.0,"X":418.0,"Y":291.0}]},{"StartTime":202815.0,"Objects":[{"StartTime":202815.0,"EndTime":202815.0,"X":296.0,"Y":155.0}]},{"StartTime":203026.0,"Objects":[{"StartTime":203026.0,"EndTime":203026.0,"X":315.0,"Y":338.0}]},{"StartTime":203131.0,"Objects":[{"StartTime":203131.0,"EndTime":203131.0,"X":281.0,"Y":308.0}]},{"StartTime":203237.0,"Objects":[{"StartTime":203237.0,"EndTime":203237.0,"X":239.0,"Y":292.0}]},{"StartTime":203342.0,"Objects":[{"StartTime":203342.0,"EndTime":203342.0,"X":195.0,"Y":291.0}]},{"StartTime":203448.0,"Objects":[{"StartTime":203448.0,"EndTime":203448.0,"X":152.0,"Y":306.0}]},{"StartTime":203660.0,"Objects":[{"StartTime":203660.0,"EndTime":203660.0,"X":328.0,"Y":380.0}]},{"StartTime":203871.0,"Objects":[{"StartTime":203871.0,"EndTime":203871.0,"X":312.0,"Y":204.0}]},{"StartTime":204083.0,"Objects":[{"StartTime":204083.0,"EndTime":204083.0,"X":120.0,"Y":266.0}]},{"StartTime":204294.0,"Objects":[{"StartTime":204294.0,"EndTime":204294.0,"X":284.0,"Y":136.0}]},{"StartTime":204506.0,"Objects":[{"StartTime":204506.0,"EndTime":204506.0,"X":241.0,"Y":334.0}]},{"StartTime":204716.0,"Objects":[{"StartTime":204716.0,"EndTime":204716.0,"X":210.0,"Y":130.0}]},{"StartTime":204928.0,"Objects":[{"StartTime":204928.0,"EndTime":204928.0,"X":359.0,"Y":267.0}]},{"StartTime":205139.0,"Objects":[{"StartTime":205139.0,"EndTime":205139.0,"X":152.0,"Y":180.0}]},{"StartTime":205351.0,"Objects":[{"StartTime":205351.0,"EndTime":205351.0,"X":345.0,"Y":120.0}]},{"StartTime":205562.0,"Objects":[{"StartTime":205562.0,"EndTime":205562.0,"X":84.0,"Y":136.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":205737.0,"EndTime":205737.0,"X":83.80006,"Y":221.6485,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":205984.0,"Objects":[{"StartTime":205984.0,"EndTime":205984.0,"X":284.0,"Y":136.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":206159.0,"EndTime":206159.0,"X":284.199921,"Y":50.3514977,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":206407.0,"Objects":[{"StartTime":206407.0,"EndTime":206407.0,"X":184.0,"Y":248.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":206582.0,"EndTime":206582.0,"X":269.6485,"Y":248.199936,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":206829.0,"Objects":[{"StartTime":206829.0,"EndTime":206829.0,"X":180.0,"Y":28.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":207004.0,"EndTime":207004.0,"X":94.3514938,"Y":27.80006,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":207252.0,"Objects":[{"StartTime":207252.0,"EndTime":207252.0,"X":153.0,"Y":305.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":207532.0,"EndTime":207532.0,"X":151.988937,"Y":179.081238,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":207674.0,"Objects":[{"StartTime":207674.0,"EndTime":207674.0,"X":140.0,"Y":160.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":207849.0,"EndTime":207849.0,"X":54.3514977,"Y":159.800079,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":208097.0,"Objects":[{"StartTime":208097.0,"EndTime":208097.0,"X":72.0,"Y":336.0}]},{"StartTime":208308.0,"Objects":[{"StartTime":208308.0,"EndTime":208308.0,"X":256.0,"Y":292.0}]},{"StartTime":208519.0,"Objects":[{"StartTime":208519.0,"EndTime":208519.0,"X":100.0,"Y":224.0}]},{"StartTime":208730.0,"Objects":[{"StartTime":208730.0,"EndTime":208730.0,"X":204.0,"Y":381.0}]},{"StartTime":208942.0,"Objects":[{"StartTime":208942.0,"EndTime":208942.0,"X":351.0,"Y":209.0}]},{"StartTime":209153.0,"Objects":[{"StartTime":209153.0,"EndTime":209153.0,"X":178.0,"Y":305.0}]},{"StartTime":209364.0,"Objects":[{"StartTime":209364.0,"EndTime":209364.0,"X":312.0,"Y":344.0}]},{"StartTime":209576.0,"Objects":[{"StartTime":209576.0,"EndTime":209576.0,"X":217.0,"Y":171.0}]},{"StartTime":209787.0,"Objects":[{"StartTime":209787.0,"EndTime":209787.0,"X":472.0,"Y":144.0}]},{"StartTime":209998.0,"Objects":[{"StartTime":209998.0,"EndTime":209998.0,"X":264.0,"Y":259.0}]},{"StartTime":210209.0,"Objects":[{"StartTime":210209.0,"EndTime":210209.0,"X":425.0,"Y":306.0}]},{"StartTime":210421.0,"Objects":[{"StartTime":210421.0,"EndTime":210421.0,"X":311.0,"Y":98.0}]},{"StartTime":210632.0,"Objects":[{"StartTime":210632.0,"EndTime":210632.0,"X":332.0,"Y":312.0}]},{"StartTime":210843.0,"Objects":[{"StartTime":210843.0,"EndTime":210843.0,"X":396.0,"Y":100.0}]},{"StartTime":211055.0,"Objects":[{"StartTime":211055.0,"EndTime":211055.0,"X":192.0,"Y":160.0}]},{"StartTime":211266.0,"Objects":[{"StartTime":211266.0,"EndTime":211266.0,"X":403.0,"Y":224.0}]},{"StartTime":211477.0,"Objects":[{"StartTime":211477.0,"EndTime":211477.0,"X":328.0,"Y":24.0}]},{"StartTime":211688.0,"Objects":[{"StartTime":211688.0,"EndTime":211688.0,"X":255.0,"Y":267.0}]},{"StartTime":211900.0,"Objects":[{"StartTime":211900.0,"EndTime":211900.0,"X":488.0,"Y":198.0}]},{"StartTime":212111.0,"Objects":[{"StartTime":212111.0,"EndTime":212111.0,"X":247.0,"Y":125.0}]},{"StartTime":212322.0,"Objects":[{"StartTime":212322.0,"EndTime":212322.0,"X":392.0,"Y":312.0}]},{"StartTime":212533.0,"Objects":[{"StartTime":212533.0,"EndTime":212533.0,"X":334.0,"Y":66.0}]},{"StartTime":212745.0,"Objects":[{"StartTime":212745.0,"EndTime":212745.0,"X":342.0,"Y":351.0}]},{"StartTime":212956.0,"Objects":[{"StartTime":212956.0,"EndTime":212956.0,"X":372.0,"Y":100.0}]},{"StartTime":213167.0,"Objects":[{"StartTime":213167.0,"EndTime":213167.0,"X":251.0,"Y":373.0}]},{"StartTime":213378.0,"Objects":[{"StartTime":213378.0,"EndTime":213378.0,"X":402.0,"Y":170.0}]},{"StartTime":213590.0,"Objects":[{"StartTime":213590.0,"EndTime":213590.0,"X":136.0,"Y":327.0}]},{"StartTime":213801.0,"Objects":[{"StartTime":213801.0,"EndTime":213801.0,"X":382.0,"Y":270.0}]},{"StartTime":214012.0,"Objects":[{"StartTime":214012.0,"EndTime":214012.0,"X":212.0,"Y":144.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":214187.0,"EndTime":214187.0,"X":220.116043,"Y":240.231522,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":214435.0,"Objects":[{"StartTime":214435.0,"EndTime":214435.0,"X":152.0,"Y":88.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":214610.0,"EndTime":214610.0,"X":65.05222,"Y":46.2239647,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":214857.0,"Objects":[{"StartTime":214857.0,"EndTime":214857.0,"X":232.0,"Y":64.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":215032.0,"EndTime":215032.0,"X":310.786377,"Y":7.698365,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":215280.0,"Objects":[{"StartTime":215280.0,"EndTime":215280.0,"X":80.0,"Y":120.0}]},{"StartTime":215491.0,"Objects":[{"StartTime":215491.0,"EndTime":215491.0,"X":272.0,"Y":188.0}]},{"StartTime":215702.0,"Objects":[{"StartTime":215702.0,"EndTime":215702.0,"X":192.0,"Y":8.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":215877.0,"EndTime":215877.0,"X":194.429779,"Y":88.99472,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":216125.0,"Objects":[{"StartTime":216125.0,"EndTime":216125.0,"X":384.0,"Y":64.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":216300.0,"EndTime":216300.0,"X":328.026855,"Y":123.368477,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":216547.0,"Objects":[{"StartTime":216547.0,"EndTime":216547.0,"X":432.0,"Y":244.0}]},{"StartTime":216759.0,"Objects":[{"StartTime":216759.0,"EndTime":216759.0,"X":260.0,"Y":264.0}]},{"StartTime":216970.0,"Objects":[{"StartTime":216970.0,"EndTime":216970.0,"X":328.0,"Y":123.0}]},{"StartTime":217075.0,"Objects":[{"StartTime":217075.0,"EndTime":217075.0,"X":333.0,"Y":175.0}]},{"StartTime":217181.0,"Objects":[{"StartTime":217181.0,"EndTime":217181.0,"X":338.0,"Y":227.0}]},{"StartTime":217286.0,"Objects":[{"StartTime":217286.0,"EndTime":217286.0,"X":344.0,"Y":279.0}]},{"StartTime":217392.0,"Objects":[{"StartTime":217392.0,"EndTime":217392.0,"X":349.0,"Y":331.0}]},{"StartTime":218238.0,"Objects":[{"StartTime":218238.0,"EndTime":218238.0,"X":349.0,"Y":331.0}]},{"StartTime":218343.0,"Objects":[{"StartTime":218343.0,"EndTime":218343.0,"X":310.0,"Y":323.0}]},{"StartTime":218449.0,"Objects":[{"StartTime":218449.0,"EndTime":218449.0,"X":273.0,"Y":317.0}]},{"StartTime":218554.0,"Objects":[{"StartTime":218554.0,"EndTime":218554.0,"X":236.0,"Y":312.0}]},{"StartTime":218660.0,"Objects":[{"StartTime":218660.0,"EndTime":218660.0,"X":198.0,"Y":306.0}]},{"StartTime":218765.0,"Objects":[{"StartTime":218765.0,"EndTime":218765.0,"X":253.0,"Y":296.0}]},{"StartTime":218871.0,"Objects":[{"StartTime":218871.0,"EndTime":218871.0,"X":309.0,"Y":287.0}]},{"StartTime":218976.0,"Objects":[{"StartTime":218976.0,"EndTime":218976.0,"X":365.0,"Y":278.0}]},{"StartTime":219082.0,"Objects":[{"StartTime":219082.0,"EndTime":219082.0,"X":421.0,"Y":268.0}]},{"StartTime":219294.0,"Objects":[{"StartTime":219294.0,"EndTime":219294.0,"X":348.0,"Y":92.0}]},{"StartTime":219505.0,"Objects":[{"StartTime":219505.0,"EndTime":219505.0,"X":205.0,"Y":236.0}]},{"StartTime":219717.0,"Objects":[{"StartTime":219717.0,"EndTime":219717.0,"X":381.0,"Y":163.0}]},{"StartTime":219928.0,"Objects":[{"StartTime":219928.0,"EndTime":219928.0,"X":237.0,"Y":24.0}]},{"StartTime":220140.0,"Objects":[{"StartTime":220140.0,"EndTime":220140.0,"X":310.0,"Y":200.0}]},{"StartTime":220350.0,"Objects":[{"StartTime":220350.0,"EndTime":220350.0,"X":449.0,"Y":52.0}]},{"StartTime":220562.0,"Objects":[{"StartTime":220562.0,"EndTime":220562.0,"X":273.0,"Y":125.0}]},{"StartTime":220773.0,"Objects":[{"StartTime":220773.0,"EndTime":220773.0,"X":392.0,"Y":272.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":220948.0,"EndTime":220948.0,"X":493.387451,"Y":282.365265,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":221195.0,"Objects":[{"StartTime":221195.0,"EndTime":221195.0,"X":257.0,"Y":249.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":221370.0,"EndTime":221370.0,"X":168.323166,"Y":298.312439,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":221618.0,"Objects":[{"StartTime":221618.0,"EndTime":221618.0,"X":380.0,"Y":189.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":221793.0,"EndTime":221793.0,"X":421.2337,"Y":95.80929,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":222040.0,"Objects":[{"StartTime":222040.0,"EndTime":222040.0,"X":317.0,"Y":308.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":222215.0,"EndTime":222215.0,"X":392.657227,"Y":376.100861,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":222463.0,"Objects":[{"StartTime":222463.0,"EndTime":222463.0,"X":297.0,"Y":175.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":222743.0,"EndTime":222743.0,"X":252.84137,"Y":29.1527958,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":222885.0,"Objects":[{"StartTime":222885.0,"EndTime":222885.0,"X":253.0,"Y":29.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":223060.0,"EndTime":223060.0,"X":343.9761,"Y":72.90899,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":223308.0,"Objects":[{"StartTime":223308.0,"EndTime":223308.0,"X":168.0,"Y":34.0}]},{"StartTime":223519.0,"Objects":[{"StartTime":223519.0,"EndTime":223519.0,"X":63.0,"Y":216.0}]},{"StartTime":223731.0,"Objects":[{"StartTime":223731.0,"EndTime":223731.0,"X":220.0,"Y":125.0}]},{"StartTime":223942.0,"Objects":[{"StartTime":223942.0,"EndTime":223942.0,"X":10.0,"Y":125.0}]},{"StartTime":224153.0,"Objects":[{"StartTime":224153.0,"EndTime":224153.0,"X":168.0,"Y":216.0}]},{"StartTime":224364.0,"Objects":[{"StartTime":224364.0,"EndTime":224364.0,"X":63.0,"Y":34.0}]},{"StartTime":224576.0,"Objects":[{"StartTime":224576.0,"EndTime":224576.0,"X":0.0,"Y":264.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":224751.0,"EndTime":224751.0,"X":93.40772,"Y":288.831,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":224998.0,"Objects":[{"StartTime":224998.0,"EndTime":224998.0,"X":144.0,"Y":140.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":225067.0,"EndTime":225067.0,"X":149.111465,"Y":87.74942,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":225209.0,"Objects":[{"StartTime":225209.0,"EndTime":225209.0,"X":208.0,"Y":304.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":225278.0,"EndTime":225278.0,"X":201.982239,"Y":356.153961,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":225421.0,"Objects":[{"StartTime":225421.0,"EndTime":225421.0,"X":256.0,"Y":144.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":225490.0,"EndTime":225490.0,"X":261.111481,"Y":91.74942,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":225632.0,"Objects":[{"StartTime":225632.0,"EndTime":225632.0,"X":320.0,"Y":308.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":225701.0,"EndTime":225701.0,"X":313.982239,"Y":360.153961,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":225843.0,"Objects":[{"StartTime":225843.0,"EndTime":225843.0,"X":425.0,"Y":265.0}]},{"StartTime":226055.0,"Objects":[{"StartTime":226055.0,"EndTime":226055.0,"X":256.0,"Y":188.0}]},{"StartTime":226266.0,"Objects":[{"StartTime":226266.0,"EndTime":226266.0,"X":425.0,"Y":102.0}]},{"StartTime":226477.0,"Objects":[{"StartTime":226477.0,"EndTime":226477.0,"X":299.0,"Y":248.0}]},{"StartTime":226688.0,"Objects":[{"StartTime":226688.0,"EndTime":226688.0,"X":271.0,"Y":53.0}]},{"StartTime":226900.0,"Objects":[{"StartTime":226900.0,"EndTime":226900.0,"X":369.0,"Y":225.0}]},{"StartTime":227111.0,"Objects":[{"StartTime":227111.0,"EndTime":227111.0,"X":176.0,"Y":183.0}]},{"StartTime":227322.0,"Objects":[{"StartTime":227322.0,"EndTime":227322.0,"X":369.0,"Y":151.0}]},{"StartTime":227533.0,"Objects":[{"StartTime":227533.0,"EndTime":227533.0,"X":274.0,"Y":339.0}]},{"StartTime":227745.0,"Objects":[{"StartTime":227745.0,"EndTime":227745.0,"X":307.0,"Y":116.0}]},{"StartTime":227956.0,"Objects":[{"StartTime":227956.0,"EndTime":227956.0,"X":458.0,"Y":279.0}]},{"StartTime":228168.0,"Objects":[{"StartTime":228168.0,"EndTime":228168.0,"X":256.0,"Y":187.0}]},{"StartTime":228379.0,"Objects":[{"StartTime":228379.0,"EndTime":228379.0,"X":458.0,"Y":83.0}]},{"StartTime":228590.0,"Objects":[{"StartTime":228590.0,"EndTime":228590.0,"X":308.0,"Y":256.0}]},{"StartTime":228801.0,"Objects":[{"StartTime":228801.0,"EndTime":228801.0,"X":274.0,"Y":25.0}]},{"StartTime":229013.0,"Objects":[{"StartTime":229013.0,"EndTime":229013.0,"X":391.0,"Y":231.0}]},{"StartTime":229224.0,"Objects":[{"StartTime":229224.0,"EndTime":229224.0,"X":160.0,"Y":181.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":229399.0,"EndTime":229399.0,"X":175.200348,"Y":84.64736,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":229646.0,"Objects":[{"StartTime":229646.0,"EndTime":229646.0,"X":257.0,"Y":263.0}]},{"StartTime":229858.0,"Objects":[{"StartTime":229858.0,"EndTime":229858.0,"X":288.0,"Y":39.0}]},{"StartTime":230069.0,"Objects":[{"StartTime":230069.0,"EndTime":230069.0,"X":348.0,"Y":227.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":230244.0,"EndTime":230244.0,"X":257.087128,"Y":263.065033,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":230491.0,"Objects":[{"StartTime":230491.0,"EndTime":230491.0,"X":366.0,"Y":100.0}]},{"StartTime":230703.0,"Objects":[{"StartTime":230703.0,"EndTime":230703.0,"X":160.0,"Y":181.0}]},{"StartTime":230914.0,"Objects":[{"StartTime":230914.0,"EndTime":230914.0,"X":288.0,"Y":39.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":231089.0,"EndTime":231089.0,"X":366.498749,"Y":100.621391,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":231336.0,"Objects":[{"StartTime":231336.0,"EndTime":231336.0,"X":175.0,"Y":84.0}]},{"StartTime":231547.0,"Objects":[{"StartTime":231547.0,"EndTime":231547.0,"X":348.0,"Y":227.0}]},{"StartTime":231759.0,"Objects":[{"StartTime":231759.0,"EndTime":231759.0,"X":184.0,"Y":336.0}]},{"StartTime":231864.0,"Objects":[{"StartTime":231864.0,"EndTime":231864.0,"X":181.0,"Y":283.0}]},{"StartTime":231970.0,"Objects":[{"StartTime":231970.0,"EndTime":231970.0,"X":179.0,"Y":231.0}]},{"StartTime":232075.0,"Objects":[{"StartTime":232075.0,"EndTime":232075.0,"X":176.0,"Y":178.0}]},{"StartTime":232181.0,"Objects":[{"StartTime":232181.0,"EndTime":232181.0,"X":174.0,"Y":126.0}]},{"StartTime":232393.0,"Objects":[{"StartTime":232393.0,"EndTime":232393.0,"X":366.0,"Y":100.0}]},{"StartTime":232604.0,"Objects":[{"StartTime":232604.0,"EndTime":232604.0,"X":268.0,"Y":228.0}]},{"StartTime":232815.0,"Objects":[{"StartTime":232815.0,"EndTime":232815.0,"X":412.0,"Y":280.0}]},{"StartTime":233026.0,"Objects":[{"StartTime":233026.0,"EndTime":233026.0,"X":268.0,"Y":188.0}]},{"StartTime":233237.0,"Objects":[{"StartTime":233237.0,"EndTime":233237.0,"X":451.0,"Y":187.0}]},{"StartTime":233449.0,"Objects":[{"StartTime":233449.0,"EndTime":233449.0,"X":256.0,"Y":152.0}]},{"StartTime":233660.0,"Objects":[{"StartTime":233660.0,"EndTime":233660.0,"X":473.0,"Y":113.0}]},{"StartTime":233871.0,"Objects":[{"StartTime":233871.0,"EndTime":233871.0,"X":328.0,"Y":248.0}]},{"StartTime":234082.0,"Objects":[{"StartTime":234082.0,"EndTime":234082.0,"X":289.0,"Y":31.0}]},{"StartTime":234294.0,"Objects":[{"StartTime":234294.0,"EndTime":234294.0,"X":192.0,"Y":204.0}]},{"StartTime":234505.0,"Objects":[{"StartTime":234505.0,"EndTime":234505.0,"X":410.0,"Y":241.0}]},{"StartTime":234716.0,"Objects":[{"StartTime":234716.0,"EndTime":234716.0,"X":112.0,"Y":188.0}]},{"StartTime":234927.0,"Objects":[{"StartTime":234927.0,"EndTime":234927.0,"X":305.0,"Y":297.0}]},{"StartTime":235139.0,"Objects":[{"StartTime":235139.0,"EndTime":235139.0,"X":36.0,"Y":176.0}]},{"StartTime":235350.0,"Objects":[{"StartTime":235350.0,"EndTime":235350.0,"X":181.0,"Y":344.0}]},{"StartTime":235562.0,"Objects":[{"StartTime":235562.0,"EndTime":235562.0,"X":252.0,"Y":136.0}]},{"StartTime":235773.0,"Objects":[{"StartTime":235773.0,"EndTime":235773.0,"X":84.0,"Y":281.0}]},{"StartTime":235984.0,"Objects":[{"StartTime":235984.0,"EndTime":235984.0,"X":316.0,"Y":188.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":236159.0,"EndTime":236159.0,"X":320.0774,"Y":88.93266,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":236407.0,"Objects":[{"StartTime":236407.0,"EndTime":236407.0,"X":328.0,"Y":268.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":236582.0,"EndTime":236582.0,"X":399.9171,"Y":200.393,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":236829.0,"Objects":[{"StartTime":236829.0,"EndTime":236829.0,"X":276.0,"Y":333.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":237004.0,"EndTime":237004.0,"X":374.878357,"Y":336.5995,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":237252.0,"Objects":[{"StartTime":237252.0,"EndTime":237252.0,"X":316.0,"Y":188.0}]},{"StartTime":237463.0,"Objects":[{"StartTime":237463.0,"EndTime":237463.0,"X":204.0,"Y":296.0}]},{"StartTime":237674.0,"Objects":[{"StartTime":237674.0,"EndTime":237674.0,"X":452.0,"Y":336.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":237849.0,"EndTime":237849.0,"X":469.90686,"Y":232.5382,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":238097.0,"Objects":[{"StartTime":238097.0,"EndTime":238097.0,"X":209.0,"Y":104.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":238272.0,"EndTime":238272.0,"X":227.870361,"Y":207.290421,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":238519.0,"Objects":[{"StartTime":238519.0,"EndTime":238519.0,"X":425.0,"Y":45.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":238588.0,"EndTime":238588.0,"X":477.25058,"Y":50.11147,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":238731.0,"Objects":[{"StartTime":238731.0,"EndTime":238731.0,"X":421.0,"Y":157.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":238800.0,"EndTime":238800.0,"X":473.25058,"Y":162.111465,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":238942.0,"Objects":[{"StartTime":238942.0,"EndTime":238942.0,"X":227.0,"Y":207.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":239011.0,"EndTime":239011.0,"X":174.833221,"Y":201.09433,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":239153.0,"Objects":[{"StartTime":239153.0,"EndTime":239153.0,"X":223.0,"Y":319.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":239222.0,"EndTime":239222.0,"X":170.833221,"Y":313.09433,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":239364.0,"Objects":[{"StartTime":239364.0,"EndTime":239364.0,"X":475.0,"Y":370.0}]},{"StartTime":239576.0,"Objects":[{"StartTime":239576.0,"EndTime":239576.0,"X":496.0,"Y":228.0}]},{"StartTime":239787.0,"Objects":[{"StartTime":239787.0,"EndTime":239787.0,"X":380.0,"Y":344.0}]},{"StartTime":239999.0,"Objects":[{"StartTime":239999.0,"EndTime":239999.0,"X":405.0,"Y":173.0}]},{"StartTime":240209.0,"Objects":[{"StartTime":240209.0,"EndTime":240209.0,"X":272.0,"Y":320.0}]},{"StartTime":240421.0,"Objects":[{"StartTime":240421.0,"EndTime":240421.0,"X":302.0,"Y":114.0}]},{"StartTime":240632.0,"Objects":[{"StartTime":240632.0,"EndTime":240632.0,"X":156.0,"Y":300.0}]},{"StartTime":240844.0,"Objects":[{"StartTime":240844.0,"EndTime":240844.0,"X":192.0,"Y":52.0}]},{"StartTime":241055.0,"Objects":[{"StartTime":241055.0,"EndTime":241055.0,"X":20.0,"Y":164.0}]},{"StartTime":241267.0,"Objects":[{"StartTime":241267.0,"EndTime":241267.0,"X":252.0,"Y":84.0}]},{"StartTime":241477.0,"Objects":[{"StartTime":241477.0,"EndTime":241477.0,"X":40.0,"Y":8.0}]},{"StartTime":241689.0,"Objects":[{"StartTime":241689.0,"EndTime":241689.0,"X":240.0,"Y":164.0}]},{"StartTime":241900.0,"Objects":[{"StartTime":241900.0,"EndTime":241900.0,"X":116.0,"Y":28.0}]},{"StartTime":242111.0,"Objects":[{"StartTime":242111.0,"EndTime":242111.0,"X":80.0,"Y":274.0}]},{"StartTime":242322.0,"Objects":[{"StartTime":242322.0,"EndTime":242322.0,"X":32.0,"Y":88.0}]},{"StartTime":242534.0,"Objects":[{"StartTime":242534.0,"EndTime":242534.0,"X":227.0,"Y":242.0}]},{"StartTime":242745.0,"Objects":[{"StartTime":242745.0,"EndTime":242745.0,"X":218.0,"Y":61.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":242920.0,"EndTime":242920.0,"X":239.304214,"Y":163.81601,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":243167.0,"Objects":[{"StartTime":243167.0,"EndTime":243167.0,"X":131.0,"Y":120.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":243342.0,"EndTime":243342.0,"X":31.3882523,"Y":86.79608,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":243590.0,"Objects":[{"StartTime":243590.0,"EndTime":243590.0,"X":292.0,"Y":32.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":243765.0,"EndTime":243765.0,"X":313.30423,"Y":134.81601,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":244012.0,"Objects":[{"StartTime":244012.0,"EndTime":244012.0,"X":132.0,"Y":204.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":244187.0,"EndTime":244187.0,"X":32.3882523,"Y":170.796082,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":244435.0,"Objects":[{"StartTime":244435.0,"EndTime":244435.0,"X":368.0,"Y":4.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":244821.0,"EndTime":244821.0,"X":393.857056,"Y":151.754578,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":245280.0,"Objects":[{"StartTime":245280.0,"EndTime":245280.0,"X":136.0,"Y":288.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":245560.0,"EndTime":245560.0,"X":28.55529,"Y":254.65509,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":245702.0,"Objects":[{"StartTime":245702.0,"EndTime":245702.0,"X":31.7391319,"Y":257.739136}]},{"StartTime":245914.0,"Objects":[{"StartTime":245914.0,"EndTime":245914.0,"X":196.521729,"Y":236.521729}]},{"StartTime":246020.0,"Objects":[{"StartTime":246020.0,"EndTime":246020.0,"X":200.260864,"Y":240.260864}]},{"StartTime":246125.0,"Objects":[{"StartTime":246125.0,"EndTime":246125.0,"X":204.0,"Y":244.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":246300.0,"EndTime":246300.0,"X":198.885376,"Y":339.263245,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":246547.0,"Objects":[{"StartTime":246547.0,"EndTime":246547.0,"X":100.0,"Y":188.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":246722.0,"EndTime":246722.0,"X":93.48614,"Y":92.7003,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":246970.0,"Objects":[{"StartTime":246970.0,"EndTime":246970.0,"X":120.0,"Y":272.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":247145.0,"EndTime":247145.0,"X":24.73676,"Y":266.885345,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":247393.0,"Objects":[{"StartTime":247393.0,"EndTime":247393.0,"X":176.0,"Y":160.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":247568.0,"EndTime":247568.0,"X":271.263245,"Y":165.114624,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":247815.0,"Objects":[{"StartTime":247815.0,"EndTime":247815.0,"X":277.0,"Y":260.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":247990.0,"EndTime":247990.0,"X":270.486145,"Y":164.7003,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":248238.0,"Objects":[{"StartTime":248238.0,"EndTime":248238.0,"X":357.0,"Y":288.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":248413.0,"EndTime":248413.0,"X":276.222839,"Y":340.022369,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":248660.0,"Objects":[{"StartTime":248660.0,"EndTime":248660.0,"X":341.0,"Y":208.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":248835.0,"EndTime":248835.0,"X":425.827118,"Y":250.5753,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":249083.0,"Objects":[{"StartTime":249083.0,"EndTime":249083.0,"X":276.0,"Y":340.0}]},{"StartTime":249294.0,"Objects":[{"StartTime":249294.0,"EndTime":249294.0,"X":341.0,"Y":208.0}]},{"StartTime":249505.0,"Objects":[{"StartTime":249505.0,"EndTime":249505.0,"X":200.0,"Y":120.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":249680.0,"EndTime":249680.0,"X":101.756859,"Y":119.9113,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":249928.0,"Objects":[{"StartTime":249928.0,"EndTime":249928.0,"X":64.0,"Y":300.0}]},{"StartTime":250139.0,"Objects":[{"StartTime":250139.0,"EndTime":250139.0,"X":152.0,"Y":176.0}]},{"StartTime":250350.0,"Objects":[{"StartTime":250350.0,"EndTime":250350.0,"X":12.0,"Y":196.0}]},{"StartTime":250561.0,"Objects":[{"StartTime":250561.0,"EndTime":250561.0,"X":164.0,"Y":210.0}]},{"StartTime":250773.0,"Objects":[{"StartTime":250773.0,"EndTime":250773.0,"X":32.0,"Y":88.0}]},{"StartTime":250984.0,"Objects":[{"StartTime":250984.0,"EndTime":250984.0,"X":49.0,"Y":269.0}]},{"StartTime":251195.0,"Objects":[{"StartTime":251195.0,"EndTime":251195.0,"X":218.0,"Y":129.0}]},{"StartTime":251406.0,"Objects":[{"StartTime":251406.0,"EndTime":251406.0,"X":293.0,"Y":294.0}]},{"StartTime":251618.0,"Objects":[{"StartTime":251618.0,"EndTime":251618.0,"X":341.0,"Y":84.0}]},{"StartTime":251829.0,"Objects":[{"StartTime":251829.0,"EndTime":251829.0,"X":164.0,"Y":210.0}]},{"StartTime":252040.0,"Objects":[{"StartTime":252040.0,"EndTime":252040.0,"X":400.0,"Y":176.0}]},{"StartTime":252251.0,"Objects":[{"StartTime":252251.0,"EndTime":252251.0,"X":232.0,"Y":80.0}]},{"StartTime":252463.0,"Objects":[{"StartTime":252463.0,"EndTime":252463.0,"X":340.0,"Y":272.0}]},{"StartTime":252674.0,"Objects":[{"StartTime":252674.0,"EndTime":252674.0,"X":456.0,"Y":80.0}]},{"StartTime":252885.0,"Objects":[{"StartTime":252885.0,"EndTime":252885.0,"X":452.0,"Y":316.0}]},{"StartTime":253307.0,"Objects":[{"StartTime":253307.0,"EndTime":253307.0,"X":452.0,"Y":316.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":253482.0,"EndTime":253482.0,"X":474.438171,"Y":213.4255,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":253730.0,"Objects":[{"StartTime":253730.0,"EndTime":253730.0,"X":284.0,"Y":220.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":253905.0,"EndTime":253905.0,"X":306.438171,"Y":117.4255,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":254153.0,"Objects":[{"StartTime":254153.0,"EndTime":254153.0,"X":116.0,"Y":132.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":254328.0,"EndTime":254328.0,"X":138.438171,"Y":29.425499,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":254576.0,"Objects":[{"StartTime":254576.0,"EndTime":254576.0,"X":36.0,"Y":236.0}]},{"StartTime":254998.0,"Objects":[{"StartTime":254998.0,"EndTime":254998.0,"X":36.0,"Y":236.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":255173.0,"EndTime":255173.0,"X":111.50975,"Y":251.103058,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":255421.0,"Objects":[{"StartTime":255421.0,"EndTime":255421.0,"X":204.0,"Y":152.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":255596.0,"EndTime":255596.0,"X":279.509766,"Y":167.103058,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":255843.0,"Objects":[{"StartTime":255843.0,"EndTime":255843.0,"X":356.0,"Y":56.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":256018.0,"EndTime":256018.0,"X":431.509766,"Y":71.10306,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":256266.0,"Objects":[{"StartTime":256266.0,"EndTime":256266.0,"X":356.0,"Y":204.0}]},{"StartTime":256688.0,"Objects":[{"StartTime":256688.0,"EndTime":256688.0,"X":356.0,"Y":204.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":256863.0,"EndTime":256863.0,"X":358.602356,"Y":299.339142,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":257111.0,"Objects":[{"StartTime":257111.0,"EndTime":257111.0,"X":252.0,"Y":184.0}]},{"StartTime":257322.0,"Objects":[{"StartTime":257322.0,"EndTime":257322.0,"X":296.0,"Y":340.0}]},{"StartTime":257533.0,"Objects":[{"StartTime":257533.0,"EndTime":257533.0,"X":192.0,"Y":272.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":257708.0,"EndTime":257708.0,"X":295.660339,"Y":255.2806,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":257956.0,"Objects":[{"StartTime":257956.0,"EndTime":257956.0,"X":117.0,"Y":119.0}]},{"StartTime":258167.0,"Objects":[{"StartTime":258167.0,"EndTime":258167.0,"X":285.0,"Y":31.0}]},{"StartTime":258378.0,"Objects":[{"StartTime":258378.0,"EndTime":258378.0,"X":137.0,"Y":31.0}]},{"StartTime":258589.0,"Objects":[{"StartTime":258589.0,"EndTime":258589.0,"X":305.0,"Y":119.0}]},{"StartTime":258801.0,"Objects":[{"StartTime":258801.0,"EndTime":258801.0,"X":49.0,"Y":55.0}]},{"StartTime":258906.0,"Objects":[{"StartTime":258906.0,"EndTime":258906.0,"X":26.0,"Y":101.0}]},{"StartTime":259012.0,"Objects":[{"StartTime":259012.0,"EndTime":259012.0,"X":32.0,"Y":153.0}]},{"StartTime":259117.0,"Objects":[{"StartTime":259117.0,"EndTime":259117.0,"X":64.0,"Y":194.0}]},{"StartTime":259223.0,"Objects":[{"StartTime":259223.0,"EndTime":259223.0,"X":112.0,"Y":212.0}]},{"StartTime":259435.0,"Objects":[{"StartTime":259435.0,"EndTime":259435.0,"X":255.0,"Y":75.0}]},{"StartTime":259646.0,"Objects":[{"StartTime":259646.0,"EndTime":259646.0,"X":240.0,"Y":252.0}]},{"StartTime":259857.0,"Objects":[{"StartTime":259857.0,"EndTime":259857.0,"X":112.0,"Y":212.0}]},{"StartTime":260068.0,"Objects":[{"StartTime":260068.0,"EndTime":260068.0,"X":236.0,"Y":330.0}]},{"StartTime":260280.0,"Objects":[{"StartTime":260280.0,"EndTime":260280.0,"X":114.0,"Y":133.0}]},{"StartTime":260491.0,"Objects":[{"StartTime":260491.0,"EndTime":260491.0,"X":146.0,"Y":308.0}]},{"StartTime":260702.0,"Objects":[{"StartTime":260702.0,"EndTime":260702.0,"X":204.0,"Y":154.0}]},{"StartTime":260914.0,"Objects":[{"StartTime":260914.0,"EndTime":260914.0,"X":51.0,"Y":304.0}]},{"StartTime":261125.0,"Objects":[{"StartTime":261125.0,"EndTime":261125.0,"X":298.0,"Y":156.0}]},{"StartTime":261336.0,"Objects":[{"StartTime":261336.0,"EndTime":261336.0,"X":28.0,"Y":232.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":261511.0,"EndTime":261511.0,"X":26.3694744,"Y":134.648315,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":261759.0,"Objects":[{"StartTime":261759.0,"EndTime":261759.0,"X":320.0,"Y":228.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":261934.0,"EndTime":261934.0,"X":321.6305,"Y":325.351685,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":262181.0,"Objects":[{"StartTime":262181.0,"EndTime":262181.0,"X":64.0,"Y":208.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":262356.0,"EndTime":262356.0,"X":59.4033928,"Y":109.17572,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":262604.0,"Objects":[{"StartTime":262604.0,"EndTime":262604.0,"X":364.0,"Y":248.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":262779.0,"EndTime":262779.0,"X":367.656372,"Y":346.437134,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":263026.0,"Objects":[{"StartTime":263026.0,"EndTime":263026.0,"X":484.0,"Y":148.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":263306.0,"EndTime":263306.0,"X":348.198273,"Y":146.574341,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":263449.0,"Objects":[{"StartTime":263449.0,"EndTime":263449.0,"X":315.0,"Y":131.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":263624.0,"EndTime":263624.0,"X":216.875748,"Y":124.6084,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":263871.0,"Objects":[{"StartTime":263871.0,"EndTime":263871.0,"X":192.0,"Y":300.0}]},{"StartTime":264083.0,"Objects":[{"StartTime":264083.0,"EndTime":264083.0,"X":264.0,"Y":188.0}]},{"StartTime":264294.0,"Objects":[{"StartTime":264294.0,"EndTime":264294.0,"X":172.0,"Y":208.0}]},{"StartTime":264506.0,"Objects":[{"StartTime":264506.0,"EndTime":264506.0,"X":284.0,"Y":280.0}]},{"StartTime":264716.0,"Objects":[{"StartTime":264716.0,"EndTime":264716.0,"X":160.0,"Y":44.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":264996.0,"EndTime":264996.0,"X":161.425659,"Y":179.801727,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":265139.0,"Objects":[{"StartTime":265139.0,"EndTime":265139.0,"X":172.0,"Y":208.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":265314.0,"EndTime":265314.0,"X":163.511826,"Y":305.6148,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":265562.0,"Objects":[{"StartTime":265562.0,"EndTime":265562.0,"X":104.0,"Y":252.0}]},{"StartTime":265773.0,"Objects":[{"StartTime":265773.0,"EndTime":265773.0,"X":264.0,"Y":352.0}]},{"StartTime":265984.0,"Objects":[{"StartTime":265984.0,"EndTime":265984.0,"X":76.0,"Y":352.0}]},{"StartTime":266195.0,"Objects":[{"StartTime":266195.0,"EndTime":266195.0,"X":248.0,"Y":252.0}]},{"StartTime":266407.0,"Objects":[{"StartTime":266407.0,"EndTime":266407.0,"X":132.0,"Y":112.0}]},{"StartTime":266618.0,"Objects":[{"StartTime":266618.0,"EndTime":266618.0,"X":22.0,"Y":288.0}]},{"StartTime":266829.0,"Objects":[{"StartTime":266829.0,"EndTime":266829.0,"X":22.0,"Y":81.0}]},{"StartTime":267040.0,"Objects":[{"StartTime":267040.0,"EndTime":267040.0,"X":132.0,"Y":270.0}]},{"StartTime":267252.0,"Objects":[{"StartTime":267252.0,"EndTime":267252.0,"X":240.0,"Y":112.0}]},{"StartTime":267463.0,"Objects":[{"StartTime":267463.0,"EndTime":267463.0,"X":350.0,"Y":288.0}]},{"StartTime":267674.0,"Objects":[{"StartTime":267674.0,"EndTime":267674.0,"X":350.0,"Y":81.0}]},{"StartTime":267885.0,"Objects":[{"StartTime":267885.0,"EndTime":267885.0,"X":240.0,"Y":270.0}]},{"StartTime":268097.0,"Objects":[{"StartTime":268097.0,"EndTime":268097.0,"X":512.0,"Y":212.0}]},{"StartTime":268308.0,"Objects":[{"StartTime":268308.0,"EndTime":268308.0,"X":290.0,"Y":94.0}]},{"StartTime":268519.0,"Objects":[{"StartTime":268519.0,"EndTime":268519.0,"X":415.0,"Y":310.0}]},{"StartTime":268730.0,"Objects":[{"StartTime":268730.0,"EndTime":268730.0,"X":417.0,"Y":47.0}]},{"StartTime":268942.0,"Objects":[{"StartTime":268942.0,"EndTime":268942.0,"X":168.0,"Y":180.0}]},{"StartTime":269153.0,"Objects":[{"StartTime":269153.0,"EndTime":269153.0,"X":416.0,"Y":214.0}]},{"StartTime":269364.0,"Objects":[{"StartTime":269364.0,"EndTime":269364.0,"X":225.0,"Y":54.0}]},{"StartTime":269576.0,"Objects":[{"StartTime":269576.0,"EndTime":269576.0,"X":313.0,"Y":302.0}]},{"StartTime":269787.0,"Objects":[{"StartTime":269787.0,"EndTime":269787.0,"X":376.0,"Y":172.0}]},{"StartTime":269998.0,"Objects":[{"StartTime":269998.0,"EndTime":269998.0,"X":177.0,"Y":242.0}]},{"StartTime":270209.0,"Objects":[{"StartTime":270209.0,"EndTime":270209.0,"X":345.0,"Y":147.0}]},{"StartTime":270420.0,"Objects":[{"StartTime":270420.0,"EndTime":270420.0,"X":215.0,"Y":254.0}]},{"StartTime":270632.0,"Objects":[{"StartTime":270632.0,"EndTime":270632.0,"X":325.0,"Y":146.0}]},{"StartTime":270843.0,"Objects":[{"StartTime":270843.0,"EndTime":270843.0,"X":237.0,"Y":249.0}]},{"StartTime":271055.0,"Objects":[{"StartTime":271055.0,"EndTime":271055.0,"X":333.0,"Y":238.0}]},{"StartTime":271266.0,"Objects":[{"StartTime":271266.0,"EndTime":271266.0,"X":230.0,"Y":151.0}]},{"StartTime":271477.0,"Objects":[{"StartTime":271477.0,"EndTime":271477.0,"X":292.0,"Y":312.0}]},{"StartTime":271583.0,"Objects":[{"StartTime":271583.0,"EndTime":272745.0,"X":256.0,"Y":192.0}]},{"StartTime":273167.0,"Objects":[{"StartTime":273167.0,"EndTime":273167.0,"X":163.0,"Y":256.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":273342.0,"EndTime":273342.0,"X":78.18209,"Y":248.905472,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":273590.0,"Objects":[{"StartTime":273590.0,"EndTime":273590.0,"X":68.0,"Y":364.0}]},{"StartTime":273801.0,"Objects":[{"StartTime":273801.0,"EndTime":273801.0,"X":236.0,"Y":324.0}]},{"StartTime":274012.0,"Objects":[{"StartTime":274012.0,"EndTime":274012.0,"X":79.0,"Y":249.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":274187.0,"EndTime":274187.0,"X":88.9388351,"Y":159.550461,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":274435.0,"Objects":[{"StartTime":274435.0,"EndTime":274435.0,"X":280.0,"Y":264.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":274610.0,"EndTime":274610.0,"X":289.938843,"Y":353.449524,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":274857.0,"Objects":[{"StartTime":274857.0,"EndTime":274857.0,"X":420.0,"Y":130.0}]},{"StartTime":275068.0,"Objects":[{"StartTime":275068.0,"EndTime":275068.0,"X":373.0,"Y":261.0}]},{"StartTime":275279.0,"Objects":[{"StartTime":275279.0,"EndTime":275279.0,"X":512.0,"Y":227.0}]},{"StartTime":275491.0,"Objects":[{"StartTime":275491.0,"EndTime":275491.0,"X":354.0,"Y":183.0}]},{"StartTime":275702.0,"Objects":[{"StartTime":275702.0,"EndTime":275702.0,"X":308.0,"Y":358.0}]},{"StartTime":275913.0,"Objects":[{"StartTime":275913.0,"EndTime":275913.0,"X":478.0,"Y":313.0}]},{"StartTime":276125.0,"Objects":[{"StartTime":276125.0,"EndTime":276125.0,"X":245.0,"Y":278.0}]},{"StartTime":276336.0,"Objects":[{"StartTime":276336.0,"EndTime":276336.0,"X":482.0,"Y":205.0}]},{"StartTime":276547.0,"Objects":[{"StartTime":276547.0,"EndTime":276547.0,"X":349.0,"Y":94.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":276722.0,"EndTime":276722.0,"X":354.7944,"Y":183.813278,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":276970.0,"Objects":[{"StartTime":276970.0,"EndTime":276970.0,"X":239.0,"Y":240.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":277145.0,"EndTime":277145.0,"X":157.7837,"Y":226.0501,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":277393.0,"Objects":[{"StartTime":277393.0,"EndTime":277393.0,"X":0.0,"Y":268.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":277568.0,"EndTime":277568.0,"X":81.70373,"Y":254.311035,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":277815.0,"Objects":[{"StartTime":277815.0,"EndTime":277815.0,"X":128.0,"Y":380.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":277990.0,"EndTime":277990.0,"X":143.305069,"Y":299.2002,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":278237.0,"Objects":[{"StartTime":278237.0,"EndTime":278237.0,"X":116.0,"Y":96.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":278412.0,"EndTime":278412.0,"X":101.614624,"Y":177.390518,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":278660.0,"Objects":[{"StartTime":278660.0,"EndTime":278660.0,"X":104.0,"Y":16.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":278835.0,"EndTime":278835.0,"X":36.5809135,"Y":63.969265,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":279082.0,"Objects":[{"StartTime":279082.0,"EndTime":279082.0,"X":180.0,"Y":48.0}]},{"StartTime":279294.0,"Objects":[{"StartTime":279294.0,"EndTime":279294.0,"X":32.0,"Y":140.0}]},{"StartTime":279505.0,"Objects":[{"StartTime":279505.0,"EndTime":279505.0,"X":180.0,"Y":48.0}]},{"StartTime":279717.0,"Objects":[{"StartTime":279717.0,"EndTime":279717.0,"X":140.0,"Y":216.0}]},{"StartTime":279928.0,"Objects":[{"StartTime":279928.0,"EndTime":279928.0,"X":265.0,"Y":71.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":280103.0,"EndTime":280103.0,"X":243.523376,"Y":153.8613,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":280350.0,"Objects":[{"StartTime":280350.0,"EndTime":280350.0,"X":416.0,"Y":248.0}]},{"StartTime":280562.0,"Objects":[{"StartTime":280562.0,"EndTime":280562.0,"X":316.0,"Y":132.0}]},{"StartTime":280773.0,"Objects":[{"StartTime":280773.0,"EndTime":280773.0,"X":252.0,"Y":264.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":280948.0,"EndTime":280948.0,"X":341.449524,"Y":254.061157,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":281196.0,"Objects":[{"StartTime":281196.0,"EndTime":281196.0,"X":484.0,"Y":148.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":281371.0,"EndTime":281371.0,"X":394.550476,"Y":138.061157,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":281618.0,"Objects":[{"StartTime":281618.0,"EndTime":281618.0,"X":426.0,"Y":338.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":281793.0,"EndTime":281793.0,"X":416.945068,"Y":248.456665,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":282041.0,"Objects":[{"StartTime":282041.0,"EndTime":282041.0,"X":326.0,"Y":43.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":282216.0,"EndTime":282216.0,"X":316.061157,"Y":132.449539,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":282463.0,"Objects":[{"StartTime":282463.0,"EndTime":282463.0,"X":296.0,"Y":296.0}]},{"StartTime":282674.0,"Objects":[{"StartTime":282674.0,"EndTime":282674.0,"X":417.0,"Y":249.0}]},{"StartTime":282885.0,"Objects":[{"StartTime":282885.0,"EndTime":282885.0,"X":248.0,"Y":216.0}]},{"StartTime":283097.0,"Objects":[{"StartTime":283097.0,"EndTime":283097.0,"X":321.0,"Y":376.0}]},{"StartTime":283308.0,"Objects":[{"StartTime":283308.0,"EndTime":283308.0,"X":370.0,"Y":163.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":283483.0,"EndTime":283483.0,"X":379.938843,"Y":73.55046,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":283730.0,"Objects":[{"StartTime":283730.0,"EndTime":283730.0,"X":248.0,"Y":216.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":283905.0,"EndTime":283905.0,"X":257.938843,"Y":126.550461,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":284153.0,"Objects":[{"StartTime":284153.0,"EndTime":284153.0,"X":122.0,"Y":266.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":284328.0,"EndTime":284328.0,"X":131.938843,"Y":176.550461,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":284575.0,"Objects":[{"StartTime":284575.0,"EndTime":284575.0,"X":200.0,"Y":280.0}]},{"StartTime":284787.0,"Objects":[{"StartTime":284787.0,"EndTime":284787.0,"X":56.0,"Y":144.0}]},{"StartTime":284998.0,"Objects":[{"StartTime":284998.0,"EndTime":284998.0,"X":69.0,"Y":335.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":285173.0,"EndTime":285173.0,"X":151.50708,"Y":340.3292,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":285420.0,"Objects":[{"StartTime":285420.0,"EndTime":285420.0,"X":213.0,"Y":180.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":285595.0,"EndTime":285595.0,"X":130.326477,"Y":176.450455,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":285843.0,"Objects":[{"StartTime":285843.0,"EndTime":285843.0,"X":304.0,"Y":272.0}]},{"StartTime":285948.0,"Objects":[{"StartTime":285948.0,"EndTime":285948.0,"X":299.0,"Y":228.0}]},{"StartTime":286054.0,"Objects":[{"StartTime":286054.0,"EndTime":286054.0,"X":294.0,"Y":183.0}]},{"StartTime":286159.0,"Objects":[{"StartTime":286159.0,"EndTime":286159.0,"X":288.0,"Y":138.0}]},{"StartTime":286265.0,"Objects":[{"StartTime":286265.0,"EndTime":286265.0,"X":283.0,"Y":94.0}]},{"StartTime":286477.0,"Objects":[{"StartTime":286477.0,"EndTime":286477.0,"X":156.521729,"Y":44.5217361}]},{"StartTime":286583.0,"Objects":[{"StartTime":286583.0,"EndTime":286583.0,"X":160.260864,"Y":48.2608681}]},{"StartTime":286688.0,"Objects":[{"StartTime":286688.0,"EndTime":286688.0,"X":164.0,"Y":52.0,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":287110.0,"EndTime":287110.0,"X":183.3807,"Y":124.354652,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":287533.0,"EndTime":287533.0,"X":172.208191,"Y":190.150177,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":287955.0,"EndTime":287955.0,"X":124.254967,"Y":247.694046,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":288378.0,"EndTime":288378.0,"X":173.0462,"Y":261.451965,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":288800.0,"EndTime":288800.0,"X":242.3152,"Y":273.1244,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":289223.0,"EndTime":289223.0,"X":282.0523,"Y":336.8299,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":289645.0,"EndTime":289645.0,"X":313.3097,"Y":323.5751,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":290068.0,"EndTime":290068.0,"X":338.3643,"Y":252.795883,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":290490.0,"EndTime":290490.0,"X":410.361755,"Y":235.620316,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":290913.0,"EndTime":290913.0,"X":431.88385,"Y":207.80217,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":291335.0,"EndTime":291335.0,"X":373.0279,"Y":161.46875,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":291758.0,"EndTime":291758.0,"X":367.150818,"Y":92.54223,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":292180.0,"EndTime":292180.0,"X":357.807159,"Y":45.76682,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":292603.0,"EndTime":292603.0,"X":294.6491,"Y":86.36842,"StackOffset":{"X":0.0,"Y":0.0}},{"StartTime":292990.0,"EndTime":292990.0,"X":228.255249,"Y":76.85775,"StackOffset":{"X":0.0,"Y":0.0}}]},{"StartTime":293238.0,"Objects":[{"StartTime":293238.0,"EndTime":293238.0,"X":231.739136,"Y":79.7391357}]},{"StartTime":293343.0,"Objects":[{"StartTime":293343.0,"EndTime":301900.0,"X":256.0,"Y":192.0}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/1124896.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/1124896.osu new file mode 100644 index 0000000000..8a9b18ae9c --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/1124896.osu @@ -0,0 +1,1122 @@ +osu file format v14 + +[General] +StackLeniency: 0.6 +Mode: 0 + +[Difficulty] +HPDrainRate:6 +CircleSize:3.8 +OverallDifficulty:7.5 +ApproachRate:8.7 +SliderMultiplier:1.5 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +1055,422.535211267606,4,2,1,35,1,0 +1055,-111.111111111111,4,2,1,35,0,0 +8660,-111.111111111111,4,2,1,10,0,0 +8871,-111.111111111111,4,2,1,35,0,0 +13942,-111.111111111111,4,2,2,60,0,0 +14470,-111.111111111111,4,2,2,5,0,0 +14576,-100,4,2,2,45,0,0 +25562,-200,4,2,2,40,0,0 +28097,-100,4,2,2,40,0,0 +41618,-100,4,2,2,50,0,0 +55139,-100,4,2,3,45,0,0 +68660,-83.3333333333333,4,2,2,50,0,0 +69294,-50,4,2,2,50,0,0 +69505,-83.3333333333333,4,2,2,50,0,0 +70139,-50,4,2,2,50,0,0 +70350,-83.3333333333333,4,2,2,50,0,0 +70984,-50,4,2,2,50,0,0 +71195,-83.3333333333333,4,2,2,50,0,0 +71829,-50,4,2,2,50,0,0 +72040,-83.3333333333333,4,2,2,50,0,0 +72674,-50,4,2,2,50,0,0 +74576,-55.5555555555556,4,2,1,50,0,0 +75421,-76.9230769230769,4,2,2,60,0,1 +100773,-100,4,2,2,50,0,0 +102463,-83.3333333333333,4,2,2,50,0,0 +115984,-100,4,2,2,50,0,0 +129505,-100,4,2,3,45,0,0 +143026,-83.3333333333333,4,2,2,50,0,0 +143660,-50,4,2,2,50,0,0 +143871,-83.3333333333333,4,2,2,50,0,0 +144505,-50,4,2,2,50,0,0 +144716,-83.3333333333333,4,2,2,50,0,0 +145350,-50,4,2,2,50,0,0 +145562,-83.3333333333333,4,2,2,50,0,0 +146195,-50,4,2,2,50,0,0 +146407,-83.3333333333333,4,2,2,50,0,0 +147040,-50,4,2,2,50,0,0 +148942,-55.5555555555556,4,2,1,50,0,0 +149787,-76.9230769230769,4,2,2,60,0,1 +175139,-100,4,2,2,50,0,0 +175562,-83.3333333333333,4,2,3,50,0,0 +185280,-76.9230769230769,4,2,3,50,0,0 +186970,-71.4285714285714,4,2,3,50,0,0 +190350,-83.3333333333333,4,2,2,50,0,0 +214012,-71.4285714285714,4,2,2,50,0,0 +219083,-71.4285714285714,4,2,2,60,0,1 +244435,-100,4,2,2,50,0,0 +246125,-71.4285714285714,4,2,2,60,0,1 +273167,-83.3333333333333,4,2,2,50,0,0 +286688,-200,4,2,2,50,0,0 +293238,-200,4,2,0,30,0,0 +293343,-200,4,2,0,5,0,0 + +[HitObjects] +92,96,633,5,0,0:0:0:0: +92,96,844,1,0,0:0:0:0: +92,96,1055,6,0,L|76:164,1,67.4999979400635,2|0,3:0|0:0,0:0:0:0: +200,100,1477,2,0,L|184:34,1,67.4999979400635,0|2,0:0|0:3,0:0:0:0: +164,228,1900,1,0,0:0:0:0: +256,240,2111,1,0,0:0:0:0: +340,192,2322,2,0,P|352:160|348:120,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +440,200,2745,6,0,P|438:233|450:264,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +332,316,3167,1,0,0:0:0:0: +332,316,3378,2,0,B|280:296|224:320|224:320|268:344,1,134.999995880127,2|0,0:3|0:0,0:0:0:0: +332,316,4012,1,2,0:3:0:0: +312,224,4224,1,0,0:0:0:0: +284,132,4435,6,0,P|248:124|216:132,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +400,192,4857,2,0,P|436:200|468:192,1,67.4999979400635,2|0,0:3|0:3,0:3:0:0: +312,224,5280,2,0,P|304:260|312:292,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +376,108,5702,1,2,0:3:0:0: +376,108,5914,2,0,B|336:132|336:132|232:108,1,134.999995880127,2|0,0:3|0:0,0:0:0:0: +154,122,6547,6,0,P|159:80|174:56,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +107,195,6970,2,0,P|73:160|68:132,2,67.4999979400635,2|0|2,0:3|0:0|0:3,0:0:0:0: +216,232,7604,1,0,0:0:0:0: +116,280,7815,6,0,P|76:280|51:263,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +176,160,8238,1,0,0:0:0:0: +248,291,8449,2,0,P|292:291|336:335,1,101.249996910095,2|0,0:3|0:0,0:0:0:0: +334,328,8871,2,0,L|318:189,1,134.999995880127,2|0,0:3|0:0,0:0:0:0: +428,184,9505,6,0,L|436:250,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +328,128,9928,2,0,L|319:194,1,67.4999979400635,0|2,0:0|0:3,0:0:0:0: +320,108,10350,1,0,0:0:0:0: +308,88,10773,1,2,0:3:0:0: +296,68,11195,6,0,L|212:64,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +318,194,11618,1,0,0:0:0:0: +288,52,11829,2,0,L|204:48,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +236,248,12252,1,0,0:0:0:0: +299,170,12463,1,2,0:3:0:0: +300,300,12674,1,0,0:0:0:0: +168,204,12885,6,0,L|84:200,2,67.4999979400635,2|0|0,0:3|0:0|0:0,0:0:0:0: +227,332,13519,2,0,L|160:336,1,67.4999979400635,2|0,0:3|0:0,0:0:0:0: +303,366,13942,1,2,0:0:0:0: +302,365,14153,2,0,P|308:332|299:299,1,67.4999979400635,2|0,0:0|0:0,0:0:0:0: +469,258,14576,6,0,L|452:333,1,75,4|0,0:0|0:0,0:0:0:0: +376,256,14998,2,0,L|359:182,1,75,0|2,0:0|0:0,0:0:0:0: +384,80,15421,1,0,0:0:0:0: +282,102,15632,1,2,0:0:0:0: +436,148,15843,1,0,0:0:0:0: +274,186,16055,5,2,0:0:0:0: +274,186,16160,1,0,0:0:0:0: +274,186,16266,2,0,L|257:261,1,75,0|2,0:0|0:0,0:0:0:0: +160,202,16688,2,0,L|143:128,1,75,0|2,0:0|0:0,0:0:0:0: +79,35,17111,1,0,0:0:0:0: +23,123,17322,1,2,0:0:0:0: +161,42,17533,1,0,0:0:0:0: +76,188,17745,1,2,0:0:0:0: +79,35,17956,6,0,L|105:126,1,75,0|2,0:0|0:0,0:0:0:0: +211,104,18378,2,0,L|237:195,1,75,0|2,0:0|0:0,0:0:0:0: +344,170,18801,2,0,L|370:261,1,75,0|2,0:0|0:0,0:0:0:0: +433,132,19224,1,0,0:0:0:0: +372,249,19435,5,2,0:0:0:0: +372,249,19540,1,0,0:0:0:0: +372,249,19646,2,0,P|414:259|452:250,1,75,0|2,0:0|0:0,0:0:0:0: +468,104,20069,1,0,0:0:0:0: +413,180,20280,1,2,0:0:0:0: +324,58,20491,1,2,0:0:0:0: +414,31,20702,1,2,0:0:0:0: +324,151,20914,1,10,0:0:0:0: +244,40,21125,1,2,0:0:0:0: +301,186,21336,6,0,P|242:205|184:174,1,112.5,6|0,0:0|0:0,0:0:0:0: +197,187,21759,2,0,P|190:229|215:287,1,75,0|2,0:0|0:0,0:0:0:0: +287,362,22181,1,0,0:0:0:0: +330,234,22393,1,2,0:0:0:0: +197,260,22604,1,8,0:0:0:0: +360,319,22815,1,2,0:0:0:0: +360,319,23026,6,0,P|419:338|479:313,1,112.5 +465,323,23449,1,0,0:0:0:0: +402,180,23660,1,2,0:0:0:0: +402,180,23871,2,0,L|417:265,1,75,0|2,0:0|0:0,0:0:0:0: +314,145,24294,6,0,L|327:71,1,75,8|2,0:0|0:0,0:0:0:0: +472,72,24716,2,0,L|485:145,1,75,4|2,0:0|0:0,0:0:0:0: +320,222,25139,1,0,0:0:0:0: +235,116,25350,1,2,0:0:0:0: +276,295,25562,5,8,0:0:0:0: +304,305,25667,1,8,0:0:0:0: +333,306,25773,1,8,0:0:0:0: +362,299,25878,1,8,0:0:0:0: +392,280,25984,5,8,0:0:0:0: +425,239,26090,1,8,0:0:0:0: +447,193,26195,1,8,0:0:0:0: +454,143,26301,1,8,0:0:0:0: +452,88,26407,6,0,P|426:34|384:95,1,150,4|0,0:0|0:0,0:0:0:0: +368,160,27463,1,0,0:0:0:0: +487,58,27674,1,12,0:0:0:0: +300,200,28097,6,0,P|288:158|305:117,1,75,12|0,0:0|0:0,0:0:0:0: +377,238,28519,1,10,0:0:0:0: +222,217,28731,1,2,0:0:0:0: +369,92,28942,2,0,P|377:128|366:163,1,75,10|0,0:0|0:0,0:0:0:0: +223,136,29364,2,0,P|214:99|225:64,1,75,10|0,0:0|0:0,0:0:0:0: +251,276,29787,5,10,0:0:0:0: +135,240,29998,1,0,0:0:0:0: +244,356,30209,1,10,0:0:0:0: +137,161,30421,1,0,0:0:0:0: +166,327,30632,1,10,0:0:0:0: +219,187,30843,1,0,0:0:0:0: +68,322,31055,1,10,0:0:0:0: +311,192,31266,1,0,0:0:0:0: +140,89,31477,6,0,P|128:130|145:172,1,75,10|0,0:0|0:0,0:0:0:0: +217,51,31899,1,10,0:0:0:0: +62,72,32111,1,2,0:0:0:0: +209,197,32322,2,0,P|217:160|206:125,1,75,8|0,0:0|0:0,0:0:0:0: +64,168,32744,2,0,P|55:204|66:239,1,75,10|0,0:0|0:0,0:0:0:0: +209,197,33167,6,0,P|172:188|137:199,1,75,8|0,0:0|0:0,0:0:0:0: +136,340,33589,2,0,P|171:351|206:343,1,75,8|0,0:0|0:0,0:0:0:0: +285,167,34012,1,10,0:0:0:0: +308,326,34224,1,2,0:0:0:0: +176,276,34435,1,10,0:0:0:0: +362,263,34646,1,2,0:0:0:0: +184,201,34857,6,0,L|172:305,1,75,14|0,0:0|0:0,0:0:0:0: +118,138,35280,1,10,0:0:0:0: +272,162,35491,1,2,0:0:0:0: +120,57,35702,2,0,P|146:11|197:5,1,75,10|0,0:0|0:0,0:0:0:0: +294,133,36125,2,0,P|267:178|216:184,1,75,10|0,0:0|0:0,0:0:0:0: +243,11,36547,6,0,P|288:37|294:88,1,75,10|0,0:0|0:0,0:0:0:0: +171,183,36970,2,0,P|125:156|119:105,1,75,10|0,0:0|0:0,0:0:0:0: +368,94,37393,1,10,0:0:0:0: +228,243,37604,1,0,0:0:0:0: +222,94,37815,1,10,0:0:0:0: +374,238,38026,1,0,0:0:0:0: +368,94,38238,6,0,L|468:115,1,75,10|0,0:0|0:0,0:0:0:0: +240,170,38660,2,0,L|340:191,1,75,10|0,0:0|0:0,0:0:0:0: +110,240,39083,2,0,L|210:261,1,75,10|0,0:0|0:0,0:0:0:0: +106,321,39505,1,10,0:0:0:0: +148,159,39716,1,2,0:0:0:0: +35,279,39928,5,10,0:0:0:0: +213,325,40139,1,2,0:0:0:0: +61,312,40350,1,8,0:0:0:0: +237,299,40561,1,2,0:0:0:0: +120,92,40773,1,8,0:0:0:0: +124,129,40878,1,8,0:0:0:0: +128,166,40984,1,8,0:0:0:0: +132,203,41089,1,8,0:0:0:0: +136,241,41195,1,12,0:0:0:0: +281,114,41407,5,8,0:0:0:0: +281,114,41512,1,8,0:0:0:0: +281,114,41618,2,0,L|377:107,1,75,12|2,0:0|0:0,0:0:0:0: +292,34,42040,2,0,L|388:27,1,75,0|2,0:0|0:0,0:0:0:0: +400,177,42463,2,0,L|407:273,1,75,0|2,0:0|0:0,0:0:0:0: +480,188,42885,2,0,L|487:284,1,75,0|2,0:0|0:0,0:0:0:0: +330,317,43308,6,0,L|234:310,1,75,0|2,0:0|0:0,0:0:0:0: +319,237,43730,2,0,L|223:230,1,75,0|2,0:0|0:0,0:0:0:0: +129,357,44153,1,0,0:0:0:0: +43,239,44364,1,2,0:0:0:0: +181,284,44576,1,2,0:0:0:0: +43,329,44787,1,2,0:0:0:0: +129,211,44998,6,0,L|136:121,1,75,0|2,0:0|0:0,0:0:0:0: +224,157,45421,2,0,L|217:67,1,75,0|2,0:0|0:0,0:0:0:0: +312,60,45843,1,0,0:0:0:0: +414,106,46055,1,2,0:0:0:0: +401,1,46266,1,0,0:0:0:0: +310,142,46477,5,2,0:0:0:0: +310,142,46583,1,0,0:0:0:0: +310,142,46688,2,0,L|317:232,1,75,0|2,0:0|0:0,0:0:0:0: +405,196,47111,2,0,L|398:286,1,75,0|2,0:0|0:0,0:0:0:0: +280,288,47533,1,0,0:0:0:0: +388,352,47745,1,2,0:0:0:0: +492,176,47956,1,10,0:0:0:0: +465,312,48167,1,2,0:0:0:0: +315,216,48378,6,0,P|271:207|228:227,1,75,4|2,0:0|0:0,0:0:0:0: +280,288,48801,1,0,0:0:0:0: +392,188,49012,2,0,P|367:150|322:134,1,75,2|0,0:0|0:0,0:0:0:0: +472,212,49435,2,0,P|472:166|445:127,1,75,2|0,0:0|0:0,0:0:0:0: +399,270,49857,1,2,0:0:0:0: +341,136,50069,6,0,L|356:42,1,75,0|2,0:0|0:0,0:0:0:0: +430,31,50491,1,0,0:0:0:0: +274,83,50702,1,2,0:0:0:0: +423,111,50914,2,0,L|497:122,1,75,0|2,0:0|0:0,0:0:0:0: +338,215,51336,2,0,L|408:188,1,75,8|2,0:0|0:0,0:0:0:0: +282,268,51759,6,0,P|261:214|276:169,1,75,4|2,0:0|0:0,0:0:0:0: +358,289,52181,1,0,0:0:0:0: +184,202,52393,2,0,P|207:148|249:127,1,75,2|0,0:0|0:0,0:0:0:0: +190,281,52815,1,2,0:0:0:0: +119,158,53026,1,10,0:0:0:0: +262,200,53238,1,0,0:0:0:0: +99,230,53449,6,0,L|123:142,1,75,4|2,0:0|0:0,0:0:0:0: +31,295,53871,2,0,L|7:207,1,75,8|2,0:0|0:0,0:0:0:0: +131,316,54294,1,8,0:0:0:0: +222,242,54505,1,8,0:0:0:0: +118,157,54716,1,8,0:0:0:0: +118,157,54822,1,8,0:0:0:0: +118,157,54928,1,8,0:0:0:0: +226,332,55139,6,0,P|281:349|357:310,1,112.5,4|0,0:0|0:0,0:0:0:0: +332,333,55562,2,0,L|352:238,1,75,2|0,0:0|0:0,0:0:0:0: +289,191,55984,1,2,0:0:0:0: +338,116,56195,1,0,0:0:0:0: +427,103,56407,1,10,0:0:0:0: +502,151,56618,1,0,0:0:0:0: +371,38,56829,6,0,P|316:21|240:60,1,112.5,2|0,0:0|0:0,0:0:0:0: +265,37,57252,2,0,L|245:132,1,75,2|0,0:0|0:0,0:0:0:0: +132,25,57674,2,0,L|159:150,2,112.5,2|0|0,0:0|0:0|0:0,0:0:0:0: +79,150,58519,6,0,P|160:212|192:199,1,112.5,4|0,0:0|0:0,0:0:0:0: +158,212,58942,2,0,L|253:191,1,75,2|0,0:0|0:0,0:0:0:0: +249,110,59364,1,2,0:0:0:0: +324,159,59575,1,0,0:0:0:0: +337,248,59787,1,10,0:0:0:0: +289,323,59998,1,0,0:0:0:0: +406,192,60209,6,0,P|468:273|455:305,1,112.5,2|0,0:0|0:0,0:0:0:0: +469,272,60632,2,0,L|447:366,1,75,2|0,0:0|0:0,0:0:0:0: +337,248,61055,2,0,L|361:363,2,112.5,2|0|0,0:0|0:0|0:0,0:0:0:0: +232,195,61900,6,0,L|210:289,1,75,4|0,0:0|0:0,0:0:0:0: +129,122,62322,2,0,L|146:196,1,75,10|0,0:0|0:0,0:0:0:0: +177,358,62745,1,2,0:0:0:0: +108,282,62956,1,0,0:0:0:0: +286,341,63167,2,0,L|359:357,1,75,10|0,0:0|0:0,0:0:0:0: +410,231,63590,6,0,L|336:247,1,75,2|0,0:0|0:0,0:0:0:0: +465,158,64012,2,0,L|391:141,1,75,10|0,0:0|0:0,0:0:0:0: +226,111,64435,1,2,0:0:0:0: +320,175,64646,1,0,0:0:0:0: +222,34,64857,2,0,P|180:44|159:87,1,75,8|2,0:0|0:0,0:0:0:0: +218,189,65280,6,0,P|176:179|155:136,1,75,4|2,0:0|0:0,0:0:0:0: +296,70,65702,2,0,L|270:164,1,75,0|2,0:0|0:0,0:0:0:0: +236,337,66125,1,0,0:0:0:0: +325,219,66336,1,2,0:0:0:0: +152,247,66547,1,0,0:0:0:0: +316,312,66758,1,2,0:0:0:0: +88,184,66970,6,0,P|46:194|25:237,1,75,0|2,0:0|0:0,0:0:0:0: +172,320,67392,2,0,L|146:226,1,75,0|2,0:0|0:0,0:0:0:0: +194,118,67815,2,0,P|157:95|111:110,1,75,0|2,0:0|0:0,0:0:0:0: +297,315,68238,2,0,L|271:221,1,75,8|2,0:0|0:0,0:0:0:0: +300,75,68660,6,0,L|276:166,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +337,56,68977,2,0,L|313:147,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +374,43,69294,2,0,L|354:115,1,75,8|0,0:0|0:0,0:0:0:0: +385,192,69505,6,0,B|417:183|417:183|470:203,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +360,235,69822,2,0,B|391:225|391:225|444:245,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +341,274,70139,2,0,B|372:264|372:264|412:278,1,75,8|0,0:0|0:0,0:0:0:0: +245,332,70350,6,0,P|226:291|239:249,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +185,311,70667,2,0,P|200:269|239:248,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +169,248,70984,2,0,P|202:235|237:247,1,75,8|0,0:0|0:0,0:0:0:0: +78,207,71195,6,0,B|66:171|66:171|74:157|74:157|62:118,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +108,176,71512,2,0,B|96:140|96:140|104:126|104:126|92:87,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +143,143,71829,2,0,B|130:108|130:108|138:94|138:94|131:73,1,75,8|0,0:0|0:0,0:0:0:0: +307,58,72040,6,0,P|254:35|207:58,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +388,72,72357,2,0,P|335:49|288:72,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +454,91,72674,2,0,P|401:68|364:89,1,75,12|0,0:0|0:0,0:0:0:0: +338,180,72885,5,8,0:0:0:0: +269,308,73097,1,8,0:0:0:0: +304,334,73202,1,8,0:0:0:0: +348,344,73308,1,8,0:0:0:0: +391,335,73414,1,8,0:0:0:0: +428,309,73519,1,8,0:0:0:0: +450,271,73625,1,8,0:0:0:0: +453,227,73730,1,8,0:0:0:0: +453,227,74576,6,0,L|490:228,9,22.4999993133545 +506,152,74998,1,12,0:0:0:0: +222,89,75421,5,12,0:0:0:0: +194,259,75632,1,0,0:0:0:0: +320,218,75843,1,8,0:0:0:0: +150,190,76054,1,2,0:0:0:0: +339,335,76266,1,8,0:0:0:0: +372,130,76477,1,2,0:0:0:0: +221,180,76688,1,10,0:0:0:0: +425,212,76899,1,2,0:0:0:0: +285,121,77111,6,0,P|341:109|385:165,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +194,259,77533,1,8,0:0:0:0: +323,182,77745,1,2,0:0:0:0: +244,316,77956,2,0,P|200:336|140:312,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +245,179,78378,1,10,0:0:0:0: +350,277,78590,1,2,0:0:0:0: +160,228,78801,6,0,L|164:68,1,146.249993305207,8|0,0:0|0:0,0:0:0:0: +194,90,79224,2,0,P|254:105|296:75,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +129,0,79646,1,10,0:0:0:0: +22,146,79857,1,2,0:0:0:0: +194,90,80069,1,10,0:0:0:0: +22,33,80280,1,2,0:0:0:0: +129,180,80491,6,0,P|174:195|218:179,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +308,80,80913,1,10,0:0:0:0: +280,252,81125,1,0,0:0:0:0: +446,206,81336,1,8,0:0:0:0: +339,60,81547,1,2,0:0:0:0: +511,116,81759,1,10,0:0:0:0: +339,173,81970,1,2,0:0:0:0: +446,26,82181,5,12,0:0:0:0: +280,118,82393,1,0,0:0:0:0: +435,118,82604,1,8,0:0:0:0: +259,26,82816,1,2,0:0:0:0: +339,173,83026,1,8,0:0:0:0: +154,128,83238,1,2,0:0:0:0: +304,88,83449,1,10,0:0:0:0: +157,222,83661,1,2,0:0:0:0: +352,280,83871,5,8,0:0:0:0: +160,173,84083,1,0,0:0:0:0: +339,173,84294,1,8,0:0:0:0: +135,280,84506,1,2,0:0:0:0: +259,130,84716,5,8,0:0:0:0: +65,235,84928,1,2,0:0:0:0: +244,235,85139,1,10,0:0:0:0: +40,129,85351,1,2,0:0:0:0: +300,92,85562,6,0,L|274:200,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +192,43,85984,1,8,0:0:0:0: +361,34,86195,1,2,0:0:0:0: +327,233,86407,2,0,L|219:207,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +376,125,86829,1,10,0:0:0:0: +385,294,87040,1,2,0:0:0:0: +195,265,87252,6,0,L|221:157,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +303,314,87674,1,8,0:0:0:0: +134,323,87885,1,2,0:0:0:0: +177,108,88097,1,8,0:0:0:0: +223,95,88202,1,8,0:0:0:0: +267,114,88308,1,8,0:0:0:0: +291,155,88413,1,8,0:0:0:0: +284,203,88519,1,12,0:0:0:0: +102,204,88731,1,8,0:0:0:0: +224,16,88942,5,12,0:0:0:0: +207,200,89153,1,0,0:0:0:0: +96,112,89364,1,8,0:0:0:0: +113,296,89575,1,2,0:0:0:0: +0,152,89787,1,8,0:0:0:0: +184,169,89998,1,2,0:0:0:0: +16,296,90209,1,10,0:0:0:0: +211,242,90420,1,2,0:0:0:0: +88,52,90632,6,0,L|76:172,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +231,2,91055,2,0,L|160:99,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +383,22,91477,2,0,L|273:71,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +491,110,91900,2,2,L|356:101,1,97.4999955368044,10|2,0:0|0:0,0:0:0:0: +436,284,92322,6,0,L|444:144,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +304,159,92745,1,8,0:0:0:0: +304,159,92956,1,2,0:0:0:0: +412,328,93167,6,0,L|420:188,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +292,176,93590,1,8,0:0:0:0: +292,176,93801,1,2,0:0:0:0: +392,364,94012,6,2,L|400:224,1,97.4999955368044,10|2,0:0|0:0,0:0:0:0: +280,196,94435,1,8,0:0:0:0: +280,196,94646,1,2,0:0:0:0: +160,155,94857,2,0,P|148:207|192:259,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +424,112,95280,2,2,P|436:60|392:8,1,97.4999955368044,10|2,0:0|0:0,0:0:0:0: +224,192,95702,5,12,0:0:0:0: +421,192,95913,1,2,0:0:0:0: +280,56,96125,1,8,0:0:0:0: +280,253,96336,1,2,0:0:0:0: +431,112,96547,1,8,0:0:0:0: +195,112,96758,1,2,0:0:0:0: +364,268,96970,1,10,0:0:0:0: +364,32,97181,1,2,0:0:0:0: +176,264,97393,5,14,0:0:0:0: +426,108,97604,1,2,0:0:0:0: +200,184,97815,1,10,0:0:0:0: +459,264,98026,1,2,0:0:0:0: +200,108,98238,1,8,0:0:0:0: +426,184,98449,1,2,0:0:0:0: +164,32,98660,1,10,0:0:0:0: +447,32,98871,1,2,0:0:0:0: +312,264,99083,6,0,L|304:148,1,97.4999955368044,12|2,0:0|0:0,0:0:0:0: +412,236,99505,1,8,0:0:0:0: +224,224,99716,1,2,0:0:0:0: +420,144,99928,1,8,0:0:0:0: +408,332,100139,1,2,0:0:0:0: +252,136,100350,1,10,0:0:0:0: +191,314,100561,1,2,0:0:0:0: +412,236,100773,6,0,L|504:236,1,75,4|0,0:0|0:0,0:0:0:0: +348,288,101195,2,0,L|256:288,1,75,2|0,0:0|0:0,0:0:0:0: +415,339,101618,2,8,B|435:283|435:283|399:211,1,112.5 +411,235,102040,1,8,0:0:0:0: +347,127,102252,5,8,0:0:0:0: +347,127,102357,1,8,0:0:0:0: +347,127,102463,2,0,P|399:143|455:119,1,89.9999972534181,14|0,0:0|0:0,0:0:0:0: +444,20,102885,1,10,0:0:0:0: +280,60,103097,1,2,0:0:0:0: +433,135,103308,2,0,L|421:243,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +232,120,103731,2,0,L|222:30,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +92,254,104153,5,2,0:0:0:0: +139,123,104364,1,0,0:0:0:0: +0,157,104575,1,10,0:0:0:0: +158,201,104787,1,0,0:0:0:0: +204,26,104998,1,10,0:0:0:0: +34,71,105209,1,0,0:0:0:0: +267,106,105421,1,8,0:0:0:0: +30,179,105632,1,2,0:0:0:0: +163,290,105843,6,0,L|155:166,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +273,144,106266,2,0,P|327:167|371:143,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +512,116,106688,2,0,P|468:108|430:130,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +384,4,107111,2,0,P|360:58|384:102,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +396,288,107533,6,0,P|419:233|395:189,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +408,368,107956,2,0,P|462:346|477:297,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +332,336,108378,1,10,0:0:0:0: +480,244,108590,1,2,0:0:0:0: +332,336,108801,1,10,0:0:0:0: +372,168,109013,1,2,0:0:0:0: +247,313,109224,6,0,P|272:252|252:204,1,89.9999972534181,14|0,0:0|0:0,0:0:0:0: +96,136,109646,1,10,0:0:0:0: +196,252,109858,1,2,0:0:0:0: +260,120,110069,2,0,L|152:132,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +28,236,110491,2,0,L|118:246,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +86,46,110914,6,0,L|95:135,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +186,341,111337,2,0,L|196:251,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +216,88,111759,1,10,0:0:0:0: +95,135,111970,1,0,0:0:0:0: +264,168,112181,1,10,0:0:0:0: +191,8,112393,1,0,0:0:0:0: +142,221,112604,6,0,L|130:329,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +264,168,113026,2,0,L|252:276,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +396,112,113449,2,0,L|384:220,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +312,104,113871,1,10,0:0:0:0: +456,240,114083,1,0,0:0:0:0: +442,48,114294,6,0,P|401:30|360:44,1,89.9999972534181,10|2,0:0|0:0,0:0:0:0: +303,196,114716,2,0,P|343:213|386:201,1,89.9999972534181,10|2,0:0|0:0,0:0:0:0: +208,80,115139,1,8,0:0:0:0: +213,124,115244,1,8,0:0:0:0: +218,169,115350,1,8,0:0:0:0: +224,214,115455,1,8,0:0:0:0: +229,258,115561,1,12,0:0:0:0: +136,192,115773,5,8,0:0:0:0: +136,192,115878,1,8,0:0:0:0: +136,192,115984,2,0,L|40:185,1,75,12|2,0:0|0:0,0:0:0:0: +60,104,116407,2,0,L|156:110,1,75,0|2,0:0|0:0,0:0:0:0: +202,5,116829,2,0,L|209:101,1,75,0|2,0:0|0:0,0:0:0:0: +288,104,117251,2,0,L|293:29,1,75,0|2,0:0|0:0,0:0:0:0: +336,184,117674,6,0,L|240:177,1,75,0|2,0:0|0:0,0:0:0:0: +340,264,118096,2,0,L|414:258,1,75,0|2,0:0|0:0,0:0:0:0: +414,112,118519,1,0,0:0:0:0: +500,230,118730,1,2,0:0:0:0: +362,185,118942,1,10,0:0:0:0: +500,140,119153,1,2,0:0:0:0: +414,258,119364,6,0,L|340:264,1,75,0|2,0:0|0:0,0:0:0:0: +186,173,119787,2,0,L|260:178,1,75,0|2,0:0|0:0,0:0:0:0: +260,292,120209,1,0,0:0:0:0: +169,344,120421,1,2,0:0:0:0: +182,239,120632,1,0,0:0:0:0: +244,372,120843,1,2,0:0:0:0: +104,296,121054,6,0,L|14:303,1,75,0|2,0:0|0:0,0:0:0:0: +186,173,121477,2,0,L|260:178,1,75,0|2,0:0|0:0,0:0:0:0: +104,208,121899,1,0,0:0:0:0: +78,106,122111,1,2,0:0:0:0: +104,248,122322,1,10,0:0:0:0: +177,144,122534,1,2,0:0:0:0: +288,256,122744,6,0,P|244:265|201:245,1,75,4|2,0:0|0:0,0:0:0:0: +216,144,123167,1,0,0:0:0:0: +367,280,123378,2,0,P|342:318|297:334,1,75,2|0,0:0|0:0,0:0:0:0: +450,260,123801,2,0,P|447:305|416:342,1,75,2|0,0:0|0:0,0:0:0:0: +277,260,124223,1,2,0:0:0:0: +332,128,124435,6,0,L|420:160,1,75,0|2,0:0|0:0,0:0:0:0: +367,280,124857,1,0,0:0:0:0: +272,180,125069,1,2,0:0:0:0: +470,129,125280,2,0,P|475:166|460:200,1,75,0|2,0:0|0:0,0:0:0:0: +356,52,125702,1,8,0:0:0:0: +402,153,125914,1,2,0:0:0:0: +232,72,126125,6,0,P|211:126|226:171,1,75,4|2,0:0|0:0,0:0:0:0: +288,124,126547,1,0,0:0:0:0: +134,138,126759,2,0,P|157:192|199:213,1,75,2|0,0:0|0:0,0:0:0:0: +335,212,127181,1,2,0:0:0:0: +212,141,127393,1,8,0:0:0:0: +254,284,127604,1,2,0:0:0:0: +286,130,127815,6,0,L|190:143,1,75,4|2,0:0|0:0,0:0:0:0: +384,51,128237,2,0,L|296:27,1,75,8|2,0:0|0:0,0:0:0:0: +480,108,128660,1,8,0:0:0:0: +396,232,128871,1,8,0:0:0:0: +241,225,129082,1,8,0:0:0:0: +241,225,129188,1,8,0:0:0:0: +241,225,129294,1,8,0:0:0:0: +295,288,129505,6,0,P|244:309|192:292,1,112.5,6|0,0:0|0:0,0:0:0:0: +192,292,129928,2,0,L|176:365,1,75,2|0,0:0|0:0,0:0:0:0: +148,220,130350,1,2,0:0:0:0: +68,187,130561,1,0,0:0:0:0: +36,267,130772,1,10,0:0:0:0: +115,300,130983,1,0,0:0:0:0: +16,127,131195,6,0,P|67:106|124:128,1,112.5,2|0,0:0|0:0,0:0:0:0: +119,124,131618,2,0,L|192:108,1,75,2|0,0:0|0:0,0:0:0:0: +280,44,132040,2,0,L|155:17,2,112.5,2|0|0,0:0|0:0|0:0,0:0:0:0: +96,56,132885,6,0,P|72:105|91:157,1,112.5,6|0,0:0|0:0,0:0:0:0: +91,157,133308,2,0,L|164:140,1,75 +44,216,133731,1,0,0:0:0:0: +123,249,133942,1,0,0:0:0:0: +91,329,134153,1,8,0:0:0:0: +11,296,134364,1,0,0:0:0:0: +200,268,134576,6,0,P|264:280|320:244,1,112.5 +304,260,134998,2,0,L|282:354,1,75 +436,348,135421,2,0,L|413:237,2,112.5,2|0|0,0:0|0:0|0:0,0:0:0:0: +448,168,136266,6,0,P|408:156|364:180,1,75,6|0,0:0|0:0,0:0:0:0: +232,260,136688,2,0,P|272:272|316:248,1,75,10|0,0:0|0:0,0:0:0:0: +340,100,137111,1,2,0:0:0:0: +268,196,137322,1,0,0:0:0:0: +240,48,137533,2,0,L|252:136,1,75,10|0,0:0|0:0,0:0:0:0: +92,44,137956,6,0,P|132:32|172:44,1,75,2|0,0:0|0:0,0:0:0:0: +168,180,138378,2,0,P|132:192|94:177,1,75,10|0,0:0|0:0,0:0:0:0: +12,56,138801,1,2,0:0:0:0: +132,112,139012,1,0,0:0:0:0: +44,236,139223,2,0,P|20:207|20:171,1,75,10|2,0:0|0:0,0:0:0:0: +244,172,139646,6,0,P|244:208|220:236,1,75,4|2,0:0|0:0,0:0:0:0: +216,104,140069,2,0,P|215:67|239:39,1,75,0|2,0:0|0:0,0:0:0:0: +436,68,140491,1,0,0:0:0:0: +289,88,140702,1,2,0:0:0:0: +459,156,140913,1,0,0:0:0:0: +317,50,141124,1,2,0:0:0:0: +336,232,141336,6,0,L|326:306,1,75,0|2,0:0|0:0,0:0:0:0: +468,230,141759,2,0,L|458:155,1,75,0|2,0:0|0:0,0:0:0:0: +436,324,142181,2,0,L|510:333,1,75,0|2,0:0|0:0,0:0:0:0: +336,124,142604,2,0,L|261:133,1,75,8|2,0:0|0:0,0:0:0:0: +210,89,143026,6,0,P|208:140|183:171,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +261,132,143343,2,0,P|223:166|183:170,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +256,184,143660,2,0,P|204:181|181:167,1,75,8|0,0:0|0:0,0:0:0:0: +124,70,143871,6,0,L|108:173,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +96,247,144188,2,0,L|112:144,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +184,170,144505,2,0,L|79:153,1,75,8|0,0:0|0:0,0:0:0:0: +261,132,144716,6,8,L|368:150,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +336,84,145033,2,8,L|398:172,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +428,96,145350,2,8,L|412:189,1,75,12|0,0:0|0:0,0:0:0:0: +411,278,145562,6,8,P|456:273|497:240,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +324,276,145878,2,8,P|367:265|417:282,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +252,272,146195,2,8,P|295:282|340:265,1,75,8|0,0:0|0:0,0:0:0:0: +317,119,146407,6,8,L|287:227,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +240,74,146724,2,8,L|268:182,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +166,90,147040,2,8,L|237:160,1,75,12|0,0:0|0:0,0:0:0:0: +170,152,147252,5,8,0:0:0:0: +38,120,147464,1,8,0:0:0:0: +12,155,147569,1,8,0:0:0:0: +2,199,147675,1,8,0:0:0:0: +11,242,147781,1,8,0:0:0:0: +37,279,147886,1,8,0:0:0:0: +75,301,147992,1,8,0:0:0:0: +119,304,148097,1,8,0:0:0:0: +245,208,148942,6,0,L|268:196,9,22.4999993133545 +232,288,149364,1,12,0:0:0:0: +217,38,149787,5,12,0:0:0:0: +56,98,149998,1,0,0:0:0:0: +155,187,150209,1,8,0:0:0:0: +94,26,150420,1,2,0:0:0:0: +63,262,150632,5,8,0:0:0:0: +257,188,150843,1,2,0:0:0:0: +138,82,151054,1,10,0:0:0:0: +212,275,151265,1,2,0:0:0:0: +288,60,151477,6,0,L|260:184,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +204,48,151899,1,8,0:0:0:0: +346,175,152111,1,2,0:0:0:0: +130,263,152322,6,0,L|158:138,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +232,244,152744,1,10,0:0:0:0: +56,170,152956,1,2,0:0:0:0: +64,352,153167,6,0,P|136:316|220:364,1,146.249993305207,8|0,0:0|0:0,0:0:0:0: +224,348,153590,2,0,P|284:363|326:333,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +376,140,154012,1,10,0:0:0:0: +269,286,154223,1,2,0:0:0:0: +441,230,154435,1,10,0:0:0:0: +269,173,154646,1,2,0:0:0:0: +376,320,154857,6,0,P|436:335|478:305,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +496,136,155280,1,10,0:0:0:0: +420,256,155491,1,0,0:0:0:0: +330,80,155702,1,10,0:0:0:0: +223,226,155913,1,2,0:0:0:0: +395,170,156125,1,10,0:0:0:0: +223,113,156336,1,2,0:0:0:0: +330,260,156547,5,12,0:0:0:0: +408,92,156759,1,0,0:0:0:0: +168,168,156970,1,8,0:0:0:0: +408,244,157182,1,2,0:0:0:0: +256,44,157392,5,8,0:0:0:0: +264,296,157604,1,2,0:0:0:0: +436,168,157815,1,10,0:0:0:0: +188,92,158027,1,2,0:0:0:0: +212,336,158238,5,8,0:0:0:0: +290,168,158450,1,0,0:0:0:0: +50,244,158661,1,8,0:0:0:0: +290,320,158871,1,2,0:0:0:0: +138,120,159083,5,8,0:0:0:0: +146,372,159295,1,2,0:0:0:0: +318,244,159506,1,10,0:0:0:0: +70,168,159716,1,2,0:0:0:0: +324,164,159928,6,0,P|384:197|399:266,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +291,354,160350,1,8,0:0:0:0: +209,190,160562,1,2,0:0:0:0: +377,321,160773,6,0,P|317:355|255:335,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +209,190,161195,1,10,0:0:0:0: +396,220,161407,1,2,0:0:0:0: +200,283,161618,6,0,P|198:212|240:163,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +396,221,162040,1,8,0:0:0:0: +290,353,162251,1,2,0:0:0:0: +264,56,162463,5,8,0:0:0:0: +277,102,162568,1,8,0:0:0:0: +290,149,162674,1,8,0:0:0:0: +304,196,162779,1,8,0:0:0:0: +317,243,162885,1,12,0:0:0:0: +172,164,163097,1,8,0:0:0:0: +416,108,163308,5,12,0:0:0:0: +232,91,163519,1,0,0:0:0:0: +400,12,163730,1,8,0:0:0:0: +383,196,163941,1,2,0:0:0:0: +217,0,164153,5,8,0:0:0:0: +200,184,164364,1,2,0:0:0:0: +313,16,164575,1,10,0:0:0:0: +112,32,164786,1,2,0:0:0:0: +200,184,164998,6,0,P|216:136|204:88,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +112,256,165421,2,0,P|96:304|108:352,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +116,176,165843,2,0,P|68:160|20:172,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +196,264,166266,2,2,P|244:280|292:268,1,97.4999955368044,10|2,0:0|0:0,0:0:0:0: +248,60,166688,5,8,0:0:0:0: +248,201,166899,1,0,0:0:0:0: +333,55,167111,1,8,0:0:0:0: +248,201,167322,1,2,0:0:0:0: +424,101,167533,5,8,0:0:0:0: +248,201,167744,1,2,0:0:0:0: +468,224,167956,1,10,0:0:0:0: +292,124,168167,1,2,0:0:0:0: +364,328,168378,5,8,0:0:0:0: +364,158,168589,1,0,0:0:0:0: +244,304,168801,1,8,0:0:0:0: +464,327,169013,1,2,0:0:0:0: +192,248,169224,6,0,L|184:359,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +508,272,169646,2,2,L|500:161,1,97.4999955368044,10|2,0:0|0:0,0:0:0:0: +268,60,170068,5,12,0:0:0:0: +268,257,170279,1,2,0:0:0:0: +404,116,170491,1,8,0:0:0:0: +207,116,170702,1,2,0:0:0:0: +348,267,170913,5,8,0:0:0:0: +348,31,171124,1,2,0:0:0:0: +192,200,171336,1,8,0:0:0:0: +428,200,171547,1,2,0:0:0:0: +268,60,171759,5,12,0:0:0:0: +386,236,171970,1,2,0:0:0:0: +386,11,172181,1,8,0:0:0:0: +268,187,172393,1,2,0:0:0:0: +149,55,172604,5,10,0:0:0:0: +30,231,172815,1,2,0:0:0:0: +30,7,173026,1,10,0:0:0:0: +149,183,173238,1,2,0:0:0:0: +30,7,173449,6,0,L|58:127,1,97.4999955368044,12|0,0:0|0:0,0:0:0:0: +240,64,173871,2,0,L|122:28,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +80,216,174294,2,0,L|169:131,1,97.4999955368044,8|2,0:0|0:0,0:0:0:0: +124,280,174716,1,10,0:0:0:0: +56,128,174928,1,2,0:0:0:0: +216,212,175139,6,0,L|200:312,1,75,4|0,0:0|0:0,0:0:0:0: +296,216,175562,6,0,L|276:332,1,89.9999972534181,2|0,0:0|0:0,0:0:0:0: +376,208,175984,6,8,L|352:352,1,134.999995880127 +353,341,176406,1,8,0:0:0:0: +328,144,176618,5,8,0:0:0:0: +328,144,176723,1,8,0:0:0:0: +328,144,176829,2,0,P|376:128|432:160,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +248,152,177252,2,0,P|200:168|144:136,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +344,120,177674,2,0,P|392:104|448:136,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +236,168,178097,2,0,P|188:184|132:152,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +192,272,178519,6,0,P|208:320|176:376,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +152,172,178942,2,0,P|136:124|168:68,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +228,284,179364,2,0,P|244:332|212:388,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +116,152,179787,2,0,P|100:104|132:48,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +100,256,180209,6,0,P|52:272|-4:240,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +240,184,180632,2,0,P|288:168|344:200,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +288,336,181055,2,0,L|284:232,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +432,84,181477,2,0,L|420:204,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +368,352,181900,6,0,L|364:248,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +512,100,182322,2,0,L|500:220,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +272,104,182745,2,0,L|392:116,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +356,132,183062,1,0,0:0:0:0: +352,156,183167,1,8,0:0:0:0: +276,20,183378,1,0,0:0:0:0: +304,240,183590,6,0,P|264:256|216:240,1,89.9999972534181,12|0,0:0|0:0,0:0:0:0: +392,272,184012,2,0,P|425:298|436:348,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +376,184,184435,2,0,P|382:141|419:107,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +320,336,184857,1,8,0:0:0:0: +260,180,185069,1,0,0:0:0:0: +176,304,185280,6,0,B|160:372|160:372|144:344,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +207,176,185702,2,0,B|273:155|273:155|257:183,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +84,224,186125,2,0,B|33:176|33:176|65:176,1,97.4999955368044,8|0,0:0|0:0,0:0:0:0: +244,260,186547,1,8,0:0:0:0: +88,300,186759,1,0,0:0:0:0: +128,44,186970,6,0,L|136:188,1,104.999996795654,8|0,0:0|0:0,0:0:0:0: +340,208,187393,2,0,L|348:64,1,104.999996795654,8|0,0:0|0:0,0:0:0:0: +244,260,187815,1,8,0:0:0:0: +424,240,188026,1,0,0:0:0:0: +211,244,188238,1,8,0:0:0:0: +377,317,188449,1,0,0:0:0:0: +196,336,188660,5,8,0:0:0:0: +224,154,188871,1,0,0:0:0:0: +367,270,189083,1,8,0:0:0:0: +132,216,189294,1,0,0:0:0:0: +338,135,189505,1,8,0:0:0:0: +330,186,189610,1,8,0:0:0:0: +322,238,189716,1,8,0:0:0:0: +314,290,189821,1,8,0:0:0:0: +306,342,189927,1,12,0:0:0:0: +228,252,190139,1,8,0:0:0:0: +420,216,190350,5,12,0:0:0:0: +247,160,190562,1,0,0:0:0:0: +406,252,190773,1,8,0:0:0:0: +368,74,190985,1,2,0:0:0:0: +373,269,191195,1,8,0:0:0:0: +507,146,191407,1,2,0:0:0:0: +335,271,191618,1,10,0:0:0:0: +508,325,191830,1,2,0:0:0:0: +219,271,192040,6,0,P|199:219|231:155,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +279,327,192463,2,0,P|217:353|163:323,1,89.9999972534181,8|2,0:0|0:0,0:0:0:0: +335,271,192885,2,0,P|361:332|331:387,1,89.9999972534181,8|2,0:0|0:0,0:0:0:0: +279,219,193308,2,2,P|340:193|395:223,1,89.9999972534181,10|2,0:0|0:0,0:0:0:0: +108,296,193731,6,0,L|112:124,1,134.999995880127,8|0,0:0|0:0,0:0:0:0: +72,100,194153,2,0,P|120:116|172:84,1,89.9999972534181,8|2,0:0|0:0,0:0:0:0: +24,24,194576,1,8,0:0:0:0: +36,168,194787,1,2,0:0:0:0: +116,40,194998,1,10,0:0:0:0: +184,184,195209,1,2,0:0:0:0: +256,56,195421,5,8,0:0:0:0: +112,155,195632,1,2,0:0:0:0: +276,224,195843,2,0,L|268:132,1,89.9999972534181 +160,72,196266,1,10,0:0:0:0: +16,171,196477,1,2,0:0:0:0: +180,240,196688,1,8,0:0:0:0: +72,108,196899,1,2,0:0:0:0: +76,328,197111,5,12,0:0:0:0: +249,274,197323,1,0,0:0:0:0: +83,171,197534,1,8,0:0:0:0: +217,295,197745,1,2,0:0:0:0: +218,119,197956,1,8,0:0:0:0: +179,297,198168,1,2,0:0:0:0: +317,223,198379,1,10,0:0:0:0: +144,279,198591,1,2,0:0:0:0: +295,284,198801,6,0,L|271:164,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +489,254,199224,2,0,L|465:374,1,89.9999972534181,8|2,0:0|0:0,0:0:0:0: +277,195,199646,2,0,L|253:75,1,89.9999972534181,8|2,0:0|0:0,0:0:0:0: +506,165,200069,2,2,L|482:285,1,89.9999972534181,10|2,0:0|0:0,0:0:0:0: +301,42,200491,6,0,P|361:10|425:38,1,134.999995880127,8|0,0:0|0:0,0:0:0:0: +432,52,200914,2,0,L|420:164,1,89.9999972534181,8|2,0:0|0:0,0:0:0:0: +262,226,201336,1,8,0:0:0:0: +352,103,201547,1,2,0:0:0:0: +352,256,201759,1,10,0:0:0:0: +262,132,201970,1,2,0:0:0:0: +407,179,202181,5,8,0:0:0:0: +240,253,202393,1,2,0:0:0:0: +418,291,202604,1,8,0:0:0:0: +296,155,202815,1,2,0:0:0:0: +315,338,203026,1,8,0:0:0:0: +281,308,203131,1,8,0:0:0:0: +239,292,203237,1,8,0:0:0:0: +195,291,203342,1,8,0:0:0:0: +152,306,203448,1,12,0:0:0:0: +328,380,203660,1,8,0:0:0:0: +312,204,203871,5,12,0:0:0:0: +120,266,204083,1,0,0:0:0:0: +284,136,204294,1,8,0:0:0:0: +241,334,204506,1,2,0:0:0:0: +210,130,204716,5,8,0:0:0:0: +359,267,204928,1,2,0:0:0:0: +152,180,205139,1,10,0:0:0:0: +345,120,205351,1,2,0:0:0:0: +84,136,205562,6,0,P|72:176|88:228,1,89.9999972534181,8|0,0:0|0:0,0:0:0:0: +284,136,205984,2,0,P|296:96|280:44,1,89.9999972534181,8|2,0:0|0:0,0:0:0:0: +184,248,206407,2,0,P|224:260|276:244,1,89.9999972534181,8|2,0:0|0:0,0:0:0:0: +180,28,206829,2,2,P|140:16|88:32,1,89.9999972534181,10|2,0:0|0:0,0:0:0:0: +153,305,207252,6,0,P|173:233|137:163,1,134.999995880127,12|0,0:0|0:0,0:0:0:0: +140,160,207674,2,0,P|100:148|48:164,1,89.9999972534181,8|2,0:0|0:0,0:0:0:0: +72,336,208097,5,8,0:0:0:0: +256,292,208308,1,2,0:0:0:0: +100,224,208519,1,10,0:0:0:0: +204,381,208730,1,2,0:0:0:0: +351,209,208942,5,8,0:0:0:0: +178,305,209153,1,2,0:0:0:0: +312,344,209364,1,8,0:0:0:0: +217,171,209576,1,2,0:0:0:0: +472,144,209787,5,8,0:0:0:0: +264,259,209998,1,2,0:0:0:0: +425,306,210209,1,10,0:0:0:0: +311,98,210421,1,2,0:0:0:0: +332,312,210632,5,12,0:0:0:0: +396,100,210843,1,2,0:0:0:0: +192,160,211055,1,8,0:0:0:0: +403,224,211266,1,2,0:0:0:0: +328,24,211477,5,8,0:0:0:0: +255,267,211688,1,2,0:0:0:0: +488,198,211900,1,10,0:0:0:0: +247,125,212111,1,2,0:0:0:0: +392,312,212322,5,12,0:0:0:0: +334,66,212533,1,2,0:0:0:0: +342,351,212745,1,8,0:0:0:0: +372,100,212956,1,2,0:0:0:0: +251,373,213167,5,8,0:0:0:0: +402,170,213378,1,2,0:0:0:0: +136,327,213590,1,10,0:0:0:0: +382,270,213801,1,2,0:0:0:0: +212,144,214012,6,0,P|200:204|224:244,1,104.999996795654,12|2,0:0|0:0,0:0:0:0: +152,88,214435,2,0,P|106:47|59:48,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +232,64,214857,2,0,P|289:44|312:3,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +80,120,215280,1,10,0:0:0:0: +272,188,215491,1,2,0:0:0:0: +192,8,215702,6,0,B|183:98|183:98|216:72,1,104.999996795654,12|2,0:0|0:0,0:0:0:0: +384,64,216125,2,0,B|314:122|314:122|355:126,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +432,244,216547,1,8,0:0:0:0: +260,264,216759,1,8,0:0:0:0: +328,123,216970,1,8,0:0:0:0: +333,175,217075,1,8,0:0:0:0: +338,227,217181,1,8,0:0:0:0: +344,279,217286,1,8,0:0:0:0: +349,331,217392,1,8,0:0:0:0: +349,331,218238,5,8,0:0:0:0: +310,323,218343,1,8,0:0:0:0: +273,317,218449,1,8,0:0:0:0: +236,312,218554,1,8,0:0:0:0: +198,306,218660,5,8,0:0:0:0: +253,296,218765,1,8,0:0:0:0: +309,287,218871,1,8,0:0:0:0: +365,278,218976,1,8,0:0:0:0: +421,268,219082,5,12,0:0:0:0: +348,92,219294,1,0,0:0:0:0: +205,236,219505,5,8,0:0:0:0: +381,163,219717,1,2,0:0:0:0: +237,24,219928,5,8,0:0:0:0: +310,200,220140,1,2,0:0:0:0: +449,52,220350,5,10,0:0:0:0: +273,125,220562,1,2,0:0:0:0: +392,272,220773,6,0,P|441:288|509:276,1,104.999996795654,8|0,0:0|0:0,0:0:0:0: +257,249,221195,2,0,P|206:264|159:314,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +380,189,221618,2,0,P|411:146|420:79,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +317,308,222040,2,2,P|347:350|409:380,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +297,175,222463,6,0,P|297:122|248:24,1,157.499995193482,8|0,0:0|0:0,0:0:0:0: +253,29,222885,2,0,P|308:68|384:64,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +168,34,223308,1,10,0:0:0:0: +63,216,223519,1,2,0:0:0:0: +220,125,223731,1,10,0:0:0:0: +10,125,223942,1,2,0:0:0:0: +168,216,224153,5,10,0:0:0:0: +63,34,224364,1,2,0:0:0:0: +0,264,224576,2,0,P|60:296|120:268,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +144,140,224998,6,0,L|153:48,1,52.4999983978272,8|8,0:0|0:0,0:0:0:0: +208,304,225209,2,0,L|202:356,1,52.4999983978272,8|8,0:0|0:0,0:0:0:0: +256,144,225421,2,0,L|265:52,1,52.4999983978272,8|8,0:0|0:0,0:0:0:0: +320,308,225632,2,0,L|314:360,1,52.4999983978272,8|8,0:0|0:0,0:0:0:0: +425,265,225843,5,12,0:0:0:0: +256,188,226055,1,0,0:0:0:0: +425,102,226266,5,8,0:0:0:0: +299,248,226477,1,2,0:0:0:0: +271,53,226688,5,8,0:0:0:0: +369,225,226900,1,2,0:0:0:0: +176,183,227111,5,10,0:0:0:0: +369,151,227322,1,2,0:0:0:0: +274,339,227533,5,8,0:0:0:0: +307,116,227745,1,0,0:0:0:0: +458,279,227956,5,8,0:0:0:0: +256,187,228168,1,2,0:0:0:0: +458,83,228379,5,10,0:0:0:0: +308,256,228590,1,2,0:0:0:0: +274,25,228801,5,10,0:0:0:0: +391,231,229013,1,2,0:0:0:0: +160,181,229224,6,0,P|159:106|212:65,1,104.999996795654,8|0,0:0|0:0,0:0:0:0: +257,263,229646,1,8,0:0:0:0: +288,39,229858,1,2,0:0:0:0: +348,227,230069,6,0,P|282:266|220:241,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +366,100,230491,1,10,0:0:0:0: +160,181,230703,1,2,0:0:0:0: +288,39,230914,6,0,P|353:76|372:145,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +175,84,231336,1,8,0:0:0:0: +348,227,231547,1,2,0:0:0:0: +184,336,231759,5,8,0:0:0:0: +181,283,231864,1,8,0:0:0:0: +179,231,231970,1,8,0:0:0:0: +176,178,232075,1,8,0:0:0:0: +174,126,232181,1,12,0:0:0:0: +366,100,232393,1,8,0:0:0:0: +268,228,232604,5,12,0:0:0:0: +412,280,232815,1,0,0:0:0:0: +268,188,233026,5,8,0:0:0:0: +451,187,233237,1,2,0:0:0:0: +256,152,233449,5,8,0:0:0:0: +473,113,233660,1,2,0:0:0:0: +328,248,233871,5,10,0:0:0:0: +289,31,234082,1,2,0:0:0:0: +192,204,234294,5,8,0:0:0:0: +410,241,234505,1,0,0:0:0:0: +112,188,234716,5,8,0:0:0:0: +305,297,234927,1,2,0:0:0:0: +36,176,235139,5,10,0:0:0:0: +181,344,235350,1,2,0:0:0:0: +252,136,235562,5,10,0:0:0:0: +84,281,235773,1,2,0:0:0:0: +316,188,235984,6,0,P|333:134|317:84,1,104.999996795654,8|0,0:0|0:0,0:0:0:0: +328,268,236407,2,0,P|378:242|401:195,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +276,333,236829,2,0,P|329:350|379:334,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +316,188,237252,1,10,0:0:0:0: +204,296,237463,1,2,0:0:0:0: +452,336,237674,6,0,L|470:232,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +209,104,238097,2,0,L|228:208,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +425,45,238519,6,0,L|517:54,1,52.4999983978272,8|8,0:0|0:0,0:0:0:0: +421,157,238731,2,0,L|513:166,1,52.4999983978272,8|8,0:0|0:0,0:0:0:0: +227,207,238942,2,0,L|174:201,1,52.4999983978272,8|8,0:0|0:0,0:0:0:0: +223,319,239153,2,0,L|170:313,1,52.4999983978272,8|8,0:0|0:0,0:0:0:0: +475,370,239364,5,12,0:0:0:0: +496,228,239576,1,2,0:0:0:0: +380,344,239787,5,8,0:0:0:0: +405,173,239999,1,2,0:0:0:0: +272,320,240209,5,8,0:0:0:0: +302,114,240421,1,2,0:0:0:0: +156,300,240632,5,8,0:0:0:0: +192,52,240844,1,2,0:0:0:0: +20,164,241055,5,12,0:0:0:0: +252,84,241267,1,0,0:0:0:0: +40,8,241477,5,8,0:0:0:0: +240,164,241689,1,2,0:0:0:0: +116,28,241900,5,8,0:0:0:0: +80,274,242111,1,2,0:0:0:0: +32,88,242322,5,8,0:0:0:0: +227,242,242534,1,2,0:0:0:0: +218,61,242745,6,0,L|241:172,1,104.999996795654,12|0,0:0|0:0,0:0:0:0: +131,120,243167,2,0,L|23:84,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +292,32,243590,2,0,L|315:143,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +132,204,244012,2,2,L|24:168,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +368,4,244435,6,0,L|396:164,1,150,12|0,0:0|0:0,0:0:0:0: +136,288,245280,2,8,L|20:252,1,112.5 +28,254,245702,1,8,0:0:0:0: +204,244,245914,5,8,0:0:0:0: +204,244,246020,1,8,0:0:0:0: +204,244,246125,2,0,P|220:296|188:348,1,104.999996795654,12|2,0:0|0:0,0:0:0:0: +100,188,246547,2,0,P|78:141|94:92,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +120,272,246970,2,0,P|68:288|16:256,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +176,160,247393,2,2,P|228:144|280:176,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +277,260,247815,6,0,P|255:213|271:164,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +357,288,248238,2,0,P|327:329|276:340,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +341,208,248660,2,2,P|392:212|426:251,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +276,340,249083,1,10,0:0:0:0: +341,208,249294,1,2,0:0:0:0: +200,120,249505,6,0,P|152:104|92:128,1,104.999996795654,12|2,0:0|0:0,0:0:0:0: +64,300,249928,1,8,0:0:0:0: +152,176,250139,1,2,0:0:0:0: +12,196,250350,1,8,0:0:0:0: +164,210,250561,1,2,0:0:0:0: +32,88,250773,1,10,0:0:0:0: +49,269,250984,1,2,0:0:0:0: +218,129,251195,5,8,0:0:0:0: +293,294,251406,1,2,0:0:0:0: +341,84,251618,1,8,0:0:0:0: +164,210,251829,1,2,0:0:0:0: +400,176,252040,1,10,0:0:0:0: +232,80,252251,1,2,0:0:0:0: +340,272,252463,1,10,0:0:0:0: +456,80,252674,1,2,0:0:0:0: +452,316,252885,5,12,0:0:0:0: +452,316,253307,2,0,L|480:188,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +284,220,253730,2,0,L|312:92,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +116,132,254153,2,2,L|144:4,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +36,236,254576,5,12,0:0:0:0: +36,236,254998,2,0,B|120:232|120:232|104:268,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +204,152,255421,2,2,B|288:148|288:148|272:184,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +356,56,255843,2,2,B|440:52|440:52|424:88,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +356,204,256266,5,12,0:0:0:0: +356,204,256688,2,0,P|376:248|344:312,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +252,184,257111,1,8,0:0:0:0: +296,340,257322,1,2,0:0:0:0: +192,272,257533,2,2,L|316:252,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +117,119,257956,5,12,0:0:0:0: +285,31,258167,1,2,0:0:0:0: +137,31,258378,1,8,0:0:0:0: +305,119,258589,1,2,0:0:0:0: +49,55,258801,1,8,0:0:0:0: +26,101,258906,1,8,0:0:0:0: +32,153,259012,1,8,0:0:0:0: +64,194,259117,1,8,0:0:0:0: +112,212,259223,1,12,0:0:0:0: +255,75,259435,1,8,0:0:0:0: +240,252,259646,5,12,0:0:0:0: +112,212,259857,1,0,0:0:0:0: +236,330,260068,1,8,0:0:0:0: +114,133,260280,1,2,0:0:0:0: +146,308,260491,1,8,0:0:0:0: +204,154,260702,1,2,0:0:0:0: +51,304,260914,1,10,0:0:0:0: +298,156,261125,1,2,0:0:0:0: +28,232,261336,6,0,P|44:180|16:124,1,104.999996795654,12|0,0:0|0:0,0:0:0:0: +320,228,261759,2,0,P|304:280|332:336,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +64,208,262181,2,2,P|76:149|40:90,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +364,248,262604,2,2,P|351:307|387:365,1,104.999996795654,10|2,0:0|0:0,0:0:0:0: +484,148,263026,6,4,B|448:184|448:184|320:136,1,157.499995193482,12|0,0:0|0:0,0:0:0:0: +315,131,263449,2,0,P|268:112|218:124,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +192,300,263871,1,8,0:0:0:0: +264,188,264083,1,2,0:0:0:0: +172,208,264294,1,10,0:0:0:0: +284,280,264506,1,2,0:0:0:0: +160,44,264716,6,0,B|124:80|124:80|172:208,1,157.499995193482,12|0,0:0|0:0,0:0:0:0: +172,208,265139,2,0,P|184:258|164:305,1,104.999996795654,8|2,0:0|0:0,0:0:0:0: +104,252,265562,1,10,0:0:0:0: +264,352,265773,1,2,0:0:0:0: +76,352,265984,1,10,0:0:0:0: +248,252,266195,1,2,0:0:0:0: +132,112,266407,5,12,0:0:0:0: +22,288,266618,1,2,0:0:0:0: +22,81,266829,1,8,0:0:0:0: +132,270,267040,1,2,0:0:0:0: +240,112,267252,1,8,0:0:0:0: +350,288,267463,1,2,0:0:0:0: +350,81,267674,1,8,0:0:0:0: +240,270,267885,1,2,0:0:0:0: +512,212,268097,5,12,0:0:0:0: +290,94,268308,1,2,0:0:0:0: +415,310,268519,1,8,0:0:0:0: +417,47,268730,1,2,0:0:0:0: +168,180,268942,1,8,0:0:0:0: +416,214,269153,1,2,0:0:0:0: +225,54,269364,1,10,0:0:0:0: +313,302,269576,1,2,0:0:0:0: +376,172,269787,5,12,0:0:0:0: +177,242,269998,1,2,0:0:0:0: +345,147,270209,1,8,0:0:0:0: +215,254,270420,1,2,0:0:0:0: +325,146,270632,1,8,0:0:0:0: +237,249,270843,1,2,0:0:0:0: +333,238,271055,1,8,0:0:0:0: +230,151,271266,1,2,0:0:0:0: +292,312,271477,1,12,0:0:0:0: +256,192,271583,12,0,272745,0:0:0:0: +163,256,273167,6,0,P|123:240|67:256,1,89.9999972534181,14|0,0:0|0:0,0:0:0:0: +68,364,273590,1,10,0:0:0:0: +236,324,273801,1,2,0:0:0:0: +79,249,274012,2,0,L|91:141,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +280,264,274435,2,0,L|290:354,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +420,130,274857,5,2,0:0:0:0: +373,261,275068,1,0,0:0:0:0: +512,227,275279,1,10,0:0:0:0: +354,183,275491,1,0,0:0:0:0: +308,358,275702,1,10,0:0:0:0: +478,313,275913,1,0,0:0:0:0: +245,278,276125,1,8,0:0:0:0: +482,205,276336,1,2,0:0:0:0: +349,94,276547,6,0,L|357:218,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +239,240,276970,2,0,P|185:217|141:241,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +0,268,277393,2,0,P|44:276|82:254,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +128,380,277815,2,0,P|152:326|128:282,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +116,96,278237,6,0,P|93:151|117:195,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +104,16,278660,2,0,P|50:38|35:87,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +180,48,279082,1,10,0:0:0:0: +32,140,279294,1,2,0:0:0:0: +180,48,279505,1,10,0:0:0:0: +140,216,279717,1,2,0:0:0:0: +265,71,279928,6,0,P|240:132|260:184,1,89.9999972534181,14|0,0:0|0:0,0:0:0:0: +416,248,280350,1,10,0:0:0:0: +316,132,280562,1,2,0:0:0:0: +252,264,280773,2,0,L|360:252,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +484,148,281196,2,0,L|394:138,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +426,338,281618,6,0,L|417:249,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +326,43,282041,2,0,L|316:133,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +296,296,282463,1,10,0:0:0:0: +417,249,282674,1,0,0:0:0:0: +248,216,282885,1,10,0:0:0:0: +321,376,283097,1,0,0:0:0:0: +370,163,283308,6,0,L|382:55,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +248,216,283730,2,0,L|260:108,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +122,266,284153,2,0,L|134:158,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +200,280,284575,1,10,0:0:0:0: +56,144,284787,1,0,0:0:0:0: +69,335,284998,6,0,P|110:353|152:340,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +213,180,285420,2,0,P|173:163|131:176,1,89.9999972534181,10|0,0:0|0:0,0:0:0:0: +304,272,285843,1,8,0:0:0:0: +299,228,285948,1,8,0:0:0:0: +294,183,286054,1,8,0:0:0:0: +288,138,286159,1,8,0:0:0:0: +283,94,286265,1,12,0:0:0:0: +164,52,286477,5,8,0:0:0:0: +164,52,286583,1,8,0:0:0:0: +164,52,286688,2,0,B|194:164|194:164|114:260|114:260|236:263|236:263|299:364|299:364|339:251|339:251|455:226|455:226|361:152|361:152|373:36|373:36|275:99|275:99|218:72,1,1124.99994039536,4|0,0:0|0:0,0:1:0:0: +228,76,293238,5,0,0:0:0:0: +256,192,293343,12,0,301900,0:0:0:0: diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic-expected-conversion.json rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/basic.osu similarity index 96% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/basic.osu index 40b4409760..abd2ff2ee6 100644 --- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/basic.osu +++ b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/basic.osu @@ -1,27 +1,27 @@ -osu file format v14 - -[Difficulty] -HPDrainRate:6 -CircleSize:4 -OverallDifficulty:7 -ApproachRate:8.3 -SliderMultiplier:1.6 -SliderTickRate:1 - -[TimingPoints] -500,500,4,2,1,50,1,0 -13426,-100,4,3,1,45,0,0 -14884,-100,4,2,1,50,0,0 - -[HitObjects] -96,192,500,6,0,L|416:192,2,320 -256,192,3000,12,0,4000,0:0:0:0: -256,192,4500,12,0,5500,0:0:0:0: -256,192,6000,12,0,6500,0:0:0:0: -256,128,7000,6,0,L|352:128,4,80 -32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 -256,192,11500,12,0,12000,0:0:0:0: -512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 -256,256,17000,6,0,L|160:256,4,80 -256,192,18500,12,0,19450,0:0:0:0: -216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 +osu file format v14 + +[Difficulty] +HPDrainRate:6 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:8.3 +SliderMultiplier:1.6 +SliderTickRate:1 + +[TimingPoints] +500,500,4,2,1,50,1,0 +13426,-100,4,3,1,45,0,0 +14884,-100,4,2,1,50,0,0 + +[HitObjects] +96,192,500,6,0,L|416:192,2,320 +256,192,3000,12,0,4000,0:0:0:0: +256,192,4500,12,0,5500,0:0:0:0: +256,192,6000,12,0,6500,0:0:0:0: +256,128,7000,6,0,L|352:128,4,80 +32,192,8500,6,0,B|32:384|256:384|256:192|256:192|256:0|512:0|512:192,1,800 +256,192,11500,12,0,12000,0:0:0:0: +512,320,12500,6,0,B|0:256|0:256|512:96|512:96|256:32,1,1280 +256,256,17000,6,0,L|160:256,4,80 +256,192,18500,12,0,19450,0:0:0:0: +216,231,19875,6,0,B|216:135|280:135|344:135|344:199|344:263|248:327|248:327|120:327|120:327|56:39|408:39|408:39|472:150|408:342,1,1280 diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/colinear-perfect-curve-expected-conversion.json diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/colinear-perfect-curve.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/colinear-perfect-curve.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/colinear-perfect-curve.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/diffcalc-test.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/diffcalc-test.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/diffcalc-test.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/multi-segment-slider-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/multi-segment-slider-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/multi-segment-slider-expected-conversion.json rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/multi-segment-slider-expected-conversion.json diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/multi-segment-slider.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/multi-segment-slider.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/multi-segment-slider.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/multi-segment-slider.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/nan-slider-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/nan-slider-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/nan-slider-expected-conversion.json rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/nan-slider-expected-conversion.json diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/nan-slider.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/nan-slider.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/nan-slider.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/nan-slider.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/old-stacking-expected-conversion.json diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/old-stacking.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/old-stacking.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/old-stacking.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/repeat-slider-expected-conversion.json diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/repeat-slider.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/repeat-slider.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/repeat-slider.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-paths-edge-case-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-paths-edge-case-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-paths-edge-case-expected-conversion.json rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-paths-edge-case-expected-conversion.json diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-paths-edge-case.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-paths-edge-case.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-paths-edge-case.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-paths-edge-case.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-edge-case-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-ticks-edge-case-expected-conversion.json similarity index 99% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-edge-case-expected-conversion.json rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-ticks-edge-case-expected-conversion.json index 6d97b643b1..0bfe776dc7 100644 --- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-edge-case-expected-conversion.json +++ b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-ticks-edge-case-expected-conversion.json @@ -13,16 +13,6 @@ "Y": 0.0 } }, - { - "StartTime": 7817.0, - "EndTime": 7817.0, - "X": 30.9946651, - "Y": 208.5157, - "StackOffset": { - "X": 0.0, - "Y": 0.0 - } - }, { "StartTime": 7843.0, "EndTime": 7843.0, @@ -32,8 +22,18 @@ "X": 0.0, "Y": 0.0 } + }, + { + "StartTime": 7817.0, + "EndTime": 7817.0, + "X": 30.9946651, + "Y": 208.5157, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } } ] } ] -} +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-edge-case.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-ticks-edge-case.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-edge-case.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-ticks-edge-case.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-ticks-expected-conversion.json similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks-expected-conversion.json rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-ticks-expected-conversion.json diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-ticks.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/slider-ticks.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/slider-ticks.osu diff --git a/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json new file mode 100644 index 0000000000..dda9078e57 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json @@ -0,0 +1,579 @@ +{ + "Mappings": [ + { + "StartTime": 369.0, + "Objects": [ + { + "StartTime": 369.0, + "EndTime": 369.0, + "X": 127.0, + "Y": 194.0, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 450.0, + "EndTime": 450.0, + "X": 166.53389, + "Y": 193.8691, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 532.0, + "EndTime": 532.0, + "X": 206.555847, + "Y": 193.736572, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 614.0, + "EndTime": 614.0, + "X": 246.57782, + "Y": 193.60405, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 696.0, + "EndTime": 696.0, + "X": 286.5998, + "Y": 193.471527, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 778.0, + "EndTime": 778.0, + "X": 326.621765, + "Y": 193.339, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 860.0, + "EndTime": 860.0, + "X": 366.6437, + "Y": 193.206482, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 942.0, + "EndTime": 942.0, + "X": 406.66568, + "Y": 193.073959, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 970.0, + "EndTime": 970.0, + "X": 420.331726, + "Y": 193.0287, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 997.0, + "EndTime": 997.0, + "X": 407.153748, + "Y": 193.072342, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1079.0, + "EndTime": 1079.0, + "X": 367.131775, + "Y": 193.204865, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1161.0, + "EndTime": 1161.0, + "X": 327.1098, + "Y": 193.337387, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1243.0, + "EndTime": 1243.0, + "X": 287.08783, + "Y": 193.46991, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1325.0, + "EndTime": 1325.0, + "X": 247.0659, + "Y": 193.602432, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1407.0, + "EndTime": 1407.0, + "X": 207.043915, + "Y": 193.734955, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1489.0, + "EndTime": 1489.0, + "X": 167.021988, + "Y": 193.867477, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1571.0, + "EndTime": 1571.0, + "X": 127.0, + "Y": 194.0, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1653.0, + "EndTime": 1653.0, + "X": 167.021988, + "Y": 193.867477, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1735.0, + "EndTime": 1735.0, + "X": 207.043976, + "Y": 193.734955, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1817.0, + "EndTime": 1817.0, + "X": 247.065887, + "Y": 193.602432, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1899.0, + "EndTime": 1899.0, + "X": 287.08783, + "Y": 193.46991, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 1981.0, + "EndTime": 1981.0, + "X": 327.1098, + "Y": 193.337387, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2062.0, + "EndTime": 2062.0, + "X": 366.643738, + "Y": 193.206482, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2144.0, + "EndTime": 2144.0, + "X": 406.665649, + "Y": 193.073959, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2172.0, + "EndTime": 2172.0, + "X": 420.331726, + "Y": 193.0287, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2199.0, + "EndTime": 2199.0, + "X": 407.153748, + "Y": 193.072342, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2281.0, + "EndTime": 2281.0, + "X": 367.1318, + "Y": 193.204865, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2363.0, + "EndTime": 2363.0, + "X": 327.1098, + "Y": 193.337387, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2445.0, + "EndTime": 2445.0, + "X": 287.08783, + "Y": 193.46991, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2527.0, + "EndTime": 2527.0, + "X": 247.065887, + "Y": 193.602432, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2609.0, + "EndTime": 2609.0, + "X": 207.043976, + "Y": 193.734955, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2691.0, + "EndTime": 2691.0, + "X": 167.021988, + "Y": 193.867477, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2773.0, + "EndTime": 2773.0, + "X": 127.0, + "Y": 194.0, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2855.0, + "EndTime": 2855.0, + "X": 167.021988, + "Y": 193.867477, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 2937.0, + "EndTime": 2937.0, + "X": 207.043976, + "Y": 193.734955, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3019.0, + "EndTime": 3019.0, + "X": 247.065948, + "Y": 193.602432, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3101.0, + "EndTime": 3101.0, + "X": 287.087952, + "Y": 193.46991, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3183.0, + "EndTime": 3183.0, + "X": 327.109772, + "Y": 193.337387, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3265.0, + "EndTime": 3265.0, + "X": 367.131775, + "Y": 193.204865, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3347.0, + "EndTime": 3347.0, + "X": 407.153748, + "Y": 193.072342, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3374.0, + "EndTime": 3374.0, + "X": 420.331726, + "Y": 193.0287, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3401.0, + "EndTime": 3401.0, + "X": 407.153748, + "Y": 193.072342, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3483.0, + "EndTime": 3483.0, + "X": 367.131775, + "Y": 193.204865, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3565.0, + "EndTime": 3565.0, + "X": 327.109772, + "Y": 193.337387, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3647.0, + "EndTime": 3647.0, + "X": 287.087952, + "Y": 193.46991, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3729.0, + "EndTime": 3729.0, + "X": 247.065948, + "Y": 193.602432, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3811.0, + "EndTime": 3811.0, + "X": 207.043976, + "Y": 193.734955, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3893.0, + "EndTime": 3893.0, + "X": 167.021988, + "Y": 193.867477, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 3975.0, + "EndTime": 3975.0, + "X": 127.0, + "Y": 194.0, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 4057.0, + "EndTime": 4057.0, + "X": 167.021988, + "Y": 193.867477, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 4139.0, + "EndTime": 4139.0, + "X": 207.043976, + "Y": 193.734955, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 4221.0, + "EndTime": 4221.0, + "X": 247.065948, + "Y": 193.602432, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 4303.0, + "EndTime": 4303.0, + "X": 287.087952, + "Y": 193.46991, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 4385.0, + "EndTime": 4385.0, + "X": 327.109772, + "Y": 193.337387, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 4467.0, + "EndTime": 4467.0, + "X": 367.131775, + "Y": 193.204865, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 4549.0, + "EndTime": 4549.0, + "X": 407.153748, + "Y": 193.072342, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + }, + { + "StartTime": 4540.0, + "EndTime": 4540.0, + "X": 420.331726, + "Y": 193.0287, + "StackOffset": { + "X": 0.0, + "Y": 0.0 + } + } + ] + } + ] +} \ No newline at end of file diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/uneven-repeat-slider.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/uneven-repeat-slider.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/very-fast-slider.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/very-fast-slider.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/very-fast-slider.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/very-fast-slider.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/zero-length-sliders.osu b/osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/zero-length-sliders.osu similarity index 100% rename from osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/zero-length-sliders.osu rename to osu.Game.Rulesets.Osu.Tests/Resources/Testing/Beatmaps/zero-length-sliders.osu diff --git a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json b/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json deleted file mode 100644 index 12d1645c04..0000000000 --- a/osu.Game.Rulesets.Osu/Resources/Testing/Beatmaps/uneven-repeat-slider-expected-conversion.json +++ /dev/null @@ -1,348 +0,0 @@ -{ - "Mappings": [{ - "StartTime": 369, - "Objects": [{ - "StartTime": 369, - "EndTime": 369, - "X": 127, - "Y": 194 - }, - { - "StartTime": 450, - "EndTime": 450, - "X": 166.53389, - "Y": 193.8691 - }, - { - "StartTime": 532, - "EndTime": 532, - "X": 206.555847, - "Y": 193.736572 - }, - { - "StartTime": 614, - "EndTime": 614, - "X": 246.57782, - "Y": 193.60405 - }, - { - "StartTime": 696, - "EndTime": 696, - "X": 286.5998, - "Y": 193.471527 - }, - { - "StartTime": 778, - "EndTime": 778, - "X": 326.621765, - "Y": 193.339 - }, - { - "StartTime": 860, - "EndTime": 860, - "X": 366.6437, - "Y": 193.206482 - }, - { - "StartTime": 942, - "EndTime": 942, - "X": 406.66568, - "Y": 193.073959 - }, - { - "StartTime": 970, - "EndTime": 970, - "X": 420.331726, - "Y": 193.0287 - }, - { - "StartTime": 997, - "EndTime": 997, - "X": 407.153748, - "Y": 193.072342 - }, - { - "StartTime": 1079, - "EndTime": 1079, - "X": 367.131775, - "Y": 193.204865 - }, - { - "StartTime": 1161, - "EndTime": 1161, - "X": 327.1098, - "Y": 193.337387 - }, - { - "StartTime": 1243, - "EndTime": 1243, - "X": 287.08783, - "Y": 193.46991 - }, - { - "StartTime": 1325, - "EndTime": 1325, - "X": 247.0659, - "Y": 193.602432 - }, - { - "StartTime": 1407, - "EndTime": 1407, - "X": 207.043915, - "Y": 193.734955 - }, - { - "StartTime": 1489, - "EndTime": 1489, - "X": 167.021988, - "Y": 193.867477 - }, - { - "StartTime": 1571, - "EndTime": 1571, - "X": 127, - "Y": 194 - }, - { - "StartTime": 1653, - "EndTime": 1653, - "X": 167.021988, - "Y": 193.867477 - }, - { - "StartTime": 1735, - "EndTime": 1735, - "X": 207.043976, - "Y": 193.734955 - }, - { - "StartTime": 1817, - "EndTime": 1817, - "X": 247.065887, - "Y": 193.602432 - }, - { - "StartTime": 1899, - "EndTime": 1899, - "X": 287.08783, - "Y": 193.46991 - }, - { - "StartTime": 1981, - "EndTime": 1981, - "X": 327.1098, - "Y": 193.337387 - }, - { - "StartTime": 2062, - "EndTime": 2062, - "X": 366.643738, - "Y": 193.206482 - }, - { - "StartTime": 2144, - "EndTime": 2144, - "X": 406.665649, - "Y": 193.073959 - }, - { - "StartTime": 2172, - "EndTime": 2172, - "X": 420.331726, - "Y": 193.0287 - }, - { - "StartTime": 2199, - "EndTime": 2199, - "X": 407.153748, - "Y": 193.072342 - }, - { - "StartTime": 2281, - "EndTime": 2281, - "X": 367.1318, - "Y": 193.204865 - }, - { - "StartTime": 2363, - "EndTime": 2363, - "X": 327.1098, - "Y": 193.337387 - }, - { - "StartTime": 2445, - "EndTime": 2445, - "X": 287.08783, - "Y": 193.46991 - }, - { - "StartTime": 2527, - "EndTime": 2527, - "X": 247.065887, - "Y": 193.602432 - }, - { - "StartTime": 2609, - "EndTime": 2609, - "X": 207.043976, - "Y": 193.734955 - }, - { - "StartTime": 2691, - "EndTime": 2691, - "X": 167.021988, - "Y": 193.867477 - }, - { - "StartTime": 2773, - "EndTime": 2773, - "X": 127, - "Y": 194 - }, - { - "StartTime": 2855, - "EndTime": 2855, - "X": 167.021988, - "Y": 193.867477 - }, - { - "StartTime": 2937, - "EndTime": 2937, - "X": 207.043976, - "Y": 193.734955 - }, - { - "StartTime": 3019, - "EndTime": 3019, - "X": 247.065948, - "Y": 193.602432 - }, - { - "StartTime": 3101, - "EndTime": 3101, - "X": 287.087952, - "Y": 193.46991 - }, - { - "StartTime": 3183, - "EndTime": 3183, - "X": 327.109772, - "Y": 193.337387 - }, - { - "StartTime": 3265, - "EndTime": 3265, - "X": 367.131775, - "Y": 193.204865 - }, - { - "StartTime": 3347, - "EndTime": 3347, - "X": 407.153748, - "Y": 193.072342 - }, - { - "StartTime": 3374, - "EndTime": 3374, - "X": 420.331726, - "Y": 193.0287 - }, - { - "StartTime": 3401, - "EndTime": 3401, - "X": 407.153748, - "Y": 193.072342 - }, - { - "StartTime": 3483, - "EndTime": 3483, - "X": 367.131775, - "Y": 193.204865 - }, - { - "StartTime": 3565, - "EndTime": 3565, - "X": 327.109772, - "Y": 193.337387 - }, - { - "StartTime": 3647, - "EndTime": 3647, - "X": 287.087952, - "Y": 193.46991 - }, - { - "StartTime": 3729, - "EndTime": 3729, - "X": 247.065948, - "Y": 193.602432 - }, - { - "StartTime": 3811, - "EndTime": 3811, - "X": 207.043976, - "Y": 193.734955 - }, - { - "StartTime": 3893, - "EndTime": 3893, - "X": 167.021988, - "Y": 193.867477 - }, - { - "StartTime": 3975, - "EndTime": 3975, - "X": 127, - "Y": 194 - }, - { - "StartTime": 4057, - "EndTime": 4057, - "X": 167.021988, - "Y": 193.867477 - }, - { - "StartTime": 4139, - "EndTime": 4139, - "X": 207.043976, - "Y": 193.734955 - }, - { - "StartTime": 4221, - "EndTime": 4221, - "X": 247.065948, - "Y": 193.602432 - }, - { - "StartTime": 4303, - "EndTime": 4303, - "X": 287.087952, - "Y": 193.46991 - }, - { - "StartTime": 4385, - "EndTime": 4385, - "X": 327.109772, - "Y": 193.337387 - }, - { - "StartTime": 4467, - "EndTime": 4467, - "X": 367.131775, - "Y": 193.204865 - }, - { - "StartTime": 4540, - "EndTime": 4540, - "X": 420.331726, - "Y": 193.0287 - }, - { - "StartTime": 4549, - "EndTime": 4549, - "X": 407.153748, - "Y": 193.072342 - } - ] - }] -} \ No newline at end of file From 07dc44ccd769213a1078ac36bd867c7f6ee300f2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2023 16:18:54 +0900 Subject: [PATCH 0431/1118] Make log export async and show notification on completion --- .../Sections/General/UpdateSettings.cs | 62 +++++++++++++------ 1 file changed, 44 insertions(+), 18 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index 398e4b39af..a8f5b655d0 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -31,8 +31,11 @@ namespace osu.Game.Overlays.Settings.Sections.General [Resolved] private INotificationOverlay? notifications { get; set; } + [Resolved] + private Storage storage { get; set; } = null!; + [BackgroundDependencyLoader] - private void load(Storage storage, OsuConfigManager config, OsuGame game) + private void load(OsuConfigManager config, OsuGame game) { Add(new SettingsEnumDropdown { @@ -78,23 +81,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { Text = GeneralSettingsStrings.ExportLogs, Keywords = new[] { @"bug", "report", "logs", "files" }, - Action = () => - { - var logStorage = Logger.Storage; - - const string archive_filename = "exports/compressed-logs.zip"; - - using (var outStream = storage.CreateFileSafely(archive_filename)) - using (var zip = ZipArchive.Create()) - { - foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) - zip.AddEntry(f, logStorage.GetStream(f), true); - - zip.SaveTo(outStream); - } - - storage.PresentFileExternally(archive_filename); - }, + Action = () => Task.Run(exportLogs), }); Add(new SettingsButton @@ -104,5 +91,44 @@ namespace osu.Game.Overlays.Settings.Sections.General }); } } + + private void exportLogs() + { + ProgressNotification notification = new ProgressNotification + { + State = ProgressNotificationState.Active, + Text = "Exporting logs...", + }; + + notifications?.Post(notification); + + const string archive_filename = "exports/compressed-logs.zip"; + + try + { + var logStorage = Logger.Storage; + + using (var outStream = storage.CreateFileSafely(archive_filename)) + using (var zip = ZipArchive.Create()) + { + foreach (string? f in logStorage.GetFiles(string.Empty, "*.log")) zip.AddEntry(f, logStorage.GetStream(f), true); + + zip.SaveTo(outStream); + } + } + catch + { + notification.State = ProgressNotificationState.Cancelled; + + // cleanup if export is failed or canceled. + storage.Delete(archive_filename); + throw; + } + + notification.CompletionText = "Exported logs! Click to view."; + notification.CompletionClickAction = () => storage.PresentFileExternally(archive_filename); + + notification.State = ProgressNotificationState.Completed; + } } } From 856310e954fd0a5b25ba20a26239a314bc2363ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 7 Dec 2023 08:22:01 +0100 Subject: [PATCH 0432/1118] Remove reference to removed comment from another comment --- osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs index 4a217a19ea..838bd35dd4 100644 --- a/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Osu.Tests/OsuBeatmapConversionTest.cs @@ -50,9 +50,8 @@ namespace osu.Game.Rulesets.Osu.Tests double startTime = obj.StartTime; double endTime = obj.GetEndTime(); - // as stated in the inline comment above, this is locally bringing back - // the stable treatment of the "legacy last tick" just to make sure - // that the conversion output matches. + // this is locally bringing back the stable treatment of the "legacy last tick" + // just to make sure that the conversion output matches. // compare: `SliderEventGenerator.Generate()`, and the calculation of `legacyLastTickTime`. if (obj is SliderTailCircle && parent is Slider slider) { From 323808ad1e915ef2b9f2aeb5b9846ba8f92cc9dc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 7 Dec 2023 16:34:26 +0900 Subject: [PATCH 0433/1118] Add more inline commenting around `VELOCITY_MULTIPLIER` application to `TimeRange` --- osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs index 88085dfe97..49b0ad811d 100644 --- a/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/UI/DrawableTaikoRuleset.cs @@ -71,14 +71,18 @@ namespace osu.Game.Rulesets.Taiko.UI protected virtual double ComputeTimeRange() { // Taiko scrolls at a constant 100px per 1000ms. More notes become visible as the playfield is lengthened. - const float scroll_rate = 10 / TaikoBeatmapConverter.VELOCITY_MULTIPLIER; + const float scroll_rate = 10; // Since the time range will depend on a positional value, it is referenced to the x480 pixel space. // Width is used because it defines how many notes fit on the playfield. // We clamp the ratio to the maximum aspect ratio to keep scroll speed consistent on widths lower than the default. float ratio = Math.Max(DrawSize.X / 768f, TaikoPlayfieldAdjustmentContainer.MAXIMUM_ASPECT); - return (Playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate; + // Stable internally increased the slider velocity of objects by a factor of `VELOCITY_MULTIPLIER`. + // To simulate this, we shrink the time range by that factor here. + // This, when combined with the rest of the scrolling ruleset machinery (see `MultiplierControlPoint` et al.), + // has the effect of increasing each multiplier control point's multiplier by `VELOCITY_MULTIPLIER`, ensuring parity with stable. + return (Playfield.HitObjectContainer.DrawWidth / ratio) * scroll_rate / TaikoBeatmapConverter.VELOCITY_MULTIPLIER; } protected override void UpdateAfterChildren() From 0fe2e1e8d60bde53ded276d1787d8404c6035570 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 8 Dec 2023 16:33:10 +0900 Subject: [PATCH 0434/1118] Re-fix mania conversion following new discoveries --- .../Beatmaps/100374-expected-conversion.json | 2 +- .../Beatmaps/20544-expected-conversion.json | 2 +- .../Beatmaps/basic-expected-conversion.json | 296 ++++++++++-------- ...ero-length-slider-expected-conversion.json | 28 +- .../Patterns/Legacy/PatternGenerator.cs | 8 +- 5 files changed, 186 insertions(+), 150 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374-expected-conversion.json index 59f73f7ad4..59cf6d2672 100644 --- a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374-expected-conversion.json +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/100374-expected-conversion.json @@ -1 +1 @@ -{"Mappings":[{"RandomW":273084013,"RandomX":842502087,"RandomY":3579807591,"RandomZ":273326509,"StartTime":15562.0,"Objects":[{"StartTime":15562.0,"EndTime":17155.0,"Column":0}]},{"RandomW":2659258901,"RandomX":3579807591,"RandomY":273326509,"RandomZ":273084013,"StartTime":17686.0,"Objects":[{"StartTime":17686.0,"EndTime":17686.0,"Column":0},{"StartTime":17686.0,"EndTime":17686.0,"Column":1}]},{"RandomW":3083655709,"RandomX":273326509,"RandomY":273084013,"RandomZ":2659258901,"StartTime":17951.0,"Objects":[{"StartTime":17951.0,"EndTime":17951.0,"Column":1}]},{"RandomW":3588026162,"RandomX":2659258901,"RandomY":3083655709,"RandomZ":4073603712,"StartTime":18217.0,"Objects":[{"StartTime":18217.0,"EndTime":18217.0,"Column":2},{"StartTime":18217.0,"EndTime":18217.0,"Column":4}]},{"RandomW":1130061350,"RandomX":3083655709,"RandomY":4073603712,"RandomZ":3588026162,"StartTime":18482.0,"Objects":[{"StartTime":18482.0,"EndTime":18482.0,"Column":2}]},{"RandomW":315421426,"RandomX":3588026162,"RandomY":1130061350,"RandomZ":2459334754,"StartTime":18748.0,"Objects":[{"StartTime":18748.0,"EndTime":19013.0,"Column":0}]},{"RandomW":3110660773,"RandomX":2459334754,"RandomY":315421426,"RandomZ":542845670,"StartTime":19279.0,"Objects":[{"StartTime":19279.0,"EndTime":19809.0,"Column":3},{"StartTime":19544.0,"EndTime":19544.0,"Column":1},{"StartTime":19809.0,"EndTime":19809.0,"Column":1}]},{"RandomW":3110660773,"RandomX":2459334754,"RandomY":315421426,"RandomZ":542845670,"StartTime":20075.0,"Objects":[{"StartTime":20075.0,"EndTime":20075.0,"Column":4},{"StartTime":20075.0,"EndTime":20075.0,"Column":2}]},{"RandomW":2552021122,"RandomX":315421426,"RandomY":542845670,"RandomZ":3110660773,"StartTime":20341.0,"Objects":[{"StartTime":20341.0,"EndTime":20341.0,"Column":3}]},{"RandomW":3979536913,"RandomX":542845670,"RandomY":3110660773,"RandomZ":2552021122,"StartTime":20606.0,"Objects":[{"StartTime":20606.0,"EndTime":20606.0,"Column":2},{"StartTime":20606.0,"EndTime":20606.0,"Column":3}]},{"RandomW":3926138036,"RandomX":2552021122,"RandomY":3979536913,"RandomZ":348643659,"StartTime":20871.0,"Objects":[{"StartTime":20871.0,"EndTime":21401.0,"Column":4}]},{"RandomW":4001028953,"RandomX":348643659,"RandomY":3926138036,"RandomZ":2489502118,"StartTime":21933.0,"Objects":[{"StartTime":21933.0,"EndTime":22198.0,"Column":5}]},{"RandomW":263714783,"RandomX":2489502118,"RandomY":4001028953,"RandomZ":3315380836,"StartTime":22464.0,"Objects":[{"StartTime":22464.0,"EndTime":22729.0,"Column":0}]},{"RandomW":3045229215,"RandomX":3315380836,"RandomY":263714783,"RandomZ":2367299702,"StartTime":22995.0,"Objects":[{"StartTime":22995.0,"EndTime":23791.0,"Column":2}]},{"RandomW":622075324,"RandomX":2367299702,"RandomY":3045229215,"RandomZ":2511145433,"StartTime":24057.0,"Objects":[{"StartTime":24057.0,"EndTime":24322.0,"Column":1}]},{"RandomW":1428674661,"RandomX":3630592823,"RandomY":628640291,"RandomZ":2684635853,"StartTime":24588.0,"Objects":[{"StartTime":24588.0,"EndTime":24853.0,"Column":4},{"StartTime":24588.0,"EndTime":24853.0,"Column":3}]},{"RandomW":2963472042,"RandomX":3191072317,"RandomY":1509788298,"RandomZ":3677221210,"StartTime":25119.0,"Objects":[{"StartTime":25119.0,"EndTime":25649.0,"Column":2}]},{"RandomW":2441208973,"RandomX":1509788298,"RandomY":3677221210,"RandomZ":2963472042,"StartTime":26181.0,"Objects":[{"StartTime":26181.0,"EndTime":26181.0,"Column":2},{"StartTime":26181.0,"EndTime":26181.0,"Column":3}]},{"RandomW":614303213,"RandomX":3677221210,"RandomY":2963472042,"RandomZ":2441208973,"StartTime":26447.0,"Objects":[{"StartTime":26447.0,"EndTime":26447.0,"Column":3}]},{"RandomW":931064848,"RandomX":2441208973,"RandomY":614303213,"RandomZ":2425227013,"StartTime":26712.0,"Objects":[{"StartTime":26712.0,"EndTime":26977.0,"Column":2}]},{"RandomW":1631554006,"RandomX":2425227013,"RandomY":931064848,"RandomZ":2839921662,"StartTime":27243.0,"Objects":[{"StartTime":27243.0,"EndTime":27508.0,"Column":4}]},{"RandomW":1102544522,"RandomX":2839921662,"RandomY":1631554006,"RandomZ":2171149531,"StartTime":27774.0,"Objects":[{"StartTime":27774.0,"EndTime":28039.0,"Column":3}]},{"RandomW":1535528787,"RandomX":2171149531,"RandomY":1102544522,"RandomZ":3328843633,"StartTime":28305.0,"Objects":[{"StartTime":28305.0,"EndTime":28835.0,"Column":4},{"StartTime":28305.0,"EndTime":28305.0,"Column":3},{"StartTime":28570.0,"EndTime":28570.0,"Column":3},{"StartTime":28835.0,"EndTime":28835.0,"Column":3}]},{"RandomW":2462060348,"RandomX":1102544522,"RandomY":3328843633,"RandomZ":1535528787,"StartTime":29102.0,"Objects":[{"StartTime":29102.0,"EndTime":29102.0,"Column":3}]},{"RandomW":2548780898,"RandomX":2462060348,"RandomY":1752789184,"RandomZ":4269701929,"StartTime":29367.0,"Objects":[{"StartTime":29367.0,"EndTime":29897.0,"Column":5},{"StartTime":29367.0,"EndTime":29897.0,"Column":1}]},{"RandomW":2872444045,"RandomX":2548780898,"RandomY":96471884,"RandomZ":2795275332,"StartTime":30429.0,"Objects":[{"StartTime":30429.0,"EndTime":30694.0,"Column":2}]},{"RandomW":554186146,"RandomX":2872444045,"RandomY":1718345430,"RandomZ":1676944188,"StartTime":30960.0,"Objects":[{"StartTime":30960.0,"EndTime":31225.0,"Column":4},{"StartTime":30960.0,"EndTime":31225.0,"Column":1}]},{"RandomW":44350362,"RandomX":1676944188,"RandomY":554186146,"RandomZ":973164386,"StartTime":31491.0,"Objects":[{"StartTime":31491.0,"EndTime":32287.0,"Column":0}]},{"RandomW":2689469863,"RandomX":973164386,"RandomY":44350362,"RandomZ":3230373169,"StartTime":32553.0,"Objects":[{"StartTime":32553.0,"EndTime":32818.0,"Column":1}]},{"RandomW":3076210018,"RandomX":3230373169,"RandomY":2689469863,"RandomZ":2416196755,"StartTime":33084.0,"Objects":[{"StartTime":33084.0,"EndTime":33349.0,"Column":2}]},{"RandomW":4212524875,"RandomX":2416196755,"RandomY":3076210018,"RandomZ":736433317,"StartTime":33615.0,"Objects":[{"StartTime":33615.0,"EndTime":34145.0,"Column":5}]},{"RandomW":668643347,"RandomX":4212524875,"RandomY":1246190622,"RandomZ":614058009,"StartTime":34677.0,"Objects":[{"StartTime":34677.0,"EndTime":34677.0,"Column":0},{"StartTime":34677.0,"EndTime":34677.0,"Column":5}]},{"RandomW":4133034829,"RandomX":668643347,"RandomY":1824376828,"RandomZ":476758489,"StartTime":34942.0,"Objects":[{"StartTime":34942.0,"EndTime":34942.0,"Column":1},{"StartTime":34942.0,"EndTime":34942.0,"Column":5}]},{"RandomW":82933693,"RandomX":1824376828,"RandomY":476758489,"RandomZ":4133034829,"StartTime":35208.0,"Objects":[{"StartTime":35208.0,"EndTime":35208.0,"Column":0},{"StartTime":35208.0,"EndTime":35208.0,"Column":1}]},{"RandomW":2263995128,"RandomX":476758489,"RandomY":4133034829,"RandomZ":82933693,"StartTime":35473.0,"Objects":[{"StartTime":35473.0,"EndTime":35473.0,"Column":1}]},{"RandomW":3437211638,"RandomX":4133034829,"RandomY":82933693,"RandomZ":2263995128,"StartTime":35739.0,"Objects":[{"StartTime":35739.0,"EndTime":35739.0,"Column":2}]},{"RandomW":2107738941,"RandomX":2263995128,"RandomY":3437211638,"RandomZ":4066526803,"StartTime":36004.0,"Objects":[{"StartTime":36004.0,"EndTime":36004.0,"Column":2},{"StartTime":36004.0,"EndTime":36004.0,"Column":5}]},{"RandomW":1976561763,"RandomX":3437211638,"RandomY":4066526803,"RandomZ":2107738941,"StartTime":36270.0,"Objects":[{"StartTime":36270.0,"EndTime":36270.0,"Column":3},{"StartTime":36270.0,"EndTime":36270.0,"Column":4}]},{"RandomW":1147027763,"RandomX":4066526803,"RandomY":2107738941,"RandomZ":1976561763,"StartTime":36535.0,"Objects":[{"StartTime":36535.0,"EndTime":36535.0,"Column":3}]},{"RandomW":3580315894,"RandomX":1976561763,"RandomY":1147027763,"RandomZ":2767111989,"StartTime":36801.0,"Objects":[{"StartTime":36801.0,"EndTime":37331.0,"Column":4}]},{"RandomW":3743545041,"RandomX":1147027763,"RandomY":2767111989,"RandomZ":3580315894,"StartTime":37597.0,"Objects":[{"StartTime":37597.0,"EndTime":37597.0,"Column":1}]},{"RandomW":1409948107,"RandomX":3743545041,"RandomY":1774216159,"RandomZ":3150304957,"StartTime":37863.0,"Objects":[{"StartTime":37863.0,"EndTime":38393.0,"Column":2},{"StartTime":37863.0,"EndTime":38393.0,"Column":3}]},{"RandomW":4009340712,"RandomX":3150304957,"RandomY":1409948107,"RandomZ":2219703013,"StartTime":38925.0,"Objects":[{"StartTime":38925.0,"EndTime":39190.0,"Column":5}]},{"RandomW":3071167491,"RandomX":2065497204,"RandomY":2145154717,"RandomZ":2494378321,"StartTime":39456.0,"Objects":[{"StartTime":39456.0,"EndTime":39721.0,"Column":0},{"StartTime":39456.0,"EndTime":39721.0,"Column":2}]},{"RandomW":1245938367,"RandomX":3071167491,"RandomY":728627658,"RandomZ":3080260260,"StartTime":39987.0,"Objects":[{"StartTime":39987.0,"EndTime":40783.0,"Column":3}]},{"RandomW":3032241617,"RandomX":1245938367,"RandomY":2414391712,"RandomZ":3406801470,"StartTime":41048.0,"Objects":[{"StartTime":41048.0,"EndTime":41313.0,"Column":2}]},{"RandomW":3367991920,"RandomX":3804000131,"RandomY":672376773,"RandomZ":2667292323,"StartTime":41579.0,"Objects":[{"StartTime":41579.0,"EndTime":41844.0,"Column":1},{"StartTime":41579.0,"EndTime":41844.0,"Column":3}]},{"RandomW":2095476726,"RandomX":2667292323,"RandomY":3367991920,"RandomZ":3380532371,"StartTime":42110.0,"Objects":[{"StartTime":42110.0,"EndTime":42640.0,"Column":5}]},{"RandomW":869340745,"RandomX":2095476726,"RandomY":1063981175,"RandomZ":204767504,"StartTime":43172.0,"Objects":[{"StartTime":43172.0,"EndTime":43172.0,"Column":1},{"StartTime":43172.0,"EndTime":43172.0,"Column":4}]},{"RandomW":461904197,"RandomX":204767504,"RandomY":869340745,"RandomZ":2080855578,"StartTime":43438.0,"Objects":[{"StartTime":43438.0,"EndTime":43438.0,"Column":2},{"StartTime":43438.0,"EndTime":43438.0,"Column":1}]},{"RandomW":3004966693,"RandomX":869340745,"RandomY":2080855578,"RandomZ":461904197,"StartTime":43703.0,"Objects":[{"StartTime":43703.0,"EndTime":43703.0,"Column":3},{"StartTime":43703.0,"EndTime":43703.0,"Column":4}]},{"RandomW":147065937,"RandomX":2080855578,"RandomY":461904197,"RandomZ":3004966693,"StartTime":43969.0,"Objects":[{"StartTime":43969.0,"EndTime":43969.0,"Column":4}]},{"RandomW":1312111829,"RandomX":461904197,"RandomY":3004966693,"RandomZ":147065937,"StartTime":44234.0,"Objects":[{"StartTime":44234.0,"EndTime":44234.0,"Column":4}]},{"RandomW":355223143,"RandomX":3004966693,"RandomY":147065937,"RandomZ":1312111829,"StartTime":44500.0,"Objects":[{"StartTime":44500.0,"EndTime":44500.0,"Column":3}]},{"RandomW":1197174504,"RandomX":147065937,"RandomY":1312111829,"RandomZ":355223143,"StartTime":44765.0,"Objects":[{"StartTime":44765.0,"EndTime":44765.0,"Column":2},{"StartTime":44765.0,"EndTime":44765.0,"Column":3}]},{"RandomW":2296450669,"RandomX":355223143,"RandomY":1197174504,"RandomZ":1876247766,"StartTime":45031.0,"Objects":[{"StartTime":45031.0,"EndTime":45031.0,"Column":1},{"StartTime":45031.0,"EndTime":45031.0,"Column":0}]},{"RandomW":1664705375,"RandomX":1876247766,"RandomY":2296450669,"RandomZ":4287200872,"StartTime":45296.0,"Objects":[{"StartTime":45296.0,"EndTime":45296.0,"Column":0},{"StartTime":45296.0,"EndTime":45296.0,"Column":4}]},{"RandomW":2786027546,"RandomX":2296450669,"RandomY":4287200872,"RandomZ":1664705375,"StartTime":45562.0,"Objects":[{"StartTime":45562.0,"EndTime":45562.0,"Column":1}]},{"RandomW":639469776,"RandomX":4287200872,"RandomY":1664705375,"RandomZ":2786027546,"StartTime":45827.0,"Objects":[{"StartTime":45827.0,"EndTime":45827.0,"Column":3},{"StartTime":45827.0,"EndTime":45827.0,"Column":4}]},{"RandomW":2463352901,"RandomX":1664705375,"RandomY":2786027546,"RandomZ":639469776,"StartTime":46093.0,"Objects":[{"StartTime":46093.0,"EndTime":46093.0,"Column":4}]},{"RandomW":760995091,"RandomX":2463352901,"RandomY":978871003,"RandomZ":3888812594,"StartTime":46358.0,"Objects":[{"StartTime":46358.0,"EndTime":46888.0,"Column":2}]},{"RandomW":3631307076,"RandomX":3888812594,"RandomY":760995091,"RandomZ":566667549,"StartTime":47420.0,"Objects":[{"StartTime":47420.0,"EndTime":47685.0,"Column":4}]},{"RandomW":2353216536,"RandomX":3631307076,"RandomY":1805196154,"RandomZ":2564415583,"StartTime":47951.0,"Objects":[{"StartTime":47951.0,"EndTime":48216.0,"Column":1},{"StartTime":47951.0,"EndTime":48216.0,"Column":0}]},{"RandomW":717730087,"RandomX":2353216536,"RandomY":3735744429,"RandomZ":2102099401,"StartTime":48482.0,"Objects":[{"StartTime":48482.0,"EndTime":49278.0,"Column":5},{"StartTime":48482.0,"EndTime":49278.0,"Column":2}]},{"RandomW":271333990,"RandomX":717730087,"RandomY":3220302747,"RandomZ":917482575,"StartTime":49544.0,"Objects":[{"StartTime":49544.0,"EndTime":49809.0,"Column":0}]},{"RandomW":937976203,"RandomX":917482575,"RandomY":271333990,"RandomZ":125173709,"StartTime":50075.0,"Objects":[{"StartTime":50075.0,"EndTime":50340.0,"Column":2}]},{"RandomW":2781059562,"RandomX":937976203,"RandomY":2087616237,"RandomZ":232817676,"StartTime":50606.0,"Objects":[{"StartTime":50606.0,"EndTime":51667.0,"Column":0},{"StartTime":50606.0,"EndTime":51667.0,"Column":1}]},{"RandomW":3511898336,"RandomX":2087616237,"RandomY":232817676,"RandomZ":2781059562,"StartTime":52730.0,"Objects":[{"StartTime":52730.0,"EndTime":52730.0,"Column":4}]},{"RandomW":623291556,"RandomX":3737503025,"RandomY":3607951873,"RandomZ":1857627587,"StartTime":53792.0,"Objects":[{"StartTime":53792.0,"EndTime":54322.0,"Column":5},{"StartTime":53792.0,"EndTime":54322.0,"Column":1}]},{"RandomW":3577350524,"RandomX":3607951873,"RandomY":1857627587,"RandomZ":623291556,"StartTime":54588.0,"Objects":[{"StartTime":54588.0,"EndTime":54588.0,"Column":2}]},{"RandomW":3611414219,"RandomX":1700150568,"RandomY":3261504380,"RandomZ":3526708248,"StartTime":54854.0,"Objects":[{"StartTime":54854.0,"EndTime":55384.0,"Column":3},{"StartTime":54854.0,"EndTime":55384.0,"Column":4}]},{"RandomW":4116828180,"RandomX":3526708248,"RandomY":3611414219,"RandomZ":53089910,"StartTime":55916.0,"Objects":[{"StartTime":55916.0,"EndTime":56446.0,"Column":5}]},{"RandomW":1419945944,"RandomX":53089910,"RandomY":4116828180,"RandomZ":2370574124,"StartTime":56978.0,"Objects":[{"StartTime":56978.0,"EndTime":57549.0,"Column":3}]},{"RandomW":4235330325,"RandomX":2370574124,"RandomY":1419945944,"RandomZ":124293788,"StartTime":58120.0,"Objects":[{"StartTime":58120.0,"EndTime":58405.0,"Column":5}]},{"RandomW":1354196818,"RandomX":124293788,"RandomY":4235330325,"RandomZ":292200128,"StartTime":58692.0,"Objects":[{"StartTime":58692.0,"EndTime":58973.0,"Column":3}]},{"RandomW":2131632245,"RandomX":292200128,"RandomY":1354196818,"RandomZ":319349674,"StartTime":59325.0,"Objects":[{"StartTime":59325.0,"EndTime":60170.0,"Column":5}]},{"RandomW":987180490,"RandomX":1354196818,"RandomY":319349674,"RandomZ":2131632245,"StartTime":60513.0,"Objects":[{"StartTime":60513.0,"EndTime":60513.0,"Column":3}]},{"RandomW":2247158810,"RandomX":2131632245,"RandomY":987180490,"RandomZ":3518058549,"StartTime":60778.0,"Objects":[{"StartTime":60778.0,"EndTime":61043.0,"Column":0}]},{"RandomW":2347989337,"RandomX":987180490,"RandomY":3518058549,"RandomZ":2247158810,"StartTime":61309.0,"Objects":[{"StartTime":61309.0,"EndTime":61309.0,"Column":3}]},{"RandomW":82954311,"RandomX":1403151684,"RandomY":1362150166,"RandomZ":1092174296,"StartTime":61840.0,"Objects":[{"StartTime":61840.0,"EndTime":62105.0,"Column":0}]},{"RandomW":408605211,"RandomX":82954311,"RandomY":1144587736,"RandomZ":2479248954,"StartTime":62371.0,"Objects":[{"StartTime":62371.0,"EndTime":62901.0,"Column":1}]},{"RandomW":2455999143,"RandomX":1144587736,"RandomY":2479248954,"RandomZ":408605211,"StartTime":63168.0,"Objects":[{"StartTime":63168.0,"EndTime":63168.0,"Column":2}]},{"RandomW":1898608481,"RandomX":2455999143,"RandomY":519590646,"RandomZ":3207504021,"StartTime":63433.0,"Objects":[{"StartTime":63433.0,"EndTime":63963.0,"Column":5}]},{"RandomW":601995191,"RandomX":3207504021,"RandomY":1898608481,"RandomZ":4283573577,"StartTime":64230.0,"Objects":[{"StartTime":64230.0,"EndTime":64230.0,"Column":5},{"StartTime":64230.0,"EndTime":64230.0,"Column":1}]},{"RandomW":3909194070,"RandomX":1898608481,"RandomY":4283573577,"RandomZ":601995191,"StartTime":64495.0,"Objects":[{"StartTime":64495.0,"EndTime":64495.0,"Column":3},{"StartTime":64495.0,"EndTime":64495.0,"Column":4}]},{"RandomW":3417465448,"RandomX":4283573577,"RandomY":601995191,"RandomZ":3909194070,"StartTime":64761.0,"Objects":[{"StartTime":64761.0,"EndTime":64761.0,"Column":4}]},{"RandomW":2779016762,"RandomX":601995191,"RandomY":3909194070,"RandomZ":3417465448,"StartTime":65026.0,"Objects":[{"StartTime":65026.0,"EndTime":65026.0,"Column":4},{"StartTime":65026.0,"EndTime":65026.0,"Column":5}]},{"RandomW":2346068278,"RandomX":3909194070,"RandomY":3417465448,"RandomZ":2779016762,"StartTime":65292.0,"Objects":[{"StartTime":65292.0,"EndTime":65292.0,"Column":3}]},{"RandomW":1857589819,"RandomX":3417465448,"RandomY":2779016762,"RandomZ":2346068278,"StartTime":65557.0,"Objects":[{"StartTime":65557.0,"EndTime":65557.0,"Column":4},{"StartTime":65557.0,"EndTime":65557.0,"Column":5}]},{"RandomW":910236838,"RandomX":2779016762,"RandomY":2346068278,"RandomZ":1857589819,"StartTime":66088.0,"Objects":[{"StartTime":66088.0,"EndTime":66088.0,"Column":2},{"StartTime":66088.0,"EndTime":66088.0,"Column":3}]},{"RandomW":910236838,"RandomX":2779016762,"RandomY":2346068278,"RandomZ":1857589819,"StartTime":66354.0,"Objects":[{"StartTime":66354.0,"EndTime":66354.0,"Column":3},{"StartTime":66354.0,"EndTime":66354.0,"Column":2}]},{"RandomW":2327273799,"RandomX":1857589819,"RandomY":910236838,"RandomZ":2953998826,"StartTime":66619.0,"Objects":[{"StartTime":66619.0,"EndTime":67149.0,"Column":0}]},{"RandomW":540283744,"RandomX":910236838,"RandomY":2953998826,"RandomZ":2327273799,"StartTime":67416.0,"Objects":[{"StartTime":67416.0,"EndTime":67416.0,"Column":0}]},{"RandomW":1024467186,"RandomX":2327273799,"RandomY":540283744,"RandomZ":514760684,"StartTime":67681.0,"Objects":[{"StartTime":67681.0,"EndTime":68211.0,"Column":2}]},{"RandomW":211600206,"RandomX":540283744,"RandomY":514760684,"RandomZ":1024467186,"StartTime":68478.0,"Objects":[{"StartTime":68478.0,"EndTime":68478.0,"Column":2}]},{"RandomW":2360573614,"RandomX":514760684,"RandomY":1024467186,"RandomZ":211600206,"StartTime":68743.0,"Objects":[{"StartTime":68743.0,"EndTime":68743.0,"Column":4},{"StartTime":68743.0,"EndTime":68743.0,"Column":5}]},{"RandomW":3867722027,"RandomX":1024467186,"RandomY":211600206,"RandomZ":2360573614,"StartTime":69009.0,"Objects":[{"StartTime":69009.0,"EndTime":69009.0,"Column":3}]},{"RandomW":1512274616,"RandomX":211600206,"RandomY":2360573614,"RandomZ":3867722027,"StartTime":69274.0,"Objects":[{"StartTime":69274.0,"EndTime":69274.0,"Column":4},{"StartTime":69274.0,"EndTime":69274.0,"Column":5}]},{"RandomW":2957984769,"RandomX":2360573614,"RandomY":3867722027,"RandomZ":1512274616,"StartTime":69540.0,"Objects":[{"StartTime":69540.0,"EndTime":69540.0,"Column":3}]},{"RandomW":2803767976,"RandomX":3867722027,"RandomY":1512274616,"RandomZ":2957984769,"StartTime":69805.0,"Objects":[{"StartTime":69805.0,"EndTime":69805.0,"Column":4},{"StartTime":69805.0,"EndTime":69805.0,"Column":5}]},{"RandomW":1183341084,"RandomX":2957984769,"RandomY":2803767976,"RandomZ":121575161,"StartTime":70336.0,"Objects":[{"StartTime":70336.0,"EndTime":70601.0,"Column":3}]},{"RandomW":3685872119,"RandomX":121575161,"RandomY":1183341084,"RandomZ":2351788416,"StartTime":70867.0,"Objects":[{"StartTime":70867.0,"EndTime":71397.0,"Column":4}]},{"RandomW":617004198,"RandomX":1183341084,"RandomY":2351788416,"RandomZ":3685872119,"StartTime":71663.0,"Objects":[{"StartTime":71663.0,"EndTime":71663.0,"Column":3}]},{"RandomW":2478235967,"RandomX":617004198,"RandomY":546986648,"RandomZ":3353120378,"StartTime":71929.0,"Objects":[{"StartTime":71929.0,"EndTime":72459.0,"Column":0}]},{"RandomW":2189712483,"RandomX":546986648,"RandomY":3353120378,"RandomZ":2478235967,"StartTime":72725.0,"Objects":[{"StartTime":72725.0,"EndTime":72725.0,"Column":2}]},{"RandomW":1882757169,"RandomX":3353120378,"RandomY":2478235967,"RandomZ":2189712483,"StartTime":72991.0,"Objects":[{"StartTime":72991.0,"EndTime":72991.0,"Column":3},{"StartTime":72991.0,"EndTime":72991.0,"Column":4}]},{"RandomW":1404331794,"RandomX":2478235967,"RandomY":2189712483,"RandomZ":1882757169,"StartTime":73256.0,"Objects":[{"StartTime":73256.0,"EndTime":73256.0,"Column":1}]},{"RandomW":1999620930,"RandomX":2189712483,"RandomY":1882757169,"RandomZ":1404331794,"StartTime":73522.0,"Objects":[{"StartTime":73522.0,"EndTime":73522.0,"Column":3},{"StartTime":73522.0,"EndTime":73522.0,"Column":4}]},{"RandomW":3622364800,"RandomX":1882757169,"RandomY":1404331794,"RandomZ":1999620930,"StartTime":73787.0,"Objects":[{"StartTime":73787.0,"EndTime":73787.0,"Column":2}]},{"RandomW":1671763292,"RandomX":1404331794,"RandomY":1999620930,"RandomZ":3622364800,"StartTime":74053.0,"Objects":[{"StartTime":74053.0,"EndTime":74053.0,"Column":3},{"StartTime":74053.0,"EndTime":74053.0,"Column":4}]},{"RandomW":2594561583,"RandomX":3622364800,"RandomY":1671763292,"RandomZ":2480497357,"StartTime":74584.0,"Objects":[{"StartTime":74584.0,"EndTime":74849.0,"Column":1}]},{"RandomW":1101860073,"RandomX":2480497357,"RandomY":2594561583,"RandomZ":183105309,"StartTime":75115.0,"Objects":[{"StartTime":75115.0,"EndTime":75645.0,"Column":3}]},{"RandomW":423280923,"RandomX":2594561583,"RandomY":183105309,"RandomZ":1101860073,"StartTime":75911.0,"Objects":[{"StartTime":75911.0,"EndTime":75911.0,"Column":2}]},{"RandomW":3905841932,"RandomX":1101860073,"RandomY":423280923,"RandomZ":2916757685,"StartTime":76177.0,"Objects":[{"StartTime":76177.0,"EndTime":76707.0,"Column":4}]},{"RandomW":3241015480,"RandomX":423280923,"RandomY":2916757685,"RandomZ":3905841932,"StartTime":76973.0,"Objects":[{"StartTime":76973.0,"EndTime":76973.0,"Column":3}]},{"RandomW":1928531304,"RandomX":3905841932,"RandomY":3241015480,"RandomZ":248564639,"StartTime":77239.0,"Objects":[{"StartTime":77239.0,"EndTime":77504.0,"Column":5}]},{"RandomW":634267655,"RandomX":3925777969,"RandomY":1203262350,"RandomZ":3485263061,"StartTime":77770.0,"Objects":[{"StartTime":77770.0,"EndTime":78035.0,"Column":3},{"StartTime":77770.0,"EndTime":78035.0,"Column":1}]},{"RandomW":953955737,"RandomX":1203262350,"RandomY":3485263061,"RandomZ":634267655,"StartTime":78301.0,"Objects":[{"StartTime":78301.0,"EndTime":78301.0,"Column":3}]},{"RandomW":3179099439,"RandomX":3485263061,"RandomY":634267655,"RandomZ":953955737,"StartTime":78566.0,"Objects":[{"StartTime":78566.0,"EndTime":78566.0,"Column":2},{"StartTime":78566.0,"EndTime":78566.0,"Column":3}]},{"RandomW":2513433625,"RandomX":634267655,"RandomY":953955737,"RandomZ":3179099439,"StartTime":78832.0,"Objects":[{"StartTime":78832.0,"EndTime":78832.0,"Column":3},{"StartTime":78832.0,"EndTime":78832.0,"Column":4}]},{"RandomW":3239409847,"RandomX":953955737,"RandomY":3179099439,"RandomZ":2513433625,"StartTime":79097.0,"Objects":[{"StartTime":79097.0,"EndTime":79097.0,"Column":5},{"StartTime":79097.0,"EndTime":79097.0,"Column":0}]},{"RandomW":1279031172,"RandomX":2513433625,"RandomY":3239409847,"RandomZ":415034865,"StartTime":79363.0,"Objects":[{"StartTime":79363.0,"EndTime":79893.0,"Column":3}]},{"RandomW":2797153574,"RandomX":3239409847,"RandomY":415034865,"RandomZ":1279031172,"StartTime":80159.0,"Objects":[{"StartTime":80159.0,"EndTime":80159.0,"Column":3}]},{"RandomW":858752658,"RandomX":1279031172,"RandomY":2797153574,"RandomZ":3422759302,"StartTime":80424.0,"Objects":[{"StartTime":80424.0,"EndTime":80954.0,"Column":2}]},{"RandomW":2617268004,"RandomX":2797153574,"RandomY":3422759302,"RandomZ":858752658,"StartTime":81221.0,"Objects":[{"StartTime":81221.0,"EndTime":81221.0,"Column":4}]},{"RandomW":4089416095,"RandomX":3422759302,"RandomY":858752658,"RandomZ":2617268004,"StartTime":81486.0,"Objects":[{"StartTime":81486.0,"EndTime":81486.0,"Column":4},{"StartTime":81486.0,"EndTime":81486.0,"Column":5}]},{"RandomW":640008567,"RandomX":858752658,"RandomY":2617268004,"RandomZ":4089416095,"StartTime":81752.0,"Objects":[{"StartTime":81752.0,"EndTime":81752.0,"Column":4}]},{"RandomW":1769064503,"RandomX":2617268004,"RandomY":4089416095,"RandomZ":640008567,"StartTime":82017.0,"Objects":[{"StartTime":82017.0,"EndTime":82017.0,"Column":5},{"StartTime":82017.0,"EndTime":82017.0,"Column":0}]},{"RandomW":4171929422,"RandomX":640008567,"RandomY":1769064503,"RandomZ":4149611338,"StartTime":82283.0,"Objects":[{"StartTime":82283.0,"EndTime":82283.0,"Column":3},{"StartTime":82283.0,"EndTime":82283.0,"Column":5}]},{"RandomW":4035764053,"RandomX":1769064503,"RandomY":4149611338,"RandomZ":4171929422,"StartTime":82548.0,"Objects":[{"StartTime":82548.0,"EndTime":82548.0,"Column":5},{"StartTime":82548.0,"EndTime":82548.0,"Column":0}]},{"RandomW":391872771,"RandomX":4149611338,"RandomY":4171929422,"RandomZ":4035764053,"StartTime":83079.0,"Objects":[{"StartTime":83079.0,"EndTime":83079.0,"Column":3},{"StartTime":83079.0,"EndTime":83079.0,"Column":4}]},{"RandomW":391872771,"RandomX":4149611338,"RandomY":4171929422,"RandomZ":4035764053,"StartTime":83345.0,"Objects":[{"StartTime":83345.0,"EndTime":83345.0,"Column":2},{"StartTime":83345.0,"EndTime":83345.0,"Column":1}]},{"RandomW":4239141202,"RandomX":4035764053,"RandomY":391872771,"RandomZ":1343280377,"StartTime":83610.0,"Objects":[{"StartTime":83610.0,"EndTime":84140.0,"Column":5}]},{"RandomW":2008371177,"RandomX":4239141202,"RandomY":1783379941,"RandomZ":2715086902,"StartTime":84407.0,"Objects":[{"StartTime":84407.0,"EndTime":84407.0,"Column":1},{"StartTime":84407.0,"EndTime":84407.0,"Column":5}]},{"RandomW":980563717,"RandomX":3939376884,"RandomY":3778473815,"RandomZ":3882214919,"StartTime":84672.0,"Objects":[{"StartTime":84672.0,"EndTime":85202.0,"Column":4},{"StartTime":84672.0,"EndTime":85202.0,"Column":2}]},{"RandomW":2698098433,"RandomX":3778473815,"RandomY":3882214919,"RandomZ":980563717,"StartTime":85469.0,"Objects":[{"StartTime":85469.0,"EndTime":85469.0,"Column":1}]},{"RandomW":4140546075,"RandomX":3882214919,"RandomY":980563717,"RandomZ":2698098433,"StartTime":85734.0,"Objects":[{"StartTime":85734.0,"EndTime":85734.0,"Column":3},{"StartTime":85734.0,"EndTime":85734.0,"Column":4}]},{"RandomW":1045835035,"RandomX":980563717,"RandomY":2698098433,"RandomZ":4140546075,"StartTime":86000.0,"Objects":[{"StartTime":86000.0,"EndTime":86000.0,"Column":1}]},{"RandomW":2503475147,"RandomX":2698098433,"RandomY":4140546075,"RandomZ":1045835035,"StartTime":86265.0,"Objects":[{"StartTime":86265.0,"EndTime":86265.0,"Column":1},{"StartTime":86265.0,"EndTime":86265.0,"Column":2}]},{"RandomW":3094559699,"RandomX":4140546075,"RandomY":1045835035,"RandomZ":2503475147,"StartTime":86531.0,"Objects":[{"StartTime":86531.0,"EndTime":86531.0,"Column":3}]},{"RandomW":332613542,"RandomX":1045835035,"RandomY":2503475147,"RandomZ":3094559699,"StartTime":86796.0,"Objects":[{"StartTime":86796.0,"EndTime":86796.0,"Column":2},{"StartTime":86796.0,"EndTime":86796.0,"Column":3}]},{"RandomW":2534271858,"RandomX":332613542,"RandomY":2623704626,"RandomZ":3061969874,"StartTime":87327.0,"Objects":[{"StartTime":87327.0,"EndTime":87592.0,"Column":1}]},{"RandomW":794230988,"RandomX":2534271858,"RandomY":510287938,"RandomZ":2532404899,"StartTime":87858.0,"Objects":[{"StartTime":87858.0,"EndTime":88388.0,"Column":2}]},{"RandomW":3623430191,"RandomX":510287938,"RandomY":2532404899,"RandomZ":794230988,"StartTime":88655.0,"Objects":[{"StartTime":88655.0,"EndTime":88655.0,"Column":2}]},{"RandomW":2269498220,"RandomX":794230988,"RandomY":3623430191,"RandomZ":2598120162,"StartTime":88920.0,"Objects":[{"StartTime":88920.0,"EndTime":89450.0,"Column":0}]},{"RandomW":277080616,"RandomX":3623430191,"RandomY":2598120162,"RandomZ":2269498220,"StartTime":89717.0,"Objects":[{"StartTime":89717.0,"EndTime":89717.0,"Column":2}]},{"RandomW":237305927,"RandomX":2598120162,"RandomY":2269498220,"RandomZ":277080616,"StartTime":89982.0,"Objects":[{"StartTime":89982.0,"EndTime":89982.0,"Column":1},{"StartTime":89982.0,"EndTime":89982.0,"Column":2}]},{"RandomW":3697412902,"RandomX":277080616,"RandomY":237305927,"RandomZ":1976938587,"StartTime":90247.0,"Objects":[{"StartTime":90247.0,"EndTime":90247.0,"Column":1},{"StartTime":90247.0,"EndTime":90247.0,"Column":4}]},{"RandomW":3552536616,"RandomX":237305927,"RandomY":1976938587,"RandomZ":3697412902,"StartTime":90513.0,"Objects":[{"StartTime":90513.0,"EndTime":90513.0,"Column":2},{"StartTime":90513.0,"EndTime":90513.0,"Column":3}]},{"RandomW":758205604,"RandomX":3697412902,"RandomY":3552536616,"RandomZ":4122897696,"StartTime":90778.0,"Objects":[{"StartTime":90778.0,"EndTime":90778.0,"Column":1},{"StartTime":90778.0,"EndTime":90778.0,"Column":2}]},{"RandomW":3787868447,"RandomX":3552536616,"RandomY":4122897696,"RandomZ":758205604,"StartTime":91044.0,"Objects":[{"StartTime":91044.0,"EndTime":91044.0,"Column":2},{"StartTime":91044.0,"EndTime":91044.0,"Column":3}]},{"RandomW":1748107640,"RandomX":3787868447,"RandomY":3373302567,"RandomZ":3485540424,"StartTime":91575.0,"Objects":[{"StartTime":91575.0,"EndTime":91840.0,"Column":4}]},{"RandomW":4130051617,"RandomX":3485540424,"RandomY":1748107640,"RandomZ":3144627152,"StartTime":92106.0,"Objects":[{"StartTime":92106.0,"EndTime":92636.0,"Column":5}]},{"RandomW":808332236,"RandomX":1748107640,"RandomY":3144627152,"RandomZ":4130051617,"StartTime":92902.0,"Objects":[{"StartTime":92902.0,"EndTime":92902.0,"Column":3}]},{"RandomW":182226446,"RandomX":4130051617,"RandomY":808332236,"RandomZ":3371160944,"StartTime":93168.0,"Objects":[{"StartTime":93168.0,"EndTime":93698.0,"Column":0}]},{"RandomW":2699856874,"RandomX":808332236,"RandomY":3371160944,"RandomZ":182226446,"StartTime":93964.0,"Objects":[{"StartTime":93964.0,"EndTime":93964.0,"Column":1}]},{"RandomW":3110990203,"RandomX":2699856874,"RandomY":3789399152,"RandomZ":1462741358,"StartTime":94230.0,"Objects":[{"StartTime":94230.0,"EndTime":94495.0,"Column":4},{"StartTime":94230.0,"EndTime":94495.0,"Column":2}]},{"RandomW":2375429180,"RandomX":2098892391,"RandomY":1911053200,"RandomZ":1537665050,"StartTime":94761.0,"Objects":[{"StartTime":94761.0,"EndTime":95026.0,"Column":5},{"StartTime":94761.0,"EndTime":95026.0,"Column":0}]},{"RandomW":391186846,"RandomX":1537665050,"RandomY":2375429180,"RandomZ":609673823,"StartTime":95292.0,"Objects":[{"StartTime":95292.0,"EndTime":96353.0,"Column":1}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":96486.0,"Objects":[{"StartTime":96486.0,"EndTime":98478.0,"Column":5}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":113345.0,"Objects":[{"StartTime":113345.0,"EndTime":113345.0,"Column":4}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":113876.0,"Objects":[{"StartTime":113876.0,"EndTime":113876.0,"Column":1}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":114407.0,"Objects":[{"StartTime":114407.0,"EndTime":114407.0,"Column":4}]},{"RandomW":1192288733,"RandomX":609673823,"RandomY":391186846,"RandomZ":2078004566,"StartTime":114672.0,"Objects":[{"StartTime":114672.0,"EndTime":114672.0,"Column":2},{"StartTime":114672.0,"EndTime":114672.0,"Column":3}]},{"RandomW":3569858426,"RandomX":391186846,"RandomY":2078004566,"RandomZ":1192288733,"StartTime":114938.0,"Objects":[{"StartTime":114938.0,"EndTime":114938.0,"Column":2}]},{"RandomW":1262832005,"RandomX":2078004566,"RandomY":1192288733,"RandomZ":3569858426,"StartTime":115203.0,"Objects":[{"StartTime":115203.0,"EndTime":115203.0,"Column":3},{"StartTime":115203.0,"EndTime":115203.0,"Column":4}]},{"RandomW":4002501854,"RandomX":1192288733,"RandomY":3569858426,"RandomZ":1262832005,"StartTime":115469.0,"Objects":[{"StartTime":115469.0,"EndTime":115469.0,"Column":3},{"StartTime":115469.0,"EndTime":115469.0,"Column":4}]},{"RandomW":776953560,"RandomX":3569858426,"RandomY":1262832005,"RandomZ":4002501854,"StartTime":116000.0,"Objects":[{"StartTime":116000.0,"EndTime":116000.0,"Column":3},{"StartTime":116000.0,"EndTime":116000.0,"Column":4}]},{"RandomW":776953560,"RandomX":3569858426,"RandomY":1262832005,"RandomZ":4002501854,"StartTime":116531.0,"Objects":[{"StartTime":116531.0,"EndTime":116531.0,"Column":2},{"StartTime":116531.0,"EndTime":116531.0,"Column":1}]},{"RandomW":3352969228,"RandomX":1262832005,"RandomY":4002501854,"RandomZ":776953560,"StartTime":117062.0,"Objects":[{"StartTime":117062.0,"EndTime":117062.0,"Column":3},{"StartTime":117062.0,"EndTime":117062.0,"Column":4}]},{"RandomW":2796695571,"RandomX":4002501854,"RandomY":776953560,"RandomZ":3352969228,"StartTime":117327.0,"Objects":[{"StartTime":117327.0,"EndTime":117327.0,"Column":2}]},{"RandomW":3269572543,"RandomX":776953560,"RandomY":3352969228,"RandomZ":2796695571,"StartTime":117593.0,"Objects":[{"StartTime":117593.0,"EndTime":117593.0,"Column":4},{"StartTime":117593.0,"EndTime":117593.0,"Column":5}]},{"RandomW":3269572543,"RandomX":776953560,"RandomY":3352969228,"RandomZ":2796695571,"StartTime":118124.0,"Objects":[{"StartTime":118124.0,"EndTime":118124.0,"Column":1},{"StartTime":118124.0,"EndTime":118124.0,"Column":0}]},{"RandomW":3269572543,"RandomX":776953560,"RandomY":3352969228,"RandomZ":2796695571,"StartTime":118655.0,"Objects":[{"StartTime":118655.0,"EndTime":118655.0,"Column":5},{"StartTime":118655.0,"EndTime":118655.0,"Column":4}]},{"RandomW":2517403813,"RandomX":3352969228,"RandomY":2796695571,"RandomZ":3269572543,"StartTime":118920.0,"Objects":[{"StartTime":118920.0,"EndTime":118920.0,"Column":2},{"StartTime":118920.0,"EndTime":118920.0,"Column":3}]},{"RandomW":2210619464,"RandomX":2796695571,"RandomY":3269572543,"RandomZ":2517403813,"StartTime":119186.0,"Objects":[{"StartTime":119186.0,"EndTime":119186.0,"Column":4}]},{"RandomW":3032935051,"RandomX":3269572543,"RandomY":2517403813,"RandomZ":2210619464,"StartTime":119451.0,"Objects":[{"StartTime":119451.0,"EndTime":119451.0,"Column":5},{"StartTime":119451.0,"EndTime":119451.0,"Column":0}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":119717.0,"Objects":[{"StartTime":119717.0,"EndTime":119717.0,"Column":4},{"StartTime":119717.0,"EndTime":119717.0,"Column":5}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":120247.0,"Objects":[{"StartTime":120247.0,"EndTime":120247.0,"Column":1},{"StartTime":120247.0,"EndTime":120247.0,"Column":0}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":120778.0,"Objects":[{"StartTime":120778.0,"EndTime":120778.0,"Column":5},{"StartTime":120778.0,"EndTime":120778.0,"Column":4}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":121309.0,"Objects":[{"StartTime":121309.0,"EndTime":121309.0,"Column":1},{"StartTime":121309.0,"EndTime":121309.0,"Column":0}]},{"RandomW":2314078604,"RandomX":2210619464,"RandomY":3032935051,"RandomZ":2069229539,"StartTime":121575.0,"Objects":[{"StartTime":121575.0,"EndTime":121575.0,"Column":3}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":121840.0,"Objects":[{"StartTime":121840.0,"EndTime":121840.0,"Column":2},{"StartTime":121840.0,"EndTime":121840.0,"Column":3}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":122371.0,"Objects":[{"StartTime":122371.0,"EndTime":122371.0,"Column":3},{"StartTime":122371.0,"EndTime":122371.0,"Column":2}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":122902.0,"Objects":[{"StartTime":122902.0,"EndTime":122902.0,"Column":3},{"StartTime":122902.0,"EndTime":122902.0,"Column":2}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":123433.0,"Objects":[{"StartTime":123433.0,"EndTime":123433.0,"Column":3},{"StartTime":123433.0,"EndTime":123433.0,"Column":2}]},{"RandomW":2460408790,"RandomX":2069229539,"RandomY":2314078604,"RandomZ":297269721,"StartTime":123699.0,"Objects":[{"StartTime":123699.0,"EndTime":123699.0,"Column":1}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":123964.0,"Objects":[{"StartTime":123964.0,"EndTime":123964.0,"Column":3},{"StartTime":123964.0,"EndTime":123964.0,"Column":4}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":124495.0,"Objects":[{"StartTime":124495.0,"EndTime":124495.0,"Column":2},{"StartTime":124495.0,"EndTime":124495.0,"Column":1}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":125026.0,"Objects":[{"StartTime":125026.0,"EndTime":125026.0,"Column":4},{"StartTime":125026.0,"EndTime":125026.0,"Column":3}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":125557.0,"Objects":[{"StartTime":125557.0,"EndTime":125557.0,"Column":2},{"StartTime":125557.0,"EndTime":125557.0,"Column":1}]},{"RandomW":3204700088,"RandomX":297269721,"RandomY":2460408790,"RandomZ":1180177558,"StartTime":125823.0,"Objects":[{"StartTime":125823.0,"EndTime":125823.0,"Column":2}]},{"RandomW":299141296,"RandomX":2460408790,"RandomY":1180177558,"RandomZ":3204700088,"StartTime":126088.0,"Objects":[{"StartTime":126088.0,"EndTime":126088.0,"Column":3},{"StartTime":126088.0,"EndTime":126088.0,"Column":4}]},{"RandomW":299141296,"RandomX":2460408790,"RandomY":1180177558,"RandomZ":3204700088,"StartTime":126619.0,"Objects":[{"StartTime":126619.0,"EndTime":126619.0,"Column":2},{"StartTime":126619.0,"EndTime":126619.0,"Column":1}]},{"RandomW":299141296,"RandomX":2460408790,"RandomY":1180177558,"RandomZ":3204700088,"StartTime":127150.0,"Objects":[{"StartTime":127150.0,"EndTime":127150.0,"Column":4},{"StartTime":127150.0,"EndTime":127150.0,"Column":3}]},{"RandomW":3037239607,"RandomX":1180177558,"RandomY":3204700088,"RandomZ":299141296,"StartTime":127416.0,"Objects":[{"StartTime":127416.0,"EndTime":127416.0,"Column":4},{"StartTime":127416.0,"EndTime":127416.0,"Column":5}]},{"RandomW":863164324,"RandomX":3204700088,"RandomY":299141296,"RandomZ":3037239607,"StartTime":127681.0,"Objects":[{"StartTime":127681.0,"EndTime":127681.0,"Column":5}]},{"RandomW":2456647781,"RandomX":299141296,"RandomY":3037239607,"RandomZ":863164324,"StartTime":127947.0,"Objects":[{"StartTime":127947.0,"EndTime":127947.0,"Column":4},{"StartTime":127947.0,"EndTime":127947.0,"Column":5}]},{"RandomW":659157904,"RandomX":3037239607,"RandomY":863164324,"RandomZ":2456647781,"StartTime":128212.0,"Objects":[{"StartTime":128212.0,"EndTime":128212.0,"Column":3},{"StartTime":128212.0,"EndTime":128212.0,"Column":4}]},{"RandomW":659157904,"RandomX":3037239607,"RandomY":863164324,"RandomZ":2456647781,"StartTime":128743.0,"Objects":[{"StartTime":128743.0,"EndTime":128743.0,"Column":2},{"StartTime":128743.0,"EndTime":128743.0,"Column":1}]},{"RandomW":659157904,"RandomX":3037239607,"RandomY":863164324,"RandomZ":2456647781,"StartTime":129274.0,"Objects":[{"StartTime":129274.0,"EndTime":129274.0,"Column":4},{"StartTime":129274.0,"EndTime":129274.0,"Column":3}]},{"RandomW":3598260079,"RandomX":863164324,"RandomY":2456647781,"RandomZ":659157904,"StartTime":129540.0,"Objects":[{"StartTime":129540.0,"EndTime":129540.0,"Column":3},{"StartTime":129540.0,"EndTime":129540.0,"Column":4}]},{"RandomW":1930638835,"RandomX":2456647781,"RandomY":659157904,"RandomZ":3598260079,"StartTime":129805.0,"Objects":[{"StartTime":129805.0,"EndTime":129805.0,"Column":1},{"StartTime":129805.0,"EndTime":129805.0,"Column":2}]},{"RandomW":4230333264,"RandomX":1930638835,"RandomY":2319762852,"RandomZ":3807998479,"StartTime":130071.0,"Objects":[{"StartTime":130071.0,"EndTime":130071.0,"Column":2},{"StartTime":130071.0,"EndTime":130071.0,"Column":3}]},{"RandomW":2482386774,"RandomX":4230333264,"RandomY":376688010,"RandomZ":3132506885,"StartTime":132460.0,"Objects":[{"StartTime":132460.0,"EndTime":132990.0,"Column":0}]},{"RandomW":3381449487,"RandomX":3132506885,"RandomY":2482386774,"RandomZ":1092311355,"StartTime":133522.0,"Objects":[{"StartTime":133522.0,"EndTime":134052.0,"Column":3}]},{"RandomW":3812940964,"RandomX":1092311355,"RandomY":3381449487,"RandomZ":3240759120,"StartTime":134318.0,"Objects":[{"StartTime":134318.0,"EndTime":134848.0,"Column":4}]},{"RandomW":2199106412,"RandomX":2014155638,"RandomY":3619038163,"RandomZ":1182263034,"StartTime":135115.0,"Objects":[{"StartTime":135115.0,"EndTime":135380.0,"Column":3},{"StartTime":135115.0,"EndTime":135380.0,"Column":0}]},{"RandomW":4049541057,"RandomX":1182263034,"RandomY":2199106412,"RandomZ":2542868059,"StartTime":135646.0,"Objects":[{"StartTime":135646.0,"EndTime":136176.0,"Column":5}]},{"RandomW":376448389,"RandomX":2542868059,"RandomY":4049541057,"RandomZ":149323558,"StartTime":136708.0,"Objects":[{"StartTime":136708.0,"EndTime":136973.0,"Column":1}]},{"RandomW":10761513,"RandomX":149323558,"RandomY":376448389,"RandomZ":156027614,"StartTime":137239.0,"Objects":[{"StartTime":137239.0,"EndTime":137504.0,"Column":0}]},{"RandomW":2890609580,"RandomX":156027614,"RandomY":10761513,"RandomZ":998270292,"StartTime":137770.0,"Objects":[{"StartTime":137770.0,"EndTime":138566.0,"Column":2}]},{"RandomW":3792858866,"RandomX":998270292,"RandomY":2890609580,"RandomZ":3275622081,"StartTime":138832.0,"Objects":[{"StartTime":138832.0,"EndTime":139097.0,"Column":4}]},{"RandomW":479756469,"RandomX":3792858866,"RandomY":3665829153,"RandomZ":799245198,"StartTime":139363.0,"Objects":[{"StartTime":139363.0,"EndTime":139628.0,"Column":2},{"StartTime":139363.0,"EndTime":139628.0,"Column":1}]},{"RandomW":1559664190,"RandomX":1837897770,"RandomY":3074386351,"RandomZ":2226336565,"StartTime":139894.0,"Objects":[{"StartTime":139894.0,"EndTime":140690.0,"Column":0},{"StartTime":139894.0,"EndTime":140690.0,"Column":4}]},{"RandomW":1370921154,"RandomX":3074386351,"RandomY":2226336565,"RandomZ":1559664190,"StartTime":140955.0,"Objects":[{"StartTime":140955.0,"EndTime":140955.0,"Column":4}]},{"RandomW":12534613,"RandomX":1559664190,"RandomY":1370921154,"RandomZ":495513930,"StartTime":141221.0,"Objects":[{"StartTime":141221.0,"EndTime":141751.0,"Column":3},{"StartTime":141486.0,"EndTime":141486.0,"Column":1},{"StartTime":141751.0,"EndTime":141751.0,"Column":1}]},{"RandomW":1474110729,"RandomX":12534613,"RandomY":3893387802,"RandomZ":226854738,"StartTime":142017.0,"Objects":[{"StartTime":142017.0,"EndTime":142017.0,"Column":2},{"StartTime":142017.0,"EndTime":142017.0,"Column":3}]},{"RandomW":3883366092,"RandomX":1474110729,"RandomY":2911002956,"RandomZ":3337209428,"StartTime":142283.0,"Objects":[{"StartTime":142283.0,"EndTime":142548.0,"Column":4}]},{"RandomW":1868157439,"RandomX":3883366092,"RandomY":1497166406,"RandomZ":3876220972,"StartTime":142814.0,"Objects":[{"StartTime":142814.0,"EndTime":143079.0,"Column":5}]},{"RandomW":868486094,"RandomX":1497166406,"RandomY":3876220972,"RandomZ":1868157439,"StartTime":143345.0,"Objects":[{"StartTime":143345.0,"EndTime":143345.0,"Column":2}]},{"RandomW":2379505970,"RandomX":3876220972,"RandomY":1868157439,"RandomZ":868486094,"StartTime":143610.0,"Objects":[{"StartTime":143610.0,"EndTime":143610.0,"Column":2}]},{"RandomW":971762612,"RandomX":1868157439,"RandomY":868486094,"RandomZ":2379505970,"StartTime":143876.0,"Objects":[{"StartTime":143876.0,"EndTime":143876.0,"Column":4}]},{"RandomW":2333467129,"RandomX":2379505970,"RandomY":971762612,"RandomZ":2560365407,"StartTime":144141.0,"Objects":[{"StartTime":144141.0,"EndTime":144671.0,"Column":0}]},{"RandomW":3275109659,"RandomX":2560365407,"RandomY":2333467129,"RandomZ":2783370328,"StartTime":145203.0,"Objects":[{"StartTime":145203.0,"EndTime":145468.0,"Column":3}]},{"RandomW":2675369072,"RandomX":2783370328,"RandomY":3275109659,"RandomZ":3142107337,"StartTime":145734.0,"Objects":[{"StartTime":145734.0,"EndTime":145999.0,"Column":1}]},{"RandomW":2114821552,"RandomX":3142107337,"RandomY":2675369072,"RandomZ":216133594,"StartTime":146265.0,"Objects":[{"StartTime":146265.0,"EndTime":146795.0,"Column":5}]},{"RandomW":2210288688,"RandomX":2675369072,"RandomY":216133594,"RandomZ":2114821552,"StartTime":147062.0,"Objects":[{"StartTime":147062.0,"EndTime":147062.0,"Column":3}]},{"RandomW":2824847566,"RandomX":2114821552,"RandomY":2210288688,"RandomZ":2881713491,"StartTime":147327.0,"Objects":[{"StartTime":147327.0,"EndTime":147592.0,"Column":1}]},{"RandomW":3418617049,"RandomX":2881713491,"RandomY":2824847566,"RandomZ":3131910248,"StartTime":147858.0,"Objects":[{"StartTime":147858.0,"EndTime":148123.0,"Column":3}]},{"RandomW":4264037536,"RandomX":3418617049,"RandomY":2065328415,"RandomZ":756387586,"StartTime":148389.0,"Objects":[{"StartTime":148389.0,"EndTime":149450.0,"Column":2},{"StartTime":148389.0,"EndTime":149450.0,"Column":5}]},{"RandomW":714689152,"RandomX":2065328415,"RandomY":756387586,"RandomZ":4264037536,"StartTime":149717.0,"Objects":[{"StartTime":149717.0,"EndTime":149717.0,"Column":2}]},{"RandomW":2187562077,"RandomX":756387586,"RandomY":4264037536,"RandomZ":714689152,"StartTime":149982.0,"Objects":[{"StartTime":149982.0,"EndTime":149982.0,"Column":1},{"StartTime":149982.0,"EndTime":149982.0,"Column":2}]},{"RandomW":59731596,"RandomX":4264037536,"RandomY":714689152,"RandomZ":2187562077,"StartTime":150247.0,"Objects":[{"StartTime":150247.0,"EndTime":150247.0,"Column":0}]},{"RandomW":3179032401,"RandomX":714689152,"RandomY":2187562077,"RandomZ":59731596,"StartTime":150513.0,"Objects":[{"StartTime":150513.0,"EndTime":150513.0,"Column":1}]},{"RandomW":1565638452,"RandomX":2187562077,"RandomY":59731596,"RandomZ":3179032401,"StartTime":150778.0,"Objects":[{"StartTime":150778.0,"EndTime":150778.0,"Column":2}]},{"RandomW":3285111207,"RandomX":59731596,"RandomY":3179032401,"RandomZ":1565638452,"StartTime":151044.0,"Objects":[{"StartTime":151044.0,"EndTime":151044.0,"Column":3},{"StartTime":151044.0,"EndTime":151044.0,"Column":4}]},{"RandomW":3142401116,"RandomX":3179032401,"RandomY":1565638452,"RandomZ":3285111207,"StartTime":151309.0,"Objects":[{"StartTime":151309.0,"EndTime":151309.0,"Column":4}]},{"RandomW":2191101353,"RandomX":3142401116,"RandomY":3877079747,"RandomZ":930029834,"StartTime":151575.0,"Objects":[{"StartTime":151575.0,"EndTime":152105.0,"Column":2},{"StartTime":151575.0,"EndTime":152105.0,"Column":0}]},{"RandomW":1171726387,"RandomX":2191101353,"RandomY":1357180538,"RandomZ":201209655,"StartTime":152637.0,"Objects":[{"StartTime":152637.0,"EndTime":152902.0,"Column":3}]},{"RandomW":2089660876,"RandomX":201209655,"RandomY":1171726387,"RandomZ":191699429,"StartTime":153168.0,"Objects":[{"StartTime":153168.0,"EndTime":153698.0,"Column":5}]},{"RandomW":2251323109,"RandomX":1171726387,"RandomY":191699429,"RandomZ":2089660876,"StartTime":153964.0,"Objects":[{"StartTime":153964.0,"EndTime":153964.0,"Column":2}]},{"RandomW":147408153,"RandomX":2251323109,"RandomY":2048526504,"RandomZ":433820735,"StartTime":154230.0,"Objects":[{"StartTime":154230.0,"EndTime":154230.0,"Column":0},{"StartTime":154230.0,"EndTime":154230.0,"Column":5}]},{"RandomW":223059387,"RandomX":2048526504,"RandomY":433820735,"RandomZ":147408153,"StartTime":154495.0,"Objects":[{"StartTime":154495.0,"EndTime":154495.0,"Column":3}]},{"RandomW":1644267862,"RandomX":147408153,"RandomY":223059387,"RandomZ":2814282738,"StartTime":154761.0,"Objects":[{"StartTime":154761.0,"EndTime":155026.0,"Column":4}]},{"RandomW":585628331,"RandomX":1644267862,"RandomY":547547522,"RandomZ":1901399656,"StartTime":155292.0,"Objects":[{"StartTime":155292.0,"EndTime":155292.0,"Column":0},{"StartTime":155292.0,"EndTime":155292.0,"Column":5}]},{"RandomW":1287818392,"RandomX":547547522,"RandomY":1901399656,"RandomZ":585628331,"StartTime":155557.0,"Objects":[{"StartTime":155557.0,"EndTime":155557.0,"Column":1}]},{"RandomW":3879046214,"RandomX":2065404539,"RandomY":2732913982,"RandomZ":3217781099,"StartTime":155823.0,"Objects":[{"StartTime":155823.0,"EndTime":156088.0,"Column":2},{"StartTime":155823.0,"EndTime":156088.0,"Column":4}]},{"RandomW":3318878889,"RandomX":3217781099,"RandomY":3879046214,"RandomZ":1075466897,"StartTime":156354.0,"Objects":[{"StartTime":156354.0,"EndTime":156619.0,"Column":3}]},{"RandomW":1785367685,"RandomX":1075466897,"RandomY":3318878889,"RandomZ":561406801,"StartTime":156885.0,"Objects":[{"StartTime":156885.0,"EndTime":157415.0,"Column":4}]},{"RandomW":2909067134,"RandomX":561406801,"RandomY":1785367685,"RandomZ":4168537475,"StartTime":157947.0,"Objects":[{"StartTime":157947.0,"EndTime":157947.0,"Column":5},{"StartTime":157947.0,"EndTime":157947.0,"Column":2}]},{"RandomW":1067074920,"RandomX":1785367685,"RandomY":4168537475,"RandomZ":2909067134,"StartTime":158212.0,"Objects":[{"StartTime":158212.0,"EndTime":158212.0,"Column":4}]},{"RandomW":27977914,"RandomX":4168537475,"RandomY":2909067134,"RandomZ":1067074920,"StartTime":158478.0,"Objects":[{"StartTime":158478.0,"EndTime":158478.0,"Column":5},{"StartTime":158478.0,"EndTime":158478.0,"Column":0}]},{"RandomW":1329528769,"RandomX":2909067134,"RandomY":1067074920,"RandomZ":27977914,"StartTime":158743.0,"Objects":[{"StartTime":158743.0,"EndTime":158743.0,"Column":4}]},{"RandomW":3295284863,"RandomX":1067074920,"RandomY":27977914,"RandomZ":1329528769,"StartTime":159009.0,"Objects":[{"StartTime":159009.0,"EndTime":159009.0,"Column":5}]},{"RandomW":691446431,"RandomX":27977914,"RandomY":1329528769,"RandomZ":3295284863,"StartTime":159540.0,"Objects":[{"StartTime":159540.0,"EndTime":159540.0,"Column":3},{"StartTime":159540.0,"EndTime":159540.0,"Column":4}]},{"RandomW":3354872060,"RandomX":3295284863,"RandomY":691446431,"RandomZ":2140106811,"StartTime":159805.0,"Objects":[{"StartTime":159805.0,"EndTime":159805.0,"Column":2},{"StartTime":159805.0,"EndTime":159805.0,"Column":3}]},{"RandomW":1400553355,"RandomX":691446431,"RandomY":2140106811,"RandomZ":3354872060,"StartTime":160071.0,"Objects":[{"StartTime":160071.0,"EndTime":160071.0,"Column":2}]},{"RandomW":1400553355,"RandomX":691446431,"RandomY":2140106811,"RandomZ":3354872060,"StartTime":160601.0,"Objects":[{"StartTime":160601.0,"EndTime":160601.0,"Column":3}]},{"RandomW":3485781281,"RandomX":2140106811,"RandomY":3354872060,"RandomZ":1400553355,"StartTime":160867.0,"Objects":[{"StartTime":160867.0,"EndTime":160867.0,"Column":3}]},{"RandomW":3053679463,"RandomX":1400553355,"RandomY":3485781281,"RandomZ":3419304522,"StartTime":161132.0,"Objects":[{"StartTime":161132.0,"EndTime":161397.0,"Column":2}]},{"RandomW":3645336111,"RandomX":3419304522,"RandomY":3053679463,"RandomZ":805504203,"StartTime":161663.0,"Objects":[{"StartTime":161663.0,"EndTime":162193.0,"Column":4}]},{"RandomW":1638076271,"RandomX":3053679463,"RandomY":805504203,"RandomZ":3645336111,"StartTime":162460.0,"Objects":[{"StartTime":162460.0,"EndTime":162460.0,"Column":3}]},{"RandomW":107981020,"RandomX":1638076271,"RandomY":3432435831,"RandomZ":3835408498,"StartTime":162725.0,"Objects":[{"StartTime":162725.0,"EndTime":162725.0,"Column":0},{"StartTime":162725.0,"EndTime":162725.0,"Column":5}]},{"RandomW":94467567,"RandomX":3835408498,"RandomY":107981020,"RandomZ":2144208649,"StartTime":163256.0,"Objects":[{"StartTime":163256.0,"EndTime":163256.0,"Column":4},{"StartTime":163256.0,"EndTime":163256.0,"Column":0}]},{"RandomW":1015041289,"RandomX":107981020,"RandomY":2144208649,"RandomZ":94467567,"StartTime":163522.0,"Objects":[{"StartTime":163522.0,"EndTime":163522.0,"Column":3}]},{"RandomW":2029876639,"RandomX":1204955917,"RandomY":1210817201,"RandomZ":1177260118,"StartTime":163787.0,"Objects":[{"StartTime":163787.0,"EndTime":164052.0,"Column":5}]},{"RandomW":3125496505,"RandomX":1177260118,"RandomY":2029876639,"RandomZ":2929832910,"StartTime":164318.0,"Objects":[{"StartTime":164318.0,"EndTime":164583.0,"Column":2}]},{"RandomW":2426857185,"RandomX":3125496505,"RandomY":2700661894,"RandomZ":859446411,"StartTime":164849.0,"Objects":[{"StartTime":164849.0,"EndTime":165114.0,"Column":0}]},{"RandomW":4116661924,"RandomX":2426857185,"RandomY":1884842190,"RandomZ":375578279,"StartTime":165380.0,"Objects":[{"StartTime":165380.0,"EndTime":165910.0,"Column":1},{"StartTime":165380.0,"EndTime":165910.0,"Column":5}]},{"RandomW":3787729819,"RandomX":375578279,"RandomY":4116661924,"RandomZ":1382622976,"StartTime":166442.0,"Objects":[{"StartTime":166442.0,"EndTime":166972.0,"Column":4}]},{"RandomW":3780331234,"RandomX":4116661924,"RandomY":1382622976,"RandomZ":3787729819,"StartTime":167239.0,"Objects":[{"StartTime":167239.0,"EndTime":167239.0,"Column":3}]},{"RandomW":891570220,"RandomX":3780331234,"RandomY":3996538378,"RandomZ":4118560235,"StartTime":167504.0,"Objects":[{"StartTime":167504.0,"EndTime":168034.0,"Column":5},{"StartTime":167504.0,"EndTime":168034.0,"Column":2}]},{"RandomW":1312521276,"RandomX":3996538378,"RandomY":4118560235,"RandomZ":891570220,"StartTime":168301.0,"Objects":[{"StartTime":168301.0,"EndTime":168301.0,"Column":0}]},{"RandomW":316798455,"RandomX":4118560235,"RandomY":891570220,"RandomZ":1312521276,"StartTime":168566.0,"Objects":[{"StartTime":168566.0,"EndTime":168566.0,"Column":2},{"StartTime":168566.0,"EndTime":168566.0,"Column":3}]},{"RandomW":107348261,"RandomX":891570220,"RandomY":1312521276,"RandomZ":316798455,"StartTime":168832.0,"Objects":[{"StartTime":168832.0,"EndTime":168832.0,"Column":1}]},{"RandomW":286543085,"RandomX":1312521276,"RandomY":316798455,"RandomZ":107348261,"StartTime":169097.0,"Objects":[{"StartTime":169097.0,"EndTime":169097.0,"Column":1},{"StartTime":169097.0,"EndTime":169097.0,"Column":2}]},{"RandomW":2220558447,"RandomX":316798455,"RandomY":107348261,"RandomZ":286543085,"StartTime":169363.0,"Objects":[{"StartTime":169363.0,"EndTime":169363.0,"Column":2}]},{"RandomW":2567445342,"RandomX":107348261,"RandomY":286543085,"RandomZ":2220558447,"StartTime":169628.0,"Objects":[{"StartTime":169628.0,"EndTime":169628.0,"Column":1},{"StartTime":169628.0,"EndTime":169628.0,"Column":2}]},{"RandomW":2941341299,"RandomX":286543085,"RandomY":2220558447,"RandomZ":2567445342,"StartTime":170159.0,"Objects":[{"StartTime":170159.0,"EndTime":170159.0,"Column":3},{"StartTime":170159.0,"EndTime":170159.0,"Column":4}]},{"RandomW":2941341299,"RandomX":286543085,"RandomY":2220558447,"RandomZ":2567445342,"StartTime":170424.0,"Objects":[{"StartTime":170424.0,"EndTime":170424.0,"Column":2},{"StartTime":170424.0,"EndTime":170424.0,"Column":1}]},{"RandomW":1087727581,"RandomX":2567445342,"RandomY":2941341299,"RandomZ":479267920,"StartTime":170690.0,"Objects":[{"StartTime":170690.0,"EndTime":171220.0,"Column":3}]},{"RandomW":2581485170,"RandomX":2941341299,"RandomY":479267920,"RandomZ":1087727581,"StartTime":171486.0,"Objects":[{"StartTime":171486.0,"EndTime":171486.0,"Column":5}]},{"RandomW":683596203,"RandomX":1087727581,"RandomY":2581485170,"RandomZ":3168383468,"StartTime":171752.0,"Objects":[{"StartTime":171752.0,"EndTime":172282.0,"Column":1}]},{"RandomW":3284056302,"RandomX":2581485170,"RandomY":3168383468,"RandomZ":683596203,"StartTime":172548.0,"Objects":[{"StartTime":172548.0,"EndTime":172548.0,"Column":2}]},{"RandomW":2830633773,"RandomX":3168383468,"RandomY":683596203,"RandomZ":3284056302,"StartTime":172814.0,"Objects":[{"StartTime":172814.0,"EndTime":172814.0,"Column":3},{"StartTime":172814.0,"EndTime":172814.0,"Column":4}]},{"RandomW":3651115271,"RandomX":683596203,"RandomY":3284056302,"RandomZ":2830633773,"StartTime":173079.0,"Objects":[{"StartTime":173079.0,"EndTime":173079.0,"Column":3}]},{"RandomW":120746014,"RandomX":3284056302,"RandomY":2830633773,"RandomZ":3651115271,"StartTime":173345.0,"Objects":[{"StartTime":173345.0,"EndTime":173345.0,"Column":3},{"StartTime":173345.0,"EndTime":173345.0,"Column":4}]},{"RandomW":830325214,"RandomX":2830633773,"RandomY":3651115271,"RandomZ":120746014,"StartTime":173610.0,"Objects":[{"StartTime":173610.0,"EndTime":173610.0,"Column":4}]},{"RandomW":1509180863,"RandomX":3651115271,"RandomY":120746014,"RandomZ":830325214,"StartTime":173876.0,"Objects":[{"StartTime":173876.0,"EndTime":173876.0,"Column":3},{"StartTime":173876.0,"EndTime":173876.0,"Column":4}]},{"RandomW":2233493011,"RandomX":3902833961,"RandomY":923589330,"RandomZ":3425613873,"StartTime":174407.0,"Objects":[{"StartTime":174407.0,"EndTime":174672.0,"Column":2},{"StartTime":174407.0,"EndTime":174672.0,"Column":0}]},{"RandomW":2517643905,"RandomX":1207989122,"RandomY":993303558,"RandomZ":3011821377,"StartTime":174938.0,"Objects":[{"StartTime":174938.0,"EndTime":175468.0,"Column":3},{"StartTime":174938.0,"EndTime":175468.0,"Column":1}]},{"RandomW":3720863650,"RandomX":993303558,"RandomY":3011821377,"RandomZ":2517643905,"StartTime":175734.0,"Objects":[{"StartTime":175734.0,"EndTime":175734.0,"Column":2}]},{"RandomW":3563355415,"RandomX":2517643905,"RandomY":3720863650,"RandomZ":1116519600,"StartTime":176000.0,"Objects":[{"StartTime":176000.0,"EndTime":176530.0,"Column":3}]},{"RandomW":3287800096,"RandomX":3720863650,"RandomY":1116519600,"RandomZ":3563355415,"StartTime":176796.0,"Objects":[{"StartTime":176796.0,"EndTime":176796.0,"Column":3}]},{"RandomW":539898931,"RandomX":1116519600,"RandomY":3563355415,"RandomZ":3287800096,"StartTime":177062.0,"Objects":[{"StartTime":177062.0,"EndTime":177062.0,"Column":2},{"StartTime":177062.0,"EndTime":177062.0,"Column":3}]},{"RandomW":123758010,"RandomX":3563355415,"RandomY":3287800096,"RandomZ":539898931,"StartTime":177327.0,"Objects":[{"StartTime":177327.0,"EndTime":177327.0,"Column":4}]},{"RandomW":4028312708,"RandomX":3287800096,"RandomY":539898931,"RandomZ":123758010,"StartTime":177593.0,"Objects":[{"StartTime":177593.0,"EndTime":177593.0,"Column":2},{"StartTime":177593.0,"EndTime":177593.0,"Column":3}]},{"RandomW":2371409278,"RandomX":539898931,"RandomY":123758010,"RandomZ":4028312708,"StartTime":177858.0,"Objects":[{"StartTime":177858.0,"EndTime":177858.0,"Column":3}]},{"RandomW":3699828554,"RandomX":123758010,"RandomY":4028312708,"RandomZ":2371409278,"StartTime":178124.0,"Objects":[{"StartTime":178124.0,"EndTime":178124.0,"Column":2},{"StartTime":178124.0,"EndTime":178124.0,"Column":3}]},{"RandomW":4053363780,"RandomX":2371409278,"RandomY":3699828554,"RandomZ":3637445845,"StartTime":178655.0,"Objects":[{"StartTime":178655.0,"EndTime":178920.0,"Column":5}]},{"RandomW":1366734997,"RandomX":3637445845,"RandomY":4053363780,"RandomZ":3122766892,"StartTime":179186.0,"Objects":[{"StartTime":179186.0,"EndTime":179716.0,"Column":3}]},{"RandomW":2085192570,"RandomX":1366734997,"RandomY":4047501250,"RandomZ":3422445293,"StartTime":179982.0,"Objects":[{"StartTime":179982.0,"EndTime":179982.0,"Column":3},{"StartTime":179982.0,"EndTime":179982.0,"Column":5}]},{"RandomW":2526042960,"RandomX":3422445293,"RandomY":2085192570,"RandomZ":2552180342,"StartTime":180247.0,"Objects":[{"StartTime":180247.0,"EndTime":180777.0,"Column":1}]},{"RandomW":2946528857,"RandomX":2085192570,"RandomY":2552180342,"RandomZ":2526042960,"StartTime":181044.0,"Objects":[{"StartTime":181044.0,"EndTime":181044.0,"Column":2}]},{"RandomW":4275012500,"RandomX":2526042960,"RandomY":2946528857,"RandomZ":2680316548,"StartTime":181309.0,"Objects":[{"StartTime":181309.0,"EndTime":181574.0,"Column":5}]},{"RandomW":716767862,"RandomX":1177533555,"RandomY":3396673648,"RandomZ":1210370441,"StartTime":181840.0,"Objects":[{"StartTime":181840.0,"EndTime":182105.0,"Column":3},{"StartTime":181840.0,"EndTime":182105.0,"Column":2}]},{"RandomW":1918581647,"RandomX":1210370441,"RandomY":716767862,"RandomZ":290385782,"StartTime":182371.0,"Objects":[{"StartTime":182371.0,"EndTime":182636.0,"Column":5}]},{"RandomW":2554770024,"RandomX":1918581647,"RandomY":475913420,"RandomZ":4262840195,"StartTime":182902.0,"Objects":[{"StartTime":182902.0,"EndTime":183432.0,"Column":1}]},{"RandomW":862610860,"RandomX":475913420,"RandomY":4262840195,"RandomZ":2554770024,"StartTime":183699.0,"Objects":[{"StartTime":183699.0,"EndTime":185557.0,"Column":2}]},{"RandomW":3240322225,"RandomX":4262840195,"RandomY":2554770024,"RandomZ":862610860,"StartTime":202017.0,"Objects":[{"StartTime":202017.0,"EndTime":202017.0,"Column":0}]},{"RandomW":2438630089,"RandomX":2554770024,"RandomY":862610860,"RandomZ":3240322225,"StartTime":202283.0,"Objects":[{"StartTime":202283.0,"EndTime":202283.0,"Column":1}]},{"RandomW":1543895637,"RandomX":3240322225,"RandomY":2438630089,"RandomZ":1008910200,"StartTime":202548.0,"Objects":[{"StartTime":202548.0,"EndTime":203078.0,"Column":4}]},{"RandomW":2262375304,"RandomX":2438630089,"RandomY":1008910200,"RandomZ":1543895637,"StartTime":203345.0,"Objects":[{"StartTime":203345.0,"EndTime":203345.0,"Column":2}]},{"RandomW":3932191533,"RandomX":1543895637,"RandomY":2262375304,"RandomZ":3281044824,"StartTime":203610.0,"Objects":[{"StartTime":203610.0,"EndTime":203875.0,"Column":4}]},{"RandomW":2456816417,"RandomX":3932191533,"RandomY":2579817318,"RandomZ":3616517773,"StartTime":204141.0,"Objects":[{"StartTime":204141.0,"EndTime":204406.0,"Column":0}]},{"RandomW":1863357795,"RandomX":2456816417,"RandomY":2065740625,"RandomZ":3309416576,"StartTime":204672.0,"Objects":[{"StartTime":204672.0,"EndTime":205202.0,"Column":3},{"StartTime":204672.0,"EndTime":205202.0,"Column":5}]},{"RandomW":66010220,"RandomX":3309416576,"RandomY":1863357795,"RandomZ":2100015779,"StartTime":205469.0,"Objects":[{"StartTime":205469.0,"EndTime":205469.0,"Column":4},{"StartTime":205469.0,"EndTime":205469.0,"Column":0}]},{"RandomW":548562611,"RandomX":2100015779,"RandomY":66010220,"RandomZ":3420604705,"StartTime":205734.0,"Objects":[{"StartTime":205734.0,"EndTime":205999.0,"Column":1}]},{"RandomW":2052728473,"RandomX":3420604705,"RandomY":548562611,"RandomZ":2913964,"StartTime":206265.0,"Objects":[{"StartTime":206265.0,"EndTime":206530.0,"Column":5}]},{"RandomW":1944462115,"RandomX":2052728473,"RandomY":2737357746,"RandomZ":270315162,"StartTime":206796.0,"Objects":[{"StartTime":206796.0,"EndTime":206796.0,"Column":2},{"StartTime":206796.0,"EndTime":206796.0,"Column":3}]},{"RandomW":3626216744,"RandomX":2737357746,"RandomY":270315162,"RandomZ":1944462115,"StartTime":207062.0,"Objects":[{"StartTime":207062.0,"EndTime":207062.0,"Column":5}]},{"RandomW":1039388877,"RandomX":270315162,"RandomY":1944462115,"RandomZ":3626216744,"StartTime":207327.0,"Objects":[{"StartTime":207327.0,"EndTime":207327.0,"Column":4}]},{"RandomW":3362701719,"RandomX":1944462115,"RandomY":3626216744,"RandomZ":1039388877,"StartTime":207593.0,"Objects":[{"StartTime":207593.0,"EndTime":207593.0,"Column":3}]},{"RandomW":3968495235,"RandomX":3362701719,"RandomY":2329091202,"RandomZ":1331472925,"StartTime":207858.0,"Objects":[{"StartTime":207858.0,"EndTime":208388.0,"Column":5}]},{"RandomW":1381394684,"RandomX":2329091202,"RandomY":1331472925,"RandomZ":3968495235,"StartTime":208655.0,"Objects":[{"StartTime":208655.0,"EndTime":208655.0,"Column":5}]},{"RandomW":1435798214,"RandomX":1381394684,"RandomY":1081301304,"RandomZ":3939835753,"StartTime":208920.0,"Objects":[{"StartTime":208920.0,"EndTime":209450.0,"Column":4}]},{"RandomW":3026458880,"RandomX":1081301304,"RandomY":3939835753,"RandomZ":1435798214,"StartTime":209717.0,"Objects":[{"StartTime":209717.0,"EndTime":209717.0,"Column":5}]},{"RandomW":3713738018,"RandomX":3026458880,"RandomY":1845767213,"RandomZ":745035987,"StartTime":209982.0,"Objects":[{"StartTime":209982.0,"EndTime":210512.0,"Column":2},{"StartTime":209982.0,"EndTime":210512.0,"Column":4}]},{"RandomW":1231260560,"RandomX":1845767213,"RandomY":745035987,"RandomZ":3713738018,"StartTime":210778.0,"Objects":[{"StartTime":210778.0,"EndTime":210778.0,"Column":4}]},{"RandomW":105489365,"RandomX":745035987,"RandomY":3713738018,"RandomZ":1231260560,"StartTime":211044.0,"Objects":[{"StartTime":211044.0,"EndTime":211044.0,"Column":4}]},{"RandomW":1753861391,"RandomX":3713738018,"RandomY":1231260560,"RandomZ":105489365,"StartTime":211309.0,"Objects":[{"StartTime":211309.0,"EndTime":211309.0,"Column":2}]},{"RandomW":966114829,"RandomX":105489365,"RandomY":1753861391,"RandomZ":1828685577,"StartTime":211575.0,"Objects":[{"StartTime":211575.0,"EndTime":211575.0,"Column":3},{"StartTime":211575.0,"EndTime":211575.0,"Column":2}]},{"RandomW":1431749195,"RandomX":1836275468,"RandomY":1290011463,"RandomZ":1159621643,"StartTime":211840.0,"Objects":[{"StartTime":211840.0,"EndTime":212370.0,"Column":5},{"StartTime":211840.0,"EndTime":212370.0,"Column":4}]},{"RandomW":3472418283,"RandomX":1159621643,"RandomY":1431749195,"RandomZ":2724869338,"StartTime":212637.0,"Objects":[{"StartTime":212637.0,"EndTime":212902.0,"Column":3}]},{"RandomW":1755864208,"RandomX":3472418283,"RandomY":2016458251,"RandomZ":2610391004,"StartTime":213168.0,"Objects":[{"StartTime":213168.0,"EndTime":213698.0,"Column":1},{"StartTime":213168.0,"EndTime":213698.0,"Column":4}]},{"RandomW":1635138515,"RandomX":2016458251,"RandomY":2610391004,"RandomZ":1755864208,"StartTime":213964.0,"Objects":[{"StartTime":213964.0,"EndTime":213964.0,"Column":3}]},{"RandomW":3162662082,"RandomX":1755864208,"RandomY":1635138515,"RandomZ":2617989400,"StartTime":214230.0,"Objects":[{"StartTime":214230.0,"EndTime":214495.0,"Column":2}]},{"RandomW":1184692914,"RandomX":2617989400,"RandomY":3162662082,"RandomZ":2531582750,"StartTime":214761.0,"Objects":[{"StartTime":214761.0,"EndTime":215026.0,"Column":3}]},{"RandomW":798124101,"RandomX":2531582750,"RandomY":1184692914,"RandomZ":2157553888,"StartTime":215292.0,"Objects":[{"StartTime":215292.0,"EndTime":215557.0,"Column":2}]},{"RandomW":1923400471,"RandomX":798124101,"RandomY":2665448122,"RandomZ":1060614841,"StartTime":215823.0,"Objects":[{"StartTime":215823.0,"EndTime":216088.0,"Column":5}]},{"RandomW":775950648,"RandomX":1923400471,"RandomY":3469237574,"RandomZ":2892029047,"StartTime":216354.0,"Objects":[{"StartTime":216354.0,"EndTime":216354.0,"Column":1},{"StartTime":216354.0,"EndTime":216354.0,"Column":4}]},{"RandomW":1321234603,"RandomX":4127626210,"RandomY":1546611249,"RandomZ":1925740893,"StartTime":216885.0,"Objects":[{"StartTime":216885.0,"EndTime":217150.0,"Column":5},{"StartTime":216885.0,"EndTime":217150.0,"Column":3}]},{"RandomW":2881678930,"RandomX":1925740893,"RandomY":1321234603,"RandomZ":2358993682,"StartTime":217416.0,"Objects":[{"StartTime":217416.0,"EndTime":217946.0,"Column":2}]},{"RandomW":2599512294,"RandomX":1321234603,"RandomY":2358993682,"RandomZ":2881678930,"StartTime":218212.0,"Objects":[{"StartTime":218212.0,"EndTime":218212.0,"Column":1}]},{"RandomW":2150464549,"RandomX":2881678930,"RandomY":2599512294,"RandomZ":3623425595,"StartTime":218478.0,"Objects":[{"StartTime":218478.0,"EndTime":219008.0,"Column":0}]},{"RandomW":763775798,"RandomX":3623425595,"RandomY":2150464549,"RandomZ":1008837132,"StartTime":219274.0,"Objects":[{"StartTime":219274.0,"EndTime":221132.0,"Column":2}]},{"RandomW":3656799832,"RandomX":1008837132,"RandomY":763775798,"RandomZ":852609139,"StartTime":221663.0,"Objects":[{"StartTime":221663.0,"EndTime":222193.0,"Column":4}]},{"RandomW":4147545979,"RandomX":852609139,"RandomY":3656799832,"RandomZ":3908484776,"StartTime":222460.0,"Objects":[{"StartTime":222460.0,"EndTime":222460.0,"Column":2},{"StartTime":222460.0,"EndTime":222460.0,"Column":5}]},{"RandomW":540508179,"RandomX":3908484776,"RandomY":4147545979,"RandomZ":1259887550,"StartTime":222725.0,"Objects":[{"StartTime":222725.0,"EndTime":223255.0,"Column":1}]},{"RandomW":1042752714,"RandomX":1259887550,"RandomY":540508179,"RandomZ":2104064323,"StartTime":223522.0,"Objects":[{"StartTime":223522.0,"EndTime":223522.0,"Column":5},{"StartTime":223522.0,"EndTime":223522.0,"Column":2}]},{"RandomW":3077262619,"RandomX":540508179,"RandomY":2104064323,"RandomZ":1042752714,"StartTime":223787.0,"Objects":[{"StartTime":223787.0,"EndTime":223787.0,"Column":3},{"StartTime":223787.0,"EndTime":223787.0,"Column":4}]},{"RandomW":734033149,"RandomX":2104064323,"RandomY":1042752714,"RandomZ":3077262619,"StartTime":224053.0,"Objects":[{"StartTime":224053.0,"EndTime":224053.0,"Column":4}]},{"RandomW":492155815,"RandomX":1042752714,"RandomY":3077262619,"RandomZ":734033149,"StartTime":224318.0,"Objects":[{"StartTime":224318.0,"EndTime":224318.0,"Column":4},{"StartTime":224318.0,"EndTime":224318.0,"Column":5}]},{"RandomW":441697715,"RandomX":3077262619,"RandomY":734033149,"RandomZ":492155815,"StartTime":224584.0,"Objects":[{"StartTime":224584.0,"EndTime":224584.0,"Column":3}]},{"RandomW":4156379255,"RandomX":734033149,"RandomY":492155815,"RandomZ":441697715,"StartTime":224849.0,"Objects":[{"StartTime":224849.0,"EndTime":224849.0,"Column":4},{"StartTime":224849.0,"EndTime":224849.0,"Column":5}]},{"RandomW":3757225441,"RandomX":492155815,"RandomY":441697715,"RandomZ":4156379255,"StartTime":225380.0,"Objects":[{"StartTime":225380.0,"EndTime":225380.0,"Column":2},{"StartTime":225380.0,"EndTime":225380.0,"Column":3}]},{"RandomW":3757225441,"RandomX":492155815,"RandomY":441697715,"RandomZ":4156379255,"StartTime":225646.0,"Objects":[{"StartTime":225646.0,"EndTime":225646.0,"Column":3},{"StartTime":225646.0,"EndTime":225646.0,"Column":2}]},{"RandomW":2225043333,"RandomX":3950035756,"RandomY":4132636893,"RandomZ":3158636107,"StartTime":225911.0,"Objects":[{"StartTime":225911.0,"EndTime":226441.0,"Column":5},{"StartTime":225911.0,"EndTime":226441.0,"Column":0}]},{"RandomW":479006094,"RandomX":2225043333,"RandomY":3919293849,"RandomZ":2279622039,"StartTime":226708.0,"Objects":[{"StartTime":226708.0,"EndTime":226708.0,"Column":0},{"StartTime":226708.0,"EndTime":226708.0,"Column":1}]},{"RandomW":3529234379,"RandomX":479006094,"RandomY":1674670789,"RandomZ":1460857923,"StartTime":226973.0,"Objects":[{"StartTime":226973.0,"EndTime":227503.0,"Column":4},{"StartTime":226973.0,"EndTime":227503.0,"Column":3}]},{"RandomW":2798539123,"RandomX":1674670789,"RandomY":1460857923,"RandomZ":3529234379,"StartTime":227770.0,"Objects":[{"StartTime":227770.0,"EndTime":227770.0,"Column":3}]},{"RandomW":1315002421,"RandomX":1460857923,"RandomY":3529234379,"RandomZ":2798539123,"StartTime":228035.0,"Objects":[{"StartTime":228035.0,"EndTime":228035.0,"Column":2},{"StartTime":228035.0,"EndTime":228035.0,"Column":3}]},{"RandomW":2396116302,"RandomX":3529234379,"RandomY":2798539123,"RandomZ":1315002421,"StartTime":228301.0,"Objects":[{"StartTime":228301.0,"EndTime":228301.0,"Column":1}]},{"RandomW":2184752848,"RandomX":2798539123,"RandomY":1315002421,"RandomZ":2396116302,"StartTime":228566.0,"Objects":[{"StartTime":228566.0,"EndTime":228566.0,"Column":2},{"StartTime":228566.0,"EndTime":228566.0,"Column":3}]},{"RandomW":1453929005,"RandomX":1315002421,"RandomY":2396116302,"RandomZ":2184752848,"StartTime":228832.0,"Objects":[{"StartTime":228832.0,"EndTime":228832.0,"Column":1}]},{"RandomW":307062845,"RandomX":2396116302,"RandomY":2184752848,"RandomZ":1453929005,"StartTime":229097.0,"Objects":[{"StartTime":229097.0,"EndTime":229097.0,"Column":2},{"StartTime":229097.0,"EndTime":229097.0,"Column":3}]},{"RandomW":2488853431,"RandomX":1430246951,"RandomY":1243135735,"RandomZ":862796553,"StartTime":229628.0,"Objects":[{"StartTime":229628.0,"EndTime":229893.0,"Column":0}]},{"RandomW":2954723307,"RandomX":862796553,"RandomY":2488853431,"RandomZ":1065193973,"StartTime":230159.0,"Objects":[{"StartTime":230159.0,"EndTime":230689.0,"Column":2}]},{"RandomW":3118771232,"RandomX":1065193973,"RandomY":2954723307,"RandomZ":3941773202,"StartTime":230955.0,"Objects":[{"StartTime":230955.0,"EndTime":230955.0,"Column":3},{"StartTime":230955.0,"EndTime":230955.0,"Column":2}]},{"RandomW":1630107201,"RandomX":3532926875,"RandomY":2476115689,"RandomZ":1207743047,"StartTime":231221.0,"Objects":[{"StartTime":231221.0,"EndTime":231751.0,"Column":0},{"StartTime":231221.0,"EndTime":231751.0,"Column":4}]},{"RandomW":313681160,"RandomX":2476115689,"RandomY":1207743047,"RandomZ":1630107201,"StartTime":232017.0,"Objects":[{"StartTime":232017.0,"EndTime":232017.0,"Column":2}]},{"RandomW":892602489,"RandomX":1207743047,"RandomY":1630107201,"RandomZ":313681160,"StartTime":232283.0,"Objects":[{"StartTime":232283.0,"EndTime":232283.0,"Column":3},{"StartTime":232283.0,"EndTime":232283.0,"Column":4}]},{"RandomW":2549672466,"RandomX":1630107201,"RandomY":313681160,"RandomZ":892602489,"StartTime":232548.0,"Objects":[{"StartTime":232548.0,"EndTime":232548.0,"Column":1}]},{"RandomW":3175685586,"RandomX":313681160,"RandomY":892602489,"RandomZ":2549672466,"StartTime":232814.0,"Objects":[{"StartTime":232814.0,"EndTime":232814.0,"Column":3},{"StartTime":232814.0,"EndTime":232814.0,"Column":4}]},{"RandomW":1012053334,"RandomX":892602489,"RandomY":2549672466,"RandomZ":3175685586,"StartTime":233079.0,"Objects":[{"StartTime":233079.0,"EndTime":233079.0,"Column":2}]},{"RandomW":2846885221,"RandomX":2549672466,"RandomY":3175685586,"RandomZ":1012053334,"StartTime":233345.0,"Objects":[{"StartTime":233345.0,"EndTime":233345.0,"Column":3},{"StartTime":233345.0,"EndTime":233345.0,"Column":4}]},{"RandomW":2773158813,"RandomX":2846885221,"RandomY":4182295099,"RandomZ":203093837,"StartTime":233876.0,"Objects":[{"StartTime":233876.0,"EndTime":234141.0,"Column":0},{"StartTime":233876.0,"EndTime":234141.0,"Column":1}]},{"RandomW":857734082,"RandomX":203093837,"RandomY":2773158813,"RandomZ":2365172092,"StartTime":234407.0,"Objects":[{"StartTime":234407.0,"EndTime":234937.0,"Column":2}]},{"RandomW":3898917491,"RandomX":2773158813,"RandomY":2365172092,"RandomZ":857734082,"StartTime":235203.0,"Objects":[{"StartTime":235203.0,"EndTime":235203.0,"Column":2}]},{"RandomW":1417532037,"RandomX":857734082,"RandomY":3898917491,"RandomZ":361638657,"StartTime":235469.0,"Objects":[{"StartTime":235469.0,"EndTime":235999.0,"Column":3}]},{"RandomW":2557538851,"RandomX":3898917491,"RandomY":361638657,"RandomZ":1417532037,"StartTime":236265.0,"Objects":[{"StartTime":236265.0,"EndTime":236265.0,"Column":3}]},{"RandomW":846935039,"RandomX":1417532037,"RandomY":2557538851,"RandomZ":1456065540,"StartTime":236531.0,"Objects":[{"StartTime":236531.0,"EndTime":236796.0,"Column":2}]},{"RandomW":2547399683,"RandomX":1456065540,"RandomY":846935039,"RandomZ":2284332751,"StartTime":237062.0,"Objects":[{"StartTime":237062.0,"EndTime":237327.0,"Column":1}]},{"RandomW":2405919505,"RandomX":846935039,"RandomY":2284332751,"RandomZ":2547399683,"StartTime":237593.0,"Objects":[{"StartTime":237593.0,"EndTime":237593.0,"Column":3},{"StartTime":237593.0,"EndTime":237593.0,"Column":4}]},{"RandomW":1684559305,"RandomX":2284332751,"RandomY":2547399683,"RandomZ":2405919505,"StartTime":237858.0,"Objects":[{"StartTime":237858.0,"EndTime":237858.0,"Column":5},{"StartTime":237858.0,"EndTime":237858.0,"Column":0}]},{"RandomW":2914982357,"RandomX":2547399683,"RandomY":2405919505,"RandomZ":1684559305,"StartTime":238124.0,"Objects":[{"StartTime":238124.0,"EndTime":238124.0,"Column":2},{"StartTime":238124.0,"EndTime":238124.0,"Column":3}]},{"RandomW":2343509573,"RandomX":2405919505,"RandomY":1684559305,"RandomZ":2914982357,"StartTime":238389.0,"Objects":[{"StartTime":238389.0,"EndTime":238389.0,"Column":5}]},{"RandomW":1059378114,"RandomX":1684559305,"RandomY":2914982357,"RandomZ":2343509573,"StartTime":238655.0,"Objects":[{"StartTime":238655.0,"EndTime":240778.0,"Column":2}]}]} \ No newline at end of file +{"Mappings":[{"RandomW":273084013,"RandomX":842502087,"RandomY":3579807591,"RandomZ":273326509,"StartTime":15562.0,"Objects":[{"StartTime":15562.0,"EndTime":17155.0,"Column":0}]},{"RandomW":2659258901,"RandomX":3579807591,"RandomY":273326509,"RandomZ":273084013,"StartTime":17686.0,"Objects":[{"StartTime":17686.0,"EndTime":17686.0,"Column":0},{"StartTime":17686.0,"EndTime":17686.0,"Column":1}]},{"RandomW":3083655709,"RandomX":273326509,"RandomY":273084013,"RandomZ":2659258901,"StartTime":17951.0,"Objects":[{"StartTime":17951.0,"EndTime":17951.0,"Column":1}]},{"RandomW":3588026162,"RandomX":2659258901,"RandomY":3083655709,"RandomZ":4073603712,"StartTime":18217.0,"Objects":[{"StartTime":18217.0,"EndTime":18217.0,"Column":2},{"StartTime":18217.0,"EndTime":18217.0,"Column":4}]},{"RandomW":1130061350,"RandomX":3083655709,"RandomY":4073603712,"RandomZ":3588026162,"StartTime":18482.0,"Objects":[{"StartTime":18482.0,"EndTime":18482.0,"Column":2}]},{"RandomW":315421426,"RandomX":3588026162,"RandomY":1130061350,"RandomZ":2459334754,"StartTime":18748.0,"Objects":[{"StartTime":18748.0,"EndTime":19013.0,"Column":0}]},{"RandomW":3110660773,"RandomX":2459334754,"RandomY":315421426,"RandomZ":542845670,"StartTime":19279.0,"Objects":[{"StartTime":19279.0,"EndTime":19809.0,"Column":3},{"StartTime":19544.0,"EndTime":19544.0,"Column":1},{"StartTime":19809.0,"EndTime":19809.0,"Column":1}]},{"RandomW":3110660773,"RandomX":2459334754,"RandomY":315421426,"RandomZ":542845670,"StartTime":20075.0,"Objects":[{"StartTime":20075.0,"EndTime":20075.0,"Column":4},{"StartTime":20075.0,"EndTime":20075.0,"Column":2}]},{"RandomW":2552021122,"RandomX":315421426,"RandomY":542845670,"RandomZ":3110660773,"StartTime":20341.0,"Objects":[{"StartTime":20341.0,"EndTime":20341.0,"Column":3}]},{"RandomW":3979536913,"RandomX":542845670,"RandomY":3110660773,"RandomZ":2552021122,"StartTime":20606.0,"Objects":[{"StartTime":20606.0,"EndTime":20606.0,"Column":2},{"StartTime":20606.0,"EndTime":20606.0,"Column":3}]},{"RandomW":3926138036,"RandomX":2552021122,"RandomY":3979536913,"RandomZ":348643659,"StartTime":20871.0,"Objects":[{"StartTime":20871.0,"EndTime":21401.0,"Column":4}]},{"RandomW":4001028953,"RandomX":348643659,"RandomY":3926138036,"RandomZ":2489502118,"StartTime":21933.0,"Objects":[{"StartTime":21933.0,"EndTime":22198.0,"Column":5}]},{"RandomW":263714783,"RandomX":2489502118,"RandomY":4001028953,"RandomZ":3315380836,"StartTime":22464.0,"Objects":[{"StartTime":22464.0,"EndTime":22729.0,"Column":0}]},{"RandomW":3045229215,"RandomX":3315380836,"RandomY":263714783,"RandomZ":2367299702,"StartTime":22995.0,"Objects":[{"StartTime":22995.0,"EndTime":23791.0,"Column":2}]},{"RandomW":622075324,"RandomX":2367299702,"RandomY":3045229215,"RandomZ":2511145433,"StartTime":24057.0,"Objects":[{"StartTime":24057.0,"EndTime":24322.0,"Column":1}]},{"RandomW":1428674661,"RandomX":3630592823,"RandomY":628640291,"RandomZ":2684635853,"StartTime":24588.0,"Objects":[{"StartTime":24588.0,"EndTime":24853.0,"Column":4},{"StartTime":24588.0,"EndTime":24853.0,"Column":3}]},{"RandomW":2963472042,"RandomX":3191072317,"RandomY":1509788298,"RandomZ":3677221210,"StartTime":25119.0,"Objects":[{"StartTime":25119.0,"EndTime":25649.0,"Column":2}]},{"RandomW":2441208973,"RandomX":1509788298,"RandomY":3677221210,"RandomZ":2963472042,"StartTime":26181.0,"Objects":[{"StartTime":26181.0,"EndTime":26181.0,"Column":2},{"StartTime":26181.0,"EndTime":26181.0,"Column":3}]},{"RandomW":614303213,"RandomX":3677221210,"RandomY":2963472042,"RandomZ":2441208973,"StartTime":26447.0,"Objects":[{"StartTime":26447.0,"EndTime":26447.0,"Column":3}]},{"RandomW":931064848,"RandomX":2441208973,"RandomY":614303213,"RandomZ":2425227013,"StartTime":26712.0,"Objects":[{"StartTime":26712.0,"EndTime":26977.0,"Column":2}]},{"RandomW":1631554006,"RandomX":2425227013,"RandomY":931064848,"RandomZ":2839921662,"StartTime":27243.0,"Objects":[{"StartTime":27243.0,"EndTime":27508.0,"Column":4}]},{"RandomW":1102544522,"RandomX":2839921662,"RandomY":1631554006,"RandomZ":2171149531,"StartTime":27774.0,"Objects":[{"StartTime":27774.0,"EndTime":28039.0,"Column":3}]},{"RandomW":1535528787,"RandomX":2171149531,"RandomY":1102544522,"RandomZ":3328843633,"StartTime":28305.0,"Objects":[{"StartTime":28305.0,"EndTime":28835.0,"Column":4},{"StartTime":28305.0,"EndTime":28305.0,"Column":3},{"StartTime":28570.0,"EndTime":28570.0,"Column":3},{"StartTime":28835.0,"EndTime":28835.0,"Column":3}]},{"RandomW":2462060348,"RandomX":1102544522,"RandomY":3328843633,"RandomZ":1535528787,"StartTime":29102.0,"Objects":[{"StartTime":29102.0,"EndTime":29102.0,"Column":3}]},{"RandomW":2548780898,"RandomX":2462060348,"RandomY":1752789184,"RandomZ":4269701929,"StartTime":29367.0,"Objects":[{"StartTime":29367.0,"EndTime":29897.0,"Column":5},{"StartTime":29367.0,"EndTime":29897.0,"Column":1}]},{"RandomW":2872444045,"RandomX":2548780898,"RandomY":96471884,"RandomZ":2795275332,"StartTime":30429.0,"Objects":[{"StartTime":30429.0,"EndTime":30694.0,"Column":2}]},{"RandomW":554186146,"RandomX":2872444045,"RandomY":1718345430,"RandomZ":1676944188,"StartTime":30960.0,"Objects":[{"StartTime":30960.0,"EndTime":31225.0,"Column":4},{"StartTime":30960.0,"EndTime":31225.0,"Column":1}]},{"RandomW":44350362,"RandomX":1676944188,"RandomY":554186146,"RandomZ":973164386,"StartTime":31491.0,"Objects":[{"StartTime":31491.0,"EndTime":32287.0,"Column":0}]},{"RandomW":2689469863,"RandomX":973164386,"RandomY":44350362,"RandomZ":3230373169,"StartTime":32553.0,"Objects":[{"StartTime":32553.0,"EndTime":32818.0,"Column":1}]},{"RandomW":3076210018,"RandomX":3230373169,"RandomY":2689469863,"RandomZ":2416196755,"StartTime":33084.0,"Objects":[{"StartTime":33084.0,"EndTime":33349.0,"Column":2}]},{"RandomW":4212524875,"RandomX":2416196755,"RandomY":3076210018,"RandomZ":736433317,"StartTime":33615.0,"Objects":[{"StartTime":33615.0,"EndTime":34145.0,"Column":5}]},{"RandomW":668643347,"RandomX":4212524875,"RandomY":1246190622,"RandomZ":614058009,"StartTime":34677.0,"Objects":[{"StartTime":34677.0,"EndTime":34677.0,"Column":0},{"StartTime":34677.0,"EndTime":34677.0,"Column":5}]},{"RandomW":4133034829,"RandomX":668643347,"RandomY":1824376828,"RandomZ":476758489,"StartTime":34942.0,"Objects":[{"StartTime":34942.0,"EndTime":34942.0,"Column":1},{"StartTime":34942.0,"EndTime":34942.0,"Column":5}]},{"RandomW":82933693,"RandomX":1824376828,"RandomY":476758489,"RandomZ":4133034829,"StartTime":35208.0,"Objects":[{"StartTime":35208.0,"EndTime":35208.0,"Column":0},{"StartTime":35208.0,"EndTime":35208.0,"Column":1}]},{"RandomW":2263995128,"RandomX":476758489,"RandomY":4133034829,"RandomZ":82933693,"StartTime":35473.0,"Objects":[{"StartTime":35473.0,"EndTime":35473.0,"Column":1}]},{"RandomW":3437211638,"RandomX":4133034829,"RandomY":82933693,"RandomZ":2263995128,"StartTime":35739.0,"Objects":[{"StartTime":35739.0,"EndTime":35739.0,"Column":2}]},{"RandomW":2107738941,"RandomX":2263995128,"RandomY":3437211638,"RandomZ":4066526803,"StartTime":36004.0,"Objects":[{"StartTime":36004.0,"EndTime":36004.0,"Column":2},{"StartTime":36004.0,"EndTime":36004.0,"Column":5}]},{"RandomW":1976561763,"RandomX":3437211638,"RandomY":4066526803,"RandomZ":2107738941,"StartTime":36270.0,"Objects":[{"StartTime":36270.0,"EndTime":36270.0,"Column":3},{"StartTime":36270.0,"EndTime":36270.0,"Column":4}]},{"RandomW":1147027763,"RandomX":4066526803,"RandomY":2107738941,"RandomZ":1976561763,"StartTime":36535.0,"Objects":[{"StartTime":36535.0,"EndTime":36535.0,"Column":3}]},{"RandomW":3580315894,"RandomX":1976561763,"RandomY":1147027763,"RandomZ":2767111989,"StartTime":36801.0,"Objects":[{"StartTime":36801.0,"EndTime":37331.0,"Column":4}]},{"RandomW":3743545041,"RandomX":1147027763,"RandomY":2767111989,"RandomZ":3580315894,"StartTime":37597.0,"Objects":[{"StartTime":37597.0,"EndTime":37597.0,"Column":1}]},{"RandomW":1409948107,"RandomX":3743545041,"RandomY":1774216159,"RandomZ":3150304957,"StartTime":37863.0,"Objects":[{"StartTime":37863.0,"EndTime":38393.0,"Column":2},{"StartTime":37863.0,"EndTime":38393.0,"Column":3}]},{"RandomW":4009340712,"RandomX":3150304957,"RandomY":1409948107,"RandomZ":2219703013,"StartTime":38925.0,"Objects":[{"StartTime":38925.0,"EndTime":39190.0,"Column":5}]},{"RandomW":3071167491,"RandomX":2065497204,"RandomY":2145154717,"RandomZ":2494378321,"StartTime":39456.0,"Objects":[{"StartTime":39456.0,"EndTime":39721.0,"Column":0},{"StartTime":39456.0,"EndTime":39721.0,"Column":2}]},{"RandomW":1245938367,"RandomX":3071167491,"RandomY":728627658,"RandomZ":3080260260,"StartTime":39987.0,"Objects":[{"StartTime":39987.0,"EndTime":40783.0,"Column":3}]},{"RandomW":3032241617,"RandomX":1245938367,"RandomY":2414391712,"RandomZ":3406801470,"StartTime":41048.0,"Objects":[{"StartTime":41048.0,"EndTime":41313.0,"Column":2}]},{"RandomW":3367991920,"RandomX":3804000131,"RandomY":672376773,"RandomZ":2667292323,"StartTime":41579.0,"Objects":[{"StartTime":41579.0,"EndTime":41844.0,"Column":1},{"StartTime":41579.0,"EndTime":41844.0,"Column":3}]},{"RandomW":2095476726,"RandomX":2667292323,"RandomY":3367991920,"RandomZ":3380532371,"StartTime":42110.0,"Objects":[{"StartTime":42110.0,"EndTime":42640.0,"Column":5}]},{"RandomW":869340745,"RandomX":2095476726,"RandomY":1063981175,"RandomZ":204767504,"StartTime":43172.0,"Objects":[{"StartTime":43172.0,"EndTime":43172.0,"Column":1},{"StartTime":43172.0,"EndTime":43172.0,"Column":4}]},{"RandomW":461904197,"RandomX":204767504,"RandomY":869340745,"RandomZ":2080855578,"StartTime":43438.0,"Objects":[{"StartTime":43438.0,"EndTime":43438.0,"Column":2},{"StartTime":43438.0,"EndTime":43438.0,"Column":1}]},{"RandomW":3004966693,"RandomX":869340745,"RandomY":2080855578,"RandomZ":461904197,"StartTime":43703.0,"Objects":[{"StartTime":43703.0,"EndTime":43703.0,"Column":3},{"StartTime":43703.0,"EndTime":43703.0,"Column":4}]},{"RandomW":147065937,"RandomX":2080855578,"RandomY":461904197,"RandomZ":3004966693,"StartTime":43969.0,"Objects":[{"StartTime":43969.0,"EndTime":43969.0,"Column":4}]},{"RandomW":1312111829,"RandomX":461904197,"RandomY":3004966693,"RandomZ":147065937,"StartTime":44234.0,"Objects":[{"StartTime":44234.0,"EndTime":44234.0,"Column":4}]},{"RandomW":355223143,"RandomX":3004966693,"RandomY":147065937,"RandomZ":1312111829,"StartTime":44500.0,"Objects":[{"StartTime":44500.0,"EndTime":44500.0,"Column":3}]},{"RandomW":1197174504,"RandomX":147065937,"RandomY":1312111829,"RandomZ":355223143,"StartTime":44765.0,"Objects":[{"StartTime":44765.0,"EndTime":44765.0,"Column":2},{"StartTime":44765.0,"EndTime":44765.0,"Column":3}]},{"RandomW":2296450669,"RandomX":355223143,"RandomY":1197174504,"RandomZ":1876247766,"StartTime":45031.0,"Objects":[{"StartTime":45031.0,"EndTime":45031.0,"Column":1},{"StartTime":45031.0,"EndTime":45031.0,"Column":0}]},{"RandomW":1664705375,"RandomX":1876247766,"RandomY":2296450669,"RandomZ":4287200872,"StartTime":45296.0,"Objects":[{"StartTime":45296.0,"EndTime":45296.0,"Column":0},{"StartTime":45296.0,"EndTime":45296.0,"Column":4}]},{"RandomW":2786027546,"RandomX":2296450669,"RandomY":4287200872,"RandomZ":1664705375,"StartTime":45562.0,"Objects":[{"StartTime":45562.0,"EndTime":45562.0,"Column":1}]},{"RandomW":639469776,"RandomX":4287200872,"RandomY":1664705375,"RandomZ":2786027546,"StartTime":45827.0,"Objects":[{"StartTime":45827.0,"EndTime":45827.0,"Column":3},{"StartTime":45827.0,"EndTime":45827.0,"Column":4}]},{"RandomW":2463352901,"RandomX":1664705375,"RandomY":2786027546,"RandomZ":639469776,"StartTime":46093.0,"Objects":[{"StartTime":46093.0,"EndTime":46093.0,"Column":4}]},{"RandomW":760995091,"RandomX":2463352901,"RandomY":978871003,"RandomZ":3888812594,"StartTime":46358.0,"Objects":[{"StartTime":46358.0,"EndTime":46888.0,"Column":2}]},{"RandomW":3631307076,"RandomX":3888812594,"RandomY":760995091,"RandomZ":566667549,"StartTime":47420.0,"Objects":[{"StartTime":47420.0,"EndTime":47685.0,"Column":4}]},{"RandomW":2353216536,"RandomX":3631307076,"RandomY":1805196154,"RandomZ":2564415583,"StartTime":47951.0,"Objects":[{"StartTime":47951.0,"EndTime":48216.0,"Column":1},{"StartTime":47951.0,"EndTime":48216.0,"Column":0}]},{"RandomW":717730087,"RandomX":2353216536,"RandomY":3735744429,"RandomZ":2102099401,"StartTime":48482.0,"Objects":[{"StartTime":48482.0,"EndTime":49278.0,"Column":5},{"StartTime":48482.0,"EndTime":49278.0,"Column":2}]},{"RandomW":271333990,"RandomX":717730087,"RandomY":3220302747,"RandomZ":917482575,"StartTime":49544.0,"Objects":[{"StartTime":49544.0,"EndTime":49809.0,"Column":0}]},{"RandomW":937976203,"RandomX":917482575,"RandomY":271333990,"RandomZ":125173709,"StartTime":50075.0,"Objects":[{"StartTime":50075.0,"EndTime":50340.0,"Column":2}]},{"RandomW":2781059562,"RandomX":937976203,"RandomY":2087616237,"RandomZ":232817676,"StartTime":50606.0,"Objects":[{"StartTime":50606.0,"EndTime":51667.0,"Column":0},{"StartTime":50606.0,"EndTime":51667.0,"Column":1}]},{"RandomW":3511898336,"RandomX":2087616237,"RandomY":232817676,"RandomZ":2781059562,"StartTime":52730.0,"Objects":[{"StartTime":52730.0,"EndTime":52730.0,"Column":4}]},{"RandomW":623291556,"RandomX":3737503025,"RandomY":3607951873,"RandomZ":1857627587,"StartTime":53792.0,"Objects":[{"StartTime":53792.0,"EndTime":54322.0,"Column":5},{"StartTime":53792.0,"EndTime":54322.0,"Column":1}]},{"RandomW":3577350524,"RandomX":3607951873,"RandomY":1857627587,"RandomZ":623291556,"StartTime":54588.0,"Objects":[{"StartTime":54588.0,"EndTime":54588.0,"Column":2}]},{"RandomW":3611414219,"RandomX":1700150568,"RandomY":3261504380,"RandomZ":3526708248,"StartTime":54854.0,"Objects":[{"StartTime":54854.0,"EndTime":55384.0,"Column":3},{"StartTime":54854.0,"EndTime":55384.0,"Column":4}]},{"RandomW":4116828180,"RandomX":3526708248,"RandomY":3611414219,"RandomZ":53089910,"StartTime":55916.0,"Objects":[{"StartTime":55916.0,"EndTime":56446.0,"Column":5}]},{"RandomW":1419945944,"RandomX":53089910,"RandomY":4116828180,"RandomZ":2370574124,"StartTime":56978.0,"Objects":[{"StartTime":56978.0,"EndTime":57549.0,"Column":3}]},{"RandomW":4235330325,"RandomX":2370574124,"RandomY":1419945944,"RandomZ":124293788,"StartTime":58120.0,"Objects":[{"StartTime":58120.0,"EndTime":58405.0,"Column":5}]},{"RandomW":1354196818,"RandomX":124293788,"RandomY":4235330325,"RandomZ":292200128,"StartTime":58692.0,"Objects":[{"StartTime":58692.0,"EndTime":58973.0,"Column":3}]},{"RandomW":2131632245,"RandomX":292200128,"RandomY":1354196818,"RandomZ":319349674,"StartTime":59325.0,"Objects":[{"StartTime":59325.0,"EndTime":60170.0,"Column":5}]},{"RandomW":987180490,"RandomX":1354196818,"RandomY":319349674,"RandomZ":2131632245,"StartTime":60513.0,"Objects":[{"StartTime":60513.0,"EndTime":60513.0,"Column":3}]},{"RandomW":2247158810,"RandomX":2131632245,"RandomY":987180490,"RandomZ":3518058549,"StartTime":60778.0,"Objects":[{"StartTime":60778.0,"EndTime":61043.0,"Column":0}]},{"RandomW":2347989337,"RandomX":987180490,"RandomY":3518058549,"RandomZ":2247158810,"StartTime":61309.0,"Objects":[{"StartTime":61309.0,"EndTime":61309.0,"Column":3}]},{"RandomW":82954311,"RandomX":1403151684,"RandomY":1362150166,"RandomZ":1092174296,"StartTime":61840.0,"Objects":[{"StartTime":61840.0,"EndTime":62105.0,"Column":0}]},{"RandomW":408605211,"RandomX":82954311,"RandomY":1144587736,"RandomZ":2479248954,"StartTime":62371.0,"Objects":[{"StartTime":62371.0,"EndTime":62901.0,"Column":1}]},{"RandomW":2455999143,"RandomX":1144587736,"RandomY":2479248954,"RandomZ":408605211,"StartTime":63168.0,"Objects":[{"StartTime":63168.0,"EndTime":63168.0,"Column":2}]},{"RandomW":1898608481,"RandomX":2455999143,"RandomY":519590646,"RandomZ":3207504021,"StartTime":63433.0,"Objects":[{"StartTime":63433.0,"EndTime":63963.0,"Column":5}]},{"RandomW":601995191,"RandomX":3207504021,"RandomY":1898608481,"RandomZ":4283573577,"StartTime":64230.0,"Objects":[{"StartTime":64230.0,"EndTime":64230.0,"Column":5},{"StartTime":64230.0,"EndTime":64230.0,"Column":1}]},{"RandomW":3909194070,"RandomX":1898608481,"RandomY":4283573577,"RandomZ":601995191,"StartTime":64495.0,"Objects":[{"StartTime":64495.0,"EndTime":64495.0,"Column":3},{"StartTime":64495.0,"EndTime":64495.0,"Column":4}]},{"RandomW":3417465448,"RandomX":4283573577,"RandomY":601995191,"RandomZ":3909194070,"StartTime":64761.0,"Objects":[{"StartTime":64761.0,"EndTime":64761.0,"Column":4}]},{"RandomW":2779016762,"RandomX":601995191,"RandomY":3909194070,"RandomZ":3417465448,"StartTime":65026.0,"Objects":[{"StartTime":65026.0,"EndTime":65026.0,"Column":4},{"StartTime":65026.0,"EndTime":65026.0,"Column":5}]},{"RandomW":2346068278,"RandomX":3909194070,"RandomY":3417465448,"RandomZ":2779016762,"StartTime":65292.0,"Objects":[{"StartTime":65292.0,"EndTime":65292.0,"Column":3}]},{"RandomW":1857589819,"RandomX":3417465448,"RandomY":2779016762,"RandomZ":2346068278,"StartTime":65557.0,"Objects":[{"StartTime":65557.0,"EndTime":65557.0,"Column":4},{"StartTime":65557.0,"EndTime":65557.0,"Column":5}]},{"RandomW":910236838,"RandomX":2779016762,"RandomY":2346068278,"RandomZ":1857589819,"StartTime":66088.0,"Objects":[{"StartTime":66088.0,"EndTime":66088.0,"Column":3},{"StartTime":66088.0,"EndTime":66088.0,"Column":4}]},{"RandomW":910236838,"RandomX":2779016762,"RandomY":2346068278,"RandomZ":1857589819,"StartTime":66354.0,"Objects":[{"StartTime":66354.0,"EndTime":66354.0,"Column":2},{"StartTime":66354.0,"EndTime":66354.0,"Column":1}]},{"RandomW":2327273799,"RandomX":1857589819,"RandomY":910236838,"RandomZ":2953998826,"StartTime":66619.0,"Objects":[{"StartTime":66619.0,"EndTime":67149.0,"Column":0}]},{"RandomW":540283744,"RandomX":910236838,"RandomY":2953998826,"RandomZ":2327273799,"StartTime":67416.0,"Objects":[{"StartTime":67416.0,"EndTime":67416.0,"Column":0}]},{"RandomW":1024467186,"RandomX":2327273799,"RandomY":540283744,"RandomZ":514760684,"StartTime":67681.0,"Objects":[{"StartTime":67681.0,"EndTime":68211.0,"Column":2}]},{"RandomW":211600206,"RandomX":540283744,"RandomY":514760684,"RandomZ":1024467186,"StartTime":68478.0,"Objects":[{"StartTime":68478.0,"EndTime":68478.0,"Column":3}]},{"RandomW":2360573614,"RandomX":514760684,"RandomY":1024467186,"RandomZ":211600206,"StartTime":68743.0,"Objects":[{"StartTime":68743.0,"EndTime":68743.0,"Column":4},{"StartTime":68743.0,"EndTime":68743.0,"Column":5}]},{"RandomW":3867722027,"RandomX":1024467186,"RandomY":211600206,"RandomZ":2360573614,"StartTime":69009.0,"Objects":[{"StartTime":69009.0,"EndTime":69009.0,"Column":3}]},{"RandomW":1512274616,"RandomX":211600206,"RandomY":2360573614,"RandomZ":3867722027,"StartTime":69274.0,"Objects":[{"StartTime":69274.0,"EndTime":69274.0,"Column":4},{"StartTime":69274.0,"EndTime":69274.0,"Column":5}]},{"RandomW":2957984769,"RandomX":2360573614,"RandomY":3867722027,"RandomZ":1512274616,"StartTime":69540.0,"Objects":[{"StartTime":69540.0,"EndTime":69540.0,"Column":3}]},{"RandomW":2803767976,"RandomX":3867722027,"RandomY":1512274616,"RandomZ":2957984769,"StartTime":69805.0,"Objects":[{"StartTime":69805.0,"EndTime":69805.0,"Column":4},{"StartTime":69805.0,"EndTime":69805.0,"Column":5}]},{"RandomW":1183341084,"RandomX":2957984769,"RandomY":2803767976,"RandomZ":121575161,"StartTime":70336.0,"Objects":[{"StartTime":70336.0,"EndTime":70601.0,"Column":3}]},{"RandomW":3685872119,"RandomX":121575161,"RandomY":1183341084,"RandomZ":2351788416,"StartTime":70867.0,"Objects":[{"StartTime":70867.0,"EndTime":71397.0,"Column":4}]},{"RandomW":617004198,"RandomX":1183341084,"RandomY":2351788416,"RandomZ":3685872119,"StartTime":71663.0,"Objects":[{"StartTime":71663.0,"EndTime":71663.0,"Column":3}]},{"RandomW":2478235967,"RandomX":617004198,"RandomY":546986648,"RandomZ":3353120378,"StartTime":71929.0,"Objects":[{"StartTime":71929.0,"EndTime":72459.0,"Column":0}]},{"RandomW":2189712483,"RandomX":546986648,"RandomY":3353120378,"RandomZ":2478235967,"StartTime":72725.0,"Objects":[{"StartTime":72725.0,"EndTime":72725.0,"Column":2}]},{"RandomW":1882757169,"RandomX":3353120378,"RandomY":2478235967,"RandomZ":2189712483,"StartTime":72991.0,"Objects":[{"StartTime":72991.0,"EndTime":72991.0,"Column":3},{"StartTime":72991.0,"EndTime":72991.0,"Column":4}]},{"RandomW":1404331794,"RandomX":2478235967,"RandomY":2189712483,"RandomZ":1882757169,"StartTime":73256.0,"Objects":[{"StartTime":73256.0,"EndTime":73256.0,"Column":1}]},{"RandomW":1999620930,"RandomX":2189712483,"RandomY":1882757169,"RandomZ":1404331794,"StartTime":73522.0,"Objects":[{"StartTime":73522.0,"EndTime":73522.0,"Column":3},{"StartTime":73522.0,"EndTime":73522.0,"Column":4}]},{"RandomW":3622364800,"RandomX":1882757169,"RandomY":1404331794,"RandomZ":1999620930,"StartTime":73787.0,"Objects":[{"StartTime":73787.0,"EndTime":73787.0,"Column":2}]},{"RandomW":1671763292,"RandomX":1404331794,"RandomY":1999620930,"RandomZ":3622364800,"StartTime":74053.0,"Objects":[{"StartTime":74053.0,"EndTime":74053.0,"Column":3},{"StartTime":74053.0,"EndTime":74053.0,"Column":4}]},{"RandomW":2594561583,"RandomX":3622364800,"RandomY":1671763292,"RandomZ":2480497357,"StartTime":74584.0,"Objects":[{"StartTime":74584.0,"EndTime":74849.0,"Column":1}]},{"RandomW":1101860073,"RandomX":2480497357,"RandomY":2594561583,"RandomZ":183105309,"StartTime":75115.0,"Objects":[{"StartTime":75115.0,"EndTime":75645.0,"Column":3}]},{"RandomW":423280923,"RandomX":2594561583,"RandomY":183105309,"RandomZ":1101860073,"StartTime":75911.0,"Objects":[{"StartTime":75911.0,"EndTime":75911.0,"Column":2}]},{"RandomW":3905841932,"RandomX":1101860073,"RandomY":423280923,"RandomZ":2916757685,"StartTime":76177.0,"Objects":[{"StartTime":76177.0,"EndTime":76707.0,"Column":4}]},{"RandomW":3241015480,"RandomX":423280923,"RandomY":2916757685,"RandomZ":3905841932,"StartTime":76973.0,"Objects":[{"StartTime":76973.0,"EndTime":76973.0,"Column":3}]},{"RandomW":1928531304,"RandomX":3905841932,"RandomY":3241015480,"RandomZ":248564639,"StartTime":77239.0,"Objects":[{"StartTime":77239.0,"EndTime":77504.0,"Column":5}]},{"RandomW":634267655,"RandomX":3925777969,"RandomY":1203262350,"RandomZ":3485263061,"StartTime":77770.0,"Objects":[{"StartTime":77770.0,"EndTime":78035.0,"Column":3},{"StartTime":77770.0,"EndTime":78035.0,"Column":1}]},{"RandomW":953955737,"RandomX":1203262350,"RandomY":3485263061,"RandomZ":634267655,"StartTime":78301.0,"Objects":[{"StartTime":78301.0,"EndTime":78301.0,"Column":3}]},{"RandomW":3179099439,"RandomX":3485263061,"RandomY":634267655,"RandomZ":953955737,"StartTime":78566.0,"Objects":[{"StartTime":78566.0,"EndTime":78566.0,"Column":2},{"StartTime":78566.0,"EndTime":78566.0,"Column":3}]},{"RandomW":2513433625,"RandomX":634267655,"RandomY":953955737,"RandomZ":3179099439,"StartTime":78832.0,"Objects":[{"StartTime":78832.0,"EndTime":78832.0,"Column":3},{"StartTime":78832.0,"EndTime":78832.0,"Column":4}]},{"RandomW":3239409847,"RandomX":953955737,"RandomY":3179099439,"RandomZ":2513433625,"StartTime":79097.0,"Objects":[{"StartTime":79097.0,"EndTime":79097.0,"Column":5},{"StartTime":79097.0,"EndTime":79097.0,"Column":0}]},{"RandomW":1279031172,"RandomX":2513433625,"RandomY":3239409847,"RandomZ":415034865,"StartTime":79363.0,"Objects":[{"StartTime":79363.0,"EndTime":79893.0,"Column":3}]},{"RandomW":2797153574,"RandomX":3239409847,"RandomY":415034865,"RandomZ":1279031172,"StartTime":80159.0,"Objects":[{"StartTime":80159.0,"EndTime":80159.0,"Column":3}]},{"RandomW":858752658,"RandomX":1279031172,"RandomY":2797153574,"RandomZ":3422759302,"StartTime":80424.0,"Objects":[{"StartTime":80424.0,"EndTime":80954.0,"Column":2}]},{"RandomW":2617268004,"RandomX":2797153574,"RandomY":3422759302,"RandomZ":858752658,"StartTime":81221.0,"Objects":[{"StartTime":81221.0,"EndTime":81221.0,"Column":4}]},{"RandomW":4089416095,"RandomX":3422759302,"RandomY":858752658,"RandomZ":2617268004,"StartTime":81486.0,"Objects":[{"StartTime":81486.0,"EndTime":81486.0,"Column":4},{"StartTime":81486.0,"EndTime":81486.0,"Column":5}]},{"RandomW":640008567,"RandomX":858752658,"RandomY":2617268004,"RandomZ":4089416095,"StartTime":81752.0,"Objects":[{"StartTime":81752.0,"EndTime":81752.0,"Column":4}]},{"RandomW":1769064503,"RandomX":2617268004,"RandomY":4089416095,"RandomZ":640008567,"StartTime":82017.0,"Objects":[{"StartTime":82017.0,"EndTime":82017.0,"Column":5},{"StartTime":82017.0,"EndTime":82017.0,"Column":0}]},{"RandomW":4171929422,"RandomX":640008567,"RandomY":1769064503,"RandomZ":4149611338,"StartTime":82283.0,"Objects":[{"StartTime":82283.0,"EndTime":82283.0,"Column":3},{"StartTime":82283.0,"EndTime":82283.0,"Column":5}]},{"RandomW":4035764053,"RandomX":1769064503,"RandomY":4149611338,"RandomZ":4171929422,"StartTime":82548.0,"Objects":[{"StartTime":82548.0,"EndTime":82548.0,"Column":5},{"StartTime":82548.0,"EndTime":82548.0,"Column":0}]},{"RandomW":391872771,"RandomX":4149611338,"RandomY":4171929422,"RandomZ":4035764053,"StartTime":83079.0,"Objects":[{"StartTime":83079.0,"EndTime":83079.0,"Column":3},{"StartTime":83079.0,"EndTime":83079.0,"Column":4}]},{"RandomW":391872771,"RandomX":4149611338,"RandomY":4171929422,"RandomZ":4035764053,"StartTime":83345.0,"Objects":[{"StartTime":83345.0,"EndTime":83345.0,"Column":2},{"StartTime":83345.0,"EndTime":83345.0,"Column":1}]},{"RandomW":4239141202,"RandomX":4035764053,"RandomY":391872771,"RandomZ":1343280377,"StartTime":83610.0,"Objects":[{"StartTime":83610.0,"EndTime":84140.0,"Column":5}]},{"RandomW":2008371177,"RandomX":4239141202,"RandomY":1783379941,"RandomZ":2715086902,"StartTime":84407.0,"Objects":[{"StartTime":84407.0,"EndTime":84407.0,"Column":1},{"StartTime":84407.0,"EndTime":84407.0,"Column":5}]},{"RandomW":980563717,"RandomX":3939376884,"RandomY":3778473815,"RandomZ":3882214919,"StartTime":84672.0,"Objects":[{"StartTime":84672.0,"EndTime":85202.0,"Column":4},{"StartTime":84672.0,"EndTime":85202.0,"Column":2}]},{"RandomW":2698098433,"RandomX":3778473815,"RandomY":3882214919,"RandomZ":980563717,"StartTime":85469.0,"Objects":[{"StartTime":85469.0,"EndTime":85469.0,"Column":1}]},{"RandomW":4140546075,"RandomX":3882214919,"RandomY":980563717,"RandomZ":2698098433,"StartTime":85734.0,"Objects":[{"StartTime":85734.0,"EndTime":85734.0,"Column":3},{"StartTime":85734.0,"EndTime":85734.0,"Column":4}]},{"RandomW":1045835035,"RandomX":980563717,"RandomY":2698098433,"RandomZ":4140546075,"StartTime":86000.0,"Objects":[{"StartTime":86000.0,"EndTime":86000.0,"Column":1}]},{"RandomW":2503475147,"RandomX":2698098433,"RandomY":4140546075,"RandomZ":1045835035,"StartTime":86265.0,"Objects":[{"StartTime":86265.0,"EndTime":86265.0,"Column":1},{"StartTime":86265.0,"EndTime":86265.0,"Column":2}]},{"RandomW":3094559699,"RandomX":4140546075,"RandomY":1045835035,"RandomZ":2503475147,"StartTime":86531.0,"Objects":[{"StartTime":86531.0,"EndTime":86531.0,"Column":3}]},{"RandomW":332613542,"RandomX":1045835035,"RandomY":2503475147,"RandomZ":3094559699,"StartTime":86796.0,"Objects":[{"StartTime":86796.0,"EndTime":86796.0,"Column":2},{"StartTime":86796.0,"EndTime":86796.0,"Column":3}]},{"RandomW":2534271858,"RandomX":332613542,"RandomY":2623704626,"RandomZ":3061969874,"StartTime":87327.0,"Objects":[{"StartTime":87327.0,"EndTime":87592.0,"Column":1}]},{"RandomW":794230988,"RandomX":2534271858,"RandomY":510287938,"RandomZ":2532404899,"StartTime":87858.0,"Objects":[{"StartTime":87858.0,"EndTime":88388.0,"Column":2}]},{"RandomW":3623430191,"RandomX":510287938,"RandomY":2532404899,"RandomZ":794230988,"StartTime":88655.0,"Objects":[{"StartTime":88655.0,"EndTime":88655.0,"Column":2}]},{"RandomW":2269498220,"RandomX":794230988,"RandomY":3623430191,"RandomZ":2598120162,"StartTime":88920.0,"Objects":[{"StartTime":88920.0,"EndTime":89450.0,"Column":0}]},{"RandomW":277080616,"RandomX":3623430191,"RandomY":2598120162,"RandomZ":2269498220,"StartTime":89717.0,"Objects":[{"StartTime":89717.0,"EndTime":89717.0,"Column":2}]},{"RandomW":237305927,"RandomX":2598120162,"RandomY":2269498220,"RandomZ":277080616,"StartTime":89982.0,"Objects":[{"StartTime":89982.0,"EndTime":89982.0,"Column":1},{"StartTime":89982.0,"EndTime":89982.0,"Column":2}]},{"RandomW":3697412902,"RandomX":277080616,"RandomY":237305927,"RandomZ":1976938587,"StartTime":90247.0,"Objects":[{"StartTime":90247.0,"EndTime":90247.0,"Column":1},{"StartTime":90247.0,"EndTime":90247.0,"Column":4}]},{"RandomW":3552536616,"RandomX":237305927,"RandomY":1976938587,"RandomZ":3697412902,"StartTime":90513.0,"Objects":[{"StartTime":90513.0,"EndTime":90513.0,"Column":2},{"StartTime":90513.0,"EndTime":90513.0,"Column":3}]},{"RandomW":758205604,"RandomX":3697412902,"RandomY":3552536616,"RandomZ":4122897696,"StartTime":90778.0,"Objects":[{"StartTime":90778.0,"EndTime":90778.0,"Column":1},{"StartTime":90778.0,"EndTime":90778.0,"Column":2}]},{"RandomW":3787868447,"RandomX":3552536616,"RandomY":4122897696,"RandomZ":758205604,"StartTime":91044.0,"Objects":[{"StartTime":91044.0,"EndTime":91044.0,"Column":2},{"StartTime":91044.0,"EndTime":91044.0,"Column":3}]},{"RandomW":1748107640,"RandomX":3787868447,"RandomY":3373302567,"RandomZ":3485540424,"StartTime":91575.0,"Objects":[{"StartTime":91575.0,"EndTime":91840.0,"Column":4}]},{"RandomW":4130051617,"RandomX":3485540424,"RandomY":1748107640,"RandomZ":3144627152,"StartTime":92106.0,"Objects":[{"StartTime":92106.0,"EndTime":92636.0,"Column":5}]},{"RandomW":808332236,"RandomX":1748107640,"RandomY":3144627152,"RandomZ":4130051617,"StartTime":92902.0,"Objects":[{"StartTime":92902.0,"EndTime":92902.0,"Column":3}]},{"RandomW":182226446,"RandomX":4130051617,"RandomY":808332236,"RandomZ":3371160944,"StartTime":93168.0,"Objects":[{"StartTime":93168.0,"EndTime":93698.0,"Column":0}]},{"RandomW":2699856874,"RandomX":808332236,"RandomY":3371160944,"RandomZ":182226446,"StartTime":93964.0,"Objects":[{"StartTime":93964.0,"EndTime":93964.0,"Column":1}]},{"RandomW":3110990203,"RandomX":2699856874,"RandomY":3789399152,"RandomZ":1462741358,"StartTime":94230.0,"Objects":[{"StartTime":94230.0,"EndTime":94495.0,"Column":4},{"StartTime":94230.0,"EndTime":94495.0,"Column":2}]},{"RandomW":2375429180,"RandomX":2098892391,"RandomY":1911053200,"RandomZ":1537665050,"StartTime":94761.0,"Objects":[{"StartTime":94761.0,"EndTime":95026.0,"Column":5},{"StartTime":94761.0,"EndTime":95026.0,"Column":0}]},{"RandomW":391186846,"RandomX":1537665050,"RandomY":2375429180,"RandomZ":609673823,"StartTime":95292.0,"Objects":[{"StartTime":95292.0,"EndTime":96353.0,"Column":1}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":96486.0,"Objects":[{"StartTime":96486.0,"EndTime":98478.0,"Column":5}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":113345.0,"Objects":[{"StartTime":113345.0,"EndTime":113345.0,"Column":4}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":113876.0,"Objects":[{"StartTime":113876.0,"EndTime":113876.0,"Column":1}]},{"RandomW":2078004566,"RandomX":2375429180,"RandomY":609673823,"RandomZ":391186846,"StartTime":114407.0,"Objects":[{"StartTime":114407.0,"EndTime":114407.0,"Column":4}]},{"RandomW":1192288733,"RandomX":609673823,"RandomY":391186846,"RandomZ":2078004566,"StartTime":114672.0,"Objects":[{"StartTime":114672.0,"EndTime":114672.0,"Column":2},{"StartTime":114672.0,"EndTime":114672.0,"Column":3}]},{"RandomW":3569858426,"RandomX":391186846,"RandomY":2078004566,"RandomZ":1192288733,"StartTime":114938.0,"Objects":[{"StartTime":114938.0,"EndTime":114938.0,"Column":2}]},{"RandomW":1262832005,"RandomX":2078004566,"RandomY":1192288733,"RandomZ":3569858426,"StartTime":115203.0,"Objects":[{"StartTime":115203.0,"EndTime":115203.0,"Column":3},{"StartTime":115203.0,"EndTime":115203.0,"Column":4}]},{"RandomW":4002501854,"RandomX":1192288733,"RandomY":3569858426,"RandomZ":1262832005,"StartTime":115469.0,"Objects":[{"StartTime":115469.0,"EndTime":115469.0,"Column":3},{"StartTime":115469.0,"EndTime":115469.0,"Column":4}]},{"RandomW":776953560,"RandomX":3569858426,"RandomY":1262832005,"RandomZ":4002501854,"StartTime":116000.0,"Objects":[{"StartTime":116000.0,"EndTime":116000.0,"Column":3},{"StartTime":116000.0,"EndTime":116000.0,"Column":4}]},{"RandomW":776953560,"RandomX":3569858426,"RandomY":1262832005,"RandomZ":4002501854,"StartTime":116531.0,"Objects":[{"StartTime":116531.0,"EndTime":116531.0,"Column":2},{"StartTime":116531.0,"EndTime":116531.0,"Column":1}]},{"RandomW":3352969228,"RandomX":1262832005,"RandomY":4002501854,"RandomZ":776953560,"StartTime":117062.0,"Objects":[{"StartTime":117062.0,"EndTime":117062.0,"Column":3},{"StartTime":117062.0,"EndTime":117062.0,"Column":4}]},{"RandomW":2796695571,"RandomX":4002501854,"RandomY":776953560,"RandomZ":3352969228,"StartTime":117327.0,"Objects":[{"StartTime":117327.0,"EndTime":117327.0,"Column":2}]},{"RandomW":3269572543,"RandomX":776953560,"RandomY":3352969228,"RandomZ":2796695571,"StartTime":117593.0,"Objects":[{"StartTime":117593.0,"EndTime":117593.0,"Column":4},{"StartTime":117593.0,"EndTime":117593.0,"Column":5}]},{"RandomW":3269572543,"RandomX":776953560,"RandomY":3352969228,"RandomZ":2796695571,"StartTime":118124.0,"Objects":[{"StartTime":118124.0,"EndTime":118124.0,"Column":1},{"StartTime":118124.0,"EndTime":118124.0,"Column":0}]},{"RandomW":3269572543,"RandomX":776953560,"RandomY":3352969228,"RandomZ":2796695571,"StartTime":118655.0,"Objects":[{"StartTime":118655.0,"EndTime":118655.0,"Column":5},{"StartTime":118655.0,"EndTime":118655.0,"Column":4}]},{"RandomW":2517403813,"RandomX":3352969228,"RandomY":2796695571,"RandomZ":3269572543,"StartTime":118920.0,"Objects":[{"StartTime":118920.0,"EndTime":118920.0,"Column":2},{"StartTime":118920.0,"EndTime":118920.0,"Column":3}]},{"RandomW":2210619464,"RandomX":2796695571,"RandomY":3269572543,"RandomZ":2517403813,"StartTime":119186.0,"Objects":[{"StartTime":119186.0,"EndTime":119186.0,"Column":4}]},{"RandomW":3032935051,"RandomX":3269572543,"RandomY":2517403813,"RandomZ":2210619464,"StartTime":119451.0,"Objects":[{"StartTime":119451.0,"EndTime":119451.0,"Column":5},{"StartTime":119451.0,"EndTime":119451.0,"Column":0}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":119717.0,"Objects":[{"StartTime":119717.0,"EndTime":119717.0,"Column":4},{"StartTime":119717.0,"EndTime":119717.0,"Column":5}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":120247.0,"Objects":[{"StartTime":120247.0,"EndTime":120247.0,"Column":1},{"StartTime":120247.0,"EndTime":120247.0,"Column":0}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":120778.0,"Objects":[{"StartTime":120778.0,"EndTime":120778.0,"Column":5},{"StartTime":120778.0,"EndTime":120778.0,"Column":4}]},{"RandomW":2069229539,"RandomX":2517403813,"RandomY":2210619464,"RandomZ":3032935051,"StartTime":121309.0,"Objects":[{"StartTime":121309.0,"EndTime":121309.0,"Column":1},{"StartTime":121309.0,"EndTime":121309.0,"Column":0}]},{"RandomW":2314078604,"RandomX":2210619464,"RandomY":3032935051,"RandomZ":2069229539,"StartTime":121575.0,"Objects":[{"StartTime":121575.0,"EndTime":121575.0,"Column":3}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":121840.0,"Objects":[{"StartTime":121840.0,"EndTime":121840.0,"Column":3},{"StartTime":121840.0,"EndTime":121840.0,"Column":4}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":122371.0,"Objects":[{"StartTime":122371.0,"EndTime":122371.0,"Column":2},{"StartTime":122371.0,"EndTime":122371.0,"Column":1}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":122902.0,"Objects":[{"StartTime":122902.0,"EndTime":122902.0,"Column":4},{"StartTime":122902.0,"EndTime":122902.0,"Column":3}]},{"RandomW":297269721,"RandomX":3032935051,"RandomY":2069229539,"RandomZ":2314078604,"StartTime":123433.0,"Objects":[{"StartTime":123433.0,"EndTime":123433.0,"Column":2},{"StartTime":123433.0,"EndTime":123433.0,"Column":1}]},{"RandomW":2460408790,"RandomX":2069229539,"RandomY":2314078604,"RandomZ":297269721,"StartTime":123699.0,"Objects":[{"StartTime":123699.0,"EndTime":123699.0,"Column":1}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":123964.0,"Objects":[{"StartTime":123964.0,"EndTime":123964.0,"Column":3},{"StartTime":123964.0,"EndTime":123964.0,"Column":4}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":124495.0,"Objects":[{"StartTime":124495.0,"EndTime":124495.0,"Column":2},{"StartTime":124495.0,"EndTime":124495.0,"Column":1}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":125026.0,"Objects":[{"StartTime":125026.0,"EndTime":125026.0,"Column":4},{"StartTime":125026.0,"EndTime":125026.0,"Column":3}]},{"RandomW":1180177558,"RandomX":2314078604,"RandomY":297269721,"RandomZ":2460408790,"StartTime":125557.0,"Objects":[{"StartTime":125557.0,"EndTime":125557.0,"Column":2},{"StartTime":125557.0,"EndTime":125557.0,"Column":1}]},{"RandomW":3204700088,"RandomX":297269721,"RandomY":2460408790,"RandomZ":1180177558,"StartTime":125823.0,"Objects":[{"StartTime":125823.0,"EndTime":125823.0,"Column":2}]},{"RandomW":299141296,"RandomX":2460408790,"RandomY":1180177558,"RandomZ":3204700088,"StartTime":126088.0,"Objects":[{"StartTime":126088.0,"EndTime":126088.0,"Column":3},{"StartTime":126088.0,"EndTime":126088.0,"Column":4}]},{"RandomW":299141296,"RandomX":2460408790,"RandomY":1180177558,"RandomZ":3204700088,"StartTime":126619.0,"Objects":[{"StartTime":126619.0,"EndTime":126619.0,"Column":2},{"StartTime":126619.0,"EndTime":126619.0,"Column":1}]},{"RandomW":299141296,"RandomX":2460408790,"RandomY":1180177558,"RandomZ":3204700088,"StartTime":127150.0,"Objects":[{"StartTime":127150.0,"EndTime":127150.0,"Column":4},{"StartTime":127150.0,"EndTime":127150.0,"Column":3}]},{"RandomW":3037239607,"RandomX":1180177558,"RandomY":3204700088,"RandomZ":299141296,"StartTime":127416.0,"Objects":[{"StartTime":127416.0,"EndTime":127416.0,"Column":4},{"StartTime":127416.0,"EndTime":127416.0,"Column":5}]},{"RandomW":863164324,"RandomX":3204700088,"RandomY":299141296,"RandomZ":3037239607,"StartTime":127681.0,"Objects":[{"StartTime":127681.0,"EndTime":127681.0,"Column":5}]},{"RandomW":2456647781,"RandomX":299141296,"RandomY":3037239607,"RandomZ":863164324,"StartTime":127947.0,"Objects":[{"StartTime":127947.0,"EndTime":127947.0,"Column":4},{"StartTime":127947.0,"EndTime":127947.0,"Column":5}]},{"RandomW":659157904,"RandomX":3037239607,"RandomY":863164324,"RandomZ":2456647781,"StartTime":128212.0,"Objects":[{"StartTime":128212.0,"EndTime":128212.0,"Column":3},{"StartTime":128212.0,"EndTime":128212.0,"Column":4}]},{"RandomW":659157904,"RandomX":3037239607,"RandomY":863164324,"RandomZ":2456647781,"StartTime":128743.0,"Objects":[{"StartTime":128743.0,"EndTime":128743.0,"Column":2},{"StartTime":128743.0,"EndTime":128743.0,"Column":1}]},{"RandomW":659157904,"RandomX":3037239607,"RandomY":863164324,"RandomZ":2456647781,"StartTime":129274.0,"Objects":[{"StartTime":129274.0,"EndTime":129274.0,"Column":4},{"StartTime":129274.0,"EndTime":129274.0,"Column":3}]},{"RandomW":3598260079,"RandomX":863164324,"RandomY":2456647781,"RandomZ":659157904,"StartTime":129540.0,"Objects":[{"StartTime":129540.0,"EndTime":129540.0,"Column":3},{"StartTime":129540.0,"EndTime":129540.0,"Column":4}]},{"RandomW":1930638835,"RandomX":2456647781,"RandomY":659157904,"RandomZ":3598260079,"StartTime":129805.0,"Objects":[{"StartTime":129805.0,"EndTime":129805.0,"Column":1},{"StartTime":129805.0,"EndTime":129805.0,"Column":2}]},{"RandomW":4230333264,"RandomX":1930638835,"RandomY":2319762852,"RandomZ":3807998479,"StartTime":130071.0,"Objects":[{"StartTime":130071.0,"EndTime":130071.0,"Column":2},{"StartTime":130071.0,"EndTime":130071.0,"Column":3}]},{"RandomW":2482386774,"RandomX":4230333264,"RandomY":376688010,"RandomZ":3132506885,"StartTime":132460.0,"Objects":[{"StartTime":132460.0,"EndTime":132990.0,"Column":0}]},{"RandomW":3381449487,"RandomX":3132506885,"RandomY":2482386774,"RandomZ":1092311355,"StartTime":133522.0,"Objects":[{"StartTime":133522.0,"EndTime":134052.0,"Column":3}]},{"RandomW":3812940964,"RandomX":1092311355,"RandomY":3381449487,"RandomZ":3240759120,"StartTime":134318.0,"Objects":[{"StartTime":134318.0,"EndTime":134848.0,"Column":4}]},{"RandomW":2199106412,"RandomX":2014155638,"RandomY":3619038163,"RandomZ":1182263034,"StartTime":135115.0,"Objects":[{"StartTime":135115.0,"EndTime":135380.0,"Column":3},{"StartTime":135115.0,"EndTime":135380.0,"Column":0}]},{"RandomW":4049541057,"RandomX":1182263034,"RandomY":2199106412,"RandomZ":2542868059,"StartTime":135646.0,"Objects":[{"StartTime":135646.0,"EndTime":136176.0,"Column":5}]},{"RandomW":376448389,"RandomX":2542868059,"RandomY":4049541057,"RandomZ":149323558,"StartTime":136708.0,"Objects":[{"StartTime":136708.0,"EndTime":136973.0,"Column":1}]},{"RandomW":10761513,"RandomX":149323558,"RandomY":376448389,"RandomZ":156027614,"StartTime":137239.0,"Objects":[{"StartTime":137239.0,"EndTime":137504.0,"Column":0}]},{"RandomW":2890609580,"RandomX":156027614,"RandomY":10761513,"RandomZ":998270292,"StartTime":137770.0,"Objects":[{"StartTime":137770.0,"EndTime":138566.0,"Column":2}]},{"RandomW":3792858866,"RandomX":998270292,"RandomY":2890609580,"RandomZ":3275622081,"StartTime":138832.0,"Objects":[{"StartTime":138832.0,"EndTime":139097.0,"Column":4}]},{"RandomW":479756469,"RandomX":3792858866,"RandomY":3665829153,"RandomZ":799245198,"StartTime":139363.0,"Objects":[{"StartTime":139363.0,"EndTime":139628.0,"Column":2},{"StartTime":139363.0,"EndTime":139628.0,"Column":1}]},{"RandomW":1559664190,"RandomX":1837897770,"RandomY":3074386351,"RandomZ":2226336565,"StartTime":139894.0,"Objects":[{"StartTime":139894.0,"EndTime":140690.0,"Column":0},{"StartTime":139894.0,"EndTime":140690.0,"Column":4}]},{"RandomW":1370921154,"RandomX":3074386351,"RandomY":2226336565,"RandomZ":1559664190,"StartTime":140955.0,"Objects":[{"StartTime":140955.0,"EndTime":140955.0,"Column":4}]},{"RandomW":12534613,"RandomX":1559664190,"RandomY":1370921154,"RandomZ":495513930,"StartTime":141221.0,"Objects":[{"StartTime":141221.0,"EndTime":141751.0,"Column":3},{"StartTime":141486.0,"EndTime":141486.0,"Column":1},{"StartTime":141751.0,"EndTime":141751.0,"Column":1}]},{"RandomW":1474110729,"RandomX":12534613,"RandomY":3893387802,"RandomZ":226854738,"StartTime":142017.0,"Objects":[{"StartTime":142017.0,"EndTime":142017.0,"Column":2},{"StartTime":142017.0,"EndTime":142017.0,"Column":3}]},{"RandomW":3883366092,"RandomX":1474110729,"RandomY":2911002956,"RandomZ":3337209428,"StartTime":142283.0,"Objects":[{"StartTime":142283.0,"EndTime":142548.0,"Column":4}]},{"RandomW":1868157439,"RandomX":3883366092,"RandomY":1497166406,"RandomZ":3876220972,"StartTime":142814.0,"Objects":[{"StartTime":142814.0,"EndTime":143079.0,"Column":5}]},{"RandomW":868486094,"RandomX":1497166406,"RandomY":3876220972,"RandomZ":1868157439,"StartTime":143345.0,"Objects":[{"StartTime":143345.0,"EndTime":143345.0,"Column":2}]},{"RandomW":2379505970,"RandomX":3876220972,"RandomY":1868157439,"RandomZ":868486094,"StartTime":143610.0,"Objects":[{"StartTime":143610.0,"EndTime":143610.0,"Column":2}]},{"RandomW":971762612,"RandomX":1868157439,"RandomY":868486094,"RandomZ":2379505970,"StartTime":143876.0,"Objects":[{"StartTime":143876.0,"EndTime":143876.0,"Column":4}]},{"RandomW":2333467129,"RandomX":2379505970,"RandomY":971762612,"RandomZ":2560365407,"StartTime":144141.0,"Objects":[{"StartTime":144141.0,"EndTime":144671.0,"Column":0}]},{"RandomW":3275109659,"RandomX":2560365407,"RandomY":2333467129,"RandomZ":2783370328,"StartTime":145203.0,"Objects":[{"StartTime":145203.0,"EndTime":145468.0,"Column":3}]},{"RandomW":2675369072,"RandomX":2783370328,"RandomY":3275109659,"RandomZ":3142107337,"StartTime":145734.0,"Objects":[{"StartTime":145734.0,"EndTime":145999.0,"Column":1}]},{"RandomW":2114821552,"RandomX":3142107337,"RandomY":2675369072,"RandomZ":216133594,"StartTime":146265.0,"Objects":[{"StartTime":146265.0,"EndTime":146795.0,"Column":5}]},{"RandomW":2210288688,"RandomX":2675369072,"RandomY":216133594,"RandomZ":2114821552,"StartTime":147062.0,"Objects":[{"StartTime":147062.0,"EndTime":147062.0,"Column":3}]},{"RandomW":2824847566,"RandomX":2114821552,"RandomY":2210288688,"RandomZ":2881713491,"StartTime":147327.0,"Objects":[{"StartTime":147327.0,"EndTime":147592.0,"Column":1}]},{"RandomW":3418617049,"RandomX":2881713491,"RandomY":2824847566,"RandomZ":3131910248,"StartTime":147858.0,"Objects":[{"StartTime":147858.0,"EndTime":148123.0,"Column":3}]},{"RandomW":4264037536,"RandomX":3418617049,"RandomY":2065328415,"RandomZ":756387586,"StartTime":148389.0,"Objects":[{"StartTime":148389.0,"EndTime":149450.0,"Column":2},{"StartTime":148389.0,"EndTime":149450.0,"Column":5}]},{"RandomW":714689152,"RandomX":2065328415,"RandomY":756387586,"RandomZ":4264037536,"StartTime":149717.0,"Objects":[{"StartTime":149717.0,"EndTime":149717.0,"Column":2}]},{"RandomW":2187562077,"RandomX":756387586,"RandomY":4264037536,"RandomZ":714689152,"StartTime":149982.0,"Objects":[{"StartTime":149982.0,"EndTime":149982.0,"Column":1},{"StartTime":149982.0,"EndTime":149982.0,"Column":2}]},{"RandomW":59731596,"RandomX":4264037536,"RandomY":714689152,"RandomZ":2187562077,"StartTime":150247.0,"Objects":[{"StartTime":150247.0,"EndTime":150247.0,"Column":0}]},{"RandomW":3179032401,"RandomX":714689152,"RandomY":2187562077,"RandomZ":59731596,"StartTime":150513.0,"Objects":[{"StartTime":150513.0,"EndTime":150513.0,"Column":1}]},{"RandomW":1565638452,"RandomX":2187562077,"RandomY":59731596,"RandomZ":3179032401,"StartTime":150778.0,"Objects":[{"StartTime":150778.0,"EndTime":150778.0,"Column":2}]},{"RandomW":3285111207,"RandomX":59731596,"RandomY":3179032401,"RandomZ":1565638452,"StartTime":151044.0,"Objects":[{"StartTime":151044.0,"EndTime":151044.0,"Column":3},{"StartTime":151044.0,"EndTime":151044.0,"Column":4}]},{"RandomW":3142401116,"RandomX":3179032401,"RandomY":1565638452,"RandomZ":3285111207,"StartTime":151309.0,"Objects":[{"StartTime":151309.0,"EndTime":151309.0,"Column":4}]},{"RandomW":2191101353,"RandomX":3142401116,"RandomY":3877079747,"RandomZ":930029834,"StartTime":151575.0,"Objects":[{"StartTime":151575.0,"EndTime":152105.0,"Column":2},{"StartTime":151575.0,"EndTime":152105.0,"Column":0}]},{"RandomW":1171726387,"RandomX":2191101353,"RandomY":1357180538,"RandomZ":201209655,"StartTime":152637.0,"Objects":[{"StartTime":152637.0,"EndTime":152902.0,"Column":3}]},{"RandomW":2089660876,"RandomX":201209655,"RandomY":1171726387,"RandomZ":191699429,"StartTime":153168.0,"Objects":[{"StartTime":153168.0,"EndTime":153698.0,"Column":5}]},{"RandomW":2251323109,"RandomX":1171726387,"RandomY":191699429,"RandomZ":2089660876,"StartTime":153964.0,"Objects":[{"StartTime":153964.0,"EndTime":153964.0,"Column":3}]},{"RandomW":147408153,"RandomX":2251323109,"RandomY":2048526504,"RandomZ":433820735,"StartTime":154230.0,"Objects":[{"StartTime":154230.0,"EndTime":154230.0,"Column":0},{"StartTime":154230.0,"EndTime":154230.0,"Column":5}]},{"RandomW":223059387,"RandomX":2048526504,"RandomY":433820735,"RandomZ":147408153,"StartTime":154495.0,"Objects":[{"StartTime":154495.0,"EndTime":154495.0,"Column":3}]},{"RandomW":1644267862,"RandomX":147408153,"RandomY":223059387,"RandomZ":2814282738,"StartTime":154761.0,"Objects":[{"StartTime":154761.0,"EndTime":155026.0,"Column":4}]},{"RandomW":585628331,"RandomX":1644267862,"RandomY":547547522,"RandomZ":1901399656,"StartTime":155292.0,"Objects":[{"StartTime":155292.0,"EndTime":155292.0,"Column":0},{"StartTime":155292.0,"EndTime":155292.0,"Column":5}]},{"RandomW":1287818392,"RandomX":547547522,"RandomY":1901399656,"RandomZ":585628331,"StartTime":155557.0,"Objects":[{"StartTime":155557.0,"EndTime":155557.0,"Column":1}]},{"RandomW":3879046214,"RandomX":2065404539,"RandomY":2732913982,"RandomZ":3217781099,"StartTime":155823.0,"Objects":[{"StartTime":155823.0,"EndTime":156088.0,"Column":2},{"StartTime":155823.0,"EndTime":156088.0,"Column":4}]},{"RandomW":3318878889,"RandomX":3217781099,"RandomY":3879046214,"RandomZ":1075466897,"StartTime":156354.0,"Objects":[{"StartTime":156354.0,"EndTime":156619.0,"Column":3}]},{"RandomW":1785367685,"RandomX":1075466897,"RandomY":3318878889,"RandomZ":561406801,"StartTime":156885.0,"Objects":[{"StartTime":156885.0,"EndTime":157415.0,"Column":4}]},{"RandomW":2909067134,"RandomX":561406801,"RandomY":1785367685,"RandomZ":4168537475,"StartTime":157947.0,"Objects":[{"StartTime":157947.0,"EndTime":157947.0,"Column":5},{"StartTime":157947.0,"EndTime":157947.0,"Column":2}]},{"RandomW":1067074920,"RandomX":1785367685,"RandomY":4168537475,"RandomZ":2909067134,"StartTime":158212.0,"Objects":[{"StartTime":158212.0,"EndTime":158212.0,"Column":4}]},{"RandomW":27977914,"RandomX":4168537475,"RandomY":2909067134,"RandomZ":1067074920,"StartTime":158478.0,"Objects":[{"StartTime":158478.0,"EndTime":158478.0,"Column":5},{"StartTime":158478.0,"EndTime":158478.0,"Column":0}]},{"RandomW":1329528769,"RandomX":2909067134,"RandomY":1067074920,"RandomZ":27977914,"StartTime":158743.0,"Objects":[{"StartTime":158743.0,"EndTime":158743.0,"Column":4}]},{"RandomW":3295284863,"RandomX":1067074920,"RandomY":27977914,"RandomZ":1329528769,"StartTime":159009.0,"Objects":[{"StartTime":159009.0,"EndTime":159009.0,"Column":5}]},{"RandomW":691446431,"RandomX":27977914,"RandomY":1329528769,"RandomZ":3295284863,"StartTime":159540.0,"Objects":[{"StartTime":159540.0,"EndTime":159540.0,"Column":3},{"StartTime":159540.0,"EndTime":159540.0,"Column":4}]},{"RandomW":3354872060,"RandomX":3295284863,"RandomY":691446431,"RandomZ":2140106811,"StartTime":159805.0,"Objects":[{"StartTime":159805.0,"EndTime":159805.0,"Column":2},{"StartTime":159805.0,"EndTime":159805.0,"Column":3}]},{"RandomW":1400553355,"RandomX":691446431,"RandomY":2140106811,"RandomZ":3354872060,"StartTime":160071.0,"Objects":[{"StartTime":160071.0,"EndTime":160071.0,"Column":2}]},{"RandomW":1400553355,"RandomX":691446431,"RandomY":2140106811,"RandomZ":3354872060,"StartTime":160601.0,"Objects":[{"StartTime":160601.0,"EndTime":160601.0,"Column":3}]},{"RandomW":3485781281,"RandomX":2140106811,"RandomY":3354872060,"RandomZ":1400553355,"StartTime":160867.0,"Objects":[{"StartTime":160867.0,"EndTime":160867.0,"Column":3}]},{"RandomW":3053679463,"RandomX":1400553355,"RandomY":3485781281,"RandomZ":3419304522,"StartTime":161132.0,"Objects":[{"StartTime":161132.0,"EndTime":161397.0,"Column":2}]},{"RandomW":3645336111,"RandomX":3419304522,"RandomY":3053679463,"RandomZ":805504203,"StartTime":161663.0,"Objects":[{"StartTime":161663.0,"EndTime":162193.0,"Column":4}]},{"RandomW":1638076271,"RandomX":3053679463,"RandomY":805504203,"RandomZ":3645336111,"StartTime":162460.0,"Objects":[{"StartTime":162460.0,"EndTime":162460.0,"Column":3}]},{"RandomW":107981020,"RandomX":1638076271,"RandomY":3432435831,"RandomZ":3835408498,"StartTime":162725.0,"Objects":[{"StartTime":162725.0,"EndTime":162725.0,"Column":0},{"StartTime":162725.0,"EndTime":162725.0,"Column":5}]},{"RandomW":94467567,"RandomX":3835408498,"RandomY":107981020,"RandomZ":2144208649,"StartTime":163256.0,"Objects":[{"StartTime":163256.0,"EndTime":163256.0,"Column":4},{"StartTime":163256.0,"EndTime":163256.0,"Column":0}]},{"RandomW":1015041289,"RandomX":107981020,"RandomY":2144208649,"RandomZ":94467567,"StartTime":163522.0,"Objects":[{"StartTime":163522.0,"EndTime":163522.0,"Column":3}]},{"RandomW":2029876639,"RandomX":1204955917,"RandomY":1210817201,"RandomZ":1177260118,"StartTime":163787.0,"Objects":[{"StartTime":163787.0,"EndTime":164052.0,"Column":5}]},{"RandomW":3125496505,"RandomX":1177260118,"RandomY":2029876639,"RandomZ":2929832910,"StartTime":164318.0,"Objects":[{"StartTime":164318.0,"EndTime":164583.0,"Column":2}]},{"RandomW":2426857185,"RandomX":3125496505,"RandomY":2700661894,"RandomZ":859446411,"StartTime":164849.0,"Objects":[{"StartTime":164849.0,"EndTime":165114.0,"Column":0}]},{"RandomW":4116661924,"RandomX":2426857185,"RandomY":1884842190,"RandomZ":375578279,"StartTime":165380.0,"Objects":[{"StartTime":165380.0,"EndTime":165910.0,"Column":1},{"StartTime":165380.0,"EndTime":165910.0,"Column":5}]},{"RandomW":3787729819,"RandomX":375578279,"RandomY":4116661924,"RandomZ":1382622976,"StartTime":166442.0,"Objects":[{"StartTime":166442.0,"EndTime":166972.0,"Column":4}]},{"RandomW":3780331234,"RandomX":4116661924,"RandomY":1382622976,"RandomZ":3787729819,"StartTime":167239.0,"Objects":[{"StartTime":167239.0,"EndTime":167239.0,"Column":3}]},{"RandomW":891570220,"RandomX":3780331234,"RandomY":3996538378,"RandomZ":4118560235,"StartTime":167504.0,"Objects":[{"StartTime":167504.0,"EndTime":168034.0,"Column":5},{"StartTime":167504.0,"EndTime":168034.0,"Column":2}]},{"RandomW":1312521276,"RandomX":3996538378,"RandomY":4118560235,"RandomZ":891570220,"StartTime":168301.0,"Objects":[{"StartTime":168301.0,"EndTime":168301.0,"Column":0}]},{"RandomW":316798455,"RandomX":4118560235,"RandomY":891570220,"RandomZ":1312521276,"StartTime":168566.0,"Objects":[{"StartTime":168566.0,"EndTime":168566.0,"Column":2},{"StartTime":168566.0,"EndTime":168566.0,"Column":3}]},{"RandomW":107348261,"RandomX":891570220,"RandomY":1312521276,"RandomZ":316798455,"StartTime":168832.0,"Objects":[{"StartTime":168832.0,"EndTime":168832.0,"Column":1}]},{"RandomW":286543085,"RandomX":1312521276,"RandomY":316798455,"RandomZ":107348261,"StartTime":169097.0,"Objects":[{"StartTime":169097.0,"EndTime":169097.0,"Column":1},{"StartTime":169097.0,"EndTime":169097.0,"Column":2}]},{"RandomW":2220558447,"RandomX":316798455,"RandomY":107348261,"RandomZ":286543085,"StartTime":169363.0,"Objects":[{"StartTime":169363.0,"EndTime":169363.0,"Column":2}]},{"RandomW":2567445342,"RandomX":107348261,"RandomY":286543085,"RandomZ":2220558447,"StartTime":169628.0,"Objects":[{"StartTime":169628.0,"EndTime":169628.0,"Column":1},{"StartTime":169628.0,"EndTime":169628.0,"Column":2}]},{"RandomW":2941341299,"RandomX":286543085,"RandomY":2220558447,"RandomZ":2567445342,"StartTime":170159.0,"Objects":[{"StartTime":170159.0,"EndTime":170159.0,"Column":3},{"StartTime":170159.0,"EndTime":170159.0,"Column":4}]},{"RandomW":2941341299,"RandomX":286543085,"RandomY":2220558447,"RandomZ":2567445342,"StartTime":170424.0,"Objects":[{"StartTime":170424.0,"EndTime":170424.0,"Column":2},{"StartTime":170424.0,"EndTime":170424.0,"Column":1}]},{"RandomW":1087727581,"RandomX":2567445342,"RandomY":2941341299,"RandomZ":479267920,"StartTime":170690.0,"Objects":[{"StartTime":170690.0,"EndTime":171220.0,"Column":3}]},{"RandomW":2581485170,"RandomX":2941341299,"RandomY":479267920,"RandomZ":1087727581,"StartTime":171486.0,"Objects":[{"StartTime":171486.0,"EndTime":171486.0,"Column":5}]},{"RandomW":683596203,"RandomX":1087727581,"RandomY":2581485170,"RandomZ":3168383468,"StartTime":171752.0,"Objects":[{"StartTime":171752.0,"EndTime":172282.0,"Column":1}]},{"RandomW":3284056302,"RandomX":2581485170,"RandomY":3168383468,"RandomZ":683596203,"StartTime":172548.0,"Objects":[{"StartTime":172548.0,"EndTime":172548.0,"Column":2}]},{"RandomW":2830633773,"RandomX":3168383468,"RandomY":683596203,"RandomZ":3284056302,"StartTime":172814.0,"Objects":[{"StartTime":172814.0,"EndTime":172814.0,"Column":3},{"StartTime":172814.0,"EndTime":172814.0,"Column":4}]},{"RandomW":3651115271,"RandomX":683596203,"RandomY":3284056302,"RandomZ":2830633773,"StartTime":173079.0,"Objects":[{"StartTime":173079.0,"EndTime":173079.0,"Column":3}]},{"RandomW":120746014,"RandomX":3284056302,"RandomY":2830633773,"RandomZ":3651115271,"StartTime":173345.0,"Objects":[{"StartTime":173345.0,"EndTime":173345.0,"Column":3},{"StartTime":173345.0,"EndTime":173345.0,"Column":4}]},{"RandomW":830325214,"RandomX":2830633773,"RandomY":3651115271,"RandomZ":120746014,"StartTime":173610.0,"Objects":[{"StartTime":173610.0,"EndTime":173610.0,"Column":4}]},{"RandomW":1509180863,"RandomX":3651115271,"RandomY":120746014,"RandomZ":830325214,"StartTime":173876.0,"Objects":[{"StartTime":173876.0,"EndTime":173876.0,"Column":3},{"StartTime":173876.0,"EndTime":173876.0,"Column":4}]},{"RandomW":2233493011,"RandomX":3902833961,"RandomY":923589330,"RandomZ":3425613873,"StartTime":174407.0,"Objects":[{"StartTime":174407.0,"EndTime":174672.0,"Column":2},{"StartTime":174407.0,"EndTime":174672.0,"Column":0}]},{"RandomW":2517643905,"RandomX":1207989122,"RandomY":993303558,"RandomZ":3011821377,"StartTime":174938.0,"Objects":[{"StartTime":174938.0,"EndTime":175468.0,"Column":3},{"StartTime":174938.0,"EndTime":175468.0,"Column":1}]},{"RandomW":3720863650,"RandomX":993303558,"RandomY":3011821377,"RandomZ":2517643905,"StartTime":175734.0,"Objects":[{"StartTime":175734.0,"EndTime":175734.0,"Column":2}]},{"RandomW":3563355415,"RandomX":2517643905,"RandomY":3720863650,"RandomZ":1116519600,"StartTime":176000.0,"Objects":[{"StartTime":176000.0,"EndTime":176530.0,"Column":3}]},{"RandomW":3287800096,"RandomX":3720863650,"RandomY":1116519600,"RandomZ":3563355415,"StartTime":176796.0,"Objects":[{"StartTime":176796.0,"EndTime":176796.0,"Column":3}]},{"RandomW":539898931,"RandomX":1116519600,"RandomY":3563355415,"RandomZ":3287800096,"StartTime":177062.0,"Objects":[{"StartTime":177062.0,"EndTime":177062.0,"Column":2},{"StartTime":177062.0,"EndTime":177062.0,"Column":3}]},{"RandomW":123758010,"RandomX":3563355415,"RandomY":3287800096,"RandomZ":539898931,"StartTime":177327.0,"Objects":[{"StartTime":177327.0,"EndTime":177327.0,"Column":4}]},{"RandomW":4028312708,"RandomX":3287800096,"RandomY":539898931,"RandomZ":123758010,"StartTime":177593.0,"Objects":[{"StartTime":177593.0,"EndTime":177593.0,"Column":2},{"StartTime":177593.0,"EndTime":177593.0,"Column":3}]},{"RandomW":2371409278,"RandomX":539898931,"RandomY":123758010,"RandomZ":4028312708,"StartTime":177858.0,"Objects":[{"StartTime":177858.0,"EndTime":177858.0,"Column":3}]},{"RandomW":3699828554,"RandomX":123758010,"RandomY":4028312708,"RandomZ":2371409278,"StartTime":178124.0,"Objects":[{"StartTime":178124.0,"EndTime":178124.0,"Column":2},{"StartTime":178124.0,"EndTime":178124.0,"Column":3}]},{"RandomW":4053363780,"RandomX":2371409278,"RandomY":3699828554,"RandomZ":3637445845,"StartTime":178655.0,"Objects":[{"StartTime":178655.0,"EndTime":178920.0,"Column":5}]},{"RandomW":1366734997,"RandomX":3637445845,"RandomY":4053363780,"RandomZ":3122766892,"StartTime":179186.0,"Objects":[{"StartTime":179186.0,"EndTime":179716.0,"Column":3}]},{"RandomW":2085192570,"RandomX":1366734997,"RandomY":4047501250,"RandomZ":3422445293,"StartTime":179982.0,"Objects":[{"StartTime":179982.0,"EndTime":179982.0,"Column":3},{"StartTime":179982.0,"EndTime":179982.0,"Column":5}]},{"RandomW":2526042960,"RandomX":3422445293,"RandomY":2085192570,"RandomZ":2552180342,"StartTime":180247.0,"Objects":[{"StartTime":180247.0,"EndTime":180777.0,"Column":1}]},{"RandomW":2946528857,"RandomX":2085192570,"RandomY":2552180342,"RandomZ":2526042960,"StartTime":181044.0,"Objects":[{"StartTime":181044.0,"EndTime":181044.0,"Column":2}]},{"RandomW":4275012500,"RandomX":2526042960,"RandomY":2946528857,"RandomZ":2680316548,"StartTime":181309.0,"Objects":[{"StartTime":181309.0,"EndTime":181574.0,"Column":5}]},{"RandomW":716767862,"RandomX":1177533555,"RandomY":3396673648,"RandomZ":1210370441,"StartTime":181840.0,"Objects":[{"StartTime":181840.0,"EndTime":182105.0,"Column":3},{"StartTime":181840.0,"EndTime":182105.0,"Column":2}]},{"RandomW":1918581647,"RandomX":1210370441,"RandomY":716767862,"RandomZ":290385782,"StartTime":182371.0,"Objects":[{"StartTime":182371.0,"EndTime":182636.0,"Column":5}]},{"RandomW":2554770024,"RandomX":1918581647,"RandomY":475913420,"RandomZ":4262840195,"StartTime":182902.0,"Objects":[{"StartTime":182902.0,"EndTime":183432.0,"Column":1}]},{"RandomW":862610860,"RandomX":475913420,"RandomY":4262840195,"RandomZ":2554770024,"StartTime":183699.0,"Objects":[{"StartTime":183699.0,"EndTime":185557.0,"Column":2}]},{"RandomW":3240322225,"RandomX":4262840195,"RandomY":2554770024,"RandomZ":862610860,"StartTime":202017.0,"Objects":[{"StartTime":202017.0,"EndTime":202017.0,"Column":0}]},{"RandomW":2438630089,"RandomX":2554770024,"RandomY":862610860,"RandomZ":3240322225,"StartTime":202283.0,"Objects":[{"StartTime":202283.0,"EndTime":202283.0,"Column":1}]},{"RandomW":1543895637,"RandomX":3240322225,"RandomY":2438630089,"RandomZ":1008910200,"StartTime":202548.0,"Objects":[{"StartTime":202548.0,"EndTime":203078.0,"Column":4}]},{"RandomW":2262375304,"RandomX":2438630089,"RandomY":1008910200,"RandomZ":1543895637,"StartTime":203345.0,"Objects":[{"StartTime":203345.0,"EndTime":203345.0,"Column":2}]},{"RandomW":3932191533,"RandomX":1543895637,"RandomY":2262375304,"RandomZ":3281044824,"StartTime":203610.0,"Objects":[{"StartTime":203610.0,"EndTime":203875.0,"Column":4}]},{"RandomW":2456816417,"RandomX":3932191533,"RandomY":2579817318,"RandomZ":3616517773,"StartTime":204141.0,"Objects":[{"StartTime":204141.0,"EndTime":204406.0,"Column":0}]},{"RandomW":1863357795,"RandomX":2456816417,"RandomY":2065740625,"RandomZ":3309416576,"StartTime":204672.0,"Objects":[{"StartTime":204672.0,"EndTime":205202.0,"Column":3},{"StartTime":204672.0,"EndTime":205202.0,"Column":5}]},{"RandomW":66010220,"RandomX":3309416576,"RandomY":1863357795,"RandomZ":2100015779,"StartTime":205469.0,"Objects":[{"StartTime":205469.0,"EndTime":205469.0,"Column":4},{"StartTime":205469.0,"EndTime":205469.0,"Column":0}]},{"RandomW":548562611,"RandomX":2100015779,"RandomY":66010220,"RandomZ":3420604705,"StartTime":205734.0,"Objects":[{"StartTime":205734.0,"EndTime":205999.0,"Column":1}]},{"RandomW":2052728473,"RandomX":3420604705,"RandomY":548562611,"RandomZ":2913964,"StartTime":206265.0,"Objects":[{"StartTime":206265.0,"EndTime":206530.0,"Column":5}]},{"RandomW":1944462115,"RandomX":2052728473,"RandomY":2737357746,"RandomZ":270315162,"StartTime":206796.0,"Objects":[{"StartTime":206796.0,"EndTime":206796.0,"Column":2},{"StartTime":206796.0,"EndTime":206796.0,"Column":3}]},{"RandomW":3626216744,"RandomX":2737357746,"RandomY":270315162,"RandomZ":1944462115,"StartTime":207062.0,"Objects":[{"StartTime":207062.0,"EndTime":207062.0,"Column":5}]},{"RandomW":1039388877,"RandomX":270315162,"RandomY":1944462115,"RandomZ":3626216744,"StartTime":207327.0,"Objects":[{"StartTime":207327.0,"EndTime":207327.0,"Column":4}]},{"RandomW":3362701719,"RandomX":1944462115,"RandomY":3626216744,"RandomZ":1039388877,"StartTime":207593.0,"Objects":[{"StartTime":207593.0,"EndTime":207593.0,"Column":3}]},{"RandomW":3968495235,"RandomX":3362701719,"RandomY":2329091202,"RandomZ":1331472925,"StartTime":207858.0,"Objects":[{"StartTime":207858.0,"EndTime":208388.0,"Column":5}]},{"RandomW":1381394684,"RandomX":2329091202,"RandomY":1331472925,"RandomZ":3968495235,"StartTime":208655.0,"Objects":[{"StartTime":208655.0,"EndTime":208655.0,"Column":5}]},{"RandomW":1435798214,"RandomX":1381394684,"RandomY":1081301304,"RandomZ":3939835753,"StartTime":208920.0,"Objects":[{"StartTime":208920.0,"EndTime":209450.0,"Column":4}]},{"RandomW":3026458880,"RandomX":1081301304,"RandomY":3939835753,"RandomZ":1435798214,"StartTime":209717.0,"Objects":[{"StartTime":209717.0,"EndTime":209717.0,"Column":5}]},{"RandomW":3713738018,"RandomX":3026458880,"RandomY":1845767213,"RandomZ":745035987,"StartTime":209982.0,"Objects":[{"StartTime":209982.0,"EndTime":210512.0,"Column":2},{"StartTime":209982.0,"EndTime":210512.0,"Column":4}]},{"RandomW":1231260560,"RandomX":1845767213,"RandomY":745035987,"RandomZ":3713738018,"StartTime":210778.0,"Objects":[{"StartTime":210778.0,"EndTime":210778.0,"Column":4}]},{"RandomW":105489365,"RandomX":745035987,"RandomY":3713738018,"RandomZ":1231260560,"StartTime":211044.0,"Objects":[{"StartTime":211044.0,"EndTime":211044.0,"Column":4}]},{"RandomW":1753861391,"RandomX":3713738018,"RandomY":1231260560,"RandomZ":105489365,"StartTime":211309.0,"Objects":[{"StartTime":211309.0,"EndTime":211309.0,"Column":2}]},{"RandomW":966114829,"RandomX":105489365,"RandomY":1753861391,"RandomZ":1828685577,"StartTime":211575.0,"Objects":[{"StartTime":211575.0,"EndTime":211575.0,"Column":3},{"StartTime":211575.0,"EndTime":211575.0,"Column":2}]},{"RandomW":1431749195,"RandomX":1836275468,"RandomY":1290011463,"RandomZ":1159621643,"StartTime":211840.0,"Objects":[{"StartTime":211840.0,"EndTime":212370.0,"Column":5},{"StartTime":211840.0,"EndTime":212370.0,"Column":4}]},{"RandomW":3472418283,"RandomX":1159621643,"RandomY":1431749195,"RandomZ":2724869338,"StartTime":212637.0,"Objects":[{"StartTime":212637.0,"EndTime":212902.0,"Column":3}]},{"RandomW":1755864208,"RandomX":3472418283,"RandomY":2016458251,"RandomZ":2610391004,"StartTime":213168.0,"Objects":[{"StartTime":213168.0,"EndTime":213698.0,"Column":1},{"StartTime":213168.0,"EndTime":213698.0,"Column":4}]},{"RandomW":1635138515,"RandomX":2016458251,"RandomY":2610391004,"RandomZ":1755864208,"StartTime":213964.0,"Objects":[{"StartTime":213964.0,"EndTime":213964.0,"Column":3}]},{"RandomW":3162662082,"RandomX":1755864208,"RandomY":1635138515,"RandomZ":2617989400,"StartTime":214230.0,"Objects":[{"StartTime":214230.0,"EndTime":214495.0,"Column":2}]},{"RandomW":1184692914,"RandomX":2617989400,"RandomY":3162662082,"RandomZ":2531582750,"StartTime":214761.0,"Objects":[{"StartTime":214761.0,"EndTime":215026.0,"Column":3}]},{"RandomW":798124101,"RandomX":2531582750,"RandomY":1184692914,"RandomZ":2157553888,"StartTime":215292.0,"Objects":[{"StartTime":215292.0,"EndTime":215557.0,"Column":2}]},{"RandomW":1923400471,"RandomX":798124101,"RandomY":2665448122,"RandomZ":1060614841,"StartTime":215823.0,"Objects":[{"StartTime":215823.0,"EndTime":216088.0,"Column":5}]},{"RandomW":775950648,"RandomX":1923400471,"RandomY":3469237574,"RandomZ":2892029047,"StartTime":216354.0,"Objects":[{"StartTime":216354.0,"EndTime":216354.0,"Column":1},{"StartTime":216354.0,"EndTime":216354.0,"Column":4}]},{"RandomW":1321234603,"RandomX":4127626210,"RandomY":1546611249,"RandomZ":1925740893,"StartTime":216885.0,"Objects":[{"StartTime":216885.0,"EndTime":217150.0,"Column":5},{"StartTime":216885.0,"EndTime":217150.0,"Column":3}]},{"RandomW":2881678930,"RandomX":1925740893,"RandomY":1321234603,"RandomZ":2358993682,"StartTime":217416.0,"Objects":[{"StartTime":217416.0,"EndTime":217946.0,"Column":2}]},{"RandomW":2599512294,"RandomX":1321234603,"RandomY":2358993682,"RandomZ":2881678930,"StartTime":218212.0,"Objects":[{"StartTime":218212.0,"EndTime":218212.0,"Column":1}]},{"RandomW":2150464549,"RandomX":2881678930,"RandomY":2599512294,"RandomZ":3623425595,"StartTime":218478.0,"Objects":[{"StartTime":218478.0,"EndTime":219008.0,"Column":0}]},{"RandomW":763775798,"RandomX":3623425595,"RandomY":2150464549,"RandomZ":1008837132,"StartTime":219274.0,"Objects":[{"StartTime":219274.0,"EndTime":221132.0,"Column":2}]},{"RandomW":3656799832,"RandomX":1008837132,"RandomY":763775798,"RandomZ":852609139,"StartTime":221663.0,"Objects":[{"StartTime":221663.0,"EndTime":222193.0,"Column":4}]},{"RandomW":4147545979,"RandomX":852609139,"RandomY":3656799832,"RandomZ":3908484776,"StartTime":222460.0,"Objects":[{"StartTime":222460.0,"EndTime":222460.0,"Column":2},{"StartTime":222460.0,"EndTime":222460.0,"Column":5}]},{"RandomW":540508179,"RandomX":3908484776,"RandomY":4147545979,"RandomZ":1259887550,"StartTime":222725.0,"Objects":[{"StartTime":222725.0,"EndTime":223255.0,"Column":1}]},{"RandomW":1042752714,"RandomX":1259887550,"RandomY":540508179,"RandomZ":2104064323,"StartTime":223522.0,"Objects":[{"StartTime":223522.0,"EndTime":223522.0,"Column":5},{"StartTime":223522.0,"EndTime":223522.0,"Column":2}]},{"RandomW":3077262619,"RandomX":540508179,"RandomY":2104064323,"RandomZ":1042752714,"StartTime":223787.0,"Objects":[{"StartTime":223787.0,"EndTime":223787.0,"Column":3},{"StartTime":223787.0,"EndTime":223787.0,"Column":4}]},{"RandomW":734033149,"RandomX":2104064323,"RandomY":1042752714,"RandomZ":3077262619,"StartTime":224053.0,"Objects":[{"StartTime":224053.0,"EndTime":224053.0,"Column":4}]},{"RandomW":492155815,"RandomX":1042752714,"RandomY":3077262619,"RandomZ":734033149,"StartTime":224318.0,"Objects":[{"StartTime":224318.0,"EndTime":224318.0,"Column":4},{"StartTime":224318.0,"EndTime":224318.0,"Column":5}]},{"RandomW":441697715,"RandomX":3077262619,"RandomY":734033149,"RandomZ":492155815,"StartTime":224584.0,"Objects":[{"StartTime":224584.0,"EndTime":224584.0,"Column":3}]},{"RandomW":4156379255,"RandomX":734033149,"RandomY":492155815,"RandomZ":441697715,"StartTime":224849.0,"Objects":[{"StartTime":224849.0,"EndTime":224849.0,"Column":4},{"StartTime":224849.0,"EndTime":224849.0,"Column":5}]},{"RandomW":3757225441,"RandomX":492155815,"RandomY":441697715,"RandomZ":4156379255,"StartTime":225380.0,"Objects":[{"StartTime":225380.0,"EndTime":225380.0,"Column":2},{"StartTime":225380.0,"EndTime":225380.0,"Column":3}]},{"RandomW":3757225441,"RandomX":492155815,"RandomY":441697715,"RandomZ":4156379255,"StartTime":225646.0,"Objects":[{"StartTime":225646.0,"EndTime":225646.0,"Column":3},{"StartTime":225646.0,"EndTime":225646.0,"Column":2}]},{"RandomW":2225043333,"RandomX":3950035756,"RandomY":4132636893,"RandomZ":3158636107,"StartTime":225911.0,"Objects":[{"StartTime":225911.0,"EndTime":226441.0,"Column":5},{"StartTime":225911.0,"EndTime":226441.0,"Column":0}]},{"RandomW":479006094,"RandomX":2225043333,"RandomY":3919293849,"RandomZ":2279622039,"StartTime":226708.0,"Objects":[{"StartTime":226708.0,"EndTime":226708.0,"Column":0},{"StartTime":226708.0,"EndTime":226708.0,"Column":1}]},{"RandomW":3529234379,"RandomX":479006094,"RandomY":1674670789,"RandomZ":1460857923,"StartTime":226973.0,"Objects":[{"StartTime":226973.0,"EndTime":227503.0,"Column":4},{"StartTime":226973.0,"EndTime":227503.0,"Column":3}]},{"RandomW":2798539123,"RandomX":1674670789,"RandomY":1460857923,"RandomZ":3529234379,"StartTime":227770.0,"Objects":[{"StartTime":227770.0,"EndTime":227770.0,"Column":3}]},{"RandomW":1315002421,"RandomX":1460857923,"RandomY":3529234379,"RandomZ":2798539123,"StartTime":228035.0,"Objects":[{"StartTime":228035.0,"EndTime":228035.0,"Column":2},{"StartTime":228035.0,"EndTime":228035.0,"Column":3}]},{"RandomW":2396116302,"RandomX":3529234379,"RandomY":2798539123,"RandomZ":1315002421,"StartTime":228301.0,"Objects":[{"StartTime":228301.0,"EndTime":228301.0,"Column":1}]},{"RandomW":2184752848,"RandomX":2798539123,"RandomY":1315002421,"RandomZ":2396116302,"StartTime":228566.0,"Objects":[{"StartTime":228566.0,"EndTime":228566.0,"Column":2},{"StartTime":228566.0,"EndTime":228566.0,"Column":3}]},{"RandomW":1453929005,"RandomX":1315002421,"RandomY":2396116302,"RandomZ":2184752848,"StartTime":228832.0,"Objects":[{"StartTime":228832.0,"EndTime":228832.0,"Column":1}]},{"RandomW":307062845,"RandomX":2396116302,"RandomY":2184752848,"RandomZ":1453929005,"StartTime":229097.0,"Objects":[{"StartTime":229097.0,"EndTime":229097.0,"Column":2},{"StartTime":229097.0,"EndTime":229097.0,"Column":3}]},{"RandomW":2488853431,"RandomX":1430246951,"RandomY":1243135735,"RandomZ":862796553,"StartTime":229628.0,"Objects":[{"StartTime":229628.0,"EndTime":229893.0,"Column":0}]},{"RandomW":2954723307,"RandomX":862796553,"RandomY":2488853431,"RandomZ":1065193973,"StartTime":230159.0,"Objects":[{"StartTime":230159.0,"EndTime":230689.0,"Column":2}]},{"RandomW":3118771232,"RandomX":1065193973,"RandomY":2954723307,"RandomZ":3941773202,"StartTime":230955.0,"Objects":[{"StartTime":230955.0,"EndTime":230955.0,"Column":3},{"StartTime":230955.0,"EndTime":230955.0,"Column":2}]},{"RandomW":1630107201,"RandomX":3532926875,"RandomY":2476115689,"RandomZ":1207743047,"StartTime":231221.0,"Objects":[{"StartTime":231221.0,"EndTime":231751.0,"Column":0},{"StartTime":231221.0,"EndTime":231751.0,"Column":4}]},{"RandomW":313681160,"RandomX":2476115689,"RandomY":1207743047,"RandomZ":1630107201,"StartTime":232017.0,"Objects":[{"StartTime":232017.0,"EndTime":232017.0,"Column":2}]},{"RandomW":892602489,"RandomX":1207743047,"RandomY":1630107201,"RandomZ":313681160,"StartTime":232283.0,"Objects":[{"StartTime":232283.0,"EndTime":232283.0,"Column":3},{"StartTime":232283.0,"EndTime":232283.0,"Column":4}]},{"RandomW":2549672466,"RandomX":1630107201,"RandomY":313681160,"RandomZ":892602489,"StartTime":232548.0,"Objects":[{"StartTime":232548.0,"EndTime":232548.0,"Column":1}]},{"RandomW":3175685586,"RandomX":313681160,"RandomY":892602489,"RandomZ":2549672466,"StartTime":232814.0,"Objects":[{"StartTime":232814.0,"EndTime":232814.0,"Column":3},{"StartTime":232814.0,"EndTime":232814.0,"Column":4}]},{"RandomW":1012053334,"RandomX":892602489,"RandomY":2549672466,"RandomZ":3175685586,"StartTime":233079.0,"Objects":[{"StartTime":233079.0,"EndTime":233079.0,"Column":2}]},{"RandomW":2846885221,"RandomX":2549672466,"RandomY":3175685586,"RandomZ":1012053334,"StartTime":233345.0,"Objects":[{"StartTime":233345.0,"EndTime":233345.0,"Column":3},{"StartTime":233345.0,"EndTime":233345.0,"Column":4}]},{"RandomW":2773158813,"RandomX":2846885221,"RandomY":4182295099,"RandomZ":203093837,"StartTime":233876.0,"Objects":[{"StartTime":233876.0,"EndTime":234141.0,"Column":0},{"StartTime":233876.0,"EndTime":234141.0,"Column":1}]},{"RandomW":857734082,"RandomX":203093837,"RandomY":2773158813,"RandomZ":2365172092,"StartTime":234407.0,"Objects":[{"StartTime":234407.0,"EndTime":234937.0,"Column":2}]},{"RandomW":3898917491,"RandomX":2773158813,"RandomY":2365172092,"RandomZ":857734082,"StartTime":235203.0,"Objects":[{"StartTime":235203.0,"EndTime":235203.0,"Column":2}]},{"RandomW":1417532037,"RandomX":857734082,"RandomY":3898917491,"RandomZ":361638657,"StartTime":235469.0,"Objects":[{"StartTime":235469.0,"EndTime":235999.0,"Column":3}]},{"RandomW":2557538851,"RandomX":3898917491,"RandomY":361638657,"RandomZ":1417532037,"StartTime":236265.0,"Objects":[{"StartTime":236265.0,"EndTime":236265.0,"Column":3}]},{"RandomW":846935039,"RandomX":1417532037,"RandomY":2557538851,"RandomZ":1456065540,"StartTime":236531.0,"Objects":[{"StartTime":236531.0,"EndTime":236796.0,"Column":2}]},{"RandomW":2547399683,"RandomX":1456065540,"RandomY":846935039,"RandomZ":2284332751,"StartTime":237062.0,"Objects":[{"StartTime":237062.0,"EndTime":237327.0,"Column":1}]},{"RandomW":2405919505,"RandomX":846935039,"RandomY":2284332751,"RandomZ":2547399683,"StartTime":237593.0,"Objects":[{"StartTime":237593.0,"EndTime":237593.0,"Column":3},{"StartTime":237593.0,"EndTime":237593.0,"Column":4}]},{"RandomW":1684559305,"RandomX":2284332751,"RandomY":2547399683,"RandomZ":2405919505,"StartTime":237858.0,"Objects":[{"StartTime":237858.0,"EndTime":237858.0,"Column":5},{"StartTime":237858.0,"EndTime":237858.0,"Column":0}]},{"RandomW":2914982357,"RandomX":2547399683,"RandomY":2405919505,"RandomZ":1684559305,"StartTime":238124.0,"Objects":[{"StartTime":238124.0,"EndTime":238124.0,"Column":3},{"StartTime":238124.0,"EndTime":238124.0,"Column":4}]},{"RandomW":2343509573,"RandomX":2405919505,"RandomY":1684559305,"RandomZ":2914982357,"StartTime":238389.0,"Objects":[{"StartTime":238389.0,"EndTime":238389.0,"Column":5}]},{"RandomW":1059378114,"RandomX":1684559305,"RandomY":2914982357,"RandomZ":2343509573,"StartTime":238655.0,"Objects":[{"StartTime":238655.0,"EndTime":240778.0,"Column":2}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544-expected-conversion.json index 2289a7243f..ed4b550f01 100644 --- a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544-expected-conversion.json +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/20544-expected-conversion.json @@ -1 +1 @@ -{"Mappings":[{"RandomW":273523780,"RandomX":842502087,"RandomY":3579807591,"RandomZ":273326509,"StartTime":7693.0,"Objects":[{"StartTime":7693.0,"EndTime":7693.0,"Column":0}]},{"RandomW":2659866685,"RandomX":3579807591,"RandomY":273326509,"RandomZ":273523780,"StartTime":8043.0,"Objects":[{"StartTime":8043.0,"EndTime":8043.0,"Column":1}]},{"RandomW":3083309108,"RandomX":273326509,"RandomY":273523780,"RandomZ":2659866685,"StartTime":8393.0,"Objects":[{"StartTime":8393.0,"EndTime":8393.0,"Column":2}]},{"RandomW":2413296944,"RandomX":2659866685,"RandomY":3083309108,"RandomZ":4072999080,"StartTime":8626.0,"Objects":[{"StartTime":8626.0,"EndTime":8626.0,"Column":2},{"StartTime":8626.0,"EndTime":8626.0,"Column":0}]},{"RandomW":1129322311,"RandomX":3083309108,"RandomY":4072999080,"RandomZ":2413296944,"StartTime":8860.0,"Objects":[{"StartTime":8860.0,"EndTime":8860.0,"Column":2}]},{"RandomW":3365759273,"RandomX":4072999080,"RandomY":2413296944,"RandomZ":1129322311,"StartTime":9326.0,"Objects":[{"StartTime":9326.0,"EndTime":9326.0,"Column":3}]},{"RandomW":315078874,"RandomX":2413296944,"RandomY":1129322311,"RandomZ":3365759273,"StartTime":9560.0,"Objects":[{"StartTime":9560.0,"EndTime":9560.0,"Column":3}]},{"RandomW":583662031,"RandomX":1129322311,"RandomY":3365759273,"RandomZ":315078874,"StartTime":9793.0,"Objects":[{"StartTime":9793.0,"EndTime":9793.0,"Column":3}]},{"RandomW":3789568254,"RandomX":3365759273,"RandomY":315078874,"RandomZ":583662031,"StartTime":10260.0,"Objects":[{"StartTime":10260.0,"EndTime":10260.0,"Column":2}]},{"RandomW":3256340938,"RandomX":315078874,"RandomY":583662031,"RandomZ":3789568254,"StartTime":10493.0,"Objects":[{"StartTime":10493.0,"EndTime":10493.0,"Column":2}]},{"RandomW":2152938451,"RandomX":3789568254,"RandomY":3256340938,"RandomZ":3979976762,"StartTime":10727.0,"Objects":[{"StartTime":10727.0,"EndTime":10727.0,"Column":1},{"StartTime":10727.0,"EndTime":10727.0,"Column":0}]},{"RandomW":1620362479,"RandomX":3256340938,"RandomY":3979976762,"RandomZ":2152938451,"StartTime":11427.0,"Objects":[{"StartTime":11427.0,"EndTime":11427.0,"Column":1}]},{"RandomW":477221046,"RandomX":3979976762,"RandomY":2152938451,"RandomZ":1620362479,"StartTime":11777.0,"Objects":[{"StartTime":11777.0,"EndTime":11777.0,"Column":1}]},{"RandomW":1013554034,"RandomX":2152938451,"RandomY":1620362479,"RandomZ":477221046,"StartTime":12127.0,"Objects":[{"StartTime":12127.0,"EndTime":12127.0,"Column":2}]},{"RandomW":637383311,"RandomX":1620362479,"RandomY":477221046,"RandomZ":1013554034,"StartTime":12360.0,"Objects":[{"StartTime":12360.0,"EndTime":12360.0,"Column":2}]},{"RandomW":3817388387,"RandomX":477221046,"RandomY":1013554034,"RandomZ":637383311,"StartTime":12594.0,"Objects":[{"StartTime":12594.0,"EndTime":12594.0,"Column":3}]},{"RandomW":19695232,"RandomX":637383311,"RandomY":3817388387,"RandomZ":1911435716,"StartTime":13060.0,"Objects":[{"StartTime":13060.0,"EndTime":13060.0,"Column":3},{"StartTime":13060.0,"EndTime":13060.0,"Column":0}]},{"RandomW":3381470688,"RandomX":3817388387,"RandomY":1911435716,"RandomZ":19695232,"StartTime":13294.0,"Objects":[{"StartTime":13294.0,"EndTime":13294.0,"Column":3}]},{"RandomW":1862836779,"RandomX":19695232,"RandomY":3381470688,"RandomZ":1869143571,"StartTime":13527.0,"Objects":[{"StartTime":13527.0,"EndTime":13527.0,"Column":3},{"StartTime":13527.0,"EndTime":13527.0,"Column":5}]},{"RandomW":175452620,"RandomX":3381470688,"RandomY":1869143571,"RandomZ":1862836779,"StartTime":13994.0,"Objects":[{"StartTime":13994.0,"EndTime":13994.0,"Column":4}]},{"RandomW":2859972423,"RandomX":1869143571,"RandomY":1862836779,"RandomZ":175452620,"StartTime":14227.0,"Objects":[{"StartTime":14227.0,"EndTime":14227.0,"Column":4}]},{"RandomW":2210823260,"RandomX":1862836779,"RandomY":175452620,"RandomZ":2859972423,"StartTime":14461.0,"Objects":[{"StartTime":14461.0,"EndTime":14461.0,"Column":5}]},{"RandomW":2851442677,"RandomX":175452620,"RandomY":2859972423,"RandomZ":2210823260,"StartTime":14927.0,"Objects":[{"StartTime":14927.0,"EndTime":16561.0,"Column":1}]},{"RandomW":179122262,"RandomX":2859972423,"RandomY":2210823260,"RandomZ":2851442677,"StartTime":16794.0,"Objects":[{"StartTime":16794.0,"EndTime":18078.0,"Column":0}]},{"RandomW":2917386405,"RandomX":2851442677,"RandomY":179122262,"RandomZ":494367691,"StartTime":18661.0,"Objects":[{"StartTime":18661.0,"EndTime":19127.0,"Column":2}]},{"RandomW":3407923728,"RandomX":494367691,"RandomY":2917386405,"RandomZ":2825679051,"StartTime":19595.0,"Objects":[{"StartTime":19595.0,"EndTime":20061.0,"Column":3}]},{"RandomW":358318928,"RandomX":3407923728,"RandomY":1835995540,"RandomZ":3732560508,"StartTime":20528.0,"Objects":[{"StartTime":20528.0,"EndTime":20994.0,"Column":4},{"StartTime":20528.0,"EndTime":20994.0,"Column":1}]},{"RandomW":3440439960,"RandomX":3732560508,"RandomY":358318928,"RandomZ":3638999969,"StartTime":21462.0,"Objects":[{"StartTime":21462.0,"EndTime":21928.0,"Column":3}]},{"RandomW":3249928444,"RandomX":358318928,"RandomY":3638999969,"RandomZ":3440439960,"StartTime":22395.0,"Objects":[{"StartTime":22395.0,"EndTime":22395.0,"Column":1}]},{"RandomW":3857394572,"RandomX":3440439960,"RandomY":3249928444,"RandomZ":138257049,"StartTime":22628.0,"Objects":[{"StartTime":22628.0,"EndTime":24028.0,"Column":4}]},{"RandomW":2938470811,"RandomX":3249928444,"RandomY":138257049,"RandomZ":3857394572,"StartTime":24262.0,"Objects":[{"StartTime":24262.0,"EndTime":24262.0,"Column":3}]},{"RandomW":3241803419,"RandomX":138257049,"RandomY":3857394572,"RandomZ":2938470811,"StartTime":24495.0,"Objects":[{"StartTime":24495.0,"EndTime":24495.0,"Column":4}]},{"RandomW":620078415,"RandomX":3857394572,"RandomY":2938470811,"RandomZ":3241803419,"StartTime":25195.0,"Objects":[{"StartTime":25195.0,"EndTime":25195.0,"Column":4}]},{"RandomW":2566806806,"RandomX":2938470811,"RandomY":3241803419,"RandomZ":620078415,"StartTime":25429.0,"Objects":[{"StartTime":25429.0,"EndTime":25429.0,"Column":4}]},{"RandomW":458505931,"RandomX":3241803419,"RandomY":620078415,"RandomZ":2566806806,"StartTime":26129.0,"Objects":[{"StartTime":26129.0,"EndTime":26129.0,"Column":3}]},{"RandomW":2629948988,"RandomX":2566806806,"RandomY":458505931,"RandomZ":362272284,"StartTime":26362.0,"Objects":[{"StartTime":26362.0,"EndTime":27762.0,"Column":1}]},{"RandomW":1285940261,"RandomX":362272284,"RandomY":2629948988,"RandomZ":4139597407,"StartTime":27996.0,"Objects":[{"StartTime":27996.0,"EndTime":27996.0,"Column":1},{"StartTime":27996.0,"EndTime":27996.0,"Column":3}]},{"RandomW":3878288539,"RandomX":2629948988,"RandomY":4139597407,"RandomZ":1285940261,"StartTime":28229.0,"Objects":[{"StartTime":28229.0,"EndTime":28229.0,"Column":1}]},{"RandomW":1788551508,"RandomX":1285940261,"RandomY":3878288539,"RandomZ":1976280692,"StartTime":28929.0,"Objects":[{"StartTime":28929.0,"EndTime":28929.0,"Column":1},{"StartTime":28929.0,"EndTime":28929.0,"Column":4}]},{"RandomW":159147246,"RandomX":3878288539,"RandomY":1976280692,"RandomZ":1788551508,"StartTime":29163.0,"Objects":[{"StartTime":29163.0,"EndTime":29163.0,"Column":1}]},{"RandomW":2702806142,"RandomX":1976280692,"RandomY":1788551508,"RandomZ":159147246,"StartTime":29863.0,"Objects":[{"StartTime":29863.0,"EndTime":29863.0,"Column":2}]},{"RandomW":2311677487,"RandomX":1788551508,"RandomY":159147246,"RandomZ":2702806142,"StartTime":30213.0,"Objects":[{"StartTime":30213.0,"EndTime":30213.0,"Column":3}]},{"RandomW":3175953261,"RandomX":2311677487,"RandomY":988506051,"RandomZ":3495571300,"StartTime":30446.0,"Objects":[{"StartTime":30446.0,"EndTime":31146.0,"Column":2}]},{"RandomW":516122535,"RandomX":3495571300,"RandomY":3175953261,"RandomZ":2138555125,"StartTime":31730.0,"Objects":[{"StartTime":31730.0,"EndTime":31730.0,"Column":2},{"StartTime":31730.0,"EndTime":31730.0,"Column":1}]},{"RandomW":534989332,"RandomX":3175953261,"RandomY":2138555125,"RandomZ":516122535,"StartTime":32080.0,"Objects":[{"StartTime":32080.0,"EndTime":32080.0,"Column":2}]},{"RandomW":3420570846,"RandomX":2138555125,"RandomY":516122535,"RandomZ":534989332,"StartTime":32430.0,"Objects":[{"StartTime":32430.0,"EndTime":32430.0,"Column":2}]},{"RandomW":172021565,"RandomX":516122535,"RandomY":534989332,"RandomZ":3420570846,"StartTime":32663.0,"Objects":[{"StartTime":32663.0,"EndTime":32663.0,"Column":2}]},{"RandomW":168636292,"RandomX":3420570846,"RandomY":172021565,"RandomZ":263944077,"StartTime":32780.0,"Objects":[{"StartTime":32780.0,"EndTime":32780.0,"Column":0}]},{"RandomW":3473923375,"RandomX":172021565,"RandomY":263944077,"RandomZ":168636292,"StartTime":33597.0,"Objects":[{"StartTime":33597.0,"EndTime":33597.0,"Column":1}]},{"RandomW":3287941836,"RandomX":263944077,"RandomY":168636292,"RandomZ":3473923375,"StartTime":33947.0,"Objects":[{"StartTime":33947.0,"EndTime":33947.0,"Column":1}]},{"RandomW":1950056015,"RandomX":3473923375,"RandomY":3287941836,"RandomZ":388563489,"StartTime":34180.0,"Objects":[{"StartTime":34180.0,"EndTime":35230.0,"Column":5}]},{"RandomW":3600000321,"RandomX":388563489,"RandomY":1950056015,"RandomZ":3312202562,"StartTime":35464.0,"Objects":[{"StartTime":35464.0,"EndTime":36164.0,"Column":4}]},{"RandomW":647123919,"RandomX":3312202562,"RandomY":3600000321,"RandomZ":2314505656,"StartTime":36397.0,"Objects":[{"StartTime":36397.0,"EndTime":37097.0,"Column":1}]},{"RandomW":3375531720,"RandomX":2314505656,"RandomY":647123919,"RandomZ":2193654396,"StartTime":37564.0,"Objects":[{"StartTime":37564.0,"EndTime":37914.0,"Column":3}]},{"RandomW":2335314869,"RandomX":3834006299,"RandomY":1346269295,"RandomZ":3597388662,"StartTime":38264.0,"Objects":[{"StartTime":38264.0,"EndTime":38264.0,"Column":4},{"StartTime":38380.0,"EndTime":38380.0,"Column":3},{"StartTime":38496.0,"EndTime":38496.0,"Column":4}]},{"RandomW":1564102491,"RandomX":1346269295,"RandomY":3597388662,"RandomZ":2335314869,"StartTime":39197.0,"Objects":[{"StartTime":39197.0,"EndTime":39197.0,"Column":2}]},{"RandomW":1989977426,"RandomX":2335314869,"RandomY":1564102491,"RandomZ":4263834011,"StartTime":39431.0,"Objects":[{"StartTime":39431.0,"EndTime":39431.0,"Column":2},{"StartTime":39431.0,"EndTime":39431.0,"Column":5}]},{"RandomW":3806815718,"RandomX":4263834011,"RandomY":1989977426,"RandomZ":1831387023,"StartTime":39664.0,"Objects":[{"StartTime":39664.0,"EndTime":39664.0,"Column":1},{"StartTime":39664.0,"EndTime":39664.0,"Column":4}]},{"RandomW":999749640,"RandomX":1989977426,"RandomY":1831387023,"RandomZ":3806815718,"StartTime":39898.0,"Objects":[{"StartTime":39898.0,"EndTime":40831.0,"Column":1}]},{"RandomW":2830335005,"RandomX":1831387023,"RandomY":3806815718,"RandomZ":999749640,"StartTime":41298.0,"Objects":[{"StartTime":41298.0,"EndTime":41298.0,"Column":1}]},{"RandomW":2152692291,"RandomX":3806815718,"RandomY":999749640,"RandomZ":2830335005,"StartTime":41648.0,"Objects":[{"StartTime":41648.0,"EndTime":41648.0,"Column":1}]},{"RandomW":1499396089,"RandomX":999749640,"RandomY":2830335005,"RandomZ":2152692291,"StartTime":41998.0,"Objects":[{"StartTime":41998.0,"EndTime":41998.0,"Column":2}]},{"RandomW":3582202466,"RandomX":2830335005,"RandomY":2152692291,"RandomZ":1499396089,"StartTime":42231.0,"Objects":[{"StartTime":42231.0,"EndTime":42231.0,"Column":2}]},{"RandomW":3873754971,"RandomX":2152692291,"RandomY":1499396089,"RandomZ":3582202466,"StartTime":42931.0,"Objects":[{"StartTime":42931.0,"EndTime":42931.0,"Column":4}]},{"RandomW":495070374,"RandomX":1499396089,"RandomY":3582202466,"RandomZ":3873754971,"StartTime":43165.0,"Objects":[{"StartTime":43165.0,"EndTime":43165.0,"Column":4}]},{"RandomW":3016618448,"RandomX":3582202466,"RandomY":3873754971,"RandomZ":495070374,"StartTime":43398.0,"Objects":[{"StartTime":43398.0,"EndTime":43398.0,"Column":4}]},{"RandomW":1177547465,"RandomX":3873754971,"RandomY":495070374,"RandomZ":3016618448,"StartTime":43631.0,"Objects":[{"StartTime":43631.0,"EndTime":43631.0,"Column":3}]},{"RandomW":2255582016,"RandomX":495070374,"RandomY":3016618448,"RandomZ":1177547465,"StartTime":43865.0,"Objects":[{"StartTime":43865.0,"EndTime":43865.0,"Column":3}]},{"RandomW":2325387316,"RandomX":3016618448,"RandomY":1177547465,"RandomZ":2255582016,"StartTime":44098.0,"Objects":[{"StartTime":44098.0,"EndTime":44098.0,"Column":2}]},{"RandomW":1443216326,"RandomX":1177547465,"RandomY":2255582016,"RandomZ":2325387316,"StartTime":44332.0,"Objects":[{"StartTime":44332.0,"EndTime":44332.0,"Column":2}]},{"RandomW":1650665398,"RandomX":2325387316,"RandomY":1443216326,"RandomZ":1871032949,"StartTime":44565.0,"Objects":[{"StartTime":44565.0,"EndTime":44565.0,"Column":1},{"StartTime":44565.0,"EndTime":44565.0,"Column":4}]},{"RandomW":1204166455,"RandomX":1871032949,"RandomY":1650665398,"RandomZ":1013336310,"StartTime":44798.0,"Objects":[{"StartTime":44798.0,"EndTime":45498.0,"Column":3}]},{"RandomW":2125976115,"RandomX":1013336310,"RandomY":1204166455,"RandomZ":93461408,"StartTime":45732.0,"Objects":[{"StartTime":45732.0,"EndTime":46432.0,"Column":5}]},{"RandomW":1391245329,"RandomX":1889010923,"RandomY":131109480,"RandomZ":2450179625,"StartTime":46665.0,"Objects":[{"StartTime":46665.0,"EndTime":47365.0,"Column":0},{"StartTime":46665.0,"EndTime":47365.0,"Column":3}]},{"RandomW":1629740061,"RandomX":2450179625,"RandomY":1391245329,"RandomZ":3806548475,"StartTime":47599.0,"Objects":[{"StartTime":47599.0,"EndTime":47949.0,"Column":4}]},{"RandomW":2462543108,"RandomX":3806548475,"RandomY":1629740061,"RandomZ":2782684574,"StartTime":48532.0,"Objects":[{"StartTime":48532.0,"EndTime":49232.0,"Column":0}]},{"RandomW":1398343675,"RandomX":2462543108,"RandomY":1783863854,"RandomZ":368009293,"StartTime":49466.0,"Objects":[{"StartTime":49466.0,"EndTime":50166.0,"Column":1},{"StartTime":49466.0,"EndTime":50166.0,"Column":3}]},{"RandomW":1655209110,"RandomX":1398343675,"RandomY":4200591321,"RandomZ":204183638,"StartTime":50399.0,"Objects":[{"StartTime":50399.0,"EndTime":51099.0,"Column":0},{"StartTime":50399.0,"EndTime":51099.0,"Column":4}]},{"RandomW":2898792131,"RandomX":1655209110,"RandomY":4183149031,"RandomZ":4235317299,"StartTime":51333.0,"Objects":[{"StartTime":51333.0,"EndTime":52033.0,"Column":5},{"StartTime":51333.0,"EndTime":52033.0,"Column":2}]},{"RandomW":2376440576,"RandomX":4183149031,"RandomY":4235317299,"RandomZ":2898792131,"StartTime":52266.0,"Objects":[{"StartTime":52266.0,"EndTime":52266.0,"Column":0}]},{"RandomW":3672662434,"RandomX":4235317299,"RandomY":2898792131,"RandomZ":2376440576,"StartTime":52499.0,"Objects":[{"StartTime":52499.0,"EndTime":52499.0,"Column":1}]},{"RandomW":1144553308,"RandomX":2376440576,"RandomY":3672662434,"RandomZ":2825568900,"StartTime":52849.0,"Objects":[{"StartTime":52849.0,"EndTime":53199.0,"Column":3}]},{"RandomW":3856961856,"RandomX":3672662434,"RandomY":2825568900,"RandomZ":1144553308,"StartTime":54133.0,"Objects":[{"StartTime":54133.0,"EndTime":54133.0,"Column":3}]},{"RandomW":3856961856,"RandomX":3672662434,"RandomY":2825568900,"RandomZ":1144553308,"StartTime":54366.0,"Objects":[{"StartTime":54366.0,"EndTime":54366.0,"Column":2}]},{"RandomW":3856961856,"RandomX":3672662434,"RandomY":2825568900,"RandomZ":1144553308,"StartTime":54600.0,"Objects":[{"StartTime":54600.0,"EndTime":54600.0,"Column":3}]},{"RandomW":2182646490,"RandomX":1144553308,"RandomY":3856961856,"RandomZ":2090342703,"StartTime":55066.0,"Objects":[{"StartTime":55066.0,"EndTime":55066.0,"Column":2},{"StartTime":55066.0,"EndTime":55066.0,"Column":0}]},{"RandomW":2182646490,"RandomX":1144553308,"RandomY":3856961856,"RandomZ":2090342703,"StartTime":55300.0,"Objects":[{"StartTime":55300.0,"EndTime":55300.0,"Column":5},{"StartTime":55300.0,"EndTime":55300.0,"Column":3}]},{"RandomW":2182646490,"RandomX":1144553308,"RandomY":3856961856,"RandomZ":2090342703,"StartTime":55533.0,"Objects":[{"StartTime":55533.0,"EndTime":55533.0,"Column":2},{"StartTime":55533.0,"EndTime":55533.0,"Column":0}]},{"RandomW":3304208416,"RandomX":2090342703,"RandomY":2182646490,"RandomZ":90031962,"StartTime":56000.0,"Objects":[{"StartTime":56000.0,"EndTime":56233.0,"Column":3}]},{"RandomW":1041697651,"RandomX":90031962,"RandomY":3304208416,"RandomZ":2015301872,"StartTime":56583.0,"Objects":[{"StartTime":56583.0,"EndTime":56583.0,"Column":1},{"StartTime":56583.0,"EndTime":56583.0,"Column":2}]},{"RandomW":3818981880,"RandomX":15037736,"RandomY":2251270868,"RandomZ":2287819377,"StartTime":56700.0,"Objects":[{"StartTime":56700.0,"EndTime":56700.0,"Column":0},{"StartTime":56700.0,"EndTime":56700.0,"Column":4}]},{"RandomW":3368447121,"RandomX":2251270868,"RandomY":2287819377,"RandomZ":3818981880,"StartTime":56933.0,"Objects":[{"StartTime":56933.0,"EndTime":56933.0,"Column":1}]},{"RandomW":860096087,"RandomX":2287819377,"RandomY":3818981880,"RandomZ":3368447121,"StartTime":57867.0,"Objects":[{"StartTime":57867.0,"EndTime":57867.0,"Column":3}]},{"RandomW":860096087,"RandomX":2287819377,"RandomY":3818981880,"RandomZ":3368447121,"StartTime":58100.0,"Objects":[{"StartTime":58100.0,"EndTime":58100.0,"Column":2}]},{"RandomW":860096087,"RandomX":2287819377,"RandomY":3818981880,"RandomZ":3368447121,"StartTime":58334.0,"Objects":[{"StartTime":58334.0,"EndTime":58334.0,"Column":3}]},{"RandomW":1369988252,"RandomX":3818981880,"RandomY":3368447121,"RandomZ":860096087,"StartTime":58800.0,"Objects":[{"StartTime":58800.0,"EndTime":58800.0,"Column":4}]},{"RandomW":1369988252,"RandomX":3818981880,"RandomY":3368447121,"RandomZ":860096087,"StartTime":59034.0,"Objects":[{"StartTime":59034.0,"EndTime":59034.0,"Column":1}]},{"RandomW":1369988252,"RandomX":3818981880,"RandomY":3368447121,"RandomZ":860096087,"StartTime":59267.0,"Objects":[{"StartTime":59267.0,"EndTime":59267.0,"Column":4}]}]} \ No newline at end of file +{"Mappings":[{"RandomW":273523780,"RandomX":842502087,"RandomY":3579807591,"RandomZ":273326509,"StartTime":7693.0,"Objects":[{"StartTime":7693.0,"EndTime":7693.0,"Column":0}]},{"RandomW":2659866685,"RandomX":3579807591,"RandomY":273326509,"RandomZ":273523780,"StartTime":8043.0,"Objects":[{"StartTime":8043.0,"EndTime":8043.0,"Column":1}]},{"RandomW":3083309108,"RandomX":273326509,"RandomY":273523780,"RandomZ":2659866685,"StartTime":8393.0,"Objects":[{"StartTime":8393.0,"EndTime":8393.0,"Column":2}]},{"RandomW":2413296944,"RandomX":2659866685,"RandomY":3083309108,"RandomZ":4072999080,"StartTime":8626.0,"Objects":[{"StartTime":8626.0,"EndTime":8626.0,"Column":2},{"StartTime":8626.0,"EndTime":8626.0,"Column":0}]},{"RandomW":1129322311,"RandomX":3083309108,"RandomY":4072999080,"RandomZ":2413296944,"StartTime":8860.0,"Objects":[{"StartTime":8860.0,"EndTime":8860.0,"Column":3}]},{"RandomW":3365759273,"RandomX":4072999080,"RandomY":2413296944,"RandomZ":1129322311,"StartTime":9326.0,"Objects":[{"StartTime":9326.0,"EndTime":9326.0,"Column":3}]},{"RandomW":315078874,"RandomX":2413296944,"RandomY":1129322311,"RandomZ":3365759273,"StartTime":9560.0,"Objects":[{"StartTime":9560.0,"EndTime":9560.0,"Column":3}]},{"RandomW":583662031,"RandomX":1129322311,"RandomY":3365759273,"RandomZ":315078874,"StartTime":9793.0,"Objects":[{"StartTime":9793.0,"EndTime":9793.0,"Column":3}]},{"RandomW":3789568254,"RandomX":3365759273,"RandomY":315078874,"RandomZ":583662031,"StartTime":10260.0,"Objects":[{"StartTime":10260.0,"EndTime":10260.0,"Column":2}]},{"RandomW":3256340938,"RandomX":315078874,"RandomY":583662031,"RandomZ":3789568254,"StartTime":10493.0,"Objects":[{"StartTime":10493.0,"EndTime":10493.0,"Column":2}]},{"RandomW":2152938451,"RandomX":3789568254,"RandomY":3256340938,"RandomZ":3979976762,"StartTime":10727.0,"Objects":[{"StartTime":10727.0,"EndTime":10727.0,"Column":1},{"StartTime":10727.0,"EndTime":10727.0,"Column":0}]},{"RandomW":1620362479,"RandomX":3256340938,"RandomY":3979976762,"RandomZ":2152938451,"StartTime":11427.0,"Objects":[{"StartTime":11427.0,"EndTime":11427.0,"Column":1}]},{"RandomW":477221046,"RandomX":3979976762,"RandomY":2152938451,"RandomZ":1620362479,"StartTime":11777.0,"Objects":[{"StartTime":11777.0,"EndTime":11777.0,"Column":1}]},{"RandomW":1013554034,"RandomX":2152938451,"RandomY":1620362479,"RandomZ":477221046,"StartTime":12127.0,"Objects":[{"StartTime":12127.0,"EndTime":12127.0,"Column":2}]},{"RandomW":637383311,"RandomX":1620362479,"RandomY":477221046,"RandomZ":1013554034,"StartTime":12360.0,"Objects":[{"StartTime":12360.0,"EndTime":12360.0,"Column":2}]},{"RandomW":3817388387,"RandomX":477221046,"RandomY":1013554034,"RandomZ":637383311,"StartTime":12594.0,"Objects":[{"StartTime":12594.0,"EndTime":12594.0,"Column":3}]},{"RandomW":19695232,"RandomX":637383311,"RandomY":3817388387,"RandomZ":1911435716,"StartTime":13060.0,"Objects":[{"StartTime":13060.0,"EndTime":13060.0,"Column":3},{"StartTime":13060.0,"EndTime":13060.0,"Column":0}]},{"RandomW":3381470688,"RandomX":3817388387,"RandomY":1911435716,"RandomZ":19695232,"StartTime":13294.0,"Objects":[{"StartTime":13294.0,"EndTime":13294.0,"Column":3}]},{"RandomW":1862836779,"RandomX":19695232,"RandomY":3381470688,"RandomZ":1869143571,"StartTime":13527.0,"Objects":[{"StartTime":13527.0,"EndTime":13527.0,"Column":3},{"StartTime":13527.0,"EndTime":13527.0,"Column":5}]},{"RandomW":175452620,"RandomX":3381470688,"RandomY":1869143571,"RandomZ":1862836779,"StartTime":13994.0,"Objects":[{"StartTime":13994.0,"EndTime":13994.0,"Column":4}]},{"RandomW":2859972423,"RandomX":1869143571,"RandomY":1862836779,"RandomZ":175452620,"StartTime":14227.0,"Objects":[{"StartTime":14227.0,"EndTime":14227.0,"Column":4}]},{"RandomW":2210823260,"RandomX":1862836779,"RandomY":175452620,"RandomZ":2859972423,"StartTime":14461.0,"Objects":[{"StartTime":14461.0,"EndTime":14461.0,"Column":5}]},{"RandomW":2851442677,"RandomX":175452620,"RandomY":2859972423,"RandomZ":2210823260,"StartTime":14927.0,"Objects":[{"StartTime":14927.0,"EndTime":16561.0,"Column":1}]},{"RandomW":179122262,"RandomX":2859972423,"RandomY":2210823260,"RandomZ":2851442677,"StartTime":16794.0,"Objects":[{"StartTime":16794.0,"EndTime":18078.0,"Column":0}]},{"RandomW":2917386405,"RandomX":2851442677,"RandomY":179122262,"RandomZ":494367691,"StartTime":18661.0,"Objects":[{"StartTime":18661.0,"EndTime":19127.0,"Column":2}]},{"RandomW":3407923728,"RandomX":494367691,"RandomY":2917386405,"RandomZ":2825679051,"StartTime":19595.0,"Objects":[{"StartTime":19595.0,"EndTime":20061.0,"Column":3}]},{"RandomW":358318928,"RandomX":3407923728,"RandomY":1835995540,"RandomZ":3732560508,"StartTime":20528.0,"Objects":[{"StartTime":20528.0,"EndTime":20994.0,"Column":4},{"StartTime":20528.0,"EndTime":20994.0,"Column":1}]},{"RandomW":3440439960,"RandomX":3732560508,"RandomY":358318928,"RandomZ":3638999969,"StartTime":21462.0,"Objects":[{"StartTime":21462.0,"EndTime":21928.0,"Column":3}]},{"RandomW":3249928444,"RandomX":358318928,"RandomY":3638999969,"RandomZ":3440439960,"StartTime":22395.0,"Objects":[{"StartTime":22395.0,"EndTime":22395.0,"Column":1}]},{"RandomW":3857394572,"RandomX":3440439960,"RandomY":3249928444,"RandomZ":138257049,"StartTime":22628.0,"Objects":[{"StartTime":22628.0,"EndTime":24028.0,"Column":4}]},{"RandomW":2938470811,"RandomX":3249928444,"RandomY":138257049,"RandomZ":3857394572,"StartTime":24262.0,"Objects":[{"StartTime":24262.0,"EndTime":24262.0,"Column":3}]},{"RandomW":3241803419,"RandomX":138257049,"RandomY":3857394572,"RandomZ":2938470811,"StartTime":24495.0,"Objects":[{"StartTime":24495.0,"EndTime":24495.0,"Column":4}]},{"RandomW":620078415,"RandomX":3857394572,"RandomY":2938470811,"RandomZ":3241803419,"StartTime":25195.0,"Objects":[{"StartTime":25195.0,"EndTime":25195.0,"Column":4}]},{"RandomW":2566806806,"RandomX":2938470811,"RandomY":3241803419,"RandomZ":620078415,"StartTime":25429.0,"Objects":[{"StartTime":25429.0,"EndTime":25429.0,"Column":4}]},{"RandomW":458505931,"RandomX":3241803419,"RandomY":620078415,"RandomZ":2566806806,"StartTime":26129.0,"Objects":[{"StartTime":26129.0,"EndTime":26129.0,"Column":3}]},{"RandomW":2629948988,"RandomX":2566806806,"RandomY":458505931,"RandomZ":362272284,"StartTime":26362.0,"Objects":[{"StartTime":26362.0,"EndTime":27762.0,"Column":1}]},{"RandomW":1285940261,"RandomX":362272284,"RandomY":2629948988,"RandomZ":4139597407,"StartTime":27996.0,"Objects":[{"StartTime":27996.0,"EndTime":27996.0,"Column":1},{"StartTime":27996.0,"EndTime":27996.0,"Column":3}]},{"RandomW":3878288539,"RandomX":2629948988,"RandomY":4139597407,"RandomZ":1285940261,"StartTime":28229.0,"Objects":[{"StartTime":28229.0,"EndTime":28229.0,"Column":1}]},{"RandomW":1788551508,"RandomX":1285940261,"RandomY":3878288539,"RandomZ":1976280692,"StartTime":28929.0,"Objects":[{"StartTime":28929.0,"EndTime":28929.0,"Column":1},{"StartTime":28929.0,"EndTime":28929.0,"Column":4}]},{"RandomW":159147246,"RandomX":3878288539,"RandomY":1976280692,"RandomZ":1788551508,"StartTime":29163.0,"Objects":[{"StartTime":29163.0,"EndTime":29163.0,"Column":1}]},{"RandomW":2702806142,"RandomX":1976280692,"RandomY":1788551508,"RandomZ":159147246,"StartTime":29863.0,"Objects":[{"StartTime":29863.0,"EndTime":29863.0,"Column":3}]},{"RandomW":2311677487,"RandomX":1788551508,"RandomY":159147246,"RandomZ":2702806142,"StartTime":30213.0,"Objects":[{"StartTime":30213.0,"EndTime":30213.0,"Column":3}]},{"RandomW":3175953261,"RandomX":2311677487,"RandomY":988506051,"RandomZ":3495571300,"StartTime":30446.0,"Objects":[{"StartTime":30446.0,"EndTime":31146.0,"Column":2}]},{"RandomW":516122535,"RandomX":3495571300,"RandomY":3175953261,"RandomZ":2138555125,"StartTime":31730.0,"Objects":[{"StartTime":31730.0,"EndTime":31730.0,"Column":2},{"StartTime":31730.0,"EndTime":31730.0,"Column":1}]},{"RandomW":534989332,"RandomX":3175953261,"RandomY":2138555125,"RandomZ":516122535,"StartTime":32080.0,"Objects":[{"StartTime":32080.0,"EndTime":32080.0,"Column":2}]},{"RandomW":3420570846,"RandomX":2138555125,"RandomY":516122535,"RandomZ":534989332,"StartTime":32430.0,"Objects":[{"StartTime":32430.0,"EndTime":32430.0,"Column":2}]},{"RandomW":172021565,"RandomX":516122535,"RandomY":534989332,"RandomZ":3420570846,"StartTime":32663.0,"Objects":[{"StartTime":32663.0,"EndTime":32663.0,"Column":2}]},{"RandomW":168636292,"RandomX":3420570846,"RandomY":172021565,"RandomZ":263944077,"StartTime":32780.0,"Objects":[{"StartTime":32780.0,"EndTime":32780.0,"Column":0}]},{"RandomW":3473923375,"RandomX":172021565,"RandomY":263944077,"RandomZ":168636292,"StartTime":33597.0,"Objects":[{"StartTime":33597.0,"EndTime":33597.0,"Column":1}]},{"RandomW":3287941836,"RandomX":263944077,"RandomY":168636292,"RandomZ":3473923375,"StartTime":33947.0,"Objects":[{"StartTime":33947.0,"EndTime":33947.0,"Column":1}]},{"RandomW":1950056015,"RandomX":3473923375,"RandomY":3287941836,"RandomZ":388563489,"StartTime":34180.0,"Objects":[{"StartTime":34180.0,"EndTime":35230.0,"Column":5}]},{"RandomW":3600000321,"RandomX":388563489,"RandomY":1950056015,"RandomZ":3312202562,"StartTime":35464.0,"Objects":[{"StartTime":35464.0,"EndTime":36164.0,"Column":4}]},{"RandomW":647123919,"RandomX":3312202562,"RandomY":3600000321,"RandomZ":2314505656,"StartTime":36397.0,"Objects":[{"StartTime":36397.0,"EndTime":37097.0,"Column":1}]},{"RandomW":3375531720,"RandomX":2314505656,"RandomY":647123919,"RandomZ":2193654396,"StartTime":37564.0,"Objects":[{"StartTime":37564.0,"EndTime":37914.0,"Column":3}]},{"RandomW":2335314869,"RandomX":3834006299,"RandomY":1346269295,"RandomZ":3597388662,"StartTime":38264.0,"Objects":[{"StartTime":38264.0,"EndTime":38264.0,"Column":4},{"StartTime":38380.0,"EndTime":38380.0,"Column":3},{"StartTime":38496.0,"EndTime":38496.0,"Column":4}]},{"RandomW":1564102491,"RandomX":1346269295,"RandomY":3597388662,"RandomZ":2335314869,"StartTime":39197.0,"Objects":[{"StartTime":39197.0,"EndTime":39197.0,"Column":2}]},{"RandomW":1989977426,"RandomX":2335314869,"RandomY":1564102491,"RandomZ":4263834011,"StartTime":39431.0,"Objects":[{"StartTime":39431.0,"EndTime":39431.0,"Column":2},{"StartTime":39431.0,"EndTime":39431.0,"Column":5}]},{"RandomW":3806815718,"RandomX":4263834011,"RandomY":1989977426,"RandomZ":1831387023,"StartTime":39664.0,"Objects":[{"StartTime":39664.0,"EndTime":39664.0,"Column":1},{"StartTime":39664.0,"EndTime":39664.0,"Column":4}]},{"RandomW":999749640,"RandomX":1989977426,"RandomY":1831387023,"RandomZ":3806815718,"StartTime":39898.0,"Objects":[{"StartTime":39898.0,"EndTime":40831.0,"Column":1}]},{"RandomW":2830335005,"RandomX":1831387023,"RandomY":3806815718,"RandomZ":999749640,"StartTime":41298.0,"Objects":[{"StartTime":41298.0,"EndTime":41298.0,"Column":1}]},{"RandomW":2152692291,"RandomX":3806815718,"RandomY":999749640,"RandomZ":2830335005,"StartTime":41648.0,"Objects":[{"StartTime":41648.0,"EndTime":41648.0,"Column":1}]},{"RandomW":1499396089,"RandomX":999749640,"RandomY":2830335005,"RandomZ":2152692291,"StartTime":41998.0,"Objects":[{"StartTime":41998.0,"EndTime":41998.0,"Column":2}]},{"RandomW":3582202466,"RandomX":2830335005,"RandomY":2152692291,"RandomZ":1499396089,"StartTime":42231.0,"Objects":[{"StartTime":42231.0,"EndTime":42231.0,"Column":2}]},{"RandomW":3873754971,"RandomX":2152692291,"RandomY":1499396089,"RandomZ":3582202466,"StartTime":42931.0,"Objects":[{"StartTime":42931.0,"EndTime":42931.0,"Column":4}]},{"RandomW":495070374,"RandomX":1499396089,"RandomY":3582202466,"RandomZ":3873754971,"StartTime":43165.0,"Objects":[{"StartTime":43165.0,"EndTime":43165.0,"Column":4}]},{"RandomW":3016618448,"RandomX":3582202466,"RandomY":3873754971,"RandomZ":495070374,"StartTime":43398.0,"Objects":[{"StartTime":43398.0,"EndTime":43398.0,"Column":4}]},{"RandomW":1177547465,"RandomX":3873754971,"RandomY":495070374,"RandomZ":3016618448,"StartTime":43631.0,"Objects":[{"StartTime":43631.0,"EndTime":43631.0,"Column":3}]},{"RandomW":2255582016,"RandomX":495070374,"RandomY":3016618448,"RandomZ":1177547465,"StartTime":43865.0,"Objects":[{"StartTime":43865.0,"EndTime":43865.0,"Column":3}]},{"RandomW":2325387316,"RandomX":3016618448,"RandomY":1177547465,"RandomZ":2255582016,"StartTime":44098.0,"Objects":[{"StartTime":44098.0,"EndTime":44098.0,"Column":2}]},{"RandomW":1443216326,"RandomX":1177547465,"RandomY":2255582016,"RandomZ":2325387316,"StartTime":44332.0,"Objects":[{"StartTime":44332.0,"EndTime":44332.0,"Column":2}]},{"RandomW":1650665398,"RandomX":2325387316,"RandomY":1443216326,"RandomZ":1871032949,"StartTime":44565.0,"Objects":[{"StartTime":44565.0,"EndTime":44565.0,"Column":1},{"StartTime":44565.0,"EndTime":44565.0,"Column":4}]},{"RandomW":1204166455,"RandomX":1871032949,"RandomY":1650665398,"RandomZ":1013336310,"StartTime":44798.0,"Objects":[{"StartTime":44798.0,"EndTime":45498.0,"Column":3}]},{"RandomW":2125976115,"RandomX":1013336310,"RandomY":1204166455,"RandomZ":93461408,"StartTime":45732.0,"Objects":[{"StartTime":45732.0,"EndTime":46432.0,"Column":5}]},{"RandomW":1391245329,"RandomX":1889010923,"RandomY":131109480,"RandomZ":2450179625,"StartTime":46665.0,"Objects":[{"StartTime":46665.0,"EndTime":47365.0,"Column":0},{"StartTime":46665.0,"EndTime":47365.0,"Column":3}]},{"RandomW":1629740061,"RandomX":2450179625,"RandomY":1391245329,"RandomZ":3806548475,"StartTime":47599.0,"Objects":[{"StartTime":47599.0,"EndTime":47949.0,"Column":4}]},{"RandomW":2462543108,"RandomX":3806548475,"RandomY":1629740061,"RandomZ":2782684574,"StartTime":48532.0,"Objects":[{"StartTime":48532.0,"EndTime":49232.0,"Column":0}]},{"RandomW":1398343675,"RandomX":2462543108,"RandomY":1783863854,"RandomZ":368009293,"StartTime":49466.0,"Objects":[{"StartTime":49466.0,"EndTime":50166.0,"Column":1},{"StartTime":49466.0,"EndTime":50166.0,"Column":3}]},{"RandomW":1655209110,"RandomX":1398343675,"RandomY":4200591321,"RandomZ":204183638,"StartTime":50399.0,"Objects":[{"StartTime":50399.0,"EndTime":51099.0,"Column":0},{"StartTime":50399.0,"EndTime":51099.0,"Column":4}]},{"RandomW":2898792131,"RandomX":1655209110,"RandomY":4183149031,"RandomZ":4235317299,"StartTime":51333.0,"Objects":[{"StartTime":51333.0,"EndTime":52033.0,"Column":5},{"StartTime":51333.0,"EndTime":52033.0,"Column":2}]},{"RandomW":2376440576,"RandomX":4183149031,"RandomY":4235317299,"RandomZ":2898792131,"StartTime":52266.0,"Objects":[{"StartTime":52266.0,"EndTime":52266.0,"Column":0}]},{"RandomW":3672662434,"RandomX":4235317299,"RandomY":2898792131,"RandomZ":2376440576,"StartTime":52499.0,"Objects":[{"StartTime":52499.0,"EndTime":52499.0,"Column":1}]},{"RandomW":1144553308,"RandomX":2376440576,"RandomY":3672662434,"RandomZ":2825568900,"StartTime":52849.0,"Objects":[{"StartTime":52849.0,"EndTime":53199.0,"Column":3}]},{"RandomW":3856961856,"RandomX":3672662434,"RandomY":2825568900,"RandomZ":1144553308,"StartTime":54133.0,"Objects":[{"StartTime":54133.0,"EndTime":54133.0,"Column":3}]},{"RandomW":3856961856,"RandomX":3672662434,"RandomY":2825568900,"RandomZ":1144553308,"StartTime":54366.0,"Objects":[{"StartTime":54366.0,"EndTime":54366.0,"Column":2}]},{"RandomW":3856961856,"RandomX":3672662434,"RandomY":2825568900,"RandomZ":1144553308,"StartTime":54600.0,"Objects":[{"StartTime":54600.0,"EndTime":54600.0,"Column":3}]},{"RandomW":2182646490,"RandomX":1144553308,"RandomY":3856961856,"RandomZ":2090342703,"StartTime":55066.0,"Objects":[{"StartTime":55066.0,"EndTime":55066.0,"Column":2},{"StartTime":55066.0,"EndTime":55066.0,"Column":0}]},{"RandomW":2182646490,"RandomX":1144553308,"RandomY":3856961856,"RandomZ":2090342703,"StartTime":55300.0,"Objects":[{"StartTime":55300.0,"EndTime":55300.0,"Column":5},{"StartTime":55300.0,"EndTime":55300.0,"Column":3}]},{"RandomW":2182646490,"RandomX":1144553308,"RandomY":3856961856,"RandomZ":2090342703,"StartTime":55533.0,"Objects":[{"StartTime":55533.0,"EndTime":55533.0,"Column":2},{"StartTime":55533.0,"EndTime":55533.0,"Column":0}]},{"RandomW":3304208416,"RandomX":2090342703,"RandomY":2182646490,"RandomZ":90031962,"StartTime":56000.0,"Objects":[{"StartTime":56000.0,"EndTime":56233.0,"Column":3}]},{"RandomW":1041697651,"RandomX":90031962,"RandomY":3304208416,"RandomZ":2015301872,"StartTime":56583.0,"Objects":[{"StartTime":56583.0,"EndTime":56583.0,"Column":1},{"StartTime":56583.0,"EndTime":56583.0,"Column":2}]},{"RandomW":3818981880,"RandomX":15037736,"RandomY":2251270868,"RandomZ":2287819377,"StartTime":56700.0,"Objects":[{"StartTime":56700.0,"EndTime":56700.0,"Column":0},{"StartTime":56700.0,"EndTime":56700.0,"Column":4}]},{"RandomW":3368447121,"RandomX":2251270868,"RandomY":2287819377,"RandomZ":3818981880,"StartTime":56933.0,"Objects":[{"StartTime":56933.0,"EndTime":56933.0,"Column":1}]},{"RandomW":860096087,"RandomX":2287819377,"RandomY":3818981880,"RandomZ":3368447121,"StartTime":57867.0,"Objects":[{"StartTime":57867.0,"EndTime":57867.0,"Column":3}]},{"RandomW":860096087,"RandomX":2287819377,"RandomY":3818981880,"RandomZ":3368447121,"StartTime":58100.0,"Objects":[{"StartTime":58100.0,"EndTime":58100.0,"Column":2}]},{"RandomW":860096087,"RandomX":2287819377,"RandomY":3818981880,"RandomZ":3368447121,"StartTime":58334.0,"Objects":[{"StartTime":58334.0,"EndTime":58334.0,"Column":3}]},{"RandomW":1369988252,"RandomX":3818981880,"RandomY":3368447121,"RandomZ":860096087,"StartTime":58800.0,"Objects":[{"StartTime":58800.0,"EndTime":58800.0,"Column":4}]},{"RandomW":1369988252,"RandomX":3818981880,"RandomY":3368447121,"RandomZ":860096087,"StartTime":59034.0,"Objects":[{"StartTime":59034.0,"EndTime":59034.0,"Column":1}]},{"RandomW":1369988252,"RandomX":3818981880,"RandomY":3368447121,"RandomZ":860096087,"StartTime":59267.0,"Objects":[{"StartTime":59267.0,"EndTime":59267.0,"Column":4}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json index 753db99856..a25c8a12ab 100644 --- a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/basic-expected-conversion.json @@ -1,132 +1,168 @@ { - "Mappings": [{ - "RandomW": 2659373485, - "RandomX": 3579807591, - "RandomY": 273326509, - "RandomZ": 272969173, - "StartTime": 500.0, - "Objects": [{ - "StartTime": 500.0, - "EndTime": 2500.0, - "Column": 0 - }, { - "StartTime": 1500.0, - "EndTime": 2500.0, - "Column": 1 - }] - }, { - "RandomW": 3083803045, - "RandomX": 273326509, - "RandomY": 272969173, - "RandomZ": 2659373485, - "StartTime": 3000.0, - "Objects": [{ - "StartTime": 3000.0, - "EndTime": 4000.0, - "Column": 2 - }] - }, { - "RandomW": 4073554232, - "RandomX": 272969173, - "RandomY": 2659373485, - "RandomZ": 3083803045, - "StartTime": 4500.0, - "Objects": [{ - "StartTime": 4500.0, - "EndTime": 5500.0, - "Column": 4 - }] - }, { - "RandomW": 3420401969, - "RandomX": 2659373485, - "RandomY": 3083803045, - "RandomZ": 4073554232, - "StartTime": 6000.0, - "Objects": [{ - "StartTime": 6000.0, - "EndTime": 6500.0, - "Column": 2 - }] - }, { - "RandomW": 1129881182, - "RandomX": 3083803045, - "RandomY": 4073554232, - "RandomZ": 3420401969, - "StartTime": 7000.0, - "Objects": [{ - "StartTime": 7000.0, - "EndTime": 8000.0, - "Column": 2 - }] - }, { - "RandomW": 315568458, - "RandomX": 3420401969, - "RandomY": 1129881182, - "RandomZ": 2358617505, - "StartTime": 8500.0, - "Objects": [{ - "StartTime": 8500.0, - "EndTime": 11000.0, - "Column": 0 - }] - }, { - "RandomW": 548134043, - "RandomX": 1129881182, - "RandomY": 2358617505, - "RandomZ": 315568458, - "StartTime": 11500.0, - "Objects": [{ - "StartTime": 11500.0, - "EndTime": 12000.0, - "Column": 1 - }] - }, { - "RandomW": 3979422122, - "RandomX": 548134043, - "RandomY": 2810584254, - "RandomZ": 2250186050, - "StartTime": 12500.0, - "Objects": [{ - "StartTime": 12500.0, - "EndTime": 16500.0, - "Column": 4 - }] - }, { - "RandomW": 2466283411, - "RandomX": 2810584254, - "RandomY": 2250186050, - "RandomZ": 3979422122, - "StartTime": 17000.0, - "Objects": [{ - "StartTime": 17000.0, - "EndTime": 18000.0, - "Column": 2 - }] - }, { - "RandomW": 83157665, - "RandomX": 2250186050, - "RandomY": 3979422122, - "RandomZ": 2466283411, - "StartTime": 18500.0, - "Objects": [{ - "StartTime": 18500.0, - "EndTime": 19450.0, - "Column": 0 - }] - }, { - "RandomW": 2383087700, - "RandomX": 83157665, - "RandomY": 2055150192, - "RandomZ": 510071020, - "StartTime": 19875.0, - "Objects": [{ - "StartTime": 19875.0, - "EndTime": 23875.0, - "Column": 1 - }, { - "StartTime": 19875.0, - "EndTime": 23875.0, - "Column": 0 - }] - }] + "Mappings": [ + { + "RandomW": 2659373485, + "RandomX": 3579807591, + "RandomY": 273326509, + "RandomZ": 272969173, + "StartTime": 500.0, + "Objects": [ + { + "StartTime": 500.0, + "EndTime": 2500.0, + "Column": 0 + }, + { + "StartTime": 1500.0, + "EndTime": 2500.0, + "Column": 1 + } + ] + }, + { + "RandomW": 3083803045, + "RandomX": 273326509, + "RandomY": 272969173, + "RandomZ": 2659373485, + "StartTime": 3000.0, + "Objects": [ + { + "StartTime": 3000.0, + "EndTime": 4000.0, + "Column": 2 + } + ] + }, + { + "RandomW": 4073554232, + "RandomX": 272969173, + "RandomY": 2659373485, + "RandomZ": 3083803045, + "StartTime": 4500.0, + "Objects": [ + { + "StartTime": 4500.0, + "EndTime": 5500.0, + "Column": 4 + } + ] + }, + { + "RandomW": 3420401969, + "RandomX": 2659373485, + "RandomY": 3083803045, + "RandomZ": 4073554232, + "StartTime": 6000.0, + "Objects": [ + { + "StartTime": 6000.0, + "EndTime": 6500.0, + "Column": 2 + } + ] + }, + { + "RandomW": 1129881182, + "RandomX": 3083803045, + "RandomY": 4073554232, + "RandomZ": 3420401969, + "StartTime": 7000.0, + "Objects": [ + { + "StartTime": 7000.0, + "EndTime": 8000.0, + "Column": 2 + } + ] + }, + { + "RandomW": 315568458, + "RandomX": 3420401969, + "RandomY": 1129881182, + "RandomZ": 2358617505, + "StartTime": 8500.0, + "Objects": [ + { + "StartTime": 8500.0, + "EndTime": 11000.0, + "Column": 0 + } + ] + }, + { + "RandomW": 548134043, + "RandomX": 1129881182, + "RandomY": 2358617505, + "RandomZ": 315568458, + "StartTime": 11500.0, + "Objects": [ + { + "StartTime": 11500.0, + "EndTime": 12000.0, + "Column": 1 + } + ] + }, + { + "RandomW": 3979422122, + "RandomX": 548134043, + "RandomY": 2810584254, + "RandomZ": 2250186050, + "StartTime": 12500.0, + "Objects": [ + { + "StartTime": 12500.0, + "EndTime": 16500.0, + "Column": 4 + } + ] + }, + { + "RandomW": 2466283411, + "RandomX": 2810584254, + "RandomY": 2250186050, + "RandomZ": 3979422122, + "StartTime": 17000.0, + "Objects": [ + { + "StartTime": 17000.0, + "EndTime": 18000.0, + "Column": 2 + } + ] + }, + { + "RandomW": 83157665, + "RandomX": 2250186050, + "RandomY": 3979422122, + "RandomZ": 2466283411, + "StartTime": 18500.0, + "Objects": [ + { + "StartTime": 18500.0, + "EndTime": 19450.0, + "Column": 0 + } + ] + }, + { + "RandomW": 2383087700, + "RandomX": 83157665, + "RandomY": 2055150192, + "RandomZ": 510071020, + "StartTime": 19875.0, + "Objects": [ + { + "StartTime": 19875.0, + "EndTime": 23875.0, + "Column": 1 + }, + { + "StartTime": 19875.0, + "EndTime": 23875.0, + "Column": 0 + } + ] + } + ] } \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json index 229760cd1c..400ce9cc1c 100644 --- a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/zero-length-slider-expected-conversion.json @@ -1,14 +1,18 @@ { - "Mappings": [{ - "RandomW": 3083084786, - "RandomX": 273326509, - "RandomY": 273553282, - "RandomZ": 2659838971, - "StartTime": 4836, - "Objects": [{ - "StartTime": 4836, - "EndTime": 4836, - "Column": 0 - }] - }] + "Mappings": [ + { + "RandomW": 3083084786, + "RandomX": 273326509, + "RandomY": 273553282, + "RandomZ": 2659838971, + "StartTime": 4836.0, + "Objects": [ + { + "StartTime": 4836.0, + "EndTime": 4836.0, + "Column": 0 + } + ] + } + ] } \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs index e04b44311e..77f93b4ef9 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/Patterns/Legacy/PatternGenerator.cs @@ -52,18 +52,14 @@ namespace osu.Game.Rulesets.Mania.Beatmaps.Patterns.Legacy /// The column. protected int GetColumn(float position, bool allowSpecial = false) { - // Casts to doubles are present here because, although code is originally written as float division, - // the division actually appears to occur on doubles in osu!stable. This is likely a result of - // differences in optimisations between .NET versions due to the presence of the double parameter type of Math.Floor(). - if (allowSpecial && TotalColumns == 8) { const float local_x_divisor = 512f / 7; - return Math.Clamp((int)Math.Floor((double)position / local_x_divisor), 0, 6) + 1; + return Math.Clamp((int)MathF.Floor(position / local_x_divisor), 0, 6) + 1; } float localXDivisor = 512f / TotalColumns; - return Math.Clamp((int)Math.Floor((double)position / localXDivisor), 0, TotalColumns - 1); + return Math.Clamp((int)MathF.Floor(position / localXDivisor), 0, TotalColumns - 1); } /// From 9a83d7be81d7f2d2115ab76283be02c0806525f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 8 Dec 2023 09:27:12 +0100 Subject: [PATCH 0435/1118] Ensure that `SoloScoreInfo` serialisation result does not contain interface members --- .../TestSoloScoreInfoJsonSerialization.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/osu.Game.Tests/Online/TestSoloScoreInfoJsonSerialization.cs b/osu.Game.Tests/Online/TestSoloScoreInfoJsonSerialization.cs index 19bc96c677..509768530f 100644 --- a/osu.Game.Tests/Online/TestSoloScoreInfoJsonSerialization.cs +++ b/osu.Game.Tests/Online/TestSoloScoreInfoJsonSerialization.cs @@ -5,6 +5,7 @@ using Newtonsoft.Json; using NUnit.Framework; using osu.Game.IO.Serialization; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Scoring; using osu.Game.Tests.Resources; namespace osu.Game.Tests.Online @@ -36,5 +37,31 @@ namespace osu.Game.Tests.Online Assert.That(serialised, Contains.Substring("large_tick_hit")); Assert.That(serialised, Contains.Substring("\"rank\":\"S\"")); } + + /// + /// Ensures that the proxy implementations of by + /// do not get serialised to JSON. + /// + [Test] + public void TestScoreSerialisationSkipsInterfaceMembers() + { + var score = SoloScoreInfo.ForSubmission(TestResources.CreateTestScoreInfo()); + + string[] variants = + { + JsonConvert.SerializeObject(score), + score.Serialize() + }; + + foreach (string serialised in variants) + { + Assert.That(serialised, Does.Not.Contain("\"online_id\":")); + Assert.That(serialised, Does.Not.Contain("\"user\":")); + Assert.That(serialised, Does.Not.Contain("\"date\":")); + Assert.That(serialised, Does.Not.Contain("\"legacy_online_id\":")); + Assert.That(serialised, Does.Not.Contain("\"beatmap\":")); + Assert.That(serialised, Does.Not.Contain("\"ruleset\":")); + } + } } } From 81fe15f2882e54d0041538c1af5b16a1748b347f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 9 Dec 2023 15:39:54 +0900 Subject: [PATCH 0436/1118] Fix osu!mania converted key count edge cases --- .../ManiaBeatmapConversionTest.cs | 1 + .../Beatmaps/1450162-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/1450162.osu | 297 ++++++++++++++++++ .../Beatmaps/ManiaBeatmapConverter.cs | 5 +- 4 files changed, 303 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/1450162-expected-conversion.json create mode 100644 osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/1450162.osu diff --git a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs index 435d5e737e..609c2e8953 100644 --- a/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Mania.Tests/ManiaBeatmapConversionTest.cs @@ -24,6 +24,7 @@ namespace osu.Game.Rulesets.Mania.Tests [TestCase("zero-length-slider")] [TestCase("20544")] [TestCase("100374")] + [TestCase("1450162")] public void Test(string name) => base.Test(name); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/1450162-expected-conversion.json b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/1450162-expected-conversion.json new file mode 100644 index 0000000000..4981951267 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/1450162-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"RandomW":2659430625,"RandomX":3579807591,"RandomY":273326509,"RandomZ":272911513,"StartTime":1107.0,"Objects":[{"StartTime":1107.0,"EndTime":1838.0,"Column":1}]},{"RandomW":4073513076,"RandomX":272911513,"RandomY":2659430625,"RandomZ":3083761897,"StartTime":2570.0,"Objects":[{"StartTime":2570.0,"EndTime":2935.0,"Column":6}]},{"RandomW":1129971314,"RandomX":3083761897,"RandomY":4073513076,"RandomZ":3235797552,"StartTime":3302.0,"Objects":[{"StartTime":3302.0,"EndTime":3667.0,"Column":3}]},{"RandomW":315510790,"RandomX":3235797552,"RandomY":1129971314,"RandomZ":2274676672,"StartTime":4033.0,"Objects":[{"StartTime":4033.0,"EndTime":4764.0,"Column":1}]},{"RandomW":2899658679,"RandomX":2274676672,"RandomY":315510790,"RandomZ":552830901,"StartTime":5497.0,"Objects":[{"StartTime":5497.0,"EndTime":5862.0,"Column":2}]},{"RandomW":3979364583,"RandomX":552830901,"RandomY":2899658679,"RandomZ":2367584034,"StartTime":6228.0,"Objects":[{"StartTime":6228.0,"EndTime":6593.0,"Column":5}]},{"RandomW":1470933435,"RandomX":2367584034,"RandomY":3979364583,"RandomZ":1363326171,"StartTime":6960.0,"Objects":[{"StartTime":6960.0,"EndTime":7142.0,"Column":4}]},{"RandomW":695558923,"RandomX":3979364583,"RandomY":1363326171,"RandomZ":1470933435,"StartTime":7326.0,"Objects":[{"StartTime":7326.0,"EndTime":7326.0,"Column":2},{"StartTime":7326.0,"EndTime":7326.0,"Column":3}]},{"RandomW":47047112,"RandomX":1470933435,"RandomY":695558923,"RandomZ":1181573554,"StartTime":7509.0,"Objects":[{"StartTime":7509.0,"EndTime":7691.0,"Column":0}]},{"RandomW":807301467,"RandomX":695558923,"RandomY":1181573554,"RandomZ":47047112,"StartTime":7875.0,"Objects":[{"StartTime":7875.0,"EndTime":7875.0,"Column":5}]},{"RandomW":2679940725,"RandomX":47047112,"RandomY":807301467,"RandomZ":3002147176,"StartTime":8058.0,"Objects":[{"StartTime":8058.0,"EndTime":8240.0,"Column":1}]},{"RandomW":176449914,"RandomX":2679940725,"RandomY":4061321195,"RandomZ":826668123,"StartTime":8424.0,"Objects":[{"StartTime":8424.0,"EndTime":8789.0,"Column":2},{"StartTime":8424.0,"EndTime":8789.0,"Column":0}]},{"RandomW":3697485076,"RandomX":347653435,"RandomY":172035291,"RandomZ":598178640,"StartTime":8972.0,"Objects":[{"StartTime":8972.0,"EndTime":9154.0,"Column":1},{"StartTime":8972.0,"EndTime":9154.0,"Column":5}]},{"RandomW":237023934,"RandomX":172035291,"RandomY":598178640,"RandomZ":3697485076,"StartTime":9338.0,"Objects":[{"StartTime":9338.0,"EndTime":9338.0,"Column":4},{"StartTime":9338.0,"EndTime":9338.0,"Column":5}]},{"RandomW":201670773,"RandomX":598178640,"RandomY":3697485076,"RandomZ":237023934,"StartTime":9521.0,"Objects":[{"StartTime":9521.0,"EndTime":9521.0,"Column":3}]},{"RandomW":3522038595,"RandomX":237023934,"RandomY":201670773,"RandomZ":341886814,"StartTime":9887.0,"Objects":[{"StartTime":9887.0,"EndTime":10069.0,"Column":4}]},{"RandomW":3662734978,"RandomX":201670773,"RandomY":341886814,"RandomZ":3522038595,"StartTime":10253.0,"Objects":[{"StartTime":10253.0,"EndTime":10253.0,"Column":3},{"StartTime":10253.0,"EndTime":10253.0,"Column":4}]},{"RandomW":4235203413,"RandomX":341886814,"RandomY":3522038595,"RandomZ":3662734978,"StartTime":10436.0,"Objects":[{"StartTime":10436.0,"EndTime":10436.0,"Column":2},{"StartTime":10436.0,"EndTime":10436.0,"Column":3}]},{"RandomW":3996672434,"RandomX":3522038595,"RandomY":3662734978,"RandomZ":4235203413,"StartTime":10619.0,"Objects":[{"StartTime":10619.0,"EndTime":10619.0,"Column":1},{"StartTime":10619.0,"EndTime":10619.0,"Column":2}]},{"RandomW":1328405285,"RandomX":3662734978,"RandomY":4235203413,"RandomZ":3996672434,"StartTime":10802.0,"Objects":[{"StartTime":10802.0,"EndTime":10802.0,"Column":0},{"StartTime":10802.0,"EndTime":10802.0,"Column":1}]},{"RandomW":303317172,"RandomX":4235203413,"RandomY":3996672434,"RandomZ":1328405285,"StartTime":10985.0,"Objects":[{"StartTime":10985.0,"EndTime":10985.0,"Column":1},{"StartTime":10985.0,"EndTime":10985.0,"Column":2}]},{"RandomW":1854018328,"RandomX":3996672434,"RandomY":1328405285,"RandomZ":303317172,"StartTime":11167.0,"Objects":[{"StartTime":11167.0,"EndTime":11167.0,"Column":2},{"StartTime":11167.0,"EndTime":11167.0,"Column":3}]},{"RandomW":1134221963,"RandomX":1328405285,"RandomY":303317172,"RandomZ":1854018328,"StartTime":12814.0,"Objects":[{"StartTime":12814.0,"EndTime":12814.0,"Column":1}]},{"RandomW":2894789541,"RandomX":1134221963,"RandomY":1649399086,"RandomZ":3538823219,"StartTime":13180.0,"Objects":[{"StartTime":13180.0,"EndTime":13362.0,"Column":4},{"StartTime":13180.0,"EndTime":13362.0,"Column":2}]},{"RandomW":2259123626,"RandomX":2894789541,"RandomY":961618493,"RandomZ":631989916,"StartTime":13546.0,"Objects":[{"StartTime":13546.0,"EndTime":13728.0,"Column":0}]},{"RandomW":3004853499,"RandomX":2259123626,"RandomY":2097932552,"RandomZ":3455806558,"StartTime":13911.0,"Objects":[{"StartTime":13911.0,"EndTime":14093.0,"Column":4},{"StartTime":13911.0,"EndTime":14093.0,"Column":2}]},{"RandomW":1511929919,"RandomX":250420511,"RandomY":747435619,"RandomZ":973338160,"StartTime":14277.0,"Objects":[{"StartTime":14277.0,"EndTime":14277.0,"Column":5},{"StartTime":14277.0,"EndTime":14277.0,"Column":6},{"StartTime":14459.0,"EndTime":14459.0,"Column":2},{"StartTime":14459.0,"EndTime":14459.0,"Column":3},{"StartTime":14641.0,"EndTime":14641.0,"Column":3},{"StartTime":14641.0,"EndTime":14641.0,"Column":4}]},{"RandomW":1997079940,"RandomX":973338160,"RandomY":1511929919,"RandomZ":1014879110,"StartTime":14826.0,"Objects":[{"StartTime":14826.0,"EndTime":15191.0,"Column":6}]},{"RandomW":735692759,"RandomX":1997079940,"RandomY":1386139427,"RandomZ":4192918159,"StartTime":15375.0,"Objects":[{"StartTime":15375.0,"EndTime":15557.0,"Column":2}]},{"RandomW":348373517,"RandomX":1386139427,"RandomY":4192918159,"RandomZ":735692759,"StartTime":15741.0,"Objects":[{"StartTime":15741.0,"EndTime":15741.0,"Column":5},{"StartTime":15741.0,"EndTime":15741.0,"Column":6}]},{"RandomW":521239132,"RandomX":735692759,"RandomY":348373517,"RandomZ":2961240161,"StartTime":16106.0,"Objects":[{"StartTime":16106.0,"EndTime":16288.0,"Column":1}]},{"RandomW":1199465075,"RandomX":521239132,"RandomY":4195606806,"RandomZ":4039804915,"StartTime":16472.0,"Objects":[{"StartTime":16472.0,"EndTime":16654.0,"Column":6},{"StartTime":16472.0,"EndTime":16654.0,"Column":3}]},{"RandomW":3059180408,"RandomX":4039804915,"RandomY":1199465075,"RandomZ":3542692698,"StartTime":16838.0,"Objects":[{"StartTime":16838.0,"EndTime":17020.0,"Column":2}]},{"RandomW":834119344,"RandomX":302423902,"RandomY":2799635095,"RandomZ":1022775029,"StartTime":17204.0,"Objects":[{"StartTime":17204.0,"EndTime":17204.0,"Column":4},{"StartTime":17204.0,"EndTime":17204.0,"Column":5},{"StartTime":17386.0,"EndTime":17386.0,"Column":2},{"StartTime":17386.0,"EndTime":17386.0,"Column":3},{"StartTime":17568.0,"EndTime":17568.0,"Column":3},{"StartTime":17568.0,"EndTime":17568.0,"Column":4}]},{"RandomW":1236797567,"RandomX":1022775029,"RandomY":834119344,"RandomZ":393032631,"StartTime":17753.0,"Objects":[{"StartTime":17753.0,"EndTime":18118.0,"Column":4}]},{"RandomW":892840048,"RandomX":1236797567,"RandomY":3350685275,"RandomZ":1270471227,"StartTime":18302.0,"Objects":[{"StartTime":18302.0,"EndTime":18484.0,"Column":2}]},{"RandomW":3233581364,"RandomX":1270471227,"RandomY":892840048,"RandomZ":3158680921,"StartTime":18667.0,"Objects":[{"StartTime":18667.0,"EndTime":19032.0,"Column":3}]},{"RandomW":1163000602,"RandomX":892840048,"RandomY":3158680921,"RandomZ":3233581364,"StartTime":19216.0,"Objects":[{"StartTime":19216.0,"EndTime":19216.0,"Column":2}]},{"RandomW":1548989545,"RandomX":3233581364,"RandomY":1163000602,"RandomZ":3450712040,"StartTime":19399.0,"Objects":[{"StartTime":19399.0,"EndTime":19581.0,"Column":5}]},{"RandomW":313779584,"RandomX":1548989545,"RandomY":2021811198,"RandomZ":2999045855,"StartTime":19765.0,"Objects":[{"StartTime":19765.0,"EndTime":19947.0,"Column":2},{"StartTime":19765.0,"EndTime":19947.0,"Column":1}]},{"RandomW":3548572483,"RandomX":2021811198,"RandomY":2999045855,"RandomZ":313779584,"StartTime":20131.0,"Objects":[{"StartTime":20131.0,"EndTime":20131.0,"Column":6}]},{"RandomW":75459001,"RandomX":313779584,"RandomY":3548572483,"RandomZ":3094675294,"StartTime":20314.0,"Objects":[{"StartTime":20314.0,"EndTime":20496.0,"Column":0}]},{"RandomW":1299261902,"RandomX":3094675294,"RandomY":75459001,"RandomZ":2305626963,"StartTime":20680.0,"Objects":[{"StartTime":20680.0,"EndTime":21045.0,"Column":4}]},{"RandomW":2905421941,"RandomX":2305626963,"RandomY":1299261902,"RandomZ":1390453041,"StartTime":21228.0,"Objects":[{"StartTime":21228.0,"EndTime":21410.0,"Column":2}]},{"RandomW":2294300184,"RandomX":1390453041,"RandomY":2905421941,"RandomZ":1278955784,"StartTime":21594.0,"Objects":[{"StartTime":21594.0,"EndTime":22325.0,"Column":6},{"StartTime":21594.0,"EndTime":21594.0,"Column":4},{"StartTime":21959.0,"EndTime":21959.0,"Column":4},{"StartTime":22324.0,"EndTime":22324.0,"Column":4}]},{"RandomW":3749637912,"RandomX":2905421941,"RandomY":1278955784,"RandomZ":2294300184,"StartTime":22509.0,"Objects":[{"StartTime":22509.0,"EndTime":22509.0,"Column":6},{"StartTime":22509.0,"EndTime":22509.0,"Column":0}]},{"RandomW":753327495,"RandomX":458525202,"RandomY":2373004129,"RandomZ":80278569,"StartTime":22692.0,"Objects":[{"StartTime":22692.0,"EndTime":22874.0,"Column":2}]},{"RandomW":3562609217,"RandomX":753327495,"RandomY":2472396307,"RandomZ":2540952890,"StartTime":23058.0,"Objects":[{"StartTime":23058.0,"EndTime":23058.0,"Column":1},{"StartTime":23058.0,"EndTime":23058.0,"Column":5}]},{"RandomW":3562609217,"RandomX":753327495,"RandomY":2472396307,"RandomZ":2540952890,"StartTime":23241.0,"Objects":[{"StartTime":23241.0,"EndTime":23241.0,"Column":5},{"StartTime":23241.0,"EndTime":23241.0,"Column":1}]},{"RandomW":3009004844,"RandomX":2540952890,"RandomY":3562609217,"RandomZ":3460951976,"StartTime":23606.0,"Objects":[{"StartTime":23606.0,"EndTime":23971.0,"Column":2}]},{"RandomW":1524995266,"RandomX":3133817968,"RandomY":2791164538,"RandomZ":669533622,"StartTime":24155.0,"Objects":[{"StartTime":24155.0,"EndTime":24337.0,"Column":4}]},{"RandomW":2667749121,"RandomX":1524995266,"RandomY":3001332266,"RandomZ":4204965910,"StartTime":24521.0,"Objects":[{"StartTime":24521.0,"EndTime":24521.0,"Column":0},{"StartTime":24521.0,"EndTime":24521.0,"Column":6}]},{"RandomW":1230014889,"RandomX":2127937865,"RandomY":2434329733,"RandomZ":443126576,"StartTime":24704.0,"Objects":[{"StartTime":24704.0,"EndTime":25069.0,"Column":1},{"StartTime":24704.0,"EndTime":25069.0,"Column":4}]},{"RandomW":1409501366,"RandomX":2573194819,"RandomY":3480583465,"RandomZ":2580776932,"StartTime":25253.0,"Objects":[{"StartTime":25253.0,"EndTime":25253.0,"Column":0},{"StartTime":25253.0,"EndTime":25253.0,"Column":1},{"StartTime":25435.0,"EndTime":25435.0,"Column":4},{"StartTime":25435.0,"EndTime":25435.0,"Column":5},{"StartTime":25617.0,"EndTime":25617.0,"Column":1},{"StartTime":25617.0,"EndTime":25617.0,"Column":2}]},{"RandomW":864641467,"RandomX":3480583465,"RandomY":2580776932,"RandomZ":1409501366,"StartTime":25802.0,"Objects":[{"StartTime":25802.0,"EndTime":25802.0,"Column":1}]},{"RandomW":1467076310,"RandomX":2580776932,"RandomY":1409501366,"RandomZ":864641467,"StartTime":25985.0,"Objects":[{"StartTime":25985.0,"EndTime":25985.0,"Column":2}]},{"RandomW":479214438,"RandomX":864641467,"RandomY":1467076310,"RandomZ":1385729915,"StartTime":26167.0,"Objects":[{"StartTime":26167.0,"EndTime":26532.0,"Column":1}]},{"RandomW":3054605916,"RandomX":1684801014,"RandomY":3182588115,"RandomZ":734516041,"StartTime":26716.0,"Objects":[{"StartTime":26716.0,"EndTime":26716.0,"Column":4},{"StartTime":26716.0,"EndTime":26716.0,"Column":2},{"StartTime":26898.0,"EndTime":26898.0,"Column":3},{"StartTime":26898.0,"EndTime":26898.0,"Column":1},{"StartTime":27080.0,"EndTime":27080.0,"Column":2},{"StartTime":27080.0,"EndTime":27080.0,"Column":6}]},{"RandomW":2992010973,"RandomX":3182588115,"RandomY":734516041,"RandomZ":3054605916,"StartTime":27265.0,"Objects":[{"StartTime":27265.0,"EndTime":27265.0,"Column":2}]},{"RandomW":2622274732,"RandomX":734516041,"RandomY":3054605916,"RandomZ":2992010973,"StartTime":27448.0,"Objects":[{"StartTime":27448.0,"EndTime":27448.0,"Column":3}]},{"RandomW":3013455357,"RandomX":2992010973,"RandomY":2622274732,"RandomZ":2298767863,"StartTime":27631.0,"Objects":[{"StartTime":27631.0,"EndTime":27996.0,"Column":2}]},{"RandomW":2994521549,"RandomX":2622274732,"RandomY":2298767863,"RandomZ":3013455357,"StartTime":28180.0,"Objects":[{"StartTime":28180.0,"EndTime":28180.0,"Column":5}]},{"RandomW":3949426364,"RandomX":1261217522,"RandomY":3788322225,"RandomZ":3210845744,"StartTime":28363.0,"Objects":[{"StartTime":28363.0,"EndTime":28363.0,"Column":5},{"StartTime":28363.0,"EndTime":28363.0,"Column":2},{"StartTime":28545.0,"EndTime":28545.0,"Column":5},{"StartTime":28545.0,"EndTime":28545.0,"Column":2},{"StartTime":28727.0,"EndTime":28727.0,"Column":3},{"StartTime":28727.0,"EndTime":28727.0,"Column":6}]},{"RandomW":1304069042,"RandomX":3210845744,"RandomY":3949426364,"RandomZ":3310503444,"StartTime":28911.0,"Objects":[{"StartTime":28911.0,"EndTime":29276.0,"Column":4}]},{"RandomW":781934546,"RandomX":3310503444,"RandomY":1304069042,"RandomZ":4271440939,"StartTime":29460.0,"Objects":[{"StartTime":29460.0,"EndTime":29460.0,"Column":6},{"StartTime":29460.0,"EndTime":29460.0,"Column":2}]},{"RandomW":3592330498,"RandomX":781934546,"RandomY":2041503475,"RandomZ":3767559527,"StartTime":29643.0,"Objects":[{"StartTime":29643.0,"EndTime":30008.0,"Column":5},{"StartTime":29643.0,"EndTime":30008.0,"Column":4}]},{"RandomW":579808732,"RandomX":2041503475,"RandomY":3767559527,"RandomZ":3592330498,"StartTime":30192.0,"Objects":[{"StartTime":30192.0,"EndTime":30192.0,"Column":4}]},{"RandomW":769209912,"RandomX":3767559527,"RandomY":3592330498,"RandomZ":579808732,"StartTime":30375.0,"Objects":[{"StartTime":30375.0,"EndTime":30375.0,"Column":4}]},{"RandomW":1825941494,"RandomX":579808732,"RandomY":769209912,"RandomZ":1308743097,"StartTime":30558.0,"Objects":[{"StartTime":30558.0,"EndTime":30923.0,"Column":5}]},{"RandomW":1378114054,"RandomX":930055805,"RandomY":3554877022,"RandomZ":2467280262,"StartTime":31106.0,"Objects":[{"StartTime":31106.0,"EndTime":31106.0,"Column":2},{"StartTime":31106.0,"EndTime":31106.0,"Column":5},{"StartTime":31288.0,"EndTime":31288.0,"Column":4},{"StartTime":31288.0,"EndTime":31288.0,"Column":1},{"StartTime":31470.0,"EndTime":31470.0,"Column":1},{"StartTime":31470.0,"EndTime":31470.0,"Column":4}]},{"RandomW":422797905,"RandomX":3554877022,"RandomY":2467280262,"RandomZ":1378114054,"StartTime":31655.0,"Objects":[{"StartTime":31655.0,"EndTime":31655.0,"Column":1}]},{"RandomW":3538525895,"RandomX":2467280262,"RandomY":1378114054,"RandomZ":422797905,"StartTime":31838.0,"Objects":[{"StartTime":31838.0,"EndTime":31838.0,"Column":0}]},{"RandomW":1277180769,"RandomX":422797905,"RandomY":3538525895,"RandomZ":1017422489,"StartTime":32021.0,"Objects":[{"StartTime":32021.0,"EndTime":32386.0,"Column":4}]},{"RandomW":69027963,"RandomX":3464755550,"RandomY":1342331375,"RandomZ":1235978524,"StartTime":32570.0,"Objects":[{"StartTime":32570.0,"EndTime":32752.0,"Column":0}]},{"RandomW":3582265519,"RandomX":1342331375,"RandomY":1235978524,"RandomZ":69027963,"StartTime":32936.0,"Objects":[{"StartTime":32936.0,"EndTime":32936.0,"Column":2},{"StartTime":32936.0,"EndTime":32936.0,"Column":3}]},{"RandomW":2197579333,"RandomX":69027963,"RandomY":3582265519,"RandomZ":2534080209,"StartTime":33302.0,"Objects":[{"StartTime":33302.0,"EndTime":33667.0,"Column":0}]},{"RandomW":820123404,"RandomX":1816967409,"RandomY":2440103335,"RandomZ":1364041006,"StartTime":33850.0,"Objects":[{"StartTime":33850.0,"EndTime":34215.0,"Column":4},{"StartTime":33850.0,"EndTime":34215.0,"Column":2}]},{"RandomW":962636497,"RandomX":2440103335,"RandomY":1364041006,"RandomZ":820123404,"StartTime":34399.0,"Objects":[{"StartTime":34399.0,"EndTime":34399.0,"Column":3},{"StartTime":34399.0,"EndTime":34399.0,"Column":4}]},{"RandomW":539348071,"RandomX":1364041006,"RandomY":820123404,"RandomZ":962636497,"StartTime":34582.0,"Objects":[{"StartTime":34582.0,"EndTime":34582.0,"Column":4}]},{"RandomW":1036431212,"RandomX":962636497,"RandomY":539348071,"RandomZ":498893216,"StartTime":34765.0,"Objects":[{"StartTime":34765.0,"EndTime":34947.0,"Column":3}]},{"RandomW":30194727,"RandomX":539348071,"RandomY":498893216,"RandomZ":1036431212,"StartTime":35131.0,"Objects":[{"StartTime":35131.0,"EndTime":35131.0,"Column":4},{"StartTime":35131.0,"EndTime":35131.0,"Column":5}]},{"RandomW":4140580700,"RandomX":1036431212,"RandomY":30194727,"RandomZ":260312717,"StartTime":35314.0,"Objects":[{"StartTime":35314.0,"EndTime":35496.0,"Column":6}]},{"RandomW":4269364006,"RandomX":30194727,"RandomY":260312717,"RandomZ":4140580700,"StartTime":35680.0,"Objects":[{"StartTime":35680.0,"EndTime":35680.0,"Column":5},{"StartTime":35680.0,"EndTime":35680.0,"Column":6}]},{"RandomW":3052364007,"RandomX":4140580700,"RandomY":4269364006,"RandomZ":2586895690,"StartTime":35863.0,"Objects":[{"StartTime":35863.0,"EndTime":36045.0,"Column":2}]},{"RandomW":575578073,"RandomX":4269364006,"RandomY":2586895690,"RandomZ":3052364007,"StartTime":36228.0,"Objects":[{"StartTime":36228.0,"EndTime":36228.0,"Column":4}]},{"RandomW":379197653,"RandomX":2586895690,"RandomY":3052364007,"RandomZ":575578073,"StartTime":36411.0,"Objects":[{"StartTime":36411.0,"EndTime":36411.0,"Column":3}]},{"RandomW":2472409868,"RandomX":379197653,"RandomY":194885113,"RandomZ":3317367861,"StartTime":36594.0,"Objects":[{"StartTime":36594.0,"EndTime":36776.0,"Column":1}]},{"RandomW":3530386304,"RandomX":1439106306,"RandomY":3004383294,"RandomZ":2928959685,"StartTime":36960.0,"Objects":[{"StartTime":36960.0,"EndTime":36960.0,"Column":1},{"StartTime":36960.0,"EndTime":36960.0,"Column":5},{"StartTime":37142.0,"EndTime":37142.0,"Column":2},{"StartTime":37142.0,"EndTime":37142.0,"Column":6},{"StartTime":37324.0,"EndTime":37324.0,"Column":2},{"StartTime":37324.0,"EndTime":37324.0,"Column":6}]},{"RandomW":3220147162,"RandomX":3004383294,"RandomY":2928959685,"RandomZ":3530386304,"StartTime":37509.0,"Objects":[{"StartTime":37509.0,"EndTime":37509.0,"Column":2}]},{"RandomW":2530492073,"RandomX":2928959685,"RandomY":3530386304,"RandomZ":3220147162,"StartTime":37692.0,"Objects":[{"StartTime":37692.0,"EndTime":37692.0,"Column":1}]},{"RandomW":2605446910,"RandomX":3530386304,"RandomY":3220147162,"RandomZ":2530492073,"StartTime":37875.0,"Objects":[{"StartTime":37875.0,"EndTime":37875.0,"Column":2}]},{"RandomW":3786494373,"RandomX":2530492073,"RandomY":2605446910,"RandomZ":583253884,"StartTime":38058.0,"Objects":[{"StartTime":38058.0,"EndTime":38240.0,"Column":5}]},{"RandomW":1188028287,"RandomX":3601275468,"RandomY":312474208,"RandomZ":764976912,"StartTime":38424.0,"Objects":[{"StartTime":38424.0,"EndTime":38424.0,"Column":4},{"StartTime":38424.0,"EndTime":38424.0,"Column":2},{"StartTime":38606.0,"EndTime":38606.0,"Column":1},{"StartTime":38606.0,"EndTime":38606.0,"Column":5},{"StartTime":38788.0,"EndTime":38788.0,"Column":2},{"StartTime":38788.0,"EndTime":38788.0,"Column":6}]},{"RandomW":2824132752,"RandomX":312474208,"RandomY":764976912,"RandomZ":1188028287,"StartTime":38972.0,"Objects":[{"StartTime":38972.0,"EndTime":38972.0,"Column":3}]},{"RandomW":1173715712,"RandomX":764976912,"RandomY":1188028287,"RandomZ":2824132752,"StartTime":39155.0,"Objects":[{"StartTime":39155.0,"EndTime":39155.0,"Column":4}]},{"RandomW":2490370662,"RandomX":2824132752,"RandomY":1173715712,"RandomZ":2893810865,"StartTime":39338.0,"Objects":[{"StartTime":39338.0,"EndTime":39520.0,"Column":1}]},{"RandomW":1949144326,"RandomX":2893810865,"RandomY":2490370662,"RandomZ":2599342112,"StartTime":39704.0,"Objects":[{"StartTime":39704.0,"EndTime":40069.0,"Column":6}]},{"RandomW":743381221,"RandomX":2599342112,"RandomY":1949144326,"RandomZ":947390134,"StartTime":40253.0,"Objects":[{"StartTime":40253.0,"EndTime":40435.0,"Column":2}]},{"RandomW":3629226534,"RandomX":947390134,"RandomY":743381221,"RandomZ":3234636444,"StartTime":40619.0,"Objects":[{"StartTime":40619.0,"EndTime":40984.0,"Column":4}]},{"RandomW":551844396,"RandomX":743381221,"RandomY":3234636444,"RandomZ":3629226534,"StartTime":41167.0,"Objects":[{"StartTime":41167.0,"EndTime":41167.0,"Column":3}]},{"RandomW":2240897560,"RandomX":551844396,"RandomY":1949877989,"RandomZ":3510981308,"StartTime":41350.0,"Objects":[{"StartTime":41350.0,"EndTime":41532.0,"Column":4},{"StartTime":41350.0,"EndTime":41532.0,"Column":0}]},{"RandomW":874163267,"RandomX":3510981308,"RandomY":2240897560,"RandomZ":2259115420,"StartTime":41716.0,"Objects":[{"StartTime":41716.0,"EndTime":41898.0,"Column":2}]},{"RandomW":3476146382,"RandomX":2240897560,"RandomY":2259115420,"RandomZ":874163267,"StartTime":42082.0,"Objects":[{"StartTime":42082.0,"EndTime":42082.0,"Column":4}]},{"RandomW":2101943428,"RandomX":874163267,"RandomY":3476146382,"RandomZ":3250516626,"StartTime":42265.0,"Objects":[{"StartTime":42265.0,"EndTime":42447.0,"Column":6}]},{"RandomW":2630934490,"RandomX":3476146382,"RandomY":3250516626,"RandomZ":2101943428,"StartTime":42631.0,"Objects":[{"StartTime":42631.0,"EndTime":42631.0,"Column":4}]},{"RandomW":3294029476,"RandomX":3722838838,"RandomY":3959050362,"RandomZ":3731020989,"StartTime":42814.0,"Objects":[{"StartTime":42814.0,"EndTime":42814.0,"Column":6},{"StartTime":42814.0,"EndTime":42814.0,"Column":4},{"StartTime":42996.0,"EndTime":42996.0,"Column":5},{"StartTime":42996.0,"EndTime":42996.0,"Column":3},{"StartTime":43178.0,"EndTime":43178.0,"Column":5},{"StartTime":43178.0,"EndTime":43178.0,"Column":3}]},{"RandomW":692368043,"RandomX":3959050362,"RandomY":3731020989,"RandomZ":3294029476,"StartTime":43363.0,"Objects":[{"StartTime":43363.0,"EndTime":43363.0,"Column":5}]},{"RandomW":268717689,"RandomX":3731020989,"RandomY":3294029476,"RandomZ":692368043,"StartTime":43546.0,"Objects":[{"StartTime":43546.0,"EndTime":43546.0,"Column":4}]},{"RandomW":3628859376,"RandomX":3294029476,"RandomY":692368043,"RandomZ":268717689,"StartTime":43728.0,"Objects":[{"StartTime":43728.0,"EndTime":43728.0,"Column":3}]},{"RandomW":2810605489,"RandomX":268717689,"RandomY":3628859376,"RandomZ":2874884507,"StartTime":43911.0,"Objects":[{"StartTime":43911.0,"EndTime":44093.0,"Column":2}]},{"RandomW":317739913,"RandomX":2874884507,"RandomY":2810605489,"RandomZ":2512620222,"StartTime":44277.0,"Objects":[{"StartTime":44277.0,"EndTime":44459.0,"Column":1}]},{"RandomW":967116709,"RandomX":4156133369,"RandomY":2124840394,"RandomZ":3998877068,"StartTime":44643.0,"Objects":[{"StartTime":44643.0,"EndTime":44825.0,"Column":6},{"StartTime":44643.0,"EndTime":44825.0,"Column":3}]},{"RandomW":1331553411,"RandomX":3998877068,"RandomY":967116709,"RandomZ":39354671,"StartTime":45009.0,"Objects":[{"StartTime":45009.0,"EndTime":45374.0,"Column":4}]},{"RandomW":2785797100,"RandomX":1331553411,"RandomY":1897266817,"RandomZ":1620854569,"StartTime":45558.0,"Objects":[{"StartTime":45558.0,"EndTime":45923.0,"Column":5},{"StartTime":45558.0,"EndTime":45923.0,"Column":2}]},{"RandomW":114455122,"RandomX":1897266817,"RandomY":1620854569,"RandomZ":2785797100,"StartTime":46106.0,"Objects":[{"StartTime":46106.0,"EndTime":46106.0,"Column":3},{"StartTime":46106.0,"EndTime":46106.0,"Column":4}]},{"RandomW":3639436799,"RandomX":1620854569,"RandomY":2785797100,"RandomZ":114455122,"StartTime":46289.0,"Objects":[{"StartTime":46289.0,"EndTime":46289.0,"Column":4}]},{"RandomW":2239997850,"RandomX":1523242180,"RandomY":2737260786,"RandomZ":921894438,"StartTime":46472.0,"Objects":[{"StartTime":46472.0,"EndTime":46472.0,"Column":5},{"StartTime":46472.0,"EndTime":46472.0,"Column":3},{"StartTime":46654.0,"EndTime":46654.0,"Column":1},{"StartTime":46654.0,"EndTime":46654.0,"Column":5},{"StartTime":46836.0,"EndTime":46836.0,"Column":3},{"StartTime":46836.0,"EndTime":46836.0,"Column":1}]},{"RandomW":270173708,"RandomX":921894438,"RandomY":2239997850,"RandomZ":2313367322,"StartTime":47021.0,"Objects":[{"StartTime":47021.0,"EndTime":47386.0,"Column":0}]},{"RandomW":2981644775,"RandomX":2239997850,"RandomY":2313367322,"RandomZ":270173708,"StartTime":47570.0,"Objects":[{"StartTime":47570.0,"EndTime":47570.0,"Column":5}]},{"RandomW":698324797,"RandomX":2313367322,"RandomY":270173708,"RandomZ":2981644775,"StartTime":47936.0,"Objects":[{"StartTime":47936.0,"EndTime":47936.0,"Column":2}]},{"RandomW":2105158963,"RandomX":2981644775,"RandomY":698324797,"RandomZ":3113547499,"StartTime":48119.0,"Objects":[{"StartTime":48119.0,"EndTime":48667.0,"Column":6}]},{"RandomW":3675126935,"RandomX":3113547499,"RandomY":2105158963,"RandomZ":251569162,"StartTime":48850.0,"Objects":[{"StartTime":48850.0,"EndTime":49398.0,"Column":4}]},{"RandomW":1771033747,"RandomX":251569162,"RandomY":3675126935,"RandomZ":3308284595,"StartTime":49582.0,"Objects":[{"StartTime":49582.0,"EndTime":50130.0,"Column":5}]},{"RandomW":653741274,"RandomX":3308284595,"RandomY":1771033747,"RandomZ":2460676956,"StartTime":50314.0,"Objects":[{"StartTime":50314.0,"EndTime":50862.0,"Column":2}]},{"RandomW":3908591175,"RandomX":2011739264,"RandomY":2988284210,"RandomZ":772833847,"StartTime":51046.0,"Objects":[{"StartTime":51046.0,"EndTime":51594.0,"Column":6},{"StartTime":51046.0,"EndTime":51594.0,"Column":5}]},{"RandomW":782718603,"RandomX":3908591175,"RandomY":3666262892,"RandomZ":2215410951,"StartTime":51777.0,"Objects":[{"StartTime":51777.0,"EndTime":51959.0,"Column":0},{"StartTime":51777.0,"EndTime":51959.0,"Column":2}]},{"RandomW":3946166617,"RandomX":2215410951,"RandomY":782718603,"RandomZ":75972478,"StartTime":52143.0,"Objects":[{"StartTime":52143.0,"EndTime":52508.0,"Column":5}]},{"RandomW":204866941,"RandomX":782718603,"RandomY":75972478,"RandomZ":3946166617,"StartTime":52692.0,"Objects":[{"StartTime":52692.0,"EndTime":52692.0,"Column":3},{"StartTime":52692.0,"EndTime":52692.0,"Column":4}]},{"RandomW":628140489,"RandomX":3946166617,"RandomY":204866941,"RandomZ":405870974,"StartTime":52875.0,"Objects":[{"StartTime":52875.0,"EndTime":53240.0,"Column":2}]},{"RandomW":1325586396,"RandomX":628140489,"RandomY":1674126159,"RandomZ":3748192166,"StartTime":53424.0,"Objects":[{"StartTime":53424.0,"EndTime":53606.0,"Column":5},{"StartTime":53424.0,"EndTime":53606.0,"Column":4}]},{"RandomW":3311768819,"RandomX":3748192166,"RandomY":1325586396,"RandomZ":4019978516,"StartTime":53789.0,"Objects":[{"StartTime":53789.0,"EndTime":53789.0,"Column":4},{"StartTime":53789.0,"EndTime":53789.0,"Column":3}]},{"RandomW":1550448150,"RandomX":1325586396,"RandomY":4019978516,"RandomZ":3311768819,"StartTime":53972.0,"Objects":[{"StartTime":53972.0,"EndTime":53972.0,"Column":5}]},{"RandomW":169296756,"RandomX":3311768819,"RandomY":1550448150,"RandomZ":93091440,"StartTime":54155.0,"Objects":[{"StartTime":54155.0,"EndTime":54337.0,"Column":0}]},{"RandomW":2528106598,"RandomX":169296756,"RandomY":3812396233,"RandomZ":4042657790,"StartTime":54521.0,"Objects":[{"StartTime":54521.0,"EndTime":54703.0,"Column":6},{"StartTime":54521.0,"EndTime":54703.0,"Column":1}]},{"RandomW":1636289987,"RandomX":2528106598,"RandomY":638788900,"RandomZ":558809067,"StartTime":54887.0,"Objects":[{"StartTime":54887.0,"EndTime":55069.0,"Column":5}]},{"RandomW":914779004,"RandomX":558809067,"RandomY":1636289987,"RandomZ":2298692989,"StartTime":55253.0,"Objects":[{"StartTime":55253.0,"EndTime":55618.0,"Column":2}]},{"RandomW":1650670496,"RandomX":1636289987,"RandomY":2298692989,"RandomZ":914779004,"StartTime":55802.0,"Objects":[{"StartTime":55802.0,"EndTime":55802.0,"Column":2}]},{"RandomW":3497220679,"RandomX":1037372410,"RandomY":2926479760,"RandomZ":2880883370,"StartTime":55985.0,"Objects":[{"StartTime":55985.0,"EndTime":56350.0,"Column":4}]},{"RandomW":1164710248,"RandomX":2926479760,"RandomY":2880883370,"RandomZ":3497220679,"StartTime":56533.0,"Objects":[{"StartTime":56533.0,"EndTime":56533.0,"Column":5}]},{"RandomW":2188007582,"RandomX":3497220679,"RandomY":1164710248,"RandomZ":2677289564,"StartTime":56716.0,"Objects":[{"StartTime":56716.0,"EndTime":57081.0,"Column":0}]},{"RandomW":3363933174,"RandomX":1164710248,"RandomY":2677289564,"RandomZ":2188007582,"StartTime":57265.0,"Objects":[{"StartTime":57265.0,"EndTime":57265.0,"Column":3}]},{"RandomW":50721184,"RandomX":3363933174,"RandomY":3980600543,"RandomZ":3548114425,"StartTime":57448.0,"Objects":[{"StartTime":57448.0,"EndTime":57630.0,"Column":4},{"StartTime":57448.0,"EndTime":57630.0,"Column":0}]},{"RandomW":864990701,"RandomX":3548114425,"RandomY":50721184,"RandomZ":3340702733,"StartTime":57814.0,"Objects":[{"StartTime":57814.0,"EndTime":57996.0,"Column":2}]},{"RandomW":322108643,"RandomX":3340702733,"RandomY":864990701,"RandomZ":1066828352,"StartTime":58180.0,"Objects":[{"StartTime":58180.0,"EndTime":58362.0,"Column":1}]},{"RandomW":1792394322,"RandomX":1066828352,"RandomY":322108643,"RandomZ":749878772,"StartTime":58546.0,"Objects":[{"StartTime":58546.0,"EndTime":58728.0,"Column":5}]},{"RandomW":475567653,"RandomX":3789213642,"RandomY":1703666422,"RandomZ":3630902830,"StartTime":58911.0,"Objects":[{"StartTime":58911.0,"EndTime":59093.0,"Column":4},{"StartTime":58911.0,"EndTime":59093.0,"Column":1}]},{"RandomW":292381990,"RandomX":3630902830,"RandomY":475567653,"RandomZ":734768891,"StartTime":59277.0,"Objects":[{"StartTime":59277.0,"EndTime":59459.0,"Column":0}]},{"RandomW":1221027582,"RandomX":734768891,"RandomY":292381990,"RandomZ":2432050043,"StartTime":59643.0,"Objects":[{"StartTime":59643.0,"EndTime":60008.0,"Column":3}]},{"RandomW":1041081707,"RandomX":292381990,"RandomY":2432050043,"RandomZ":1221027582,"StartTime":60192.0,"Objects":[{"StartTime":60192.0,"EndTime":60192.0,"Column":0}]},{"RandomW":1144239065,"RandomX":2432050043,"RandomY":1221027582,"RandomZ":1041081707,"StartTime":60375.0,"Objects":[{"StartTime":60375.0,"EndTime":60375.0,"Column":1}]},{"RandomW":1711255007,"RandomX":1221027582,"RandomY":1041081707,"RandomZ":1144239065,"StartTime":60558.0,"Objects":[{"StartTime":60558.0,"EndTime":60558.0,"Column":2},{"StartTime":60558.0,"EndTime":60558.0,"Column":3}]},{"RandomW":377276168,"RandomX":1041081707,"RandomY":1144239065,"RandomZ":1711255007,"StartTime":60649.0,"Objects":[{"StartTime":60649.0,"EndTime":60649.0,"Column":1}]},{"RandomW":377276168,"RandomX":1041081707,"RandomY":1144239065,"RandomZ":1711255007,"StartTime":60741.0,"Objects":[{"StartTime":60741.0,"EndTime":60741.0,"Column":2}]},{"RandomW":1158225489,"RandomX":1144239065,"RandomY":1711255007,"RandomZ":377276168,"StartTime":60924.0,"Objects":[{"StartTime":60924.0,"EndTime":60924.0,"Column":3}]},{"RandomW":74717015,"RandomX":377276168,"RandomY":1158225489,"RandomZ":2625486930,"StartTime":61106.0,"Objects":[{"StartTime":61106.0,"EndTime":61288.0,"Column":0}]},{"RandomW":4106277974,"RandomX":1158225489,"RandomY":2625486930,"RandomZ":74717015,"StartTime":61472.0,"Objects":[{"StartTime":61472.0,"EndTime":61472.0,"Column":2},{"StartTime":61472.0,"EndTime":61472.0,"Column":3}]},{"RandomW":3720471658,"RandomX":4181108489,"RandomY":2335938349,"RandomZ":793896882,"StartTime":61655.0,"Objects":[{"StartTime":61655.0,"EndTime":61655.0,"Column":6},{"StartTime":61746.0,"EndTime":61746.0,"Column":0},{"StartTime":61837.0,"EndTime":61837.0,"Column":2}]},{"RandomW":3031050452,"RandomX":2441289268,"RandomY":3327554006,"RandomZ":1721397977,"StartTime":62021.0,"Objects":[{"StartTime":62021.0,"EndTime":62021.0,"Column":0},{"StartTime":62112.0,"EndTime":62112.0,"Column":3},{"StartTime":62203.0,"EndTime":62203.0,"Column":5}]},{"RandomW":1028780747,"RandomX":3327554006,"RandomY":1721397977,"RandomZ":3031050452,"StartTime":62387.0,"Objects":[{"StartTime":62387.0,"EndTime":62387.0,"Column":1}]},{"RandomW":4249178890,"RandomX":3031050452,"RandomY":1028780747,"RandomZ":1224535158,"StartTime":62570.0,"Objects":[{"StartTime":62570.0,"EndTime":62935.0,"Column":6}]},{"RandomW":407644414,"RandomX":1028780747,"RandomY":1224535158,"RandomZ":4249178890,"StartTime":63119.0,"Objects":[{"StartTime":63119.0,"EndTime":63119.0,"Column":4}]},{"RandomW":84513019,"RandomX":4249178890,"RandomY":407644414,"RandomZ":2855880342,"StartTime":63302.0,"Objects":[{"StartTime":63302.0,"EndTime":63667.0,"Column":0}]},{"RandomW":2876344117,"RandomX":2855880342,"RandomY":84513019,"RandomZ":3523432019,"StartTime":63850.0,"Objects":[{"StartTime":63850.0,"EndTime":63850.0,"Column":5},{"StartTime":63850.0,"EndTime":63850.0,"Column":2}]},{"RandomW":1247936821,"RandomX":2876344117,"RandomY":3407636795,"RandomZ":2195437291,"StartTime":64033.0,"Objects":[{"StartTime":64033.0,"EndTime":64033.0,"Column":1},{"StartTime":64033.0,"EndTime":64033.0,"Column":5}]},{"RandomW":1165002312,"RandomX":2195437291,"RandomY":1247936821,"RandomZ":1829597027,"StartTime":64216.0,"Objects":[{"StartTime":64216.0,"EndTime":64216.0,"Column":6},{"StartTime":64216.0,"EndTime":64216.0,"Column":3}]},{"RandomW":440601827,"RandomX":1247936821,"RandomY":1829597027,"RandomZ":1165002312,"StartTime":64399.0,"Objects":[{"StartTime":64399.0,"EndTime":64399.0,"Column":6},{"StartTime":64399.0,"EndTime":64399.0,"Column":0}]},{"RandomW":1174586413,"RandomX":1165002312,"RandomY":440601827,"RandomZ":1081265463,"StartTime":64582.0,"Objects":[{"StartTime":64582.0,"EndTime":64947.0,"Column":3}]},{"RandomW":1399461522,"RandomX":1174586413,"RandomY":2273396835,"RandomZ":2242340964,"StartTime":65131.0,"Objects":[{"StartTime":65131.0,"EndTime":65313.0,"Column":0},{"StartTime":65131.0,"EndTime":65313.0,"Column":4}]},{"RandomW":806141128,"RandomX":1399461522,"RandomY":52007806,"RandomZ":2388001070,"StartTime":65497.0,"Objects":[{"StartTime":65497.0,"EndTime":65862.0,"Column":2}]},{"RandomW":869393117,"RandomX":52007806,"RandomY":2388001070,"RandomZ":806141128,"StartTime":66046.0,"Objects":[{"StartTime":66046.0,"EndTime":66046.0,"Column":2}]},{"RandomW":2114055664,"RandomX":932480042,"RandomY":484530218,"RandomZ":2599754617,"StartTime":66228.0,"Objects":[{"StartTime":66228.0,"EndTime":66593.0,"Column":3},{"StartTime":66228.0,"EndTime":66593.0,"Column":1},{"StartTime":66228.0,"EndTime":66593.0,"Column":6}]},{"RandomW":4212241992,"RandomX":2599754617,"RandomY":2114055664,"RandomZ":3978789838,"StartTime":66777.0,"Objects":[{"StartTime":66777.0,"EndTime":66777.0,"Column":0},{"StartTime":66777.0,"EndTime":66777.0,"Column":6}]},{"RandomW":1778029315,"RandomX":4212241992,"RandomY":3373094016,"RandomZ":3088207420,"StartTime":66960.0,"Objects":[{"StartTime":66960.0,"EndTime":67142.0,"Column":3},{"StartTime":66960.0,"EndTime":67142.0,"Column":5}]},{"RandomW":523225986,"RandomX":3373094016,"RandomY":3088207420,"RandomZ":1778029315,"StartTime":67326.0,"Objects":[{"StartTime":67326.0,"EndTime":67326.0,"Column":2},{"StartTime":67326.0,"EndTime":67326.0,"Column":3}]},{"RandomW":2523721637,"RandomX":1778029315,"RandomY":523225986,"RandomZ":3156555187,"StartTime":67509.0,"Objects":[{"StartTime":67509.0,"EndTime":67874.0,"Column":1}]},{"RandomW":3753678213,"RandomX":2523721637,"RandomY":733156576,"RandomZ":1252112847,"StartTime":68058.0,"Objects":[{"StartTime":68058.0,"EndTime":68240.0,"Column":4},{"StartTime":68058.0,"EndTime":68240.0,"Column":5}]},{"RandomW":765988363,"RandomX":2650496303,"RandomY":3671318686,"RandomZ":3791148796,"StartTime":68424.0,"Objects":[{"StartTime":68424.0,"EndTime":68789.0,"Column":1},{"StartTime":68424.0,"EndTime":68789.0,"Column":2}]},{"RandomW":1639351583,"RandomX":1794981044,"RandomY":795866725,"RandomZ":201525954,"StartTime":68972.0,"Objects":[{"StartTime":68972.0,"EndTime":69337.0,"Column":0},{"StartTime":68972.0,"EndTime":69337.0,"Column":5}]},{"RandomW":3794603265,"RandomX":795866725,"RandomY":201525954,"RandomZ":1639351583,"StartTime":69521.0,"Objects":[{"StartTime":69521.0,"EndTime":69521.0,"Column":4},{"StartTime":69521.0,"EndTime":69521.0,"Column":5}]},{"RandomW":2799716979,"RandomX":1639351583,"RandomY":3794603265,"RandomZ":2996900863,"StartTime":69704.0,"Objects":[{"StartTime":69704.0,"EndTime":69886.0,"Column":2}]},{"RandomW":1138768260,"RandomX":2799716979,"RandomY":1940635085,"RandomZ":4184142780,"StartTime":70070.0,"Objects":[{"StartTime":70070.0,"EndTime":70252.0,"Column":6},{"StartTime":70070.0,"EndTime":70252.0,"Column":3}]},{"RandomW":3382500543,"RandomX":4184142780,"RandomY":1138768260,"RandomZ":3891744857,"StartTime":70436.0,"Objects":[{"StartTime":70436.0,"EndTime":70436.0,"Column":3},{"StartTime":70436.0,"EndTime":70436.0,"Column":4}]},{"RandomW":665559990,"RandomX":398143267,"RandomY":1440028745,"RandomZ":150863666,"StartTime":70619.0,"Objects":[{"StartTime":70619.0,"EndTime":70984.0,"Column":0},{"StartTime":70619.0,"EndTime":70984.0,"Column":2}]},{"RandomW":340592762,"RandomX":150863666,"RandomY":665559990,"RandomZ":3920056919,"StartTime":71167.0,"Objects":[{"StartTime":71167.0,"EndTime":71167.0,"Column":5},{"StartTime":71167.0,"EndTime":71167.0,"Column":1}]},{"RandomW":1518605551,"RandomX":340592762,"RandomY":4088291758,"RandomZ":2304957054,"StartTime":71350.0,"Objects":[{"StartTime":71350.0,"EndTime":71532.0,"Column":0},{"StartTime":71350.0,"EndTime":71532.0,"Column":4}]},{"RandomW":972812530,"RandomX":1518605551,"RandomY":653707549,"RandomZ":2799009660,"StartTime":71716.0,"Objects":[{"StartTime":71716.0,"EndTime":71898.0,"Column":2},{"StartTime":71716.0,"EndTime":71898.0,"Column":3}]},{"RandomW":3736044692,"RandomX":972812530,"RandomY":1134737486,"RandomZ":3549179654,"StartTime":72082.0,"Objects":[{"StartTime":72082.0,"EndTime":72264.0,"Column":4},{"StartTime":72082.0,"EndTime":72264.0,"Column":5}]},{"RandomW":2646968586,"RandomX":3695561354,"RandomY":2121039538,"RandomZ":3939713463,"StartTime":72448.0,"Objects":[{"StartTime":72448.0,"EndTime":72630.0,"Column":6},{"StartTime":72448.0,"EndTime":72630.0,"Column":1}]},{"RandomW":34357760,"RandomX":2646968586,"RandomY":1864765858,"RandomZ":1923246874,"StartTime":72814.0,"Objects":[{"StartTime":72814.0,"EndTime":72814.0,"Column":0},{"StartTime":72814.0,"EndTime":72814.0,"Column":6}]},{"RandomW":34357760,"RandomX":2646968586,"RandomY":1864765858,"RandomZ":1923246874,"StartTime":72997.0,"Objects":[{"StartTime":72997.0,"EndTime":72997.0,"Column":6},{"StartTime":72997.0,"EndTime":72997.0,"Column":0}]},{"RandomW":3006273170,"RandomX":1864765858,"RandomY":1923246874,"RandomZ":34357760,"StartTime":73363.0,"Objects":[{"StartTime":73363.0,"EndTime":73363.0,"Column":4},{"StartTime":73363.0,"EndTime":73363.0,"Column":5}]},{"RandomW":3978541447,"RandomX":3006273170,"RandomY":3972311639,"RandomZ":2371876462,"StartTime":73728.0,"Objects":[{"StartTime":73728.0,"EndTime":73728.0,"Column":2},{"StartTime":73819.0,"EndTime":73819.0,"Column":5},{"StartTime":73910.0,"EndTime":73910.0,"Column":0}]},{"RandomW":399194528,"RandomX":2371876462,"RandomY":3978541447,"RandomZ":3734283831,"StartTime":74094.0,"Objects":[{"StartTime":74094.0,"EndTime":74094.0,"Column":3},{"StartTime":74094.0,"EndTime":74094.0,"Column":1}]},{"RandomW":2883430810,"RandomX":4002716317,"RandomY":2698819798,"RandomZ":1875619237,"StartTime":74277.0,"Objects":[{"StartTime":74277.0,"EndTime":74642.0,"Column":6},{"StartTime":74277.0,"EndTime":74642.0,"Column":2}]},{"RandomW":3502984571,"RandomX":3789000206,"RandomY":2760409322,"RandomZ":2518464347,"StartTime":74826.0,"Objects":[{"StartTime":74826.0,"EndTime":74826.0,"Column":1},{"StartTime":74826.0,"EndTime":74826.0,"Column":4}]},{"RandomW":2447473462,"RandomX":1834893326,"RandomY":512459921,"RandomZ":2493625006,"StartTime":75009.0,"Objects":[{"StartTime":75009.0,"EndTime":75374.0,"Column":5},{"StartTime":75009.0,"EndTime":75374.0,"Column":0}]},{"RandomW":236980020,"RandomX":512459921,"RandomY":2493625006,"RandomZ":2447473462,"StartTime":75558.0,"Objects":[{"StartTime":75558.0,"EndTime":75558.0,"Column":4}]},{"RandomW":1338160073,"RandomX":236980020,"RandomY":1288545645,"RandomZ":3579861656,"StartTime":75741.0,"Objects":[{"StartTime":75741.0,"EndTime":75741.0,"Column":1},{"StartTime":75741.0,"EndTime":75741.0,"Column":5}]},{"RandomW":1104479394,"RandomX":1288545645,"RandomY":3579861656,"RandomZ":1338160073,"StartTime":75924.0,"Objects":[{"StartTime":75924.0,"EndTime":75924.0,"Column":6}]},{"RandomW":1611802424,"RandomX":3579861656,"RandomY":1338160073,"RandomZ":1104479394,"StartTime":76106.0,"Objects":[{"StartTime":76106.0,"EndTime":76106.0,"Column":5},{"StartTime":76106.0,"EndTime":76106.0,"Column":6}]},{"RandomW":74337788,"RandomX":1611802424,"RandomY":3077637432,"RandomZ":3984045284,"StartTime":76289.0,"Objects":[{"StartTime":76289.0,"EndTime":76654.0,"Column":0}]},{"RandomW":2589155279,"RandomX":74337788,"RandomY":4122247598,"RandomZ":3402826469,"StartTime":76838.0,"Objects":[{"StartTime":76838.0,"EndTime":77020.0,"Column":4},{"StartTime":76838.0,"EndTime":77020.0,"Column":1}]},{"RandomW":4015672441,"RandomX":2589155279,"RandomY":3961839828,"RandomZ":3184309519,"StartTime":77204.0,"Objects":[{"StartTime":77204.0,"EndTime":77569.0,"Column":3},{"StartTime":77204.0,"EndTime":77569.0,"Column":6}]},{"RandomW":605987856,"RandomX":3184309519,"RandomY":4015672441,"RandomZ":4025998202,"StartTime":77753.0,"Objects":[{"StartTime":77753.0,"EndTime":77753.0,"Column":0},{"StartTime":77753.0,"EndTime":77753.0,"Column":1}]},{"RandomW":1497070673,"RandomX":2430309501,"RandomY":1093966930,"RandomZ":2905669028,"StartTime":77936.0,"Objects":[{"StartTime":77936.0,"EndTime":78301.0,"Column":3},{"StartTime":77936.0,"EndTime":78301.0,"Column":2},{"StartTime":77936.0,"EndTime":78301.0,"Column":4}]},{"RandomW":353334135,"RandomX":1093966930,"RandomY":2905669028,"RandomZ":1497070673,"StartTime":78485.0,"Objects":[{"StartTime":78485.0,"EndTime":78485.0,"Column":1}]},{"RandomW":912971684,"RandomX":4030507912,"RandomY":3670783478,"RandomZ":1485865738,"StartTime":78667.0,"Objects":[{"StartTime":78667.0,"EndTime":78849.0,"Column":4},{"StartTime":78667.0,"EndTime":78849.0,"Column":2}]},{"RandomW":589257226,"RandomX":3670783478,"RandomY":1485865738,"RandomZ":912971684,"StartTime":79033.0,"Objects":[{"StartTime":79033.0,"EndTime":79033.0,"Column":3},{"StartTime":79033.0,"EndTime":79033.0,"Column":4}]},{"RandomW":2024304860,"RandomX":912971684,"RandomY":589257226,"RandomZ":2767994778,"StartTime":79216.0,"Objects":[{"StartTime":79216.0,"EndTime":79581.0,"Column":6}]},{"RandomW":2219601613,"RandomX":2024304860,"RandomY":404709274,"RandomZ":3238631833,"StartTime":79765.0,"Objects":[{"StartTime":79765.0,"EndTime":79947.0,"Column":3},{"StartTime":79765.0,"EndTime":79947.0,"Column":0}]},{"RandomW":3490718869,"RandomX":2219601613,"RandomY":3210330120,"RandomZ":1566096374,"StartTime":80131.0,"Objects":[{"StartTime":80131.0,"EndTime":80496.0,"Column":5},{"StartTime":80131.0,"EndTime":80496.0,"Column":4}]},{"RandomW":1189469485,"RandomX":1566096374,"RandomY":3490718869,"RandomZ":936182364,"StartTime":80680.0,"Objects":[{"StartTime":80680.0,"EndTime":81045.0,"Column":3}]},{"RandomW":3740948748,"RandomX":3490718869,"RandomY":936182364,"RandomZ":1189469485,"StartTime":81228.0,"Objects":[{"StartTime":81228.0,"EndTime":81228.0,"Column":5},{"StartTime":81228.0,"EndTime":81228.0,"Column":6}]},{"RandomW":3491747463,"RandomX":1189469485,"RandomY":3740948748,"RandomZ":2409626314,"StartTime":81411.0,"Objects":[{"StartTime":81411.0,"EndTime":81776.0,"Column":4}]},{"RandomW":3095098652,"RandomX":3740948748,"RandomY":2409626314,"RandomZ":3491747463,"StartTime":81960.0,"Objects":[{"StartTime":81960.0,"EndTime":81960.0,"Column":3},{"StartTime":81960.0,"EndTime":81960.0,"Column":4}]},{"RandomW":3024447782,"RandomX":2409626314,"RandomY":3491747463,"RandomZ":3095098652,"StartTime":82143.0,"Objects":[{"StartTime":82143.0,"EndTime":82143.0,"Column":2},{"StartTime":82143.0,"EndTime":82143.0,"Column":3}]},{"RandomW":3942236456,"RandomX":3095098652,"RandomY":3024447782,"RandomZ":3296500942,"StartTime":82326.0,"Objects":[{"StartTime":82326.0,"EndTime":82508.0,"Column":5}]},{"RandomW":912304721,"RandomX":3942236456,"RandomY":2303302398,"RandomZ":383442600,"StartTime":82692.0,"Objects":[{"StartTime":82692.0,"EndTime":82874.0,"Column":1},{"StartTime":82692.0,"EndTime":82874.0,"Column":2}]},{"RandomW":2431170151,"RandomX":3622775798,"RandomY":385908797,"RandomZ":604082862,"StartTime":83058.0,"Objects":[{"StartTime":83058.0,"EndTime":83240.0,"Column":4},{"StartTime":83058.0,"EndTime":83240.0,"Column":0}]},{"RandomW":4088921973,"RandomX":1523770388,"RandomY":1345324755,"RandomZ":2436511051,"StartTime":83424.0,"Objects":[{"StartTime":83424.0,"EndTime":83606.0,"Column":2},{"StartTime":83424.0,"EndTime":83606.0,"Column":6}]},{"RandomW":2663434012,"RandomX":3999189199,"RandomY":2928551970,"RandomZ":3800966865,"StartTime":83789.0,"Objects":[{"StartTime":83789.0,"EndTime":83971.0,"Column":5},{"StartTime":83789.0,"EndTime":83971.0,"Column":1}]},{"RandomW":183339481,"RandomX":3405481532,"RandomY":1385906264,"RandomZ":3611020052,"StartTime":84155.0,"Objects":[{"StartTime":84155.0,"EndTime":84337.0,"Column":4},{"StartTime":84155.0,"EndTime":84337.0,"Column":0}]},{"RandomW":472982750,"RandomX":1385906264,"RandomY":3611020052,"RandomZ":183339481,"StartTime":84521.0,"Objects":[{"StartTime":84521.0,"EndTime":84521.0,"Column":4}]},{"RandomW":2485141120,"RandomX":3611020052,"RandomY":183339481,"RandomZ":472982750,"StartTime":84704.0,"Objects":[{"StartTime":84704.0,"EndTime":84704.0,"Column":5}]},{"RandomW":2638881915,"RandomX":183339481,"RandomY":472982750,"RandomZ":2485141120,"StartTime":84887.0,"Objects":[{"StartTime":84887.0,"EndTime":84887.0,"Column":6},{"StartTime":84887.0,"EndTime":84887.0,"Column":0}]},{"RandomW":2991178386,"RandomX":1846348081,"RandomY":4216122958,"RandomZ":938042528,"StartTime":85070.0,"Objects":[{"StartTime":85070.0,"EndTime":85070.0,"Column":5},{"StartTime":85161.0,"EndTime":85161.0,"Column":6},{"StartTime":85252.0,"EndTime":85252.0,"Column":3}]},{"RandomW":2830634920,"RandomX":3020624235,"RandomY":682207034,"RandomZ":1410927339,"StartTime":85436.0,"Objects":[{"StartTime":85436.0,"EndTime":85436.0,"Column":4},{"StartTime":85527.0,"EndTime":85527.0,"Column":2},{"StartTime":85618.0,"EndTime":85618.0,"Column":4}]},{"RandomW":1154798493,"RandomX":682207034,"RandomY":1410927339,"RandomZ":2830634920,"StartTime":85802.0,"Objects":[{"StartTime":85802.0,"EndTime":85802.0,"Column":3}]},{"RandomW":3579754894,"RandomX":1154798493,"RandomY":555826250,"RandomZ":3186828503,"StartTime":85985.0,"Objects":[{"StartTime":85985.0,"EndTime":86167.0,"Column":4}]},{"RandomW":522156379,"RandomX":3186828503,"RandomY":3579754894,"RandomZ":938791043,"StartTime":86350.0,"Objects":[{"StartTime":86350.0,"EndTime":86532.0,"Column":1}]},{"RandomW":2327696617,"RandomX":522156379,"RandomY":1005466611,"RandomZ":459042761,"StartTime":86716.0,"Objects":[{"StartTime":86716.0,"EndTime":86898.0,"Column":0}]},{"RandomW":3698157493,"RandomX":2327696617,"RandomY":1854714180,"RandomZ":615999181,"StartTime":87082.0,"Objects":[{"StartTime":87082.0,"EndTime":87264.0,"Column":2},{"StartTime":87082.0,"EndTime":87264.0,"Column":5}]},{"RandomW":2615638464,"RandomX":3088317005,"RandomY":3005119130,"RandomZ":738255674,"StartTime":87448.0,"Objects":[{"StartTime":87448.0,"EndTime":87448.0,"Column":4},{"StartTime":87448.0,"EndTime":87448.0,"Column":1},{"StartTime":87630.0,"EndTime":87630.0,"Column":2},{"StartTime":87630.0,"EndTime":87630.0,"Column":5},{"StartTime":87812.0,"EndTime":87812.0,"Column":2},{"StartTime":87812.0,"EndTime":87812.0,"Column":5}]},{"RandomW":4236988115,"RandomX":738255674,"RandomY":2615638464,"RandomZ":3154196835,"StartTime":87997.0,"Objects":[{"StartTime":87997.0,"EndTime":88362.0,"Column":6}]},{"RandomW":3260011681,"RandomX":4236988115,"RandomY":3619257163,"RandomZ":1999646981,"StartTime":88546.0,"Objects":[{"StartTime":88546.0,"EndTime":88728.0,"Column":3}]},{"RandomW":1679091693,"RandomX":3619257163,"RandomY":1999646981,"RandomZ":3260011681,"StartTime":88911.0,"Objects":[{"StartTime":88911.0,"EndTime":88911.0,"Column":1},{"StartTime":88911.0,"EndTime":88911.0,"Column":2}]},{"RandomW":4053500035,"RandomX":2020322055,"RandomY":2384790806,"RandomZ":846406319,"StartTime":89277.0,"Objects":[{"StartTime":89277.0,"EndTime":89459.0,"Column":0},{"StartTime":89277.0,"EndTime":89459.0,"Column":6}]},{"RandomW":3656101543,"RandomX":4053500035,"RandomY":3566026276,"RandomZ":1915132950,"StartTime":89643.0,"Objects":[{"StartTime":89643.0,"EndTime":89825.0,"Column":4}]},{"RandomW":3002483376,"RandomX":1234751024,"RandomY":253242681,"RandomZ":2332173547,"StartTime":90009.0,"Objects":[{"StartTime":90009.0,"EndTime":90191.0,"Column":0},{"StartTime":90009.0,"EndTime":90191.0,"Column":2}]},{"RandomW":1769147212,"RandomX":1032909712,"RandomY":4079968510,"RandomZ":1771054860,"StartTime":90375.0,"Objects":[{"StartTime":90375.0,"EndTime":90375.0,"Column":3},{"StartTime":90375.0,"EndTime":90375.0,"Column":6},{"StartTime":90557.0,"EndTime":90557.0,"Column":6},{"StartTime":90557.0,"EndTime":90557.0,"Column":3},{"StartTime":90739.0,"EndTime":90739.0,"Column":5},{"StartTime":90739.0,"EndTime":90739.0,"Column":2}]},{"RandomW":1533402007,"RandomX":1771054860,"RandomY":1769147212,"RandomZ":3552934273,"StartTime":90924.0,"Objects":[{"StartTime":90924.0,"EndTime":91289.0,"Column":4}]},{"RandomW":1123904499,"RandomX":3552934273,"RandomY":1533402007,"RandomZ":3005562800,"StartTime":91472.0,"Objects":[{"StartTime":91472.0,"EndTime":91654.0,"Column":3}]},{"RandomW":3485521641,"RandomX":3005562800,"RandomY":1123904499,"RandomZ":3121355612,"StartTime":91838.0,"Objects":[{"StartTime":91838.0,"EndTime":92203.0,"Column":4}]},{"RandomW":1434626078,"RandomX":1123904499,"RandomY":3121355612,"RandomZ":3485521641,"StartTime":92387.0,"Objects":[{"StartTime":92387.0,"EndTime":92387.0,"Column":4}]},{"RandomW":4013632575,"RandomX":1434626078,"RandomY":4236899246,"RandomZ":646300056,"StartTime":92570.0,"Objects":[{"StartTime":92570.0,"EndTime":92752.0,"Column":2},{"StartTime":92570.0,"EndTime":92752.0,"Column":6}]},{"RandomW":471738692,"RandomX":646300056,"RandomY":4013632575,"RandomZ":2948180894,"StartTime":92936.0,"Objects":[{"StartTime":92936.0,"EndTime":93118.0,"Column":1}]},{"RandomW":1081382077,"RandomX":471738692,"RandomY":346006110,"RandomZ":586362406,"StartTime":93302.0,"Objects":[{"StartTime":93302.0,"EndTime":93302.0,"Column":1},{"StartTime":93302.0,"EndTime":93302.0,"Column":5}]},{"RandomW":1151929163,"RandomX":586362406,"RandomY":1081382077,"RandomZ":2915942910,"StartTime":93485.0,"Objects":[{"StartTime":93485.0,"EndTime":93667.0,"Column":3}]},{"RandomW":3634683246,"RandomX":1151929163,"RandomY":4287668198,"RandomZ":463810005,"StartTime":93850.0,"Objects":[{"StartTime":93850.0,"EndTime":94215.0,"Column":1},{"StartTime":93850.0,"EndTime":94215.0,"Column":4}]},{"RandomW":2941238432,"RandomX":463810005,"RandomY":3634683246,"RandomZ":3562759778,"StartTime":94399.0,"Objects":[{"StartTime":94399.0,"EndTime":94581.0,"Column":2}]},{"RandomW":1661408876,"RandomX":3562759778,"RandomY":2941238432,"RandomZ":2646009625,"StartTime":94765.0,"Objects":[{"StartTime":94765.0,"EndTime":95130.0,"Column":5}]},{"RandomW":3189251976,"RandomX":2646009625,"RandomY":1661408876,"RandomZ":1818231832,"StartTime":95314.0,"Objects":[{"StartTime":95314.0,"EndTime":95314.0,"Column":5},{"StartTime":95314.0,"EndTime":95314.0,"Column":3}]},{"RandomW":2743067846,"RandomX":3189251976,"RandomY":2495392125,"RandomZ":3478354416,"StartTime":95497.0,"Objects":[{"StartTime":95497.0,"EndTime":95497.0,"Column":0},{"StartTime":95497.0,"EndTime":95497.0,"Column":6}]},{"RandomW":2762867836,"RandomX":3722791806,"RandomY":2892228350,"RandomZ":4171994747,"StartTime":95680.0,"Objects":[{"StartTime":95680.0,"EndTime":95680.0,"Column":5},{"StartTime":95680.0,"EndTime":95680.0,"Column":3},{"StartTime":95862.0,"EndTime":95862.0,"Column":2},{"StartTime":95862.0,"EndTime":95862.0,"Column":6},{"StartTime":96044.0,"EndTime":96044.0,"Column":6},{"StartTime":96044.0,"EndTime":96044.0,"Column":4}]},{"RandomW":1153177485,"RandomX":2762867836,"RandomY":1407653164,"RandomZ":3758120376,"StartTime":96228.0,"Objects":[{"StartTime":96228.0,"EndTime":96228.0,"Column":1},{"StartTime":96228.0,"EndTime":96228.0,"Column":5}]},{"RandomW":1153177485,"RandomX":2762867836,"RandomY":1407653164,"RandomZ":3758120376,"StartTime":96411.0,"Objects":[{"StartTime":96411.0,"EndTime":96411.0,"Column":5},{"StartTime":96411.0,"EndTime":96411.0,"Column":1}]},{"RandomW":2430957186,"RandomX":1407653164,"RandomY":3758120376,"RandomZ":1153177485,"StartTime":96777.0,"Objects":[{"StartTime":96777.0,"EndTime":96777.0,"Column":3},{"StartTime":96777.0,"EndTime":96777.0,"Column":4}]},{"RandomW":4223688647,"RandomX":3758120376,"RandomY":1153177485,"RandomZ":2430957186,"StartTime":97143.0,"Objects":[{"StartTime":97143.0,"EndTime":97143.0,"Column":4},{"StartTime":97143.0,"EndTime":97143.0,"Column":5}]},{"RandomW":433008794,"RandomX":1153177485,"RandomY":2430957186,"RandomZ":4223688647,"StartTime":97509.0,"Objects":[{"StartTime":97509.0,"EndTime":97509.0,"Column":5},{"StartTime":97509.0,"EndTime":97509.0,"Column":6}]},{"RandomW":3177925713,"RandomX":2430957186,"RandomY":4223688647,"RandomZ":433008794,"StartTime":97692.0,"Objects":[{"StartTime":97692.0,"EndTime":100619.0,"Column":3}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/1450162.osu b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/1450162.osu new file mode 100644 index 0000000000..42669b1516 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Resources/Testing/Beatmaps/1450162.osu @@ -0,0 +1,297 @@ +osu file format v14 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:5 +CircleSize:4 +OverallDifficulty:7 +ApproachRate:7.5 +SliderMultiplier:1.4 +SliderTickRate:1 + +[Events] +//Background and Video events +//Break Periods +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples + +[TimingPoints] +1107,365.853658536585,4,2,1,50,1,0 +1107,-166.666666666667,4,2,1,50,0,0 +6960,-111.111111111111,4,2,1,50,0,0 +8424,-100,4,2,1,50,0,0 +48119,-125,4,2,1,50,0,0 +52143,-100,4,2,1,50,0,0 +62570,-100,4,2,1,60,0,1 +85985,-100,4,2,1,50,0,0 +97692,-100,4,2,1,30,0,0 +99155,-100,4,2,1,20,0,0 +100619,-100,4,2,1,5,0,0 + +[HitObjects] +38,247,1107,6,0,P|96:269|170:192,1,167.999994873047,2|0,0:0|0:0,0:0:0:0: +201,128,2570,6,0,L|205:221,1,83.9999974365235,2|0,0:0|0:0,0:0:0:0: +242,230,3302,2,0,L|234:324,1,83.9999974365235,2|0,0:0|0:0,0:0:0:0: +205,343,4033,6,0,P|246:296|351:314,1,167.999994873047,2|0,0:0|0:0,0:0:0:0: +400,368,5497,6,0,L|412:269,1,83.9999974365235,6|0,0:0|0:0,0:0:0:0: +436,251,6228,2,0,P|425:203|408:153,1,83.9999974365235,2|0,0:0|0:0,0:0:0:0: +304,200,6960,6,0,P|262:186|234:181,1,62.9999980773926,6|0,0:0|0:0,0:0:0:0: +202,179,7326,1,8,0:0:0:0: +276,94,7509,2,0,P|313:92|353:87,1,62.9999980773926,2|0,0:0|0:0,0:0:0:0: +398,31,7875,1,2,0:0:0:0: +464,81,8058,2,0,L|450:150,1,62.9999980773926,2|0,0:0|0:0,0:0:0:0: +449,230,8424,6,0,P|347:206|306:217,1,140,2|8,0:0|0:0,0:0:0:0: +229,273,8972,2,0,P|225:339|235:361,1,70,2|0,0:0|0:0,0:0:0:0: +304,313,9338,1,8,0:0:0:0: +224,190,9521,1,2,0:0:0:0: +296,45,9887,6,0,P|297:97|288:125,1,70,6|0,0:0|0:0,0:0:0:0: +224,190,10253,1,8,0:0:0:0: +167,118,10436,1,8,0:0:0:0: +76,126,10619,1,8,0:0:0:0: +39,209,10802,1,8,0:0:0:0: +93,282,10985,1,10,0:0:0:0: +184,280,11167,1,10,0:0:0:0: +102,136,12814,5,2,0:0:0:0: +102,136,13180,2,0,L|199:130,1,70,8|0,0:0|0:0,0:0:0:0: +256,167,13546,2,0,L|339:161,1,70,8|2,0:0|0:0,0:0:0:0: +408,201,13911,2,0,P|454:176|471:143,1,70,8|2,0:0|0:0,0:0:0:0: +373,54,14277,6,0,L|396:137,2,70,6|0|8,0:0|0:0|0:0,0:0:0:0: +305,111,14826,2,0,L|287:274,1,140,0|2,0:0|0:0,0:0:0:0: +262,337,15375,2,0,L|349:327,1,70,8|2,0:0|0:0,0:0:0:0: +419,354,15741,1,8,0:0:0:0: +477,197,16106,6,0,P|423:197|385:209,1,70,8|0,0:0|0:0,0:0:0:0: +321,170,16472,2,0,P|278:190|253:219,1,70,8|2,0:0|0:0,0:0:0:0: +171,213,16838,2,0,P|152:259|158:304,1,70,8|2,0:0|0:0,0:0:0:0: +305,294,17204,6,0,L|224:278,2,70,6|0|8,0:0|0:0|0:0,0:0:0:0: +310,202,17753,2,0,L|149:214,1,140,0|2,0:0|0:0,0:0:0:0: +84,244,18302,2,0,L|92:152,1,70,8|2,0:0|0:0,0:0:0:0: +47,93,18667,6,0,P|78:53|176:80,1,140,6|8,0:0|0:0,0:0:0:0: +218,130,19216,1,0,0:0:0:0: +299,88,19399,2,0,L|387:91,1,70,8|0,0:0|0:0,0:0:0:0: +458,106,19765,2,0,P|447:139|444:205,1,70,8|0,0:0|0:0,0:0:0:0: +455,274,20131,5,2,0:0:0:0: +366,292,20314,2,0,L|353:211,1,70,0|8,0:0|0:0,0:0:0:0: +277,173,20680,2,0,L|253:342,1,140,0|2,0:0|0:0,0:0:0:0: +322,376,21228,2,0,P|368:368|416:370,1,70,8|2,0:0|0:0,0:0:0:0: +500,287,21594,6,0,P|427:273|362:293,2,140,6|8|8,0:0|0:0|0:0,0:0:0:0: +496,111,22509,1,8,0:0:0:0: +499,189,22692,2,0,L|418:191,1,70,8|2,0:0|0:0,0:0:0:0: +344,164,23058,5,6,0:0:0:0: +344,164,23241,1,12,0:0:0:0: +261,326,23606,2,0,L|246:178,1,140,8|2,0:0|0:0,0:0:0:0: +277,100,24155,2,0,P|225:99|196:109,1,70,8|2,0:0|0:0,0:0:0:0: +165,273,24521,5,6,0:0:0:0: +83,235,24704,2,0,L|93:81,1,140,0|0,0:0|0:0,0:0:0:0: +21,37,25253,2,0,L|1:120,2,70,2|0|8,0:0|0:0|0:0,0:0:0:0: +110,17,25802,1,0,0:0:0:0: +172,83,25985,5,2,0:0:0:0: +236,19,26167,2,0,P|223:70|227:170,1,140,0|0,0:0|0:0,0:0:0:0: +293,216,26716,2,0,P|316:165|314:134,2,70,2|0|8,0:0|0:0|0:0,0:0:0:0: +206,245,27265,1,0,0:0:0:0: +274,305,27448,5,2,0:0:0:0: +194,348,27631,2,0,L|363:332,1,140,0|0,0:0|0:0,0:0:0:0: +424,336,28180,1,2,0:0:0:0: +431,245,28363,2,0,P|381:252|354:276,2,70,0|8|0,0:0|0:0|0:0,0:0:0:0: +509,291,28911,6,0,L|496:128,1,140,2|8,0:0|0:0,0:0:0:0: +504,60,29460,1,0,0:0:0:0: +417,34,29643,2,0,L|402:183,1,140,2|8,0:0|0:0,0:0:0:0: +365,262,30192,1,0,0:0:0:0: +295,202,30375,5,2,0:0:0:0: +309,112,30558,2,0,P|282:172|196:176,1,140,0|0,0:0|0:0,0:0:0:0: +148,120,31106,2,0,P|189:99|225:99,2,70,2|0|8,0:0|0:0|0:0,0:0:0:0: +129,209,31655,1,0,0:0:0:0: +63,146,31838,5,2,0:0:0:0: +16,67,32021,2,0,L|27:220,1,140,0|0,0:0|0:0,0:0:0:0: +23,297,32570,2,0,P|81:286|111:290,1,70,2|0,0:0|0:0,0:0:0:0: +173,327,32936,1,8,0:0:0:0: +338,251,33302,6,0,P|268:254|227:199,1,140,2|8,0:0|0:0,0:0:0:0: +203,114,33850,2,0,L|185:262,1,140,0|0,0:0|0:0,0:0:0:0: +244,323,34399,1,8,0:0:0:0: +334,335,34582,1,0,0:0:0:0: +419,219,34765,6,0,L|410:304,1,70,2|0,0:0|0:0,0:0:0:0: +338,251,35131,1,8,0:0:0:0: +301,111,35314,2,0,L|301:190,1,70,6|0,0:0|0:0,0:0:0:0: +383,141,35680,1,8,0:0:0:0: +462,97,35863,2,0,P|427:64|393:54,1,70,2|0,0:0|0:0,0:0:0:0: +321,23,36228,5,2,0:0:0:0: +237,60,36411,1,0,0:0:0:0: +148,38,36594,2,0,P|107:33|56:43,1,70,8|0,0:0|0:0,0:0:0:0: +86,125,36960,2,0,P|51:125|17:117,2,70,2|0|8,0:0|0:0|0:0,0:0:0:0: +175,123,37509,1,0,0:0:0:0: +129,201,37692,5,2,0:0:0:0: +198,259,37875,1,0,0:0:0:0: +205,349,38058,2,0,P|251:330|284:326,1,70,8|0,0:0|0:0,0:0:0:0: +352,285,38424,2,0,P|361:318|357:353,2,70,2|0|8,0:0|0:0|0:0,0:0:0:0: +282,239,38972,1,0,0:0:0:0: +362,195,39155,5,2,0:0:0:0: +436,142,39338,2,0,P|398:115|354:112,1,70,0|8,0:0|0:0,0:0:0:0: +286,92,39704,2,0,L|451:74,1,140,0|0,0:0|0:0,0:0:0:0: +512,118,40253,2,0,L|494:198,1,70,8|0,0:0|0:0,0:0:0:0: +430,297,40619,6,0,P|423:236|336:195,1,140,2|8,0:0|0:0,0:0:0:0: +282,239,41167,1,0,0:0:0:0: +209,184,41350,2,0,L|222:112,1,70,2|2,0:0|0:0,0:0:0:0: +177,34,41716,2,0,P|230:26|269:38,1,70,8|0,0:0|0:0,0:0:0:0: +307,95,42082,5,2,0:0:0:0: +363,23,42265,2,0,L|359:114,1,70,0|8,0:0|0:0,0:0:0:0: +360,184,42631,1,0,0:0:0:0: +450,191,42814,2,0,P|443:145|424:119,2,70,2|0|8,0:0|0:0|0:0,0:0:0:0: +393,263,43363,1,0,0:0:0:0: +304,242,43546,5,2,0:0:0:0: +241,308,43728,1,0,0:0:0:0: +167,256,43911,2,0,P|205:228|245:226,1,70,8|0,0:0|0:0,0:0:0:0: +166,341,44277,2,0,P|118:325|90:289,1,70,2|0,0:0|0:0,0:0:0:0: +125,177,44643,2,0,P|168:152|201:153,1,70,8|0,0:0|0:0,0:0:0:0: +276,132,45009,6,0,L|119:105,1,140,2|8,0:0|0:0,0:0:0:0: +52,74,45558,2,0,L|210:57,1,140,2|0,0:0|0:0,0:0:0:0: +277,28,46106,1,8,0:0:0:0: +349,82,46289,1,0,0:0:0:0: +425,32,46472,6,0,L|451:110,2,70,6|2|8,0:0|0:0|0:0,0:0:0:0: +349,82,47021,2,0,L|344:235,1,140,2|8,0:0|0:0,0:0:0:0: +372,308,47570,1,2,0:0:0:0: +170,324,47936,5,2,0:0:0:0: +99,286,48119,2,0,L|112:112,1,168,2|2,0:0|0:0,0:0:0:0: +64,48,48850,2,0,P|125:36|195:111,1,168,2|2,0:0|0:0,0:0:0:0: +199,189,49582,6,0,L|369:166,1,168,2|2,0:0|0:0,0:0:0:0: +413,97,50314,2,0,P|390:180|377:274,1,168,2|2,0:0|0:0,0:0:0:0: +347,339,51046,6,0,P|424:333|463:251,1,168,2|2,0:0|0:0,0:0:0:0: +473,175,51777,2,0,L|477:105,1,56,2|2,0:0|0:0,0:0:0:0: +446,24,52143,6,0,P|363:22|308:82,1,140,12|2,0:0|0:0,0:0:0:0: +282,138,52692,1,8,0:0:0:0: +193,118,52875,2,0,L|213:281,1,140,2|8,0:0|0:0,0:0:0:0: +225,347,53424,2,0,P|268:328|286:301,1,70,2|0,0:0|0:0,0:0:0:0: +304,222,53789,5,2,0:0:0:0: +385,263,53972,1,0,0:0:0:0: +462,214,54155,2,0,P|421:185|383:179,1,70,8|0,0:0|0:0,0:0:0:0: +322,136,54521,2,0,P|360:105|400:93,1,70,2|0,0:0|0:0,0:0:0:0: +469,107,54887,2,0,L|483:24,1,70,8|0,0:0|0:0,0:0:0:0: +390,22,55253,6,0,L|223:30,1,140,2|8,0:0|0:0,0:0:0:0: +180,87,55802,1,0,0:0:0:0: +230,162,55985,2,0,L|391:154,1,140,2|8,0:0|0:0,0:0:0:0: +430,223,56533,1,0,0:0:0:0: +407,311,56716,6,0,P|356:347|285:307,1,140,2|8,0:0|0:0,0:0:0:0: +236,245,57265,1,0,0:0:0:0: +145,237,57448,2,0,L|162:316,1,70,2|0,0:0|0:0,0:0:0:0: +233,360,57814,6,0,P|185:349|142:350,1,70,8|0,0:0|0:0,0:0:0:0: +11,311,58180,2,0,P|64:302|104:306,1,70,2|0,0:0|0:0,0:0:0:0: +213,248,58546,2,0,P|162:237|130:237,1,70,8|0,0:0|0:0,0:0:0:0: +1,194,58911,2,0,P|47:183|74:185,1,70,2|0,0:0|0:0,0:0:0:0: +234,142,59277,2,0,P|175:129|152:128,1,70,8|0,0:0|0:0,0:0:0:0: +12,26,59643,6,0,P|66:38|71:140,1,140,2|8,0:0|0:0,0:0:0:0: +1,194,60192,1,0,0:0:0:0: +84,230,60375,1,2,0:0:0:0: +173,216,60558,1,8,0:0:0:0: +173,216,60649,1,8,0:0:0:0: +173,216,60741,1,8,0:0:0:0: +263,213,60924,1,2,0:0:0:0: +345,174,61106,6,0,P|320:144|286:130,1,70,2|0,0:0|0:0,0:0:0:0: +200,134,61472,1,8,0:0:0:0: +249,57,61655,2,0,L|263:12,2,35,12|8|8,0:0|0:0|0:0,0:0:0:0: +157,64,62021,2,0,L|153:13,2,35,12|8|8,0:0|0:0|0:0,0:0:0:0: +118,150,62387,1,2,0:0:0:0: +101,260,62570,6,0,P|207:236|257:243,1,140,2|8,0:0|0:0,0:0:0:0: +328,304,63119,1,0,0:0:0:0: +434,156,63302,2,0,P|373:157|329:217,1,140,2|8,0:0|0:0,0:0:0:0: +408,230,63850,1,2,0:0:0:0: +483,215,64033,5,6,0:0:0:0: +508,142,64216,1,0,0:0:0:0: +482,69,64399,1,8,0:0:0:0: +413,34,64582,2,0,P|336:30|256:49,1,140,0|2,0:0|0:0,0:0:0:0: +150,97,65131,2,0,P|190:97|243:107,1,70,8|2,0:0|0:0,0:0:0:0: +257,168,65497,6,0,L|225:323,1,140,2|8,0:0|0:0,0:0:0:0: +155,329,66046,1,0,0:0:0:0: +20,204,66228,2,0,P|92:202|133:271,1,140,8|8,0:0|0:0,0:0:0:0: +56,274,66777,1,2,0:0:0:0: +18,125,66960,6,0,L|93:119,1,70,6|0,0:0|0:0,0:0:0:0: +162,156,67326,1,8,0:0:0:0: +223,52,67509,2,0,L|227:219,1,140,0|2,0:0|0:0,0:0:0:0: +266,263,68058,2,0,P|300:229|308:199,1,70,8|2,0:0|0:0,0:0:0:0: +298,95,68424,6,0,L|458:75,1,140,6|8,0:0|0:0,0:0:0:0: +512,164,68972,2,0,L|358:154,1,140,0|2,0:0|0:0,0:0:0:0: +306,209,69521,1,8,0:0:0:0: +342,334,69704,6,0,P|361:289|369:244,1,70,2|6,0:0|0:0,0:0:0:0: +250,277,70070,2,0,P|223:228|219:186,1,70,0|8,0:0|0:0,0:0:0:0: +272,128,70436,1,0,0:0:0:0: +172,111,70619,2,0,L|343:97,1,140,8|8,0:0|0:0,0:0:0:0: +385,128,71167,1,2,0:0:0:0: +494,63,71350,6,0,L|413:54,1,70,6|0,0:0|0:0,0:0:0:0: +385,128,71716,2,0,L|475:140,1,70,8|0,0:0|0:0,0:0:0:0: +467,217,72082,2,0,L|386:208,1,70,8|2,0:0|0:0,0:0:0:0: +358,282,72448,2,0,L|448:294,1,70,8|2,0:0|0:0,0:0:0:0: +498,339,72814,5,12,0:0:0:0: +498,339,72997,1,12,0:0:0:0: +301,343,73363,1,8,0:0:0:0: +211,173,73728,2,0,L|221:216,2,35,2|2|8,0:0|0:0|0:0,0:0:0:0: +250,100,74094,1,2,0:0:0:0: +123,92,74277,6,0,P|129:156|129:236,1,140,2|8,0:0|0:0,0:0:0:0: +109,321,74826,1,0,0:0:0:0: +211,173,75009,2,0,P|266:165|333:237,1,140,8|8,0:0|0:0,0:0:0:0: +341,302,75558,1,2,0:0:0:0: +418,272,75741,5,6,0:0:0:0: +484,322,75924,1,0,0:0:0:0: +407,352,76106,1,8,0:0:0:0: +341,302,76289,2,0,L|364:147,1,140,0|2,0:0|0:0,0:0:0:0: +269,60,76838,2,0,P|315:69|349:94,1,70,8|0,0:0|0:0,0:0:0:0: +269,150,77204,6,0,P|228:160|114:139,1,140,2|8,0:0|0:0,0:0:0:0: +49,80,77753,1,0,0:0:0:0: +39,235,77936,2,0,P|103:222|160:277,1,140,8|8,0:0|0:0,0:0:0:0: +82,297,78485,1,2,0:0:0:0: +227,326,78667,6,0,L|233:241,1,70,4|0,0:0|0:0,0:0:0:0: +269,150,79033,1,8,0:0:0:0: +408,194,79216,2,0,P|359:172|271:187,1,140,0|2,0:0|0:0,0:0:0:0: +409,281,79765,2,0,P|447:272|478:250,1,70,8|2,0:0|0:0,0:0:0:0: +497,168,80131,6,0,L|481:332,1,140,6|8,0:0|0:0,0:0:0:0: +389,365,80680,2,0,L|376:198,1,140,0|2,0:0|0:0,0:0:0:0: +414,157,81228,1,8,0:0:0:0: +229,89,81411,6,0,P|304:91|338:167,1,140,2|0,0:0|0:0,0:0:0:0: +290,222,81960,1,8,0:0:0:0: +211,214,82143,1,8,0:0:0:0: +93,155,82326,2,0,P|137:143|172:150,1,70,2|2,0:0|0:0,0:0:0:0: +235,301,82692,2,0,P|177:296|141:279,1,70,8|2,0:0|0:0,0:0:0:0: +68,244,83058,6,0,L|72:328,1,70,6|0,0:0|0:0,0:0:0:0: +166,292,83424,2,0,L|157:372,1,70,8|0,0:0|0:0,0:0:0:0: +254,227,83789,2,0,L|258:310,1,70,8|2,0:0|0:0,0:0:0:0: +345,265,84155,2,0,L|336:349,1,70,8|0,0:0|0:0,0:0:0:0: +331,175,84521,5,2,0:0:0:0: +416,205,84704,1,2,0:0:0:0: +481,141,84887,1,8,0:0:0:0: +431,64,85070,2,0,L|444:26,2,35,8|8|2,0:0|0:0|0:0,0:0:0:0: +339,79,85436,2,0,L|341:39,2,35,8|8|8,0:0|0:0|0:0,0:0:0:0: +256,109,85802,1,2,0:0:0:0: +165,97,85985,6,0,P|167:150|164:187,1,70,2|0,0:0|0:0,0:0:0:0: +117,244,86350,2,0,P|163:241|204:235,1,70,8|0,0:0|0:0,0:0:0:0: +229,317,86716,2,0,P|273:305|300:294,1,70,8|2,0:0|0:0,0:0:0:0: +365,354,87082,2,0,P|404:334|430:310,1,70,8|0,0:0|0:0,0:0:0:0: +352,230,87448,6,0,L|271:216,2,70,6|0|8,0:0|0:0|0:0,0:0:0:0: +378,142,87997,2,0,L|222:144,1,140,0|2,0:0|0:0,0:0:0:0: +152,112,88546,2,0,L|166:214,1,70,8|2,0:0|0:0,0:0:0:0: +139,270,88911,5,8,0:0:0:0: +12,138,89277,2,0,L|29:55,1,70,8|0,0:0|0:0,0:0:0:0: +91,5,89643,2,0,L|104:97,1,70,8|2,0:0|0:0,0:0:0:0: +153,149,90009,2,0,L|175:78,1,70,8|0,0:0|0:0,0:0:0:0: +279,36,90375,6,0,L|357:27,2,70,6|0|8,0:0|0:0|0:0,0:0:0:0: +248,122,90924,2,0,L|398:125,1,140,0|2,0:0|0:0,0:0:0:0: +479,123,91472,2,0,P|468:170|445:195,1,70,8|2,0:0|0:0,0:0:0:0: +365,204,91838,6,0,P|414:220|409:320,1,140,6|8,0:0|0:0,0:0:0:0: +354,354,92387,1,0,0:0:0:0: +262,353,92570,2,0,L|271:273,1,70,8|2,0:0|0:0,0:0:0:0: +297,196,92936,2,0,P|243:198|216:215,1,70,8|0,0:0|0:0,0:0:0:0: +172,276,93302,5,6,0:0:0:0: +137,360,93485,2,0,L|127:265,1,70,0|8,0:0|0:0,0:0:0:0: +81,212,93850,2,0,P|93:138|118:67,1,140,0|2,0:0|0:0,0:0:0:0: +170,4,94399,2,0,P|195:37|204:74,1,70,8|2,0:0|0:0,0:0:0:0: +186,153,94765,6,0,L|340:139,1,140,6|8,0:0|0:0,0:0:0:0: +408,101,95314,1,2,0:0:0:0: +443,184,95497,1,6,0:0:0:0: +369,237,95680,2,0,L|300:224,2,70,8|8|2,0:0|0:0|0:0,0:0:0:0: +448,282,96228,5,12,0:0:0:0: +448,282,96411,1,12,0:0:0:0: +270,320,96777,1,8,0:0:0:0: +313,143,97143,1,8,0:0:0:0: +377,314,97509,1,8,0:0:0:0: +256,192,97692,12,0,100619,0:0:0:0: diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index ccfe1501bd..184c09f378 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -64,7 +64,10 @@ namespace osu.Game.Rulesets.Mania.Beatmaps double roundedOverallDifficulty = Math.Round(difficulty.OverallDifficulty); int countSliderOrSpinner = difficulty.TotalObjectCount - difficulty.CircleCount; - float percentSpecialObjects = (float)countSliderOrSpinner / difficulty.TotalObjectCount; + + // In osu!stable, this division appears as if it happens on floats, but due to release-mode + // optimisations, it actually ends up happening on doubles. + double percentSpecialObjects = (double)countSliderOrSpinner / difficulty.TotalObjectCount; if (percentSpecialObjects < 0.2) return 7; From 767d5c8018abb2a8aca3d8aaf8a3259862f976e2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 9 Dec 2023 21:57:34 +0900 Subject: [PATCH 0437/1118] Add object counts to IBeatmapDifficultyInfo --- osu.Game/BackgroundDataStoreProcessor.cs | 38 +++++++++++++++++++ osu.Game/Beatmaps/BeatmapDifficulty.cs | 9 +++++ osu.Game/Beatmaps/BeatmapImporter.cs | 3 ++ osu.Game/Beatmaps/BeatmapUpdater.cs | 20 +++++++++- osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 13 +++++++ osu.Game/Database/RealmAccess.cs | 3 +- .../API/Requests/Responses/APIBeatmap.cs | 2 + .../LegacyBeatmapConversionDifficultyInfo.cs | 1 + 8 files changed, 87 insertions(+), 2 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 90e55dea6d..8195856991 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Screens.Play; +using Realms; namespace osu.Game { @@ -68,6 +69,7 @@ namespace osu.Game checkForOutdatedStarRatings(); processBeatmapSetsWithMissingMetrics(); + processBeatmapsWithMissingObjectCounts(); processScoresWithMissingStatistics(); convertLegacyTotalScoreToStandardised(); }, TaskCreationOptions.LongRunning).ContinueWith(t => @@ -178,6 +180,42 @@ namespace osu.Game } } + private void processBeatmapsWithMissingObjectCounts() + { + Logger.Log("Querying for beatmaps with missing hitobject counts to reprocess..."); + + HashSet beatmapIds = realmAccess.Run(r => new HashSet(r.All() + .Filter($"{nameof(BeatmapInfo.Difficulty)}.{nameof(BeatmapDifficulty.TotalObjectCount)} == 0") + .AsEnumerable().Select(b => b.ID))); + + Logger.Log($"Found {beatmapIds.Count} beatmaps which require reprocessing."); + + int i = 0; + + foreach (var id in beatmapIds) + { + sleepIfRequired(); + + realmAccess.Run(r => + { + var beatmap = r.Find(id); + + if (beatmap != null) + { + try + { + Logger.Log($"Background processing {beatmap} ({++i} / {beatmapIds.Count})"); + beatmapUpdater.ProcessObjectCounts(beatmap); + } + catch (Exception e) + { + Logger.Log($"Background processing failed on {beatmap}: {e}"); + } + } + }); + } + } + private void processScoresWithMissingStatistics() { HashSet scoreIds = new HashSet(); diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index ac2267380d..785728141e 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -21,6 +21,9 @@ namespace osu.Game.Beatmaps public double SliderMultiplier { get; set; } = 1.4; public double SliderTickRate { get; set; } = 1; + public int EndTimeObjectCount { get; set; } + public int TotalObjectCount { get; set; } + public BeatmapDifficulty() { } @@ -44,6 +47,9 @@ namespace osu.Game.Beatmaps difficulty.SliderMultiplier = SliderMultiplier; difficulty.SliderTickRate = SliderTickRate; + + difficulty.EndTimeObjectCount = EndTimeObjectCount; + difficulty.TotalObjectCount = TotalObjectCount; } public virtual void CopyFrom(IBeatmapDifficultyInfo other) @@ -55,6 +61,9 @@ namespace osu.Game.Beatmaps SliderMultiplier = other.SliderMultiplier; SliderTickRate = other.SliderTickRate; + + EndTimeObjectCount = other.EndTimeObjectCount; + TotalObjectCount = other.TotalObjectCount; } } } diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index e89e5339e1..31d6b0108e 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -20,6 +20,7 @@ using osu.Game.IO; using osu.Game.IO.Archives; using osu.Game.Overlays.Notifications; using osu.Game.Rulesets; +using osu.Game.Rulesets.Objects.Types; using Realms; namespace osu.Game.Beatmaps @@ -388,6 +389,8 @@ namespace osu.Game.Beatmaps ApproachRate = decodedDifficulty.ApproachRate, SliderMultiplier = decodedDifficulty.SliderMultiplier, SliderTickRate = decodedDifficulty.SliderTickRate, + EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration), + TotalObjectCount = decoded.HitObjects.Count }; var metadata = new BeatmapMetadata diff --git a/osu.Game/Beatmaps/BeatmapUpdater.cs b/osu.Game/Beatmaps/BeatmapUpdater.cs index 56bfdc5001..472ac26ebe 100644 --- a/osu.Game/Beatmaps/BeatmapUpdater.cs +++ b/osu.Game/Beatmaps/BeatmapUpdater.cs @@ -3,6 +3,7 @@ using System; using System.Diagnostics; +using System.Linq; using System.Threading.Tasks; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Logging; @@ -10,6 +11,7 @@ using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Database; using osu.Game.Online.API; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Beatmaps { @@ -44,7 +46,8 @@ namespace osu.Game.Beatmaps public void Queue(Live beatmapSet, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst) { Logger.Log($"Queueing change for local beatmap {beatmapSet}"); - Task.Factory.StartNew(() => beatmapSet.PerformRead(b => Process(b, lookupScope)), default, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, updateScheduler); + Task.Factory.StartNew(() => beatmapSet.PerformRead(b => Process(b, lookupScope)), default, TaskCreationOptions.HideScheduler | TaskCreationOptions.RunContinuationsAsynchronously, + updateScheduler); } /// @@ -80,6 +83,21 @@ namespace osu.Game.Beatmaps workingBeatmapCache.Invalidate(beatmapSet); }); + public void ProcessObjectCounts(BeatmapInfo beatmapInfo, MetadataLookupScope lookupScope = MetadataLookupScope.LocalCacheFirst) => beatmapInfo.Realm!.Write(_ => + { + // Before we use below, we want to invalidate. + workingBeatmapCache.Invalidate(beatmapInfo); + + var working = workingBeatmapCache.GetWorkingBeatmap(beatmapInfo); + var beatmap = working.Beatmap; + + beatmapInfo.Difficulty.EndTimeObjectCount = beatmap.HitObjects.Count(h => h is IHasDuration); + beatmapInfo.Difficulty.TotalObjectCount = beatmap.HitObjects.Count; + + // And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required. + workingBeatmapCache.Invalidate(beatmapInfo); + }); + #region Implementation of IDisposable public void Dispose() diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index e7a3d87d0a..47b261d1f6 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -44,6 +44,19 @@ namespace osu.Game.Beatmaps /// double SliderTickRate { get; } + /// + /// The number of hitobjects in the beatmap with a distinct end time. + /// + /// + /// Canonically, these are hitobjects are either sliders or spinners. + /// + int EndTimeObjectCount { get; } + + /// + /// The total number of hitobjects in the beatmap. + /// + int TotalObjectCount { get; } + /// /// Maps a difficulty value [0, 10] to a two-piece linear range of values. /// diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index e9f49ec662..9c7fe464dd 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -88,8 +88,9 @@ namespace osu.Game.Database /// 34 2023-08-21 Add BackgroundReprocessingFailed flag to ScoreInfo to track upgrade failures. /// 35 2023-10-16 Clear key combinations of keybindings that are assigned to more than one action in a given settings section. /// 36 2023-10-26 Add LegacyOnlineID to ScoreInfo. Move osu_scores_*_high IDs stored in OnlineID to LegacyOnlineID. Reset anomalous OnlineIDs. + /// 37 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapDifficulty. /// - private const int schema_version = 36; + private const int schema_version = 37; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index 902b651be9..c1ceff7c43 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -109,6 +109,8 @@ namespace osu.Game.Online.API.Requests.Responses CircleSize = CircleSize, ApproachRate = ApproachRate, OverallDifficulty = OverallDifficulty, + EndTimeObjectCount = SliderCount + SpinnerCount, + TotalObjectCount = CircleCount + SliderCount + SpinnerCount }; IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet; diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs index 97ccf787af..9c9294417f 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs @@ -45,6 +45,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy float IBeatmapDifficultyInfo.ApproachRate => 0; double IBeatmapDifficultyInfo.SliderMultiplier => 0; double IBeatmapDifficultyInfo.SliderTickRate => 0; + int IBeatmapDifficultyInfo.EndTimeObjectCount => TotalObjectCount - CircleCount; public static LegacyBeatmapConversionDifficultyInfo FromAPIBeatmap(APIBeatmap apiBeatmap) => new LegacyBeatmapConversionDifficultyInfo { From b36db3518c79486a94fc97411fbf340fd3741a38 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 9 Dec 2023 22:09:49 +0900 Subject: [PATCH 0438/1118] Add keycount to song select details panel and carousel panels --- .../Beatmaps/ManiaBeatmapConverter.cs | 13 ++----- .../ManiaFilterCriteria.cs | 3 +- osu.Game.Rulesets.Mania/ManiaRuleset.cs | 3 ++ osu.Game/Rulesets/ILegacyRuleset.cs | 7 ++++ .../LegacyBeatmapConversionDifficultyInfo.cs | 28 +++++--------- .../Carousel/DrawableCarouselBeatmap.cs | 37 +++++++++++++++++++ .../Screens/Select/Details/AdvancedStats.cs | 16 ++++++-- 7 files changed, 74 insertions(+), 33 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index ccfe1501bd..c4a8db92ed 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -57,10 +57,11 @@ namespace osu.Game.Rulesets.Mania.Beatmaps public static int GetColumnCount(LegacyBeatmapConversionDifficultyInfo difficulty) { - if (new ManiaRuleset().RulesetInfo.Equals(difficulty.SourceRuleset)) - return GetColumnCountForNonConvert(difficulty); - double roundedCircleSize = Math.Round(difficulty.CircleSize); + + if (new ManiaRuleset().RulesetInfo.Equals(difficulty.SourceRuleset)) + return (int)Math.Max(1, roundedCircleSize); + double roundedOverallDifficulty = Math.Round(difficulty.OverallDifficulty); int countSliderOrSpinner = difficulty.TotalObjectCount - difficulty.CircleCount; @@ -76,12 +77,6 @@ namespace osu.Game.Rulesets.Mania.Beatmaps return Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7)); } - public static int GetColumnCountForNonConvert(IBeatmapDifficultyInfo difficulty) - { - double roundedCircleSize = Math.Round(difficulty.CircleSize); - return (int)Math.Max(1, roundedCircleSize); - } - public override bool CanConvert() => Beatmap.HitObjects.All(h => h is IHasXPosition); protected override Beatmap ConvertBeatmap(IBeatmap original, CancellationToken cancellationToken) diff --git a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs index 7f8a00bf88..930ca217cd 100644 --- a/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs +++ b/osu.Game.Rulesets.Mania/ManiaFilterCriteria.cs @@ -4,6 +4,7 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Filter; using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; @@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Mania public bool Matches(BeatmapInfo beatmapInfo) { - return !keys.HasFilter || (beatmapInfo.Ruleset.OnlineID == new ManiaRuleset().LegacyID && keys.IsInRange(ManiaBeatmapConverter.GetColumnCountForNonConvert(beatmapInfo.Difficulty))); + return !keys.HasFilter || keys.IsInRange(ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo))); } public bool TryParseCustomKeywordCriteria(string key, Operator op, string value) diff --git a/osu.Game.Rulesets.Mania/ManiaRuleset.cs b/osu.Game.Rulesets.Mania/ManiaRuleset.cs index 0c317e0f8a..c38d6519bd 100644 --- a/osu.Game.Rulesets.Mania/ManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/ManiaRuleset.cs @@ -420,6 +420,9 @@ namespace osu.Game.Rulesets.Mania public override RulesetSetupSection CreateEditorSetupSection() => new ManiaSetupSection(); public override DifficultySection CreateEditorDifficultySection() => new ManiaDifficultySection(); + + public int GetKeyCount(IBeatmapInfo beatmapInfo) + => ManiaBeatmapConverter.GetColumnCount(LegacyBeatmapConversionDifficultyInfo.FromBeatmapInfo(beatmapInfo)); } public enum PlayfieldType diff --git a/osu.Game/Rulesets/ILegacyRuleset.cs b/osu.Game/Rulesets/ILegacyRuleset.cs index 6900afa243..18d86f477a 100644 --- a/osu.Game/Rulesets/ILegacyRuleset.cs +++ b/osu.Game/Rulesets/ILegacyRuleset.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Beatmaps; using osu.Game.Rulesets.Scoring.Legacy; namespace osu.Game.Rulesets @@ -14,6 +15,12 @@ namespace osu.Game.Rulesets /// int LegacyID { get; } + /// + /// Retrieves the number of mania keys required to play the beatmap. + /// + /// + int GetKeyCount(IBeatmapInfo beatmapInfo) => 0; + ILegacyScoreSimulator CreateLegacyScoreSimulator(); } } diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs index 9c9294417f..6f379e4ef1 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs @@ -1,10 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Linq; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Scoring.Legacy { @@ -31,9 +29,6 @@ namespace osu.Game.Rulesets.Scoring.Legacy /// /// The count of hitcircles in the beatmap. /// - /// - /// When converting from osu! ruleset beatmaps, this is equivalent to the sum of sliders and spinners in the beatmap. - /// public int CircleCount { get; set; } /// @@ -47,22 +42,17 @@ namespace osu.Game.Rulesets.Scoring.Legacy double IBeatmapDifficultyInfo.SliderTickRate => 0; int IBeatmapDifficultyInfo.EndTimeObjectCount => TotalObjectCount - CircleCount; - public static LegacyBeatmapConversionDifficultyInfo FromAPIBeatmap(APIBeatmap apiBeatmap) => new LegacyBeatmapConversionDifficultyInfo - { - SourceRuleset = apiBeatmap.Ruleset, - CircleSize = apiBeatmap.CircleSize, - OverallDifficulty = apiBeatmap.OverallDifficulty, - CircleCount = apiBeatmap.CircleCount, - TotalObjectCount = apiBeatmap.SliderCount + apiBeatmap.SpinnerCount + apiBeatmap.CircleCount - }; + public static LegacyBeatmapConversionDifficultyInfo FromAPIBeatmap(APIBeatmap apiBeatmap) => FromBeatmapInfo(apiBeatmap); - public static LegacyBeatmapConversionDifficultyInfo FromBeatmap(IBeatmap beatmap) => new LegacyBeatmapConversionDifficultyInfo + public static LegacyBeatmapConversionDifficultyInfo FromBeatmap(IBeatmap beatmap) => FromBeatmapInfo(beatmap.BeatmapInfo); + + public static LegacyBeatmapConversionDifficultyInfo FromBeatmapInfo(IBeatmapInfo beatmapInfo) => new LegacyBeatmapConversionDifficultyInfo { - SourceRuleset = beatmap.BeatmapInfo.Ruleset, - CircleSize = beatmap.Difficulty.CircleSize, - OverallDifficulty = beatmap.Difficulty.OverallDifficulty, - CircleCount = beatmap.HitObjects.Count(h => h is not IHasDuration), - TotalObjectCount = beatmap.HitObjects.Count + SourceRuleset = beatmapInfo.Ruleset, + CircleSize = beatmapInfo.Difficulty.CircleSize, + OverallDifficulty = beatmapInfo.Difficulty.OverallDifficulty, + CircleCount = beatmapInfo.Difficulty.TotalObjectCount - beatmapInfo.Difficulty.EndTimeObjectCount, + TotalObjectCount = beatmapInfo.Difficulty.TotalObjectCount }; } } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 3dfd801f02..cda95a9d29 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -27,6 +27,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Resources.Localisation.Web; +using osu.Game.Rulesets; using osuTK; using osuTK.Graphics; @@ -57,6 +58,8 @@ namespace osu.Game.Screens.Select.Carousel private StarCounter starCounter = null!; private DifficultyIcon difficultyIcon = null!; + private OsuSpriteText keyCountText = null!; + [Resolved] private BeatmapSetOverlay? beatmapOverlay { get; set; } @@ -69,6 +72,9 @@ namespace osu.Game.Screens.Select.Carousel [Resolved] private RealmAccess realm { get; set; } = null!; + [Resolved] + private IBindable ruleset { get; set; } = null!; + private IBindable starDifficultyBindable = null!; private CancellationTokenSource? starDifficultyCancellationSource; @@ -133,6 +139,13 @@ namespace osu.Game.Screens.Select.Carousel AutoSizeAxes = Axes.Both, Children = new[] { + keyCountText = new OsuSpriteText + { + Font = OsuFont.GetFont(size: 20), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + Alpha = 0, + }, new OsuSpriteText { Text = beatmapInfo.DifficultyName, @@ -167,6 +180,13 @@ namespace osu.Game.Screens.Select.Carousel }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + ruleset.BindValueChanged(_ => updateKeyCount()); + } + protected override void Selected() { base.Selected(); @@ -216,11 +236,28 @@ namespace osu.Game.Screens.Select.Carousel if (d.NewValue != null) difficultyIcon.Current.Value = d.NewValue.Value; }, true); + + updateKeyCount(); } base.ApplyState(); } + private void updateKeyCount() + { + if (ruleset.Value.OnlineID == 3) + { + // Account for mania differences locally for now. + // Eventually this should be handled in a more modular way, allowing rulesets to add more information to the panel. + ILegacyRuleset legacyRuleset = (ILegacyRuleset)ruleset.Value.CreateInstance(); + + keyCountText.Alpha = 1; + keyCountText.Text = $"[{legacyRuleset.GetKeyCount(beatmapInfo)}K]"; + } + else + keyCountText.Alpha = 0; + } + public MenuItem[] ContextMenuItems { get diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index a383298faa..87185c351e 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -126,13 +126,21 @@ namespace osu.Game.Screens.Select.Details mod.ApplyToDifficulty(adjustedDifficulty); } - switch (BeatmapInfo?.Ruleset.OnlineID) + switch (gameRuleset.Value.OnlineID) { case 3: - // Account for mania differences locally for now - // Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes + // Account for mania differences locally for now. + // Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes. + ILegacyRuleset legacyRuleset = (ILegacyRuleset)gameRuleset.Value.CreateInstance(); + + // For the time being, the key count is static no matter what, because: + // a) The method doesn't have knowledge of the active keymods. Doing so may require considerations for filtering. + // b) Using the difficulty adjustment mod to adjust OD doesn't have an effect on conversion. + int keyCount = baseDifficulty == null ? 0 : legacyRuleset.GetKeyCount(BeatmapInfo); + FirstValue.Title = BeatmapsetsStrings.ShowStatsCsMania; - FirstValue.Value = (baseDifficulty?.CircleSize ?? 0, null); + FirstValue.Value = (keyCount, keyCount); + break; default: From 1d0c37e13875b57b4f8dfaf2f0b4ab299b222a15 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 10 Dec 2023 00:40:05 +0200 Subject: [PATCH 0439/1118] fixed test errors --- osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs | 10 ++++++---- osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs | 6 +++--- osu.Game/Screens/Select/Details/AdvancedStats.cs | 8 -------- 3 files changed, 9 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs index 3e800bfaf1..5314c6f4a9 100644 --- a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -17,11 +17,11 @@ namespace osu.Game.Overlays.Mods { public partial class AdjustedAttributesTooltip : CompositeDrawable, ITooltip { - private Dictionary> attributes = new Dictionary>(); + private readonly Dictionary> attributes = new Dictionary>(); - private Container content; + private readonly Container content; - private FillFlowContainer attributesFillFlow; + private readonly FillFlowContainer attributesFillFlow; [Resolved] private OsuColour colours { get; set; } = null!; @@ -83,8 +83,8 @@ namespace osu.Game.Overlays.Mods return; } } - content.Hide(); + content.Hide(); } public void AddAttribute(string name) @@ -97,6 +97,8 @@ namespace osu.Game.Overlays.Mods public void UpdateAttribute(string name, double oldValue, double newValue) { + if (!attributes.ContainsKey(name)) return; + Bindable attribute = attributes[name]; OldNewPair attributeValue = attribute.Value; diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index fa8f9cb7a4..d74f088ac2 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -104,6 +104,9 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); + rateAdjustTooltip.AddAttribute("AR"); + rateAdjustTooltip.AddAttribute("OD"); + mods.BindValueChanged(_ => { modSettingChangeTracker?.Dispose(); @@ -126,9 +129,6 @@ namespace osu.Game.Overlays.Mods BeatmapInfo.BindValueChanged(_ => updateValues(), true); - rateAdjustTooltip.AddAttribute("AR"); - rateAdjustTooltip.AddAttribute("OD"); - updateCollapsedState(); } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index d3a6a88c68..30cb0601f3 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -214,14 +214,6 @@ namespace osu.Game.Screens.Select.Details starDifficultyCancellationSource?.Cancel(); } - private static bool hasRateAdjustedProperties(BeatmapDifficulty a, BeatmapDifficulty b) - { - if (!Precision.AlmostEquals(a.ApproachRate, b.ApproachRate)) return true; - if (!Precision.AlmostEquals(a.OverallDifficulty, b.OverallDifficulty)) return true; - - return false; - } - public partial class StatisticRow : Container, IHasAccentColour { private const float value_width = 25; From 78cdedf34d1f0c24b5b1f7d342c58fc96d27e18a Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 10 Dec 2023 00:42:02 +0200 Subject: [PATCH 0440/1118] Update BeatmapAttributesDisplay.cs --- osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index d74f088ac2..0c35e55df5 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -62,7 +62,6 @@ namespace osu.Game.Overlays.Mods public ITooltip GetCustomTooltip() => rateAdjustTooltip; public object TooltipContent => this; - private const float transition_duration = 250; [BackgroundDependencyLoader] From f5b93121f1194591aab5d538062e48e7315f6589 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 10 Dec 2023 00:51:50 +0200 Subject: [PATCH 0441/1118] Update AdjustedAttributesTooltip.cs --- osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs index 5314c6f4a9..e9b7ee5c54 100644 --- a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -111,6 +111,7 @@ namespace osu.Game.Overlays.Mods protected override void Update() { } + public void SetContent(object content) { } @@ -128,12 +129,13 @@ namespace osu.Game.Overlays.Mods private partial class AttributeDisplay : CompositeDrawable { public readonly Bindable AttributeValues; - public string AttributeName; + public readonly string AttributeName; - private OsuSpriteText text = new OsuSpriteText + private readonly OsuSpriteText text = new OsuSpriteText { Font = OsuFont.Default.With(weight: FontWeight.Bold) }; + public AttributeDisplay(string name, Bindable boundCopy) { AutoSizeAxes = Axes.Both; From 2d94841929aae312a09b81c8aee45d2befabcb27 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Sun, 10 Dec 2023 02:00:32 +0200 Subject: [PATCH 0442/1118] fixed one test --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 30cb0601f3..819fe122fa 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -128,7 +128,8 @@ namespace osu.Game.Screens.Select.Details IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; BeatmapDifficulty adjustedDifficulty = null; - if (baseDifficulty != null) + if (baseDifficulty != null && + (mods.Value.Any(m => m is IApplicableToDifficulty) || mods.Value.Any(m => m is IApplicableToRate))) { BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty); From 6320194e19510bcd7cb26ad69b84a492fb0db6ab Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 11 Dec 2023 21:52:39 +0900 Subject: [PATCH 0443/1118] Fix catch applying positional clamping too early --- .../CatchBeatmapConversionTest.cs | 1 + .../Beatmaps/112643-expected-conversion.json | 1 + .../Resources/Testing/Beatmaps/112643.osu | 582 ++++++++++++++++++ .../Beatmaps/CatchBeatmapProcessor.cs | 12 + .../Objects/JuiceStream.cs | 12 +- 5 files changed, 600 insertions(+), 8 deletions(-) create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/112643-expected-conversion.json create mode 100644 osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/112643.osu diff --git a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs index 7572c6670f..d0ecb828df 100644 --- a/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs +++ b/osu.Game.Rulesets.Catch.Tests/CatchBeatmapConversionTest.cs @@ -52,6 +52,7 @@ namespace osu.Game.Rulesets.Catch.Tests [TestCase("3644427", new[] { typeof(CatchModEasy), typeof(CatchModFlashlight) })] [TestCase("3689906", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })] [TestCase("3949367", new[] { typeof(CatchModDoubleTime), typeof(CatchModEasy) })] + [TestCase("112643")] public new void Test(string name, params Type[] mods) => base.Test(name, mods); protected override IEnumerable CreateConvertValue(HitObject hitObject) diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/112643-expected-conversion.json b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/112643-expected-conversion.json new file mode 100644 index 0000000000..7d6e29b6c1 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/112643-expected-conversion.json @@ -0,0 +1 @@ +{"Mappings":[{"StartTime":2375.0,"Objects":[{"StartTime":2375.0,"Position":64.0,"HyperDash":false}]},{"StartTime":2625.0,"Objects":[{"StartTime":2625.0,"Position":172.0,"HyperDash":false}]},{"StartTime":2875.0,"Objects":[{"StartTime":2875.0,"Position":152.0,"HyperDash":false}]},{"StartTime":3125.0,"Objects":[{"StartTime":3125.0,"Position":80.0,"HyperDash":false}]},{"StartTime":3375.0,"Objects":[{"StartTime":3375.0,"Position":224.0,"HyperDash":false}]},{"StartTime":3625.0,"Objects":[{"StartTime":3625.0,"Position":192.0,"HyperDash":false}]},{"StartTime":3875.0,"Objects":[{"StartTime":3875.0,"Position":136.0,"HyperDash":false}]},{"StartTime":4125.0,"Objects":[{"StartTime":4125.0,"Position":272.0,"HyperDash":false},{"StartTime":4187.0,"Position":295.965057,"HyperDash":false},{"StartTime":4250.0,"Position":339.30658,"HyperDash":false},{"StartTime":4312.0,"Position":372.55603,"HyperDash":false},{"StartTime":4375.0,"Position":372.509583,"HyperDash":false},{"StartTime":4437.0,"Position":372.203644,"HyperDash":false},{"StartTime":4500.0,"Position":340.885864,"HyperDash":false},{"StartTime":4562.0,"Position":348.843384,"HyperDash":false},{"StartTime":4625.0,"Position":384.566772,"HyperDash":false},{"StartTime":4749.0,"Position":462.643433,"HyperDash":false}]},{"StartTime":4875.0,"Objects":[{"StartTime":4875.0,"Position":504.0,"HyperDash":false},{"StartTime":4937.0,"Position":456.809235,"HyperDash":false},{"StartTime":5000.0,"Position":413.577362,"HyperDash":false},{"StartTime":5062.0,"Position":384.032623,"HyperDash":false},{"StartTime":5125.0,"Position":351.76297,"HyperDash":false},{"StartTime":5178.0,"Position":327.56488,"HyperDash":false},{"StartTime":5232.0,"Position":288.905457,"HyperDash":false},{"StartTime":5285.0,"Position":281.458923,"HyperDash":false},{"StartTime":5375.0,"Position":249.3499,"HyperDash":false}]},{"StartTime":5625.0,"Objects":[{"StartTime":5625.0,"Position":384.0,"HyperDash":false}]},{"StartTime":5875.0,"Objects":[{"StartTime":5875.0,"Position":272.0,"HyperDash":false}]},{"StartTime":6000.0,"Objects":[{"StartTime":6000.0,"Position":272.0,"HyperDash":false}]},{"StartTime":6125.0,"Objects":[{"StartTime":6125.0,"Position":272.0,"HyperDash":false}]},{"StartTime":6375.0,"Objects":[{"StartTime":6375.0,"Position":92.0,"HyperDash":false}]},{"StartTime":6625.0,"Objects":[{"StartTime":6625.0,"Position":124.0,"HyperDash":false}]},{"StartTime":6875.0,"Objects":[{"StartTime":6875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":7125.0,"Objects":[{"StartTime":7125.0,"Position":388.0,"HyperDash":false}]},{"StartTime":7375.0,"Objects":[{"StartTime":7375.0,"Position":420.0,"HyperDash":false}]},{"StartTime":7625.0,"Objects":[{"StartTime":7625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":7875.0,"Objects":[{"StartTime":7875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":8125.0,"Objects":[{"StartTime":8125.0,"Position":443.0,"HyperDash":false},{"StartTime":8187.0,"Position":392.598877,"HyperDash":false},{"StartTime":8250.0,"Position":365.1502,"HyperDash":false},{"StartTime":8312.0,"Position":352.954926,"HyperDash":false},{"StartTime":8375.0,"Position":294.614716,"HyperDash":false},{"StartTime":8437.0,"Position":268.171936,"HyperDash":false},{"StartTime":8500.0,"Position":207.09552,"HyperDash":false},{"StartTime":8562.0,"Position":158.395874,"HyperDash":false},{"StartTime":8625.0,"Position":135.590256,"HyperDash":false},{"StartTime":8749.0,"Position":67.66239,"HyperDash":false}]},{"StartTime":8875.0,"Objects":[{"StartTime":8875.0,"Position":24.0,"HyperDash":false},{"StartTime":8937.0,"Position":54.41505,"HyperDash":false},{"StartTime":9000.0,"Position":92.0854,"HyperDash":false},{"StartTime":9062.0,"Position":91.62684,"HyperDash":false},{"StartTime":9125.0,"Position":114.961037,"HyperDash":false},{"StartTime":9178.0,"Position":112.725426,"HyperDash":false},{"StartTime":9232.0,"Position":118.526962,"HyperDash":false},{"StartTime":9285.0,"Position":72.53759,"HyperDash":false},{"StartTime":9374.0,"Position":43.35332,"HyperDash":false}]},{"StartTime":9625.0,"Objects":[{"StartTime":9625.0,"Position":16.0,"HyperDash":false}]},{"StartTime":9875.0,"Objects":[{"StartTime":9875.0,"Position":136.0,"HyperDash":false}]},{"StartTime":10000.0,"Objects":[{"StartTime":10000.0,"Position":136.0,"HyperDash":false}]},{"StartTime":10125.0,"Objects":[{"StartTime":10125.0,"Position":136.0,"HyperDash":false}]},{"StartTime":10375.0,"Objects":[{"StartTime":10375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":10625.0,"Objects":[{"StartTime":10625.0,"Position":368.0,"HyperDash":false}]},{"StartTime":10875.0,"Objects":[{"StartTime":10875.0,"Position":196.0,"HyperDash":false}]},{"StartTime":11125.0,"Objects":[{"StartTime":11125.0,"Position":316.0,"HyperDash":false}]},{"StartTime":11375.0,"Objects":[{"StartTime":11375.0,"Position":144.0,"HyperDash":false}]},{"StartTime":11625.0,"Objects":[{"StartTime":11625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":11875.0,"Objects":[{"StartTime":11875.0,"Position":112.0,"HyperDash":false}]},{"StartTime":12125.0,"Objects":[{"StartTime":12125.0,"Position":164.0,"HyperDash":false},{"StartTime":12250.0,"Position":238.49942,"HyperDash":false}]},{"StartTime":12500.0,"Objects":[{"StartTime":12500.0,"Position":100.0,"HyperDash":false},{"StartTime":12625.0,"Position":25.50058,"HyperDash":false}]},{"StartTime":12875.0,"Objects":[{"StartTime":12875.0,"Position":144.0,"HyperDash":false},{"StartTime":13000.0,"Position":69.50058,"HyperDash":false}]},{"StartTime":13250.0,"Objects":[{"StartTime":13250.0,"Position":208.0,"HyperDash":false},{"StartTime":13375.0,"Position":282.49942,"HyperDash":false}]},{"StartTime":13625.0,"Objects":[{"StartTime":13625.0,"Position":332.0,"HyperDash":false}]},{"StartTime":13875.0,"Objects":[{"StartTime":13875.0,"Position":180.0,"HyperDash":false}]},{"StartTime":14125.0,"Objects":[{"StartTime":14125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":14250.0,"Objects":[{"StartTime":14250.0,"Position":256.0,"HyperDash":false}]},{"StartTime":14500.0,"Objects":[{"StartTime":14500.0,"Position":324.0,"HyperDash":false}]},{"StartTime":14625.0,"Objects":[{"StartTime":14625.0,"Position":324.0,"HyperDash":false}]},{"StartTime":14875.0,"Objects":[{"StartTime":14875.0,"Position":192.0,"HyperDash":false}]},{"StartTime":15000.0,"Objects":[{"StartTime":15000.0,"Position":192.0,"HyperDash":false}]},{"StartTime":15250.0,"Objects":[{"StartTime":15250.0,"Position":256.0,"HyperDash":false}]},{"StartTime":15375.0,"Objects":[{"StartTime":15375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":15625.0,"Objects":[{"StartTime":15625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":15875.0,"Objects":[{"StartTime":15875.0,"Position":120.0,"HyperDash":false}]},{"StartTime":16125.0,"Objects":[{"StartTime":16125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":18375.0,"Objects":[{"StartTime":18375.0,"Position":20.0,"HyperDash":false}]},{"StartTime":18625.0,"Objects":[{"StartTime":18625.0,"Position":180.0,"HyperDash":false}]},{"StartTime":18875.0,"Objects":[{"StartTime":18875.0,"Position":52.0,"HyperDash":false}]},{"StartTime":19125.0,"Objects":[{"StartTime":19125.0,"Position":120.0,"HyperDash":false}]},{"StartTime":19375.0,"Objects":[{"StartTime":19375.0,"Position":128.0,"HyperDash":false}]},{"StartTime":19625.0,"Objects":[{"StartTime":19625.0,"Position":48.0,"HyperDash":false}]},{"StartTime":19875.0,"Objects":[{"StartTime":19875.0,"Position":192.0,"HyperDash":false}]},{"StartTime":20125.0,"Objects":[{"StartTime":20125.0,"Position":300.0,"HyperDash":false},{"StartTime":20187.0,"Position":319.510284,"HyperDash":false},{"StartTime":20250.0,"Position":361.959717,"HyperDash":false},{"StartTime":20312.0,"Position":410.823639,"HyperDash":false},{"StartTime":20375.0,"Position":393.9937,"HyperDash":false},{"StartTime":20428.0,"Position":389.407,"HyperDash":false},{"StartTime":20482.0,"Position":394.563232,"HyperDash":false},{"StartTime":20535.0,"Position":430.098541,"HyperDash":false},{"StartTime":20624.0,"Position":486.9303,"HyperDash":false}]},{"StartTime":20875.0,"Objects":[{"StartTime":20875.0,"Position":472.0,"HyperDash":false},{"StartTime":20937.0,"Position":454.614349,"HyperDash":false},{"StartTime":21000.0,"Position":395.812744,"HyperDash":false},{"StartTime":21062.0,"Position":377.009979,"HyperDash":false},{"StartTime":21125.0,"Position":345.3677,"HyperDash":false},{"StartTime":21178.0,"Position":342.8652,"HyperDash":false},{"StartTime":21232.0,"Position":325.856567,"HyperDash":false},{"StartTime":21285.0,"Position":310.223846,"HyperDash":false},{"StartTime":21374.0,"Position":280.7244,"HyperDash":false}]},{"StartTime":21625.0,"Objects":[{"StartTime":21625.0,"Position":404.0,"HyperDash":false}]},{"StartTime":21875.0,"Objects":[{"StartTime":21875.0,"Position":432.0,"HyperDash":false}]},{"StartTime":22000.0,"Objects":[{"StartTime":22000.0,"Position":432.0,"HyperDash":false}]},{"StartTime":22125.0,"Objects":[{"StartTime":22125.0,"Position":432.0,"HyperDash":false}]},{"StartTime":22375.0,"Objects":[{"StartTime":22375.0,"Position":296.0,"HyperDash":false}]},{"StartTime":22625.0,"Objects":[{"StartTime":22625.0,"Position":168.0,"HyperDash":false},{"StartTime":22678.0,"Position":157.672318,"HyperDash":false},{"StartTime":22732.0,"Position":121.82901,"HyperDash":false},{"StartTime":22785.0,"Position":68.50134,"HyperDash":false},{"StartTime":22875.0,"Position":39.09584,"HyperDash":false}]},{"StartTime":23125.0,"Objects":[{"StartTime":23125.0,"Position":268.0,"HyperDash":false},{"StartTime":23178.0,"Position":252.906113,"HyperDash":false},{"StartTime":23232.0,"Position":215.4331,"HyperDash":false},{"StartTime":23285.0,"Position":192.339218,"HyperDash":false},{"StartTime":23375.0,"Position":173.217529,"HyperDash":false}]},{"StartTime":23625.0,"Objects":[{"StartTime":23625.0,"Position":252.0,"HyperDash":false},{"StartTime":23678.0,"Position":297.327667,"HyperDash":false},{"StartTime":23732.0,"Position":299.171,"HyperDash":false},{"StartTime":23785.0,"Position":350.498657,"HyperDash":false},{"StartTime":23875.0,"Position":380.904175,"HyperDash":false}]},{"StartTime":24125.0,"Objects":[{"StartTime":24125.0,"Position":484.0,"HyperDash":false},{"StartTime":24187.0,"Position":459.330444,"HyperDash":false},{"StartTime":24250.0,"Position":410.3108,"HyperDash":false},{"StartTime":24312.0,"Position":381.927948,"HyperDash":false},{"StartTime":24375.0,"Position":342.702942,"HyperDash":false},{"StartTime":24437.0,"Position":307.727,"HyperDash":false},{"StartTime":24500.0,"Position":254.618744,"HyperDash":false},{"StartTime":24562.0,"Position":219.823792,"HyperDash":false},{"StartTime":24625.0,"Position":195.842667,"HyperDash":false},{"StartTime":24750.0,"Position":124.114441,"HyperDash":false}]},{"StartTime":24875.0,"Objects":[{"StartTime":24875.0,"Position":72.0,"HyperDash":false},{"StartTime":24937.0,"Position":90.6446,"HyperDash":false},{"StartTime":25000.0,"Position":102.976662,"HyperDash":false},{"StartTime":25062.0,"Position":121.259918,"HyperDash":false},{"StartTime":25125.0,"Position":115.072632,"HyperDash":false},{"StartTime":25178.0,"Position":104.017952,"HyperDash":false},{"StartTime":25232.0,"Position":66.87554,"HyperDash":false},{"StartTime":25285.0,"Position":53.7148743,"HyperDash":false},{"StartTime":25374.0,"Position":0.0,"HyperDash":false}]},{"StartTime":25625.0,"Objects":[{"StartTime":25625.0,"Position":56.0,"HyperDash":false}]},{"StartTime":25875.0,"Objects":[{"StartTime":25875.0,"Position":176.0,"HyperDash":false}]},{"StartTime":26000.0,"Objects":[{"StartTime":26000.0,"Position":176.0,"HyperDash":false}]},{"StartTime":26125.0,"Objects":[{"StartTime":26125.0,"Position":176.0,"HyperDash":false}]},{"StartTime":26375.0,"Objects":[{"StartTime":26375.0,"Position":316.0,"HyperDash":false}]},{"StartTime":26625.0,"Objects":[{"StartTime":26625.0,"Position":464.0,"HyperDash":false},{"StartTime":26678.0,"Position":423.678864,"HyperDash":false},{"StartTime":26732.0,"Position":428.026764,"HyperDash":false},{"StartTime":26785.0,"Position":431.558746,"HyperDash":false},{"StartTime":26875.0,"Position":408.8022,"HyperDash":false}]},{"StartTime":27125.0,"Objects":[{"StartTime":27125.0,"Position":232.0,"HyperDash":false},{"StartTime":27178.0,"Position":266.0937,"HyperDash":false},{"StartTime":27232.0,"Position":284.472229,"HyperDash":false},{"StartTime":27285.0,"Position":289.223022,"HyperDash":false},{"StartTime":27374.0,"Position":288.2113,"HyperDash":false}]},{"StartTime":27625.0,"Objects":[{"StartTime":27625.0,"Position":136.0,"HyperDash":false}]},{"StartTime":27875.0,"Objects":[{"StartTime":27875.0,"Position":60.0,"HyperDash":false}]},{"StartTime":28125.0,"Objects":[{"StartTime":28125.0,"Position":212.0,"HyperDash":false},{"StartTime":28250.0,"Position":244.219086,"HyperDash":false}]},{"StartTime":28500.0,"Objects":[{"StartTime":28500.0,"Position":340.0,"HyperDash":false},{"StartTime":28625.0,"Position":372.2191,"HyperDash":false}]},{"StartTime":28875.0,"Objects":[{"StartTime":28875.0,"Position":256.0,"HyperDash":false},{"StartTime":29000.0,"Position":223.780914,"HyperDash":false}]},{"StartTime":29250.0,"Objects":[{"StartTime":29250.0,"Position":128.0,"HyperDash":false},{"StartTime":29375.0,"Position":95.7809143,"HyperDash":false}]},{"StartTime":29625.0,"Objects":[{"StartTime":29625.0,"Position":238.0,"HyperDash":false},{"StartTime":29678.0,"Position":279.04657,"HyperDash":false},{"StartTime":29731.0,"Position":322.09314,"HyperDash":false},{"StartTime":29784.0,"Position":325.1397,"HyperDash":false},{"StartTime":29874.0,"Position":397.954651,"HyperDash":false}]},{"StartTime":30125.0,"Objects":[{"StartTime":30125.0,"Position":512.0,"HyperDash":false}]},{"StartTime":30250.0,"Objects":[{"StartTime":30250.0,"Position":512.0,"HyperDash":false}]},{"StartTime":30500.0,"Objects":[{"StartTime":30500.0,"Position":416.0,"HyperDash":false}]},{"StartTime":30625.0,"Objects":[{"StartTime":30625.0,"Position":416.0,"HyperDash":false}]},{"StartTime":30875.0,"Objects":[{"StartTime":30875.0,"Position":300.0,"HyperDash":false}]},{"StartTime":31000.0,"Objects":[{"StartTime":31000.0,"Position":300.0,"HyperDash":false}]},{"StartTime":31250.0,"Objects":[{"StartTime":31250.0,"Position":236.0,"HyperDash":false}]},{"StartTime":31375.0,"Objects":[{"StartTime":31375.0,"Position":236.0,"HyperDash":false}]},{"StartTime":31625.0,"Objects":[{"StartTime":31625.0,"Position":152.0,"HyperDash":false}]},{"StartTime":31875.0,"Objects":[{"StartTime":31875.0,"Position":300.0,"HyperDash":false}]},{"StartTime":32125.0,"Objects":[{"StartTime":32125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":34625.0,"Objects":[{"StartTime":34625.0,"Position":52.0,"HyperDash":false}]},{"StartTime":34875.0,"Objects":[{"StartTime":34875.0,"Position":152.0,"HyperDash":false}]},{"StartTime":35125.0,"Objects":[{"StartTime":35125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":35625.0,"Objects":[{"StartTime":35625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":36125.0,"Objects":[{"StartTime":36125.0,"Position":256.0,"HyperDash":false},{"StartTime":36178.0,"Position":285.74295,"HyperDash":false},{"StartTime":36232.0,"Position":306.695557,"HyperDash":false},{"StartTime":36285.0,"Position":338.9461,"HyperDash":false},{"StartTime":36375.0,"Position":338.0262,"HyperDash":false}]},{"StartTime":36625.0,"Objects":[{"StartTime":36625.0,"Position":320.0,"HyperDash":false}]},{"StartTime":36875.0,"Objects":[{"StartTime":36875.0,"Position":204.0,"HyperDash":false}]},{"StartTime":37125.0,"Objects":[{"StartTime":37125.0,"Position":104.0,"HyperDash":false},{"StartTime":37178.0,"Position":84.88513,"HyperDash":false},{"StartTime":37232.0,"Position":58.02897,"HyperDash":false},{"StartTime":37285.0,"Position":32.3897247,"HyperDash":false},{"StartTime":37375.0,"Position":42.93435,"HyperDash":false}]},{"StartTime":37625.0,"Objects":[{"StartTime":37625.0,"Position":92.0,"HyperDash":false}]},{"StartTime":37875.0,"Objects":[{"StartTime":37875.0,"Position":212.0,"HyperDash":false}]},{"StartTime":38000.0,"Objects":[{"StartTime":38000.0,"Position":268.0,"HyperDash":false}]},{"StartTime":38125.0,"Objects":[{"StartTime":38125.0,"Position":324.0,"HyperDash":false},{"StartTime":38178.0,"Position":338.3627,"HyperDash":false},{"StartTime":38232.0,"Position":380.1851,"HyperDash":false},{"StartTime":38285.0,"Position":411.5478,"HyperDash":false},{"StartTime":38375.0,"Position":438.918457,"HyperDash":false}]},{"StartTime":38625.0,"Objects":[{"StartTime":38625.0,"Position":504.0,"HyperDash":false}]},{"StartTime":38875.0,"Objects":[{"StartTime":38875.0,"Position":364.0,"HyperDash":false}]},{"StartTime":39125.0,"Objects":[{"StartTime":39125.0,"Position":232.0,"HyperDash":false},{"StartTime":39187.0,"Position":199.986359,"HyperDash":false},{"StartTime":39250.0,"Position":169.811844,"HyperDash":false},{"StartTime":39312.0,"Position":133.274048,"HyperDash":false},{"StartTime":39375.0,"Position":115.502953,"HyperDash":false},{"StartTime":39437.0,"Position":95.79658,"HyperDash":false},{"StartTime":39500.0,"Position":126.272606,"HyperDash":false},{"StartTime":39562.0,"Position":153.43367,"HyperDash":false},{"StartTime":39625.0,"Position":177.594223,"HyperDash":false},{"StartTime":39687.0,"Position":138.43367,"HyperDash":false},{"StartTime":39750.0,"Position":126.007256,"HyperDash":false},{"StartTime":39812.0,"Position":110.796577,"HyperDash":false},{"StartTime":39875.0,"Position":115.652954,"HyperDash":false},{"StartTime":39928.0,"Position":111.270706,"HyperDash":false},{"StartTime":39982.0,"Position":160.599289,"HyperDash":false},{"StartTime":40035.0,"Position":158.120911,"HyperDash":false},{"StartTime":40124.0,"Position":232.0,"HyperDash":false}]},{"StartTime":40375.0,"Objects":[{"StartTime":40375.0,"Position":280.0,"HyperDash":false}]},{"StartTime":40625.0,"Objects":[{"StartTime":40625.0,"Position":400.0,"HyperDash":false},{"StartTime":40678.0,"Position":429.074829,"HyperDash":false},{"StartTime":40732.0,"Position":455.5662,"HyperDash":false},{"StartTime":40785.0,"Position":457.641022,"HyperDash":false},{"StartTime":40875.0,"Position":504.126617,"HyperDash":false}]},{"StartTime":41125.0,"Objects":[{"StartTime":41125.0,"Position":480.0,"HyperDash":false}]},{"StartTime":41375.0,"Objects":[{"StartTime":41375.0,"Position":324.0,"HyperDash":false}]},{"StartTime":41625.0,"Objects":[{"StartTime":41625.0,"Position":168.0,"HyperDash":false}]},{"StartTime":41875.0,"Objects":[{"StartTime":41875.0,"Position":72.0,"HyperDash":false}]},{"StartTime":42000.0,"Objects":[{"StartTime":42000.0,"Position":48.0,"HyperDash":false}]},{"StartTime":42125.0,"Objects":[{"StartTime":42125.0,"Position":96.0,"HyperDash":false},{"StartTime":42178.0,"Position":114.931221,"HyperDash":false},{"StartTime":42232.0,"Position":153.604843,"HyperDash":false},{"StartTime":42285.0,"Position":193.4396,"HyperDash":false},{"StartTime":42374.0,"Position":240.778946,"HyperDash":false}]},{"StartTime":42625.0,"Objects":[{"StartTime":42625.0,"Position":400.0,"HyperDash":false}]},{"StartTime":42875.0,"Objects":[{"StartTime":42875.0,"Position":440.0,"HyperDash":false}]},{"StartTime":43000.0,"Objects":[{"StartTime":43000.0,"Position":464.0,"HyperDash":false}]},{"StartTime":43125.0,"Objects":[{"StartTime":43125.0,"Position":416.0,"HyperDash":false},{"StartTime":43178.0,"Position":375.182983,"HyperDash":false},{"StartTime":43232.0,"Position":366.663025,"HyperDash":false},{"StartTime":43285.0,"Position":335.968475,"HyperDash":false},{"StartTime":43375.0,"Position":271.221039,"HyperDash":false}]},{"StartTime":43625.0,"Objects":[{"StartTime":43625.0,"Position":112.0,"HyperDash":false}]},{"StartTime":43875.0,"Objects":[{"StartTime":43875.0,"Position":140.0,"HyperDash":false}]},{"StartTime":44125.0,"Objects":[{"StartTime":44125.0,"Position":52.0,"HyperDash":false}]},{"StartTime":44375.0,"Objects":[{"StartTime":44375.0,"Position":208.0,"HyperDash":false}]},{"StartTime":44625.0,"Objects":[{"StartTime":44625.0,"Position":344.0,"HyperDash":false}]},{"StartTime":44875.0,"Objects":[{"StartTime":44875.0,"Position":448.0,"HyperDash":false},{"StartTime":44937.0,"Position":411.344635,"HyperDash":false},{"StartTime":45000.0,"Position":386.572845,"HyperDash":false},{"StartTime":45062.0,"Position":355.1799,"HyperDash":false},{"StartTime":45125.0,"Position":304.139374,"HyperDash":false},{"StartTime":45187.0,"Position":271.8332,"HyperDash":false},{"StartTime":45250.0,"Position":232.840988,"HyperDash":false},{"StartTime":45312.0,"Position":235.629944,"HyperDash":false},{"StartTime":45375.0,"Position":232.882874,"HyperDash":false},{"StartTime":45437.0,"Position":251.629944,"HyperDash":false},{"StartTime":45500.0,"Position":243.152222,"HyperDash":false},{"StartTime":45562.0,"Position":270.8332,"HyperDash":false},{"StartTime":45625.0,"Position":304.729126,"HyperDash":false},{"StartTime":45678.0,"Position":323.441345,"HyperDash":false},{"StartTime":45732.0,"Position":370.914246,"HyperDash":false},{"StartTime":45785.0,"Position":421.2586,"HyperDash":false},{"StartTime":45874.0,"Position":448.0,"HyperDash":false}]},{"StartTime":46125.0,"Objects":[{"StartTime":46125.0,"Position":326.0,"HyperDash":false},{"StartTime":46187.0,"Position":309.377716,"HyperDash":false},{"StartTime":46250.0,"Position":271.650543,"HyperDash":false},{"StartTime":46312.0,"Position":219.299332,"HyperDash":false},{"StartTime":46375.0,"Position":182.286819,"HyperDash":false},{"StartTime":46428.0,"Position":144.357529,"HyperDash":false},{"StartTime":46482.0,"Position":145.0256,"HyperDash":false},{"StartTime":46535.0,"Position":101.934631,"HyperDash":false},{"StartTime":46625.0,"Position":110.882874,"HyperDash":false}]},{"StartTime":46875.0,"Objects":[{"StartTime":46875.0,"Position":230.0,"HyperDash":false},{"StartTime":46937.0,"Position":247.622284,"HyperDash":false},{"StartTime":47000.0,"Position":299.3495,"HyperDash":false},{"StartTime":47062.0,"Position":322.700653,"HyperDash":false},{"StartTime":47125.0,"Position":373.7132,"HyperDash":false},{"StartTime":47178.0,"Position":390.642456,"HyperDash":false},{"StartTime":47232.0,"Position":424.974426,"HyperDash":false},{"StartTime":47285.0,"Position":428.065369,"HyperDash":false},{"StartTime":47375.0,"Position":445.1171,"HyperDash":false}]},{"StartTime":47625.0,"Objects":[{"StartTime":47625.0,"Position":376.0,"HyperDash":false}]},{"StartTime":48125.0,"Objects":[{"StartTime":48125.0,"Position":376.0,"HyperDash":false},{"StartTime":48178.0,"Position":340.223816,"HyperDash":false},{"StartTime":48232.0,"Position":305.204224,"HyperDash":false},{"StartTime":48285.0,"Position":270.449249,"HyperDash":false},{"StartTime":48375.0,"Position":222.9901,"HyperDash":false}]},{"StartTime":48625.0,"Objects":[{"StartTime":48625.0,"Position":84.0,"HyperDash":false}]},{"StartTime":48875.0,"Objects":[{"StartTime":48875.0,"Position":152.0,"HyperDash":false}]},{"StartTime":49125.0,"Objects":[{"StartTime":49125.0,"Position":44.0,"HyperDash":false},{"StartTime":49178.0,"Position":69.96314,"HyperDash":false},{"StartTime":49232.0,"Position":103.8065,"HyperDash":false},{"StartTime":49285.0,"Position":156.7781,"HyperDash":false},{"StartTime":49374.0,"Position":197.1017,"HyperDash":false}]},{"StartTime":49625.0,"Objects":[{"StartTime":49625.0,"Position":336.0,"HyperDash":false}]},{"StartTime":49875.0,"Objects":[{"StartTime":49875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":50125.0,"Objects":[{"StartTime":50125.0,"Position":176.0,"HyperDash":false}]},{"StartTime":50625.0,"Objects":[{"StartTime":50625.0,"Position":340.0,"HyperDash":false}]},{"StartTime":50875.0,"Objects":[{"StartTime":50875.0,"Position":420.0,"HyperDash":false}]},{"StartTime":51125.0,"Objects":[{"StartTime":51125.0,"Position":500.0,"HyperDash":false}]},{"StartTime":51625.0,"Objects":[{"StartTime":51625.0,"Position":172.0,"HyperDash":false}]},{"StartTime":51875.0,"Objects":[{"StartTime":51875.0,"Position":92.0,"HyperDash":false}]},{"StartTime":52125.0,"Objects":[{"StartTime":52125.0,"Position":12.0,"HyperDash":false},{"StartTime":52178.0,"Position":43.4575653,"HyperDash":false},{"StartTime":52232.0,"Position":57.4520721,"HyperDash":false},{"StartTime":52285.0,"Position":85.90964,"HyperDash":false},{"StartTime":52375.0,"Position":146.23381,"HyperDash":false}]},{"StartTime":52625.0,"Objects":[{"StartTime":52625.0,"Position":304.0,"HyperDash":false}]},{"StartTime":52875.0,"Objects":[{"StartTime":52875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":53125.0,"Objects":[{"StartTime":53125.0,"Position":216.0,"HyperDash":false},{"StartTime":53178.0,"Position":229.457565,"HyperDash":false},{"StartTime":53232.0,"Position":269.452057,"HyperDash":false},{"StartTime":53285.0,"Position":304.909637,"HyperDash":false},{"StartTime":53375.0,"Position":350.233826,"HyperDash":false}]},{"StartTime":53625.0,"Objects":[{"StartTime":53625.0,"Position":508.0,"HyperDash":false}]},{"StartTime":53875.0,"Objects":[{"StartTime":53875.0,"Position":460.0,"HyperDash":false}]},{"StartTime":54125.0,"Objects":[{"StartTime":54125.0,"Position":344.0,"HyperDash":false}]},{"StartTime":54375.0,"Objects":[{"StartTime":54375.0,"Position":228.0,"HyperDash":false}]},{"StartTime":54625.0,"Objects":[{"StartTime":54625.0,"Position":153.0,"HyperDash":false}]},{"StartTime":54875.0,"Objects":[{"StartTime":54875.0,"Position":72.0,"HyperDash":false}]},{"StartTime":55125.0,"Objects":[{"StartTime":55125.0,"Position":180.0,"HyperDash":false}]},{"StartTime":55375.0,"Objects":[{"StartTime":55375.0,"Position":284.0,"HyperDash":false}]},{"StartTime":55625.0,"Objects":[{"StartTime":55625.0,"Position":359.0,"HyperDash":false}]},{"StartTime":55875.0,"Objects":[{"StartTime":55875.0,"Position":440.0,"HyperDash":false}]},{"StartTime":56125.0,"Objects":[{"StartTime":56125.0,"Position":352.0,"HyperDash":false},{"StartTime":56178.0,"Position":355.0677,"HyperDash":false},{"StartTime":56231.0,"Position":396.135376,"HyperDash":false},{"StartTime":56284.0,"Position":431.2031,"HyperDash":false},{"StartTime":56374.0,"Position":455.6765,"HyperDash":false}]},{"StartTime":56625.0,"Objects":[{"StartTime":56625.0,"Position":312.0,"HyperDash":false}]},{"StartTime":56875.0,"Objects":[{"StartTime":56875.0,"Position":200.0,"HyperDash":false}]},{"StartTime":57125.0,"Objects":[{"StartTime":57125.0,"Position":160.0,"HyperDash":false},{"StartTime":57178.0,"Position":134.932312,"HyperDash":false},{"StartTime":57231.0,"Position":131.864609,"HyperDash":false},{"StartTime":57284.0,"Position":84.7969055,"HyperDash":false},{"StartTime":57374.0,"Position":56.32347,"HyperDash":false}]},{"StartTime":57625.0,"Objects":[{"StartTime":57625.0,"Position":200.0,"HyperDash":false}]},{"StartTime":57875.0,"Objects":[{"StartTime":57875.0,"Position":312.0,"HyperDash":false}]},{"StartTime":58125.0,"Objects":[{"StartTime":58125.0,"Position":444.0,"HyperDash":false},{"StartTime":58178.0,"Position":405.081421,"HyperDash":false},{"StartTime":58232.0,"Position":380.062256,"HyperDash":false},{"StartTime":58285.0,"Position":399.193085,"HyperDash":false},{"StartTime":58374.0,"Position":377.6735,"HyperDash":false}]},{"StartTime":58500.0,"Objects":[{"StartTime":58500.0,"Position":344.0,"HyperDash":false}]},{"StartTime":58625.0,"Objects":[{"StartTime":58625.0,"Position":272.0,"HyperDash":false},{"StartTime":58678.0,"Position":263.870544,"HyperDash":false},{"StartTime":58732.0,"Position":246.779541,"HyperDash":false},{"StartTime":58785.0,"Position":179.497513,"HyperDash":false},{"StartTime":58875.0,"Position":139.25528,"HyperDash":false}]},{"StartTime":59125.0,"Objects":[{"StartTime":59125.0,"Position":68.0,"HyperDash":false},{"StartTime":59178.0,"Position":89.57149,"HyperDash":false},{"StartTime":59232.0,"Position":123.207489,"HyperDash":false},{"StartTime":59285.0,"Position":141.936157,"HyperDash":false},{"StartTime":59375.0,"Position":133.961975,"HyperDash":false}]},{"StartTime":59500.0,"Objects":[{"StartTime":59500.0,"Position":168.0,"HyperDash":false}]},{"StartTime":59625.0,"Objects":[{"StartTime":59625.0,"Position":240.0,"HyperDash":false},{"StartTime":59678.0,"Position":245.129486,"HyperDash":false},{"StartTime":59732.0,"Position":270.220459,"HyperDash":false},{"StartTime":59785.0,"Position":296.5025,"HyperDash":false},{"StartTime":59875.0,"Position":372.74472,"HyperDash":false}]},{"StartTime":60125.0,"Objects":[{"StartTime":60125.0,"Position":456.0,"HyperDash":false}]},{"StartTime":60375.0,"Objects":[{"StartTime":60375.0,"Position":328.0,"HyperDash":false}]},{"StartTime":60625.0,"Objects":[{"StartTime":60625.0,"Position":216.0,"HyperDash":false}]},{"StartTime":60875.0,"Objects":[{"StartTime":60875.0,"Position":72.0,"HyperDash":false},{"StartTime":60937.0,"Position":71.25553,"HyperDash":false},{"StartTime":61000.0,"Position":61.5583878,"HyperDash":false},{"StartTime":61062.0,"Position":98.84126,"HyperDash":false},{"StartTime":61125.0,"Position":119.510284,"HyperDash":false},{"StartTime":61187.0,"Position":142.845825,"HyperDash":false},{"StartTime":61250.0,"Position":184.319992,"HyperDash":false},{"StartTime":61312.0,"Position":240.90744,"HyperDash":false},{"StartTime":61375.0,"Position":269.728363,"HyperDash":false},{"StartTime":61437.0,"Position":239.90744,"HyperDash":false},{"StartTime":61500.0,"Position":197.687851,"HyperDash":false},{"StartTime":61562.0,"Position":150.845825,"HyperDash":false},{"StartTime":61625.0,"Position":119.024872,"HyperDash":false},{"StartTime":61678.0,"Position":90.12531,"HyperDash":false},{"StartTime":61732.0,"Position":72.3374557,"HyperDash":false},{"StartTime":61785.0,"Position":89.06496,"HyperDash":false},{"StartTime":61874.0,"Position":72.0,"HyperDash":false}]},{"StartTime":62125.0,"Objects":[{"StartTime":62125.0,"Position":200.0,"HyperDash":false},{"StartTime":62187.0,"Position":191.234039,"HyperDash":false},{"StartTime":62250.0,"Position":203.319962,"HyperDash":false},{"StartTime":62312.0,"Position":235.3192,"HyperDash":false},{"StartTime":62375.0,"Position":246.7092,"HyperDash":false},{"StartTime":62428.0,"Position":291.675018,"HyperDash":false},{"StartTime":62482.0,"Position":309.9024,"HyperDash":false},{"StartTime":62535.0,"Position":336.449463,"HyperDash":false},{"StartTime":62625.0,"Position":396.8608,"HyperDash":false}]},{"StartTime":62875.0,"Objects":[{"StartTime":62875.0,"Position":480.0,"HyperDash":false},{"StartTime":62937.0,"Position":492.1737,"HyperDash":false},{"StartTime":63000.0,"Position":476.1641,"HyperDash":false},{"StartTime":63062.0,"Position":475.045135,"HyperDash":false},{"StartTime":63125.0,"Position":433.461975,"HyperDash":false},{"StartTime":63178.0,"Position":389.354034,"HyperDash":false},{"StartTime":63232.0,"Position":366.034546,"HyperDash":false},{"StartTime":63285.0,"Position":321.454956,"HyperDash":false},{"StartTime":63375.0,"Position":283.111176,"HyperDash":false}]},{"StartTime":63625.0,"Objects":[{"StartTime":63625.0,"Position":136.0,"HyperDash":false},{"StartTime":63678.0,"Position":111.887825,"HyperDash":false},{"StartTime":63732.0,"Position":108.904541,"HyperDash":false},{"StartTime":63785.0,"Position":105.234535,"HyperDash":false},{"StartTime":63874.0,"Position":128.127991,"HyperDash":false}]},{"StartTime":64125.0,"Objects":[{"StartTime":64125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":64375.0,"Objects":[{"StartTime":64375.0,"Position":284.0,"HyperDash":false}]},{"StartTime":64625.0,"Objects":[{"StartTime":64625.0,"Position":440.0,"HyperDash":false}]},{"StartTime":64875.0,"Objects":[{"StartTime":64875.0,"Position":420.0,"HyperDash":false}]},{"StartTime":65125.0,"Objects":[{"StartTime":65125.0,"Position":300.0,"HyperDash":false}]},{"StartTime":65375.0,"Objects":[{"StartTime":65375.0,"Position":272.0,"HyperDash":false}]},{"StartTime":65625.0,"Objects":[{"StartTime":65625.0,"Position":116.0,"HyperDash":false}]},{"StartTime":65875.0,"Objects":[{"StartTime":65875.0,"Position":136.0,"HyperDash":false}]},{"StartTime":66125.0,"Objects":[{"StartTime":66125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":68125.0,"Objects":[{"StartTime":68125.0,"Position":256.0,"HyperDash":false},{"StartTime":68187.0,"Position":266.157,"HyperDash":false},{"StartTime":68250.0,"Position":280.344269,"HyperDash":false},{"StartTime":68312.0,"Position":243.4508,"HyperDash":false},{"StartTime":68375.0,"Position":216.9601,"HyperDash":false},{"StartTime":68428.0,"Position":173.102234,"HyperDash":false},{"StartTime":68482.0,"Position":150.915558,"HyperDash":false},{"StartTime":68535.0,"Position":106.794662,"HyperDash":false},{"StartTime":68625.0,"Position":73.61266,"HyperDash":false}]},{"StartTime":68875.0,"Objects":[{"StartTime":68875.0,"Position":132.0,"HyperDash":false},{"StartTime":68937.0,"Position":160.783325,"HyperDash":false},{"StartTime":69000.0,"Position":193.9825,"HyperDash":false},{"StartTime":69062.0,"Position":205.765823,"HyperDash":false},{"StartTime":69125.0,"Position":235.965,"HyperDash":false},{"StartTime":69178.0,"Position":262.005585,"HyperDash":false},{"StartTime":69232.0,"Position":285.462,"HyperDash":false},{"StartTime":69285.0,"Position":302.5026,"HyperDash":false},{"StartTime":69375.0,"Position":339.93,"HyperDash":false}]},{"StartTime":69625.0,"Objects":[{"StartTime":69625.0,"Position":456.0,"HyperDash":false}]},{"StartTime":69875.0,"Objects":[{"StartTime":69875.0,"Position":340.0,"HyperDash":false}]},{"StartTime":70000.0,"Objects":[{"StartTime":70000.0,"Position":340.0,"HyperDash":false}]},{"StartTime":70125.0,"Objects":[{"StartTime":70125.0,"Position":340.0,"HyperDash":false}]},{"StartTime":70375.0,"Objects":[{"StartTime":70375.0,"Position":228.0,"HyperDash":false}]},{"StartTime":70625.0,"Objects":[{"StartTime":70625.0,"Position":256.0,"HyperDash":false},{"StartTime":70678.0,"Position":210.6065,"HyperDash":false},{"StartTime":70732.0,"Position":177.325424,"HyperDash":false},{"StartTime":70785.0,"Position":151.573288,"HyperDash":false},{"StartTime":70875.0,"Position":107.425896,"HyperDash":false}]},{"StartTime":71125.0,"Objects":[{"StartTime":71125.0,"Position":148.0,"HyperDash":false},{"StartTime":71178.0,"Position":184.328445,"HyperDash":false},{"StartTime":71232.0,"Position":200.780228,"HyperDash":false},{"StartTime":71285.0,"Position":257.6842,"HyperDash":false},{"StartTime":71374.0,"Position":296.433563,"HyperDash":false}]},{"StartTime":71625.0,"Objects":[{"StartTime":71625.0,"Position":424.0,"HyperDash":false}]},{"StartTime":71875.0,"Objects":[{"StartTime":71875.0,"Position":336.0,"HyperDash":false}]},{"StartTime":72000.0,"Objects":[{"StartTime":72000.0,"Position":336.0,"HyperDash":false}]},{"StartTime":72125.0,"Objects":[{"StartTime":72125.0,"Position":336.0,"HyperDash":false}]},{"StartTime":72375.0,"Objects":[{"StartTime":72375.0,"Position":228.0,"HyperDash":false},{"StartTime":72428.0,"Position":211.104858,"HyperDash":false},{"StartTime":72482.0,"Position":163.608932,"HyperDash":false},{"StartTime":72535.0,"Position":134.045914,"HyperDash":false},{"StartTime":72625.0,"Position":143.764755,"HyperDash":false}]},{"StartTime":72875.0,"Objects":[{"StartTime":72875.0,"Position":268.0,"HyperDash":false},{"StartTime":72937.0,"Position":248.6492,"HyperDash":false},{"StartTime":73000.0,"Position":273.503021,"HyperDash":false},{"StartTime":73062.0,"Position":247.768143,"HyperDash":false},{"StartTime":73125.0,"Position":228.062622,"HyperDash":false},{"StartTime":73178.0,"Position":204.959824,"HyperDash":false},{"StartTime":73232.0,"Position":170.633987,"HyperDash":false},{"StartTime":73285.0,"Position":155.368179,"HyperDash":false},{"StartTime":73375.0,"Position":103.8164,"HyperDash":false}]},{"StartTime":73625.0,"Objects":[{"StartTime":73625.0,"Position":24.0,"HyperDash":false}]},{"StartTime":73875.0,"Objects":[{"StartTime":73875.0,"Position":92.0,"HyperDash":false}]},{"StartTime":74000.0,"Objects":[{"StartTime":74000.0,"Position":92.0,"HyperDash":false}]},{"StartTime":74125.0,"Objects":[{"StartTime":74125.0,"Position":92.0,"HyperDash":false}]},{"StartTime":74375.0,"Objects":[{"StartTime":74375.0,"Position":224.0,"HyperDash":false}]},{"StartTime":74625.0,"Objects":[{"StartTime":74625.0,"Position":340.0,"HyperDash":false},{"StartTime":74678.0,"Position":381.308228,"HyperDash":false},{"StartTime":74732.0,"Position":376.477844,"HyperDash":false},{"StartTime":74785.0,"Position":399.771942,"HyperDash":false},{"StartTime":74875.0,"Position":387.2963,"HyperDash":false}]},{"StartTime":75125.0,"Objects":[{"StartTime":75125.0,"Position":268.0,"HyperDash":false},{"StartTime":75178.0,"Position":219.691772,"HyperDash":false},{"StartTime":75232.0,"Position":224.522156,"HyperDash":false},{"StartTime":75285.0,"Position":185.228043,"HyperDash":false},{"StartTime":75375.0,"Position":220.70369,"HyperDash":false}]},{"StartTime":75625.0,"Objects":[{"StartTime":75625.0,"Position":268.0,"HyperDash":false},{"StartTime":75678.0,"Position":251.437485,"HyperDash":false},{"StartTime":75732.0,"Position":209.2417,"HyperDash":false},{"StartTime":75785.0,"Position":166.6792,"HyperDash":false},{"StartTime":75875.0,"Position":109.686234,"HyperDash":false}]},{"StartTime":76125.0,"Objects":[{"StartTime":76125.0,"Position":24.0,"HyperDash":false},{"StartTime":76250.0,"Position":103.510704,"HyperDash":false}]},{"StartTime":76375.0,"Objects":[{"StartTime":76375.0,"Position":176.0,"HyperDash":false}]},{"StartTime":76625.0,"Objects":[{"StartTime":76625.0,"Position":348.0,"HyperDash":false}]},{"StartTime":76875.0,"Objects":[{"StartTime":76875.0,"Position":248.0,"HyperDash":false}]},{"StartTime":77125.0,"Objects":[{"StartTime":77125.0,"Position":264.0,"HyperDash":false}]},{"StartTime":77375.0,"Objects":[{"StartTime":77375.0,"Position":324.0,"HyperDash":false}]},{"StartTime":77625.0,"Objects":[{"StartTime":77625.0,"Position":180.0,"HyperDash":false}]},{"StartTime":77875.0,"Objects":[{"StartTime":77875.0,"Position":240.0,"HyperDash":false}]},{"StartTime":78125.0,"Objects":[{"StartTime":78125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":78375.0,"Objects":[{"StartTime":78375.0,"Position":100.0,"HyperDash":false}]},{"StartTime":78625.0,"Objects":[{"StartTime":78625.0,"Position":8.0,"HyperDash":false},{"StartTime":78678.0,"Position":30.0805969,"HyperDash":false},{"StartTime":78732.0,"Position":72.26928,"HyperDash":false},{"StartTime":78785.0,"Position":94.77067,"HyperDash":false},{"StartTime":78874.0,"Position":149.724487,"HyperDash":false}]},{"StartTime":79125.0,"Objects":[{"StartTime":79125.0,"Position":304.0,"HyperDash":false},{"StartTime":79178.0,"Position":282.0235,"HyperDash":false},{"StartTime":79232.0,"Position":238.981018,"HyperDash":false},{"StartTime":79285.0,"Position":222.634567,"HyperDash":false},{"StartTime":79375.0,"Position":162.2755,"HyperDash":false}]},{"StartTime":79625.0,"Objects":[{"StartTime":79625.0,"Position":304.0,"HyperDash":false}]},{"StartTime":79875.0,"Objects":[{"StartTime":79875.0,"Position":460.0,"HyperDash":false}]},{"StartTime":80125.0,"Objects":[{"StartTime":80125.0,"Position":420.0,"HyperDash":false},{"StartTime":80250.0,"Position":340.0,"HyperDash":false}]},{"StartTime":80375.0,"Objects":[{"StartTime":80375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":80625.0,"Objects":[{"StartTime":80625.0,"Position":344.0,"HyperDash":false}]},{"StartTime":80875.0,"Objects":[{"StartTime":80875.0,"Position":168.0,"HyperDash":false}]},{"StartTime":81125.0,"Objects":[{"StartTime":81125.0,"Position":384.0,"HyperDash":false}]},{"StartTime":81375.0,"Objects":[{"StartTime":81375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":81625.0,"Objects":[{"StartTime":81625.0,"Position":168.0,"HyperDash":false}]},{"StartTime":81875.0,"Objects":[{"StartTime":81875.0,"Position":344.0,"HyperDash":false}]},{"StartTime":82125.0,"Objects":[{"StartTime":82125.0,"Position":128.0,"HyperDash":false}]},{"StartTime":82250.0,"Objects":[{"StartTime":82250.0,"Position":48.0,"HyperDash":false},{"StartTime":82303.0,"Position":38.86482,"HyperDash":false},{"StartTime":82357.0,"Position":53.93512,"HyperDash":false},{"StartTime":82410.0,"Position":60.0134125,"HyperDash":false},{"StartTime":82500.0,"Position":124.821884,"HyperDash":false}]},{"StartTime":82625.0,"Objects":[{"StartTime":82625.0,"Position":204.0,"HyperDash":false},{"StartTime":82678.0,"Position":208.888657,"HyperDash":false},{"StartTime":82731.0,"Position":211.78508,"HyperDash":false},{"StartTime":82784.0,"Position":215.863892,"HyperDash":false},{"StartTime":82874.0,"Position":280.821869,"HyperDash":false}]},{"StartTime":83000.0,"Objects":[{"StartTime":83000.0,"Position":352.0,"HyperDash":false},{"StartTime":83053.0,"Position":303.246552,"HyperDash":false},{"StartTime":83107.0,"Position":291.2771,"HyperDash":false},{"StartTime":83160.0,"Position":254.710571,"HyperDash":false},{"StartTime":83250.0,"Position":222.496063,"HyperDash":false}]},{"StartTime":83375.0,"Objects":[{"StartTime":83375.0,"Position":192.0,"HyperDash":false},{"StartTime":83428.0,"Position":152.246567,"HyperDash":false},{"StartTime":83482.0,"Position":112.277092,"HyperDash":false},{"StartTime":83535.0,"Position":87.71058,"HyperDash":false},{"StartTime":83625.0,"Position":62.496067,"HyperDash":false}]},{"StartTime":83875.0,"Objects":[{"StartTime":83875.0,"Position":32.0,"HyperDash":false}]},{"StartTime":84125.0,"Objects":[{"StartTime":84125.0,"Position":172.0,"HyperDash":false}]},{"StartTime":84250.0,"Objects":[{"StartTime":84250.0,"Position":179.0,"HyperDash":false},{"StartTime":84308.0,"Position":278.0,"HyperDash":false},{"StartTime":84367.0,"Position":474.0,"HyperDash":false},{"StartTime":84425.0,"Position":50.0,"HyperDash":false},{"StartTime":84484.0,"Position":458.0,"HyperDash":false},{"StartTime":84542.0,"Position":425.0,"HyperDash":false},{"StartTime":84601.0,"Position":466.0,"HyperDash":false},{"StartTime":84660.0,"Position":56.0,"HyperDash":false},{"StartTime":84718.0,"Position":109.0,"HyperDash":false},{"StartTime":84777.0,"Position":482.0,"HyperDash":false},{"StartTime":84835.0,"Position":147.0,"HyperDash":false},{"StartTime":84894.0,"Position":285.0,"HyperDash":false},{"StartTime":84953.0,"Position":452.0,"HyperDash":false},{"StartTime":85011.0,"Position":419.0,"HyperDash":false},{"StartTime":85070.0,"Position":269.0,"HyperDash":false},{"StartTime":85128.0,"Position":249.0,"HyperDash":false},{"StartTime":85187.0,"Position":233.0,"HyperDash":false},{"StartTime":85246.0,"Position":449.0,"HyperDash":false},{"StartTime":85304.0,"Position":411.0,"HyperDash":false},{"StartTime":85363.0,"Position":75.0,"HyperDash":false},{"StartTime":85421.0,"Position":474.0,"HyperDash":false},{"StartTime":85480.0,"Position":176.0,"HyperDash":false},{"StartTime":85539.0,"Position":1.0,"HyperDash":false},{"StartTime":85597.0,"Position":37.0,"HyperDash":false},{"StartTime":85656.0,"Position":481.0,"HyperDash":false},{"StartTime":85714.0,"Position":375.0,"HyperDash":false},{"StartTime":85773.0,"Position":407.0,"HyperDash":false},{"StartTime":85832.0,"Position":231.0,"HyperDash":false},{"StartTime":85890.0,"Position":338.0,"HyperDash":false},{"StartTime":85949.0,"Position":322.0,"HyperDash":false},{"StartTime":86007.0,"Position":347.0,"HyperDash":false},{"StartTime":86066.0,"Position":365.0,"HyperDash":false},{"StartTime":86125.0,"Position":453.0,"HyperDash":false}]},{"StartTime":86250.0,"Objects":[{"StartTime":86250.0,"Position":486.0,"HyperDash":false},{"StartTime":86304.0,"Position":68.0,"HyperDash":false},{"StartTime":86359.0,"Position":498.0,"HyperDash":false},{"StartTime":86414.0,"Position":164.0,"HyperDash":false},{"StartTime":86468.0,"Position":1.0,"HyperDash":false},{"StartTime":86523.0,"Position":501.0,"HyperDash":false},{"StartTime":86578.0,"Position":82.0,"HyperDash":false},{"StartTime":86632.0,"Position":494.0,"HyperDash":false},{"StartTime":86687.0,"Position":479.0,"HyperDash":false},{"StartTime":86742.0,"Position":373.0,"HyperDash":false},{"StartTime":86796.0,"Position":450.0,"HyperDash":false},{"StartTime":86851.0,"Position":144.0,"HyperDash":false},{"StartTime":86906.0,"Position":365.0,"HyperDash":false},{"StartTime":86960.0,"Position":285.0,"HyperDash":false},{"StartTime":87015.0,"Position":45.0,"HyperDash":false},{"StartTime":87070.0,"Position":65.0,"HyperDash":false},{"StartTime":87125.0,"Position":337.0,"HyperDash":false}]},{"StartTime":88125.0,"Objects":[{"StartTime":88125.0,"Position":256.0,"HyperDash":false},{"StartTime":88178.0,"Position":292.30423,"HyperDash":false},{"StartTime":88232.0,"Position":341.450134,"HyperDash":false},{"StartTime":88285.0,"Position":358.591034,"HyperDash":false},{"StartTime":88375.0,"Position":390.822968,"HyperDash":false}]},{"StartTime":88625.0,"Objects":[{"StartTime":88625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":88875.0,"Objects":[{"StartTime":88875.0,"Position":136.0,"HyperDash":false}]},{"StartTime":89125.0,"Objects":[{"StartTime":89125.0,"Position":8.0,"HyperDash":false},{"StartTime":89178.0,"Position":0.0,"HyperDash":false},{"StartTime":89232.0,"Position":12.7492714,"HyperDash":false},{"StartTime":89285.0,"Position":7.342363,"HyperDash":false},{"StartTime":89375.0,"Position":41.059124,"HyperDash":false}]},{"StartTime":89625.0,"Objects":[{"StartTime":89625.0,"Position":164.0,"HyperDash":false}]},{"StartTime":89875.0,"Objects":[{"StartTime":89875.0,"Position":288.0,"HyperDash":false}]},{"StartTime":90000.0,"Objects":[{"StartTime":90000.0,"Position":288.0,"HyperDash":false}]},{"StartTime":90125.0,"Objects":[{"StartTime":90125.0,"Position":288.0,"HyperDash":false},{"StartTime":90178.0,"Position":307.058655,"HyperDash":false},{"StartTime":90232.0,"Position":366.7033,"HyperDash":false},{"StartTime":90285.0,"Position":400.761932,"HyperDash":false},{"StartTime":90375.0,"Position":434.503052,"HyperDash":false}]},{"StartTime":90625.0,"Objects":[{"StartTime":90625.0,"Position":476.0,"HyperDash":false}]},{"StartTime":90875.0,"Objects":[{"StartTime":90875.0,"Position":332.0,"HyperDash":false}]},{"StartTime":91125.0,"Objects":[{"StartTime":91125.0,"Position":180.0,"HyperDash":false}]},{"StartTime":91375.0,"Objects":[{"StartTime":91375.0,"Position":36.0,"HyperDash":false}]},{"StartTime":91625.0,"Objects":[{"StartTime":91625.0,"Position":56.0,"HyperDash":false}]},{"StartTime":92125.0,"Objects":[{"StartTime":92125.0,"Position":56.0,"HyperDash":false},{"StartTime":92178.0,"Position":78.15752,"HyperDash":false},{"StartTime":92232.0,"Position":134.940643,"HyperDash":false},{"StartTime":92285.0,"Position":142.098145,"HyperDash":false},{"StartTime":92375.0,"Position":212.403366,"HyperDash":false}]},{"StartTime":92625.0,"Objects":[{"StartTime":92625.0,"Position":84.0,"HyperDash":false}]},{"StartTime":92875.0,"Objects":[{"StartTime":92875.0,"Position":220.0,"HyperDash":false}]},{"StartTime":93125.0,"Objects":[{"StartTime":93125.0,"Position":320.0,"HyperDash":false},{"StartTime":93178.0,"Position":369.1575,"HyperDash":false},{"StartTime":93232.0,"Position":398.940643,"HyperDash":false},{"StartTime":93285.0,"Position":408.098145,"HyperDash":false},{"StartTime":93375.0,"Position":476.403381,"HyperDash":false}]},{"StartTime":93625.0,"Objects":[{"StartTime":93625.0,"Position":432.0,"HyperDash":false}]},{"StartTime":93875.0,"Objects":[{"StartTime":93875.0,"Position":296.0,"HyperDash":false}]},{"StartTime":94000.0,"Objects":[{"StartTime":94000.0,"Position":296.0,"HyperDash":false}]},{"StartTime":94125.0,"Objects":[{"StartTime":94125.0,"Position":296.0,"HyperDash":false},{"StartTime":94178.0,"Position":273.1039,"HyperDash":false},{"StartTime":94232.0,"Position":244.445969,"HyperDash":false},{"StartTime":94285.0,"Position":219.02182,"HyperDash":false},{"StartTime":94374.0,"Position":170.471848,"HyperDash":false}]},{"StartTime":94625.0,"Objects":[{"StartTime":94625.0,"Position":216.0,"HyperDash":false},{"StartTime":94678.0,"Position":259.7602,"HyperDash":false},{"StartTime":94732.0,"Position":282.299927,"HyperDash":false},{"StartTime":94785.0,"Position":294.678436,"HyperDash":false},{"StartTime":94875.0,"Position":341.528168,"HyperDash":false}]},{"StartTime":95000.0,"Objects":[{"StartTime":95000.0,"Position":341.0,"HyperDash":false}]},{"StartTime":95125.0,"Objects":[{"StartTime":95125.0,"Position":341.0,"HyperDash":false},{"StartTime":95178.0,"Position":347.282532,"HyperDash":false},{"StartTime":95232.0,"Position":344.6459,"HyperDash":false},{"StartTime":95285.0,"Position":339.928436,"HyperDash":false},{"StartTime":95375.0,"Position":361.200684,"HyperDash":false}]},{"StartTime":95625.0,"Objects":[{"StartTime":95625.0,"Position":171.0,"HyperDash":false},{"StartTime":95678.0,"Position":158.717453,"HyperDash":false},{"StartTime":95732.0,"Position":169.354111,"HyperDash":false},{"StartTime":95785.0,"Position":172.071564,"HyperDash":false},{"StartTime":95875.0,"Position":150.799316,"HyperDash":false}]},{"StartTime":96125.0,"Objects":[{"StartTime":96125.0,"Position":43.0,"HyperDash":false}]},{"StartTime":96375.0,"Objects":[{"StartTime":96375.0,"Position":81.0,"HyperDash":false}]},{"StartTime":96625.0,"Objects":[{"StartTime":96625.0,"Position":169.0,"HyperDash":false}]},{"StartTime":96875.0,"Objects":[{"StartTime":96875.0,"Position":304.0,"HyperDash":false},{"StartTime":96937.0,"Position":333.433136,"HyperDash":false},{"StartTime":97000.0,"Position":385.325043,"HyperDash":false},{"StartTime":97062.0,"Position":379.667,"HyperDash":false},{"StartTime":97125.0,"Position":401.778076,"HyperDash":false},{"StartTime":97187.0,"Position":418.125366,"HyperDash":false},{"StartTime":97250.0,"Position":403.005768,"HyperDash":false},{"StartTime":97312.0,"Position":375.9013,"HyperDash":false},{"StartTime":97375.0,"Position":343.426239,"HyperDash":false},{"StartTime":97437.0,"Position":382.9013,"HyperDash":false},{"StartTime":97499.0,"Position":392.005768,"HyperDash":false},{"StartTime":97561.0,"Position":388.066345,"HyperDash":false},{"StartTime":97624.0,"Position":401.778076,"HyperDash":false},{"StartTime":97677.0,"Position":380.074066,"HyperDash":false},{"StartTime":97731.0,"Position":366.190063,"HyperDash":false},{"StartTime":97785.0,"Position":348.305481,"HyperDash":false},{"StartTime":97874.0,"Position":304.0,"HyperDash":false}]},{"StartTime":98125.0,"Objects":[{"StartTime":98125.0,"Position":240.0,"HyperDash":false},{"StartTime":98187.0,"Position":220.193451,"HyperDash":false},{"StartTime":98250.0,"Position":179.67662,"HyperDash":false},{"StartTime":98312.0,"Position":167.455551,"HyperDash":false},{"StartTime":98375.0,"Position":115.407051,"HyperDash":false},{"StartTime":98428.0,"Position":97.24337,"HyperDash":false},{"StartTime":98482.0,"Position":115.416969,"HyperDash":false},{"StartTime":98535.0,"Position":122.237556,"HyperDash":false},{"StartTime":98624.0,"Position":166.963364,"HyperDash":false}]},{"StartTime":98875.0,"Objects":[{"StartTime":98875.0,"Position":240.0,"HyperDash":false},{"StartTime":98937.0,"Position":273.329651,"HyperDash":false},{"StartTime":99000.0,"Position":306.601349,"HyperDash":false},{"StartTime":99062.0,"Position":324.816467,"HyperDash":false},{"StartTime":99125.0,"Position":363.818481,"HyperDash":false},{"StartTime":99178.0,"Position":391.8492,"HyperDash":false},{"StartTime":99232.0,"Position":363.507568,"HyperDash":false},{"StartTime":99285.0,"Position":349.543182,"HyperDash":false},{"StartTime":99374.0,"Position":311.711731,"HyperDash":false}]},{"StartTime":99625.0,"Objects":[{"StartTime":99625.0,"Position":180.0,"HyperDash":false},{"StartTime":99678.0,"Position":143.011124,"HyperDash":false},{"StartTime":99732.0,"Position":113.192444,"HyperDash":false},{"StartTime":99785.0,"Position":79.4256439,"HyperDash":false},{"StartTime":99874.0,"Position":45.3982735,"HyperDash":false}]},{"StartTime":100125.0,"Objects":[{"StartTime":100125.0,"Position":48.0,"HyperDash":false},{"StartTime":100178.0,"Position":75.85622,"HyperDash":false},{"StartTime":100231.0,"Position":116.712425,"HyperDash":false},{"StartTime":100284.0,"Position":156.568634,"HyperDash":false},{"StartTime":100374.0,"Position":202.3622,"HyperDash":false}]},{"StartTime":100625.0,"Objects":[{"StartTime":100625.0,"Position":348.0,"HyperDash":false},{"StartTime":100678.0,"Position":383.8562,"HyperDash":false},{"StartTime":100731.0,"Position":402.712433,"HyperDash":false},{"StartTime":100784.0,"Position":456.568634,"HyperDash":false},{"StartTime":100874.0,"Position":502.362183,"HyperDash":false}]},{"StartTime":101125.0,"Objects":[{"StartTime":101125.0,"Position":504.0,"HyperDash":false},{"StartTime":101178.0,"Position":488.1438,"HyperDash":false},{"StartTime":101231.0,"Position":446.287567,"HyperDash":false},{"StartTime":101284.0,"Position":423.431366,"HyperDash":false},{"StartTime":101374.0,"Position":349.637817,"HyperDash":false}]},{"StartTime":101625.0,"Objects":[{"StartTime":101625.0,"Position":204.0,"HyperDash":false},{"StartTime":101678.0,"Position":156.143784,"HyperDash":false},{"StartTime":101731.0,"Position":133.287567,"HyperDash":false},{"StartTime":101784.0,"Position":117.431358,"HyperDash":false},{"StartTime":101874.0,"Position":49.6378021,"HyperDash":false}]},{"StartTime":102000.0,"Objects":[{"StartTime":102000.0,"Position":49.0,"HyperDash":false}]},{"StartTime":102125.0,"Objects":[{"StartTime":102125.0,"Position":49.0,"HyperDash":false}]},{"StartTime":102625.0,"Objects":[{"StartTime":102625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":102875.0,"Objects":[{"StartTime":102875.0,"Position":384.0,"HyperDash":false}]},{"StartTime":103125.0,"Objects":[{"StartTime":103125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":103625.0,"Objects":[{"StartTime":103625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":103875.0,"Objects":[{"StartTime":103875.0,"Position":128.0,"HyperDash":false}]},{"StartTime":104125.0,"Objects":[{"StartTime":104125.0,"Position":256.0,"HyperDash":false},{"StartTime":104178.0,"Position":272.994537,"HyperDash":false},{"StartTime":104232.0,"Position":332.6524,"HyperDash":false},{"StartTime":104285.0,"Position":355.331024,"HyperDash":false},{"StartTime":104375.0,"Position":402.7333,"HyperDash":false}]},{"StartTime":104625.0,"Objects":[{"StartTime":104625.0,"Position":492.0,"HyperDash":false}]},{"StartTime":104875.0,"Objects":[{"StartTime":104875.0,"Position":332.0,"HyperDash":false}]},{"StartTime":105125.0,"Objects":[{"StartTime":105125.0,"Position":256.0,"HyperDash":false},{"StartTime":105178.0,"Position":241.889923,"HyperDash":false},{"StartTime":105232.0,"Position":211.080078,"HyperDash":false},{"StartTime":105285.0,"Position":144.258759,"HyperDash":false},{"StartTime":105374.0,"Position":109.266708,"HyperDash":false}]},{"StartTime":105625.0,"Objects":[{"StartTime":105625.0,"Position":20.0,"HyperDash":false}]},{"StartTime":105875.0,"Objects":[{"StartTime":105875.0,"Position":180.0,"HyperDash":false}]},{"StartTime":106125.0,"Objects":[{"StartTime":106125.0,"Position":368.0,"HyperDash":false},{"StartTime":106178.0,"Position":376.172974,"HyperDash":false},{"StartTime":106231.0,"Position":410.418945,"HyperDash":false},{"StartTime":106284.0,"Position":421.151764,"HyperDash":false},{"StartTime":106374.0,"Position":416.485779,"HyperDash":false}]},{"StartTime":106625.0,"Objects":[{"StartTime":106625.0,"Position":220.0,"HyperDash":false},{"StartTime":106678.0,"Position":241.054184,"HyperDash":false},{"StartTime":106731.0,"Position":282.792328,"HyperDash":false},{"StartTime":106784.0,"Position":301.047577,"HyperDash":false},{"StartTime":106874.0,"Position":349.247284,"HyperDash":false}]},{"StartTime":107125.0,"Objects":[{"StartTime":107125.0,"Position":144.0,"HyperDash":false},{"StartTime":107178.0,"Position":125.932152,"HyperDash":false},{"StartTime":107232.0,"Position":88.3730545,"HyperDash":false},{"StartTime":107285.0,"Position":97.8173752,"HyperDash":false},{"StartTime":107375.0,"Position":95.51424,"HyperDash":false}]},{"StartTime":107625.0,"Objects":[{"StartTime":107625.0,"Position":292.0,"HyperDash":false},{"StartTime":107678.0,"Position":279.032471,"HyperDash":false},{"StartTime":107732.0,"Position":263.904663,"HyperDash":false},{"StartTime":107785.0,"Position":230.72316,"HyperDash":false},{"StartTime":107875.0,"Position":162.752716,"HyperDash":false}]},{"StartTime":108125.0,"Objects":[{"StartTime":108125.0,"Position":44.0,"HyperDash":false},{"StartTime":108178.0,"Position":95.98508,"HyperDash":false},{"StartTime":108232.0,"Position":110.3604,"HyperDash":false},{"StartTime":108285.0,"Position":123.706589,"HyperDash":false},{"StartTime":108374.0,"Position":169.6919,"HyperDash":false}]},{"StartTime":108625.0,"Objects":[{"StartTime":108625.0,"Position":304.0,"HyperDash":false}]},{"StartTime":108875.0,"Objects":[{"StartTime":108875.0,"Position":408.0,"HyperDash":false}]},{"StartTime":109125.0,"Objects":[{"StartTime":109125.0,"Position":468.0,"HyperDash":false},{"StartTime":109178.0,"Position":439.149963,"HyperDash":false},{"StartTime":109232.0,"Position":396.891418,"HyperDash":false},{"StartTime":109285.0,"Position":370.5935,"HyperDash":false},{"StartTime":109375.0,"Position":342.308075,"HyperDash":false}]},{"StartTime":109625.0,"Objects":[{"StartTime":109625.0,"Position":208.0,"HyperDash":false}]},{"StartTime":109875.0,"Objects":[{"StartTime":109875.0,"Position":104.0,"HyperDash":false}]},{"StartTime":110125.0,"Objects":[{"StartTime":110125.0,"Position":256.0,"HyperDash":false},{"StartTime":110178.0,"Position":239.263885,"HyperDash":false},{"StartTime":110232.0,"Position":204.098785,"HyperDash":false},{"StartTime":110285.0,"Position":187.362686,"HyperDash":false},{"StartTime":110375.0,"Position":148.7542,"HyperDash":false}]},{"StartTime":110625.0,"Objects":[{"StartTime":110625.0,"Position":256.0,"HyperDash":false},{"StartTime":110678.0,"Position":283.827423,"HyperDash":false},{"StartTime":110731.0,"Position":319.654846,"HyperDash":false},{"StartTime":110784.0,"Position":325.482239,"HyperDash":false},{"StartTime":110874.0,"Position":363.2458,"HyperDash":false}]},{"StartTime":111125.0,"Objects":[{"StartTime":111125.0,"Position":208.0,"HyperDash":false},{"StartTime":111178.0,"Position":185.263885,"HyperDash":false},{"StartTime":111232.0,"Position":170.098785,"HyperDash":false},{"StartTime":111285.0,"Position":123.362686,"HyperDash":false},{"StartTime":111375.0,"Position":100.754196,"HyperDash":false}]},{"StartTime":111625.0,"Objects":[{"StartTime":111625.0,"Position":304.0,"HyperDash":false},{"StartTime":111678.0,"Position":318.7361,"HyperDash":false},{"StartTime":111732.0,"Position":353.901184,"HyperDash":false},{"StartTime":111785.0,"Position":357.6373,"HyperDash":false},{"StartTime":111875.0,"Position":411.2458,"HyperDash":false}]},{"StartTime":112125.0,"Objects":[{"StartTime":112125.0,"Position":252.0,"HyperDash":false}]},{"StartTime":112375.0,"Objects":[{"StartTime":112375.0,"Position":112.0,"HyperDash":false}]},{"StartTime":112625.0,"Objects":[{"StartTime":112625.0,"Position":72.0,"HyperDash":false}]},{"StartTime":112875.0,"Objects":[{"StartTime":112875.0,"Position":158.0,"HyperDash":false},{"StartTime":112937.0,"Position":180.39856,"HyperDash":false},{"StartTime":113000.0,"Position":253.684036,"HyperDash":false},{"StartTime":113062.0,"Position":263.862976,"HyperDash":false},{"StartTime":113125.0,"Position":289.459473,"HyperDash":false},{"StartTime":113187.0,"Position":294.857574,"HyperDash":false},{"StartTime":113250.0,"Position":301.491974,"HyperDash":false},{"StartTime":113312.0,"Position":306.150818,"HyperDash":false},{"StartTime":113375.0,"Position":278.112,"HyperDash":false},{"StartTime":113437.0,"Position":308.150818,"HyperDash":false},{"StartTime":113500.0,"Position":291.538177,"HyperDash":false},{"StartTime":113562.0,"Position":288.857574,"HyperDash":false},{"StartTime":113625.0,"Position":289.160065,"HyperDash":false},{"StartTime":113678.0,"Position":275.785217,"HyperDash":false},{"StartTime":113732.0,"Position":261.88623,"HyperDash":false},{"StartTime":113785.0,"Position":219.895935,"HyperDash":false},{"StartTime":113874.0,"Position":158.0,"HyperDash":false}]},{"StartTime":114125.0,"Objects":[{"StartTime":114125.0,"Position":176.0,"HyperDash":false},{"StartTime":114187.0,"Position":215.46962,"HyperDash":false},{"StartTime":114250.0,"Position":243.459351,"HyperDash":false},{"StartTime":114312.0,"Position":280.9655,"HyperDash":false},{"StartTime":114375.0,"Position":311.184082,"HyperDash":false},{"StartTime":114428.0,"Position":345.321442,"HyperDash":false},{"StartTime":114482.0,"Position":372.3753,"HyperDash":false},{"StartTime":114535.0,"Position":414.472534,"HyperDash":false},{"StartTime":114624.0,"Position":431.115143,"HyperDash":false}]},{"StartTime":114875.0,"Objects":[{"StartTime":114875.0,"Position":328.0,"HyperDash":false},{"StartTime":114937.0,"Position":303.669556,"HyperDash":false},{"StartTime":115000.0,"Position":279.312225,"HyperDash":false},{"StartTime":115062.0,"Position":265.2286,"HyperDash":false},{"StartTime":115125.0,"Position":258.051422,"HyperDash":false},{"StartTime":115178.0,"Position":262.0706,"HyperDash":false},{"StartTime":115231.0,"Position":286.7301,"HyperDash":false},{"StartTime":115284.0,"Position":315.1607,"HyperDash":false},{"StartTime":115374.0,"Position":349.780029,"HyperDash":false}]},{"StartTime":115625.0,"Objects":[{"StartTime":115625.0,"Position":488.0,"HyperDash":false},{"StartTime":115678.0,"Position":480.653168,"HyperDash":false},{"StartTime":115732.0,"Position":483.186554,"HyperDash":false},{"StartTime":115785.0,"Position":463.839722,"HyperDash":false},{"StartTime":115875.0,"Position":458.062073,"HyperDash":false}]},{"StartTime":116125.0,"Objects":[{"StartTime":116125.0,"Position":416.0,"HyperDash":false}]},{"StartTime":116375.0,"Objects":[{"StartTime":116375.0,"Position":288.0,"HyperDash":false}]},{"StartTime":116625.0,"Objects":[{"StartTime":116625.0,"Position":164.0,"HyperDash":false}]},{"StartTime":116875.0,"Objects":[{"StartTime":116875.0,"Position":36.0,"HyperDash":false}]},{"StartTime":117125.0,"Objects":[{"StartTime":117125.0,"Position":104.0,"HyperDash":false}]},{"StartTime":117375.0,"Objects":[{"StartTime":117375.0,"Position":232.0,"HyperDash":false}]},{"StartTime":117625.0,"Objects":[{"StartTime":117625.0,"Position":356.0,"HyperDash":false}]},{"StartTime":117875.0,"Objects":[{"StartTime":117875.0,"Position":484.0,"HyperDash":false}]},{"StartTime":118125.0,"Objects":[{"StartTime":118125.0,"Position":356.0,"HyperDash":false}]},{"StartTime":128125.0,"Objects":[{"StartTime":128125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":128250.0,"Objects":[{"StartTime":128250.0,"Position":256.0,"HyperDash":false}]},{"StartTime":128500.0,"Objects":[{"StartTime":128500.0,"Position":336.0,"HyperDash":false}]},{"StartTime":128625.0,"Objects":[{"StartTime":128625.0,"Position":336.0,"HyperDash":false}]},{"StartTime":128875.0,"Objects":[{"StartTime":128875.0,"Position":400.0,"HyperDash":false}]},{"StartTime":129000.0,"Objects":[{"StartTime":129000.0,"Position":400.0,"HyperDash":false}]},{"StartTime":129250.0,"Objects":[{"StartTime":129250.0,"Position":492.0,"HyperDash":false}]},{"StartTime":129375.0,"Objects":[{"StartTime":129375.0,"Position":492.0,"HyperDash":false}]},{"StartTime":129625.0,"Objects":[{"StartTime":129625.0,"Position":440.0,"HyperDash":false},{"StartTime":129678.0,"Position":420.699738,"HyperDash":false},{"StartTime":129731.0,"Position":376.399475,"HyperDash":false},{"StartTime":129784.0,"Position":327.099243,"HyperDash":false},{"StartTime":129874.0,"Position":283.551636,"HyperDash":false}]},{"StartTime":130125.0,"Objects":[{"StartTime":130125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":130250.0,"Objects":[{"StartTime":130250.0,"Position":256.0,"HyperDash":false}]},{"StartTime":130500.0,"Objects":[{"StartTime":130500.0,"Position":176.0,"HyperDash":false}]},{"StartTime":130625.0,"Objects":[{"StartTime":130625.0,"Position":176.0,"HyperDash":false}]},{"StartTime":130875.0,"Objects":[{"StartTime":130875.0,"Position":112.0,"HyperDash":false}]},{"StartTime":131000.0,"Objects":[{"StartTime":131000.0,"Position":112.0,"HyperDash":false}]},{"StartTime":131250.0,"Objects":[{"StartTime":131250.0,"Position":20.0,"HyperDash":false}]},{"StartTime":131375.0,"Objects":[{"StartTime":131375.0,"Position":20.0,"HyperDash":false}]},{"StartTime":131625.0,"Objects":[{"StartTime":131625.0,"Position":72.0,"HyperDash":false},{"StartTime":131678.0,"Position":85.16705,"HyperDash":false},{"StartTime":131732.0,"Position":139.959915,"HyperDash":false},{"StartTime":131785.0,"Position":179.126953,"HyperDash":false},{"StartTime":131875.0,"Position":228.44838,"HyperDash":false}]},{"StartTime":132125.0,"Objects":[{"StartTime":132125.0,"Position":408.0,"HyperDash":false},{"StartTime":132187.0,"Position":432.7211,"HyperDash":false},{"StartTime":132250.0,"Position":463.48645,"HyperDash":false},{"StartTime":132312.0,"Position":484.605652,"HyperDash":false},{"StartTime":132375.0,"Position":511.913147,"HyperDash":false},{"StartTime":132437.0,"Position":511.3131,"HyperDash":false},{"StartTime":132500.0,"Position":512.0,"HyperDash":false},{"StartTime":132562.0,"Position":512.0,"HyperDash":false},{"StartTime":132625.0,"Position":491.9296,"HyperDash":false},{"StartTime":132687.0,"Position":477.671265,"HyperDash":false},{"StartTime":132750.0,"Position":455.869171,"HyperDash":false},{"StartTime":132812.0,"Position":413.826355,"HyperDash":false},{"StartTime":132875.0,"Position":366.962769,"HyperDash":false},{"StartTime":132937.0,"Position":340.888336,"HyperDash":false},{"StartTime":133000.0,"Position":273.617157,"HyperDash":false},{"StartTime":133062.0,"Position":263.5604,"HyperDash":false},{"StartTime":133125.0,"Position":210.586578,"HyperDash":false},{"StartTime":133187.0,"Position":176.064163,"HyperDash":false},{"StartTime":133250.0,"Position":127.187744,"HyperDash":false},{"StartTime":133312.0,"Position":131.32103,"HyperDash":false},{"StartTime":133375.0,"Position":102.106659,"HyperDash":false},{"StartTime":133437.0,"Position":101.403084,"HyperDash":false},{"StartTime":133500.0,"Position":84.85893,"HyperDash":false},{"StartTime":133562.0,"Position":83.863945,"HyperDash":false},{"StartTime":133625.0,"Position":119.323433,"HyperDash":false},{"StartTime":133687.0,"Position":159.490738,"HyperDash":false},{"StartTime":133750.0,"Position":179.476852,"HyperDash":false},{"StartTime":133812.0,"Position":207.3787,"HyperDash":false},{"StartTime":133875.0,"Position":256.6099,"HyperDash":false},{"StartTime":133937.0,"Position":289.899384,"HyperDash":false},{"StartTime":134000.0,"Position":322.431061,"HyperDash":false},{"StartTime":134062.0,"Position":371.9527,"HyperDash":false},{"StartTime":134125.0,"Position":392.617126,"HyperDash":false},{"StartTime":134187.0,"Position":422.877838,"HyperDash":false},{"StartTime":134250.0,"Position":425.129883,"HyperDash":false},{"StartTime":134312.0,"Position":404.693054,"HyperDash":false},{"StartTime":134375.0,"Position":409.929779,"HyperDash":false},{"StartTime":134437.0,"Position":384.0832,"HyperDash":false},{"StartTime":134500.0,"Position":354.885651,"HyperDash":false},{"StartTime":134562.0,"Position":326.547424,"HyperDash":false},{"StartTime":134625.0,"Position":301.508575,"HyperDash":false},{"StartTime":134687.0,"Position":255.1601,"HyperDash":false},{"StartTime":134750.0,"Position":222.486877,"HyperDash":false},{"StartTime":134812.0,"Position":183.853729,"HyperDash":false},{"StartTime":134875.0,"Position":145.138245,"HyperDash":false},{"StartTime":134937.0,"Position":107.848343,"HyperDash":false},{"StartTime":135000.0,"Position":58.21479,"HyperDash":false},{"StartTime":135062.0,"Position":57.82658,"HyperDash":false},{"StartTime":135125.0,"Position":20.1227779,"HyperDash":false},{"StartTime":135187.0,"Position":0.0,"HyperDash":false},{"StartTime":135250.0,"Position":0.0,"HyperDash":false},{"StartTime":135312.0,"Position":0.0,"HyperDash":false},{"StartTime":135375.0,"Position":0.05981236,"HyperDash":false},{"StartTime":135428.0,"Position":14.5409756,"HyperDash":false},{"StartTime":135482.0,"Position":36.1827965,"HyperDash":false},{"StartTime":135535.0,"Position":37.5372772,"HyperDash":false},{"StartTime":135625.0,"Position":103.892265,"HyperDash":false}]},{"StartTime":135875.0,"Objects":[{"StartTime":135875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":136000.0,"Objects":[{"StartTime":136000.0,"Position":256.0,"HyperDash":false}]},{"StartTime":136125.0,"Objects":[{"StartTime":136125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":136375.0,"Objects":[{"StartTime":136375.0,"Position":136.0,"HyperDash":false}]},{"StartTime":136625.0,"Objects":[{"StartTime":136625.0,"Position":132.0,"HyperDash":false}]},{"StartTime":136750.0,"Objects":[{"StartTime":136750.0,"Position":133.0,"HyperDash":false}]},{"StartTime":137000.0,"Objects":[{"StartTime":137000.0,"Position":256.0,"HyperDash":false}]},{"StartTime":137125.0,"Objects":[{"StartTime":137125.0,"Position":255.0,"HyperDash":false}]},{"StartTime":137250.0,"Objects":[{"StartTime":137250.0,"Position":256.0,"HyperDash":false}]},{"StartTime":137375.0,"Objects":[{"StartTime":137375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":137625.0,"Objects":[{"StartTime":137625.0,"Position":380.0,"HyperDash":false}]},{"StartTime":137875.0,"Objects":[{"StartTime":137875.0,"Position":376.0,"HyperDash":false}]},{"StartTime":138125.0,"Objects":[{"StartTime":138125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":138375.0,"Objects":[{"StartTime":138375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":138625.0,"Objects":[{"StartTime":138625.0,"Position":144.0,"HyperDash":false}]},{"StartTime":138750.0,"Objects":[{"StartTime":138750.0,"Position":144.0,"HyperDash":false}]},{"StartTime":139000.0,"Objects":[{"StartTime":139000.0,"Position":256.0,"HyperDash":false}]},{"StartTime":139125.0,"Objects":[{"StartTime":139125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":139250.0,"Objects":[{"StartTime":139250.0,"Position":256.0,"HyperDash":false}]},{"StartTime":139375.0,"Objects":[{"StartTime":139375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":139625.0,"Objects":[{"StartTime":139625.0,"Position":368.0,"HyperDash":false}]},{"StartTime":139875.0,"Objects":[{"StartTime":139875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":140000.0,"Objects":[{"StartTime":140000.0,"Position":256.0,"HyperDash":false}]},{"StartTime":140125.0,"Objects":[{"StartTime":140125.0,"Position":256.0,"HyperDash":false},{"StartTime":140178.0,"Position":227.121277,"HyperDash":false},{"StartTime":140232.0,"Position":201.278854,"HyperDash":false},{"StartTime":140285.0,"Position":210.432343,"HyperDash":false},{"StartTime":140374.0,"Position":256.095947,"HyperDash":false}]},{"StartTime":140625.0,"Objects":[{"StartTime":140625.0,"Position":332.0,"HyperDash":false}]},{"StartTime":140750.0,"Objects":[{"StartTime":140750.0,"Position":332.0,"HyperDash":false}]},{"StartTime":141000.0,"Objects":[{"StartTime":141000.0,"Position":332.0,"HyperDash":false}]},{"StartTime":141125.0,"Objects":[{"StartTime":141125.0,"Position":332.0,"HyperDash":false}]},{"StartTime":141250.0,"Objects":[{"StartTime":141250.0,"Position":332.0,"HyperDash":false}]},{"StartTime":141375.0,"Objects":[{"StartTime":141375.0,"Position":332.0,"HyperDash":false}]},{"StartTime":141625.0,"Objects":[{"StartTime":141625.0,"Position":180.0,"HyperDash":false}]},{"StartTime":141875.0,"Objects":[{"StartTime":141875.0,"Position":180.0,"HyperDash":false}]},{"StartTime":142125.0,"Objects":[{"StartTime":142125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":142375.0,"Objects":[{"StartTime":142375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":142625.0,"Objects":[{"StartTime":142625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":142750.0,"Objects":[{"StartTime":142750.0,"Position":256.0,"HyperDash":false}]},{"StartTime":143000.0,"Objects":[{"StartTime":143000.0,"Position":256.0,"HyperDash":false}]},{"StartTime":143125.0,"Objects":[{"StartTime":143125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":143250.0,"Objects":[{"StartTime":143250.0,"Position":256.0,"HyperDash":false}]},{"StartTime":143375.0,"Objects":[{"StartTime":143375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":143625.0,"Objects":[{"StartTime":143625.0,"Position":188.0,"HyperDash":false}]},{"StartTime":143875.0,"Objects":[{"StartTime":143875.0,"Position":324.0,"HyperDash":false}]},{"StartTime":144000.0,"Objects":[{"StartTime":144000.0,"Position":324.0,"HyperDash":false}]},{"StartTime":144125.0,"Objects":[{"StartTime":144125.0,"Position":324.0,"HyperDash":false},{"StartTime":144178.0,"Position":375.919983,"HyperDash":false},{"StartTime":144232.0,"Position":388.48,"HyperDash":false},{"StartTime":144285.0,"Position":424.4,"HyperDash":false},{"StartTime":144375.0,"Position":484.0,"HyperDash":false}]},{"StartTime":144625.0,"Objects":[{"StartTime":144625.0,"Position":392.0,"HyperDash":false}]},{"StartTime":144750.0,"Objects":[{"StartTime":144750.0,"Position":392.0,"HyperDash":false}]},{"StartTime":145000.0,"Objects":[{"StartTime":145000.0,"Position":324.0,"HyperDash":false}]},{"StartTime":145125.0,"Objects":[{"StartTime":145125.0,"Position":324.0,"HyperDash":false}]},{"StartTime":145250.0,"Objects":[{"StartTime":145250.0,"Position":324.0,"HyperDash":false}]},{"StartTime":145375.0,"Objects":[{"StartTime":145375.0,"Position":324.0,"HyperDash":false}]},{"StartTime":145625.0,"Objects":[{"StartTime":145625.0,"Position":188.0,"HyperDash":false}]},{"StartTime":145875.0,"Objects":[{"StartTime":145875.0,"Position":120.0,"HyperDash":false}]},{"StartTime":146125.0,"Objects":[{"StartTime":146125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":146375.0,"Objects":[{"StartTime":146375.0,"Position":256.0,"HyperDash":false}]},{"StartTime":146625.0,"Objects":[{"StartTime":146625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":146750.0,"Objects":[{"StartTime":146750.0,"Position":256.0,"HyperDash":false}]},{"StartTime":147000.0,"Objects":[{"StartTime":147000.0,"Position":176.0,"HyperDash":false}]},{"StartTime":147125.0,"Objects":[{"StartTime":147125.0,"Position":176.0,"HyperDash":false}]},{"StartTime":147250.0,"Objects":[{"StartTime":147250.0,"Position":176.0,"HyperDash":false}]},{"StartTime":147375.0,"Objects":[{"StartTime":147375.0,"Position":176.0,"HyperDash":false}]},{"StartTime":147625.0,"Objects":[{"StartTime":147625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":147875.0,"Objects":[{"StartTime":147875.0,"Position":336.0,"HyperDash":false}]},{"StartTime":148000.0,"Objects":[{"StartTime":148000.0,"Position":336.0,"HyperDash":false}]},{"StartTime":148125.0,"Objects":[{"StartTime":148125.0,"Position":336.0,"HyperDash":false},{"StartTime":148178.0,"Position":375.538025,"HyperDash":false},{"StartTime":148231.0,"Position":390.979462,"HyperDash":false},{"StartTime":148284.0,"Position":386.895447,"HyperDash":false},{"StartTime":148374.0,"Position":370.6822,"HyperDash":false}]},{"StartTime":148625.0,"Objects":[{"StartTime":148625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":148750.0,"Objects":[{"StartTime":148750.0,"Position":240.0,"HyperDash":false}]},{"StartTime":149000.0,"Objects":[{"StartTime":149000.0,"Position":240.0,"HyperDash":false}]},{"StartTime":149125.0,"Objects":[{"StartTime":149125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":149250.0,"Objects":[{"StartTime":149250.0,"Position":272.0,"HyperDash":false}]},{"StartTime":149375.0,"Objects":[{"StartTime":149375.0,"Position":288.0,"HyperDash":false}]},{"StartTime":149625.0,"Objects":[{"StartTime":149625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":149875.0,"Objects":[{"StartTime":149875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":150125.0,"Objects":[{"StartTime":150125.0,"Position":116.0,"HyperDash":false}]},{"StartTime":150250.0,"Objects":[{"StartTime":150250.0,"Position":120.0,"HyperDash":false}]},{"StartTime":150375.0,"Objects":[{"StartTime":150375.0,"Position":132.0,"HyperDash":false}]},{"StartTime":150500.0,"Objects":[{"StartTime":150500.0,"Position":152.0,"HyperDash":false}]},{"StartTime":150625.0,"Objects":[{"StartTime":150625.0,"Position":176.0,"HyperDash":false}]},{"StartTime":150750.0,"Objects":[{"StartTime":150750.0,"Position":208.0,"HyperDash":false}]},{"StartTime":150875.0,"Objects":[{"StartTime":150875.0,"Position":232.0,"HyperDash":false}]},{"StartTime":151000.0,"Objects":[{"StartTime":151000.0,"Position":248.0,"HyperDash":false}]},{"StartTime":151125.0,"Objects":[{"StartTime":151125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":151250.0,"Objects":[{"StartTime":151250.0,"Position":260.0,"HyperDash":false}]},{"StartTime":151375.0,"Objects":[{"StartTime":151375.0,"Position":272.0,"HyperDash":false}]},{"StartTime":151500.0,"Objects":[{"StartTime":151500.0,"Position":292.0,"HyperDash":false}]},{"StartTime":151625.0,"Objects":[{"StartTime":151625.0,"Position":316.0,"HyperDash":false}]},{"StartTime":151750.0,"Objects":[{"StartTime":151750.0,"Position":348.0,"HyperDash":false}]},{"StartTime":151875.0,"Objects":[{"StartTime":151875.0,"Position":372.0,"HyperDash":false}]},{"StartTime":152000.0,"Objects":[{"StartTime":152000.0,"Position":388.0,"HyperDash":false}]},{"StartTime":152125.0,"Objects":[{"StartTime":152125.0,"Position":404.0,"HyperDash":false},{"StartTime":152178.0,"Position":429.642151,"HyperDash":false},{"StartTime":152232.0,"Position":425.184479,"HyperDash":false},{"StartTime":152285.0,"Position":392.507416,"HyperDash":false},{"StartTime":152375.0,"Position":342.072266,"HyperDash":false}]},{"StartTime":152625.0,"Objects":[{"StartTime":152625.0,"Position":108.0,"HyperDash":false},{"StartTime":152678.0,"Position":112.349617,"HyperDash":false},{"StartTime":152732.0,"Position":112.903786,"HyperDash":false},{"StartTime":152785.0,"Position":119.761673,"HyperDash":false},{"StartTime":152874.0,"Position":169.927719,"HyperDash":false}]},{"StartTime":153125.0,"Objects":[{"StartTime":153125.0,"Position":256.0,"HyperDash":false},{"StartTime":153250.0,"Position":256.0,"HyperDash":false}]},{"StartTime":153375.0,"Objects":[{"StartTime":153375.0,"Position":256.0,"HyperDash":false},{"StartTime":153437.0,"Position":269.0,"HyperDash":false},{"StartTime":153500.0,"Position":241.0,"HyperDash":false},{"StartTime":153562.0,"Position":247.0,"HyperDash":false},{"StartTime":153625.0,"Position":256.0,"HyperDash":false},{"StartTime":153678.0,"Position":244.0,"HyperDash":false},{"StartTime":153732.0,"Position":258.0,"HyperDash":false},{"StartTime":153785.0,"Position":240.0,"HyperDash":false},{"StartTime":153875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":154125.0,"Objects":[{"StartTime":154125.0,"Position":360.0,"HyperDash":false}]},{"StartTime":154250.0,"Objects":[{"StartTime":154250.0,"Position":360.0,"HyperDash":false}]},{"StartTime":154375.0,"Objects":[{"StartTime":154375.0,"Position":360.0,"HyperDash":false}]},{"StartTime":154625.0,"Objects":[{"StartTime":154625.0,"Position":256.0,"HyperDash":false}]},{"StartTime":154750.0,"Objects":[{"StartTime":154750.0,"Position":256.0,"HyperDash":false}]},{"StartTime":154875.0,"Objects":[{"StartTime":154875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":155125.0,"Objects":[{"StartTime":155125.0,"Position":154.0,"HyperDash":false}]},{"StartTime":155250.0,"Objects":[{"StartTime":155250.0,"Position":154.0,"HyperDash":false}]},{"StartTime":155375.0,"Objects":[{"StartTime":155375.0,"Position":155.0,"HyperDash":false},{"StartTime":155437.0,"Position":134.040146,"HyperDash":false},{"StartTime":155500.0,"Position":134.7992,"HyperDash":false},{"StartTime":155562.0,"Position":112.444061,"HyperDash":false},{"StartTime":155625.0,"Position":165.451813,"HyperDash":false},{"StartTime":155678.0,"Position":137.274612,"HyperDash":false},{"StartTime":155732.0,"Position":123.117592,"HyperDash":false},{"StartTime":155785.0,"Position":139.026031,"HyperDash":false},{"StartTime":155874.0,"Position":155.0,"HyperDash":false}]},{"StartTime":156000.0,"Objects":[{"StartTime":156000.0,"Position":163.0,"HyperDash":false}]},{"StartTime":156125.0,"Objects":[{"StartTime":156125.0,"Position":163.0,"HyperDash":false}]},{"StartTime":156250.0,"Objects":[{"StartTime":156250.0,"Position":163.0,"HyperDash":false},{"StartTime":156312.0,"Position":183.5915,"HyperDash":false},{"StartTime":156375.0,"Position":203.198776,"HyperDash":false},{"StartTime":156437.0,"Position":232.230286,"HyperDash":false},{"StartTime":156500.0,"Position":268.618439,"HyperDash":false},{"StartTime":156562.0,"Position":314.36087,"HyperDash":false},{"StartTime":156625.0,"Position":335.6506,"HyperDash":false},{"StartTime":156687.0,"Position":380.1404,"HyperDash":false},{"StartTime":156750.0,"Position":422.05127,"HyperDash":false},{"StartTime":156874.0,"Position":473.144562,"HyperDash":false}]},{"StartTime":157125.0,"Objects":[{"StartTime":157125.0,"Position":320.0,"HyperDash":false},{"StartTime":157187.0,"Position":278.6829,"HyperDash":false},{"StartTime":157250.0,"Position":245.222778,"HyperDash":false},{"StartTime":157312.0,"Position":218.502289,"HyperDash":false},{"StartTime":157375.0,"Position":221.008591,"HyperDash":false},{"StartTime":157428.0,"Position":240.596039,"HyperDash":false},{"StartTime":157482.0,"Position":248.418259,"HyperDash":false},{"StartTime":157535.0,"Position":260.110321,"HyperDash":false},{"StartTime":157624.0,"Position":317.0907,"HyperDash":false}]},{"StartTime":157750.0,"Objects":[{"StartTime":157750.0,"Position":348.0,"HyperDash":false}]},{"StartTime":157875.0,"Objects":[{"StartTime":157875.0,"Position":380.0,"HyperDash":false}]},{"StartTime":158000.0,"Objects":[{"StartTime":158000.0,"Position":404.0,"HyperDash":false}]},{"StartTime":158125.0,"Objects":[{"StartTime":158125.0,"Position":412.0,"HyperDash":false}]},{"StartTime":158250.0,"Objects":[{"StartTime":158250.0,"Position":412.0,"HyperDash":false}]},{"StartTime":158375.0,"Objects":[{"StartTime":158375.0,"Position":404.0,"HyperDash":false}]},{"StartTime":158625.0,"Objects":[{"StartTime":158625.0,"Position":264.0,"HyperDash":false},{"StartTime":158687.0,"Position":234.814957,"HyperDash":false},{"StartTime":158750.0,"Position":191.04628,"HyperDash":false},{"StartTime":158875.0,"Position":264.0,"HyperDash":false}]},{"StartTime":159125.0,"Objects":[{"StartTime":159125.0,"Position":164.0,"HyperDash":false},{"StartTime":159187.0,"Position":197.185043,"HyperDash":false},{"StartTime":159250.0,"Position":236.95372,"HyperDash":false},{"StartTime":159375.0,"Position":164.0,"HyperDash":false}]},{"StartTime":159625.0,"Objects":[{"StartTime":159625.0,"Position":56.0,"HyperDash":false}]},{"StartTime":159875.0,"Objects":[{"StartTime":159875.0,"Position":64.0,"HyperDash":false}]},{"StartTime":160000.0,"Objects":[{"StartTime":160000.0,"Position":64.0,"HyperDash":false}]},{"StartTime":160125.0,"Objects":[{"StartTime":160125.0,"Position":64.0,"HyperDash":false},{"StartTime":160187.0,"Position":64.51918,"HyperDash":false},{"StartTime":160250.0,"Position":26.7402878,"HyperDash":false},{"StartTime":160375.0,"Position":64.0,"HyperDash":false}]},{"StartTime":160500.0,"Objects":[{"StartTime":160500.0,"Position":128.0,"HyperDash":false},{"StartTime":160562.0,"Position":132.164291,"HyperDash":false},{"StartTime":160625.0,"Position":134.379623,"HyperDash":false},{"StartTime":160750.0,"Position":128.0,"HyperDash":false}]},{"StartTime":160875.0,"Objects":[{"StartTime":160875.0,"Position":192.0,"HyperDash":false},{"StartTime":160937.0,"Position":189.164291,"HyperDash":false},{"StartTime":161000.0,"Position":198.379623,"HyperDash":false},{"StartTime":161125.0,"Position":192.0,"HyperDash":false}]},{"StartTime":161250.0,"Objects":[{"StartTime":161250.0,"Position":240.0,"HyperDash":false},{"StartTime":161312.0,"Position":248.7879,"HyperDash":false},{"StartTime":161375.0,"Position":289.975616,"HyperDash":false},{"StartTime":161500.0,"Position":240.0,"HyperDash":false}]},{"StartTime":161625.0,"Objects":[{"StartTime":161625.0,"Position":284.0,"HyperDash":false},{"StartTime":161687.0,"Position":327.2897,"HyperDash":false},{"StartTime":161750.0,"Position":339.019562,"HyperDash":false},{"StartTime":161875.0,"Position":284.0,"HyperDash":false}]},{"StartTime":162000.0,"Objects":[{"StartTime":162000.0,"Position":328.0,"HyperDash":false},{"StartTime":162062.0,"Position":364.361755,"HyperDash":false},{"StartTime":162124.0,"Position":407.040955,"HyperDash":false},{"StartTime":162249.0,"Position":328.0,"HyperDash":false}]},{"StartTime":162375.0,"Objects":[{"StartTime":162375.0,"Position":308.0,"HyperDash":false},{"StartTime":162437.0,"Position":269.638245,"HyperDash":false},{"StartTime":162499.0,"Position":228.959045,"HyperDash":false},{"StartTime":162624.0,"Position":308.0,"HyperDash":false}]},{"StartTime":162750.0,"Objects":[{"StartTime":162750.0,"Position":340.0,"HyperDash":false},{"StartTime":162812.0,"Position":374.361755,"HyperDash":false},{"StartTime":162874.0,"Position":419.040955,"HyperDash":false},{"StartTime":162999.0,"Position":340.0,"HyperDash":false}]},{"StartTime":163125.0,"Objects":[{"StartTime":163125.0,"Position":284.0,"HyperDash":false},{"StartTime":163187.0,"Position":280.849731,"HyperDash":false},{"StartTime":163249.0,"Position":271.649841,"HyperDash":false},{"StartTime":163374.0,"Position":284.0,"HyperDash":false}]},{"StartTime":163500.0,"Objects":[{"StartTime":163500.0,"Position":224.0,"HyperDash":false},{"StartTime":163562.0,"Position":227.849731,"HyperDash":false},{"StartTime":163624.0,"Position":211.649857,"HyperDash":false},{"StartTime":163749.0,"Position":224.0,"HyperDash":false}]},{"StartTime":163875.0,"Objects":[{"StartTime":163875.0,"Position":180.0,"HyperDash":false},{"StartTime":163937.0,"Position":134.564423,"HyperDash":false},{"StartTime":163999.0,"Position":102.8189,"HyperDash":false},{"StartTime":164124.0,"Position":180.0,"HyperDash":false}]},{"StartTime":164250.0,"Objects":[{"StartTime":164250.0,"Position":144.0,"HyperDash":false},{"StartTime":164312.0,"Position":107.832245,"HyperDash":false},{"StartTime":164375.0,"Position":79.14566,"HyperDash":false},{"StartTime":164500.0,"Position":144.0,"HyperDash":false}]},{"StartTime":164625.0,"Objects":[{"StartTime":164625.0,"Position":168.0,"HyperDash":false},{"StartTime":164687.0,"Position":182.167755,"HyperDash":false},{"StartTime":164750.0,"Position":232.85434,"HyperDash":false},{"StartTime":164875.0,"Position":168.0,"HyperDash":false}]},{"StartTime":165000.0,"Objects":[{"StartTime":165000.0,"Position":136.0,"HyperDash":false},{"StartTime":165062.0,"Position":117.871719,"HyperDash":false},{"StartTime":165124.0,"Position":101.605316,"HyperDash":false},{"StartTime":165249.0,"Position":136.0,"HyperDash":false}]},{"StartTime":165375.0,"Objects":[{"StartTime":165375.0,"Position":188.0,"HyperDash":false},{"StartTime":165437.0,"Position":220.128281,"HyperDash":false},{"StartTime":165499.0,"Position":222.394684,"HyperDash":false},{"StartTime":165624.0,"Position":188.0,"HyperDash":false}]},{"StartTime":165750.0,"Objects":[{"StartTime":165750.0,"Position":236.0,"HyperDash":false}]},{"StartTime":165875.0,"Objects":[{"StartTime":165875.0,"Position":236.0,"HyperDash":false}]},{"StartTime":166125.0,"Objects":[{"StartTime":166125.0,"Position":364.0,"HyperDash":false},{"StartTime":166187.0,"Position":369.388123,"HyperDash":false},{"StartTime":166250.0,"Position":391.656616,"HyperDash":false},{"StartTime":166312.0,"Position":357.6028,"HyperDash":false},{"StartTime":166375.0,"Position":309.5534,"HyperDash":false},{"StartTime":166499.0,"Position":282.373474,"HyperDash":false}]},{"StartTime":166625.0,"Objects":[{"StartTime":166625.0,"Position":264.0,"HyperDash":false},{"StartTime":166687.0,"Position":284.388123,"HyperDash":false},{"StartTime":166750.0,"Position":283.656616,"HyperDash":false},{"StartTime":166812.0,"Position":260.602844,"HyperDash":false},{"StartTime":166875.0,"Position":209.5534,"HyperDash":false},{"StartTime":166999.0,"Position":182.373489,"HyperDash":false}]},{"StartTime":167125.0,"Objects":[{"StartTime":167125.0,"Position":192.0,"HyperDash":false}]},{"StartTime":167375.0,"Objects":[{"StartTime":167375.0,"Position":320.0,"HyperDash":false}]},{"StartTime":167625.0,"Objects":[{"StartTime":167625.0,"Position":192.0,"HyperDash":false}]},{"StartTime":167750.0,"Objects":[{"StartTime":167750.0,"Position":256.0,"HyperDash":false}]},{"StartTime":167875.0,"Objects":[{"StartTime":167875.0,"Position":320.0,"HyperDash":false}]},{"StartTime":168125.0,"Objects":[{"StartTime":168125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":168250.0,"Objects":[{"StartTime":168250.0,"Position":193.0,"HyperDash":false},{"StartTime":168308.0,"Position":488.0,"HyperDash":false},{"StartTime":168367.0,"Position":314.0,"HyperDash":false},{"StartTime":168425.0,"Position":135.0,"HyperDash":false},{"StartTime":168484.0,"Position":399.0,"HyperDash":false},{"StartTime":168542.0,"Position":404.0,"HyperDash":false},{"StartTime":168601.0,"Position":152.0,"HyperDash":false},{"StartTime":168660.0,"Position":353.0,"HyperDash":false},{"StartTime":168718.0,"Position":358.0,"HyperDash":false},{"StartTime":168777.0,"Position":447.0,"HyperDash":false},{"StartTime":168835.0,"Position":222.0,"HyperDash":false},{"StartTime":168894.0,"Position":382.0,"HyperDash":false},{"StartTime":168953.0,"Position":433.0,"HyperDash":false},{"StartTime":169011.0,"Position":450.0,"HyperDash":false},{"StartTime":169070.0,"Position":326.0,"HyperDash":false},{"StartTime":169128.0,"Position":414.0,"HyperDash":false},{"StartTime":169187.0,"Position":285.0,"HyperDash":false},{"StartTime":169246.0,"Position":336.0,"HyperDash":false},{"StartTime":169304.0,"Position":509.0,"HyperDash":false},{"StartTime":169363.0,"Position":334.0,"HyperDash":false},{"StartTime":169421.0,"Position":72.0,"HyperDash":false},{"StartTime":169480.0,"Position":425.0,"HyperDash":false},{"StartTime":169539.0,"Position":451.0,"HyperDash":false},{"StartTime":169597.0,"Position":220.0,"HyperDash":false},{"StartTime":169656.0,"Position":25.0,"HyperDash":false},{"StartTime":169714.0,"Position":77.0,"HyperDash":false},{"StartTime":169773.0,"Position":509.0,"HyperDash":false},{"StartTime":169832.0,"Position":90.0,"HyperDash":false},{"StartTime":169890.0,"Position":118.0,"HyperDash":false},{"StartTime":169949.0,"Position":58.0,"HyperDash":false},{"StartTime":170007.0,"Position":12.0,"HyperDash":false},{"StartTime":170066.0,"Position":215.0,"HyperDash":false},{"StartTime":170125.0,"Position":487.0,"HyperDash":false}]},{"StartTime":171125.0,"Objects":[{"StartTime":171125.0,"Position":446.0,"HyperDash":false},{"StartTime":171187.0,"Position":491.0,"HyperDash":false},{"StartTime":171250.0,"Position":459.0,"HyperDash":false},{"StartTime":171312.0,"Position":37.0,"HyperDash":false},{"StartTime":171375.0,"Position":291.0,"HyperDash":false},{"StartTime":171437.0,"Position":315.0,"HyperDash":false},{"StartTime":171500.0,"Position":35.0,"HyperDash":false},{"StartTime":171562.0,"Position":208.0,"HyperDash":false},{"StartTime":171625.0,"Position":504.0,"HyperDash":false},{"StartTime":171687.0,"Position":296.0,"HyperDash":false},{"StartTime":171750.0,"Position":105.0,"HyperDash":false},{"StartTime":171812.0,"Position":488.0,"HyperDash":false},{"StartTime":171875.0,"Position":230.0,"HyperDash":false},{"StartTime":171937.0,"Position":446.0,"HyperDash":false},{"StartTime":172000.0,"Position":241.0,"HyperDash":false},{"StartTime":172062.0,"Position":413.0,"HyperDash":false},{"StartTime":172125.0,"Position":357.0,"HyperDash":false}]},{"StartTime":172375.0,"Objects":[{"StartTime":172375.0,"Position":48.0,"HyperDash":false}]},{"StartTime":172625.0,"Objects":[{"StartTime":172625.0,"Position":20.0,"HyperDash":false},{"StartTime":172678.0,"Position":23.1916313,"HyperDash":false},{"StartTime":172732.0,"Position":25.55497,"HyperDash":false},{"StartTime":172785.0,"Position":75.26404,"HyperDash":false},{"StartTime":172875.0,"Position":108.478035,"HyperDash":false}]},{"StartTime":173125.0,"Objects":[{"StartTime":173125.0,"Position":240.0,"HyperDash":false}]},{"StartTime":173375.0,"Objects":[{"StartTime":173375.0,"Position":200.0,"HyperDash":false}]},{"StartTime":173625.0,"Objects":[{"StartTime":173625.0,"Position":324.0,"HyperDash":false},{"StartTime":173678.0,"Position":349.476471,"HyperDash":false},{"StartTime":173732.0,"Position":378.649323,"HyperDash":false},{"StartTime":173785.0,"Position":384.945282,"HyperDash":false},{"StartTime":173875.0,"Position":412.1426,"HyperDash":false}]},{"StartTime":174000.0,"Objects":[{"StartTime":174000.0,"Position":412.0,"HyperDash":false}]},{"StartTime":174125.0,"Objects":[{"StartTime":174125.0,"Position":412.0,"HyperDash":false},{"StartTime":174178.0,"Position":426.1397,"HyperDash":false},{"StartTime":174232.0,"Position":445.433044,"HyperDash":false},{"StartTime":174285.0,"Position":425.572754,"HyperDash":false},{"StartTime":174375.0,"Position":450.394928,"HyperDash":false}]},{"StartTime":174625.0,"Objects":[{"StartTime":174625.0,"Position":398.0,"HyperDash":false},{"StartTime":174678.0,"Position":380.028442,"HyperDash":false},{"StartTime":174732.0,"Position":327.434753,"HyperDash":false},{"StartTime":174785.0,"Position":306.4632,"HyperDash":false},{"StartTime":174875.0,"Position":242.473724,"HyperDash":false}]},{"StartTime":175000.0,"Objects":[{"StartTime":175000.0,"Position":245.0,"HyperDash":false}]},{"StartTime":175125.0,"Objects":[{"StartTime":175125.0,"Position":245.0,"HyperDash":false},{"StartTime":175178.0,"Position":247.860275,"HyperDash":false},{"StartTime":175232.0,"Position":229.566971,"HyperDash":false},{"StartTime":175285.0,"Position":219.427246,"HyperDash":false},{"StartTime":175375.0,"Position":206.605072,"HyperDash":false}]},{"StartTime":175625.0,"Objects":[{"StartTime":175625.0,"Position":259.0,"HyperDash":false},{"StartTime":175678.0,"Position":271.971558,"HyperDash":false},{"StartTime":175732.0,"Position":338.565247,"HyperDash":false},{"StartTime":175785.0,"Position":339.5368,"HyperDash":false},{"StartTime":175875.0,"Position":414.526276,"HyperDash":false}]},{"StartTime":176125.0,"Objects":[{"StartTime":176125.0,"Position":424.0,"HyperDash":false}]},{"StartTime":176375.0,"Objects":[{"StartTime":176375.0,"Position":272.0,"HyperDash":false}]},{"StartTime":176625.0,"Objects":[{"StartTime":176625.0,"Position":116.0,"HyperDash":false}]},{"StartTime":176875.0,"Objects":[{"StartTime":176875.0,"Position":173.0,"HyperDash":false},{"StartTime":176937.0,"Position":220.433136,"HyperDash":false},{"StartTime":177000.0,"Position":248.325027,"HyperDash":false},{"StartTime":177062.0,"Position":256.667,"HyperDash":false},{"StartTime":177125.0,"Position":270.778076,"HyperDash":false},{"StartTime":177187.0,"Position":271.125366,"HyperDash":false},{"StartTime":177250.0,"Position":267.005768,"HyperDash":false},{"StartTime":177312.0,"Position":259.9013,"HyperDash":false},{"StartTime":177375.0,"Position":212.426208,"HyperDash":false},{"StartTime":177437.0,"Position":239.901321,"HyperDash":false},{"StartTime":177500.0,"Position":249.239349,"HyperDash":false},{"StartTime":177562.0,"Position":285.125366,"HyperDash":false},{"StartTime":177625.0,"Position":270.676758,"HyperDash":false},{"StartTime":177678.0,"Position":275.8453,"HyperDash":false},{"StartTime":177732.0,"Position":255.82901,"HyperDash":false},{"StartTime":177785.0,"Position":207.305466,"HyperDash":false},{"StartTime":177874.0,"Position":173.0,"HyperDash":false}]},{"StartTime":178125.0,"Objects":[{"StartTime":178125.0,"Position":28.0,"HyperDash":false},{"StartTime":178187.0,"Position":78.55116,"HyperDash":false},{"StartTime":178250.0,"Position":102.707985,"HyperDash":false},{"StartTime":178312.0,"Position":129.259155,"HyperDash":false},{"StartTime":178375.0,"Position":179.41597,"HyperDash":false},{"StartTime":178428.0,"Position":226.516159,"HyperDash":false},{"StartTime":178482.0,"Position":240.222,"HyperDash":false},{"StartTime":178535.0,"Position":283.3222,"HyperDash":false},{"StartTime":178625.0,"Position":330.83194,"HyperDash":false}]},{"StartTime":178875.0,"Objects":[{"StartTime":178875.0,"Position":172.0,"HyperDash":false},{"StartTime":178937.0,"Position":221.551163,"HyperDash":false},{"StartTime":179000.0,"Position":253.707977,"HyperDash":false},{"StartTime":179062.0,"Position":274.259155,"HyperDash":false},{"StartTime":179125.0,"Position":323.415955,"HyperDash":false},{"StartTime":179178.0,"Position":344.516174,"HyperDash":false},{"StartTime":179232.0,"Position":379.222,"HyperDash":false},{"StartTime":179285.0,"Position":429.3222,"HyperDash":false},{"StartTime":179375.0,"Position":474.83194,"HyperDash":false}]},{"StartTime":179625.0,"Objects":[{"StartTime":179625.0,"Position":384.0,"HyperDash":false},{"StartTime":179678.0,"Position":348.327026,"HyperDash":false},{"StartTime":179732.0,"Position":316.224579,"HyperDash":false},{"StartTime":179785.0,"Position":267.12973,"HyperDash":false},{"StartTime":179875.0,"Position":244.098541,"HyperDash":false}]},{"StartTime":180000.0,"Objects":[{"StartTime":180000.0,"Position":244.0,"HyperDash":false}]},{"StartTime":180125.0,"Objects":[{"StartTime":180125.0,"Position":244.0,"HyperDash":false},{"StartTime":180178.0,"Position":217.455292,"HyperDash":false},{"StartTime":180232.0,"Position":186.277634,"HyperDash":false},{"StartTime":180285.0,"Position":129.732925,"HyperDash":false},{"StartTime":180375.0,"Position":85.77019,"HyperDash":false}]},{"StartTime":180625.0,"Objects":[{"StartTime":180625.0,"Position":100.0,"HyperDash":false},{"StartTime":180678.0,"Position":146.386475,"HyperDash":false},{"StartTime":180732.0,"Position":185.4029,"HyperDash":false},{"StartTime":180785.0,"Position":189.789368,"HyperDash":false},{"StartTime":180875.0,"Position":257.4834,"HyperDash":false}]},{"StartTime":181000.0,"Objects":[{"StartTime":181000.0,"Position":257.0,"HyperDash":false}]},{"StartTime":181125.0,"Objects":[{"StartTime":181125.0,"Position":256.0,"HyperDash":false},{"StartTime":181178.0,"Position":273.4897,"HyperDash":false},{"StartTime":181231.0,"Position":332.9794,"HyperDash":false},{"StartTime":181284.0,"Position":358.4691,"HyperDash":false},{"StartTime":181374.0,"Position":413.338379,"HyperDash":false}]},{"StartTime":181625.0,"Objects":[{"StartTime":181625.0,"Position":426.0,"HyperDash":false},{"StartTime":181678.0,"Position":383.4294,"HyperDash":false},{"StartTime":181732.0,"Position":353.2254,"HyperDash":false},{"StartTime":181785.0,"Position":325.654816,"HyperDash":false},{"StartTime":181875.0,"Position":267.648163,"HyperDash":false}]},{"StartTime":182000.0,"Objects":[{"StartTime":182000.0,"Position":267.0,"HyperDash":false}]},{"StartTime":182125.0,"Objects":[{"StartTime":182125.0,"Position":267.0,"HyperDash":false},{"StartTime":182178.0,"Position":226.982559,"HyperDash":false},{"StartTime":182232.0,"Position":205.749466,"HyperDash":false},{"StartTime":182285.0,"Position":176.327576,"HyperDash":false},{"StartTime":182375.0,"Position":168.9247,"HyperDash":false}]},{"StartTime":182625.0,"Objects":[{"StartTime":182625.0,"Position":140.0,"HyperDash":false},{"StartTime":182678.0,"Position":155.139557,"HyperDash":false},{"StartTime":182731.0,"Position":203.985977,"HyperDash":false},{"StartTime":182784.0,"Position":216.5605,"HyperDash":false},{"StartTime":182874.0,"Position":238.0753,"HyperDash":false}]},{"StartTime":183125.0,"Objects":[{"StartTime":183125.0,"Position":62.0,"HyperDash":false},{"StartTime":183178.0,"Position":70.6348648,"HyperDash":false},{"StartTime":183232.0,"Position":74.16411,"HyperDash":false},{"StartTime":183285.0,"Position":122.076561,"HyperDash":false},{"StartTime":183375.0,"Position":173.7103,"HyperDash":false}]},{"StartTime":183625.0,"Objects":[{"StartTime":183625.0,"Position":348.0,"HyperDash":false},{"StartTime":183678.0,"Position":324.143158,"HyperDash":false},{"StartTime":183732.0,"Position":333.585327,"HyperDash":false},{"StartTime":183785.0,"Position":270.711,"HyperDash":false},{"StartTime":183874.0,"Position":236.603912,"HyperDash":false}]},{"StartTime":184125.0,"Objects":[{"StartTime":184125.0,"Position":64.0,"HyperDash":false}]},{"StartTime":184250.0,"Objects":[{"StartTime":184250.0,"Position":488.0,"HyperDash":false},{"StartTime":184335.0,"Position":482.0,"HyperDash":false},{"StartTime":184421.0,"Position":321.0,"HyperDash":false},{"StartTime":184507.0,"Position":474.0,"HyperDash":false},{"StartTime":184593.0,"Position":252.0,"HyperDash":false},{"StartTime":184679.0,"Position":247.0,"HyperDash":false},{"StartTime":184765.0,"Position":406.0,"HyperDash":false},{"StartTime":184851.0,"Position":319.0,"HyperDash":false},{"StartTime":184937.0,"Position":253.0,"HyperDash":false},{"StartTime":185023.0,"Position":411.0,"HyperDash":false},{"StartTime":185109.0,"Position":205.0,"HyperDash":false},{"StartTime":185195.0,"Position":54.0,"HyperDash":false},{"StartTime":185281.0,"Position":224.0,"HyperDash":false},{"StartTime":185367.0,"Position":465.0,"HyperDash":false},{"StartTime":185453.0,"Position":432.0,"HyperDash":false},{"StartTime":185539.0,"Position":108.0,"HyperDash":false},{"StartTime":185625.0,"Position":95.0,"HyperDash":false}]},{"StartTime":186125.0,"Objects":[{"StartTime":186125.0,"Position":48.0,"HyperDash":false},{"StartTime":186187.0,"Position":89.47744,"HyperDash":false},{"StartTime":186250.0,"Position":93.06244,"HyperDash":false},{"StartTime":186312.0,"Position":160.382751,"HyperDash":false},{"StartTime":186375.0,"Position":190.718857,"HyperDash":false},{"StartTime":186437.0,"Position":209.265518,"HyperDash":false},{"StartTime":186500.0,"Position":273.188416,"HyperDash":false},{"StartTime":186562.0,"Position":294.6259,"HyperDash":false},{"StartTime":186625.0,"Position":321.75354,"HyperDash":false},{"StartTime":186678.0,"Position":352.728241,"HyperDash":false},{"StartTime":186732.0,"Position":377.1885,"HyperDash":false},{"StartTime":186785.0,"Position":409.4063,"HyperDash":false},{"StartTime":186874.0,"Position":463.955,"HyperDash":false}]},{"StartTime":187125.0,"Objects":[{"StartTime":187125.0,"Position":328.0,"HyperDash":false},{"StartTime":187178.0,"Position":313.795776,"HyperDash":false},{"StartTime":187232.0,"Position":325.474457,"HyperDash":false},{"StartTime":187285.0,"Position":313.270233,"HyperDash":false},{"StartTime":187375.0,"Position":298.734741,"HyperDash":false}]},{"StartTime":187625.0,"Objects":[{"StartTime":187625.0,"Position":184.0,"HyperDash":false},{"StartTime":187678.0,"Position":198.204239,"HyperDash":false},{"StartTime":187732.0,"Position":213.525543,"HyperDash":false},{"StartTime":187785.0,"Position":188.729767,"HyperDash":false},{"StartTime":187875.0,"Position":213.265274,"HyperDash":false}]},{"StartTime":188125.0,"Objects":[{"StartTime":188125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":188250.0,"Objects":[{"StartTime":188250.0,"Position":175.0,"HyperDash":false},{"StartTime":188335.0,"Position":48.0,"HyperDash":false},{"StartTime":188421.0,"Position":307.0,"HyperDash":false},{"StartTime":188507.0,"Position":375.0,"HyperDash":false},{"StartTime":188593.0,"Position":149.0,"HyperDash":false},{"StartTime":188679.0,"Position":250.0,"HyperDash":false},{"StartTime":188765.0,"Position":142.0,"HyperDash":false},{"StartTime":188851.0,"Position":170.0,"HyperDash":false},{"StartTime":188937.0,"Position":281.0,"HyperDash":false},{"StartTime":189023.0,"Position":444.0,"HyperDash":false},{"StartTime":189109.0,"Position":414.0,"HyperDash":false},{"StartTime":189195.0,"Position":321.0,"HyperDash":false},{"StartTime":189281.0,"Position":328.0,"HyperDash":false},{"StartTime":189367.0,"Position":32.0,"HyperDash":false},{"StartTime":189453.0,"Position":259.0,"HyperDash":false},{"StartTime":189539.0,"Position":169.0,"HyperDash":false},{"StartTime":189625.0,"Position":207.0,"HyperDash":false}]},{"StartTime":190125.0,"Objects":[{"StartTime":190125.0,"Position":464.0,"HyperDash":false},{"StartTime":190187.0,"Position":452.522552,"HyperDash":false},{"StartTime":190250.0,"Position":408.937561,"HyperDash":false},{"StartTime":190312.0,"Position":358.617249,"HyperDash":false},{"StartTime":190375.0,"Position":321.281128,"HyperDash":false},{"StartTime":190437.0,"Position":281.7345,"HyperDash":false},{"StartTime":190500.0,"Position":240.811569,"HyperDash":false},{"StartTime":190562.0,"Position":234.374115,"HyperDash":false},{"StartTime":190625.0,"Position":190.246445,"HyperDash":false},{"StartTime":190678.0,"Position":161.271759,"HyperDash":false},{"StartTime":190732.0,"Position":103.811485,"HyperDash":false},{"StartTime":190785.0,"Position":84.59368,"HyperDash":false},{"StartTime":190874.0,"Position":48.04496,"HyperDash":false}]},{"StartTime":191125.0,"Objects":[{"StartTime":191125.0,"Position":184.0,"HyperDash":false},{"StartTime":191178.0,"Position":177.204239,"HyperDash":false},{"StartTime":191232.0,"Position":205.525543,"HyperDash":false},{"StartTime":191285.0,"Position":211.729767,"HyperDash":false},{"StartTime":191375.0,"Position":213.265274,"HyperDash":false}]},{"StartTime":191625.0,"Objects":[{"StartTime":191625.0,"Position":328.0,"HyperDash":false},{"StartTime":191678.0,"Position":303.795776,"HyperDash":false},{"StartTime":191732.0,"Position":318.474457,"HyperDash":false},{"StartTime":191785.0,"Position":296.270233,"HyperDash":false},{"StartTime":191875.0,"Position":298.734741,"HyperDash":false}]},{"StartTime":192125.0,"Objects":[{"StartTime":192125.0,"Position":164.0,"HyperDash":false}]},{"StartTime":192375.0,"Objects":[{"StartTime":192375.0,"Position":28.0,"HyperDash":false}]},{"StartTime":192625.0,"Objects":[{"StartTime":192625.0,"Position":28.0,"HyperDash":false}]},{"StartTime":192875.0,"Objects":[{"StartTime":192875.0,"Position":128.0,"HyperDash":false},{"StartTime":192937.0,"Position":126.887405,"HyperDash":false},{"StartTime":193000.0,"Position":175.597244,"HyperDash":false},{"StartTime":193062.0,"Position":198.553162,"HyperDash":false},{"StartTime":193125.0,"Position":235.7683,"HyperDash":false},{"StartTime":193187.0,"Position":291.259583,"HyperDash":false},{"StartTime":193250.0,"Position":330.488678,"HyperDash":false},{"StartTime":193312.0,"Position":338.450653,"HyperDash":false},{"StartTime":193375.0,"Position":390.71225,"HyperDash":false},{"StartTime":193437.0,"Position":356.065,"HyperDash":false},{"StartTime":193500.0,"Position":315.488678,"HyperDash":false},{"StartTime":193562.0,"Position":279.894,"HyperDash":false},{"StartTime":193625.0,"Position":235.7683,"HyperDash":false},{"StartTime":193678.0,"Position":221.0309,"HyperDash":false},{"StartTime":193732.0,"Position":168.99295,"HyperDash":false},{"StartTime":193785.0,"Position":164.902176,"HyperDash":false},{"StartTime":193875.0,"Position":128.0,"HyperDash":false}]},{"StartTime":194125.0,"Objects":[{"StartTime":194125.0,"Position":276.0,"HyperDash":false},{"StartTime":194187.0,"Position":324.316467,"HyperDash":false},{"StartTime":194250.0,"Position":328.094818,"HyperDash":false},{"StartTime":194312.0,"Position":373.795776,"HyperDash":false},{"StartTime":194375.0,"Position":386.318756,"HyperDash":false},{"StartTime":194428.0,"Position":376.7576,"HyperDash":false},{"StartTime":194482.0,"Position":404.218842,"HyperDash":false},{"StartTime":194535.0,"Position":384.551483,"HyperDash":false},{"StartTime":194624.0,"Position":374.339844,"HyperDash":false}]},{"StartTime":194875.0,"Objects":[{"StartTime":194875.0,"Position":236.0,"HyperDash":false},{"StartTime":194937.0,"Position":201.752014,"HyperDash":false},{"StartTime":195000.0,"Position":162.019058,"HyperDash":false},{"StartTime":195062.0,"Position":146.331146,"HyperDash":false},{"StartTime":195125.0,"Position":125.789307,"HyperDash":false},{"StartTime":195178.0,"Position":127.304863,"HyperDash":false},{"StartTime":195232.0,"Position":133.772476,"HyperDash":false},{"StartTime":195285.0,"Position":111.34684,"HyperDash":false},{"StartTime":195375.0,"Position":137.660187,"HyperDash":false}]},{"StartTime":195625.0,"Objects":[{"StartTime":195625.0,"Position":280.0,"HyperDash":false},{"StartTime":195678.0,"Position":279.7856,"HyperDash":false},{"StartTime":195732.0,"Position":250.37854,"HyperDash":false},{"StartTime":195785.0,"Position":235.164154,"HyperDash":false},{"StartTime":195875.0,"Position":231.818985,"HyperDash":false}]},{"StartTime":196125.0,"Objects":[{"StartTime":196125.0,"Position":104.0,"HyperDash":false}]},{"StartTime":196375.0,"Objects":[{"StartTime":196375.0,"Position":136.0,"HyperDash":false}]},{"StartTime":196625.0,"Objects":[{"StartTime":196625.0,"Position":116.0,"HyperDash":false}]},{"StartTime":196875.0,"Objects":[{"StartTime":196875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":197000.0,"Objects":[{"StartTime":197000.0,"Position":332.0,"HyperDash":false}]},{"StartTime":197125.0,"Objects":[{"StartTime":197125.0,"Position":408.0,"HyperDash":false}]},{"StartTime":197250.0,"Objects":[{"StartTime":197250.0,"Position":392.0,"HyperDash":false}]},{"StartTime":197375.0,"Objects":[{"StartTime":197375.0,"Position":376.0,"HyperDash":false}]},{"StartTime":197625.0,"Objects":[{"StartTime":197625.0,"Position":396.0,"HyperDash":false}]},{"StartTime":197875.0,"Objects":[{"StartTime":197875.0,"Position":256.0,"HyperDash":false}]},{"StartTime":198000.0,"Objects":[{"StartTime":198000.0,"Position":256.0,"HyperDash":false}]},{"StartTime":198125.0,"Objects":[{"StartTime":198125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":198625.0,"Objects":[{"StartTime":198625.0,"Position":136.0,"HyperDash":false}]},{"StartTime":199125.0,"Objects":[{"StartTime":199125.0,"Position":256.0,"HyperDash":false}]},{"StartTime":199625.0,"Objects":[{"StartTime":199625.0,"Position":376.0,"HyperDash":false}]},{"StartTime":200125.0,"Objects":[{"StartTime":200125.0,"Position":256.0,"HyperDash":false}]}]} \ No newline at end of file diff --git a/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/112643.osu b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/112643.osu new file mode 100644 index 0000000000..35ef17ae34 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/Resources/Testing/Beatmaps/112643.osu @@ -0,0 +1,582 @@ +osu file format v9 + +[General] +StackLeniency: 0.7 +Mode: 0 + +[Difficulty] +HPDrainRate:7 +CircleSize:5 +OverallDifficulty:8 +ApproachRate:8 +SliderMultiplier:3.2 +SliderTickRate:2 + +[Events] +//Background and Video events +//Break Periods +2,16325,17625 +2,32325,33875 +2,66325,67375 +2,120135,127375 +//Storyboard Layer 0 (Background) +//Storyboard Layer 1 (Fail) +//Storyboard Layer 2 (Pass) +//Storyboard Layer 3 (Foreground) +//Storyboard Sound Samples +//Background Colour Transformations +3,100,163,162,255 + +[TimingPoints] +125,500,4,1,0,50,1,0 +36125,-100,4,1,0,50,0,1 +66125,-100,4,1,0,50,0,0 +88125,-100,4,1,0,50,0,1 +120125,-100,4,1,0,50,0,0 +170125,-100,4,2,0,5,0,0 +170250,-100,4,1,0,50,0,0 +172125,-100,4,1,0,50,0,1 +200125,-100,4,1,0,50,0,0 + +[HitObjects] +64,80,2375,5,0 +172,192,2625,1,2 +152,36,2875,1,0 +80,176,3125,1,2 +224,112,3375,1,0 +192,256,3625,1,8 +136,116,3875,1,0 +272,32,4125,2,2,B|376:0|408:56|412:125|320:144|304:176|328:216|368:272|496:208,1,400,6|0 +504,216,4875,2,2,B|376:232|288:280|248:384,1,320 +384,344,5625,1,8 +272,216,5875,1,0 +272,216,6000,1,0 +272,216,6125,1,4 +92,280,6375,5,0 +124,108,6625,1,8 +256,8,6875,1,0 +388,108,7125,1,2 +420,280,7375,1,8 +256,296,7625,1,8 +256,120,7875,1,0 +443,152,8125,2,2,B|397:202|305:219|256:192|203:163|114:181|68:231,1,400,2|0 +24,256,8875,2,2,B|112:227|141:134|122:36|37:1,1,320 +16,132,9625,1,8 +136,280,9875,1,0 +136,280,10000,1,0 +136,280,10125,1,4 +256,172,10375,5,0 +368,56,10625,1,8 +196,116,10875,1,0 +316,116,11125,1,2 +144,56,11375,1,0 +256,0,11625,1,8 +112,128,11875,1,0 +164,280,12125,6,0,B|256:316,1,80,4|2 +100,348,12500,2,0,B|8:312,1,80,0|2 +144,212,12875,2,0,B|52:176,1,80,0|2 +208,144,13250,2,0,B|300:180,1,80,0|2 +332,324,13625,1,8 +180,324,13875,1,0 +256,240,14125,5,4 +256,240,14250,1,2 +324,112,14500,1,0 +324,112,14625,1,2 +192,56,14875,1,4 +192,56,15000,1,2 +256,164,15250,1,0 +256,164,15375,1,2 +256,20,15625,1,8 +120,56,15875,1,0 +256,92,16125,1,6 +20,152,18375,5,0 +180,136,18625,1,8 +52,228,18875,1,0 +120,84,19125,1,2 +128,244,19375,1,0 +48,84,19625,1,8 +192,212,19875,1,0 +300,72,20125,2,4,B|396:36|444:84|396:144|352:184|372:224|416:260|532:224|528:164,1,320,4|0 +472,40,20875,2,2,B|376:72|304:164|272:260|280:320,1,320 +404,352,21625,1,8 +432,196,21875,1,0 +432,196,22000,1,0 +432,196,22125,1,4 +296,100,22375,5,0 +168,196,22625,2,0,B|32:296,1,160,8|0 +268,212,23125,2,0,B|168:76,1,160,2|8 +252,312,23625,2,0,B|388:212,1,160,8|0 +484,96,24125,2,2,B|412:0|320:36|288:120|240:136|200:132|156:116|132:96|80:44,1,400,2|0 +72,24,24875,2,2,B|158:66|148:177|67:253|-19:210,1,320 +56,108,25625,1,8 +176,200,25875,1,0 +176,200,26000,1,0 +176,200,26125,1,4 +316,92,26375,5,0 +464,164,26625,2,0,B|394:224|412:336,1,160,2|0 +232,316,27125,2,0,B|306:256|284:144,1,160,2|8 +136,88,27625,1,8 +60,224,27875,1,0 +212,132,28125,6,0,B|256:32,1,80,4|2 +340,228,28500,2,0,B|384:128,1,80,0|2 +256,284,28875,2,0,B|212:184,1,80,4|2 +128,380,29250,2,0,B|84:280,1,80,0|2 +238,383,29625,2,0,B|406:379,1,160,8|0 +512,267,30125,5,4 +512,267,30250,1,2 +416,152,30500,1,0 +416,152,30625,1,2 +300,264,30875,1,4 +300,264,31000,1,2 +236,100,31250,1,0 +236,100,31375,1,2 +152,256,31625,1,8 +300,160,31875,1,0 +256,332,32125,1,6 +52,52,34625,5,0 +152,164,34875,1,0 +256,56,35125,1,4 +256,56,35625,1,2 +256,56,36125,2,4,B|331:63|364:136|320:224,1,160,4|0 +320,312,36625,1,8 +204,228,36875,1,0 +104,328,37125,2,2,B|24:287|44:188,1,160 +92,60,37625,1,8 +212,148,37875,1,0 +268,104,38000,1,0 +324,60,38125,2,0,B|452:184,1,160,4|0 +504,300,38625,1,8 +364,340,38875,1,0 +232,280,39125,6,2,B|150:282|69:198|105:87|179:53,2,320,2|2|6 +280,148,40375,1,0 +400,228,40625,2,0,B|520:368,1,160,8|0 +480,192,41125,1,2 +324,220,41375,1,2 +168,256,41625,1,8 +72,148,41875,1,2 +48,84,42000,1,2 +96,36,42125,2,0,B|164:108|256:44,1,160,6|0 +400,72,42625,1,2 +440,236,42875,1,2 +464,300,43000,1,2 +416,348,43125,2,0,B|348:276|256:340,1,160,6|0 +112,312,43625,1,2 +140,188,43875,1,0 +52,64,44125,5,6 +208,48,44375,1,0 +344,132,44625,1,8 +448,256,44875,2,2,B|401:321|285:337|217:242|233:163,2,320,2|2|0 +326,211,46125,2,2,B|279:146|163:130|95:225|111:304,1,320,6|0 +230,287,46875,2,2,B|277:352|393:368|461:273|445:194,1,320,6|8 +376,80,47625,1,8 +376,80,48125,6,0,B|304:128|216:96,1,160,4|0 +84,56,48625,1,8 +152,200,48875,1,0 +44,320,49125,2,0,B|121:364|204:320,1,160,4|0 +336,240,49625,5,8 +256,148,49875,1,0 +176,240,50125,1,0 +340,144,50625,1,0 +420,236,50875,1,0 +500,144,51125,1,2 +172,144,51625,1,2 +92,236,51875,1,0 +12,144,52125,6,0,B|160:48,1,160,4|0 +304,76,52625,1,8 +256,228,52875,1,0 +216,112,53125,2,0,B|364:208,1,160,2|0 +508,180,53625,1,8 +460,28,53875,1,0 +344,96,54125,1,2 +228,8,54375,1,0 +153,116,54625,1,2 +72,220,54875,1,0 +180,295,55125,1,2 +284,376,55375,1,0 +359,268,55625,1,2 +440,164,55875,1,0 +352,160,56125,6,0,B|466:294,1,160,4|0 +312,228,56625,1,8 +200,300,56875,1,0 +160,160,57125,2,0,B|46:294,1,160,4|0 +200,228,57625,1,8 +312,300,57875,1,0 +444,208,58125,2,0,B|362:164|380:56,1,160,2|0 +344,12,58500,1,0 +272,4,58625,2,0,B|232:88|120:68,1,160,2|0 +68,176,59125,2,0,B|148:220|132:328,1,160,2|0 +168,372,59500,1,0 +240,380,59625,2,0,B|280:296|392:316,1,160,2|0 +456,176,60125,5,6 +328,80,60375,1,0 +216,196,60625,1,8 +72,136,60875,2,2,B|54:209|91:305|191:336|269:306,2,320,2|2|0 +200,224,62125,2,2,B|182:150|219:54|319:23|397:53,1,320,2|0 +480,179,62875,2,2,B|499:252|462:348|362:379|284:349,1,320,2|0 +136,296,63625,2,0,B|67:220|140:136,1,160,8|0 +256,56,64125,5,6 +284,212,64375,1,0 +440,180,64625,1,8 +420,24,64875,1,0 +300,132,65125,1,6 +272,288,65375,1,0 +116,256,65625,1,8 +136,100,65875,1,0 +256,8,66125,1,4 +256,56,68125,6,0,B|298:128|244:237|123:241|74:173,1,320 +132,80,68875,2,2,B|344:328,1,320 +456,224,69625,1,8 +340,116,69875,1,0 +340,116,70000,1,0 +340,116,70125,1,4 +228,4,70375,5,0 +256,160,70625,2,0,B|186:224|88:168,1,160,2|0 +148,332,71125,2,0,B|216:396|316:340,1,160,2|8 +424,248,71625,1,8 +336,112,71875,1,0 +336,112,72000,1,0 +336,112,72125,1,4 +228,208,72375,2,0,B|139:179|144:80,1,160,0|8 +268,56,72875,2,2,B|272:164|220:272|120:308|72:308,1,320 +24,192,73625,1,8 +92,64,73875,1,0 +92,64,74000,1,0 +92,64,74125,1,4 +224,140,74375,5,0 +340,224,74625,2,0,B|412:211|428:121|363:77,1,160,2|0 +268,192,75125,2,0,B|196:205|180:295|245:339,1,160,2|0 +268,192,75625,2,0,B|104:168,1,160,8|0 +24,52,76125,6,0,B|132:40,1,80 +176,32,76375,1,2 +348,60,76625,1,2 +248,164,76875,1,2 +264,20,77125,1,2 +324,140,77375,1,2 +180,116,77625,1,2 +240,240,77875,1,0 +256,92,78125,1,4 +100,124,78375,5,0 +8,256,78625,2,0,B|64:332|176:304,1,160,8|0 +304,260,79125,2,0,B|248:184|136:212,1,160,2|0 +304,260,79625,1,8 +460,284,79875,1,2 +420,128,80125,6,0,B|332:128,1,80,4|0 +256,124,80375,1,2 +344,260,80625,1,2 +168,260,80875,1,2 +384,192,81125,1,2 +256,260,81375,1,2 +168,124,81625,1,2 +344,124,81875,1,2 +128,192,82125,1,4 +48,192,82250,6,0,B|48:84|152:52,1,160,2|0 +204,44,82625,2,0,B|204:152|308:184,1,160,2|0 +352,160,83000,2,0,B|244:160|212:264,1,160,2|0 +192,316,83375,2,0,B|84:316|52:212,1,160,2|2 +32,88,83875,1,2 +172,8,84125,1,4 +256,192,84250,12,6,86125 +256,192,86250,12,4,87125 +256,100,88125,6,2,B|308:116|368:104|404:16,1,160,6|0 +256,100,88625,1,8 +136,180,88875,1,0 +8,96,89125,2,0,B|-28:168|16:232|68:256,1,160,2|0 +164,312,89625,1,8 +288,236,89875,1,2 +288,236,90000,1,2 +288,236,90125,2,2,B|452:164,1,160,6|0 +476,32,90625,1,8 +332,104,90875,1,0 +180,104,91125,5,6 +36,32,91375,1,8 +56,164,91625,1,8 +56,164,92125,2,0,B|260:208,1,160,6|0 +84,296,92625,1,8 +220,376,92875,1,0 +320,268,93125,2,0,B|524:224,1,160,6|0 +432,80,93625,1,8 +296,152,93875,1,2 +296,152,94000,1,2 +296,152,94125,2,2,B|232:164|176:132|164:52,1,160,6|0 +216,232,94625,2,2,B|280:220|336:252|348:332,1,160,2|0 +341,304,95000,1,0 +341,304,95125,2,0,B|369:84,1,160,2|0 +171,80,95625,2,0,B|143:300,1,160,2|0 +43,358,96125,5,6 +81,219,96375,1,0 +169,332,96625,1,8 +304,272,96875,2,2,B|388:252|426:161|418:63|344:19,2,320,2|2|0 +240,144,98125,2,2,B|219:244|50:229|65:60|168:58,1,320 +240,144,98875,2,2,B|260:43|429:58|414:227|311:229,1,320,2|0 +180,292,99625,2,0,B|80:304|36:208,1,160,2|0 +48,64,100125,6,0,B|224:112,1,160,4|0 +348,52,100625,2,0,B|524:4,1,160,2|0 +504,172,101125,2,0,B|328:124,1,160,2|0 +204,184,101625,2,0,B|28:232,1,160,2|0 +49,226,102000,1,0 +49,226,102125,1,2 +256,324,102625,5,8 +384,256,102875,1,0 +256,188,103125,1,6 +256,188,103625,1,2 +128,256,103875,1,0 +256,324,104125,6,0,B|324:252|432:316,1,160,6|0 +492,168,104625,1,8 +332,188,104875,1,0 +256,60,105125,2,0,B|188:132|80:68,1,160,6|0 +20,216,105625,1,8 +180,196,105875,1,0 +368,156,106125,2,0,B|418:184|462:234|408:296,1,160,2|0 +220,80,106625,2,0,B|248:30|298:-14|360:40,1,160,2|0 +144,228,107125,2,0,B|94:200|50:150|104:88,1,160,2|0 +292,304,107625,2,0,B|264:354|214:398|152:344,1,160,2|0 +44,216,108125,6,0,B|145:221|172:132,1,160,6|0 +304,224,108625,1,8 +408,104,108875,1,0 +468,216,109125,2,0,B|367:221|340:132,1,160,6|0 +208,224,109625,1,8 +104,104,109875,1,0 +256,56,110125,2,0,B|144:180,1,160,2|0 +256,328,110625,2,0,B|368:204,1,160,2|0 +208,244,111125,2,0,B|96:368,1,160,2|0 +304,140,111625,2,0,B|416:16,1,160,2|0 +252,20,112125,5,6 +112,60,112375,1,0 +72,200,112625,1,8 +158,316,112875,2,2,B|236:321|324:259|326:152|278:89,2,320,2|2|0 +176,168,114125,2,2,B|214:236|313:276|405:220|431:145,1,320,2|0 +328,64,114875,2,2,B|259:102|219:201|275:293|350:319,1,320,2|0 +488,340,115625,2,0,B|456:172,1,160,2|0 +416,72,116125,5,6 +288,140,116375,1,0 +164,68,116625,1,8 +36,136,116875,1,0 +104,264,117125,1,6 +232,332,117375,1,0 +356,260,117625,1,8 +484,328,117875,1,0 +356,384,118125,1,6 +256,12,128125,5,4 +256,12,128250,1,2 +336,128,128500,1,0 +336,128,128625,1,2 +400,0,128875,1,0 +400,0,129000,1,2 +492,112,129250,1,0 +492,112,129375,1,2 +440,248,129625,2,2,B|272:284,1,160 +256,108,130125,5,4 +256,108,130250,1,2 +176,224,130500,1,0 +176,224,130625,1,2 +112,96,130875,1,0 +112,96,131000,1,2 +20,208,131250,1,0 +20,208,131375,1,2 +72,344,131625,2,2,B|240:380,1,160 +408,376,132125,6,0,B|512:352|584:248|592:-32|416:-48|256:-80|96:-16|56:88|8:224|88:304|144:336|184:368|256:368|256:368|328:368|368:336|424:304|504:224|456:88|416:-16|256:-80|96:-48|-80:-32|-72:248|0:352|104:376,1,2240,6|0 +256,192,135875,5,2 +256,192,136000,1,0 +256,192,136125,1,4 +136,104,136375,1,0 +132,240,136625,1,8 +133,240,136750,1,0 +256,280,137000,1,0 +255,280,137125,1,8 +256,280,137250,1,0 +256,280,137375,1,0 +380,240,137625,1,8 +376,104,137875,1,0 +256,124,138125,5,4 +256,124,138375,1,0 +144,192,138625,1,8 +144,192,138750,1,0 +256,260,139000,1,0 +256,260,139125,1,8 +256,260,139250,1,0 +256,260,139375,1,0 +368,192,139625,1,8 +256,124,139875,1,0 +256,124,140000,1,0 +256,124,140125,2,2,B|188:112|212:76|188:36|256:20,1,160,6|2 +332,128,140625,5,8 +332,128,140750,1,0 +332,256,141000,1,0 +332,256,141125,1,8 +332,256,141250,1,0 +332,256,141375,1,0 +180,256,141625,1,8 +180,128,141875,1,0 +256,56,142125,5,4 +256,56,142375,1,0 +256,160,142625,1,8 +256,160,142750,1,0 +256,264,143000,1,0 +256,264,143125,1,8 +256,264,143250,1,0 +256,264,143375,1,0 +188,352,143625,1,8 +324,352,143875,1,0 +324,352,144000,1,0 +324,352,144125,2,0,B|492:352,1,160,6|2 +392,280,144625,5,8 +392,280,144750,1,0 +324,192,145000,1,0 +324,192,145125,1,8 +324,192,145250,1,0 +324,192,145375,1,0 +188,192,145625,1,8 +120,280,145875,1,0 +256,288,146125,5,4 +256,288,146375,1,0 +256,176,146625,1,8 +256,176,146750,1,0 +176,96,147000,1,0 +176,96,147125,1,8 +176,96,147250,1,0 +176,96,147375,1,0 +256,16,147625,1,8 +336,96,147875,1,0 +336,96,148000,1,0 +336,96,148125,2,6,B|400:156|388:224|364:248,1,160,6|2 +256,272,148625,5,8 +240,264,148750,1,0 +240,180,149000,1,0 +256,172,149125,1,8 +272,164,149250,1,0 +288,156,149375,1,0 +256,64,149625,1,8 +256,64,149875,1,0 +116,180,150125,5,0 +120,200,150250,1,0 +132,224,150375,1,0 +152,236,150500,1,0 +176,240,150625,1,8 +208,240,150750,1,0 +232,236,150875,1,0 +248,216,151000,1,0 +256,192,151125,1,8 +260,168,151250,1,0 +272,144,151375,1,8 +292,132,151500,1,0 +316,128,151625,1,8 +348,128,151750,1,8 +372,132,151875,1,8 +388,152,152000,1,0 +404,184,152125,6,0,B|436:250|377:334|292:300,1,160,6|0 +108,200,152625,2,0,B|76:134|135:50|220:84,1,160,6|0 +256,192,153125,2,0,B|256:100,1,80,2|0 +256,192,153375,2,0,B|256:368,2,160,2|8|0 +360,60,154125,5,0 +360,60,154250,1,0 +360,60,154375,1,2 +256,12,154625,1,0 +256,12,154750,1,0 +256,12,154875,1,2 +154,64,155125,1,0 +154,64,155250,1,2 +155,63,155375,2,0,B|87:119|115:191|179:211|227:179,2,160,0|8|0 +163,74,156000,5,0 +163,74,156125,1,0 +163,74,156250,2,2,B|174:151|299:265|445:180|473:106,1,400,2|0 +320,80,157125,2,2,B|224:88|184:188|224:288|320:295,1,320 +348,292,157750,1,0 +380,280,157875,1,0 +404,260,158000,1,0 +412,236,158125,1,0 +412,208,158250,1,0 +404,180,158375,1,0 +264,68,158625,2,0,B|184:104,2,80,2|0|2 +164,216,159125,2,0,B|244:180,2,80,2|0|2 +56,144,159625,5,8 +64,276,159875,1,8 +64,276,160000,1,8 +64,276,160125,2,0,B|24:352,2,80,2|0|0 +128,288,160500,2,0,B|136:188,2,80,2|0|0 +192,300,160875,2,0,B|200:400,2,80,2|0|0 +240,256,161250,2,0,B|304:176,2,80,2|0|0 +284,304,161625,2,0,B|356:380,2,80,2|0|0 +328,256,162000,6,0,B|456:236,2,80,0|2|0 +308,192,162375,2,0,B|180:172,2,80,0|2|0 +340,136,162750,2,0,B|468:116,2,80,0|2|0 +284,100,163125,2,0,B|264:-28,2,80,0|2|0 +224,128,163500,2,0,B|204:256,2,80,0|2|0 +180,76,163875,6,0,B|92:52,2,80,2|0|0 +144,132,164250,2,0,B|72:184,2,80,2|0|0 +168,196,164625,2,0,B|240:248,2,80,2|0|0 +136,256,165000,2,0,B|96:340,2,80,2|0|0 +188,296,165375,2,0,B|228:380,2,80,2|0|0 +236,252,165750,1,0 +236,252,165875,1,2 +364,276,166125,6,2,B|408:176|360:156|320:168|296:176|268:132|264:112|272:76|304:52|328:40,1,240,2|0 +264,24,166625,2,2,B|308:124|260:144|220:132|196:124|168:168|164:188|172:224|204:248|228:260,1,240,2|0 +192,280,167125,1,0 +320,376,167375,1,0 +192,376,167625,1,0 +256,328,167750,1,0 +320,280,167875,1,0 +256,124,168125,1,6 +256,192,168250,12,0,170125 +256,192,171125,12,6,172125 +48,56,172375,5,0 +20,184,172625,2,0,B|16:264|92:316|152:304,1,160,8|0 +240,300,173125,1,2 +200,176,173375,1,0 +324,220,173625,2,0,B|360:220|416:258|412:338,1,160,8|0 +412,334,174000,1,0 +412,334,174125,2,0,B|456:156,1,160,6|0 +398,35,174625,2,0,B|220:-8,1,160,2|0 +245,0,175000,1,0 +245,0,175125,2,0,B|201:178,1,160,6|0 +259,299,175625,2,0,B|437:342,1,160,2|0 +424,176,176125,5,6 +272,128,176375,1,0 +116,152,176625,1,8 +173,253,176875,2,2,B|257:233|295:142|287:44|213:0,2,320,2|2|0 +28,204,178125,2,2,B|356:316,1,320 +172,360,178875,2,2,B|500:248,1,320,2|0 +384,148,179625,2,0,B|292:168|224:96|232:44,1,160,2|0 +244,93,180000,1,0 +244,93,180125,6,0,B|64:120,1,160,6|0 +100,268,180625,2,0,B|256:296,1,160,8|0 +257,296,181000,1,0 +256,296,181125,2,0,B|413:267,1,160,6|0 +426,116,181625,2,0,B|267:93,1,160,8|2 +267,93,182000,5,2 +267,93,182125,2,2,B|180:112|168:212,1,160,2|0 +140,380,182625,2,0,B|227:361|239:261,1,160,8|0 +62,169,183125,2,2,B|80:256|180:268,1,160,2|0 +348,296,183625,2,0,B|329:208|229:196,1,160,8|0 +64,172,184125,1,6 +256,192,184250,12,2,185625 +48,188,186125,6,2,B|96:108|256:108|256:192|256:276|416:276|464:196,1,480,2|0 +328,144,187125,2,0,B|296:316,1,160,2|0 +184,240,187625,2,0,B|216:68,1,160,2|0 +256,192,188125,1,6 +256,192,188250,12,2,189625 +464,188,190125,6,2,B|416:108|256:108|256:192|256:276|96:276|48:196,1,480,2|0 +184,144,191125,2,0,B|216:316,1,160,2|0 +328,240,191625,2,0,B|296:68,1,160,2|0 +164,32,192125,5,6 +28,84,192375,1,0 +28,228,192625,1,8 +128,332,192875,2,2,B|160:224|300:172|408:244,2,320,2|2|0 +276,356,194125,2,2,B|384:324|436:184|364:76,1,320 +236,28,194875,2,2,B|128:60|76:200|148:308,1,320,2|0 +280,268,195625,2,0,B|232:116,1,160,2|0 +104,52,196125,5,6 +136,192,196375,1,0 +116,344,196625,1,8 +256,312,196875,1,0 +332,312,197000,1,0 +408,332,197125,1,6 +392,264,197250,1,0 +376,192,197375,1,0 +396,40,197625,1,8 +256,72,197875,5,0 +256,72,198000,1,0 +256,72,198125,1,6 +136,192,198625,1,6 +256,312,199125,1,6 +376,192,199625,1,6 +256,192,200125,1,6 diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index 02d4cdbb94..d5d4d3b694 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -44,6 +44,7 @@ namespace osu.Game.Rulesets.Catch.Beatmaps base.PostProcess(); ApplyPositionOffsets(Beatmap); + ApplyPositionClamping(Beatmap); int index = 0; @@ -114,6 +115,17 @@ namespace osu.Game.Rulesets.Catch.Beatmaps initialiseHyperDash(beatmap); } + public void ApplyPositionClamping(IBeatmap beatmap) + { + foreach (var obj in beatmap.HitObjects.OfType()) + { + if (obj.EffectiveX < 0) + obj.XOffset += Math.Abs(obj.EffectiveX); + else if (obj.EffectiveX > CatchPlayfield.WIDTH) + obj.XOffset += CatchPlayfield.WIDTH - obj.EffectiveX; + } + } + private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, LegacyRandom rng) { float offsetPosition = hitObject.OriginalX; diff --git a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs index d691ceceb1..671291ef0e 100644 --- a/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/JuiceStream.cs @@ -10,7 +10,6 @@ using osu.Framework.Bindables; using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; -using osu.Game.Rulesets.Catch.UI; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -103,8 +102,7 @@ namespace osu.Game.Rulesets.Catch.Objects AddNested(new TinyDroplet { StartTime = t + lastEvent.Value.Time, - X = ClampToPlayfield(EffectiveX + Path.PositionAt( - lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X), + X = EffectiveX + Path.PositionAt(lastEvent.Value.PathProgress + (t / sinceLastTick) * (e.PathProgress - lastEvent.Value.PathProgress)).X, }); } } @@ -121,7 +119,7 @@ namespace osu.Game.Rulesets.Catch.Objects { Samples = dropletSamples, StartTime = e.Time, - X = ClampToPlayfield(EffectiveX + Path.PositionAt(e.PathProgress).X), + X = EffectiveX + Path.PositionAt(e.PathProgress).X, }); break; @@ -132,16 +130,14 @@ namespace osu.Game.Rulesets.Catch.Objects { Samples = this.GetNodeSamples(nodeIndex++), StartTime = e.Time, - X = ClampToPlayfield(EffectiveX + Path.PositionAt(e.PathProgress).X), + X = EffectiveX + Path.PositionAt(e.PathProgress).X, }); break; } } } - public float EndX => ClampToPlayfield(EffectiveX + this.CurvePositionAt(1).X); - - public float ClampToPlayfield(float value) => Math.Clamp(value, 0, CatchPlayfield.WIDTH); + public float EndX => EffectiveX + this.CurvePositionAt(1).X; [JsonIgnore] public double Duration From b20b2203ac92c3c261df43967fe6d6e140e58496 Mon Sep 17 00:00:00 2001 From: PercyDan54 <50285552+PercyDan54@users.noreply.github.com> Date: Mon, 11 Dec 2023 21:06:46 +0800 Subject: [PATCH 0444/1118] Fix mania Autoplay mod missing 0ms hold notes --- .../Mods/TestSceneManiaModAutoplay.cs | 42 +++++++++++++++++++ .../Replays/ManiaAutoGenerator.cs | 17 +++++--- 2 files changed, 54 insertions(+), 5 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs new file mode 100644 index 0000000000..f653f209c1 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModAutoplay.cs @@ -0,0 +1,42 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Rulesets.Mania.Beatmaps; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests.Mods +{ + public partial class TestSceneManiaModAutoplay : ModTestScene + { + protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); + + [Test] + public void TestPerfectScoreOnShortHoldNote() + { + CreateModTest(new ModTestData + { + Autoplay = true, + Beatmap = new ManiaBeatmap(new StageDefinition(1)) + { + HitObjects = new List + { + new HoldNote + { + StartTime = 100, + EndTime = 100, + }, + new HoldNote + { + StartTime = 100.1, + EndTime = 150, + }, + } + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 4 + }); + } + } +} diff --git a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs index 7c8afdff12..dd3208bd89 100644 --- a/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs +++ b/osu.Game.Rulesets.Mania/Replays/ManiaAutoGenerator.cs @@ -87,15 +87,22 @@ namespace osu.Game.Rulesets.Mania.Replays private double calculateReleaseTime(HitObject currentObject, HitObject? nextObject) { double endTime = currentObject.GetEndTime(); + double releaseDelay = RELEASE_DELAY; - if (currentObject is HoldNote) - // hold note releases must be timed exactly. - return endTime; + if (currentObject is HoldNote hold) + { + if (hold.Duration > 0) + // hold note releases must be timed exactly. + return endTime; + + // Special case for super short hold notes + releaseDelay = 1; + } bool canDelayKeyUpFully = nextObject == null || - nextObject.StartTime > endTime + RELEASE_DELAY; + nextObject.StartTime > endTime + releaseDelay; - return endTime + (canDelayKeyUpFully ? RELEASE_DELAY : (nextObject.AsNonNull().StartTime - endTime) * 0.9); + return endTime + (canDelayKeyUpFully ? releaseDelay : (nextObject.AsNonNull().StartTime - endTime) * 0.9); } protected override HitObject? GetNextObject(int currentIndex) From f0ddcb22c6e33a20ae07cafeae5360fc1e2173f8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 12 Dec 2023 21:21:04 +0300 Subject: [PATCH 0445/1118] Remove arbitrary margin --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 96604275ea..ea0da30911 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -413,11 +413,6 @@ namespace osu.Game.Graphics.UserInterface private partial class DropdownSearchTextBox : SearchTextBox { - public DropdownSearchTextBox() - { - TextContainer.Margin = new MarginPadding { Top = 4f }; - } - public override bool OnPressed(KeyBindingPressEvent e) { if (e.Action == GlobalAction.Back) From 67a9eab741ab3a715ad0882fa8597529e380f844 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 12 Dec 2023 21:21:11 +0300 Subject: [PATCH 0446/1118] Update caret layout --- osu.Game/Graphics/UserInterface/OsuTextBox.cs | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/OsuTextBox.cs b/osu.Game/Graphics/UserInterface/OsuTextBox.cs index 4742da6d0b..08d38837f6 100644 --- a/osu.Game/Graphics/UserInterface/OsuTextBox.cs +++ b/osu.Game/Graphics/UserInterface/OsuTextBox.cs @@ -314,18 +314,16 @@ namespace osu.Game.Graphics.UserInterface public OsuCaret() { - RelativeSizeAxes = Axes.Y; - Size = new Vector2(1, 0.9f); - Colour = Color4.Transparent; - Anchor = Anchor.CentreLeft; - Origin = Anchor.CentreLeft; - Masking = true; - CornerRadius = 1; InternalChild = beatSync = new CaretBeatSyncedContainer { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Masking = true, + CornerRadius = 1f, RelativeSizeAxes = Axes.Both, + Height = 0.9f, }; } From daaadf3fc38efb9631eb01a618d0a0033439ca03 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 11:51:45 +0900 Subject: [PATCH 0447/1118] Fix IgnoreMiss judgements not updating accuracy --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 21 ++++++++++++++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 22 +++++++++---------- 2 files changed, 31 insertions(+), 12 deletions(-) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index fd041d3dd0..d883b91abc 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -356,6 +356,27 @@ namespace osu.Game.Tests.Rulesets.Scoring Assert.That(actual, Is.EqualTo(expected).Within(Precision.FLOAT_EPSILON)); } + [TestCase(HitResult.Great)] + [TestCase(HitResult.LargeTickHit)] + public void TestAccuracyUpdateFromIgnoreMiss(HitResult maxResult) + { + scoreProcessor.ApplyBeatmap(new Beatmap + { + HitObjects = + { + new TestHitObject(maxResult, HitResult.IgnoreMiss) + } + }); + + var judgementResult = new JudgementResult(beatmap.HitObjects.Single(), new TestJudgement(maxResult, HitResult.IgnoreMiss)) + { + Type = HitResult.IgnoreMiss + }; + scoreProcessor.ApplyResult(judgementResult); + + Assert.That(scoreProcessor.Accuracy.Value, Is.Not.EqualTo(1)); + } + private class TestJudgement : Judgement { public override HitResult MaxResult { get; } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index baeddbd0e8..f110172988 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -218,9 +218,6 @@ namespace osu.Game.Rulesets.Scoring scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) + 1; - if (!result.Type.IsScorable()) - return; - if (result.Type.IncreasesCombo()) Combo.Value++; else if (result.Type.BreaksCombo()) @@ -228,16 +225,18 @@ namespace osu.Game.Rulesets.Scoring result.ComboAfterJudgement = Combo.Value; - if (result.Type.AffectsAccuracy()) + if (result.Judgement.MaxResult.AffectsAccuracy()) { currentMaximumBaseScore += Judgement.ToNumericResult(result.Judgement.MaxResult); - currentBaseScore += Judgement.ToNumericResult(result.Type); currentAccuracyJudgementCount++; } + if (result.Type.AffectsAccuracy()) + currentBaseScore += Judgement.ToNumericResult(result.Type); + if (result.Type.IsBonus()) currentBonusPortion += GetBonusScoreChange(result); - else + else if (result.Type.IsScorable()) currentComboPortion += GetComboScoreChange(result); ApplyScoreChange(result); @@ -275,19 +274,18 @@ namespace osu.Game.Rulesets.Scoring scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) - 1; - if (!result.Type.IsScorable()) - return; - - if (result.Type.AffectsAccuracy()) + if (result.Judgement.MaxResult.AffectsAccuracy()) { currentMaximumBaseScore -= Judgement.ToNumericResult(result.Judgement.MaxResult); - currentBaseScore -= Judgement.ToNumericResult(result.Type); currentAccuracyJudgementCount--; } + if (result.Type.AffectsAccuracy()) + currentBaseScore -= Judgement.ToNumericResult(result.Type); + if (result.Type.IsBonus()) currentBonusPortion -= GetBonusScoreChange(result); - else + else if (result.Type.IsScorable()) currentComboPortion -= GetComboScoreChange(result); RemoveScoreChange(result); From 8977e74171793d6321b2b8b23a7599ee6d6e490a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 13:15:49 +0900 Subject: [PATCH 0448/1118] Fix editor test not waiting for editor to load --- .../Visual/Editing/TestSceneOpenEditorTimestamp.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs index 1a754d5145..1f46a08831 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneOpenEditorTimestamp.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Editing { public partial class TestSceneOpenEditorTimestamp : OsuGameTestScene { - private Editor editor => (Editor)Game.ScreenStack.CurrentScreen; + private Editor? editor => Game.ScreenStack.CurrentScreen as Editor; private EditorBeatmap editorBeatmap => editor.ChildrenOfType().Single(); private EditorClock editorClock => editor.ChildrenOfType().Single(); @@ -111,18 +111,18 @@ namespace osu.Game.Tests.Visual.Editing } private void addStepScreenModeTo(EditorScreenMode screenMode) => - AddStep("change screen to " + screenMode, () => editor.Mode.Value = screenMode); + AddStep("change screen to " + screenMode, () => editor!.Mode.Value = screenMode); private void assertOnScreenAt(EditorScreenMode screen, double time) { AddAssert($"stayed on {screen} at {time}", () => - editor.Mode.Value == screen + editor!.Mode.Value == screen && editorClock.CurrentTime == time ); } private void assertMovedScreenTo(EditorScreenMode screen, string text = "moved to") => - AddAssert($"{text} {screen}", () => editor.Mode.Value == screen); + AddAssert($"{text} {screen}", () => editor!.Mode.Value == screen); private void setUpEditor(RulesetInfo ruleset) { @@ -145,7 +145,7 @@ namespace osu.Game.Tests.Visual.Editing ((PlaySongSelect)Game.ScreenStack.CurrentScreen) .Edit(beatmapSet.Beatmaps.Last(beatmap => beatmap.Ruleset.Name == ruleset.Name)) ); - AddUntilStep("Wait for editor open", () => editor.ReadyForUse); + AddUntilStep("Wait for editor open", () => editor?.ReadyForUse == true); } } } From c31ff84417b76fd6e6e47ec4a92f93e029bbbeb8 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 13:32:27 +0900 Subject: [PATCH 0449/1118] Fix possible nullref --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 87185c351e..1d6b383dea 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -126,12 +126,14 @@ namespace osu.Game.Screens.Select.Details mod.ApplyToDifficulty(adjustedDifficulty); } - switch (gameRuleset.Value.OnlineID) + IRulesetInfo ruleset = gameRuleset?.Value ?? beatmapInfo.Ruleset; + + switch (ruleset.OnlineID) { case 3: // Account for mania differences locally for now. // Eventually this should be handled in a more modular way, allowing rulesets to return arbitrary difficulty attributes. - ILegacyRuleset legacyRuleset = (ILegacyRuleset)gameRuleset.Value.CreateInstance(); + ILegacyRuleset legacyRuleset = (ILegacyRuleset)ruleset.CreateInstance(); // For the time being, the key count is static no matter what, because: // a) The method doesn't have knowledge of the active keymods. Doing so may require considerations for filtering. From 8c760e511041ba5635f5c5987e89262905b74f32 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 13:41:01 +0900 Subject: [PATCH 0450/1118] Fix hitobject count when creating from an IBeatmap --- .../Legacy/LegacyBeatmapConversionDifficultyInfo.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs index 6f379e4ef1..baf771adef 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs @@ -1,8 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Rulesets.Objects.Types; namespace osu.Game.Rulesets.Scoring.Legacy { @@ -44,7 +46,14 @@ namespace osu.Game.Rulesets.Scoring.Legacy public static LegacyBeatmapConversionDifficultyInfo FromAPIBeatmap(APIBeatmap apiBeatmap) => FromBeatmapInfo(apiBeatmap); - public static LegacyBeatmapConversionDifficultyInfo FromBeatmap(IBeatmap beatmap) => FromBeatmapInfo(beatmap.BeatmapInfo); + public static LegacyBeatmapConversionDifficultyInfo FromBeatmap(IBeatmap beatmap) => new LegacyBeatmapConversionDifficultyInfo + { + SourceRuleset = beatmap.BeatmapInfo.Ruleset, + CircleSize = beatmap.Difficulty.CircleSize, + OverallDifficulty = beatmap.Difficulty.OverallDifficulty, + CircleCount = beatmap.HitObjects.Count - beatmap.HitObjects.Count(h => h is IHasDuration), + TotalObjectCount = beatmap.HitObjects.Count + }; public static LegacyBeatmapConversionDifficultyInfo FromBeatmapInfo(IBeatmapInfo beatmapInfo) => new LegacyBeatmapConversionDifficultyInfo { From 2930b53edd9aa4e007bd6db05e0072ae5ae906ce Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 13:43:14 +0900 Subject: [PATCH 0451/1118] Simplify implementation --- .../Beatmaps/ManiaBeatmapConverter.cs | 2 +- .../Legacy/LegacyBeatmapConversionDifficultyInfo.cs | 12 +++++++----- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index c4a8db92ed..c3cd623c5d 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -64,7 +64,7 @@ namespace osu.Game.Rulesets.Mania.Beatmaps double roundedOverallDifficulty = Math.Round(difficulty.OverallDifficulty); - int countSliderOrSpinner = difficulty.TotalObjectCount - difficulty.CircleCount; + int countSliderOrSpinner = difficulty.EndTimeObjectCount; float percentSpecialObjects = (float)countSliderOrSpinner / difficulty.TotalObjectCount; if (percentSpecialObjects < 0.2) diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs index baf771adef..2021aa127d 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs @@ -29,9 +29,12 @@ namespace osu.Game.Rulesets.Scoring.Legacy public float OverallDifficulty { get; set; } /// - /// The count of hitcircles in the beatmap. + /// The number of hitobjects in the beatmap with a distinct end time. /// - public int CircleCount { get; set; } + /// + /// Canonically, these are hitobjects are either sliders or spinners. + /// + public int EndTimeObjectCount { get; set; } /// /// The total count of hitobjects in the beatmap. @@ -42,7 +45,6 @@ namespace osu.Game.Rulesets.Scoring.Legacy float IBeatmapDifficultyInfo.ApproachRate => 0; double IBeatmapDifficultyInfo.SliderMultiplier => 0; double IBeatmapDifficultyInfo.SliderTickRate => 0; - int IBeatmapDifficultyInfo.EndTimeObjectCount => TotalObjectCount - CircleCount; public static LegacyBeatmapConversionDifficultyInfo FromAPIBeatmap(APIBeatmap apiBeatmap) => FromBeatmapInfo(apiBeatmap); @@ -51,7 +53,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy SourceRuleset = beatmap.BeatmapInfo.Ruleset, CircleSize = beatmap.Difficulty.CircleSize, OverallDifficulty = beatmap.Difficulty.OverallDifficulty, - CircleCount = beatmap.HitObjects.Count - beatmap.HitObjects.Count(h => h is IHasDuration), + EndTimeObjectCount = beatmap.HitObjects.Count(h => h is IHasDuration), TotalObjectCount = beatmap.HitObjects.Count }; @@ -60,7 +62,7 @@ namespace osu.Game.Rulesets.Scoring.Legacy SourceRuleset = beatmapInfo.Ruleset, CircleSize = beatmapInfo.Difficulty.CircleSize, OverallDifficulty = beatmapInfo.Difficulty.OverallDifficulty, - CircleCount = beatmapInfo.Difficulty.TotalObjectCount - beatmapInfo.Difficulty.EndTimeObjectCount, + EndTimeObjectCount = beatmapInfo.Difficulty.EndTimeObjectCount, TotalObjectCount = beatmapInfo.Difficulty.TotalObjectCount }; } From 2579b42ac5dc5d7e7f41f86628bc8503dec26b64 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 14:01:03 +0900 Subject: [PATCH 0452/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 6609db3027..bd35bb8d54 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index a6b3527466..84a4e61267 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 0be6743e87b241b36b396c840e1d5acc1bb6b578 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 14:07:38 +0900 Subject: [PATCH 0453/1118] Apply `Bindable.Parse` refactorings --- osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs | 2 +- osu.Game/Rulesets/Configuration/RulesetConfigManager.cs | 2 +- osu.Game/Rulesets/Mods/Mod.cs | 3 ++- .../Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs | 2 +- osu.Game/Skinning/ISerialisableDrawable.cs | 3 ++- osu.Game/Skinning/LegacySkin.cs | 3 ++- 6 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs index 37ea2a3f96..e5ba7f61bf 100644 --- a/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs +++ b/osu.Game/Graphics/UserInterfaceV2/SliderWithTextBoxInput.cs @@ -121,7 +121,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 break; default: - slider.Current.Parse(textBox.Current.Value); + slider.Current.Parse(textBox.Current.Value, CultureInfo.CurrentCulture); break; } } diff --git a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs index 0eea1ff215..418dc3576f 100644 --- a/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs +++ b/osu.Game/Rulesets/Configuration/RulesetConfigManager.cs @@ -84,7 +84,7 @@ namespace osu.Game.Rulesets.Configuration if (setting != null) { - bindable.Parse(setting.Value); + bindable.Parse(setting.Value, CultureInfo.InvariantCulture); } else { diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index 775f6a0ed4..d635dccab1 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Reflection; using Newtonsoft.Json; @@ -284,7 +285,7 @@ namespace osu.Game.Rulesets.Mods if (!(target is IParseable parseable)) throw new InvalidOperationException($"Bindable type {target.GetType().ReadableName()} is not {nameof(IParseable)}."); - parseable.Parse(source); + parseable.Parse(source, CultureInfo.InvariantCulture); } } diff --git a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs index eabe9b9f64..151d469415 100644 --- a/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs +++ b/osu.Game/Screens/Edit/Timing/IndeterminateSliderWithTextBoxInput.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.Edit.Timing break; default: - slider.Current.Parse(t.Text); + slider.Current.Parse(t.Text, CultureInfo.CurrentCulture); break; } } diff --git a/osu.Game/Skinning/ISerialisableDrawable.cs b/osu.Game/Skinning/ISerialisableDrawable.cs index 503b44c2dd..c9dcaca6d1 100644 --- a/osu.Game/Skinning/ISerialisableDrawable.cs +++ b/osu.Game/Skinning/ISerialisableDrawable.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Globalization; using osu.Framework.Bindables; using osu.Framework.Extensions.TypeExtensions; using osu.Framework.Graphics; @@ -46,7 +47,7 @@ namespace osu.Game.Skinning if (!(target is IParseable parseable)) throw new InvalidOperationException($"Bindable type {target.GetType().ReadableName()} is not {nameof(IParseable)}."); - parseable.Parse(source); + parseable.Parse(source, CultureInfo.InvariantCulture); } } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 2e91770919..9102231913 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.IO; using System.Linq; using JetBrains.Annotations; @@ -330,7 +331,7 @@ namespace osu.Game.Skinning var bindable = new Bindable(); if (val != null) - bindable.Parse(val); + bindable.Parse(val, CultureInfo.InvariantCulture); return bindable; } } From 996bc659e4ed9806edf9d1f433afc6c03ad7b621 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 14:29:51 +0900 Subject: [PATCH 0454/1118] Adjust some mod multipliers for initial leaderboard sanity --- osu.Game/Rulesets/Mods/ModClassic.cs | 2 +- osu.Game/Rulesets/Mods/ModSynesthesia.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModClassic.cs b/osu.Game/Rulesets/Mods/ModClassic.cs index 55b16297e2..42fdee0402 100644 --- a/osu.Game/Rulesets/Mods/ModClassic.cs +++ b/osu.Game/Rulesets/Mods/ModClassic.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "CL"; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 0.5; public override IconUsage? Icon => FontAwesome.Solid.History; diff --git a/osu.Game/Rulesets/Mods/ModSynesthesia.cs b/osu.Game/Rulesets/Mods/ModSynesthesia.cs index 23cb135c50..9084127f33 100644 --- a/osu.Game/Rulesets/Mods/ModSynesthesia.cs +++ b/osu.Game/Rulesets/Mods/ModSynesthesia.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Mods public override string Name => "Synesthesia"; public override string Acronym => "SY"; public override LocalisableString Description => "Colours hit objects based on the rhythm."; - public override double ScoreMultiplier => 1; + public override double ScoreMultiplier => 0.8; public override ModType Type => ModType.Fun; } } From eb30a603d97e878565c58504831fc149a81ccf3d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 17 Nov 2023 17:54:15 +0900 Subject: [PATCH 0455/1118] Fix typo in argument name --- osu.Game/Screens/Play/HUD/GameplayAccuracyCounter.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/GameplayAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/GameplayAccuracyCounter.cs index 9da032e489..28d664a48b 100644 --- a/osu.Game/Screens/Play/HUD/GameplayAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/GameplayAccuracyCounter.cs @@ -23,11 +23,11 @@ namespace osu.Game.Screens.Play.HUD { base.LoadComplete(); - AccuracyDisplay.BindValueChanged(mod => + AccuracyDisplay.BindValueChanged(mode => { Current.UnbindBindings(); - switch (mod.NewValue) + switch (mode.NewValue) { case AccuracyDisplayMode.Standard: Current.BindTo(scoreProcessor.Accuracy); From 0e4e916388443d9429dee33fd0e3795706252a89 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 15:04:52 +0900 Subject: [PATCH 0456/1118] Sneaky comment fix --- osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyBodyPiece.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyBodyPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyBodyPiece.cs index ee274fc45e..07045b76ca 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyBodyPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyBodyPiece.cs @@ -234,7 +234,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy break; default: - // this is where things get fucked up. + // this is where things get a bit messed up. // honestly there's three modes to handle here but they seem really pointless? // let's wait to see if anyone actually uses them in skins. if (bodySprite != null) From 5e10f9f899de50d4fe589798a486ad36fa881885 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 14:42:32 +0900 Subject: [PATCH 0457/1118] Fix failing tests due to more textboxes being present with searchable dropdowns --- .../Navigation/TestSceneScreenNavigation.cs | 10 +++++---- .../Visual/Online/TestSceneChatOverlay.cs | 2 +- .../Visual/Online/TestSceneCommentActions.cs | 2 +- .../Visual/Settings/TestSceneSettingsPanel.cs | 22 +++++++++---------- .../SongSelect/TestScenePlaySongSelect.cs | 4 ++-- osu.Game/Screens/Select/FilterControl.cs | 2 +- 6 files changed, 22 insertions(+), 20 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index c6a668a714..d8dc512787 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -143,13 +143,13 @@ namespace osu.Game.Tests.Visual.Navigation PushAndConfirm(() => songSelect = new TestPlaySongSelect()); - AddStep("set filter", () => songSelect.ChildrenOfType().Single().Current.Value = "test"); + AddStep("set filter", () => filterControlTextBox().Current.Value = "test"); AddStep("press back", () => InputManager.Click(MouseButton.Button1)); AddAssert("still at song select", () => Game.ScreenStack.CurrentScreen == songSelect); - AddAssert("filter cleared", () => string.IsNullOrEmpty(songSelect.ChildrenOfType().Single().Current.Value)); + AddAssert("filter cleared", () => string.IsNullOrEmpty(filterControlTextBox().Current.Value)); - AddStep("set filter again", () => songSelect.ChildrenOfType().Single().Current.Value = "test"); + AddStep("set filter again", () => filterControlTextBox().Current.Value = "test"); AddStep("open collections dropdown", () => { InputManager.MoveMouseTo(songSelect.ChildrenOfType().Single()); @@ -163,10 +163,12 @@ namespace osu.Game.Tests.Visual.Navigation .ChildrenOfType.DropdownMenu>().Single().State == MenuState.Closed); AddStep("press back a second time", () => InputManager.Click(MouseButton.Button1)); - AddAssert("filter cleared", () => string.IsNullOrEmpty(songSelect.ChildrenOfType().Single().Current.Value)); + AddAssert("filter cleared", () => string.IsNullOrEmpty(filterControlTextBox().Current.Value)); AddStep("press back a third time", () => InputManager.Click(MouseButton.Button1)); ConfirmAtMainMenu(); + + TextBox filterControlTextBox() => songSelect.ChildrenOfType().Single(); } [Test] diff --git a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs index 8a2a66f60f..58feab4ebb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneChatOverlay.cs @@ -631,7 +631,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Nothing happened", () => this.ChildrenOfType().Any()); AddStep("Set report data", () => { - var field = this.ChildrenOfType().Single().ChildrenOfType().Single(); + var field = this.ChildrenOfType().Single().ChildrenOfType().First(); field.Current.Value = "test other"; }); diff --git a/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs b/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs index dbf3b52572..10fdffb8e1 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCommentActions.cs @@ -262,7 +262,7 @@ namespace osu.Game.Tests.Visual.Online AddAssert("Nothing happened", () => this.ChildrenOfType().Any()); AddStep("Set report data", () => { - var field = this.ChildrenOfType().Single().ChildrenOfType().Single(); + var field = this.ChildrenOfType().Single().ChildrenOfType().First(); field.Current.Value = report_text; var reason = this.ChildrenOfType>().Single(); reason.Current.Value = CommentReportReason.Other; diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 69e489b247..5d9c2f890c 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -49,12 +49,12 @@ namespace osu.Game.Tests.Visual.Settings AddStep("reset mouse", () => InputManager.MoveMouseTo(settings)); if (beforeLoad) - AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); + AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); AddUntilStep("wait for items to load", () => settings.SectionsContainer.ChildrenOfType().Any()); if (!beforeLoad) - AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); + AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); AddAssert("ensure all items match filter", () => settings.SectionsContainer .ChildrenOfType().Where(f => f.IsPresent) @@ -76,7 +76,7 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("wait for items to load", () => settings.SectionsContainer.ChildrenOfType().Any()); - AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); + AddStep("set filter", () => settings.SectionsContainer.ChildrenOfType().First().Current.Value = "scaling"); } [Test] @@ -94,7 +94,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("reset mouse", () => InputManager.MoveMouseTo(settings)); AddUntilStep("sections loaded", () => settings.SectionsContainer.Children.Count > 0); - AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType().FirstOrDefault()?.HasFocus == true); + AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType().FirstOrDefault()?.HasFocus == true); AddStep("open key binding subpanel", () => { @@ -106,13 +106,13 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("binding panel textbox focused", () => settings .ChildrenOfType().FirstOrDefault()? - .ChildrenOfType().FirstOrDefault()?.HasFocus == true); + .ChildrenOfType().FirstOrDefault()?.HasFocus == true); AddStep("Press back", () => settings .ChildrenOfType().FirstOrDefault()? .ChildrenOfType().FirstOrDefault()?.TriggerClick()); - AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType().FirstOrDefault()?.HasFocus == true); + AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType().FirstOrDefault()?.HasFocus == true); } [Test] @@ -121,7 +121,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("reset mouse", () => InputManager.MoveMouseTo(settings)); AddUntilStep("sections loaded", () => settings.SectionsContainer.Children.Count > 0); - AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType().FirstOrDefault()?.HasFocus == true); + AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType().FirstOrDefault()?.HasFocus == true); AddStep("open key binding subpanel", () => { @@ -133,19 +133,19 @@ namespace osu.Game.Tests.Visual.Settings AddUntilStep("binding panel textbox focused", () => settings .ChildrenOfType().FirstOrDefault()? - .ChildrenOfType().FirstOrDefault()?.HasFocus == true); + .ChildrenOfType().FirstOrDefault()?.HasFocus == true); AddStep("Escape", () => InputManager.Key(Key.Escape)); - AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType().FirstOrDefault()?.HasFocus == true); + AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType().FirstOrDefault()?.HasFocus == true); } [Test] public void TestSearchTextBoxSelectedOnShow() { - SearchTextBox searchTextBox = null!; + SettingsSearchTextBox searchTextBox = null!; - AddStep("set text", () => (searchTextBox = settings.SectionsContainer.ChildrenOfType().First()).Current.Value = "some text"); + AddStep("set text", () => (searchTextBox = settings.SectionsContainer.ChildrenOfType().First()).Current.Value = "some text"); AddAssert("no text selected", () => searchTextBox.SelectedText == string.Empty); AddRepeatStep("toggle visibility", () => settings.ToggleVisibility(), 2); AddAssert("search text selected", () => searchTextBox.SelectedText == searchTextBox.Current.Value); diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 84750d4c16..64f10cb427 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -1135,7 +1135,7 @@ namespace osu.Game.Tests.Visual.SongSelect { createSongSelect(); - AddStep("set filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = "nonono"); + AddStep("set filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = "nonono"); AddStep("select all", () => InputManager.Keys(PlatformAction.SelectAll)); AddStep("press ctrl-x", () => { @@ -1144,7 +1144,7 @@ namespace osu.Game.Tests.Visual.SongSelect InputManager.ReleaseKey(Key.ControlLeft); }); - AddAssert("filter text cleared", () => songSelect!.FilterControl.ChildrenOfType().First().Text, () => Is.Empty); + AddAssert("filter text cleared", () => songSelect!.FilterControl.ChildrenOfType().First().Text, () => Is.Empty); } private void waitForInitialSelection() diff --git a/osu.Game/Screens/Select/FilterControl.cs b/osu.Game/Screens/Select/FilterControl.cs index c15bd76ef8..1827eb58ca 100644 --- a/osu.Game/Screens/Select/FilterControl.cs +++ b/osu.Game/Screens/Select/FilterControl.cs @@ -250,7 +250,7 @@ namespace osu.Game.Screens.Select protected override bool OnHover(HoverEvent e) => true; - private partial class FilterControlTextBox : SeekLimitedSearchTextBox + internal partial class FilterControlTextBox : SeekLimitedSearchTextBox { private const float filter_text_size = 12; From c2d3dcdd9c1c7bfc1a8df9756db557549a5f4e47 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 15:15:22 +0900 Subject: [PATCH 0458/1118] Fix slider tests and incorrect nullability handling around `freehandToolboxGroup` --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 7 +++++-- .../Visual/SongSelect/TestScenePlaySongSelect.cs | 9 ++++----- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index e5dada19bb..28e972bacd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -42,12 +42,15 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private int currentSegmentLength; [Resolved(CanBeNull = true)] + [CanBeNull] private IPositionSnapProvider positionSnapProvider { get; set; } [Resolved(CanBeNull = true)] + [CanBeNull] private IDistanceSnapProvider distanceSnapProvider { get; set; } [Resolved(CanBeNull = true)] + [CanBeNull] private FreehandSliderToolboxGroup freehandToolboxGroup { get; set; } private readonly IncrementalBSplineBuilder bSplineBuilder = new IncrementalBSplineBuilder { Degree = 4 }; @@ -363,7 +366,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private Vector2[] tryCircleArc(List segment) { - if (segment.Count < 3 || freehandToolboxGroup.CircleThreshold.Value == 0) return null; + if (segment.Count < 3 || freehandToolboxGroup?.CircleThreshold.Value == 0) return null; // Assume the segment creates a reasonable circular arc and then check if it reasonable var points = PathApproximator.BSplineToPiecewiseLinear(segment.ToArray(), bSplineBuilder.Degree); @@ -436,7 +439,7 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders loss /= points.Count; - return loss > freehandToolboxGroup.CircleThreshold.Value || totalWinding > MathHelper.TwoPi ? null : circleArcControlPoints; + return loss > freehandToolboxGroup?.CircleThreshold.Value || totalWinding > MathHelper.TwoPi ? null : circleArcControlPoints; } private enum SliderPlacementState diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 64f10cb427..6b53277964 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -22,7 +22,6 @@ using osu.Game.Beatmaps; using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Extensions; -using osu.Game.Graphics.UserInterface; using osu.Game.Online.API.Requests.Responses; using osu.Game.Online.Chat; using osu.Game.Overlays; @@ -614,7 +613,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("has selection", () => songSelect!.Carousel.SelectedBeatmapInfo != null); - AddStep("set filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = "nonono"); + AddStep("set filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = "nonono"); AddUntilStep("dummy selected", () => Beatmap.Value is DummyWorkingBeatmap); @@ -648,7 +647,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("carousel has correct", () => songSelect!.Carousel.SelectedBeatmapInfo?.MatchesOnlineID(target) == true); AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(target)); - AddStep("reset filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = string.Empty); + AddStep("reset filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = string.Empty); AddAssert("game still correct", () => Beatmap.Value?.BeatmapInfo.MatchesOnlineID(target) == true); AddAssert("carousel still correct", () => songSelect!.Carousel.SelectedBeatmapInfo.MatchesOnlineID(target)); @@ -666,7 +665,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("has selection", () => songSelect!.Carousel.SelectedBeatmapInfo != null); - AddStep("set filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = "nonono"); + AddStep("set filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = "nonono"); AddUntilStep("dummy selected", () => Beatmap.Value is DummyWorkingBeatmap); @@ -689,7 +688,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddUntilStep("carousel has correct", () => songSelect!.Carousel.SelectedBeatmapInfo?.MatchesOnlineID(target) == true); AddUntilStep("game has correct", () => Beatmap.Value.BeatmapInfo.MatchesOnlineID(target)); - AddStep("set filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = "nononoo"); + AddStep("set filter text", () => songSelect!.FilterControl.ChildrenOfType().First().Text = "nononoo"); AddUntilStep("game lost selection", () => Beatmap.Value is DummyWorkingBeatmap); AddAssert("carousel lost selection", () => songSelect!.Carousel.SelectedBeatmapInfo == null); From 0611a1ddc98462af34e6f1820b58dcd231769af5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 15:28:54 +0900 Subject: [PATCH 0459/1118] Avoid background processing falling over is `BeatmapSet` is `null` See https://github.com/ppy/osu/issues/10458. --- osu.Game/BackgroundDataStoreProcessor.cs | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 8195856991..c8a108a5f6 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Linq; using System.Threading; using System.Threading.Tasks; @@ -136,19 +135,13 @@ namespace osu.Game // of other possible ways), but for now avoid queueing if the user isn't logged in at startup. if (api.IsLoggedIn) { - foreach (var b in r.All().Where(b => b.StarRating < 0 || (b.OnlineID > 0 && b.LastOnlineUpdate == null))) - { - Debug.Assert(b.BeatmapSet != null); - beatmapSetIds.Add(b.BeatmapSet.ID); - } + foreach (var b in r.All().Where(b => (b.StarRating < 0 || (b.OnlineID > 0 && b.LastOnlineUpdate == null)) && b.BeatmapSet != null)) + beatmapSetIds.Add(b.BeatmapSet!.ID); } else { - foreach (var b in r.All().Where(b => b.StarRating < 0)) - { - Debug.Assert(b.BeatmapSet != null); - beatmapSetIds.Add(b.BeatmapSet.ID); - } + foreach (var b in r.All().Where(b => b.StarRating < 0 && b.BeatmapSet != null)) + beatmapSetIds.Add(b.BeatmapSet!.ID); } }); From 4983845041794eb5acff6037be4e163b50ae8418 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 15:51:26 +0900 Subject: [PATCH 0460/1118] Update HT mod multiplier to match stable better --- osu.Game/Rulesets/Mods/RateAdjustModHelper.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Mods/RateAdjustModHelper.cs b/osu.Game/Rulesets/Mods/RateAdjustModHelper.cs index ffd4de0e90..8bc481921f 100644 --- a/osu.Game/Rulesets/Mods/RateAdjustModHelper.cs +++ b/osu.Game/Rulesets/Mods/RateAdjustModHelper.cs @@ -32,9 +32,9 @@ namespace osu.Game.Rulesets.Mods value -= 1; if (SpeedChange.Value >= 1) - value /= 5; - - return 1 + value; + return 1 + value / 5; + else + return 0.6 + value; } } From eff81be6fdbfd16fdc9040c3eeb0cd0087d5d7d5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 15:38:06 +0900 Subject: [PATCH 0461/1118] Fix failing test and add coverage of conversion case --- .../SongSelect/TestSceneAdvancedStats.cs | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs index 8650119dd4..4bb2b557ff 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneAdvancedStats.cs @@ -14,7 +14,9 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; +using osu.Game.Rulesets.Mania; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Select.Details; using osuTK.Graphics; @@ -38,6 +40,12 @@ namespace osu.Game.Tests.Visual.SongSelect Width = 500 }); + [SetUpSteps] + public void SetUpSteps() + { + AddStep("reset game ruleset", () => Ruleset.Value = new OsuRuleset().RulesetInfo); + } + private BeatmapInfo exampleBeatmapInfo => new BeatmapInfo { Ruleset = rulesets.AvailableRulesets.First(), @@ -66,8 +74,10 @@ namespace osu.Game.Tests.Visual.SongSelect } [Test] - public void TestManiaFirstBarText() + public void TestManiaFirstBarTextManiaBeatmap() { + AddStep("set game ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo); + AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo { Ruleset = rulesets.GetRuleset(3) ?? throw new InvalidOperationException("osu!mania ruleset not found"), @@ -84,6 +94,27 @@ namespace osu.Game.Tests.Visual.SongSelect AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCsMania); } + [Test] + public void TestManiaFirstBarTextConvert() + { + AddStep("set game ruleset to mania", () => Ruleset.Value = new ManiaRuleset().RulesetInfo); + + AddStep("set beatmap", () => advancedStats.BeatmapInfo = new BeatmapInfo + { + Ruleset = new OsuRuleset().RulesetInfo, + Difficulty = new BeatmapDifficulty + { + CircleSize = 5, + DrainRate = 4.3f, + OverallDifficulty = 4.5f, + ApproachRate = 3.1f + }, + StarRating = 8 + }); + + AddAssert("first bar text is correct", () => advancedStats.ChildrenOfType().First().Text == BeatmapsetsStrings.ShowStatsCsMania); + } + [Test] public void TestEasyMod() { From 2abf3a55ae0bc214c484867ed9a4b67dd267a1f8 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 16:08:34 +0900 Subject: [PATCH 0462/1118] Add IsLegacyScore to SoloScoreInfo --- osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs index ac2d8152b1..732da3d5da 100644 --- a/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs +++ b/osu.Game/Online/API/Requests/Responses/SoloScoreInfo.cs @@ -150,6 +150,12 @@ namespace osu.Game.Online.API.Requests.Responses #endregion + /// + /// Whether this represents a legacy (osu!stable) score. + /// + [JsonIgnore] + public bool IsLegacyScore => LegacyScoreId != null; + public override string ToString() => $"score_id: {ID} user_id: {UserID}"; /// @@ -191,6 +197,7 @@ namespace osu.Game.Online.API.Requests.Responses { OnlineID = OnlineID, LegacyOnlineID = (long?)LegacyScoreId ?? -1, + IsLegacyScore = IsLegacyScore, User = User ?? new APIUser { Id = UserID }, BeatmapInfo = new BeatmapInfo { OnlineID = BeatmapID }, Ruleset = new RulesetInfo { OnlineID = RulesetID }, From 110749205d7366f05ca5d56dd2f037bc7770bff2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 16:13:08 +0900 Subject: [PATCH 0463/1118] Cache `GameplayClockContainer` to allow usage of `OnSeek` --- osu.Game/Screens/Play/GameplayClockContainer.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 5a713fdae7..4def1d36bb 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -17,6 +17,7 @@ namespace osu.Game.Screens.Play /// Encapsulates gameplay timing logic and provides a via DI for gameplay components to use. /// [Cached(typeof(IGameplayClock))] + [Cached(typeof(GameplayClockContainer))] public partial class GameplayClockContainer : Container, IAdjustableClock, IGameplayClock { public IBindable IsPaused => isPaused; From f2c6c348be850bb07347bfb46a9c836c6304895f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 16:13:23 +0900 Subject: [PATCH 0464/1118] Fix `HitError` `Clear` methods not correctly returning pooled drawables --- .../Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 6 +++++- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 6 +++++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index eb5221aa45..a9141110c5 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -485,7 +485,11 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters } } - public override void Clear() => judgementsContainer.Clear(); + public override void Clear() + { + foreach (var j in judgementsContainer) + j.FadeOut().Expire(); + } public enum CentreMarkerStyles { diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 5793713fca..d95959bf9e 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -63,7 +63,11 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters judgementsFlow.Push(GetColourForHitResult(judgement.Type)); } - public override void Clear() => judgementsFlow.Clear(); + public override void Clear() + { + foreach (var j in judgementsFlow) + j.FadeOut().Expire(); + } private partial class JudgementFlow : FillFlowContainer { From fb44fb18e021a153d3d44d12190d53f15c288838 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 16:42:20 +0900 Subject: [PATCH 0465/1118] Update in line with upstream changes --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 019c38679e..9433588b03 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -128,6 +128,8 @@ namespace osu.Game.Screens.Select.Details IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; BeatmapDifficulty adjustedDifficulty = null; + IRulesetInfo ruleset = gameRuleset?.Value ?? beatmapInfo.Ruleset; + if (baseDifficulty != null && (mods.Value.Any(m => m is IApplicableToDifficulty) || mods.Value.Any(m => m is IApplicableToRate))) { @@ -140,21 +142,17 @@ namespace osu.Game.Screens.Select.Details if (gameRuleset != null) { - Ruleset ruleset = gameRuleset.Value.CreateInstance(); - double rate = 1; foreach (var mod in mods.Value.OfType()) rate = mod.ApplyToRate(0, rate); - adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); + adjustedDifficulty = ruleset.CreateInstance().GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); rateAdjustTooltip.UpdateAttribute("AR", originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); rateAdjustTooltip.UpdateAttribute("OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); } } - IRulesetInfo ruleset = gameRuleset?.Value ?? beatmapInfo.Ruleset; - switch (ruleset.OnlineID) { case 3: From 9433180ffeacf239b4f688ad954387a12fcbbfb7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 16:57:31 +0900 Subject: [PATCH 0466/1118] Fix various code quality and visual issues with `AdjustedAttributesTooltip` --- .../Mods/AdjustedAttributesTooltip.cs | 135 +++++++++--------- 1 file changed, 68 insertions(+), 67 deletions(-) diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs index e9b7ee5c54..a56c09931a 100644 --- a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -15,84 +16,72 @@ using osuTK; namespace osu.Game.Overlays.Mods { - public partial class AdjustedAttributesTooltip : CompositeDrawable, ITooltip + public partial class AdjustedAttributesTooltip : VisibilityContainer, ITooltip { private readonly Dictionary> attributes = new Dictionary>(); - private readonly Container content; + private FillFlowContainer? attributesFillFlow; - private readonly FillFlowContainer attributesFillFlow; + private Container content = null!; [Resolved] private OsuColour colours { get; set; } = null!; - public AdjustedAttributesTooltip() - { - // Need to be initialized in constructor to ensure accessability in AddAttribute function - InternalChild = content = new Container - { - AutoSizeAxes = Axes.Both - }; - attributesFillFlow = new FillFlowContainer - { - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both - }; - } - [BackgroundDependencyLoader] private void load() { AutoSizeAxes = Axes.Both; Masking = true; - CornerRadius = 15; + CornerRadius = 5; - content.AddRange(new Drawable[] + InternalChildren = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = colours.Gray1, - Alpha = 0.8f - }, - new FillFlowContainer + content = new Container { AutoSizeAxes = Axes.Both, - Padding = new MarginPadding { Vertical = 10, Horizontal = 15 }, - Direction = FillDirection.Vertical, Children = new Drawable[] { - new OsuSpriteText + new Box { - Text = "One or more values are being adjusted by mods that change speed.", + RelativeSizeAxes = Axes.Both, + Colour = colours.Gray3, }, - attributesFillFlow + new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding { Vertical = 10, Horizontal = 15 }, + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new OsuSpriteText + { + Text = "One or more values are being adjusted by mods that change speed.", + }, + attributesFillFlow = new FillFlowContainer + { + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both + } + } + } } - } - }); - } + }, + }; - private void checkAttributes() - { foreach (var attribute in attributes) - { - if (!Precision.AlmostEquals(attribute.Value.Value.Old, attribute.Value.Value.New)) - { - content.Show(); - return; - } - } + attributesFillFlow?.Add(new AttributeDisplay(attribute.Key, attribute.Value.GetBoundCopy())); - content.Hide(); + updateVisibility(); } public void AddAttribute(string name) { Bindable newBindable = new Bindable(); - newBindable.BindValueChanged(_ => checkAttributes()); + newBindable.BindValueChanged(_ => updateVisibility()); attributes.Add(name, newBindable); - attributesFillFlow.Add(new AttributeDisplay(name, newBindable.GetBoundCopy())); + + attributesFillFlow?.Add(new AttributeDisplay(name, newBindable.GetBoundCopy())); } public void UpdateAttribute(string name, double oldValue, double newValue) @@ -102,8 +91,8 @@ namespace osu.Game.Overlays.Mods Bindable attribute = attributes[name]; OldNewPair attributeValue = attribute.Value; - attributeValue.Old = oldValue; - attributeValue.New = newValue; + attributeValue.OldValue = oldValue; + attributeValue.NewValue = newValue; attribute.Value = attributeValue; } @@ -116,14 +105,20 @@ namespace osu.Game.Overlays.Mods { } - public void Move(Vector2 pos) - { - Position = pos; - } + protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); + protected override void PopOut() => this.FadeOut(200, Easing.OutQuint); - private struct OldNewPair + public void Move(Vector2 pos) => Position = pos; + + private void updateVisibility() { - public double Old, New; + if (!IsLoaded) + return; + + if (attributes.Any(attribute => !Precision.AlmostEquals(attribute.Value.Value.OldValue, attribute.Value.Value.NewValue))) + content.Show(); + else + content.Hide(); } private partial class AttributeDisplay : CompositeDrawable @@ -131,33 +126,39 @@ namespace osu.Game.Overlays.Mods public readonly Bindable AttributeValues; public readonly string AttributeName; - private readonly OsuSpriteText text = new OsuSpriteText - { - Font = OsuFont.Default.With(weight: FontWeight.Bold) - }; + private readonly OsuSpriteText text; - public AttributeDisplay(string name, Bindable boundCopy) + public AttributeDisplay(string name, Bindable values) { AutoSizeAxes = Axes.Both; AttributeName = name; - AttributeValues = boundCopy; - InternalChild = text; + AttributeValues = values; + + InternalChild = text = new OsuSpriteText + { + Font = OsuFont.Default.With(weight: FontWeight.Bold) + }; + AttributeValues.BindValueChanged(_ => update(), true); } private void update() { - if (Precision.AlmostEquals(AttributeValues.Value.Old, AttributeValues.Value.New)) + if (Precision.AlmostEquals(AttributeValues.Value.OldValue, AttributeValues.Value.NewValue)) { Hide(); + return; } - else - { - Show(); - text.Text = $"{AttributeName}: {(AttributeValues.Value.Old):0.0#} → {(AttributeValues.Value.New):0.0#}"; - } + + Show(); + text.Text = $"{AttributeName}: {(AttributeValues.Value.OldValue):0.0#} → {(AttributeValues.Value.NewValue):0.0#}"; } } + + private struct OldNewPair + { + public double OldValue, NewValue; + } } } From c131f91736dc2d947fce9c0decea049358d93536 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 16:59:09 +0900 Subject: [PATCH 0467/1118] Add tests --- .../TestSceneSliderEarlyHitJudgement.cs | 187 ++++++++++++++++++ 1 file changed, 187 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs new file mode 100644 index 0000000000..46278dfb12 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs @@ -0,0 +1,187 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Replays; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public partial class TestSceneSliderEarlyHitJudgement : RateAdjustedBeatmapTestScene + { + private const double time_slider_start = 1000; + private const double time_slider_tick = 2000; + private const double time_slider_end = 3000; + + private static readonly Vector2 slider_start_position = new Vector2(256 - slider_path_length / 2, 192); + private static readonly Vector2 slider_end_position = new Vector2(256 + slider_path_length / 2, 192); + + private ScoreAccessibleReplayPlayer currentPlayer = null!; + + private const float slider_path_length = 200; + + private readonly List judgementResults = new List(); + + [Test] + public void TestHitEarlyMoveIntoFollowRegion() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 200, slider_start_position + new Vector2(32, 0), OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end - 200, slider_end_position + new Vector2(32, 0), OsuAction.LeftButton), + }); + + assertHeadJudgement(HitResult.Miss); + assertTickJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.LargeTickHit); + assertSliderJudgement(HitResult.IgnoreHit); + } + + [Test] + public void TestHitEarlyMoveOutsideFollowRegion() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 200, slider_start_position + new Vector2(96, 0), OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end - 200, slider_end_position + new Vector2(96, 0), OsuAction.LeftButton), + }); + + assertHeadJudgement(HitResult.Miss); + assertTickJudgement(HitResult.LargeTickMiss); + assertTailJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreMiss); + } + + private void assertHeadJudgement(HitResult result) + { + AddAssert( + "check head result", + () => judgementResults.SingleOrDefault(r => r.HitObject is SliderHeadCircle)?.Type, + () => Is.EqualTo(result)); + } + + private void assertTickJudgement(HitResult result) + { + AddAssert( + "check tick result", + () => judgementResults.SingleOrDefault(r => r.HitObject is SliderTick)?.Type, + () => Is.EqualTo(result)); + } + + private void assertRepeatJudgement(HitResult result) + { + AddAssert( + "check tick result", + () => judgementResults.SingleOrDefault(r => r.HitObject is SliderRepeat)?.Type, + () => Is.EqualTo(result)); + } + + private void assertTailJudgement(HitResult result) + { + AddAssert( + "check tail result", + () => judgementResults.SingleOrDefault(r => r.HitObject is SliderTailCircle)?.Type, + () => Is.EqualTo(result)); + } + + private void assertSliderJudgement(HitResult result) + { + AddAssert( + "check slider result", + () => judgementResults.SingleOrDefault(r => r.HitObject is Slider)?.Type, + () => Is.EqualTo(result)); + } + + private Vector2 computePositionFromTime(double time) + { + Vector2 dist = slider_end_position - slider_start_position; + double t = (time - time_slider_start) / (time_slider_end - time_slider_start); + return slider_start_position + dist * (float)t; + } + + private void performTest(List frames, Action? adjustSliderFunc = null, bool classic = false) + { + Slider slider = new Slider + { + StartTime = time_slider_start, + Position = new Vector2(256 - slider_path_length / 2, 192), + TickDistanceMultiplier = 3, + ClassicSliderBehaviour = classic, + Path = new SliderPath(PathType.LINEAR, new[] + { + Vector2.Zero, + new Vector2(slider_path_length, 0), + }, slider_path_length), + }; + + adjustSliderFunc?.Invoke(slider); + + AddStep("load player", () => + { + Beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + HitObjects = { slider }, + BeatmapInfo = + { + Difficulty = new BeatmapDifficulty + { + SliderMultiplier = 1, + SliderTickRate = 3 + }, + Ruleset = new OsuRuleset().RulesetInfo, + } + }); + + var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); + + p.OnLoadComplete += _ => + { + p.ScoreProcessor.NewJudgement += result => + { + if (currentPlayer == p) judgementResults.Add(result); + }; + }; + + LoadScreen(currentPlayer = p); + judgementResults.Clear(); + }); + + AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); + AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); + AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); + } + + private partial class ScoreAccessibleReplayPlayer : ReplayPlayer + { + public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + + protected override bool PauseOnFocusLost => false; + + public ScoreAccessibleReplayPlayer(Score score) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) + { + } + } + } +} From 3f67538d6103f411bbbaf6f54dcd750f650f2387 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 16:59:41 +0900 Subject: [PATCH 0468/1118] Allow slider to be tracked before its start time --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index 292f2ffd7d..a1724d6fdc 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -153,11 +153,9 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } Tracking = - // in valid time range - Time.Current >= drawableSlider.HitObject.StartTime // even in an edge case where current time has exceeded the slider's time, we may not have finished judging. // we don't want to potentially update from Tracking=true to Tracking=false at this point. - && (!drawableSlider.AllJudged || Time.Current <= drawableSlider.HitObject.GetEndTime()) + (!drawableSlider.AllJudged || Time.Current <= drawableSlider.HitObject.GetEndTime()) // in valid position range && lastScreenSpaceMousePosition.HasValue && followCircleReceptor.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && // valid action From 3131d376218af0673a1b79190f52f7aaf576105e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 17:00:21 +0900 Subject: [PATCH 0469/1118] Clear transformations with more fire --- osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs | 5 ++++- .../Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs index a9141110c5..443863fb2f 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/BarHitErrorMeter.cs @@ -488,7 +488,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters public override void Clear() { foreach (var j in judgementsContainer) - j.FadeOut().Expire(); + { + j.ClearTransforms(); + j.Expire(); + } } public enum CentreMarkerStyles diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index d95959bf9e..65f4b50dde 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -66,7 +66,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters public override void Clear() { foreach (var j in judgementsFlow) - j.FadeOut().Expire(); + { + j.ClearTransforms(); + j.Expire(); + } } private partial class JudgementFlow : FillFlowContainer From 9a982a9564e11252ba3a7e0dda77ffc6c0d86e0b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 17:13:15 +0900 Subject: [PATCH 0470/1118] Tidy up `GetRateAdjustedDisplayDifficulty` implemetations --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 7 +++++-- osu.Game.Rulesets.Osu/OsuRuleset.cs | 10 ++++++++-- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 8 +++++--- osu.Game/Rulesets/Ruleset.cs | 1 + 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 12d197109b..bcb28328f0 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -240,10 +240,13 @@ namespace osu.Game.Rulesets.Catch { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); - double preempt = adjustedDifficulty.ApproachRate < 6 ? (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); + double preempt = adjustedDifficulty.ApproachRate < 6 + ? 1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5 + : 1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5; preempt /= rate; - adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); + + adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); return adjustedDifficulty; } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 88489f2dc3..5355743a50 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -336,12 +336,18 @@ namespace osu.Game.Rulesets.Osu { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); - double preempt = adjustedDifficulty.ApproachRate < 5 ? (1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5) : (1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5); + double preempt = adjustedDifficulty.ApproachRate < 5 + ? 1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5 + : 1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5; + preempt /= rate; - adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? ((1800 - preempt) / 120) : ((1200 - preempt) / 150 + 5)); + + adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); double hitwindow = 80.0 - 6 * adjustedDifficulty.OverallDifficulty; + hitwindow /= rate; + adjustedDifficulty.OverallDifficulty = (float)(80.0 - hitwindow) / 6; return adjustedDifficulty; diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 6e5cdbf2d1..94136a11d3 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -269,9 +269,11 @@ namespace osu.Game.Rulesets.Taiko { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); - double hitwindow = 35.0 - 15.0 * (adjustedDifficulty.OverallDifficulty - 5) / 5; - hitwindow /= rate; - adjustedDifficulty.OverallDifficulty = (float)(5 * (35 - hitwindow) / 15 + 5); + double hitWindow = 35.0 - 15.0 * (adjustedDifficulty.OverallDifficulty - 5) / 5; + + hitWindow /= rate; + + adjustedDifficulty.OverallDifficulty = (float)(5 * (35 - hitWindow) / 15 + 5); return adjustedDifficulty; } diff --git a/osu.Game/Rulesets/Ruleset.cs b/osu.Game/Rulesets/Ruleset.cs index c7c81ecb55..37a35fd3ae 100644 --- a/osu.Game/Rulesets/Ruleset.cs +++ b/osu.Game/Rulesets/Ruleset.cs @@ -380,6 +380,7 @@ namespace osu.Game.Rulesets /// /// Applies changes to difficulty attributes for presenting to a user a rough estimate of how rate adjust mods affect difficulty. /// Importantly, this should NOT BE USED FOR ANY CALCULATIONS. + /// /// It is also not always correct, and arguably is never correct depending on your frame of mind. /// /// >The that will be adjusted. From e865de7a6b8cdbef85211b1d74571d1c8ac9b8b6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 13 Dec 2023 09:25:56 +0100 Subject: [PATCH 0471/1118] Update test assertions in line with half time mod multiplier changes --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index f0822ce2a8..fed5f68449 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -787,7 +787,8 @@ namespace osu.Game.Tests.Visual.UserInterface InputManager.MoveMouseTo(this.ChildrenOfType().Single(preset => preset.Preset.Value.Name == "Half Time 0.5x")); InputManager.Click(MouseButton.Left); }); - AddAssert("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.5)); + AddAssert("difficulty multiplier display shows correct value", + () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.1).Within(Precision.DOUBLE_EPSILON)); // this is highly unorthodox in a test, but because the `ModSettingChangeTracker` machinery heavily leans on events and object disposal and re-creation, // it is instrumental in the reproduction of the failure scenario that this test is supposed to cover. @@ -796,7 +797,8 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("open customisation area", () => modSelectOverlay.CustomisationButton!.TriggerClick()); AddStep("reset half time speed to default", () => modSelectOverlay.ChildrenOfType().Single() .ChildrenOfType>().Single().TriggerClick()); - AddUntilStep("difficulty multiplier display shows correct value", () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.7)); + AddUntilStep("difficulty multiplier display shows correct value", + () => modSelectOverlay.ChildrenOfType().Single().Current.Value, () => Is.EqualTo(0.3).Within(Precision.DOUBLE_EPSILON)); } private void waitForColumnLoad() => AddUntilStep("all column content loaded", () => From 01710780525bca3126764bb77805964968ed19d6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 17:33:24 +0900 Subject: [PATCH 0472/1118] Move object counts to BeatmapInfo --- osu.Game.Tournament/Models/TournamentBeatmap.cs | 6 ++++++ osu.Game/BackgroundDataStoreProcessor.cs | 11 +++++++---- osu.Game/Beatmaps/BeatmapDifficulty.cs | 9 --------- osu.Game/Beatmaps/BeatmapImporter.cs | 6 +++--- osu.Game/Beatmaps/BeatmapInfo.cs | 4 ++++ osu.Game/Beatmaps/BeatmapUpdater.cs | 4 ++-- osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 13 ------------- osu.Game/Beatmaps/IBeatmapInfo.cs | 13 +++++++++++++ .../Online/API/Requests/Responses/APIBeatmap.cs | 8 +++++--- .../Legacy/LegacyBeatmapConversionDifficultyInfo.cs | 4 ++-- 10 files changed, 42 insertions(+), 36 deletions(-) diff --git a/osu.Game.Tournament/Models/TournamentBeatmap.cs b/osu.Game.Tournament/Models/TournamentBeatmap.cs index 7f57b6a151..a7ba5b7db1 100644 --- a/osu.Game.Tournament/Models/TournamentBeatmap.cs +++ b/osu.Game.Tournament/Models/TournamentBeatmap.cs @@ -21,6 +21,10 @@ namespace osu.Game.Tournament.Models public double StarRating { get; set; } + public int EndTimeObjectCount { get; set; } + + public int TotalObjectCount { get; set; } + public IBeatmapMetadataInfo Metadata { get; set; } = new BeatmapMetadata(); public IBeatmapDifficultyInfo Difficulty { get; set; } = new BeatmapDifficulty(); @@ -41,6 +45,8 @@ namespace osu.Game.Tournament.Models Metadata = beatmap.Metadata; Difficulty = beatmap.Difficulty; Covers = beatmap.BeatmapSet?.Covers ?? new BeatmapSetOnlineCovers(); + EndTimeObjectCount = beatmap.EndTimeObjectCount; + TotalObjectCount = beatmap.TotalObjectCount; } public bool Equals(IBeatmapInfo? other) => other is TournamentBeatmap b && this.MatchesOnlineID(b); diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index c8a108a5f6..10cc13dc29 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -20,7 +20,6 @@ using osu.Game.Rulesets; using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Screens.Play; -using Realms; namespace osu.Game { @@ -177,9 +176,13 @@ namespace osu.Game { Logger.Log("Querying for beatmaps with missing hitobject counts to reprocess..."); - HashSet beatmapIds = realmAccess.Run(r => new HashSet(r.All() - .Filter($"{nameof(BeatmapInfo.Difficulty)}.{nameof(BeatmapDifficulty.TotalObjectCount)} == 0") - .AsEnumerable().Select(b => b.ID))); + HashSet beatmapIds = new HashSet(); + + realmAccess.Run(r => + { + foreach (var b in r.All().Where(b => b.TotalObjectCount == 0)) + beatmapIds.Add(b.ID); + }); Logger.Log($"Found {beatmapIds.Count} beatmaps which require reprocessing."); diff --git a/osu.Game/Beatmaps/BeatmapDifficulty.cs b/osu.Game/Beatmaps/BeatmapDifficulty.cs index 785728141e..ac2267380d 100644 --- a/osu.Game/Beatmaps/BeatmapDifficulty.cs +++ b/osu.Game/Beatmaps/BeatmapDifficulty.cs @@ -21,9 +21,6 @@ namespace osu.Game.Beatmaps public double SliderMultiplier { get; set; } = 1.4; public double SliderTickRate { get; set; } = 1; - public int EndTimeObjectCount { get; set; } - public int TotalObjectCount { get; set; } - public BeatmapDifficulty() { } @@ -47,9 +44,6 @@ namespace osu.Game.Beatmaps difficulty.SliderMultiplier = SliderMultiplier; difficulty.SliderTickRate = SliderTickRate; - - difficulty.EndTimeObjectCount = EndTimeObjectCount; - difficulty.TotalObjectCount = TotalObjectCount; } public virtual void CopyFrom(IBeatmapDifficultyInfo other) @@ -61,9 +55,6 @@ namespace osu.Game.Beatmaps SliderMultiplier = other.SliderMultiplier; SliderTickRate = other.SliderTickRate; - - EndTimeObjectCount = other.EndTimeObjectCount; - TotalObjectCount = other.TotalObjectCount; } } } diff --git a/osu.Game/Beatmaps/BeatmapImporter.cs b/osu.Game/Beatmaps/BeatmapImporter.cs index 31d6b0108e..7bb52eef52 100644 --- a/osu.Game/Beatmaps/BeatmapImporter.cs +++ b/osu.Game/Beatmaps/BeatmapImporter.cs @@ -388,9 +388,7 @@ namespace osu.Game.Beatmaps OverallDifficulty = decodedDifficulty.OverallDifficulty, ApproachRate = decodedDifficulty.ApproachRate, SliderMultiplier = decodedDifficulty.SliderMultiplier, - SliderTickRate = decodedDifficulty.SliderTickRate, - EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration), - TotalObjectCount = decoded.HitObjects.Count + SliderTickRate = decodedDifficulty.SliderTickRate }; var metadata = new BeatmapMetadata @@ -428,6 +426,8 @@ namespace osu.Game.Beatmaps GridSize = decodedInfo.GridSize, TimelineZoom = decodedInfo.TimelineZoom, MD5Hash = memoryStream.ComputeMD5Hash(), + EndTimeObjectCount = decoded.HitObjects.Count(h => h is IHasDuration), + TotalObjectCount = decoded.HitObjects.Count }; beatmaps.Add(beatmap); diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index c1aeec1f71..2d04732f91 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -120,6 +120,10 @@ namespace osu.Game.Beatmaps [JsonIgnore] public bool Hidden { get; set; } + public int EndTimeObjectCount { get; set; } + + public int TotalObjectCount { get; set; } + /// /// Reset any fetched online linking information (and history). /// diff --git a/osu.Game/Beatmaps/BeatmapUpdater.cs b/osu.Game/Beatmaps/BeatmapUpdater.cs index 472ac26ebe..27492b8bac 100644 --- a/osu.Game/Beatmaps/BeatmapUpdater.cs +++ b/osu.Game/Beatmaps/BeatmapUpdater.cs @@ -91,8 +91,8 @@ namespace osu.Game.Beatmaps var working = workingBeatmapCache.GetWorkingBeatmap(beatmapInfo); var beatmap = working.Beatmap; - beatmapInfo.Difficulty.EndTimeObjectCount = beatmap.HitObjects.Count(h => h is IHasDuration); - beatmapInfo.Difficulty.TotalObjectCount = beatmap.HitObjects.Count; + beatmapInfo.EndTimeObjectCount = beatmap.HitObjects.Count(h => h is IHasDuration); + beatmapInfo.TotalObjectCount = beatmap.HitObjects.Count; // And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required. workingBeatmapCache.Invalidate(beatmapInfo); diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index 47b261d1f6..e7a3d87d0a 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -44,19 +44,6 @@ namespace osu.Game.Beatmaps /// double SliderTickRate { get; } - /// - /// The number of hitobjects in the beatmap with a distinct end time. - /// - /// - /// Canonically, these are hitobjects are either sliders or spinners. - /// - int EndTimeObjectCount { get; } - - /// - /// The total number of hitobjects in the beatmap. - /// - int TotalObjectCount { get; } - /// /// Maps a difficulty value [0, 10] to a two-piece linear range of values. /// diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index b8c69cc525..9dcff5ce5e 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -61,5 +61,18 @@ namespace osu.Game.Beatmaps /// The basic star rating for this beatmap (with no mods applied). /// double StarRating { get; } + + /// + /// The number of hitobjects in the beatmap with a distinct end time. + /// + /// + /// Canonically, these are hitobjects are either sliders or spinners. + /// + int EndTimeObjectCount { get; } + + /// + /// The total number of hitobjects in the beatmap. + /// + int TotalObjectCount { get; } } } diff --git a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs index c1ceff7c43..e5ecfe2c99 100644 --- a/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs +++ b/osu.Game/Online/API/Requests/Responses/APIBeatmap.cs @@ -41,6 +41,10 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty(@"difficulty_rating")] public double StarRating { get; set; } + public int EndTimeObjectCount => SliderCount + SpinnerCount; + + public int TotalObjectCount => CircleCount + SliderCount + SpinnerCount; + [JsonProperty(@"drain")] public float DrainRate { get; set; } @@ -108,9 +112,7 @@ namespace osu.Game.Online.API.Requests.Responses DrainRate = DrainRate, CircleSize = CircleSize, ApproachRate = ApproachRate, - OverallDifficulty = OverallDifficulty, - EndTimeObjectCount = SliderCount + SpinnerCount, - TotalObjectCount = CircleCount + SliderCount + SpinnerCount + OverallDifficulty = OverallDifficulty }; IBeatmapSetInfo? IBeatmapInfo.BeatmapSet => BeatmapSet; diff --git a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs index 2021aa127d..7d69069455 100644 --- a/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs +++ b/osu.Game/Rulesets/Scoring/Legacy/LegacyBeatmapConversionDifficultyInfo.cs @@ -62,8 +62,8 @@ namespace osu.Game.Rulesets.Scoring.Legacy SourceRuleset = beatmapInfo.Ruleset, CircleSize = beatmapInfo.Difficulty.CircleSize, OverallDifficulty = beatmapInfo.Difficulty.OverallDifficulty, - EndTimeObjectCount = beatmapInfo.Difficulty.EndTimeObjectCount, - TotalObjectCount = beatmapInfo.Difficulty.TotalObjectCount + EndTimeObjectCount = beatmapInfo.EndTimeObjectCount, + TotalObjectCount = beatmapInfo.TotalObjectCount }; } } From 5062c53e36c1d4d68cd62bfedd14e1306553c8e7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 13 Dec 2023 17:33:39 +0900 Subject: [PATCH 0473/1118] Refactor everything for sanity --- .../Mods/AdjustedAttributesTooltip.cs | 105 +++++++----------- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 7 +- .../Screens/Select/Details/AdvancedStats.cs | 6 +- 3 files changed, 41 insertions(+), 77 deletions(-) diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs index a56c09931a..ed12d917ad 100644 --- a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -1,29 +1,30 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Collections.Generic; +using System; using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; using osu.Framework.Graphics.Shapes; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics; using osu.Framework.Utils; +using osu.Game.Beatmaps; +using osu.Game.Graphics; +using osu.Game.Graphics.Sprites; using osuTK; namespace osu.Game.Overlays.Mods { public partial class AdjustedAttributesTooltip : VisibilityContainer, ITooltip { - private readonly Dictionary> attributes = new Dictionary>(); - - private FillFlowContainer? attributesFillFlow; + private FillFlowContainer attributesFillFlow = null!; private Container content = null!; + private BeatmapDifficulty? originalDifficulty; + private BeatmapDifficulty? adjustedDifficulty; + [Resolved] private OsuColour colours { get; set; } = null!; @@ -69,36 +70,43 @@ namespace osu.Game.Overlays.Mods }, }; - foreach (var attribute in attributes) - attributesFillFlow?.Add(new AttributeDisplay(attribute.Key, attribute.Value.GetBoundCopy())); - - updateVisibility(); + updateDisplay(); } - public void AddAttribute(string name) + public void UpdateAttributes(BeatmapDifficulty original, BeatmapDifficulty adjusted) { - Bindable newBindable = new Bindable(); - newBindable.BindValueChanged(_ => updateVisibility()); - attributes.Add(name, newBindable); + originalDifficulty = original; + adjustedDifficulty = adjusted; - attributesFillFlow?.Add(new AttributeDisplay(name, newBindable.GetBoundCopy())); + if (IsLoaded) + updateDisplay(); } - public void UpdateAttribute(string name, double oldValue, double newValue) + private void updateDisplay() { - if (!attributes.ContainsKey(name)) return; + attributesFillFlow.Clear(); - Bindable attribute = attributes[name]; + if (originalDifficulty == null || adjustedDifficulty == null) + return; - OldNewPair attributeValue = attribute.Value; - attributeValue.OldValue = oldValue; - attributeValue.NewValue = newValue; + attemptAdd("AR", bd => bd.ApproachRate); + attemptAdd("OD", bd => bd.OverallDifficulty); + attemptAdd("CS", bd => bd.CircleSize); + attemptAdd("HP", bd => bd.DrainRate); - attribute.Value = attributeValue; - } + if (attributesFillFlow.Any()) + content.Show(); + else + content.Hide(); - protected override void Update() - { + void attemptAdd(string name, Func lookup) + { + double a = lookup(originalDifficulty); + double b = lookup(adjustedDifficulty); + + if (!Precision.AlmostEquals(a, b)) + attributesFillFlow.Add(new AttributeDisplay(name, a, b)); + } } public void SetContent(object content) @@ -110,55 +118,18 @@ namespace osu.Game.Overlays.Mods public void Move(Vector2 pos) => Position = pos; - private void updateVisibility() - { - if (!IsLoaded) - return; - - if (attributes.Any(attribute => !Precision.AlmostEquals(attribute.Value.Value.OldValue, attribute.Value.Value.NewValue))) - content.Show(); - else - content.Hide(); - } - private partial class AttributeDisplay : CompositeDrawable { - public readonly Bindable AttributeValues; - public readonly string AttributeName; - - private readonly OsuSpriteText text; - - public AttributeDisplay(string name, Bindable values) + public AttributeDisplay(string name, double original, double adjusted) { AutoSizeAxes = Axes.Both; - AttributeName = name; - AttributeValues = values; - - InternalChild = text = new OsuSpriteText + InternalChild = new OsuSpriteText { - Font = OsuFont.Default.With(weight: FontWeight.Bold) + Font = OsuFont.Default.With(weight: FontWeight.Bold), + Text = $"{name}: {original:0.0#} → {adjusted:0.0#}" }; - - AttributeValues.BindValueChanged(_ => update(), true); } - - private void update() - { - if (Precision.AlmostEquals(AttributeValues.Value.OldValue, AttributeValues.Value.NewValue)) - { - Hide(); - return; - } - - Show(); - text.Text = $"{AttributeName}: {(AttributeValues.Value.OldValue):0.0#} → {(AttributeValues.Value.NewValue):0.0#}"; - } - } - - private struct OldNewPair - { - public double OldValue, NewValue; } } } diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 0c35e55df5..dd3daa2b7a 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -60,6 +60,7 @@ namespace osu.Game.Overlays.Mods private AdjustedAttributesTooltip rateAdjustTooltip = null!; public ITooltip GetCustomTooltip() => rateAdjustTooltip; + public object TooltipContent => this; private const float transition_duration = 250; @@ -103,9 +104,6 @@ namespace osu.Game.Overlays.Mods { base.LoadComplete(); - rateAdjustTooltip.AddAttribute("AR"); - rateAdjustTooltip.AddAttribute("OD"); - mods.BindValueChanged(_ => { modSettingChangeTracker?.Dispose(); @@ -184,8 +182,7 @@ namespace osu.Game.Overlays.Mods Ruleset ruleset = gameRuleset.Value.CreateInstance(); BeatmapDifficulty adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); - rateAdjustTooltip.UpdateAttribute("AR", originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); - rateAdjustTooltip.UpdateAttribute("OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); + rateAdjustTooltip.UpdateAttributes(originalDifficulty, adjustedDifficulty); approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 9433588b03..2a9e69f141 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -101,9 +101,6 @@ namespace osu.Game.Screens.Select.Details gameRuleset.BindValueChanged(_ => updateStatistics()); mods.BindValueChanged(modsChanged, true); - - rateAdjustTooltip.AddAttribute("AR"); - rateAdjustTooltip.AddAttribute("OD"); } private ModSettingChangeTracker modSettingChangeTracker; @@ -148,8 +145,7 @@ namespace osu.Game.Screens.Select.Details adjustedDifficulty = ruleset.CreateInstance().GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); - rateAdjustTooltip.UpdateAttribute("AR", originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); - rateAdjustTooltip.UpdateAttribute("OD", originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); + rateAdjustTooltip.UpdateAttributes(originalDifficulty, adjustedDifficulty); } } From 812f52e793b4729548e1b452a063591fb28c546d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 17:38:16 +0900 Subject: [PATCH 0474/1118] Bump version number --- osu.Game/Database/RealmAccess.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 9c7fe464dd..191bb49b0c 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -88,9 +88,9 @@ namespace osu.Game.Database /// 34 2023-08-21 Add BackgroundReprocessingFailed flag to ScoreInfo to track upgrade failures. /// 35 2023-10-16 Clear key combinations of keybindings that are assigned to more than one action in a given settings section. /// 36 2023-10-26 Add LegacyOnlineID to ScoreInfo. Move osu_scores_*_high IDs stored in OnlineID to LegacyOnlineID. Reset anomalous OnlineIDs. - /// 37 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapDifficulty. + /// 38 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapInfo. /// - private const int schema_version = 37; + private const int schema_version = 38; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. From 7a4ea90bda6374b89a978e735c88731429e14fcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 13 Dec 2023 11:01:06 +0100 Subject: [PATCH 0475/1118] Fix test failures due to dependency becoming required --- .../Overlays/Settings/Sections/General/UpdateSettings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs index a8f5b655d0..3ff5556f4d 100644 --- a/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/UpdateSettings.cs @@ -35,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.General private Storage storage { get; set; } = null!; [BackgroundDependencyLoader] - private void load(OsuConfigManager config, OsuGame game) + private void load(OsuConfigManager config, OsuGame? game) { Add(new SettingsEnumDropdown { @@ -57,7 +57,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { notifications?.Post(new SimpleNotification { - Text = GeneralSettingsStrings.RunningLatestRelease(game.Version), + Text = GeneralSettingsStrings.RunningLatestRelease(game!.Version), Icon = FontAwesome.Solid.CheckCircle, }); } @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Settings.Sections.General Add(new SettingsButton { Text = GeneralSettingsStrings.ChangeFolderLocation, - Action = () => game.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) + Action = () => game?.PerformFromScreen(menu => menu.Push(new MigrationSelectScreen())) }); } } From 683ac6f63a169351bf2bf16c9ddd24ec1493f551 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 19:25:41 +0900 Subject: [PATCH 0476/1118] Add some more tests --- .../TestSceneSliderEarlyHitJudgement.cs | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs index 46278dfb12..fb7d00b28c 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs @@ -25,11 +25,12 @@ namespace osu.Game.Rulesets.Osu.Tests public partial class TestSceneSliderEarlyHitJudgement : RateAdjustedBeatmapTestScene { private const double time_slider_start = 1000; - private const double time_slider_tick = 2000; private const double time_slider_end = 3000; private static readonly Vector2 slider_start_position = new Vector2(256 - slider_path_length / 2, 192); private static readonly Vector2 slider_end_position = new Vector2(256 + slider_path_length / 2, 192); + private static readonly Vector2 offset_inside_follow = new Vector2(35, 0); + private static readonly Vector2 offset_outside_follow = offset_inside_follow * 2; private ScoreAccessibleReplayPlayer currentPlayer = null!; @@ -43,8 +44,8 @@ namespace osu.Game.Rulesets.Osu.Tests performTest(new List { new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_start - 200, slider_start_position + new Vector2(32, 0), OsuAction.LeftButton), - new OsuReplayFrame(time_slider_end - 200, slider_end_position + new Vector2(32, 0), OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 200, slider_start_position + offset_inside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end - 200, slider_end_position + offset_inside_follow, OsuAction.LeftButton), }); assertHeadJudgement(HitResult.Miss); @@ -53,14 +54,49 @@ namespace osu.Game.Rulesets.Osu.Tests assertSliderJudgement(HitResult.IgnoreHit); } + [Test] + public void TestHitEarlyAndReleaseInFollowRegion() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 200, slider_start_position + offset_inside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 100, slider_start_position + offset_inside_follow), + new OsuReplayFrame(time_slider_end - 100, slider_end_position + offset_inside_follow, OsuAction.LeftButton), + }); + + assertHeadJudgement(HitResult.Miss); + assertTickJudgement(HitResult.LargeTickMiss); + assertTailJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreMiss); + } + + [Test] + public void TestHitEarlyAndRepressInFollowRegion() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 200, slider_start_position + offset_inside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 100, slider_start_position + offset_inside_follow), + new OsuReplayFrame(time_slider_start - 50, slider_start_position + offset_inside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end - 50, slider_end_position + offset_inside_follow, OsuAction.LeftButton), + }); + + assertHeadJudgement(HitResult.Miss); + assertTickJudgement(HitResult.LargeTickMiss); + assertTailJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreMiss); + } + [Test] public void TestHitEarlyMoveOutsideFollowRegion() { performTest(new List { new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_start - 200, slider_start_position + new Vector2(96, 0), OsuAction.LeftButton), - new OsuReplayFrame(time_slider_end - 200, slider_end_position + new Vector2(96, 0), OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 200, slider_start_position + offset_outside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end - 200, slider_end_position + offset_outside_follow, OsuAction.LeftButton), }); assertHeadJudgement(HitResult.Miss); From 27e55def641cbf0eb233b4b094c2843b5648344b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 13 Dec 2023 20:27:14 +0900 Subject: [PATCH 0477/1118] Make animation start at the slider's start time --- osu.Game.Rulesets.Osu/Skinning/FollowCircle.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/FollowCircle.cs b/osu.Game.Rulesets.Osu/Skinning/FollowCircle.cs index 355d3f9a2f..4fadb09948 100644 --- a/osu.Game.Rulesets.Osu/Skinning/FollowCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/FollowCircle.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -26,13 +27,17 @@ namespace osu.Game.Rulesets.Osu.Skinning ((DrawableSlider?)ParentObject)?.Tracking.BindValueChanged(tracking => { Debug.Assert(ParentObject != null); + if (ParentObject.Judged) return; - if (tracking.NewValue) - OnSliderPress(); - else - OnSliderRelease(); + using (BeginAbsoluteSequence(Math.Max(Time.Current, ParentObject.HitObject?.StartTime ?? 0))) + { + if (tracking.NewValue) + OnSliderPress(); + else + OnSliderRelease(); + } }, true); } From a6bf4cdb986b0d598cb74a863d2bab4de5998354 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 13 Dec 2023 13:22:12 +0100 Subject: [PATCH 0478/1118] Remove dead clamping code `EffectiveX` is already defined as clamped to `[0, CatchPlayfield.WIDTH]`. --- .../Beatmaps/CatchBeatmapProcessor.cs | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs index d5d4d3b694..02d4cdbb94 100644 --- a/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs +++ b/osu.Game.Rulesets.Catch/Beatmaps/CatchBeatmapProcessor.cs @@ -44,7 +44,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps base.PostProcess(); ApplyPositionOffsets(Beatmap); - ApplyPositionClamping(Beatmap); int index = 0; @@ -115,17 +114,6 @@ namespace osu.Game.Rulesets.Catch.Beatmaps initialiseHyperDash(beatmap); } - public void ApplyPositionClamping(IBeatmap beatmap) - { - foreach (var obj in beatmap.HitObjects.OfType()) - { - if (obj.EffectiveX < 0) - obj.XOffset += Math.Abs(obj.EffectiveX); - else if (obj.EffectiveX > CatchPlayfield.WIDTH) - obj.XOffset += CatchPlayfield.WIDTH - obj.EffectiveX; - } - } - private static void applyHardRockOffset(CatchHitObject hitObject, ref float? lastPosition, ref double lastStartTime, LegacyRandom rng) { float offsetPosition = hitObject.OriginalX; From 31c9489cb92e6d365813f7faef4139cb9ae2ddcc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 13 Dec 2023 23:37:34 +0300 Subject: [PATCH 0479/1118] Fix failing tests --- .../Visual/SongSelect/TestSceneFilterControl.cs | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs index 00a0d4a849..94c6130f15 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs @@ -192,7 +192,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("select collection", () => { - InputManager.MoveMouseTo(getCollectionDropdownItems().ElementAt(1)); + InputManager.MoveMouseTo(getCollectionDropdownItemAt(1)); InputManager.Click(MouseButton.Left); }); @@ -206,7 +206,8 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("click manage collections filter", () => { - InputManager.MoveMouseTo(getCollectionDropdownItems().Last()); + int lastItemIndex = control.ChildrenOfType().Single().Items.Count() - 1; + InputManager.MoveMouseTo(getCollectionDropdownItemAt(lastItemIndex)); InputManager.Click(MouseButton.Left); }); @@ -232,10 +233,10 @@ namespace osu.Game.Tests.Visual.SongSelect private void assertCollectionDropdownContains(string collectionName, bool shouldContain = true) => AddUntilStep($"collection dropdown {(shouldContain ? "contains" : "does not contain")} '{collectionName}'", // A bit of a roundabout way of going about this, see: https://github.com/ppy/osu-framework/issues/3871 + https://github.com/ppy/osu-framework/issues/3872 - () => shouldContain == (getCollectionDropdownItems().Any(i => i.ChildrenOfType().OfType().First().Text == collectionName))); + () => shouldContain == control.ChildrenOfType().Any(i => i.ChildrenOfType().OfType().First().Text == collectionName)); private IconButton getAddOrRemoveButton(int index) - => getCollectionDropdownItems().ElementAt(index).ChildrenOfType().Single(); + => getCollectionDropdownItemAt(index).ChildrenOfType().Single(); private void addExpandHeaderStep() => AddStep("expand header", () => { @@ -249,7 +250,11 @@ namespace osu.Game.Tests.Visual.SongSelect InputManager.Click(MouseButton.Left); }); - private IEnumerable.DropdownMenu.DrawableDropdownMenuItem> getCollectionDropdownItems() - => control.ChildrenOfType().Single().ChildrenOfType.DropdownMenu.DrawableDropdownMenuItem>(); + private Menu.DrawableMenuItem getCollectionDropdownItemAt(int index) + { + // todo: we should be able to use Items, but apparently that's not guaranteed to be ordered... see: https://github.com/ppy/osu-framework/pull/6079 + CollectionFilterMenuItem item = control.ChildrenOfType().Single().ItemSource.ElementAt(index); + return control.ChildrenOfType().Single(i => i.Item.Text.Value == item.CollectionName); + } } } From 38e7c035008ba1e61ee364d53e6eb3bd0b1bc588 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Dec 2023 12:00:01 +0900 Subject: [PATCH 0480/1118] Update tests to not miss the head --- .../TestSceneSliderEarlyHitJudgement.cs | 43 ++++++++++--------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs index fb7d00b28c..9caee86a32 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs @@ -43,12 +43,12 @@ namespace osu.Game.Rulesets.Osu.Tests { performTest(new List { - new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_start - 200, slider_start_position + offset_inside_follow, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_end - 200, slider_end_position + offset_inside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 100, slider_start_position + offset_inside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end - 100, slider_end_position + offset_inside_follow, OsuAction.LeftButton), }); - assertHeadJudgement(HitResult.Miss); + assertHeadJudgement(HitResult.Meh); assertTickJudgement(HitResult.LargeTickHit); assertTailJudgement(HitResult.LargeTickHit); assertSliderJudgement(HitResult.IgnoreHit); @@ -59,16 +59,16 @@ namespace osu.Game.Rulesets.Osu.Tests { performTest(new List { - new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_start - 200, slider_start_position + offset_inside_follow, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_start - 100, slider_start_position + offset_inside_follow), - new OsuReplayFrame(time_slider_end - 100, slider_end_position + offset_inside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 100, slider_start_position + offset_inside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 50, slider_start_position + offset_inside_follow), + new OsuReplayFrame(time_slider_end - 50, slider_end_position + offset_inside_follow, OsuAction.LeftButton), }); - assertHeadJudgement(HitResult.Miss); + assertHeadJudgement(HitResult.Meh); assertTickJudgement(HitResult.LargeTickMiss); assertTailJudgement(HitResult.IgnoreMiss); - assertSliderJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreHit); } [Test] @@ -76,17 +76,17 @@ namespace osu.Game.Rulesets.Osu.Tests { performTest(new List { - new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_start - 200, slider_start_position + offset_inside_follow, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_start - 100, slider_start_position + offset_inside_follow), + new OsuReplayFrame(time_slider_start - 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 100, slider_start_position + offset_inside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 75, slider_start_position + offset_inside_follow), new OsuReplayFrame(time_slider_start - 50, slider_start_position + offset_inside_follow, OsuAction.LeftButton), new OsuReplayFrame(time_slider_end - 50, slider_end_position + offset_inside_follow, OsuAction.LeftButton), }); - assertHeadJudgement(HitResult.Miss); + assertHeadJudgement(HitResult.Meh); assertTickJudgement(HitResult.LargeTickMiss); assertTailJudgement(HitResult.IgnoreMiss); - assertSliderJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreHit); } [Test] @@ -94,15 +94,15 @@ namespace osu.Game.Rulesets.Osu.Tests { performTest(new List { - new OsuReplayFrame(time_slider_start - 300, slider_start_position, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_start - 200, slider_start_position + offset_outside_follow, OsuAction.LeftButton), - new OsuReplayFrame(time_slider_end - 200, slider_end_position + offset_outside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_start - 100, slider_start_position + offset_outside_follow, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end - 100, slider_end_position + offset_outside_follow, OsuAction.LeftButton), }); - assertHeadJudgement(HitResult.Miss); + assertHeadJudgement(HitResult.Meh); assertTickJudgement(HitResult.LargeTickMiss); assertTailJudgement(HitResult.IgnoreMiss); - assertSliderJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreHit); } private void assertHeadJudgement(HitResult result) @@ -179,7 +179,8 @@ namespace osu.Game.Rulesets.Osu.Tests Difficulty = new BeatmapDifficulty { SliderMultiplier = 1, - SliderTickRate = 3 + SliderTickRate = 3, + OverallDifficulty = 0 }, Ruleset = new OsuRuleset().RulesetInfo, } From 70a546b23cb3bb38f785f33dc3e18a1cbe3c129b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 09:23:03 +0100 Subject: [PATCH 0481/1118] Add setting for adjusting whether text search is active by default --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 6ef55ab919..ea526c6d54 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -49,6 +49,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.RandomSelectAlgorithm, RandomSelectAlgorithm.RandomPermutation); SetDefault(OsuSetting.ModSelectHotkeyStyle, ModSelectHotkeyStyle.Sequential); + SetDefault(OsuSetting.ModSelectTextSearchStartsActive, true); SetDefault(OsuSetting.ChatDisplayHeight, ChatOverlay.DEFAULT_HEIGHT, 0.2f, 1f); @@ -416,5 +417,6 @@ namespace osu.Game.Configuration AutomaticallyDownloadMissingBeatmaps, EditorShowSpeedChanges, TouchDisableGameplayTaps, + ModSelectTextSearchStartsActive, } } From 839a0802476deae907f773e781ab6b317434d332 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 09:30:04 +0100 Subject: [PATCH 0482/1118] Add test coverage for desired mod overlay search box behaviour --- .../TestSceneModSelectOverlay.cs | 32 +++++++++++++++---- 1 file changed, 26 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index fed5f68449..80be4412b3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Localisation; using osu.Framework.Testing; using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Overlays; using osu.Game.Overlays.Mods; @@ -38,6 +39,9 @@ namespace osu.Game.Tests.Visual.UserInterface private TestModSelectOverlay modSelectOverlay = null!; + [Resolved] + private OsuConfigManager configManager { get; set; } = null!; + [BackgroundDependencyLoader] private void load() { @@ -566,17 +570,33 @@ namespace osu.Game.Tests.Visual.UserInterface } [Test] - public void TestSearchFocusChangeViaKey() + public void TestTextSearchActiveByDefault() { + configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, true); createScreen(); - const Key focus_switch_key = Key.Tab; + AddUntilStep("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus); - AddStep("press tab", () => InputManager.Key(focus_switch_key)); - AddAssert("focused", () => modSelectOverlay.SearchTextBox.HasFocus); + AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddAssert("search text box unfocused", () => !modSelectOverlay.SearchTextBox.HasFocus); - AddStep("press tab", () => InputManager.Key(focus_switch_key)); - AddAssert("lost focus", () => !modSelectOverlay.SearchTextBox.HasFocus); + AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddAssert("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus); + } + + [Test] + public void TestTextSearchNotActiveByDefault() + { + configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, false); + createScreen(); + + AddUntilStep("search text box not focused", () => !modSelectOverlay.SearchTextBox.HasFocus); + + AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddAssert("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus); + + AddStep("press tab", () => InputManager.Key(Key.Tab)); + AddAssert("search text box unfocused", () => !modSelectOverlay.SearchTextBox.HasFocus); } [Test] From 0ab6e1879276a75702ef7b0f2ffd435a3521ac28 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 09:49:48 +0100 Subject: [PATCH 0483/1118] Automatically focus search text box on open depending on setting --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index f2b3264a84..ce798ae752 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -115,6 +115,7 @@ namespace osu.Game.Overlays.Mods public IEnumerable AllAvailableMods => AvailableMods.Value.SelectMany(pair => pair.Value); private readonly BindableBool customisationVisible = new BindableBool(); + private Bindable textSearchStartsActive = null!; private ModSettingsArea modSettingsArea = null!; private ColumnScrollContainer columnScroll = null!; @@ -154,7 +155,7 @@ namespace osu.Game.Overlays.Mods } [BackgroundDependencyLoader] - private void load(OsuGameBase game, OsuColour colours, AudioManager audio) + private void load(OsuGameBase game, OsuColour colours, AudioManager audio, OsuConfigManager configManager) { Header.Title = ModSelectOverlayStrings.ModSelectTitle; Header.Description = ModSelectOverlayStrings.ModSelectDescription; @@ -282,6 +283,8 @@ namespace osu.Game.Overlays.Mods } globalAvailableMods.BindTo(game.AvailableMods); + + textSearchStartsActive = configManager.GetBindable(OsuSetting.ModSelectTextSearchStartsActive); } public override void Hide() @@ -617,6 +620,9 @@ namespace osu.Game.Overlays.Mods nonFilteredColumnCount += 1; } + + if (textSearchStartsActive.Value) + SearchTextBox.TakeFocus(); } protected override void PopOut() From b3a7c7a7c9bba70c1372a5f39875e8e3402c80fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 10:04:37 +0100 Subject: [PATCH 0484/1118] Add control to adjust mod select search text box behaviour --- osu.Game/Localisation/UserInterfaceStrings.cs | 5 +++++ .../Settings/Sections/UserInterface/SongSelectSettings.cs | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/osu.Game/Localisation/UserInterfaceStrings.cs b/osu.Game/Localisation/UserInterfaceStrings.cs index 612668171c..68c5c3ccbc 100644 --- a/osu.Game/Localisation/UserInterfaceStrings.cs +++ b/osu.Game/Localisation/UserInterfaceStrings.cs @@ -104,6 +104,11 @@ namespace osu.Game.Localisation /// public static LocalisableString ModSelectHotkeyStyle => new TranslatableString(getKey(@"mod_select_hotkey_style"), @"Mod select hotkey style"); + /// + /// "Automatically focus search text box in mod select" + /// + public static LocalisableString ModSelectTextSearchStartsActive => new TranslatableString(getKey(@"mod_select_text_search_starts_active"), @"Automatically focus search text box in mod select"); + /// /// "no limit" /// diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs index addf5ce163..49bd17dfde 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/SongSelectSettings.cs @@ -42,6 +42,12 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface ClassicDefault = ModSelectHotkeyStyle.Classic }, new SettingsCheckbox + { + LabelText = UserInterfaceStrings.ModSelectTextSearchStartsActive, + Current = config.GetBindable(OsuSetting.ModSelectTextSearchStartsActive), + ClassicDefault = false + }, + new SettingsCheckbox { LabelText = GameplaySettingsStrings.BackgroundBlur, Current = config.GetBindable(OsuSetting.SongSelectBackgroundBlur), From d77972a39b18f17e43f347fe3bf704e1fde8936b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 14 Dec 2023 18:26:13 +0900 Subject: [PATCH 0485/1118] Show search bar by default in language and collection dropdowns --- osu.Game/Collections/CollectionDropdown.cs | 2 ++ osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/osu.Game/Collections/CollectionDropdown.cs b/osu.Game/Collections/CollectionDropdown.cs index e435992381..db7b27d30c 100644 --- a/osu.Game/Collections/CollectionDropdown.cs +++ b/osu.Game/Collections/CollectionDropdown.cs @@ -48,6 +48,8 @@ namespace osu.Game.Collections ItemSource = filters; Current.Value = new AllBeatmapsCollectionFilterMenuItem(); + + AlwaysShowSearchBar = true; } protected override void LoadComplete() diff --git a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs index cf7f63211e..2af6e36b7f 100644 --- a/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/General/LanguageSettings.cs @@ -23,6 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections.General { LabelText = GeneralSettingsStrings.LanguageDropdown, Current = game.CurrentLanguage, + AlwaysShowSearchBar = true, }, new SettingsCheckbox { From 8698835db2e1d0163b3cb1af22532d8a7d4b1554 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Thu, 14 Dec 2023 14:00:35 +0200 Subject: [PATCH 0486/1118] fixed bug fixed the bug where it's not updates tooltip when there are no mods --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 2a9e69f141..6309b2b993 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -123,6 +123,8 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; + + BeatmapDifficulty originalDifficulty = null; BeatmapDifficulty adjustedDifficulty = null; IRulesetInfo ruleset = gameRuleset?.Value ?? beatmapInfo.Ruleset; @@ -130,7 +132,7 @@ namespace osu.Game.Screens.Select.Details if (baseDifficulty != null && (mods.Value.Any(m => m is IApplicableToDifficulty) || mods.Value.Any(m => m is IApplicableToRate))) { - BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty); + originalDifficulty = new BeatmapDifficulty(baseDifficulty); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(originalDifficulty); @@ -149,6 +151,13 @@ namespace osu.Game.Screens.Select.Details } } + // update tooltip anyway + else if (baseDifficulty != null) + { + originalDifficulty = new BeatmapDifficulty(baseDifficulty); + rateAdjustTooltip.UpdateAttributes(originalDifficulty, originalDifficulty); + } + switch (ruleset.OnlineID) { case 3: From c2373bb37b6574bdae17a39a064098bf83737fe7 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Thu, 14 Dec 2023 14:31:19 +0200 Subject: [PATCH 0487/1118] change the order of attributes + simplifying the bug fix --- osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs | 2 +- osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs | 2 +- osu.Game/Screens/Select/Details/AdvancedStats.cs | 13 ++----------- 3 files changed, 4 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs index ed12d917ad..7b991dbbfb 100644 --- a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -89,8 +89,8 @@ namespace osu.Game.Overlays.Mods if (originalDifficulty == null || adjustedDifficulty == null) return; - attemptAdd("AR", bd => bd.ApproachRate); attemptAdd("OD", bd => bd.OverallDifficulty); + attemptAdd("AR", bd => bd.ApproachRate); attemptAdd("CS", bd => bd.CircleSize); attemptAdd("HP", bd => bd.DrainRate); diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index dd3daa2b7a..8b50bfc791 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -95,8 +95,8 @@ namespace osu.Game.Overlays.Mods { circleSizeDisplay = new VerticalAttributeDisplay("CS") { Shear = new Vector2(-shear, 0), }, drainRateDisplay = new VerticalAttributeDisplay("HP") { Shear = new Vector2(-shear, 0), }, - approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = new Vector2(-shear, 0), }, overallDifficultyDisplay = new VerticalAttributeDisplay("OD") { Shear = new Vector2(-shear, 0), }, + approachRateDisplay = new VerticalAttributeDisplay("AR") { Shear = new Vector2(-shear, 0), }, }); } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 6309b2b993..1c0667b9ee 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -124,15 +124,13 @@ namespace osu.Game.Screens.Select.Details { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; - BeatmapDifficulty originalDifficulty = null; BeatmapDifficulty adjustedDifficulty = null; IRulesetInfo ruleset = gameRuleset?.Value ?? beatmapInfo.Ruleset; - if (baseDifficulty != null && - (mods.Value.Any(m => m is IApplicableToDifficulty) || mods.Value.Any(m => m is IApplicableToRate))) + if (baseDifficulty != null) { - originalDifficulty = new BeatmapDifficulty(baseDifficulty); + BeatmapDifficulty originalDifficulty = new BeatmapDifficulty(baseDifficulty); foreach (var mod in mods.Value.OfType()) mod.ApplyToDifficulty(originalDifficulty); @@ -151,13 +149,6 @@ namespace osu.Game.Screens.Select.Details } } - // update tooltip anyway - else if (baseDifficulty != null) - { - originalDifficulty = new BeatmapDifficulty(baseDifficulty); - rateAdjustTooltip.UpdateAttributes(originalDifficulty, originalDifficulty); - } - switch (ruleset.OnlineID) { case 3: From b22a7cf520b7dc0408b4199783ca898b47bb02f4 Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Thu, 14 Dec 2023 14:31:58 +0200 Subject: [PATCH 0488/1118] Update AdvancedStats.cs --- osu.Game/Screens/Select/Details/AdvancedStats.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 1c0667b9ee..f56e84abce 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -123,7 +123,6 @@ namespace osu.Game.Screens.Select.Details private void updateStatistics() { IBeatmapDifficultyInfo baseDifficulty = BeatmapInfo?.Difficulty; - BeatmapDifficulty adjustedDifficulty = null; IRulesetInfo ruleset = gameRuleset?.Value ?? beatmapInfo.Ruleset; From 23c427cd3ee70781fbf226101af8d0156c1536fb Mon Sep 17 00:00:00 2001 From: Givikap120 Date: Thu, 14 Dec 2023 14:38:01 +0200 Subject: [PATCH 0489/1118] Update AdjustedAttributesTooltip.cs --- osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs index 7b991dbbfb..10b84340ae 100644 --- a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -89,10 +89,10 @@ namespace osu.Game.Overlays.Mods if (originalDifficulty == null || adjustedDifficulty == null) return; - attemptAdd("OD", bd => bd.OverallDifficulty); - attemptAdd("AR", bd => bd.ApproachRate); attemptAdd("CS", bd => bd.CircleSize); attemptAdd("HP", bd => bd.DrainRate); + attemptAdd("OD", bd => bd.OverallDifficulty); + attemptAdd("AR", bd => bd.ApproachRate); if (attributesFillFlow.Any()) content.Show(); From 67a5e01c49fe9a36ab6a70f63129156aa1ed1d8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 15:19:29 +0100 Subject: [PATCH 0490/1118] Update assertions in test --- osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs index 60fb6b8c86..325cb9e0cb 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapSetOverlay.cs @@ -220,7 +220,7 @@ namespace osu.Game.Tests.Visual.Online public void TestSelectedModsDontAffectStatistics() { AddStep("show map", () => overlay.ShowBeatmapSet(getBeatmapSet())); - AddAssert("AR displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value == (0, null)); + AddAssert("AR displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value, () => Is.EqualTo((0, 0))); AddStep("set AR10 diff adjust", () => SelectedMods.Value = new[] { new OsuModDifficultyAdjust @@ -228,7 +228,7 @@ namespace osu.Game.Tests.Visual.Online ApproachRate = { Value = 10 } } }); - AddAssert("AR still displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value == (0, null)); + AddAssert("AR still displayed as 0", () => overlay.ChildrenOfType().Single(s => s.Title == BeatmapsetsStrings.ShowStatsAr).Value, () => Is.EqualTo((0, 0))); } [Test] From 9e5b6b97ff52753140331e16aa1b001df493ce89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 16:11:08 +0100 Subject: [PATCH 0491/1118] Fix `AdjustedAttributesTooltip` being broken by design Fixes issue described in the following comment: https://github.com/ppy/osu/pull/25759#issuecomment-1855954637 That is just not how the tooltip system is supposed to be used. To name the individual sins: - Caching and returning a tooltip instance like the classes that used tooltips is incorrect. The lifetime of tooltip instances is managed by the tooltip container. `GetCustomTooltip()` is called by it exclusively. It should return a fresh instance every time. - Not putting actual data in `IHasCustomTooltip.TooltipContent` is wrong. - Having `Tooltip.SetContent()` be a no-op is *grossly and flagrantly* wrong. I'm not even sure which particular combination of the above transgressions caused the issue as it presented itself, but at this time I frankly do not care. --- .../Mods/AdjustedAttributesTooltip.cs | 55 +++++++++++-------- .../Overlays/Mods/BeatmapAttributesDisplay.cs | 12 ++-- .../Screens/Select/Details/AdvancedStats.cs | 10 ++-- 3 files changed, 39 insertions(+), 38 deletions(-) diff --git a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs index 10b84340ae..957ee23e3b 100644 --- a/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs +++ b/osu.Game/Overlays/Mods/AdjustedAttributesTooltip.cs @@ -16,14 +16,13 @@ using osuTK; namespace osu.Game.Overlays.Mods { - public partial class AdjustedAttributesTooltip : VisibilityContainer, ITooltip + public partial class AdjustedAttributesTooltip : VisibilityContainer, ITooltip { private FillFlowContainer attributesFillFlow = null!; private Container content = null!; - private BeatmapDifficulty? originalDifficulty; - private BeatmapDifficulty? adjustedDifficulty; + private Data? data; [Resolved] private OsuColour colours { get; set; } = null!; @@ -73,26 +72,17 @@ namespace osu.Game.Overlays.Mods updateDisplay(); } - public void UpdateAttributes(BeatmapDifficulty original, BeatmapDifficulty adjusted) - { - originalDifficulty = original; - adjustedDifficulty = adjusted; - - if (IsLoaded) - updateDisplay(); - } - private void updateDisplay() { attributesFillFlow.Clear(); - if (originalDifficulty == null || adjustedDifficulty == null) - return; - - attemptAdd("CS", bd => bd.CircleSize); - attemptAdd("HP", bd => bd.DrainRate); - attemptAdd("OD", bd => bd.OverallDifficulty); - attemptAdd("AR", bd => bd.ApproachRate); + if (data != null) + { + attemptAdd("CS", bd => bd.CircleSize); + attemptAdd("HP", bd => bd.DrainRate); + attemptAdd("OD", bd => bd.OverallDifficulty); + attemptAdd("AR", bd => bd.ApproachRate); + } if (attributesFillFlow.Any()) content.Show(); @@ -101,16 +91,21 @@ namespace osu.Game.Overlays.Mods void attemptAdd(string name, Func lookup) { - double a = lookup(originalDifficulty); - double b = lookup(adjustedDifficulty); + double originalValue = lookup(data.OriginalDifficulty); + double adjustedValue = lookup(data.AdjustedDifficulty); - if (!Precision.AlmostEquals(a, b)) - attributesFillFlow.Add(new AttributeDisplay(name, a, b)); + if (!Precision.AlmostEquals(originalValue, adjustedValue)) + attributesFillFlow.Add(new AttributeDisplay(name, originalValue, adjustedValue)); } } - public void SetContent(object content) + public void SetContent(Data? data) { + if (this.data == data) + return; + + this.data = data; + updateDisplay(); } protected override void PopIn() => this.FadeIn(200, Easing.OutQuint); @@ -118,6 +113,18 @@ namespace osu.Game.Overlays.Mods public void Move(Vector2 pos) => Position = pos; + public class Data + { + public BeatmapDifficulty OriginalDifficulty { get; } + public BeatmapDifficulty AdjustedDifficulty { get; } + + public Data(BeatmapDifficulty originalDifficulty, BeatmapDifficulty adjustedDifficulty) + { + OriginalDifficulty = originalDifficulty; + AdjustedDifficulty = adjustedDifficulty; + } + } + private partial class AttributeDisplay : CompositeDrawable { public AttributeDisplay(string name, double original, double adjusted) diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 8b50bfc791..d50b2c33d7 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -27,7 +27,7 @@ namespace osu.Game.Overlays.Mods /// On the mod select overlay, this provides a local updating view of BPM, star rating and other /// difficulty attributes so the user can have a better insight into what mods are changing. /// - public partial class BeatmapAttributesDisplay : ModFooterInformationDisplay, IHasCustomTooltip + public partial class BeatmapAttributesDisplay : ModFooterInformationDisplay, IHasCustomTooltip { private StarRatingDisplay starRatingDisplay = null!; private BPMDisplay bpmDisplay = null!; @@ -57,11 +57,9 @@ namespace osu.Game.Overlays.Mods private CancellationTokenSource? cancellationSource; private IBindable starDifficulty = null!; - private AdjustedAttributesTooltip rateAdjustTooltip = null!; + public ITooltip GetCustomTooltip() => new AdjustedAttributesTooltip(); - public ITooltip GetCustomTooltip() => rateAdjustTooltip; - - public object TooltipContent => this; + public AdjustedAttributesTooltip.Data? TooltipContent { get; private set; } private const float transition_duration = 250; @@ -70,8 +68,6 @@ namespace osu.Game.Overlays.Mods { const float shear = ShearedOverlayContainer.SHEAR; - rateAdjustTooltip = new AdjustedAttributesTooltip(); - LeftContent.AddRange(new Drawable[] { starRatingDisplay = new StarRatingDisplay(default, animated: true) @@ -182,7 +178,7 @@ namespace osu.Game.Overlays.Mods Ruleset ruleset = gameRuleset.Value.CreateInstance(); BeatmapDifficulty adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); - rateAdjustTooltip.UpdateAttributes(originalDifficulty, adjustedDifficulty); + TooltipContent = new AdjustedAttributesTooltip.Data(originalDifficulty, adjustedDifficulty); approachRateDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.ApproachRate, adjustedDifficulty.ApproachRate); overallDifficultyDisplay.AdjustType.Value = VerticalAttributeDisplay.CalculateEffect(originalDifficulty.OverallDifficulty, adjustedDifficulty.OverallDifficulty); diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index f56e84abce..ee805c2d12 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -30,7 +30,7 @@ using osu.Game.Overlays.Mods; namespace osu.Game.Screens.Select.Details { - public partial class AdvancedStats : Container, IHasCustomTooltip + public partial class AdvancedStats : Container, IHasCustomTooltip { [Resolved] private BeatmapDifficultyCache difficultyCache { get; set; } @@ -46,9 +46,8 @@ namespace osu.Game.Screens.Select.Details protected readonly StatisticRow FirstValue, HpDrain, Accuracy, ApproachRate; private readonly StatisticRow starDifficulty; - private AdjustedAttributesTooltip rateAdjustTooltip; - public ITooltip GetCustomTooltip() => rateAdjustTooltip; - public object TooltipContent => this; + public ITooltip GetCustomTooltip() => new AdjustedAttributesTooltip(); + public AdjustedAttributesTooltip.Data TooltipContent { get; private set; } private IBeatmapInfo beatmapInfo; @@ -86,7 +85,6 @@ namespace osu.Game.Screens.Select.Details private void load(OsuColour colours) { starDifficulty.AccentColour = colours.Yellow; - rateAdjustTooltip = new AdjustedAttributesTooltip(); } protected override void LoadComplete() @@ -144,7 +142,7 @@ namespace osu.Game.Screens.Select.Details adjustedDifficulty = ruleset.CreateInstance().GetRateAdjustedDisplayDifficulty(originalDifficulty, rate); - rateAdjustTooltip.UpdateAttributes(originalDifficulty, adjustedDifficulty); + TooltipContent = new AdjustedAttributesTooltip.Data(originalDifficulty, adjustedDifficulty); } } From 555559c5c12010122798086b6419b51442b86ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 20:41:07 +0100 Subject: [PATCH 0492/1118] Add testing for `GetRateAdjustedDisplayDifficulty()` implementations --- .../CatchRateAdjustedDisplayDifficultyTest.cs | 52 +++++++++++++++ .../OsuRateAdjustedDisplayDifficultyTest.cs | 65 +++++++++++++++++++ .../TaikoRateAdjustedDisplayDifficultyTest.cs | 52 +++++++++++++++ 3 files changed, 169 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/CatchRateAdjustedDisplayDifficultyTest.cs create mode 100644 osu.Game.Rulesets.Osu.Tests/OsuRateAdjustedDisplayDifficultyTest.cs create mode 100644 osu.Game.Rulesets.Taiko.Tests/TaikoRateAdjustedDisplayDifficultyTest.cs diff --git a/osu.Game.Rulesets.Catch.Tests/CatchRateAdjustedDisplayDifficultyTest.cs b/osu.Game.Rulesets.Catch.Tests/CatchRateAdjustedDisplayDifficultyTest.cs new file mode 100644 index 0000000000..f77ec64df3 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/CatchRateAdjustedDisplayDifficultyTest.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Catch.Tests +{ + [TestFixture] + public class CatchRateAdjustedDisplayDifficultyTest + { + private static IEnumerable difficultyValuesToTest() + { + for (float i = 0; i <= 10; i += 0.5f) + yield return i; + } + + [TestCaseSource(nameof(difficultyValuesToTest))] + public void TestApproachRateIsUnchangedWithRateEqualToOne(float originalApproachRate) + { + var ruleset = new CatchRuleset(); + var difficulty = new BeatmapDifficulty { ApproachRate = originalApproachRate }; + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1); + + Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(originalApproachRate)); + } + + [Test] + public void TestRateBelowOne() + { + var ruleset = new CatchRuleset(); + var difficulty = new BeatmapDifficulty(); + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 0.75); + + Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(1.67).Within(0.01)); + } + + [Test] + public void TestRateAboveOne() + { + var ruleset = new CatchRuleset(); + var difficulty = new BeatmapDifficulty(); + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1.5); + + Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(7.67).Within(0.01)); + } + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/OsuRateAdjustedDisplayDifficultyTest.cs b/osu.Game.Rulesets.Osu.Tests/OsuRateAdjustedDisplayDifficultyTest.cs new file mode 100644 index 0000000000..aa903205c8 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/OsuRateAdjustedDisplayDifficultyTest.cs @@ -0,0 +1,65 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Osu.Tests +{ + [TestFixture] + public class OsuRateAdjustedDisplayDifficultyTest + { + private static IEnumerable difficultyValuesToTest() + { + for (float i = 0; i <= 10; i += 0.5f) + yield return i; + } + + [TestCaseSource(nameof(difficultyValuesToTest))] + public void TestApproachRateIsUnchangedWithRateEqualToOne(float originalApproachRate) + { + var ruleset = new OsuRuleset(); + var difficulty = new BeatmapDifficulty { ApproachRate = originalApproachRate }; + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1); + + Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(originalApproachRate)); + } + + [TestCaseSource(nameof(difficultyValuesToTest))] + public void TestOverallDifficultyIsUnchangedWithRateEqualToOne(float originalOverallDifficulty) + { + var ruleset = new OsuRuleset(); + var difficulty = new BeatmapDifficulty { OverallDifficulty = originalOverallDifficulty }; + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1); + + Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(originalOverallDifficulty)); + } + + [Test] + public void TestRateBelowOne() + { + var ruleset = new OsuRuleset(); + var difficulty = new BeatmapDifficulty(); + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 0.75); + + Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(1.67).Within(0.01)); + Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(2.22).Within(0.01)); + } + + [Test] + public void TestRateAboveOne() + { + var ruleset = new OsuRuleset(); + var difficulty = new BeatmapDifficulty(); + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1.5); + + Assert.That(adjustedDifficulty.ApproachRate, Is.EqualTo(7.67).Within(0.01)); + Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(7.77).Within(0.01)); + } + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoRateAdjustedDisplayDifficultyTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoRateAdjustedDisplayDifficultyTest.cs new file mode 100644 index 0000000000..4ab3f502ad --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoRateAdjustedDisplayDifficultyTest.cs @@ -0,0 +1,52 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [TestFixture] + public class TaikoRateAdjustedDisplayDifficultyTest + { + private static IEnumerable difficultyValuesToTest() + { + for (float i = 0; i <= 10; i += 0.5f) + yield return i; + } + + [TestCaseSource(nameof(difficultyValuesToTest))] + public void TestOverallDifficultyIsUnchangedWithRateEqualToOne(float originalOverallDifficulty) + { + var ruleset = new TaikoRuleset(); + var difficulty = new BeatmapDifficulty { OverallDifficulty = originalOverallDifficulty }; + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1); + + Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(originalOverallDifficulty)); + } + + [Test] + public void TestRateBelowOne() + { + var ruleset = new TaikoRuleset(); + var difficulty = new BeatmapDifficulty(); + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 0.75); + + Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(1.11).Within(0.01)); + } + + [Test] + public void TestRateAboveOne() + { + var ruleset = new TaikoRuleset(); + var difficulty = new BeatmapDifficulty(); + + var adjustedDifficulty = ruleset.GetRateAdjustedDisplayDifficulty(difficulty, 1.5); + + Assert.That(adjustedDifficulty.OverallDifficulty, Is.EqualTo(8.89).Within(0.01)); + } + } +} From 24e31f7d9199ea89c422447c228579d7779d346a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 19:00:00 +0100 Subject: [PATCH 0493/1118] Implement inverse of `IBeatmapDifficultyInfo.DifficultyRange()` --- osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index e7a3d87d0a..07b966ca44 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -92,5 +92,21 @@ namespace osu.Game.Beatmaps /// Value to which the difficulty value maps in the specified range. static double DifficultyRange(double difficulty, (double od0, double od5, double od10) range) => DifficultyRange(difficulty, range.od0, range.od5, range.od10); + + /// + /// Inverse function to . + /// Maps a value returned by the function above back to the difficulty that produced it. + /// + /// The difficulty-dependent value to be unmapped. + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// Value to which the difficulty value maps in the specified range. + static double InverseDifficultyRange(double difficultyValue, double min, double mid, double max) + { + return difficultyValue >= mid + ? (difficultyValue - mid) / (max - mid) * 5 + 5 + : (difficultyValue - mid) / (mid - min) * 5 + 5; + } } } From fd1c72bf747cefdc9152008610550d28b24631d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 19:16:08 +0100 Subject: [PATCH 0494/1118] Use `IBeatmapDifficultyInfo.(Inverse)DifficultyRange()` instead of local reimplementations Also adds explicit references to places from where the magic constants were lifted. --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 10 ++++------ osu.Game.Rulesets.Osu/OsuRuleset.cs | 18 +++++++----------- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 9 ++++----- 3 files changed, 15 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index bcb28328f0..9b9840f71b 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -15,6 +15,7 @@ using osu.Game.Rulesets.Catch.Beatmaps; using osu.Game.Rulesets.Catch.Difficulty; using osu.Game.Rulesets.Catch.Edit; using osu.Game.Rulesets.Catch.Mods; +using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Replays; using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Catch.Skinning.Argon; @@ -236,17 +237,14 @@ namespace osu.Game.Rulesets.Catch }; } + /// public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate) { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); - double preempt = adjustedDifficulty.ApproachRate < 6 - ? 1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5 - : 1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5; - + double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, 1800, 1200, 450); preempt /= rate; - - adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); + adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, 1800, 1200, 450); return adjustedDifficulty; } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 5355743a50..744f57eae9 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -332,23 +332,19 @@ namespace osu.Game.Rulesets.Osu public override RulesetSetupSection CreateEditorSetupSection() => new OsuSetupSection(); + /// + /// public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate) { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); - double preempt = adjustedDifficulty.ApproachRate < 5 - ? 1200.0 + 600.0 * (5 - adjustedDifficulty.ApproachRate) / 5 - : 1200.0 - 750.0 * (adjustedDifficulty.ApproachRate - 5) / 5; - + double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, 1800, 1200, 450); preempt /= rate; + adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, 1800, 1200, 450); - adjustedDifficulty.ApproachRate = (float)(preempt > 1200 ? (1800 - preempt) / 120 : (1200 - preempt) / 150 + 5); - - double hitwindow = 80.0 - 6 * adjustedDifficulty.OverallDifficulty; - - hitwindow /= rate; - - adjustedDifficulty.OverallDifficulty = (float)(80.0 - hitwindow) / 6; + double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, 80, 50, 20); + greatHitWindow /= rate; + adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, 80, 50, 20); return adjustedDifficulty; } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 94136a11d3..e97f132e7a 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -265,15 +265,14 @@ namespace osu.Game.Rulesets.Taiko }; } + /// public override BeatmapDifficulty GetRateAdjustedDisplayDifficulty(IBeatmapDifficultyInfo difficulty, double rate) { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); - double hitWindow = 35.0 - 15.0 * (adjustedDifficulty.OverallDifficulty - 5) / 5; - - hitWindow /= rate; - - adjustedDifficulty.OverallDifficulty = (float)(5 * (35 - hitWindow) / 15 + 5); + double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, 50, 35, 20); + greatHitWindow /= rate; + adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, 50, 35, 20); return adjustedDifficulty; } From c0e68df20fb7d90bcd398e69a0a2f42dbfa72b02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 19:30:56 +0100 Subject: [PATCH 0495/1118] Fix `InverseDifficultyRange()` not working correctly in both directions --- osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs index 07b966ca44..48f6564084 100644 --- a/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapDifficultyInfo.cs @@ -1,6 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; + namespace osu.Game.Beatmaps { /// @@ -98,15 +100,15 @@ namespace osu.Game.Beatmaps /// Maps a value returned by the function above back to the difficulty that produced it. /// /// The difficulty-dependent value to be unmapped. - /// Minimum of the resulting range which will be achieved by a difficulty value of 0. - /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. - /// Maximum of the resulting range which will be achieved by a difficulty value of 10. + /// Minimum of the resulting range which will be achieved by a difficulty value of 0. + /// Midpoint of the resulting range which will be achieved by a difficulty value of 5. + /// Maximum of the resulting range which will be achieved by a difficulty value of 10. /// Value to which the difficulty value maps in the specified range. - static double InverseDifficultyRange(double difficultyValue, double min, double mid, double max) + static double InverseDifficultyRange(double difficultyValue, double diff0, double diff5, double diff10) { - return difficultyValue >= mid - ? (difficultyValue - mid) / (max - mid) * 5 + 5 - : (difficultyValue - mid) / (mid - min) * 5 + 5; + return Math.Sign(difficultyValue - diff5) == Math.Sign(diff10 - diff5) + ? (difficultyValue - diff5) / (diff10 - diff5) * 5 + 5 + : (difficultyValue - diff5) / (diff5 - diff0) * 5 + 5; } } } From 605269f65feea2657e5d6fae48d0ae99b5469b0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 19:54:23 +0100 Subject: [PATCH 0496/1118] Extract preempt durations to shared constants --- osu.Game.Rulesets.Catch/CatchRuleset.cs | 4 ++-- .../Objects/CatchHitObject.cs | 17 ++++++++++++++++- osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs | 12 +++++++++++- osu.Game.Rulesets.Osu/OsuRuleset.cs | 4 ++-- 4 files changed, 31 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Catch/CatchRuleset.cs b/osu.Game.Rulesets.Catch/CatchRuleset.cs index 9b9840f71b..72d1a161dd 100644 --- a/osu.Game.Rulesets.Catch/CatchRuleset.cs +++ b/osu.Game.Rulesets.Catch/CatchRuleset.cs @@ -242,9 +242,9 @@ namespace osu.Game.Rulesets.Catch { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); - double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, 1800, 1200, 450); + double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, CatchHitObject.PREEMPT_MAX, CatchHitObject.PREEMPT_MID, CatchHitObject.PREEMPT_MIN); preempt /= rate; - adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, 1800, 1200, 450); + adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, CatchHitObject.PREEMPT_MAX, CatchHitObject.PREEMPT_MID, CatchHitObject.PREEMPT_MIN); return adjustedDifficulty; } diff --git a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs index 17ff8afb87..52c42dfddb 100644 --- a/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/CatchHitObject.cs @@ -150,7 +150,7 @@ namespace osu.Game.Rulesets.Catch.Objects { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, 450); + TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_MAX, PREEMPT_MID, PREEMPT_MIN); Scale = LegacyRulesetExtensions.CalculateScaleFromCircleSize(difficulty.CircleSize); } @@ -189,6 +189,21 @@ namespace osu.Game.Rulesets.Catch.Objects // The half of the height of the osu! playfield. public const float DEFAULT_LEGACY_CONVERT_Y = 192; + /// + /// Minimum preempt time at AR=10. + /// + public const double PREEMPT_MIN = 450; + + /// + /// Median preempt time at AR=5. + /// + public const double PREEMPT_MID = 1200; + + /// + /// Maximum preempt time at AR=0. + /// + public const double PREEMPT_MAX = 1800; + /// /// The Y position of the hit object is not used in the normal osu!catch gameplay. /// It is preserved to maximize the backward compatibility with the legacy editor, in which the mappers use the Y position to organize the patterns. diff --git a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs index 21f7b4b22d..74631400ca 100644 --- a/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/OsuHitObject.cs @@ -37,6 +37,16 @@ namespace osu.Game.Rulesets.Osu.Objects /// public const double PREEMPT_MIN = 450; + /// + /// Median preempt time at AR=5. + /// + public const double PREEMPT_MID = 1200; + + /// + /// Maximum preempt time at AR=0. + /// + public const double PREEMPT_MAX = 1800; + public double TimePreempt = 600; public double TimeFadeIn = 400; @@ -148,7 +158,7 @@ namespace osu.Game.Rulesets.Osu.Objects { base.ApplyDefaultsToSelf(controlPointInfo, difficulty); - TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, 1800, 1200, PREEMPT_MIN); + TimePreempt = (float)IBeatmapDifficultyInfo.DifficultyRange(difficulty.ApproachRate, PREEMPT_MAX, PREEMPT_MID, PREEMPT_MIN); // Preempt time can go below 450ms. Normally, this is achieved via the DT mod which uniformly speeds up all animations game wide regardless of AR. // This uniform speedup is hard to match 1:1, however we can at least make AR>10 (via mods) feel good by extending the upper linear function above. diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 744f57eae9..5fe6e76e39 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -338,9 +338,9 @@ namespace osu.Game.Rulesets.Osu { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); - double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, 1800, 1200, 450); + double preempt = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.ApproachRate, OsuHitObject.PREEMPT_MAX, OsuHitObject.PREEMPT_MID, OsuHitObject.PREEMPT_MIN); preempt /= rate; - adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, 1800, 1200, 450); + adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, OsuHitObject.PREEMPT_MAX, OsuHitObject.PREEMPT_MID, OsuHitObject.PREEMPT_MIN); double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, 80, 50, 20); greatHitWindow /= rate; From 0f4d054bfef007601f13d5695749c37e1898ea53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 14 Dec 2023 20:02:02 +0100 Subject: [PATCH 0497/1118] Use `HitWindows` data directly for computing effective OD --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 5 +++-- osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs | 4 ++-- osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs | 4 ++-- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 5 +++-- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 5fe6e76e39..35cbfa3790 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -342,9 +342,10 @@ namespace osu.Game.Rulesets.Osu preempt /= rate; adjustedDifficulty.ApproachRate = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(preempt, OsuHitObject.PREEMPT_MAX, OsuHitObject.PREEMPT_MID, OsuHitObject.PREEMPT_MIN); - double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, 80, 50, 20); + var greatHitWindowRange = OsuHitWindows.OSU_RANGES.Single(range => range.Result == HitResult.Great); + double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max); greatHitWindow /= rate; - adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, 80, 50, 20); + adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max); return adjustedDifficulty; } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs index 6f55e1790f..fd86e0eeda 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Osu.Scoring /// public const double MISS_WINDOW = 400; - private static readonly DifficultyRange[] osu_ranges = + internal static readonly DifficultyRange[] OSU_RANGES = { new DifficultyRange(HitResult.Great, 80, 50, 20), new DifficultyRange(HitResult.Ok, 140, 100, 60), @@ -34,6 +34,6 @@ namespace osu.Game.Rulesets.Osu.Scoring return false; } - protected override DifficultyRange[] GetRanges() => osu_ranges; + protected override DifficultyRange[] GetRanges() => OSU_RANGES; } } diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs index cf806c0c97..b44ef8ee93 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHitWindows.cs @@ -7,7 +7,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring { public class TaikoHitWindows : HitWindows { - private static readonly DifficultyRange[] taiko_ranges = + internal static readonly DifficultyRange[] TAIKO_RANGES = { new DifficultyRange(HitResult.Great, 50, 35, 20), new DifficultyRange(HitResult.Ok, 120, 80, 50), @@ -27,6 +27,6 @@ namespace osu.Game.Rulesets.Taiko.Scoring return false; } - protected override DifficultyRange[] GetRanges() => taiko_ranges; + protected override DifficultyRange[] GetRanges() => TAIKO_RANGES; } } diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index e97f132e7a..9d34a34fce 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -270,9 +270,10 @@ namespace osu.Game.Rulesets.Taiko { BeatmapDifficulty adjustedDifficulty = new BeatmapDifficulty(difficulty); - double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, 50, 35, 20); + var greatHitWindowRange = TaikoHitWindows.TAIKO_RANGES.Single(range => range.Result == HitResult.Great); + double greatHitWindow = IBeatmapDifficultyInfo.DifficultyRange(adjustedDifficulty.OverallDifficulty, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max); greatHitWindow /= rate; - adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, 50, 35, 20); + adjustedDifficulty.OverallDifficulty = (float)IBeatmapDifficultyInfo.InverseDifficultyRange(greatHitWindow, greatHitWindowRange.Min, greatHitWindowRange.Average, greatHitWindowRange.Max); return adjustedDifficulty; } From fbef40bb1f083dad5146e8d32f8ca879b06f3f29 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Dec 2023 11:01:13 +0900 Subject: [PATCH 0498/1118] Reduce some allocations in SongSelect in the mania ruleset --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index cda95a9d29..3c64df656d 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -245,6 +245,9 @@ namespace osu.Game.Screens.Select.Carousel private void updateKeyCount() { + if (Item?.State.Value == CarouselItemState.Collapsed) + return; + if (ruleset.Value.OnlineID == 3) { // Account for mania differences locally for now. From 27296c59defef2897879ccfe2cee52c9e203e747 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 11:26:10 +0900 Subject: [PATCH 0499/1118] Show back button when spectating Avoids getting stuck at some screens. It's a bit ugly having the back button visible like this, but is the best approach we have for now. --- osu.Game/Screens/Play/SpectatorPlayer.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index d1404ac184..2faead0ee1 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -25,6 +25,8 @@ namespace osu.Game.Screens.Play private readonly Score score; + public override bool AllowBackButton => true; + protected override bool CheckModsAllowFailure() { if (!allowFail) From 599fdb0128d88cbc44b8147cd5058b83528f2287 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 14 Dec 2023 11:51:50 +0900 Subject: [PATCH 0500/1118] Add lenience for late-hit of slider heads --- .../TestSceneSliderLateHitJudgement.cs | 419 ++++++++++++++++++ .../Objects/Drawables/DrawableSlider.cs | 2 + .../Objects/Drawables/DrawableSliderBall.cs | 57 +-- .../Objects/Drawables/DrawableSliderHead.cs | 48 ++ .../Objects/Drawables/DrawableSliderRepeat.cs | 26 +- .../Objects/Drawables/DrawableSliderTail.cs | 26 +- .../Objects/Drawables/DrawableSliderTick.cs | 28 +- 7 files changed, 565 insertions(+), 41 deletions(-) create mode 100644 osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs new file mode 100644 index 0000000000..4f32a6fe9f --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -0,0 +1,419 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Screens; +using osu.Game.Beatmaps; +using osu.Game.Replays; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; +using osu.Game.Screens.Play; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests +{ + public partial class TestSceneSliderLateHitJudgement : RateAdjustedBeatmapTestScene + { + // Note: In the following tests, the terminology "in range of the follow circle" is used as meaning + // the equivalent of "in range of the follow circle as if it were in its expanded state". + + private const double time_slider_start = 1000; + private const double time_slider_end = 1500; + + private static readonly Vector2 slider_start_position = new Vector2(256 - slider_path_length / 2, 192); + private static readonly Vector2 slider_end_position = new Vector2(256 + slider_path_length / 2, 192); + + private ScoreAccessibleReplayPlayer currentPlayer = null!; + + private const float slider_path_length = 200; + + private readonly List judgementResults = new List(); + + /// + /// If the head circle is hit and the mouse is in range of the follow circle, + /// then tracking should be enabled. + /// + [Test] + public void TestHitLateInRangeTracks() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 100, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 100, slider_end_position, OsuAction.LeftButton), + }); + + assertHeadJudgement(HitResult.Ok); + assertTailJudgement(HitResult.LargeTickHit); + assertSliderJudgement(HitResult.IgnoreHit); + } + + /// + /// If the head circle is hit and the mouse is NOT in range of the follow circle, + /// then tracking should NOT be enabled. + /// + [Test] + public void TestHitLateOutOfRangeDoesNotTrack() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 100, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 100, slider_end_position, OsuAction.LeftButton), + }, s => + { + s.SliderVelocityMultiplier = 2; + }); + + assertHeadJudgement(HitResult.Ok); + assertTailJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreHit); + } + + /// + /// If the head circle is hit late and the mouse is in range of the follow circle, + /// then all ticks that the follow circle has passed through should be hit. + /// + [Test] + public void TestHitLateInRangeHitsTicks() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 150, slider_end_position, OsuAction.LeftButton), + }, s => + { + s.TickDistanceMultiplier = 0.2f; + }); + + assertHeadJudgement(HitResult.Meh); + assertTickJudgement(0, HitResult.LargeTickHit); + assertTickJudgement(1, HitResult.LargeTickHit); + assertTickJudgement(2, HitResult.LargeTickHit); + assertTickJudgement(3, HitResult.LargeTickHit); + assertTailJudgement(HitResult.LargeTickHit); + assertSliderJudgement(HitResult.IgnoreHit); + } + + /// + /// If the head circle is hit late and the mouse is NOT in range of the follow circle, + /// then all ticks that the follow circle has passed through should NOT be hit. + /// + [Test] + public void TestHitLateOutOfRangeDoesNotHitTicks() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 150, slider_end_position, OsuAction.LeftButton), + }, s => + { + s.SliderVelocityMultiplier = 2; + s.TickDistanceMultiplier = 0.2f; + }); + + assertHeadJudgement(HitResult.Meh); + assertTickJudgement(0, HitResult.LargeTickMiss); + assertTickJudgement(1, HitResult.LargeTickMiss); + assertTailJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreHit); + } + + /// + /// If the head circle is pressed after it's missed and the mouse is in range of the follow circle, + /// then tracking should NOT be enabled. + /// + [Test] + public void TestMissHeadInRangeDoesNotTrack() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 151, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 151, slider_end_position, OsuAction.LeftButton), + }, s => + { + s.TickDistanceMultiplier = 0.2f; + }); + + assertHeadJudgement(HitResult.Miss); + assertTickJudgement(0, HitResult.LargeTickMiss); + assertTickJudgement(1, HitResult.LargeTickMiss); + assertTickJudgement(2, HitResult.LargeTickMiss); + assertTickJudgement(3, HitResult.LargeTickMiss); + assertTailJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreMiss); + } + + /// + /// If the head circle is hit late but after the completion of the slider and the mouse is in range of the follow circle, + /// then all nested objects (ticks/repeats/tail) should be hit. + /// + [Test] + public void TestHitLateShortSliderHitsAll() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton), + }, s => + { + s.Path = new SliderPath(PathType.LINEAR, new[] + { + Vector2.Zero, + new Vector2(20, 0), + }, 20); + + s.TickDistanceMultiplier = 0.01f; + s.RepeatCount = 1; + }); + + assertHeadJudgement(HitResult.Meh); + assertAllTickJudgements(HitResult.LargeTickHit); + assertRepeatJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.LargeTickHit); + assertSliderJudgement(HitResult.IgnoreHit); + } + + /// + /// If the head circle is hit late and the mouse is in range of the follow circle, + /// then all the repeats that the mouse has passed through should be hit. + /// + [Test] + public void TestHitLateInRangeHitsRepeat() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton), + }, s => + { + s.Path = new SliderPath(PathType.LINEAR, new[] + { + Vector2.Zero, + new Vector2(50, 0), + }, 50); + + s.RepeatCount = 1; + }); + + assertHeadJudgement(HitResult.Meh); + assertRepeatJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.LargeTickHit); + assertSliderJudgement(HitResult.IgnoreHit); + } + + /// + /// If the head circle is hit and the mouse is in range of the follow circle, + /// then only the ticks that were in range of the follow circle at the head should be hit. + /// If any hitobject was outside the follow range, ALL hitobjects after that point should be missed. + /// + [Test] + public void TestHitLateInRangeDoesNotHitAfterAnyOutOfRange() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton), + }, s => + { + s.Path = new SliderPath(PathType.PERFECT_CURVE, new[] + { + Vector2.Zero, + new Vector2(70, 70), + new Vector2(20, 0), + }); + + s.TickDistanceMultiplier = 0.03f; + s.SliderVelocityMultiplier = 6f; + }); + + assertHeadJudgement(HitResult.Meh); + + // The first few ticks that are in the follow range of the head should be hit. + assertTickJudgement(0, HitResult.LargeTickHit); // This tick is hidden under the slider head :( + assertTickJudgement(1, HitResult.LargeTickHit); + assertTickJudgement(2, HitResult.LargeTickHit); + + // Every other tick should be missed + assertTickJudgement(3, HitResult.LargeTickMiss); + assertTickJudgement(4, HitResult.LargeTickMiss); + assertTickJudgement(5, HitResult.LargeTickMiss); + assertTickJudgement(6, HitResult.LargeTickMiss); + assertTickJudgement(7, HitResult.LargeTickMiss); + assertTickJudgement(8, HitResult.LargeTickMiss); + assertTickJudgement(9, HitResult.LargeTickMiss); + assertTickJudgement(10, HitResult.LargeTickMiss); + + // In particular, these three are in the follow range of the head, but should not be hit + // because the slider was at some point outside the follow range of the head. + assertTickJudgement(11, HitResult.LargeTickMiss); + assertTickJudgement(12, HitResult.LargeTickMiss); + + // And the tail should be hit because of its leniency. + assertTailJudgement(HitResult.LargeTickHit); + + assertSliderJudgement(HitResult.IgnoreHit); + } + + /// + /// If the head circle is hit and the mouse is in range of the follow circle, + /// then a tick outside the range of the follow circle from the head should not be hit. + /// + [Test] + public void TestHitLateInRangeDoesNotHitOutOfRange() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton), + }, s => + { + s.Path = new SliderPath(PathType.PERFECT_CURVE, new[] + { + Vector2.Zero, + new Vector2(50, 50), + new Vector2(20, 0), + }); + + s.TickDistanceMultiplier = 0.3f; + s.SliderVelocityMultiplier = 3; + }); + + assertHeadJudgement(HitResult.Meh); + assertTickJudgement(0, HitResult.LargeTickMiss); + assertTailJudgement(HitResult.LargeTickHit); + assertSliderJudgement(HitResult.IgnoreHit); + } + + private void assertHeadJudgement(HitResult result) + { + AddAssert( + "check head result", + () => judgementResults.SingleOrDefault(r => r.HitObject is SliderHeadCircle)?.Type, + () => Is.EqualTo(result)); + } + + private void assertTickJudgement(int index, HitResult result) + { + AddAssert( + $"check tick({index}) result", + () => judgementResults.Where(r => r.HitObject is SliderTick).ElementAtOrDefault(index)?.Type, + () => Is.EqualTo(result)); + } + + private void assertAllTickJudgements(HitResult result) + { + AddAssert( + "check all tick results", + () => judgementResults.Where(r => r.HitObject is SliderTick).Select(t => t.Type), + () => Has.All.EqualTo(result)); + } + + private void assertRepeatJudgement(HitResult result) + { + AddAssert( + "check repeat result", + () => judgementResults.SingleOrDefault(r => r.HitObject is SliderRepeat)?.Type, + () => Is.EqualTo(result)); + } + + private void assertTailJudgement(HitResult result) + { + AddAssert( + "check tail result", + () => judgementResults.SingleOrDefault(r => r.HitObject is SliderTailCircle)?.Type, + () => Is.EqualTo(result)); + } + + private void assertSliderJudgement(HitResult result) + { + AddAssert( + "check slider result", + () => judgementResults.SingleOrDefault(r => r.HitObject is Slider)?.Type, + () => Is.EqualTo(result)); + } + + private Vector2 computePositionFromTime(double time) + { + Vector2 dist = slider_end_position - slider_start_position; + double t = (time - time_slider_start) / (time_slider_end - time_slider_start); + return slider_start_position + dist * (float)t; + } + + private void performTest(List frames, Action? adjustSliderFunc = null, bool classic = false) + { + Slider slider = new Slider + { + StartTime = time_slider_start, + Position = new Vector2(256 - slider_path_length / 2, 192), + TickDistanceMultiplier = 3, + ClassicSliderBehaviour = classic, + Path = new SliderPath(PathType.LINEAR, new[] + { + Vector2.Zero, + new Vector2(slider_path_length, 0), + }, slider_path_length), + }; + + adjustSliderFunc?.Invoke(slider); + + AddStep("load player", () => + { + Beatmap.Value = CreateWorkingBeatmap(new Beatmap + { + HitObjects = { slider }, + BeatmapInfo = + { + Difficulty = new BeatmapDifficulty + { + SliderMultiplier = 4, + SliderTickRate = 3 + }, + Ruleset = new OsuRuleset().RulesetInfo, + } + }); + + var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); + + p.OnLoadComplete += _ => + { + p.ScoreProcessor.NewJudgement += result => + { + if (currentPlayer == p) judgementResults.Add(result); + }; + }; + + LoadScreen(currentPlayer = p); + judgementResults.Clear(); + }); + + AddUntilStep("Beatmap at 0", () => Beatmap.Value.Track.CurrentTime == 0); + AddUntilStep("Wait until player is loaded", () => currentPlayer.IsCurrentScreen()); + AddUntilStep("Wait for completion", () => currentPlayer.ScoreProcessor.HasCompleted.Value); + } + + private partial class ScoreAccessibleReplayPlayer : ReplayPlayer + { + public new ScoreProcessor ScoreProcessor => base.ScoreProcessor; + + protected override bool PauseOnFocusLost => false; + + public ScoreAccessibleReplayPlayer(Score score) + : base(score, new PlayerConfiguration + { + AllowPause = false, + ShowResults = false, + }) + { + } + } + } +} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index a053c99a53..1dd6f108f5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public DrawableSliderHead HeadCircle => headContainer.Child; public DrawableSliderTail TailCircle => tailContainer.Child; + public IEnumerable Ticks => tickContainer.Children; + public IEnumerable Repeats => repeatContainer.Children; [Cached] public DrawableSliderBall Ball { get; private set; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index a1724d6fdc..764dd43c30 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -27,7 +27,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public Func GetInitialHitAction; - private Drawable followCircleReceptor; private DrawableSlider drawableSlider; private Drawable ball; @@ -48,13 +47,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Anchor = Anchor.Centre, RelativeSizeAxes = Axes.Both, }, - followCircleReceptor = new CircularContainer - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - RelativeSizeAxes = Axes.Both, - Masking = true - }, ball = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.SliderBall), _ => new DefaultSliderBall()) { Anchor = Anchor.Centre, @@ -86,21 +78,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.ApplyTransformsAt(time, false); } - private bool tracking; - - public bool Tracking - { - get => tracking; - private set - { - if (value == tracking) - return; - - tracking = value; - - followCircleReceptor.Scale = new Vector2(tracking ? FOLLOW_AREA : 1f); - } - } + public bool Tracking { get; private set; } /// /// If the cursor moves out of the ball's radius we still need to be able to receive positional updates to stop tracking. @@ -129,6 +107,30 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// private readonly List lastPressedActions = new List(); + public bool IsMouseInFollowCircleWithState(bool expanded) + { + if (lastScreenSpaceMousePosition is not Vector2 mousePos) + return false; + + float radius = GetFollowCircleRadius(expanded); + + double followProgress = Math.Clamp((Time.Current - drawableSlider.HitObject.StartTime) / drawableSlider.HitObject.Duration, 0, 1); + Vector2 followCirclePosition = drawableSlider.HitObject.CurvePositionAt(followProgress); + Vector2 mousePositionInSlider = drawableSlider.ToLocalSpace(mousePos) - drawableSlider.OriginPosition; + + return (mousePositionInSlider - followCirclePosition).LengthSquared <= radius * radius; + } + + public float GetFollowCircleRadius(bool expanded) + { + float radius = (float)drawableSlider.HitObject.Radius; + + if (expanded) + radius *= FOLLOW_AREA; + + return radius; + } + protected override void Update() { base.Update(); @@ -152,14 +154,19 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables timeToAcceptAnyKeyAfter = Time.Current; } + bool validInFollowArea = IsMouseInFollowCircleWithState(Tracking); + bool validInHeadCircle = drawableSlider.HeadCircle.IsHit + && IsMouseInFollowCircleWithState(true) + && drawableSlider.HeadCircle.Result.TimeAbsolute == Time.Current; + Tracking = // even in an edge case where current time has exceeded the slider's time, we may not have finished judging. // we don't want to potentially update from Tracking=true to Tracking=false at this point. (!drawableSlider.AllJudged || Time.Current <= drawableSlider.HitObject.GetEndTime()) // in valid position range - && lastScreenSpaceMousePosition.HasValue && followCircleReceptor.ReceivePositionalInputAt(lastScreenSpaceMousePosition.Value) && + && (validInFollowArea || validInHeadCircle) // valid action - (actions?.Any(isValidTrackingAction) ?? false); + && (actions?.Any(isValidTrackingAction) ?? false); lastPressedActions.Clear(); if (actions != null) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index ff690417a8..0a104c123b 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -3,10 +3,14 @@ #nullable disable +using System; using System.Diagnostics; +using System.Linq; using osu.Framework.Bindables; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Scoring; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -61,6 +65,50 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables CheckHittable = (d, t, r) => DrawableSlider.CheckHittable?.Invoke(d, t, r) ?? ClickAction.Hit; } + protected override void CheckForResult(bool userTriggered, double timeOffset) + { + base.CheckForResult(userTriggered, timeOffset); + + if (!Judged || !Result.IsHit) + return; + + // If the head is hit and in radius of the would-be-expanded follow circle, + // then hit every object that the follow circle has passed through up until the current time. + if (DrawableSlider.Ball.IsMouseInFollowCircleWithState(true)) + { + foreach (var nested in DrawableSlider.NestedHitObjects.OfType()) + { + if (nested.Judged) + continue; + + if (!check(nested.HitObject)) + break; + + if (nested is DrawableSliderTick tick) + tick.HitForcefully(); + + if (nested is DrawableSliderRepeat repeat) + repeat.HitForcefully(); + + if (nested is DrawableSliderTail tail) + tail.HitForcefully(); + } + } + + bool check(OsuHitObject h) + { + if (h.StartTime > Time.Current) + return false; + + float radius = DrawableSlider.Ball.GetFollowCircleRadius(true); + + double objectProgress = Math.Clamp((h.StartTime - DrawableSlider.HitObject.StartTime) / DrawableSlider.HitObject.Duration, 0, 1); + Vector2 objectPosition = DrawableSlider.HitObject.CurvePositionAt(objectProgress); + + return objectPosition.LengthSquared <= radius * radius; + } + } + protected override HitResult ResultFor(double timeOffset) { Debug.Assert(HitObject != null); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index cdfd96514e..a7979bde27 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -85,17 +85,33 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Position = HitObject.Position - DrawableSlider.Position; } + public void HitForcefully() + { + if (Judged) + return; + + ApplyResult(r => r.Type = r.Judgement.MaxResult); + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { // shared implementation with DrawableSliderTick. if (timeOffset >= 0) { - // Attempt to preserve correct ordering of judgements as best we can by forcing - // an un-judged head to be missed when the user has clearly skipped it. - // // This check is applied to all nested slider objects apart from the head (ticks, repeats, tail). - if (Tracking && !DrawableSlider.HeadCircle.Judged) - DrawableSlider.HeadCircle.MissForcefully(); + if (!DrawableSlider.HeadCircle.Judged) + { + if (Tracking) + { + // Attempt to preserve correct ordering of judgements as best we can by forcing an un-judged head to be missed when the user has clearly skipped it. + DrawableSlider.HeadCircle.MissForcefully(); + } + else + { + // Don't judge this object as a miss before the head has been judged, to allow the head to be hit late. + return; + } + } ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult); } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index e3ed12a648..1ffbaf11c5 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -125,6 +125,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } + public void HitForcefully() + { + if (Judged) + return; + + ApplyResult(r => r.Type = r.Judgement.MaxResult); + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { if (userTriggered) @@ -141,12 +149,20 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (timeOffset < SliderEventGenerator.TAIL_LENIENCY) return; - // Attempt to preserve correct ordering of judgements as best we can by forcing - // an un-judged head to be missed when the user has clearly skipped it. - // // This check is applied to all nested slider objects apart from the head (ticks, repeats, tail). - if (Tracking && !DrawableSlider.HeadCircle.Judged) - DrawableSlider.HeadCircle.MissForcefully(); + if (!DrawableSlider.HeadCircle.Judged) + { + if (Tracking) + { + // Attempt to preserve correct ordering of judgements as best we can by forcing an un-judged head to be missed when the user has clearly skipped it. + DrawableSlider.HeadCircle.MissForcefully(); + } + else + { + // Don't judge this object as a miss before the head has been judged, to allow the head to be hit late. + return; + } + } // The player needs to have engaged in tracking at any point after the tail leniency cutoff. // An actual tick miss should only occur if reaching the tick itself. diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index 172dca356e..b2ac8ecbda 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -73,17 +73,33 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Position = HitObject.Position - DrawableSlider.HitObject.Position; } + public void HitForcefully() + { + if (Judged) + return; + + ApplyResult(r => r.Type = r.Judgement.MaxResult); + } + protected override void CheckForResult(bool userTriggered, double timeOffset) { // shared implementation with DrawableSliderRepeat. if (timeOffset >= 0) { - // Attempt to preserve correct ordering of judgements as best we can by forcing - // an un-judged head to be missed when the user has clearly skipped it. - // // This check is applied to all nested slider objects apart from the head (ticks, repeats, tail). - if (Tracking && !DrawableSlider.HeadCircle.Judged) - DrawableSlider.HeadCircle.MissForcefully(); + if (!DrawableSlider.HeadCircle.Judged) + { + if (Tracking) + { + // Attempt to preserve correct ordering of judgements as best we can by forcing an un-judged head to be missed when the user has clearly skipped it. + DrawableSlider.HeadCircle.MissForcefully(); + } + else + { + // Don't judge this object as a miss before the head has been judged, to allow the head to be hit late. + return; + } + } ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult); } @@ -107,7 +123,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables case ArmedState.Miss: this.FadeOut(ANIM_DURATION); - this.FadeColour(Color4.Red, ANIM_DURATION / 2); + this.TransformBindableTo(AccentColour, Color4.Red, 0); break; case ArmedState.Hit: From b86f387fd30acab999ad27d38dfb31bd9a993e8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 15:04:20 +0900 Subject: [PATCH 0501/1118] Fix the width of vertical attribute display numbers Not necessarily required to fix the issue at hand, but probably good practice here. --- osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs index 62c08f8132..33b7eaae1c 100644 --- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -82,7 +82,8 @@ namespace osu.Game.Overlays.Mods { Origin = Anchor.CentreLeft, Anchor = Anchor.CentreLeft, - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.Y, + Width = 50, Direction = FillDirection.Vertical, Children = new Drawable[] { From dc5c9837ed969d736645b9bf287099069774caa1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 16:00:03 +0900 Subject: [PATCH 0502/1118] Fix `StarRatingDisplay`'s display width to avoid text making slight autosize changes --- .../Beatmaps/Drawables/StarRatingDisplay.cs | 28 +++++++++++++------ 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs index df8953d57c..55ef6f705e 100644 --- a/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs +++ b/osu.Game/Beatmaps/Drawables/StarRatingDisplay.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -38,6 +39,8 @@ namespace osu.Game.Beatmaps.Drawables private readonly Bindable displayedStars = new BindableDouble(); + private readonly Container textContainer; + /// /// The currently displayed stars of this display wrapped in a bindable. /// This bindable gets transformed on change rather than instantaneous, if animation is enabled. @@ -116,15 +119,19 @@ namespace osu.Game.Beatmaps.Drawables Size = new Vector2(8f), }, Empty(), - starsText = new OsuSpriteText + textContainer = new Container { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Margin = new MarginPadding { Bottom = 1.5f }, - // todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f - // see https://github.com/ppy/osu-framework/issues/3271. - Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), - Shadow = false, + AutoSizeAxes = Axes.Y, + Child = starsText = new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Margin = new MarginPadding { Bottom = 1.5f }, + // todo: this should be size: 12f, but to match up with the design, it needs to be 14.4f + // see https://github.com/ppy/osu-framework/issues/3271. + Font = OsuFont.Torus.With(size: 14.4f, weight: FontWeight.Bold), + Shadow = false, + }, }, } } @@ -155,6 +162,11 @@ namespace osu.Game.Beatmaps.Drawables starIcon.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4Extensions.FromHex("303d47"); starsText.Colour = s.NewValue >= 6.5 ? colours.Orange1 : colourProvider?.Background5 ?? Color4.Black.Opacity(0.75f); + + // In order to avoid autosize throwing the width of these displays all over the place, + // let's lock in some sane defaults for the text width based on how many digits we're + // displaying. + textContainer.Width = 24 + Math.Max(starsText.Text.ToString().Length - 4, 0) * 6; }, true); } } From 4357bb1040b6f7e64f7bc24eb98434e682d352cf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 16:01:07 +0900 Subject: [PATCH 0503/1118] Change `LeftContent` autosize duration to match main content to reduce visual awkwards --- osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index d50b2c33d7..94dd96ec1c 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -144,8 +144,8 @@ namespace osu.Game.Overlays.Mods private void startAnimating() { - Content.AutoSizeEasing = Easing.OutQuint; - Content.AutoSizeDuration = transition_duration; + LeftContent.AutoSizeEasing = Content.AutoSizeEasing = Easing.OutQuint; + LeftContent.AutoSizeDuration = Content.AutoSizeDuration = transition_duration; } private void updateValues() => Scheduler.AddOnce(() => From 6e7e243e70492bc55dac69ce28ab7b8480c8702b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 16:05:29 +0900 Subject: [PATCH 0504/1118] Allow new common cases when a user is locating a stable osu! install directory for import --- .../Maintenance/StableDirectorySelectScreen.cs | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 1b935b0cec..73ac6efcd8 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -15,7 +15,16 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; - protected override bool IsValidDirectory(DirectoryInfo? info) => info?.GetFiles("osu!.*.cfg").Any() ?? false; + protected override bool IsValidDirectory(DirectoryInfo? info) => + // A full stable installation will have a configuration file present. + // This is the best case scenario, as it may contain a custom beatmap directory we need to traverse to. + info?.GetFiles("osu!.*.cfg").Any() == true || + // The user may only have their songs or skins folders left. + // We still want to allow them to import based on this. + info?.GetDirectories("Songs").Any() == true || + info?.GetDirectories("Skins").Any() == true || + // The user may have traverse *inside* their songs or skins folders. + shouldUseParentDirectory(info); public override LocalisableString HeaderText => "Please select your osu!stable install location"; @@ -26,7 +35,7 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override void OnSelection(DirectoryInfo directory) { - taskCompletionSource.TrySetResult(directory.FullName); + taskCompletionSource.TrySetResult(shouldUseParentDirectory(directory) ? directory.Parent!.FullName : directory.FullName); this.Exit(); } @@ -35,5 +44,8 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance taskCompletionSource.TrySetCanceled(); return base.OnExiting(e); } + + private bool shouldUseParentDirectory(DirectoryInfo? info) + => info?.Parent != null && (info?.Name == "Songs" || info?.Name == "Skins"); } } From 6bd190c55d809ee6b69db19d5ce944bd6c887bfd Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Dec 2023 16:13:32 +0900 Subject: [PATCH 0505/1118] Refactor all slider input into SliderInputManager --- .../Objects/Drawables/DrawableHitCircle.cs | 2 +- .../Objects/Drawables/DrawableOsuHitObject.cs | 5 + .../Objects/Drawables/DrawableSlider.cs | 14 +- .../Objects/Drawables/DrawableSliderBall.cs | 126 +-------- .../Objects/Drawables/DrawableSliderHead.cs | 44 +--- .../Objects/Drawables/DrawableSliderRepeat.cs | 36 +-- .../Objects/Drawables/DrawableSliderTail.cs | 53 +--- .../Objects/Drawables/DrawableSliderTick.cs | 36 +-- .../Objects/Drawables/IRequireTracking.cs | 13 - .../Objects/Drawables/SliderInputManager.cs | 248 ++++++++++++++++++ 10 files changed, 270 insertions(+), 307 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs create mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs index e87a075a11..0d665cad0c 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableHitCircle.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { public partial class DrawableHitCircle : DrawableOsuHitObject, IHasApproachCircle { - public OsuAction? HitAction => HitArea.HitAction; + public OsuAction? HitAction => HitArea?.HitAction; protected virtual OsuSkinComponents CirclePieceComponent => OsuSkinComponents.HitCircle; public SkinnableDrawable ApproachCircle { get; private set; } diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs index bdd818cf18..5b379a0d90 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableOsuHitObject.cs @@ -97,6 +97,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// public virtual void Shake() { } + /// + /// Causes this to get hit, disregarding all conditions in implementations of . + /// + public void HitForcefully() => ApplyResult(r => r.Type = r.Judgement.MaxResult); + /// /// Causes this to get missed, disregarding all conditions in implementations of . /// diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 1dd6f108f5..1f9a028045 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public DrawableSliderHead HeadCircle => headContainer.Child; public DrawableSliderTail TailCircle => tailContainer.Child; - public IEnumerable Ticks => tickContainer.Children; - public IEnumerable Repeats => repeatContainer.Children; [Cached] public DrawableSliderBall Ball { get; private set; } @@ -60,6 +58,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public IBindable PathVersion => pathVersion; private readonly Bindable pathVersion = new Bindable(); + public readonly SliderInputManager SliderInputManager; + private Container headContainer; private Container tailContainer; private Container tickContainer; @@ -74,9 +74,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public DrawableSlider([CanBeNull] Slider s = null) : base(s) { + SliderInputManager = new SliderInputManager(this); + Ball = new DrawableSliderBall { - GetInitialHitAction = () => HeadCircle.HitAction, BypassAutoSizeAxes = Axes.Both, AlwaysPresent = true, Alpha = 0 @@ -90,6 +91,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AddRangeInternal(new Drawable[] { + SliderInputManager, shakeContainer = new ShakeContainer { ShakeDuration = 30, @@ -234,7 +236,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.Update(); - Tracking.Value = Ball.Tracking; + Tracking.Value = SliderInputManager.Tracking; if (Tracking.Value && slidingSample != null) // keep the sliding sample playing at the current tracking position @@ -247,8 +249,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (DrawableHitObject hitObject in NestedHitObjects) { - if (hitObject is ITrackSnaking s) s.UpdateSnakingPosition(HitObject.Path.PositionAt(SliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(SliderBody?.SnakedEnd ?? 0)); - if (hitObject is IRequireTracking t) t.Tracking = Ball.Tracking; + if (hitObject is ITrackSnaking s) + s.UpdateSnakingPosition(HitObject.Path.PositionAt(SliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(SliderBody?.SnakedEnd ?? 0)); } Size = SliderBody?.Size ?? Vector2.Zero; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs index 764dd43c30..46f0231981 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderBall.cs @@ -4,14 +4,9 @@ #nullable disable using System; -using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Input; -using osu.Framework.Input.Events; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Skinning.Default; @@ -21,12 +16,10 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public partial class DrawableSliderBall : CircularContainer, ISliderProgress, IRequireHighFrequencyMousePosition + public partial class DrawableSliderBall : CircularContainer, ISliderProgress { public const float FOLLOW_AREA = 2.4f; - public Func GetInitialHitAction; - private DrawableSlider drawableSlider; private Drawable ball; @@ -55,14 +48,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables }; } - private Vector2? lastScreenSpaceMousePosition; - - protected override bool OnMouseMove(MouseMoveEvent e) - { - lastScreenSpaceMousePosition = e.ScreenSpaceMousePosition; - return base.OnMouseMove(e); - } - public override void ClearTransformsAfter(double time, bool propagateChildren = false, string targetMember = null) { // Consider the case of rewinding - children's transforms are handled internally, so propagating down @@ -78,115 +63,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables base.ApplyTransformsAt(time, false); } - public bool Tracking { get; private set; } - - /// - /// If the cursor moves out of the ball's radius we still need to be able to receive positional updates to stop tracking. - /// - public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; - - /// - /// The point in time after which we can accept any key for tracking. Before this time, we may need to restrict tracking to the key used to hit the head circle. - /// - /// This is a requirement to stop the case where a player holds down one key (from before the slider) and taps the second key while maintaining full scoring (tracking) of sliders. - /// Visually, this special case can be seen below (time increasing from left to right): - /// - /// Z Z+X Z - /// o========o - /// - /// Without this logic, tracking would continue through the entire slider even though no key hold action is directly attributing to it. - /// - /// In all other cases, no special handling is required (either key being pressed is allowable as valid tracking). - /// - /// The reason for storing this as a time value (rather than a bool) is to correctly handle rewind scenarios. - /// - private double? timeToAcceptAnyKeyAfter; - - /// - /// The actions that were pressed in the previous frame. - /// - private readonly List lastPressedActions = new List(); - - public bool IsMouseInFollowCircleWithState(bool expanded) - { - if (lastScreenSpaceMousePosition is not Vector2 mousePos) - return false; - - float radius = GetFollowCircleRadius(expanded); - - double followProgress = Math.Clamp((Time.Current - drawableSlider.HitObject.StartTime) / drawableSlider.HitObject.Duration, 0, 1); - Vector2 followCirclePosition = drawableSlider.HitObject.CurvePositionAt(followProgress); - Vector2 mousePositionInSlider = drawableSlider.ToLocalSpace(mousePos) - drawableSlider.OriginPosition; - - return (mousePositionInSlider - followCirclePosition).LengthSquared <= radius * radius; - } - - public float GetFollowCircleRadius(bool expanded) - { - float radius = (float)drawableSlider.HitObject.Radius; - - if (expanded) - radius *= FOLLOW_AREA; - - return radius; - } - - protected override void Update() - { - base.Update(); - - // from the point at which the head circle is hit, this will be non-null. - // it may be null if the head circle was missed. - var headCircleHitAction = GetInitialHitAction(); - - if (headCircleHitAction == null) - timeToAcceptAnyKeyAfter = null; - - var actions = drawableSlider.OsuActionInputManager?.PressedActions; - - // if the head circle was hit with a specific key, tracking should only occur while that key is pressed. - if (headCircleHitAction != null && timeToAcceptAnyKeyAfter == null) - { - var otherKey = headCircleHitAction == OsuAction.RightButton ? OsuAction.LeftButton : OsuAction.RightButton; - - // we can start accepting any key once all other keys have been released in the previous frame. - if (!lastPressedActions.Contains(otherKey)) - timeToAcceptAnyKeyAfter = Time.Current; - } - - bool validInFollowArea = IsMouseInFollowCircleWithState(Tracking); - bool validInHeadCircle = drawableSlider.HeadCircle.IsHit - && IsMouseInFollowCircleWithState(true) - && drawableSlider.HeadCircle.Result.TimeAbsolute == Time.Current; - - Tracking = - // even in an edge case where current time has exceeded the slider's time, we may not have finished judging. - // we don't want to potentially update from Tracking=true to Tracking=false at this point. - (!drawableSlider.AllJudged || Time.Current <= drawableSlider.HitObject.GetEndTime()) - // in valid position range - && (validInFollowArea || validInHeadCircle) - // valid action - && (actions?.Any(isValidTrackingAction) ?? false); - - lastPressedActions.Clear(); - if (actions != null) - lastPressedActions.AddRange(actions); - } - - /// - /// Check whether a given user input is a valid tracking action. - /// - private bool isValidTrackingAction(OsuAction action) - { - bool headCircleHit = GetInitialHitAction().HasValue; - - // if the head circle was hit, we may not yet be allowed to accept any key, so we must use the initial hit action. - if (headCircleHit && (!timeToAcceptAnyKeyAfter.HasValue || Time.Current <= timeToAcceptAnyKeyAfter.Value)) - return action == GetInitialHitAction(); - - return action == OsuAction.LeftButton || action == OsuAction.RightButton; - } - private Vector2? lastPosition; public void UpdateProgress(double completionProgress) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs index 0a104c123b..76b9fdc3ce 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderHead.cs @@ -3,14 +3,10 @@ #nullable disable -using System; using System.Diagnostics; -using System.Linq; using osu.Framework.Bindables; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Scoring; -using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { @@ -68,45 +64,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void CheckForResult(bool userTriggered, double timeOffset) { base.CheckForResult(userTriggered, timeOffset); - - if (!Judged || !Result.IsHit) - return; - - // If the head is hit and in radius of the would-be-expanded follow circle, - // then hit every object that the follow circle has passed through up until the current time. - if (DrawableSlider.Ball.IsMouseInFollowCircleWithState(true)) - { - foreach (var nested in DrawableSlider.NestedHitObjects.OfType()) - { - if (nested.Judged) - continue; - - if (!check(nested.HitObject)) - break; - - if (nested is DrawableSliderTick tick) - tick.HitForcefully(); - - if (nested is DrawableSliderRepeat repeat) - repeat.HitForcefully(); - - if (nested is DrawableSliderTail tail) - tail.HitForcefully(); - } - } - - bool check(OsuHitObject h) - { - if (h.StartTime > Time.Current) - return false; - - float radius = DrawableSlider.Ball.GetFollowCircleRadius(true); - - double objectProgress = Math.Clamp((h.StartTime - DrawableSlider.HitObject.StartTime) / DrawableSlider.HitObject.Duration, 0, 1); - Vector2 objectPosition = DrawableSlider.HitObject.CurvePositionAt(objectProgress); - - return objectPosition.LengthSquared <= radius * radius; - } + DrawableSlider.SliderInputManager.PostProcessHeadJudgement(this); } protected override HitResult ResultFor(double timeOffset) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index a7979bde27..0c8e5b765f 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -17,7 +17,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public partial class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking, IRequireTracking + public partial class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking { public new SliderRepeat HitObject => (SliderRepeat)base.HitObject; @@ -36,8 +36,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables public override bool DisplayResult => false; - public bool Tracking { get; set; } - public DrawableSliderRepeat() : base(null) { @@ -85,37 +83,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Position = HitObject.Position - DrawableSlider.Position; } - public void HitForcefully() - { - if (Judged) - return; - - ApplyResult(r => r.Type = r.Judgement.MaxResult); - } - - protected override void CheckForResult(bool userTriggered, double timeOffset) - { - // shared implementation with DrawableSliderTick. - if (timeOffset >= 0) - { - // This check is applied to all nested slider objects apart from the head (ticks, repeats, tail). - if (!DrawableSlider.HeadCircle.Judged) - { - if (Tracking) - { - // Attempt to preserve correct ordering of judgements as best we can by forcing an un-judged head to be missed when the user has clearly skipped it. - DrawableSlider.HeadCircle.MissForcefully(); - } - else - { - // Don't judge this object as a miss before the head has been judged, to allow the head to be hit late. - return; - } - } - - ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult); - } - } + protected override void CheckForResult(bool userTriggered, double timeOffset) => DrawableSlider.SliderInputManager.TryJudgeNestedObject(this, timeOffset); protected override void UpdateInitialTransforms() { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 1ffbaf11c5..60bad5d4a7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -4,12 +4,10 @@ #nullable disable using System.Diagnostics; -using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; using osu.Game.Skinning; @@ -17,7 +15,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public partial class DrawableSliderTail : DrawableOsuHitObject, IRequireTracking + public partial class DrawableSliderTail : DrawableOsuHitObject { public new SliderTailCircle HitObject => (SliderTailCircle)base.HitObject; @@ -37,8 +35,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// public bool SamplePlaysOnlyOnHit { get; set; } = true; - public bool Tracking { get; set; } - public SkinnableDrawable CirclePiece { get; private set; } private Container scaleContainer; @@ -125,52 +121,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - public void HitForcefully() - { - if (Judged) - return; - - ApplyResult(r => r.Type = r.Judgement.MaxResult); - } - - protected override void CheckForResult(bool userTriggered, double timeOffset) - { - if (userTriggered) - return; - - // Ensure the tail can only activate after all previous ticks/repeats already have. - // - // This covers the edge case where the lenience may allow the tail to activate before - // the last tick, changing ordering of score/combo awarding. - var lastTick = DrawableSlider.NestedHitObjects.LastOrDefault(o => o.HitObject is SliderTick || o.HitObject is SliderRepeat); - if (lastTick?.Judged == false) - return; - - if (timeOffset < SliderEventGenerator.TAIL_LENIENCY) - return; - - // This check is applied to all nested slider objects apart from the head (ticks, repeats, tail). - if (!DrawableSlider.HeadCircle.Judged) - { - if (Tracking) - { - // Attempt to preserve correct ordering of judgements as best we can by forcing an un-judged head to be missed when the user has clearly skipped it. - DrawableSlider.HeadCircle.MissForcefully(); - } - else - { - // Don't judge this object as a miss before the head has been judged, to allow the head to be hit late. - return; - } - } - - // The player needs to have engaged in tracking at any point after the tail leniency cutoff. - // An actual tick miss should only occur if reaching the tick itself. - if (Tracking) - ApplyResult(r => r.Type = r.Judgement.MaxResult); - else if (timeOffset > 0) - ApplyResult(r => r.Type = r.Judgement.MinResult); - } + protected override void CheckForResult(bool userTriggered, double timeOffset) => DrawableSlider.SliderInputManager.TryJudgeNestedObject(this, timeOffset); protected override void OnApply() { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index b2ac8ecbda..cb323f4ac7 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -14,14 +14,12 @@ using osu.Framework.Graphics.Containers; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public partial class DrawableSliderTick : DrawableOsuHitObject, IRequireTracking + public partial class DrawableSliderTick : DrawableOsuHitObject { public const double ANIM_DURATION = 150; private const float default_tick_size = 16; - public bool Tracking { get; set; } - public override bool DisplayResult => false; protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; @@ -73,37 +71,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Position = HitObject.Position - DrawableSlider.HitObject.Position; } - public void HitForcefully() - { - if (Judged) - return; - - ApplyResult(r => r.Type = r.Judgement.MaxResult); - } - - protected override void CheckForResult(bool userTriggered, double timeOffset) - { - // shared implementation with DrawableSliderRepeat. - if (timeOffset >= 0) - { - // This check is applied to all nested slider objects apart from the head (ticks, repeats, tail). - if (!DrawableSlider.HeadCircle.Judged) - { - if (Tracking) - { - // Attempt to preserve correct ordering of judgements as best we can by forcing an un-judged head to be missed when the user has clearly skipped it. - DrawableSlider.HeadCircle.MissForcefully(); - } - else - { - // Don't judge this object as a miss before the head has been judged, to allow the head to be hit late. - return; - } - } - - ApplyResult(r => r.Type = Tracking ? r.Judgement.MaxResult : r.Judgement.MinResult); - } - } + protected override void CheckForResult(bool userTriggered, double timeOffset) => DrawableSlider.SliderInputManager.TryJudgeNestedObject(this, timeOffset); protected override void UpdateInitialTransforms() { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs deleted file mode 100644 index b1815b23c9..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/IRequireTracking.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - public interface IRequireTracking - { - /// - /// Whether the is currently being tracked by the user. - /// - bool Tracking { get; set; } - } -} diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs new file mode 100644 index 0000000000..a5d7bdc7db --- /dev/null +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs @@ -0,0 +1,248 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using System.Linq; +using osu.Framework.Graphics; +using osu.Framework.Input; +using osu.Framework.Input.Events; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Objects.Drawables +{ + public partial class SliderInputManager : Component, IRequireHighFrequencyMousePosition + { + /// + /// Whether the slider is currently being tracked. + /// + public bool Tracking { get; private set; } + + /// + /// The point in time after which we can accept any key for tracking. Before this time, we may need to restrict tracking to the key used to hit the head circle. + /// + /// This is a requirement to stop the case where a player holds down one key (from before the slider) and taps the second key while maintaining full scoring (tracking) of sliders. + /// Visually, this special case can be seen below (time increasing from left to right): + /// + /// Z Z+X Z + /// o========o + /// + /// Without this logic, tracking would continue through the entire slider even though no key hold action is directly attributing to it. + /// + /// In all other cases, no special handling is required (either key being pressed is allowable as valid tracking). + /// + /// The reason for storing this as a time value (rather than a bool) is to correctly handle rewind scenarios. + /// + private double? timeToAcceptAnyKeyAfter; + + /// + /// The actions that were pressed in the previous frame. + /// + private readonly List lastPressedActions = new List(); + + private Vector2? screenSpaceMousePosition; + private readonly DrawableSlider slider; + + public SliderInputManager(DrawableSlider slider) + { + this.slider = slider; + } + + /// + /// This component handles all input of the slider, so it should receive input no matter the position. + /// + public override bool ReceivePositionalInputAt(Vector2 screenSpacePos) => true; + + protected override bool OnMouseMove(MouseMoveEvent e) + { + screenSpaceMousePosition = e.ScreenSpaceMousePosition; + return base.OnMouseMove(e); + } + + protected override void Update() + { + base.Update(); + updateTracking(isMouseInFollowArea(Tracking)); + } + + public void PostProcessHeadJudgement(DrawableSliderHead head) + { + if (!head.Judged || !head.Result.IsHit) + return; + + if (!isMouseInFollowArea(true)) + return; + + // When the head is hit and the mouse is in the expanded follow area, force a hit on every nested hitobject + // from the start of the slider that is within follow-radius units from the head. + + bool forceMiss = false; + + foreach (var nested in slider.NestedHitObjects.OfType()) + { + // Skip nested objects that are already judged. + if (nested.Judged) + continue; + + // Stop the process when a nested object is reached that can't be hit before the current time. + if (nested.HitObject.StartTime > Time.Current) + break; + + float radius = getFollowRadius(true); + double objectProgress = Math.Clamp((nested.HitObject.StartTime - slider.HitObject.StartTime) / slider.HitObject.Duration, 0, 1); + Vector2 objectPosition = slider.HitObject.CurvePositionAt(objectProgress); + + // When the first nested object that is further than follow-radius units away from the start of the slider is reached, + // forcefully miss all other nested objects that would otherwise be valid to be hit by this process. + if (forceMiss || objectPosition.LengthSquared > radius * radius) + { + nested.MissForcefully(); + forceMiss = true; + } + else + nested.HitForcefully(); + } + + // Enable tracking, since the mouse is within the follow area (if it were expanded). + updateTracking(true); + } + + public void TryJudgeNestedObject(DrawableOsuHitObject nestedObject, double timeOffset) + { + switch (nestedObject) + { + case DrawableSliderRepeat: + case DrawableSliderTick: + if (timeOffset < 0) + return; + + break; + + case DrawableSliderTail: + if (timeOffset < SliderEventGenerator.TAIL_LENIENCY) + return; + + // Ensure the tail can only activate after all previous ticks/repeats already have. + // + // This covers the edge case where the lenience may allow the tail to activate before + // the last tick, changing ordering of score/combo awarding. + var lastTick = slider.NestedHitObjects.LastOrDefault(o => o.HitObject is SliderTick || o.HitObject is SliderRepeat); + if (lastTick?.Judged == false) + return; + + break; + + default: + return; + } + + if (!slider.HeadCircle.Judged) + { + if (slider.Tracking.Value) + { + // Attempt to preserve correct ordering of judgements as best we can by forcing an un-judged head to be missed when the user has clearly skipped it. + slider.HeadCircle.MissForcefully(); + } + else + { + // Don't judge this object as a miss before the head has been judged, to allow the head to be hit late. + return; + } + } + + if (slider.Tracking.Value) + nestedObject.HitForcefully(); + else + nestedObject.MissForcefully(); + } + + /// + /// Whether the mouse is currently in the follow area. + /// + /// Whether to test against the maximum area of the follow circle. + private bool isMouseInFollowArea(bool expanded) + { + if (screenSpaceMousePosition is not Vector2 pos) + return false; + + float radius = getFollowRadius(expanded); + + double followProgress = Math.Clamp((Time.Current - slider.HitObject.StartTime) / slider.HitObject.Duration, 0, 1); + Vector2 followCirclePosition = slider.HitObject.CurvePositionAt(followProgress); + Vector2 mousePositionInSlider = slider.ToLocalSpace(pos) - slider.OriginPosition; + + return (mousePositionInSlider - followCirclePosition).LengthSquared <= radius * radius; + } + + /// + /// Retrieves the radius of the follow area. + /// + /// Whether to return the maximum area of the follow circle. + private float getFollowRadius(bool expanded) + { + float radius = (float)slider.HitObject.Radius; + + if (expanded) + radius *= DrawableSliderBall.FOLLOW_AREA; + + return radius; + } + + /// + /// Updates the tracking state. + /// + /// Whether the current mouse position is valid to begin tracking. + private void updateTracking(bool isValidTrackingPosition) + { + // from the point at which the head circle is hit, this will be non-null. + // it may be null if the head circle was missed. + OsuAction? headCircleHitAction = getInitialHitAction(); + + if (headCircleHitAction == null) + timeToAcceptAnyKeyAfter = null; + + var actions = slider.OsuActionInputManager?.PressedActions; + + // if the head circle was hit with a specific key, tracking should only occur while that key is pressed. + if (headCircleHitAction != null && timeToAcceptAnyKeyAfter == null) + { + var otherKey = headCircleHitAction == OsuAction.RightButton ? OsuAction.LeftButton : OsuAction.RightButton; + + // we can start accepting any key once all other keys have been released in the previous frame. + if (!lastPressedActions.Contains(otherKey)) + timeToAcceptAnyKeyAfter = Time.Current; + } + + Tracking = + // even in an edge case where current time has exceeded the slider's time, we may not have finished judging. + // we don't want to potentially update from Tracking=true to Tracking=false at this point. + (!slider.AllJudged || Time.Current <= slider.HitObject.GetEndTime()) + // in valid position range + && isValidTrackingPosition + // valid action + && (actions?.Any(isValidTrackingAction) ?? false); + + lastPressedActions.Clear(); + if (actions != null) + lastPressedActions.AddRange(actions); + } + + private OsuAction? getInitialHitAction() => slider.HeadCircle?.HitAction; + + /// + /// Check whether a given user input is a valid tracking action. + /// + private bool isValidTrackingAction(OsuAction action) + { + OsuAction? hitAction = getInitialHitAction(); + + // if the head circle was hit, we may not yet be allowed to accept any key, so we must use the initial hit action. + if (hitAction.HasValue && (!timeToAcceptAnyKeyAfter.HasValue || Time.Current <= timeToAcceptAnyKeyAfter.Value)) + return action == hitAction; + + return action == OsuAction.LeftButton || action == OsuAction.RightButton; + } + } +} From 3b8a73bf2c5ac25224ae2fc88967b58f98cb07a7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Dec 2023 16:13:34 +0900 Subject: [PATCH 0506/1118] Refactor test --- .../TestSceneSliderLateHitJudgement.cs | 19 ++++++------------- 1 file changed, 6 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs index 4f32a6fe9f..04bdd0094d 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -296,7 +296,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void assertHeadJudgement(HitResult result) { AddAssert( - "check head result", + $"head = {result}", () => judgementResults.SingleOrDefault(r => r.HitObject is SliderHeadCircle)?.Type, () => Is.EqualTo(result)); } @@ -304,7 +304,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void assertTickJudgement(int index, HitResult result) { AddAssert( - $"check tick({index}) result", + $"tick({index}) = {result}", () => judgementResults.Where(r => r.HitObject is SliderTick).ElementAtOrDefault(index)?.Type, () => Is.EqualTo(result)); } @@ -312,7 +312,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void assertAllTickJudgements(HitResult result) { AddAssert( - "check all tick results", + $"all ticks = {result}", () => judgementResults.Where(r => r.HitObject is SliderTick).Select(t => t.Type), () => Has.All.EqualTo(result)); } @@ -320,7 +320,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void assertRepeatJudgement(HitResult result) { AddAssert( - "check repeat result", + $"repeat = {result}", () => judgementResults.SingleOrDefault(r => r.HitObject is SliderRepeat)?.Type, () => Is.EqualTo(result)); } @@ -328,7 +328,7 @@ namespace osu.Game.Rulesets.Osu.Tests private void assertTailJudgement(HitResult result) { AddAssert( - "check tail result", + $"tail = {result}", () => judgementResults.SingleOrDefault(r => r.HitObject is SliderTailCircle)?.Type, () => Is.EqualTo(result)); } @@ -336,18 +336,11 @@ namespace osu.Game.Rulesets.Osu.Tests private void assertSliderJudgement(HitResult result) { AddAssert( - "check slider result", + $"slider = {result}", () => judgementResults.SingleOrDefault(r => r.HitObject is Slider)?.Type, () => Is.EqualTo(result)); } - private Vector2 computePositionFromTime(double time) - { - Vector2 dist = slider_end_position - slider_start_position; - double t = (time - time_slider_start) / (time_slider_end - time_slider_start); - return slider_start_position + dist * (float)t; - } - private void performTest(List frames, Action? adjustSliderFunc = null, bool classic = false) { Slider slider = new Slider From 12210017e44341e5378edc2af86553addeae4eab Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Dec 2023 17:05:14 +0900 Subject: [PATCH 0507/1118] Use the cursor position to test nested object validity --- .../TestSceneSliderLateHitJudgement.cs | 42 ++++++++++++++++--- .../Objects/Drawables/SliderInputManager.cs | 14 +++++-- 2 files changed, 46 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs index 04bdd0094d..80e4b04178 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -184,7 +184,7 @@ namespace osu.Game.Rulesets.Osu.Tests /// /// If the head circle is hit late and the mouse is in range of the follow circle, - /// then all the repeats that the mouse has passed through should be hit. + /// then all the repeats that the follow circle has passed through should be hit. /// [Test] public void TestHitLateInRangeHitsRepeat() @@ -212,8 +212,8 @@ namespace osu.Game.Rulesets.Osu.Tests /// /// If the head circle is hit and the mouse is in range of the follow circle, - /// then only the ticks that were in range of the follow circle at the head should be hit. - /// If any hitobject was outside the follow range, ALL hitobjects after that point should be missed. + /// then only the ticks that are in range of the cursor position should be hit. + /// If any hitobject does not meet this criteria, ALL hitobjects after that one should be missed. /// [Test] public void TestHitLateInRangeDoesNotHitAfterAnyOutOfRange() @@ -257,7 +257,7 @@ namespace osu.Game.Rulesets.Osu.Tests assertTickJudgement(11, HitResult.LargeTickMiss); assertTickJudgement(12, HitResult.LargeTickMiss); - // And the tail should be hit because of its leniency. + // This particular test actually starts tracking the slider just before the end, so the tail should be hit because of its leniency. assertTailJudgement(HitResult.LargeTickHit); assertSliderJudgement(HitResult.IgnoreHit); @@ -265,10 +265,10 @@ namespace osu.Game.Rulesets.Osu.Tests /// /// If the head circle is hit and the mouse is in range of the follow circle, - /// then a tick outside the range of the follow circle from the head should not be hit. + /// then a tick not within the follow radius from the cursor position should not be hit. /// [Test] - public void TestHitLateInRangeDoesNotHitOutOfRange() + public void TestHitLateInRangeDoesNotHitOutOfRangeTick() { performTest(new List { @@ -293,6 +293,36 @@ namespace osu.Game.Rulesets.Osu.Tests assertSliderJudgement(HitResult.IgnoreHit); } + /// + /// If the head circle is hit and the mouse is in range of the follow circle, + /// then a tick not within the follow radius from the cursor position should not be hit. + /// + [Test] + public void TestHitLateWithEdgeHit() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 150, slider_start_position - new Vector2(20), OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 150, slider_start_position - new Vector2(20), OsuAction.LeftButton), + }, s => + { + s.Path = new SliderPath(PathType.PERFECT_CURVE, new[] + { + Vector2.Zero, + new Vector2(50, 50), + new Vector2(20, 0), + }); + + s.TickDistanceMultiplier = 0.35f; + s.SliderVelocityMultiplier = 4; + }); + + assertHeadJudgement(HitResult.Meh); + assertTickJudgement(0, HitResult.LargeTickMiss); + assertTailJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreHit); + } + private void assertHeadJudgement(HitResult result) { AddAssert( diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs index a5d7bdc7db..9feeb6ef14 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using osu.Framework.Graphics; using osu.Framework.Input; @@ -75,8 +76,12 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!isMouseInFollowArea(true)) return; + Debug.Assert(screenSpaceMousePosition != null); + + Vector2 mousePositionInSlider = slider.ToLocalSpace(screenSpaceMousePosition.Value) - slider.OriginPosition; + // When the head is hit and the mouse is in the expanded follow area, force a hit on every nested hitobject - // from the start of the slider that is within follow-radius units from the head. + // from the start of the slider that is within the follow area. bool forceMiss = false; @@ -94,9 +99,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables double objectProgress = Math.Clamp((nested.HitObject.StartTime - slider.HitObject.StartTime) / slider.HitObject.Duration, 0, 1); Vector2 objectPosition = slider.HitObject.CurvePositionAt(objectProgress); - // When the first nested object that is further than follow-radius units away from the start of the slider is reached, - // forcefully miss all other nested objects that would otherwise be valid to be hit by this process. - if (forceMiss || objectPosition.LengthSquared > radius * radius) + // When the first nested object that is further outside the follow area is reached, + // forcefully miss all other nested objects that would otherwise be valid to be hit. + // This covers a case of a slider overlapping itself that requires tracking to a tick on an outer edge. + if (forceMiss || (objectPosition - mousePositionInSlider).LengthSquared > radius * radius) { nested.MissForcefully(); forceMiss = true; From 9ae3be817fa77dd142b5292e3d361953319efe07 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Dec 2023 17:40:36 +0900 Subject: [PATCH 0508/1118] Add some text to the test scene showing hits/misses --- .../TestSceneSliderLateHitJudgement.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs index 80e4b04178..2a3655eccd 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -5,12 +5,17 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics; using osu.Framework.Screens; +using osu.Framework.Testing; using osu.Game.Beatmaps; +using osu.Game.Graphics.Sprites; using osu.Game.Replays; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Replays; @@ -410,7 +415,21 @@ namespace osu.Game.Rulesets.Osu.Tests { p.ScoreProcessor.NewJudgement += result => { - if (currentPlayer == p) judgementResults.Add(result); + if (currentPlayer == p) + judgementResults.Add(result); + + DrawableHitObject drawableObj = this.ChildrenOfType().Single(h => h.HitObject == result.HitObject); + + var text = new OsuSpriteText + { + Origin = Anchor.Centre, + Position = Content.ToLocalSpace(drawableObj.ToScreenSpace(drawableObj.OriginPosition)) - new Vector2(0, 20), + Text = result.IsHit ? "hit" : "miss" + }; + + Add(text); + + text.FadeOutFromOne(1000).Expire(); }; }; From 9e3b1dbb5926684b151066fed1df14e25cbb26b2 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Dec 2023 17:41:22 +0900 Subject: [PATCH 0509/1118] Fix CI inspection --- .../Sections/Maintenance/StableDirectorySelectScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 73ac6efcd8..17f09ade4e 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -46,6 +46,6 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance } private bool shouldUseParentDirectory(DirectoryInfo? info) - => info?.Parent != null && (info?.Name == "Songs" || info?.Name == "Skins"); + => info?.Parent != null && (info.Name == "Songs" || info.Name == "Skins"); } } From 456916f680c608acbd24ed3953e97e630a656551 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 18:02:27 +0900 Subject: [PATCH 0510/1118] Fix column sizing exceeding screen width on tablets --- .../Argon/ManiaArgonSkinTransformer.cs | 10 +---- osu.Game.Rulesets.Mania/UI/ColumnFlow.cs | 45 +++++++++++++++---- 2 files changed, 38 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs index ca7f84cb4d..7f6540e7b5 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ManiaArgonSkinTransformer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using osu.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Beatmaps; @@ -100,16 +99,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon return SkinUtils.As(new Bindable(30)); case LegacyManiaSkinConfigurationLookups.ColumnWidth: - - float width; - bool isSpecialColumn = stage.IsSpecialColumn(columnIndex); - // Best effort until we have better mobile support. - if (RuntimeInfo.IsMobile) - width = 170 * Math.Min(1, 7f / beatmap.TotalColumns) * (isSpecialColumn ? 1.8f : 1); - else - width = 60 * (isSpecialColumn ? 2 : 1); + float width = 60 * (isSpecialColumn ? 2 : 1); return SkinUtils.As(new Bindable(width)); diff --git a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs index 0bc0bf4caf..44c318a2c5 100644 --- a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs +++ b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs @@ -3,14 +3,17 @@ #nullable disable +using System; using System.Collections.Generic; using System.Linq; +using osu.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Skinning; +using osuTK; namespace osu.Game.Rulesets.Mania.UI { @@ -60,6 +63,12 @@ namespace osu.Game.Rulesets.Mania.UI onSkinChanged(); } + protected override void LoadComplete() + { + base.LoadComplete(); + updateMobileSizing(); + } + private void onSkinChanged() { for (int i = 0; i < stageDefinition.Columns; i++) @@ -77,12 +86,15 @@ namespace osu.Game.Rulesets.Mania.UI new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.ColumnWidth, i)) ?.Value; - if (width == null) - // only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration) - columns[i].Width = stageDefinition.IsSpecialColumn(i) ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH; - else - columns[i].Width = width.Value; + bool isSpecialColumn = stageDefinition.IsSpecialColumn(i); + + // only used by default skin (legacy skins get defaults set in LegacyManiaSkinConfiguration) + width ??= isSpecialColumn ? Column.SPECIAL_COLUMN_WIDTH : Column.COLUMN_WIDTH; + + columns[i].Width = width.Value; } + + updateMobileSizing(); } /// @@ -92,10 +104,27 @@ namespace osu.Game.Rulesets.Mania.UI /// The content. public void SetContentForColumn(int column, TContent content) => columns[column].Child = content; - public new MarginPadding Padding + private void updateMobileSizing() { - get => base.Padding; - set => base.Padding = value; + if (!IsLoaded) + return; + + for (int i = 0; i < stageDefinition.Columns; i++) + { + // GridContainer+CellContainer containing this stage (gets split up for dual stages). + Vector2 containingCell = this.FindClosestParent().Parent!.DrawSize; + + // Best effort until we have better mobile support. + if (RuntimeInfo.IsMobile) + { + // These numbers are based on mobile phones, aspect ~1.92. + float mobileAdjust = 2.83f * Math.Min(1, 7f / stageDefinition.Columns); + // We should scale it back for cases like tablets which aren't so extreme. + mobileAdjust *= (containingCell.X / containingCell.Y) / 1.92f; + + columns[i].Width *= mobileAdjust; + } + } } protected override void Dispose(bool isDisposing) From 4ad312ef5b8bd4ce392f1a5cbb738da5cb991389 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 19:12:45 +0900 Subject: [PATCH 0511/1118] Update xmldoc for `LegacyComboIncrease` --- osu.Game/Rulesets/Scoring/HitResult.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 6380b73558..9705421571 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -139,9 +139,12 @@ namespace osu.Game.Rulesets.Scoring /// /// A special result used as a padding value for legacy rulesets. It is a hit type and affects combo, but does not affect the base score (does not affect accuracy). + /// + /// DO NOT USE FOR ANYTHING EVER. /// /// - /// DO NOT USE. + /// This is used when dealing with legacy scores, which historically only have counts stored for 300/100/50/miss. + /// For these scores, we pad the hit statistics with `LegacyComboIncrease` to meet the correct max combo for the score. /// [EnumMember(Value = "legacy_combo_increase")] [Order(99)] From eb5a8284f1200e80a8e9b616e68b8a056c8a84e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 19:15:25 +0900 Subject: [PATCH 0512/1118] Move mobile check earlier to avoid unnecessary looping --- osu.Game.Rulesets.Mania/UI/ColumnFlow.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs index 44c318a2c5..f28619fb64 100644 --- a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs +++ b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs @@ -106,7 +106,7 @@ namespace osu.Game.Rulesets.Mania.UI private void updateMobileSizing() { - if (!IsLoaded) + if (!IsLoaded || !RuntimeInfo.IsMobile) return; for (int i = 0; i < stageDefinition.Columns; i++) From e8f3e52c9e0bccbe6f67f4904e4db0a45bd29f4b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 19:17:48 +0900 Subject: [PATCH 0513/1118] Fix nullref failure in tests --- osu.Game.Rulesets.Mania/UI/ColumnFlow.cs | 31 ++++++++++++------------ 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs index f28619fb64..3ecd14ce81 100644 --- a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs +++ b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs @@ -109,22 +109,23 @@ namespace osu.Game.Rulesets.Mania.UI if (!IsLoaded || !RuntimeInfo.IsMobile) return; + // GridContainer+CellContainer containing this stage (gets split up for dual stages). + Vector2? containingCell = this.FindClosestParent()?.Parent?.DrawSize; + + // Will be null in tests. + if (containingCell == null) + return; + + float aspectRatio = containingCell.Value.X / containingCell.Value.Y; + + // These numbers are based on mobile phones, aspect ~1.92. + float mobileAdjust = 2.83f * Math.Min(1, 7f / stageDefinition.Columns); + // We should scale it back for cases like tablets which aren't so extreme. + mobileAdjust *= aspectRatio / 1.92f; + + // Best effort until we have better mobile support. for (int i = 0; i < stageDefinition.Columns; i++) - { - // GridContainer+CellContainer containing this stage (gets split up for dual stages). - Vector2 containingCell = this.FindClosestParent().Parent!.DrawSize; - - // Best effort until we have better mobile support. - if (RuntimeInfo.IsMobile) - { - // These numbers are based on mobile phones, aspect ~1.92. - float mobileAdjust = 2.83f * Math.Min(1, 7f / stageDefinition.Columns); - // We should scale it back for cases like tablets which aren't so extreme. - mobileAdjust *= (containingCell.X / containingCell.Y) / 1.92f; - - columns[i].Width *= mobileAdjust; - } - } + columns[i].Width *= mobileAdjust; } protected override void Dispose(bool isDisposing) From 94f63d604401ad7b2a9153da1b944d6986045e51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 15 Dec 2023 19:18:53 +0900 Subject: [PATCH 0514/1118] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index b0ab8e445c..807e3ede2a 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From b384c9f9388f10ca2447a47e5b0fa75000e75f16 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 15 Dec 2023 15:42:19 +0100 Subject: [PATCH 0515/1118] Extract common method for determining stable import usability of directory --- osu.Game/Database/LegacyImportManager.cs | 46 +++++++++++++++++++ .../FirstRunSetup/ScreenImportFromStable.cs | 4 +- .../StableDirectorySelectScreen.cs | 26 +++++------ 3 files changed, 59 insertions(+), 17 deletions(-) diff --git a/osu.Game/Database/LegacyImportManager.cs b/osu.Game/Database/LegacyImportManager.cs index 20738f859e..7e1641d16f 100644 --- a/osu.Game/Database/LegacyImportManager.cs +++ b/osu.Game/Database/LegacyImportManager.cs @@ -3,6 +3,9 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework; @@ -54,6 +57,49 @@ namespace osu.Game.Database public void UpdateStorage(string stablePath) => cachedStorage = new StableStorage(stablePath, gameHost as DesktopGameHost); + /// + /// Checks whether a valid location to run a stable import from can be determined starting from the supplied . + /// + /// The directory to check for stable import eligibility. + /// + /// If the return value is , + /// this parameter will contain the to use as the root directory for importing. + /// + public bool IsUsableForStableImport(DirectoryInfo? directory, [NotNullWhen(true)] out DirectoryInfo? stableRoot) + { + if (directory == null) + { + stableRoot = null; + return false; + } + + // A full stable installation will have a configuration file present. + // This is the best case scenario, as it may contain a custom beatmap directory we need to traverse to. + if (directory.GetFiles(@"osu!.*.cfg").Any()) + { + stableRoot = directory; + return true; + } + + // The user may only have their songs or skins folders left. + // We still want to allow them to import based on this. + if (directory.GetDirectories(@"Songs").Any() || directory.GetDirectories(@"Skins").Any()) + { + stableRoot = directory; + return true; + } + + // The user may have traversed *inside* their songs or skins folders. + if (directory.Parent != null && (directory.Name == @"Songs" || directory.Name == @"Skins")) + { + stableRoot = directory.Parent; + return true; + } + + stableRoot = null; + return false; + } + public bool CheckSongsFolderHardLinkAvailability() { var stableStorage = GetCurrentStableStorage(); diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index 23f3b3e1af..185a47c371 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -269,11 +269,11 @@ namespace osu.Game.Overlays.FirstRunSetup if (directory.OldValue?.FullName == directory.NewValue.FullName) return; - if (directory.NewValue?.GetFiles(@"osu!.*.cfg").Any() ?? false) + if (legacyImportManager.IsUsableForStableImport(directory.NewValue, out var stableRoot)) { this.HidePopover(); - string path = directory.NewValue.FullName; + string path = stableRoot.FullName; legacyImportManager.UpdateStorage(path); Current.Value = path; diff --git a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs index 17f09ade4e..3f12b9c0df 100644 --- a/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs +++ b/osu.Game/Overlays/Settings/Sections/Maintenance/StableDirectorySelectScreen.cs @@ -1,11 +1,13 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.IO; -using System.Linq; using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Localisation; using osu.Framework.Screens; +using osu.Game.Database; namespace osu.Game.Overlays.Settings.Sections.Maintenance { @@ -13,18 +15,12 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance { private readonly TaskCompletionSource taskCompletionSource; + [Resolved] + private LegacyImportManager legacyImportManager { get; set; } = null!; + protected override OverlayActivation InitialOverlayActivationMode => OverlayActivation.Disabled; - protected override bool IsValidDirectory(DirectoryInfo? info) => - // A full stable installation will have a configuration file present. - // This is the best case scenario, as it may contain a custom beatmap directory we need to traverse to. - info?.GetFiles("osu!.*.cfg").Any() == true || - // The user may only have their songs or skins folders left. - // We still want to allow them to import based on this. - info?.GetDirectories("Songs").Any() == true || - info?.GetDirectories("Skins").Any() == true || - // The user may have traverse *inside* their songs or skins folders. - shouldUseParentDirectory(info); + protected override bool IsValidDirectory(DirectoryInfo? info) => legacyImportManager.IsUsableForStableImport(info, out _); public override LocalisableString HeaderText => "Please select your osu!stable install location"; @@ -35,7 +31,10 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance protected override void OnSelection(DirectoryInfo directory) { - taskCompletionSource.TrySetResult(shouldUseParentDirectory(directory) ? directory.Parent!.FullName : directory.FullName); + if (!legacyImportManager.IsUsableForStableImport(directory, out var stableRoot)) + throw new InvalidOperationException($@"{nameof(OnSelection)} was called on an invalid directory. This should never happen."); + + taskCompletionSource.TrySetResult(stableRoot.FullName); this.Exit(); } @@ -44,8 +43,5 @@ namespace osu.Game.Overlays.Settings.Sections.Maintenance taskCompletionSource.TrySetCanceled(); return base.OnExiting(e); } - - private bool shouldUseParentDirectory(DirectoryInfo? info) - => info?.Parent != null && (info.Name == "Songs" || info.Name == "Skins"); } } From acb7016156c7dd3411ed1d70154eafaab970e57e Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 15 Dec 2023 23:53:26 +0900 Subject: [PATCH 0516/1118] Remove unused using --- osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs index 2a3655eccd..d12020db59 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -15,7 +15,6 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Objects.Types; -using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Replays; using osu.Game.Rulesets.Replays; From 91f4123aa79b05c0a3e56a4025b54f993e001b98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 15 Dec 2023 16:00:29 +0100 Subject: [PATCH 0517/1118] Fix first run locator stable locator directory desyncing between display & popover --- .../FirstRunSetup/ScreenImportFromStable.cs | 45 ++++++++++++------- 1 file changed, 30 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs index 185a47c371..24ac5e72e8 100644 --- a/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs +++ b/osu.Game/Overlays/FirstRunSetup/ScreenImportFromStable.cs @@ -244,6 +244,8 @@ namespace osu.Game.Overlays.FirstRunSetup [Resolved(canBeNull: true)] // Can't really be null but required to handle potential of disposal before DI completes. private OsuGameBase? game { get; set; } + private bool changingDirectory; + protected override void LoadComplete() { base.LoadComplete(); @@ -259,24 +261,37 @@ namespace osu.Game.Overlays.FirstRunSetup private void onDirectorySelected(ValueChangedEvent directory) { - if (directory.NewValue == null) - { - Current.Value = string.Empty; + if (changingDirectory) return; + + try + { + changingDirectory = true; + + if (directory.NewValue == null) + { + Current.Value = string.Empty; + return; + } + + // DirectorySelectors can trigger a noop value changed, but `DirectoryInfo` equality doesn't catch this. + if (directory.OldValue?.FullName == directory.NewValue.FullName) + return; + + if (legacyImportManager.IsUsableForStableImport(directory.NewValue, out var stableRoot)) + { + this.HidePopover(); + + string path = stableRoot.FullName; + + legacyImportManager.UpdateStorage(path); + Current.Value = path; + currentDirectory.Value = stableRoot; + } } - - // DirectorySelectors can trigger a noop value changed, but `DirectoryInfo` equality doesn't catch this. - if (directory.OldValue?.FullName == directory.NewValue.FullName) - return; - - if (legacyImportManager.IsUsableForStableImport(directory.NewValue, out var stableRoot)) + finally { - this.HidePopover(); - - string path = stableRoot.FullName; - - legacyImportManager.UpdateStorage(path); - Current.Value = path; + changingDirectory = false; } } From 432ce275c4dad16cc1ad848bfbe4900445d0c8af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 15 Dec 2023 20:43:18 +0100 Subject: [PATCH 0518/1118] Explain magic constants better --- osu.Game.Rulesets.Mania/UI/ColumnFlow.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs index 3ecd14ce81..8734f8ac8a 100644 --- a/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs +++ b/osu.Game.Rulesets.Mania/UI/ColumnFlow.cs @@ -118,8 +118,9 @@ namespace osu.Game.Rulesets.Mania.UI float aspectRatio = containingCell.Value.X / containingCell.Value.Y; - // These numbers are based on mobile phones, aspect ~1.92. + // 2.83 is a mostly arbitrary scale-up (170 / 60, based on original implementation for argon) float mobileAdjust = 2.83f * Math.Min(1, 7f / stageDefinition.Columns); + // 1.92 is a "reference" mobile screen aspect ratio for phones. // We should scale it back for cases like tablets which aren't so extreme. mobileAdjust *= aspectRatio / 1.92f; From f07771f59b19c5ac3c9d9709bb32d3baf6fe4138 Mon Sep 17 00:00:00 2001 From: clayton Date: Fri, 15 Dec 2023 22:41:55 -0800 Subject: [PATCH 0519/1118] Fix fallback column colors for legacy split stage mania skins --- .../Skinning/Legacy/LegacyManiaColumnElement.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs index 7e3fb0438c..3a69142b3c 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Legacy/LegacyManiaColumnElement.cs @@ -34,7 +34,9 @@ namespace osu.Game.Rulesets.Mania.Skinning.Legacy FallbackColumnIndex = "S"; else { - int distanceToEdge = Math.Min(Column.Index, (stage.Columns - 1) - Column.Index); + // Account for cases like dual-stage (assume that all stages have the same column count for now). + int columnInStage = Column.Index % stage.Columns; + int distanceToEdge = Math.Min(columnInStage, (stage.Columns - 1) - columnInStage); FallbackColumnIndex = distanceToEdge % 2 == 0 ? "1" : "2"; } } From d7aca2f64143d75d11fb09f2acfbb10709396206 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 17 Dec 2023 19:27:03 +0900 Subject: [PATCH 0520/1118] Add IApplicableHealthProcessor --- .../Mods/IApplicableHealthProcessor.cs | 18 ++++++++++++++++++ osu.Game/Screens/Play/Player.cs | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Rulesets/Mods/IApplicableHealthProcessor.cs diff --git a/osu.Game/Rulesets/Mods/IApplicableHealthProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableHealthProcessor.cs new file mode 100644 index 0000000000..e16faa9595 --- /dev/null +++ b/osu.Game/Rulesets/Mods/IApplicableHealthProcessor.cs @@ -0,0 +1,18 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Mods +{ + /// + /// Interface for a that provides its own health processor. + /// + public interface IApplicableHealthProcessor + { + /// + /// Creates the . + /// + HealthProcessor CreateHealthProcessor(double drainStartTime); + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index 1c97efcff7..cc08079d88 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -227,7 +227,8 @@ namespace osu.Game.Screens.Play dependencies.CacheAs(ScoreProcessor); - HealthProcessor = ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); + HealthProcessor = gameplayMods.OfType().FirstOrDefault()?.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); + HealthProcessor ??= ruleset.CreateHealthProcessor(playableBeatmap.HitObjects[0].StartTime); HealthProcessor.ApplyBeatmap(playableBeatmap); dependencies.CacheAs(HealthProcessor); From 4b9aefa6f2d7b7ad5a2fdc31685a6a900c55d444 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 17 Dec 2023 19:33:02 +0900 Subject: [PATCH 0521/1118] Change osu ruleset to use new HP algorithm by default --- .../TestSceneOsuHealthProcessor.cs | 8 ++++---- osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs | 7 ++++++- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 -- ...{OsuHealthProcessor.cs => OsuLegacyHealthProcessor.cs} | 4 ++-- osu.Game/Rulesets/Mods/IApplicableHealthProcessor.cs | 4 ++-- 5 files changed, 14 insertions(+), 11 deletions(-) rename osu.Game.Rulesets.Osu/Scoring/{OsuHealthProcessor.cs => OsuLegacyHealthProcessor.cs} (95%) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs index 16f28c0212..83c818c20d 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestNoBreak() { - OsuHealthProcessor hp = new OsuHealthProcessor(-1000); + OsuLegacyHealthProcessor hp = new OsuLegacyHealthProcessor(-1000); hp.ApplyBeatmap(new Beatmap { HitObjects = @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestSingleBreak() { - OsuHealthProcessor hp = new OsuHealthProcessor(-1000); + OsuLegacyHealthProcessor hp = new OsuLegacyHealthProcessor(-1000); hp.ApplyBeatmap(new Beatmap { HitObjects = @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestOverlappingBreak() { - OsuHealthProcessor hp = new OsuHealthProcessor(-1000); + OsuLegacyHealthProcessor hp = new OsuLegacyHealthProcessor(-1000); hp.ApplyBeatmap(new Beatmap { HitObjects = @@ -72,7 +72,7 @@ namespace osu.Game.Rulesets.Osu.Tests [Test] public void TestSequentialBreak() { - OsuHealthProcessor hp = new OsuHealthProcessor(-1000); + OsuLegacyHealthProcessor hp = new OsuLegacyHealthProcessor(-1000); hp.ApplyBeatmap(new Beatmap { HitObjects = diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs index f20f95b384..10d7af5e58 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModClassic.cs @@ -18,7 +18,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModClassic : ModClassic, IApplicableToHitObject, IApplicableToDrawableHitObject, IApplicableToDrawableRuleset + public class OsuModClassic : ModClassic, IApplicableToHitObject, IApplicableToDrawableHitObject, IApplicableToDrawableRuleset, IApplicableHealthProcessor { public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModStrictTracking)).ToArray(); @@ -34,6 +34,9 @@ namespace osu.Game.Rulesets.Osu.Mods [SettingSource("Fade out hit circles earlier", "Make hit circles fade out into a miss, rather than after it.")] public Bindable FadeHitCircleEarly { get; } = new Bindable(true); + [SettingSource("Classic health", "More closely resembles the original HP drain mechanics.")] + public Bindable ClassicHealth { get; } = new Bindable(true); + private bool usingHiddenFading; public void ApplyToHitObject(HitObject hitObject) @@ -115,5 +118,7 @@ namespace osu.Game.Rulesets.Osu.Mods } }; } + + public HealthProcessor? CreateHealthProcessor(double drainStartTime) => ClassicHealth.Value ? new OsuLegacyHealthProcessor(drainStartTime) : null; } } diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 35cbfa3790..e53f20277b 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -48,8 +48,6 @@ namespace osu.Game.Rulesets.Osu public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(); - public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new OsuHealthProcessor(drainStartTime); - public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap, this); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuLegacyHealthProcessor.cs similarity index 95% rename from osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs rename to osu.Game.Rulesets.Osu/Scoring/OsuLegacyHealthProcessor.cs index 7025a7be65..e383e82b86 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuLegacyHealthProcessor.cs @@ -10,9 +10,9 @@ using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Osu.Scoring { - public partial class OsuHealthProcessor : LegacyDrainingHealthProcessor + public partial class OsuLegacyHealthProcessor : LegacyDrainingHealthProcessor { - public OsuHealthProcessor(double drainStartTime) + public OsuLegacyHealthProcessor(double drainStartTime) : base(drainStartTime) { } diff --git a/osu.Game/Rulesets/Mods/IApplicableHealthProcessor.cs b/osu.Game/Rulesets/Mods/IApplicableHealthProcessor.cs index e16faa9595..be46828069 100644 --- a/osu.Game/Rulesets/Mods/IApplicableHealthProcessor.cs +++ b/osu.Game/Rulesets/Mods/IApplicableHealthProcessor.cs @@ -11,8 +11,8 @@ namespace osu.Game.Rulesets.Mods public interface IApplicableHealthProcessor { /// - /// Creates the . + /// Creates the . May be null to use the ruleset default. /// - HealthProcessor CreateHealthProcessor(double drainStartTime); + HealthProcessor? CreateHealthProcessor(double drainStartTime); } } From 10610d3387acd1863c406723d20e9557459def66 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 17 Dec 2023 19:33:38 +0900 Subject: [PATCH 0522/1118] Rename test scene --- ...uHealthProcessor.cs => TestSceneOsuLegacyHealthProcessor.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Rulesets.Osu.Tests/{TestSceneOsuHealthProcessor.cs => TestSceneOsuLegacyHealthProcessor.cs} (98%) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuLegacyHealthProcessor.cs similarity index 98% rename from osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs rename to osu.Game.Rulesets.Osu.Tests/TestSceneOsuLegacyHealthProcessor.cs index 83c818c20d..a7ae06a9ce 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneOsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneOsuLegacyHealthProcessor.cs @@ -10,7 +10,7 @@ using osu.Game.Rulesets.Osu.Scoring; namespace osu.Game.Rulesets.Osu.Tests { [TestFixture] - public class TestSceneOsuHealthProcessor + public class TestSceneOsuLegacyHealthProcessor { [Test] public void TestNoBreak() From f77884b62f0a8c2b8b058c9e1180c21bd25cbdba Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 17 Dec 2023 19:57:48 +0900 Subject: [PATCH 0523/1118] Only hit passed-through ticks if none were missed --- .../TestSceneSliderLateHitJudgement.cs | 23 ++------------- .../Objects/Drawables/SliderInputManager.cs | 29 ++++++++++++++----- 2 files changed, 25 insertions(+), 27 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs index d12020db59..54140e4d49 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -220,7 +220,7 @@ namespace osu.Game.Rulesets.Osu.Tests /// If any hitobject does not meet this criteria, ALL hitobjects after that one should be missed. /// [Test] - public void TestHitLateInRangeDoesNotHitAfterAnyOutOfRange() + public void TestHitLateDoesNotHitTicksIfAnyOutOfRange() { performTest(new List { @@ -241,25 +241,8 @@ namespace osu.Game.Rulesets.Osu.Tests assertHeadJudgement(HitResult.Meh); - // The first few ticks that are in the follow range of the head should be hit. - assertTickJudgement(0, HitResult.LargeTickHit); // This tick is hidden under the slider head :( - assertTickJudgement(1, HitResult.LargeTickHit); - assertTickJudgement(2, HitResult.LargeTickHit); - - // Every other tick should be missed - assertTickJudgement(3, HitResult.LargeTickMiss); - assertTickJudgement(4, HitResult.LargeTickMiss); - assertTickJudgement(5, HitResult.LargeTickMiss); - assertTickJudgement(6, HitResult.LargeTickMiss); - assertTickJudgement(7, HitResult.LargeTickMiss); - assertTickJudgement(8, HitResult.LargeTickMiss); - assertTickJudgement(9, HitResult.LargeTickMiss); - assertTickJudgement(10, HitResult.LargeTickMiss); - - // In particular, these three are in the follow range of the head, but should not be hit - // because the slider was at some point outside the follow range of the head. - assertTickJudgement(11, HitResult.LargeTickMiss); - assertTickJudgement(12, HitResult.LargeTickMiss); + // At least one tick was out of range, so they all should be missed. + assertAllTickJudgements(HitResult.LargeTickMiss); // This particular test actually starts tracking the slider just before the end, so the tail should be hit because of its leniency. assertTailJudgement(HitResult.LargeTickHit); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs index 9feeb6ef14..e71f4fcd48 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs @@ -80,10 +80,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Vector2 mousePositionInSlider = slider.ToLocalSpace(screenSpaceMousePosition.Value) - slider.OriginPosition; - // When the head is hit and the mouse is in the expanded follow area, force a hit on every nested hitobject - // from the start of the slider that is within the follow area. + // When the head is hit late: + // - If the cursor has at all times been within range of the expanded follow area, hit all nested objects that have been passed through. + // - If the cursor has at some point left the expanded follow area, miss those nested objects instead. - bool forceMiss = false; + bool allTicksInRange = true; foreach (var nested in slider.NestedHitObjects.OfType()) { @@ -102,13 +103,27 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // When the first nested object that is further outside the follow area is reached, // forcefully miss all other nested objects that would otherwise be valid to be hit. // This covers a case of a slider overlapping itself that requires tracking to a tick on an outer edge. - if (forceMiss || (objectPosition - mousePositionInSlider).LengthSquared > radius * radius) + if ((objectPosition - mousePositionInSlider).LengthSquared > radius * radius) { - nested.MissForcefully(); - forceMiss = true; + allTicksInRange = false; + break; } - else + } + + foreach (var nested in slider.NestedHitObjects.OfType()) + { + // Skip nested objects that are already judged. + if (nested.Judged) + continue; + + // Stop the process when a nested object is reached that can't be hit before the current time. + if (nested.HitObject.StartTime > Time.Current) + break; + + if (allTicksInRange) nested.HitForcefully(); + else + nested.MissForcefully(); } // Enable tracking, since the mouse is within the follow area (if it were expanded). From fbe48d7be84874d902d5559f723379bc5c9c395a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 17 Dec 2023 20:11:15 +0900 Subject: [PATCH 0524/1118] Fix tail being missed too early --- osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs index e71f4fcd48..996a477153 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (slider.Tracking.Value) nestedObject.HitForcefully(); - else + else if (timeOffset >= 0) nestedObject.MissForcefully(); } From 9b02bd712b19b34bd8d3813ce9a2c700063267c7 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 17 Dec 2023 20:12:02 +0900 Subject: [PATCH 0525/1118] Only track if in slider ball after any ticks missed --- .../TestSceneSliderLateHitJudgement.cs | 31 +++++++++++++++++++ .../Objects/Drawables/SliderInputManager.cs | 6 ++-- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs index 54140e4d49..78633369c7 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -280,6 +280,37 @@ namespace osu.Game.Rulesets.Osu.Tests assertSliderJudgement(HitResult.IgnoreHit); } + /// + /// Same as except the tracking is limited to the ball + /// because the tick was missed. + /// + [Test] + public void TestHitLateInRangeDoesNotHitOutOfRangeTickAndTrackingLimitedToBall() + { + performTest(new List + { + new OsuReplayFrame(time_slider_start + 150, slider_start_position, OsuAction.LeftButton), + new OsuReplayFrame(time_slider_end + 150, slider_start_position, OsuAction.LeftButton), + }, s => + { + s.Path = new SliderPath(PathType.PERFECT_CURVE, new[] + { + Vector2.Zero, + new Vector2(50, 50), + new Vector2(20, 0), + }); + + s.TickDistanceMultiplier = 0.25f; + s.SliderVelocityMultiplier = 3; + }); + + assertHeadJudgement(HitResult.Meh); + assertTickJudgement(0, HitResult.LargeTickMiss); + assertTickJudgement(1, HitResult.LargeTickMiss); + assertTailJudgement(HitResult.LargeTickHit); + assertSliderJudgement(HitResult.IgnoreHit); + } + /// /// If the head circle is hit and the mouse is in range of the follow circle, /// then a tick not within the follow radius from the cursor position should not be hit. diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs index 996a477153..a497c04894 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs @@ -126,8 +126,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables nested.MissForcefully(); } - // Enable tracking, since the mouse is within the follow area (if it were expanded). - updateTracking(true); + // If all ticks were hit so far, enable tracking the full extent. + // If any ticks were missed, assume tracking would've broken at some point, and should only activate if the cursor is within the slider ball. + // For the second case, this may be the last chance we have to enable tracking before other objects get judged, otherwise the same would normally happen via Update(). + updateTracking(allTicksInRange || isMouseInFollowArea(false)); } public void TryJudgeNestedObject(DrawableOsuHitObject nestedObject, double timeOffset) From fddfa33e4982a87b2b27302c0dcd057526827412 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 17 Dec 2023 20:19:17 +0900 Subject: [PATCH 0526/1118] Fix 1-frame issues due to referencing external value --- osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs index a497c04894..d698ba56c4 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs @@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!slider.HeadCircle.Judged) { - if (slider.Tracking.Value) + if (Tracking) { // Attempt to preserve correct ordering of judgements as best we can by forcing an un-judged head to be missed when the user has clearly skipped it. slider.HeadCircle.MissForcefully(); @@ -175,7 +175,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } } - if (slider.Tracking.Value) + if (Tracking) nestedObject.HitForcefully(); else if (timeOffset >= 0) nestedObject.MissForcefully(); From 2b33aec12418e17f462730a4542f81e0d68cfe57 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 17 Dec 2023 21:26:48 +0900 Subject: [PATCH 0527/1118] Require slider head to be judged before ticks --- .../TestSceneSliderLateHitJudgement.cs | 80 ++++++++++++++++--- .../Objects/Drawables/SliderInputManager.cs | 13 +-- 2 files changed, 68 insertions(+), 25 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs index 78633369c7..e1797e877e 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -341,6 +341,45 @@ namespace osu.Game.Rulesets.Osu.Tests assertSliderJudgement(HitResult.IgnoreHit); } + /// + /// Late hit and release on each slider head of a slider stream. + /// + [Test] + public void TestLateHitSliderStream() + { + var beatmap = new Beatmap(); + + for (int i = 0; i < 20; i++) + { + beatmap.HitObjects.Add(new Slider + { + StartTime = time_slider_start + 75 * i, // 200BPM @ 1/4 + Position = new Vector2(256 - slider_path_length / 2, 192), + TickDistanceMultiplier = 3, + Path = new SliderPath(PathType.LINEAR, new[] + { + Vector2.Zero, + new Vector2(20, 0), + }), + }); + } + + var replay = new List(); + + for (int i = 0; i < 20; i++) + { + replay.Add(new OsuReplayFrame(time_slider_start + 75 * i + 75, slider_start_position, i % 2 == 0 ? OsuAction.LeftButton : OsuAction.RightButton)); + replay.Add(new OsuReplayFrame(time_slider_start + 75 * i + 140, slider_start_position)); + } + + performTest(replay, beatmap); + + assertHeadJudgement(HitResult.Meh); + assertTickJudgement(0, HitResult.LargeTickMiss); + assertTailJudgement(HitResult.IgnoreMiss); + assertSliderJudgement(HitResult.IgnoreHit); + } + private void assertHeadJudgement(HitResult result) { AddAssert( @@ -406,21 +445,36 @@ namespace osu.Game.Rulesets.Osu.Tests adjustSliderFunc?.Invoke(slider); + var beatmap = new Beatmap + { + HitObjects = { slider }, + BeatmapInfo = + { + Difficulty = new BeatmapDifficulty + { + SliderMultiplier = 4, + SliderTickRate = 3 + }, + Ruleset = new OsuRuleset().RulesetInfo, + } + }; + + performTest(frames, beatmap); + } + + private void performTest(List frames, Beatmap beatmap) + { + beatmap.BeatmapInfo.Ruleset = new OsuRuleset().RulesetInfo; + beatmap.BeatmapInfo.StackLeniency = 0; + beatmap.BeatmapInfo.Difficulty = new BeatmapDifficulty + { + SliderMultiplier = 4, + SliderTickRate = 3, + }; + AddStep("load player", () => { - Beatmap.Value = CreateWorkingBeatmap(new Beatmap - { - HitObjects = { slider }, - BeatmapInfo = - { - Difficulty = new BeatmapDifficulty - { - SliderMultiplier = 4, - SliderTickRate = 3 - }, - Ruleset = new OsuRuleset().RulesetInfo, - } - }); + Beatmap.Value = CreateWorkingBeatmap(beatmap); var p = new ScoreAccessibleReplayPlayer(new Score { Replay = new Replay { Frames = frames } }); diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs index d698ba56c4..8aa982783e 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs @@ -162,18 +162,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } if (!slider.HeadCircle.Judged) - { - if (Tracking) - { - // Attempt to preserve correct ordering of judgements as best we can by forcing an un-judged head to be missed when the user has clearly skipped it. - slider.HeadCircle.MissForcefully(); - } - else - { - // Don't judge this object as a miss before the head has been judged, to allow the head to be hit late. - return; - } - } + return; if (Tracking) nestedObject.HitForcefully(); From 04d542105f1518119f18f7d3b160802d7d468e67 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 17 Dec 2023 21:52:21 +0900 Subject: [PATCH 0528/1118] Fix assertions --- .../TestSceneSliderLateHitJudgement.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs index e1797e877e..6ba9c723da 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -374,10 +374,10 @@ namespace osu.Game.Rulesets.Osu.Tests performTest(replay, beatmap); - assertHeadJudgement(HitResult.Meh); - assertTickJudgement(0, HitResult.LargeTickMiss); - assertTailJudgement(HitResult.IgnoreMiss); - assertSliderJudgement(HitResult.IgnoreHit); + AddAssert( + $"all heads = {HitResult.Ok}", + () => judgementResults.Where(r => r.HitObject is SliderHeadCircle).Select(r => r.Type), + () => Has.All.EqualTo(HitResult.Ok)); } private void assertHeadJudgement(HitResult result) From 30116512ca2dcd61a63813cab04cfc9d9bacb734 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Mon, 18 Dec 2023 12:01:51 +0900 Subject: [PATCH 0529/1118] Populate MaxCombo scoring attrib for non-osu rulesets --- .../Difficulty/CatchLegacyScoreSimulator.cs | 1 + .../Difficulty/ManiaLegacyScoreSimulator.cs | 6 +++++- .../Difficulty/TaikoLegacyScoreSimulator.cs | 1 + 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs index ed27e11208..7a84d9245d 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs @@ -75,6 +75,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty attributes.BonusScoreRatio = legacyBonusScore == 0 ? 0 : (double)standardisedBonusScore / legacyBonusScore; attributes.BonusScore = legacyBonusScore; + attributes.MaxCombo = combo; return attributes; } diff --git a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs index ddb4b868a3..d9fd96ac6a 100644 --- a/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Mania/Difficulty/ManiaLegacyScoreSimulator.cs @@ -15,7 +15,11 @@ namespace osu.Game.Rulesets.Mania.Difficulty { public LegacyScoreAttributes Simulate(IWorkingBeatmap workingBeatmap, IBeatmap playableBeatmap) { - return new LegacyScoreAttributes { ComboScore = 1000000 }; + return new LegacyScoreAttributes + { + ComboScore = 1000000, + MaxCombo = 0 // Max combo is mod-dependent, so any value here is insufficient. + }; } public double GetLegacyScoreMultiplier(IReadOnlyList mods, LegacyBeatmapConversionDifficultyInfo difficulty) diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index 1db44592b8..a8ed056c89 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -76,6 +76,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty attributes.BonusScoreRatio = legacyBonusScore == 0 ? 0 : (double)standardisedBonusScore / legacyBonusScore; attributes.BonusScore = legacyBonusScore; + attributes.MaxCombo = combo; return attributes; } From f84c1815734d5aa7a921a0feacc73611f1568c51 Mon Sep 17 00:00:00 2001 From: clayton Date: Sun, 17 Dec 2023 23:47:50 -0800 Subject: [PATCH 0530/1118] Don't convert TaikoModRandom to/from legacy mods --- .../TaikoLegacyModConversionTest.cs | 1 - osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs index 5f7a78ddf1..c15dc17ae4 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoLegacyModConversionTest.cs @@ -25,7 +25,6 @@ namespace osu.Game.Rulesets.Taiko.Tests new object[] { LegacyMods.HalfTime, new[] { typeof(TaikoModHalfTime) } }, new object[] { LegacyMods.Flashlight, new[] { typeof(TaikoModFlashlight) } }, new object[] { LegacyMods.Autoplay, new[] { typeof(TaikoModAutoplay) } }, - new object[] { LegacyMods.Random, new[] { typeof(TaikoModRandom) } }, new object[] { LegacyMods.HardRock | LegacyMods.DoubleTime, new[] { typeof(TaikoModHardRock), typeof(TaikoModDoubleTime) } }, new object[] { LegacyMods.ScoreV2, new[] { typeof(ModScoreV2) } }, }; diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index 9d34a34fce..ad3b7b09f7 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -115,23 +115,10 @@ namespace osu.Game.Rulesets.Taiko if (mods.HasFlagFast(LegacyMods.Relax)) yield return new TaikoModRelax(); - if (mods.HasFlagFast(LegacyMods.Random)) - yield return new TaikoModRandom(); - if (mods.HasFlagFast(LegacyMods.ScoreV2)) yield return new ModScoreV2(); } - public override LegacyMods ConvertToLegacyMods(Mod[] mods) - { - var value = base.ConvertToLegacyMods(mods); - - if (mods.OfType().Any()) - value |= LegacyMods.Random; - - return value; - } - public override IEnumerable GetModsFor(ModType type) { switch (type) From ef230884a87a51a25b9380193a13b94ff8144ac0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Dec 2023 09:03:01 +0100 Subject: [PATCH 0531/1118] Fix collections dropdown crashing during storage migration Closes https://github.com/ppy/osu/issues/25815. `CollectionDropdown.collectionsChanged()` was assuming that if it received `null` changes, then it must mean that the change subscription is being initialised and the `filters` list will not contain any items. However, that is not the only circumstance wherein a realm subscription can fire with `null` changes; that can also happen after the main realm instance gets recycled via the notification registration flow: https://github.com/ppy/osu/blob/2f28a92f0a03c8aed6fac2ec7fb41f7ed865f798/osu.Game/Database/RealmAccess.cs#L545-L549 https://github.com/ppy/osu/blob/2f28a92f0a03c8aed6fac2ec7fb41f7ed865f798/osu.Game/Database/RealmAccess.cs#L1228-L1251 Therefore, to fix the crash, just ensure that the list is cleared every time. --- osu.Game/Collections/CollectionDropdown.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Collections/CollectionDropdown.cs b/osu.Game/Collections/CollectionDropdown.cs index e43d8f4b02..8d83ed3ec9 100644 --- a/osu.Game/Collections/CollectionDropdown.cs +++ b/osu.Game/Collections/CollectionDropdown.cs @@ -66,6 +66,7 @@ namespace osu.Game.Collections { if (changes == null) { + filters.Clear(); filters.Add(allBeatmapsItem); filters.AddRange(collections.Select(c => new CollectionFilterMenuItem(c.ToLive(realm)))); if (ShowManageCollectionsItem) From 7462a9f4ab84997dfd550f8b3025f5630c72beab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 18:17:47 +0900 Subject: [PATCH 0532/1118] Add helper method to handle progress notifications for background jobs --- osu.Game/BackgroundDataStoreProcessor.cs | 51 +++++++++++++++++++----- 1 file changed, 41 insertions(+), 10 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 10cc13dc29..794d534f10 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -279,20 +279,17 @@ namespace osu.Game if (scoreIds.Count == 0) return; - ProgressNotification notification = new ProgressNotification { State = ProgressNotificationState.Active }; - - notificationOverlay?.Post(notification); + var notification = showProgressNotification("Upgrading scores to new scoring algorithm", "scores have been upgraded to the new scoring algorithm"); int processedCount = 0; int failedCount = 0; foreach (var id in scoreIds) { - if (notification.State == ProgressNotificationState.Cancelled) + if (notification?.State == ProgressNotificationState.Cancelled) break; - notification.Text = $"Upgrading scores to new scoring algorithm ({processedCount} of {scoreIds.Count})"; - notification.Progress = (float)processedCount / scoreIds.Count; + updateNotificationProgress(notification, processedCount, scoreIds.Count); sleepIfRequired(); @@ -325,24 +322,58 @@ namespace osu.Game } } - if (processedCount == scoreIds.Count) + completeNotification(notification, processedCount, scoreIds.Count, failedCount); + } + + private void updateNotificationProgress(ProgressNotification? notification, int processedCount, int totalCount) + { + if (notification == null) + return; + + notification.Text = notification.Text.ToString().Split('(').First().TrimEnd() + $" ({processedCount} of {totalCount})"; + notification.Progress = (float)processedCount / totalCount; + } + + private void completeNotification(ProgressNotification? notification, int processedCount, int totalCount, int? failedCount = null) + { + if (notification == null) + return; + + if (processedCount == totalCount) { - notification.CompletionText = $"{processedCount} score(s) have been upgraded to the new scoring algorithm"; + notification.CompletionText = $"{processedCount} {notification.CompletionText}"; notification.Progress = 1; notification.State = ProgressNotificationState.Completed; } else { - notification.Text = $"{processedCount} of {scoreIds.Count} score(s) have been upgraded to the new scoring algorithm."; + notification.Text = $"{processedCount} of {totalCount} {notification.CompletionText}"; // We may have arrived here due to user cancellation or completion with failures. if (failedCount > 0) - notification.Text += $" Check logs for issues with {failedCount} failed upgrades."; + notification.Text += $" Check logs for issues with {failedCount} failed items."; notification.State = ProgressNotificationState.Cancelled; } } + private ProgressNotification? showProgressNotification(string running, string completed) + { + if (notificationOverlay == null) + return null; + + ProgressNotification notification = new ProgressNotification + { + Text = running, + CompletionText = completed, + State = ProgressNotificationState.Active + }; + + notificationOverlay?.Post(notification); + + return notification; + } + private void sleepIfRequired() { while (localUserPlayInfo?.IsPlaying.Value == true) From e7d1cf7868663126479d2997b7d0da3062817072 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 18:22:40 +0900 Subject: [PATCH 0533/1118] Add progress notifications for background tasks which don't already have them --- osu.Game/BackgroundDataStoreProcessor.cs | 60 +++++++++++++++++++++--- 1 file changed, 54 insertions(+), 6 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 794d534f10..58b1c912c4 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -144,12 +144,24 @@ namespace osu.Game } }); + if (beatmapSetIds.Count == 0) + return; + Logger.Log($"Found {beatmapSetIds.Count} beatmap sets which require reprocessing."); - int i = 0; + // Technically this is doing more than just star ratings, but easier for the end user to understand. + var notification = showProgressNotification("Reprocessing star rating for beatmaps", "beatmaps' star ratings have been updated"); + + int processedCount = 0; + int failedCount = 0; foreach (var id in beatmapSetIds) { + if (notification?.State == ProgressNotificationState.Cancelled) + break; + + updateNotificationProgress(notification, processedCount, beatmapSetIds.Count); + sleepIfRequired(); realmAccess.Run(r => @@ -160,16 +172,19 @@ namespace osu.Game { try { - Logger.Log($"Background processing {set} ({++i} / {beatmapSetIds.Count})"); beatmapUpdater.Process(set); + ++processedCount; } catch (Exception e) { Logger.Log($"Background processing failed on {set}: {e}"); + ++failedCount; } } }); } + + completeNotification(notification, processedCount, beatmapSetIds.Count, failedCount); } private void processBeatmapsWithMissingObjectCounts() @@ -184,12 +199,23 @@ namespace osu.Game beatmapIds.Add(b.ID); }); - Logger.Log($"Found {beatmapIds.Count} beatmaps which require reprocessing."); + if (beatmapIds.Count == 0) + return; - int i = 0; + Logger.Log($"Found {beatmapIds.Count} beatmaps which require statistics population."); + + var notification = showProgressNotification("Populating missing statistics for beatmaps", "beatmaps have been populated with missing statistics"); + + int processedCount = 0; + int failedCount = 0; foreach (var id in beatmapIds) { + if (notification?.State == ProgressNotificationState.Cancelled) + break; + + updateNotificationProgress(notification, processedCount, beatmapIds.Count); + sleepIfRequired(); realmAccess.Run(r => @@ -200,16 +226,19 @@ namespace osu.Game { try { - Logger.Log($"Background processing {beatmap} ({++i} / {beatmapIds.Count})"); beatmapUpdater.ProcessObjectCounts(beatmap); + ++processedCount; } catch (Exception e) { Logger.Log($"Background processing failed on {beatmap}: {e}"); + ++failedCount; } } }); } + + completeNotification(notification, processedCount, beatmapIds.Count, failedCount); } private void processScoresWithMissingStatistics() @@ -231,10 +260,23 @@ namespace osu.Game } }); - Logger.Log($"Found {scoreIds.Count} scores which require reprocessing."); + if (scoreIds.Count == 0) + return; + + Logger.Log($"Found {scoreIds.Count} scores which require statistics population."); + + var notification = showProgressNotification("Populating missing statistics for scores", "scores have been populated with missing statistics"); + + int processedCount = 0; + int failedCount = 0; foreach (var id in scoreIds) { + if (notification?.State == ProgressNotificationState.Cancelled) + break; + + updateNotificationProgress(notification, processedCount, scoreIds.Count); + sleepIfRequired(); try @@ -251,6 +293,7 @@ namespace osu.Game }); Logger.Log($"Populated maximum statistics for score {id}"); + ++processedCount; } catch (ObjectDisposedException) { @@ -260,8 +303,11 @@ namespace osu.Game { Logger.Log(@$"Failed to populate maximum statistics for {id}: {e}"); realmAccess.Write(r => r.Find(id)!.BackgroundReprocessingFailed = true); + ++failedCount; } } + + completeNotification(notification, processedCount, scoreIds.Count, failedCount); } private void convertLegacyTotalScoreToStandardised() @@ -332,6 +378,8 @@ namespace osu.Game notification.Text = notification.Text.ToString().Split('(').First().TrimEnd() + $" ({processedCount} of {totalCount})"; notification.Progress = (float)processedCount / totalCount; + + // TODO add log output } private void completeNotification(ProgressNotification? notification, int processedCount, int totalCount, int? failedCount = null) From bfa90e9dcb73f0c8687a184e4b91b95d2d98d72f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 18:34:55 +0900 Subject: [PATCH 0534/1118] Also populate `ObjectCount`s when running a full beatmap process Saves running things twice on an old install --- osu.Game/BackgroundDataStoreProcessor.cs | 1 + osu.Game/Beatmaps/BeatmapUpdater.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 58b1c912c4..38dc170ccc 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -67,6 +67,7 @@ namespace osu.Game checkForOutdatedStarRatings(); processBeatmapSetsWithMissingMetrics(); + // Note that the previous method will also update these on a fresh run. processBeatmapsWithMissingObjectCounts(); processScoresWithMissingStatistics(); convertLegacyTotalScoreToStandardised(); diff --git a/osu.Game/Beatmaps/BeatmapUpdater.cs b/osu.Game/Beatmaps/BeatmapUpdater.cs index 27492b8bac..e897d28916 100644 --- a/osu.Game/Beatmaps/BeatmapUpdater.cs +++ b/osu.Game/Beatmaps/BeatmapUpdater.cs @@ -77,6 +77,8 @@ namespace osu.Game.Beatmaps beatmap.StarRating = calculator.Calculate().StarRating; beatmap.Length = working.Beatmap.CalculatePlayableLength(); beatmap.BPM = 60000 / working.Beatmap.GetMostCommonBeatLength(); + beatmap.EndTimeObjectCount = working.Beatmap.HitObjects.Count(h => h is IHasDuration); + beatmap.TotalObjectCount = working.Beatmap.HitObjects.Count; } // And invalidate again afterwards as re-fetching the most up-to-date database metadata will be required. From 5ab38151239d39d4feec8b2f84d92e651c992a60 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 18:41:36 +0900 Subject: [PATCH 0535/1118] Add logging of progress every so often --- osu.Game/BackgroundDataStoreProcessor.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 38dc170ccc..6a801ab5bf 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -380,7 +380,8 @@ namespace osu.Game notification.Text = notification.Text.ToString().Split('(').First().TrimEnd() + $" ({processedCount} of {totalCount})"; notification.Progress = (float)processedCount / totalCount; - // TODO add log output + if (processedCount % 100 == 0) + Logger.Log(notification.Text.ToString()); } private void completeNotification(ProgressNotification? notification, int processedCount, int totalCount, int? failedCount = null) From e3251b40b311e504c6bb22e75e1b1ab347704714 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 19:00:44 +0900 Subject: [PATCH 0536/1118] Fix progress notifications queueing up infinite text changes when not visible --- osu.Game/Overlays/Notifications/ProgressNotification.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Notifications/ProgressNotification.cs b/osu.Game/Overlays/Notifications/ProgressNotification.cs index 6ea032213e..2362cb11f6 100644 --- a/osu.Game/Overlays/Notifications/ProgressNotification.cs +++ b/osu.Game/Overlays/Notifications/ProgressNotification.cs @@ -54,7 +54,7 @@ namespace osu.Game.Overlays.Notifications set { text = value; - Schedule(() => textDrawable.Text = text); + Scheduler.AddOnce(t => textDrawable.Text = t, text); } } From 62444c3d0423284daf1f8511977cd8d8f93fab6b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 19:17:59 +0900 Subject: [PATCH 0537/1118] Fix song select's carousel scroll position getting reset on background processing This only happened for users using absolute right-click scroll. --- osu.Game/Graphics/Containers/OsuScrollContainer.cs | 10 +++++----- .../Graphics/Containers/UserTrackingScrollContainer.cs | 7 +++++++ 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/osu.Game/Graphics/Containers/OsuScrollContainer.cs b/osu.Game/Graphics/Containers/OsuScrollContainer.cs index da6996c170..124becc35a 100644 --- a/osu.Game/Graphics/Containers/OsuScrollContainer.cs +++ b/osu.Game/Graphics/Containers/OsuScrollContainer.cs @@ -44,9 +44,6 @@ namespace osu.Game.Graphics.Containers private bool shouldPerformRightMouseScroll(MouseButtonEvent e) => RightMouseScrollbar && e.Button == MouseButton.Right; - private void scrollFromMouseEvent(MouseEvent e) => - ScrollTo(Clamp(ToLocalSpace(e.ScreenSpaceMousePosition)[ScrollDim] / DrawSize[ScrollDim]) * Content.DrawSize[ScrollDim], true, DistanceDecayOnRightMouseScrollbar); - private bool rightMouseDragging; protected override bool IsDragging => base.IsDragging || rightMouseDragging; @@ -80,7 +77,7 @@ namespace osu.Game.Graphics.Containers { if (shouldPerformRightMouseScroll(e)) { - scrollFromMouseEvent(e); + ScrollFromMouseEvent(e); return true; } @@ -91,7 +88,7 @@ namespace osu.Game.Graphics.Containers { if (rightMouseDragging) { - scrollFromMouseEvent(e); + ScrollFromMouseEvent(e); return; } @@ -129,6 +126,9 @@ namespace osu.Game.Graphics.Containers return base.OnScroll(e); } + protected virtual void ScrollFromMouseEvent(MouseEvent e) => + ScrollTo(Clamp(ToLocalSpace(e.ScreenSpaceMousePosition)[ScrollDim] / DrawSize[ScrollDim]) * Content.DrawSize[ScrollDim], true, DistanceDecayOnRightMouseScrollbar); + protected override ScrollbarContainer CreateScrollbar(Direction direction) => new OsuScrollbar(direction); protected partial class OsuScrollbar : ScrollbarContainer diff --git a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs index 6934c95385..354a57b7d2 100644 --- a/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs +++ b/osu.Game/Graphics/Containers/UserTrackingScrollContainer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Graphics; +using osu.Framework.Input.Events; namespace osu.Game.Graphics.Containers { @@ -46,6 +47,12 @@ namespace osu.Game.Graphics.Containers base.ScrollIntoView(target, animated); } + protected override void ScrollFromMouseEvent(MouseEvent e) + { + UserScrolling = true; + base.ScrollFromMouseEvent(e); + } + public new void ScrollTo(float value, bool animated = true, double? distanceDecay = null) { UserScrolling = false; From 03ac2c30949c66900e04a67250ff21e2f34f2dfe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 19:20:57 +0900 Subject: [PATCH 0538/1118] Remove some excessive logging --- osu.Game/BackgroundDataStoreProcessor.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 6a801ab5bf..55be7f2c9e 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -293,7 +293,6 @@ namespace osu.Game r.Find(id)!.MaximumStatisticsJson = JsonConvert.SerializeObject(score.MaximumStatistics); }); - Logger.Log($"Populated maximum statistics for score {id}"); ++processedCount; } catch (ObjectDisposedException) @@ -354,7 +353,6 @@ namespace osu.Game s.TotalScoreVersion = LegacyScoreEncoder.LATEST_VERSION; }); - Logger.Log($"Converted total score for score {id}"); ++processedCount; } catch (ObjectDisposedException) From 32cc3f9ef74594875f9049bbe7f5bdea901cb1e5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 20:00:57 +0900 Subject: [PATCH 0539/1118] Combine multiple similar invalidation logic into single event --- osu.Game/Screens/Select/BeatmapCarousel.cs | 33 ++++++++-------------- 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index eb47a7201a..19ade780e1 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -168,7 +168,7 @@ namespace osu.Game.Screens.Select applyActiveCriteria(false); if (loadedTestBeatmaps) - signalBeatmapsLoaded(); + invalidateAfterChange(true); // Restore selection if (selectedBeatmapBefore != null && newRoot.BeatmapSetsByID.TryGetValue(selectedBeatmapBefore.BeatmapSet!.ID, out var newSelectionCandidates)) @@ -298,7 +298,8 @@ namespace osu.Game.Screens.Select removeBeatmapSet(id); } - signalBeatmapsLoaded(); + invalidateAfterChange(!BeatmapSetsLoaded); + BeatmapSetsLoaded = true; return; } @@ -393,12 +394,7 @@ namespace osu.Game.Screens.Select root.RemoveItem(set); } - itemsCache.Invalidate(); - - if (!Scroll.UserScrolling) - ScrollToSelected(true); - - BeatmapSetsChanged?.Invoke(); + invalidateAfterChange(true); }); public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => @@ -465,12 +461,7 @@ namespace osu.Game.Screens.Select } } - itemsCache.Invalidate(); - - if (!Scroll.UserScrolling) - ScrollToSelected(true); - - BeatmapSetsChanged?.Invoke(); + invalidateAfterChange(true); }); /// @@ -748,15 +739,15 @@ namespace osu.Game.Screens.Select } } - private void signalBeatmapsLoaded() + private void invalidateAfterChange(bool invokeSetsChangedEvent) { - if (!BeatmapSetsLoaded) - { - BeatmapSetsChanged?.Invoke(); - BeatmapSetsLoaded = true; - } - itemsCache.Invalidate(); + + if (!Scroll.UserScrolling) + ScrollToSelected(true); + + if (invokeSetsChangedEvent) + BeatmapSetsChanged?.Invoke(); } private float? scrollTarget; From 87b7699fcc29ab7e70ec1f632a0341bbe08a23b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 20:09:09 +0900 Subject: [PATCH 0540/1118] Avoid calling invalidation logic per beatmap set updated Realm will batch the updates. We don't want to do expensive operations per set when we don't need to. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 33 +++++++++++++++------- 1 file changed, 23 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 19ade780e1..63654fd36a 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -289,7 +289,7 @@ namespace osu.Game.Screens.Select foreach (var id in realmSets) { if (!root.BeatmapSetsByID.ContainsKey(id)) - UpdateBeatmapSet(realm.Realm.Find(id)!.Detach()); + updateBeatmapSet(realm.Realm.Find(id)!.Detach()); } foreach (var id in root.BeatmapSetsByID.Keys) @@ -304,10 +304,10 @@ namespace osu.Game.Screens.Select } foreach (int i in changes.NewModifiedIndices) - UpdateBeatmapSet(sender[i].Detach()); + updateBeatmapSet(sender[i].Detach()); foreach (int i in changes.InsertedIndices) - UpdateBeatmapSet(sender[i].Detach()); + updateBeatmapSet(sender[i].Detach()); if (changes.DeletedIndices.Length > 0 && SelectedBeatmapInfo != null) { @@ -348,6 +348,8 @@ namespace osu.Game.Screens.Select SelectBeatmap(sender[modifiedAndInserted.First()].Beatmaps.First()); } } + + invalidateAfterChange(!BeatmapSetsLoaded); } private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes) @@ -356,6 +358,8 @@ namespace osu.Game.Screens.Select if (changes == null) return; + bool changed = false; + foreach (int i in changes.InsertedIndices) { var beatmapInfo = sender[i]; @@ -368,17 +372,24 @@ namespace osu.Game.Screens.Select if (root.BeatmapSetsByID.TryGetValue(beatmapSet.ID, out var existingSets) && existingSets.SelectMany(s => s.Beatmaps).All(b => b.BeatmapInfo.ID != beatmapInfo.ID)) { - UpdateBeatmapSet(beatmapSet.Detach()); + updateBeatmapSet(beatmapSet.Detach()); + changed = true; } } + + if (changed) + invalidateAfterChange(true); } private IQueryable getBeatmapSets(Realm realm) => realm.All().Where(s => !s.DeletePending && !s.Protected); - public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => + public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => + { removeBeatmapSet(beatmapSet.ID); + invalidateAfterChange(true); + }); - private void removeBeatmapSet(Guid beatmapSetID) => Schedule(() => + private void removeBeatmapSet(Guid beatmapSetID) { if (!root.BeatmapSetsByID.TryGetValue(beatmapSetID, out var existingSets)) return; @@ -393,11 +404,15 @@ namespace osu.Game.Screens.Select root.RemoveItem(set); } + } + public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => + { + updateBeatmapSet(beatmapSet); invalidateAfterChange(true); }); - public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => + private void updateBeatmapSet(BeatmapSetInfo beatmapSet) { Guid? previouslySelectedID = null; @@ -460,9 +475,7 @@ namespace osu.Game.Screens.Select select((CarouselItem?)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet); } } - - invalidateAfterChange(true); - }); + } /// /// Selects a given beatmap on the carousel. From 6fa1f5ef9bfaac577cad7b29d4840e9302c6a889 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 20:10:31 +0900 Subject: [PATCH 0541/1118] Simplify invalidation logic The only case where this was checking is guaranteed by realm to only be called once. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 63654fd36a..5178413ad6 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -168,7 +168,7 @@ namespace osu.Game.Screens.Select applyActiveCriteria(false); if (loadedTestBeatmaps) - invalidateAfterChange(true); + invalidateAfterChange(); // Restore selection if (selectedBeatmapBefore != null && newRoot.BeatmapSetsByID.TryGetValue(selectedBeatmapBefore.BeatmapSet!.ID, out var newSelectionCandidates)) @@ -298,7 +298,7 @@ namespace osu.Game.Screens.Select removeBeatmapSet(id); } - invalidateAfterChange(!BeatmapSetsLoaded); + invalidateAfterChange(); BeatmapSetsLoaded = true; return; } @@ -349,7 +349,7 @@ namespace osu.Game.Screens.Select } } - invalidateAfterChange(!BeatmapSetsLoaded); + invalidateAfterChange(); } private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes) @@ -378,7 +378,7 @@ namespace osu.Game.Screens.Select } if (changed) - invalidateAfterChange(true); + invalidateAfterChange(); } private IQueryable getBeatmapSets(Realm realm) => realm.All().Where(s => !s.DeletePending && !s.Protected); @@ -386,7 +386,7 @@ namespace osu.Game.Screens.Select public void RemoveBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => { removeBeatmapSet(beatmapSet.ID); - invalidateAfterChange(true); + invalidateAfterChange(); }); private void removeBeatmapSet(Guid beatmapSetID) @@ -409,7 +409,7 @@ namespace osu.Game.Screens.Select public void UpdateBeatmapSet(BeatmapSetInfo beatmapSet) => Schedule(() => { updateBeatmapSet(beatmapSet); - invalidateAfterChange(true); + invalidateAfterChange(); }); private void updateBeatmapSet(BeatmapSetInfo beatmapSet) @@ -752,15 +752,14 @@ namespace osu.Game.Screens.Select } } - private void invalidateAfterChange(bool invokeSetsChangedEvent) + private void invalidateAfterChange() { itemsCache.Invalidate(); if (!Scroll.UserScrolling) ScrollToSelected(true); - if (invokeSetsChangedEvent) - BeatmapSetsChanged?.Invoke(); + BeatmapSetsChanged?.Invoke(); } private float? scrollTarget; From 034c5cd6541d622fe42ed1c301b647a3ac06afd8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 20:30:56 +0900 Subject: [PATCH 0542/1118] Debounce count updates for good measure --- osu.Game/Screens/Select/SongSelect.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index dfea4e3794..7c30fd5baa 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -162,7 +162,7 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, - FilterApplied = updateVisibleBeatmapCount, + FilterApplied = () => Scheduler.Add(updateVisibleBeatmapCount), GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s), }, c => carouselContainer.Child = c); @@ -843,7 +843,7 @@ namespace osu.Game.Screens.Select private void carouselBeatmapsLoaded() { bindBindables(); - updateVisibleBeatmapCount(); + Scheduler.AddOnce(updateVisibleBeatmapCount); Carousel.AllowSelection = true; @@ -877,7 +877,8 @@ namespace osu.Game.Screens.Select { // Intentionally not localised until we have proper support for this (see https://github.com/ppy/osu-framework/pull/4918 // but also in this case we want support for formatting a number within a string). - FilterControl.InformationalText = Carousel.CountDisplayed != 1 ? $"{Carousel.CountDisplayed:#,0} matches" : $"{Carousel.CountDisplayed:#,0} match"; + int carouselCountDisplayed = Carousel.CountDisplayed; + FilterControl.InformationalText = carouselCountDisplayed != 1 ? $"{carouselCountDisplayed:#,0} matches" : $"{carouselCountDisplayed:#,0} match"; } private bool boundLocalBindables; From 5755fa214a450d1663babcad4fd1eb5bcafb12e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 20:44:08 +0900 Subject: [PATCH 0543/1118] Cache non-filtered beatmap counts to massively improve count performance --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- .../Screens/Select/Carousel/CarouselGroup.cs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 5178413ad6..919a320bca 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Select /// /// The total count of non-filtered beatmaps displayed. /// - public int CountDisplayed => beatmapSets.Where(s => !s.Filtered.Value).Sum(s => s.Beatmaps.Count(b => !b.Filtered.Value)); + public int CountDisplayed => beatmapSets.Where(s => !s.Filtered.Value).Sum(s => s.TotalItemsNotFiltered); /// /// The currently selected beatmap set. diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index c353ee98ae..5aefdf5e28 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -14,6 +14,8 @@ namespace osu.Game.Screens.Select.Carousel public IReadOnlyList Items => items; + public int TotalItemsNotFiltered { get; private set; } + private readonly List items = new List(); /// @@ -31,6 +33,9 @@ namespace osu.Game.Screens.Select.Carousel { items.Remove(i); + if (!i.Filtered.Value) + TotalItemsNotFiltered--; + // it's important we do the deselection after removing, so any further actions based on // State.ValueChanged make decisions post-removal. i.State.Value = CarouselItemState.Collapsed; @@ -55,6 +60,9 @@ namespace osu.Game.Screens.Select.Carousel // criteria may be null for initial population. the filtering will be applied post-add. items.Add(i); } + + if (!i.Filtered.Value) + TotalItemsNotFiltered--; } public CarouselGroup(List? items = null) @@ -84,7 +92,14 @@ namespace osu.Game.Screens.Select.Carousel { base.Filter(criteria); - items.ForEach(c => c.Filter(criteria)); + TotalItemsNotFiltered = 0; + + foreach (var c in items) + { + c.Filter(criteria); + if (!c.Filtered.Value) + TotalItemsNotFiltered++; + } // Sorting is expensive, so only perform if it's actually changed. if (lastCriteria?.Sort != criteria.Sort) From 25df42630ecba979457a9dcd2a7678d154a85a5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 20:47:08 +0900 Subject: [PATCH 0544/1118] Improve performance of `attemptSelection` using new cached count and `LastSelected` --- osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 7f90e05744..8d2ddc5812 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; namespace osu.Game.Screens.Select.Carousel { @@ -101,7 +100,7 @@ namespace osu.Game.Screens.Select.Carousel if (State.Value != CarouselItemState.Selected) return; // we only perform eager selection if none of our items are in a selected state already. - if (Items.Any(i => i.State.Value == CarouselItemState.Selected)) return; + if (LastSelected?.State.Value == CarouselItemState.Selected || TotalItemsNotFiltered == 0) return; PerformSelection(); } From be16e0e538465369aa9e33223f2814b34fdbddaa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Dec 2023 12:47:42 +0100 Subject: [PATCH 0545/1118] Add failing test for adding collection w/ name colliding w/\ default items --- .../TestSceneManageCollectionsDialog.cs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs index cfa45ec6ef..747cf73baf 100644 --- a/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs +++ b/osu.Game.Tests/Visual/Collections/TestSceneManageCollectionsDialog.cs @@ -166,6 +166,29 @@ namespace osu.Game.Tests.Visual.Collections }))); } + [Test] + public void TestCollectionNameCollisionsWithBuiltInItems() + { + AddStep("add dropdown", () => + { + Add(new CollectionDropdown + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + RelativeSizeAxes = Axes.X, + Width = 0.4f, + }); + }); + AddStep("add two collections which collide with default items", () => Realm.Write(r => r.Add(new[] + { + new BeatmapCollection(name: "All beatmaps"), + new BeatmapCollection(name: "Manage collections...") + { + BeatmapMD5Hashes = { beatmapManager.GetAllUsableBeatmapSets().First().Beatmaps[0].MD5Hash } + }, + }))); + } + [Test] public void TestRemoveCollectionViaButton() { From eeeb5aa3d4d0eeb8360b345751157eb52f09ad40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Dec 2023 12:52:06 +0100 Subject: [PATCH 0546/1118] Fix crash when creating collections named "All beatmaps" or "Manage collections..." Closes https://github.com/ppy/osu/issues/25834 The name fallback that was there previously since https://github.com/ppy/osu/pull/11892 was half broken. This way should be a lot less prone to failure. --- .../Collections/CollectionFilterMenuItem.cs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game/Collections/CollectionFilterMenuItem.cs b/osu.Game/Collections/CollectionFilterMenuItem.cs index 2ac5784f09..49262ed917 100644 --- a/osu.Game/Collections/CollectionFilterMenuItem.cs +++ b/osu.Game/Collections/CollectionFilterMenuItem.cs @@ -37,22 +37,17 @@ namespace osu.Game.Collections CollectionName = name; } - public bool Equals(CollectionFilterMenuItem? other) + public virtual bool Equals(CollectionFilterMenuItem? other) { - if (other == null) - return false; + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; - // collections may have the same name, so compare first on reference equality. - // this relies on the assumption that only one instance of the BeatmapCollection exists game-wide, managed by CollectionManager. - if (Collection != null) - return Collection.ID == other.Collection?.ID; + if (Collection == null) return false; - // fallback to name-based comparison. - // this is required for special dropdown items which don't have a collection (all beatmaps / manage collections items below). - return CollectionName == other.CollectionName; + return Collection.ID == other.Collection?.ID; } - public override int GetHashCode() => CollectionName.GetHashCode(); + public override int GetHashCode() => Collection?.ID.GetHashCode() ?? 0; } public class AllBeatmapsCollectionFilterMenuItem : CollectionFilterMenuItem @@ -61,6 +56,10 @@ namespace osu.Game.Collections : base("All beatmaps") { } + + public override bool Equals(CollectionFilterMenuItem? other) => other is AllBeatmapsCollectionFilterMenuItem; + + public override int GetHashCode() => 1; } public class ManageCollectionsFilterMenuItem : CollectionFilterMenuItem @@ -69,5 +68,9 @@ namespace osu.Game.Collections : base("Manage collections...") { } + + public override bool Equals(CollectionFilterMenuItem? other) => other is ManageCollectionsFilterMenuItem; + + public override int GetHashCode() => 2; } } From 17f1f8bb43b7cadb1765338428870736a6f713bc Mon Sep 17 00:00:00 2001 From: 65-7a Date: Tue, 19 Dec 2023 00:28:23 +1100 Subject: [PATCH 0547/1118] Fix padding on dropdown search bar --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index ea0da30911..632036fef9 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -397,7 +397,7 @@ namespace osu.Game.Graphics.UserInterface protected override DropdownSearchBar CreateSearchBar() => new OsuDropdownSearchBar { - Padding = new MarginPadding { Right = 36 }, + Padding = new MarginPadding { Right = 26 }, }; private partial class OsuDropdownSearchBar : DropdownSearchBar From 25e3a8e82e712d18c904ccfcc29fbcf821e94163 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 22:24:57 +0900 Subject: [PATCH 0548/1118] Fix a few of silly issues --- osu.Game/Screens/Select/BeatmapCarousel.cs | 3 +++ osu.Game/Screens/Select/Carousel/CarouselGroup.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 2 +- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 919a320bca..607b891beb 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -168,7 +168,10 @@ namespace osu.Game.Screens.Select applyActiveCriteria(false); if (loadedTestBeatmaps) + { invalidateAfterChange(); + BeatmapSetsLoaded = true; + } // Restore selection if (selectedBeatmapBefore != null && newRoot.BeatmapSetsByID.TryGetValue(selectedBeatmapBefore.BeatmapSet!.ID, out var newSelectionCandidates)) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index 5aefdf5e28..f5ea32a22a 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -62,7 +62,7 @@ namespace osu.Game.Screens.Select.Carousel } if (!i.Filtered.Value) - TotalItemsNotFiltered--; + TotalItemsNotFiltered++; } public CarouselGroup(List? items = null) diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 7c30fd5baa..d23a660ff6 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -162,7 +162,7 @@ namespace osu.Game.Screens.Select BleedBottom = Footer.HEIGHT, SelectionChanged = updateSelectedBeatmap, BeatmapSetsChanged = carouselBeatmapsLoaded, - FilterApplied = () => Scheduler.Add(updateVisibleBeatmapCount), + FilterApplied = () => Scheduler.AddOnce(updateVisibleBeatmapCount), GetRecommendedBeatmap = s => recommender?.GetRecommendedBeatmap(s), }, c => carouselContainer.Child = c); From d81cabc06361ab9cfb20f7294c38166d4bdb03a9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 18 Dec 2023 22:35:36 +0900 Subject: [PATCH 0549/1118] Revert "Improve performance of `attemptSelection` using new cached count and `LastSelected`" This reverts commit 25df42630ecba979457a9dcd2a7678d154a85a5e. --- osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 8d2ddc5812..7f90e05744 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; namespace osu.Game.Screens.Select.Carousel { @@ -100,7 +101,7 @@ namespace osu.Game.Screens.Select.Carousel if (State.Value != CarouselItemState.Selected) return; // we only perform eager selection if none of our items are in a selected state already. - if (LastSelected?.State.Value == CarouselItemState.Selected || TotalItemsNotFiltered == 0) return; + if (Items.Any(i => i.State.Value == CarouselItemState.Selected)) return; PerformSelection(); } From 9aaaa128098af6a8e57e07d5131bb719779b428c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 00:01:09 +0900 Subject: [PATCH 0550/1118] Don't show progress notifications when there are too few items to be worthwhile --- osu.Game/BackgroundDataStoreProcessor.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 55be7f2c9e..33b66ecfc7 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -151,7 +151,7 @@ namespace osu.Game Logger.Log($"Found {beatmapSetIds.Count} beatmap sets which require reprocessing."); // Technically this is doing more than just star ratings, but easier for the end user to understand. - var notification = showProgressNotification("Reprocessing star rating for beatmaps", "beatmaps' star ratings have been updated"); + var notification = showProgressNotification(beatmapSetIds.Count, "Reprocessing star rating for beatmaps", "beatmaps' star ratings have been updated"); int processedCount = 0; int failedCount = 0; @@ -205,7 +205,7 @@ namespace osu.Game Logger.Log($"Found {beatmapIds.Count} beatmaps which require statistics population."); - var notification = showProgressNotification("Populating missing statistics for beatmaps", "beatmaps have been populated with missing statistics"); + var notification = showProgressNotification(beatmapIds.Count, "Populating missing statistics for beatmaps", "beatmaps have been populated with missing statistics"); int processedCount = 0; int failedCount = 0; @@ -266,7 +266,7 @@ namespace osu.Game Logger.Log($"Found {scoreIds.Count} scores which require statistics population."); - var notification = showProgressNotification("Populating missing statistics for scores", "scores have been populated with missing statistics"); + var notification = showProgressNotification(scoreIds.Count, "Populating missing statistics for scores", "scores have been populated with missing statistics"); int processedCount = 0; int failedCount = 0; @@ -325,7 +325,7 @@ namespace osu.Game if (scoreIds.Count == 0) return; - var notification = showProgressNotification("Upgrading scores to new scoring algorithm", "scores have been upgraded to the new scoring algorithm"); + var notification = showProgressNotification(scoreIds.Count, "Upgrading scores to new scoring algorithm", "scores have been upgraded to the new scoring algorithm"); int processedCount = 0; int failedCount = 0; @@ -405,11 +405,14 @@ namespace osu.Game } } - private ProgressNotification? showProgressNotification(string running, string completed) + private ProgressNotification? showProgressNotification(int totalCount, string running, string completed) { if (notificationOverlay == null) return null; + if (totalCount < 10) + return null; + ProgressNotification notification = new ProgressNotification { Text = running, From 374425ea75ee36a853f170c3a3ae04deccdb9981 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 01:07:33 +0900 Subject: [PATCH 0551/1118] Fix keyboard precision of nightcore/daycore adjustments being incorrect Closes https://github.com/ppy/osu/issues/25854. --- osu.Game/Rulesets/Mods/ModDaycore.cs | 3 ++- osu.Game/Rulesets/Mods/ModNightcore.cs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModDaycore.cs b/osu.Game/Rulesets/Mods/ModDaycore.cs index 39ebd1fe4c..09b35c249e 100644 --- a/osu.Game/Rulesets/Mods/ModDaycore.cs +++ b/osu.Game/Rulesets/Mods/ModDaycore.cs @@ -6,6 +6,7 @@ using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Game.Configuration; +using osu.Game.Overlays.Settings; namespace osu.Game.Rulesets.Mods { @@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyReduction; public override LocalisableString Description => "Whoaaaaa..."; - [SettingSource("Speed decrease", "The actual decrease to apply")] + [SettingSource("Speed decrease", "The actual decrease to apply", SettingControlType = typeof(MultiplierSettingsSlider))] public override BindableNumber SpeedChange { get; } = new BindableDouble(0.75) { MinValue = 0.5, diff --git a/osu.Game/Rulesets/Mods/ModNightcore.cs b/osu.Game/Rulesets/Mods/ModNightcore.cs index b519ab4db7..b42927256c 100644 --- a/osu.Game/Rulesets/Mods/ModNightcore.cs +++ b/osu.Game/Rulesets/Mods/ModNightcore.cs @@ -14,6 +14,7 @@ using osu.Game.Beatmaps.Timing; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.UI; using osu.Game.Skinning; @@ -28,7 +29,7 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyIncrease; public override LocalisableString Description => "Uguuuuuuuu..."; - [SettingSource("Speed increase", "The actual increase to apply")] + [SettingSource("Speed increase", "The actual increase to apply", SettingControlType = typeof(MultiplierSettingsSlider))] public override BindableNumber SpeedChange { get; } = new BindableDouble(1.5) { MinValue = 1.01, From 51f4c7254c80bca74ea0ae9e772c630feb7f6a4e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 01:32:30 +0900 Subject: [PATCH 0552/1118] Fix mod search textbox having focus while settings are visible Stopped arrow key adjust on slider bars from working. Also just felt wrong that you could type into an off-screen textbox. --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 13 ++++++++----- osu.Game/Overlays/Mods/ModSettingsArea.cs | 2 ++ 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index ce798ae752..30d7b6191e 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -508,6 +508,11 @@ namespace osu.Game.Overlays.Mods modSettingsArea.ResizeHeightTo(modAreaHeight, transition_duration, Easing.InOutCubic); TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic); + + if (customisationVisible.Value) + GetContainingInputManager().ChangeFocus(modSettingsArea); + else + Scheduler.Add(() => GetContainingInputManager().ChangeFocus(null)); } /// @@ -622,7 +627,7 @@ namespace osu.Game.Overlays.Mods } if (textSearchStartsActive.Value) - SearchTextBox.TakeFocus(); + SearchTextBox.HoldFocus = true; } protected override void PopOut() @@ -761,11 +766,9 @@ namespace osu.Game.Overlays.Mods return false; // TODO: should probably eventually support typical platform search shortcuts (`Ctrl-F`, `/`) - if (SearchTextBox.HasFocus) - SearchTextBox.KillFocus(); - else + SearchTextBox.HoldFocus = !SearchTextBox.HoldFocus; + if (SearchTextBox.HoldFocus) SearchTextBox.TakeFocus(); - return true; } diff --git a/osu.Game/Overlays/Mods/ModSettingsArea.cs b/osu.Game/Overlays/Mods/ModSettingsArea.cs index 6158c2c70f..54bfcc7199 100644 --- a/osu.Game/Overlays/Mods/ModSettingsArea.cs +++ b/osu.Game/Overlays/Mods/ModSettingsArea.cs @@ -32,6 +32,8 @@ namespace osu.Game.Overlays.Mods [Resolved] private OverlayColourProvider colourProvider { get; set; } = null!; + public override bool AcceptsFocus => true; + public ModSettingsArea() { RelativeSizeAxes = Axes.X; From 41485c19cfe597a5144911fdaff34fe218f1d963 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 02:08:25 +0900 Subject: [PATCH 0553/1118] Add realm refresh steps in an attempt to stabilise failing test I think this is required because there is a higher chance of batched updates with the new structure (and less calls to `BeatmapSetsChanged` which causes re-selection). --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 6b53277964..28b4dae56d 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -464,6 +464,8 @@ namespace osu.Game.Tests.Visual.SongSelect manager.Import(testBeatmapSetInfo); }, 10); + AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh())); + AddUntilStep("has selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID == originalOnlineSetID); Task?> updateTask = null!; @@ -476,6 +478,8 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddUntilStep("wait for update completion", () => updateTask.IsCompleted); + AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh())); + AddUntilStep("retained selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID == originalOnlineSetID); } From bf668174ecc743f5941035a57fbbbcfa8d2278fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Dec 2023 19:02:23 +0100 Subject: [PATCH 0554/1118] Use nunit constraints in test for transparency --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 28b4dae56d..6af53df725 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -466,7 +466,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh())); - AddUntilStep("has selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID == originalOnlineSetID); + AddUntilStep("has selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID, () => Is.EqualTo(originalOnlineSetID)); Task?> updateTask = null!; @@ -480,7 +480,7 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh())); - AddUntilStep("retained selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID == originalOnlineSetID); + AddUntilStep("retained selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID, () => Is.EqualTo(originalOnlineSetID)); } [Test] From 9594ae7802fce0edea82d09d2bc2ecfe4bb50f85 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 03:22:22 +0900 Subject: [PATCH 0555/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index bd35bb8d54..cf01f2f99b 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 84a4e61267..c7b9d02b26 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From cc800a18b203e474fc9fe6d43f50c2bc4e63ea03 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Mon, 18 Dec 2023 21:11:00 +0100 Subject: [PATCH 0556/1118] Fix opening log files from notification not presenting the correct file --- osu.Game/OsuGame.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 8d9e029c9d..e7ff99ef01 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -1190,7 +1190,7 @@ namespace osu.Game } else if (recentLogCount == short_term_display_limit) { - string logFile = $@"{entry.Target.Value.ToString().ToLowerInvariant()}.log"; + string logFile = Logger.GetLogger(entry.Target.Value).Filename; Schedule(() => Notifications.Post(new SimpleNotification { @@ -1198,7 +1198,7 @@ namespace osu.Game Text = NotificationsStrings.SubsequentMessagesLogged, Activated = () => { - Storage.GetStorageForDirectory(@"logs").PresentFileExternally(logFile); + Logger.Storage.PresentFileExternally(logFile); return true; } })); From 017003deea88739c244e88facd84252a940a6372 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Dec 2023 21:56:56 +0100 Subject: [PATCH 0557/1118] Fix osu! standardised score conversion sometimes exceeding bounds Co-authored-by: Zyf Closes https://github.com/ppy/osu/issues/25860 Users reported that some stable scores would convert to large negative total scores in lazer after the introduction of combo exponent. Those large negative total scores were actually mangled NaNs. The root cause of this was the following calculation going below zero unexpectedly: https://github.com/ppy/osu/blob/8e8d9b2cd96be4c4b3d8f1f01dc013fc9d41f765/osu.Game/Database/StandardisedScoreMigrationTools.cs#L323 which then propagates negative numbers onward until https://github.com/ppy/osu/blob/8e8d9b2cd96be4c4b3d8f1f01dc013fc9d41f765/osu.Game/Database/StandardisedScoreMigrationTools.cs#L337 which yields a NaN due to attempting to take the square root of a negative number. To fix, clamp `comboPortionInScoreV1` to sane limits: to `comboPortionFromLongestComboInScoreV1` from below, and to `maximumAchievableComboPortionInScoreV1` from above. This is a less direct fix than perhaps imagined, but it seems like a better one as it will also affect the calculation of both the lower and the upper estimate of the score. --- .../Database/StandardisedScoreMigrationTools.cs | 15 +++++++++++++-- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 6484500bcc..a30c40fdb0 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -293,13 +293,24 @@ namespace osu.Game.Database // Roughly corresponds to integrating f(combo) = combo ^ COMBO_EXPONENT (omitting constants) double maximumAchievableComboPortionInStandardisedScore = Math.Pow(maximumLegacyCombo, 1 + ScoreProcessor.COMBO_EXPONENT); - double comboPortionInScoreV1 = maximumAchievableComboPortionInScoreV1 * comboProportion / score.Accuracy; - // This is - roughly - how much score, in the combo portion, the longest combo on this particular play would gain in score V1. double comboPortionFromLongestComboInScoreV1 = Math.Pow(score.MaxCombo, 2); // Same for standardised score. double comboPortionFromLongestComboInStandardisedScore = Math.Pow(score.MaxCombo, 1 + ScoreProcessor.COMBO_EXPONENT); + // We estimate the combo portion of the score in score V1 terms. + // The division by accuracy is supposed to lessen the impact of accuracy on the combo portion, + // but in some edge cases it cannot sanely undo it. + // Therefore the resultant value is clamped from both sides for sanity. + // The clamp from below to `comboPortionFromLongestComboInScoreV1` targets near-FC scores wherein + // the player had bad accuracy at the end of their longest combo, which causes the division by accuracy + // to underestimate the combo portion. + // The clamp from above to `maximumAchievableComboPortionInScoreV1` targets FC scores wherein + // the player had bad accuracy at the start of the map, which causes the division by accuracy + // to overestimate the combo portion. + double comboPortionInScoreV1 = Math.Clamp(maximumAchievableComboPortionInScoreV1 * comboProportion / score.Accuracy, + comboPortionFromLongestComboInScoreV1, maximumAchievableComboPortionInScoreV1); + // Calculate how many times the longest combo the user has achieved in the play can repeat // without exceeding the combo portion in score V1 as achieved by the player. // This is a pessimistic estimate; it intentionally does not operate on object count and uses only score instead. diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 00b1f04782..5c51e68d9f 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -32,9 +32,10 @@ namespace osu.Game.Scoring.Legacy /// 30000003: First version after converting legacy total score to standardised. /// 30000004: Fixed mod multipliers during legacy score conversion. Reconvert all scores. /// 30000005: Introduce combo exponent in the osu! gamemode. Reconvert all scores. + /// 30000006: Fix edge cases in conversion after combo exponent introduction that lead to NaNs. Reconvert all scores. /// /// - public const int LATEST_VERSION = 30000005; + public const int LATEST_VERSION = 30000006; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From 9c8df4e6d1a041c0b2fc19873aa8ae8e1d0d9fb6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 18 Dec 2023 22:23:58 +0100 Subject: [PATCH 0558/1118] Run score conversion for previously-imported scores --- osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs | 3 +++ osu.Game/BackgroundDataStoreProcessor.cs | 3 +-- osu.Game/Database/StandardisedScoreMigrationTools.cs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs index e65088ca2e..43ce7200d2 100644 --- a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs +++ b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs @@ -127,8 +127,11 @@ namespace osu.Game.Tests.Database }); } + [TestCase(30000001)] [TestCase(30000002)] [TestCase(30000003)] + [TestCase(30000004)] + [TestCase(30000005)] public void TestScoreUpgradeSuccess(int scoreVersion) { ScoreInfo scoreInfo = null!; diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 33b66ecfc7..0d5cb84359 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -316,8 +316,7 @@ namespace osu.Game HashSet scoreIds = realmAccess.Run(r => new HashSet(r.All() .Where(s => !s.BackgroundReprocessingFailed && s.BeatmapInfo != null - && (s.TotalScoreVersion == 30000002 - || s.TotalScoreVersion == 30000003)) + && s.TotalScoreVersion < LegacyScoreEncoder.LATEST_VERSION) .AsEnumerable().Select(s => s.ID))); Logger.Log($"Found {scoreIds.Count} scores which require total score conversion."); diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index a30c40fdb0..6980e81f58 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -26,7 +26,7 @@ namespace osu.Game.Database if (score.IsLegacyScore) return false; - if (score.TotalScoreVersion > 30000004) + if (score.TotalScoreVersion > 30000005) return false; // Recalculate the old-style standardised score to see if this was an old lazer score. From ee8a5d5a3077c5a1045ef74fc8b037296a26a005 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 13:33:45 +0900 Subject: [PATCH 0559/1118] Update bug-issue.yml with new workflow for exporting logs --- .github/ISSUE_TEMPLATE/bug-issue.yml | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index ff6d869e72..17a3e1df41 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -46,22 +46,16 @@ body: value: | ## Logs - Attaching log files is required for every reported bug. See instructions below on how to find them. - - **Logs are reset when you reopen the game.** If the game crashed or has been closed since you found the bug, retrieve the logs using the file explorer instead. + Attaching log files is required for **every** issue, regardless of whether you deem them required or not. See instructions below on how to find them. ### Desktop platforms If the game has not yet been closed since you found the bug: - 1. Head on to game settings and click on "Open osu! folder" - 2. Then open the `logs` folder located there + 1. Head on to game settings and click on "Export logs" + 2. Click the notification to locate the file + 3. Drag the generated `.zip` files into the github issue window - The default places to find the logs on desktop platforms are as follows: - - `%AppData%/osu/logs` *on Windows* - - `~/.local/share/osu/logs` *on Linux* - - `~/Library/Application Support/osu/logs` *on macOS* - - If you have selected a custom location for the game files, you can find the `logs` folder there. + ![export logs button](https://github.com/ppy/osu/assets/191335/0866443f-0728-47bc-9dbd-f2b79ac802d5) ### Mobile platforms @@ -69,10 +63,6 @@ body: - *On Android*, navigate to `Android/data/sh.ppy.osulazer/files/logs` using a file browser app. - *On iOS*, connect your device to a PC and copy the `logs` directory from the app's document storage using iTunes. (https://support.apple.com/en-us/HT201301#copy-to-computer) - --- - - After locating the `logs` folder, select all log files inside and drag them into the "Logs" box below. - - type: textarea attributes: label: Logs From 469a659938dfb3c696c9a9fb51c7252214014a31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 13:35:02 +0900 Subject: [PATCH 0560/1118] Fix arrow pointing to wrong place --- .github/ISSUE_TEMPLATE/bug-issue.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index 17a3e1df41..00a873f9c8 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -53,9 +53,9 @@ body: If the game has not yet been closed since you found the bug: 1. Head on to game settings and click on "Export logs" 2. Click the notification to locate the file - 3. Drag the generated `.zip` files into the github issue window + 3. Drag the generated `.zip` files into the github issue window - ![export logs button](https://github.com/ppy/osu/assets/191335/0866443f-0728-47bc-9dbd-f2b79ac802d5) + ![export logs button](https://github.com/ppy/osu/assets/191335/cbfa5550-b7ed-4c5c-8dd0-8b87cc90ad9b) ### Mobile platforms From c1b55c7facaa675d62a68179657df8b9a1170846 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 19 Dec 2023 13:48:46 +0900 Subject: [PATCH 0561/1118] Add ScoreProcessor methods to override numeric result --- .../Scoring/CatchScoreProcessor.cs | 2 +- .../Scoring/ManiaScoreProcessor.cs | 2 +- .../Scoring/TaikoScoreProcessor.cs | 2 +- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 24 ++++++++++++++----- 4 files changed, 21 insertions(+), 9 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 66c76f9b17..503252df02 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Scoring } protected override double GetComboScoreChange(JudgementResult result) - => Judgement.ToNumericResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); + => GetNumericResultFor(result) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); public override ScoreRank RankFromAccuracy(double accuracy) { diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index c53f3c3e07..e5f9b33c6b 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Mania.Scoring } protected override double GetComboScoreChange(JudgementResult result) - => Judgement.ToNumericResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); + => GetNumericResultFor(result) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); private class JudgementOrderComparer : IComparer { diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index a77e6db6f3..a34e977ee9 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -28,7 +28,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring protected override double GetComboScoreChange(JudgementResult result) { - return Judgement.ToNumericResult(result.Type) + return GetNumericResultFor(result) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)) * strongScaleValue(result); } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index f110172988..aa8905c0b6 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -227,12 +227,12 @@ namespace osu.Game.Rulesets.Scoring if (result.Judgement.MaxResult.AffectsAccuracy()) { - currentMaximumBaseScore += Judgement.ToNumericResult(result.Judgement.MaxResult); + currentMaximumBaseScore += GetMaxNumericResultFor(result); currentAccuracyJudgementCount++; } if (result.Type.AffectsAccuracy()) - currentBaseScore += Judgement.ToNumericResult(result.Type); + currentBaseScore += GetNumericResultFor(result); if (result.Type.IsBonus()) currentBonusPortion += GetBonusScoreChange(result); @@ -276,12 +276,12 @@ namespace osu.Game.Rulesets.Scoring if (result.Judgement.MaxResult.AffectsAccuracy()) { - currentMaximumBaseScore -= Judgement.ToNumericResult(result.Judgement.MaxResult); + currentMaximumBaseScore -= GetMaxNumericResultFor(result); currentAccuracyJudgementCount--; } if (result.Type.AffectsAccuracy()) - currentBaseScore -= Judgement.ToNumericResult(result.Type); + currentBaseScore -= GetNumericResultFor(result); if (result.Type.IsBonus()) currentBonusPortion -= GetBonusScoreChange(result); @@ -297,9 +297,21 @@ namespace osu.Game.Rulesets.Scoring updateScore(); } - protected virtual double GetBonusScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Type); + protected virtual double GetBonusScoreChange(JudgementResult result) => GetNumericResultFor(result); - protected virtual double GetComboScoreChange(JudgementResult result) => Judgement.ToNumericResult(result.Judgement.MaxResult) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); + protected virtual double GetComboScoreChange(JudgementResult result) => GetMaxNumericResultFor(result) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); + + /// + /// Retrieves the numeric score representation for a . + /// + /// The . + protected virtual double GetNumericResultFor(JudgementResult result) => result.Judgement.NumericResultFor(result); + + /// + /// Retrieves the maximum numeric score representation for a . + /// + /// The . + protected virtual double GetMaxNumericResultFor(JudgementResult result) => result.Judgement.MaxNumericResult; protected virtual void ApplyScoreChange(JudgementResult result) { From 35c0eaee1c5ff6a55609e61aa40c14c6a3645fe9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 19 Dec 2023 13:50:46 +0900 Subject: [PATCH 0562/1118] Make taiko OKs worth 150 points --- .../Scoring/TaikoScoreProcessor.cs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index a34e977ee9..bc1e42c5d1 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -33,6 +33,17 @@ namespace osu.Game.Rulesets.Taiko.Scoring * strongScaleValue(result); } + protected override double GetNumericResultFor(JudgementResult result) + { + switch (result.Type) + { + case HitResult.Ok: + return 150; + } + + return base.GetNumericResultFor(result); + } + private double strongScaleValue(JudgementResult result) { if (result.HitObject is StrongNestedHitObject strong) From f2edb3ea54ef0bd5cbc181bd6b25a4ba20eaba8d Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 19 Dec 2023 14:01:08 +0900 Subject: [PATCH 0563/1118] Add test --- .../TestSceneTaikoScoreProcessor.cs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoScoreProcessor.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoScoreProcessor.cs new file mode 100644 index 0000000000..6f3b9f9748 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoScoreProcessor.cs @@ -0,0 +1,41 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Scoring; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [TestFixture] + public class TestSceneTaikoScoreProcessor + { + [Test] + public void TestInaccurateHitScore() + { + var beatmap = new Beatmap + { + HitObjects = + { + new Hit(), + new Hit { StartTime = 1000 } + } + }; + + var scoreProcessor = new TaikoScoreProcessor(); + scoreProcessor.ApplyBeatmap(beatmap); + + // Apply a miss judgement + scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TaikoJudgement()) { Type = HitResult.Great }); + scoreProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], new TaikoJudgement()) { Type = HitResult.Ok }); + + Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(453745)); + Assert.That(scoreProcessor.Accuracy.Value, Is.EqualTo(0.75).Within(0.0001)); + } + } +} From 30957f847cdca20f1ee3a8619c406808289080a0 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 19 Dec 2023 14:27:29 +0900 Subject: [PATCH 0564/1118] Rename class/file (this is not a test scene) --- ...stSceneTaikoScoreProcessor.cs => TaikoScoreProcessorTest.cs} | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename osu.Game.Rulesets.Taiko.Tests/{TestSceneTaikoScoreProcessor.cs => TaikoScoreProcessorTest.cs} (96%) diff --git a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoScoreProcessorTest.cs similarity index 96% rename from osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoScoreProcessor.cs rename to osu.Game.Rulesets.Taiko.Tests/TaikoScoreProcessorTest.cs index 6f3b9f9748..d74fe99a9f 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TestSceneTaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoScoreProcessorTest.cs @@ -13,7 +13,7 @@ using osu.Game.Rulesets.Taiko.Scoring; namespace osu.Game.Rulesets.Taiko.Tests { [TestFixture] - public class TestSceneTaikoScoreProcessor + public class TaikoScoreProcessorTest { [Test] public void TestInaccurateHitScore() From 44efa2c540c392c998af7cd052a88d35d053aa5b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 15:09:03 +0900 Subject: [PATCH 0565/1118] Fix incorrect ordering of items at song select when certain sort modes are used --- .../Screens/Select/Carousel/CarouselGroup.cs | 2 +- osu.Game/Screens/Select/FilterCriteria.cs | 38 +++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs index c353ee98ae..be841465bf 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroup.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroup.cs @@ -87,7 +87,7 @@ namespace osu.Game.Screens.Select.Carousel items.ForEach(c => c.Filter(criteria)); // Sorting is expensive, so only perform if it's actually changed. - if (lastCriteria?.Sort != criteria.Sort) + if (lastCriteria?.RequiresSorting(criteria) != false) { criteriaComparer = Comparer.Create((x, y) => { diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 811f623ee5..0bea2247ce 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -219,6 +219,44 @@ namespace osu.Game.Screens.Select public bool Equals(OptionalTextFilter other) => SearchTerm == other.SearchTerm; } + /// + /// Given a new filter criteria, decide whether a full sort needs to be performed. + /// + /// + /// + public bool RequiresSorting(FilterCriteria newCriteria) + { + if (Sort != newCriteria.Sort) + return true; + + switch (Sort) + { + // Some sorts are stable across all other changes. + // Running these sorts will sort all items, including currently hidden items. + case SortMode.Artist: + case SortMode.Author: + case SortMode.DateSubmitted: + case SortMode.DateAdded: + case SortMode.DateRanked: + case SortMode.Source: + case SortMode.Title: + return false; + + // Some sorts use aggregate max comparisons, which will change based on filtered items. + // These sorts generally ignore items hidden by filtered state, so we must force a sort under all circumstances here. + // + // This makes things very slow when typing a text search, and we probably want to consider a way to optimise things going forward. + case SortMode.LastPlayed: + case SortMode.BPM: + case SortMode.Length: + case SortMode.Difficulty: + return true; + + default: + throw new ArgumentOutOfRangeException(); + } + } + public enum MatchMode { /// From ddb67c87a8c20680c6ef77d8faa512b50d090be5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Dec 2023 08:13:02 +0100 Subject: [PATCH 0566/1118] Roll back incorrect change in `ShouldMigrateToNewStandardised()` --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 6980e81f58..11eacd1c6b 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -26,7 +26,7 @@ namespace osu.Game.Database if (score.IsLegacyScore) return false; - if (score.TotalScoreVersion > 30000005) + if (score.TotalScoreVersion > 30000002) return false; // Recalculate the old-style standardised score to see if this was an old lazer score. From 011bd61e7d3f7301fd00bd5acd0d89120a92b61a Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 19 Dec 2023 16:47:49 +0900 Subject: [PATCH 0567/1118] Give sliders in test scene a sample --- .../TestSceneSliderEarlyHitJudgement.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs index 9caee86a32..4ea21e51f6 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using NUnit.Framework; using osu.Framework.Screens; +using osu.Game.Audio; using osu.Game.Beatmaps; using osu.Game.Replays; using osu.Game.Rulesets.Judgements; @@ -160,6 +161,10 @@ namespace osu.Game.Rulesets.Osu.Tests Position = new Vector2(256 - slider_path_length / 2, 192), TickDistanceMultiplier = 3, ClassicSliderBehaviour = classic, + Samples = new[] + { + new HitSampleInfo(HitSampleInfo.HIT_NORMAL) + }, Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, From 7c05d66bd7913c3f7aa1a569af5fa1dd901c58dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Dec 2023 08:57:18 +0100 Subject: [PATCH 0568/1118] Add more detail to exception --- osu.Game/Screens/Select/FilterCriteria.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/FilterCriteria.cs b/osu.Game/Screens/Select/FilterCriteria.cs index 0bea2247ce..a7c8e7d093 100644 --- a/osu.Game/Screens/Select/FilterCriteria.cs +++ b/osu.Game/Screens/Select/FilterCriteria.cs @@ -253,7 +253,7 @@ namespace osu.Game.Screens.Select return true; default: - throw new ArgumentOutOfRangeException(); + throw new ArgumentOutOfRangeException(nameof(Sort), Sort, "Unknown sort mode"); } } From fe5e071e70cbd0a24f555fb9255dfa957246eb36 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 19 Dec 2023 17:01:52 +0900 Subject: [PATCH 0569/1118] Fix sliding sample playing before Slider's start time --- .../Objects/Drawables/DrawableSlider.cs | 25 +++++++++---------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 1f9a028045..b306fd38c1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -128,8 +128,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables foreach (var drawableHitObject in NestedHitObjects) drawableHitObject.AccentColour.Value = colour.NewValue; }, true); - - Tracking.BindValueChanged(updateSlidingSample); } protected override void OnApply() @@ -166,14 +164,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables slidingSample?.Stop(); } - private void updateSlidingSample(ValueChangedEvent tracking) - { - if (tracking.NewValue) - slidingSample?.Play(); - else - slidingSample?.Stop(); - } - protected override void AddNestedHitObject(DrawableHitObject hitObject) { base.AddNestedHitObject(hitObject); @@ -238,9 +228,18 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Tracking.Value = SliderInputManager.Tracking; - if (Tracking.Value && slidingSample != null) - // keep the sliding sample playing at the current tracking position - slidingSample.Balance.Value = CalculateSamplePlaybackBalance(CalculateDrawableRelativePosition(Ball)); + if (slidingSample != null) + { + if (Tracking.Value && Time.Current >= HitObject.StartTime) + { + // keep the sliding sample playing at the current tracking position + if (!slidingSample.IsPlaying) + slidingSample.Play(); + slidingSample.Balance.Value = CalculateSamplePlaybackBalance(CalculateDrawableRelativePosition(Ball)); + } + else if (slidingSample.IsPlaying) + slidingSample.Stop(); + } double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1); From 7e9c1b2acbaabb19564821d748deb2c97a964554 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 17:27:52 +0900 Subject: [PATCH 0570/1118] Use `sender`'s realm (because we can) --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 607b891beb..4c6c67c348 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -320,7 +320,7 @@ namespace osu.Game.Screens.Select // To handle the beatmap update flow, attempt to track selection changes across delete-insert transactions. // When an update occurs, the previous beatmap set is either soft or hard deleted. // Check if the current selection was potentially deleted by re-querying its validity. - bool selectedSetMarkedDeleted = realm.Run(r => r.Find(SelectedBeatmapSet.ID))?.DeletePending != false; + bool selectedSetMarkedDeleted = sender.Realm.Find(SelectedBeatmapSet.ID)?.DeletePending != false; int[] modifiedAndInserted = changes.NewModifiedIndices.Concat(changes.InsertedIndices).ToArray(); From 8f5d21dc703e3c1f3930e4b4e1c58c90acaba87a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 18:10:55 +0900 Subject: [PATCH 0571/1118] Actually fix realm selection retention regression --- .../SongSelect/TestScenePlaySongSelect.cs | 4 ---- osu.Game/Screens/Select/BeatmapCarousel.cs | 24 +++++++++++++++++-- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 6af53df725..518035fb82 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -464,8 +464,6 @@ namespace osu.Game.Tests.Visual.SongSelect manager.Import(testBeatmapSetInfo); }, 10); - AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh())); - AddUntilStep("has selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID, () => Is.EqualTo(originalOnlineSetID)); Task?> updateTask = null!; @@ -478,8 +476,6 @@ namespace osu.Game.Tests.Visual.SongSelect }); AddUntilStep("wait for update completion", () => updateTask.IsCompleted); - AddStep("Force realm refresh", () => Realm.Run(r => r.Refresh())); - AddUntilStep("retained selection", () => songSelect!.Carousel.SelectedBeatmapInfo?.BeatmapSet?.OnlineID, () => Is.EqualTo(originalOnlineSetID)); } diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 4c6c67c348..47cdbe34f4 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -269,8 +269,28 @@ namespace osu.Game.Screens.Select if (changes == null) return; - foreach (int i in changes.InsertedIndices) - removeBeatmapSet(sender[i].ID); + var removeableSets = changes.InsertedIndices.Select(i => sender[i].ID).ToHashSet(); + + // This schedule is required to retain selection of beatmaps over an ImportAsUpdate operation. + // This is covered by TestPlaySongSelect.TestSelectionRetainedOnBeatmapUpdate. + // + // In short, we have specialised logic in `beatmapSetsChanged` (directly below) to infer that an + // update operation has occurred. For this to work, we need to confirm the `DeletePending` flag + // of the current selection. + // + // If we don't schedule the following code, it is possible for the `deleteBeatmapSetsChanged` handler + // to be invoked before the `beatmapSetsChanged` handler (realm call order seems non-deterministic) + // which will lead to the currently selected beatmap changing via `CarouselGroupEagerSelect`. + // + // We need a better path forward here. A few ideas: + // - Avoid the necessity of having realm subscriptions on deleted/hidden items, maybe by storing all guids in realm + // to a local list so we can better look them up on receiving `DeletedIndices`. + // - Add a new property on `BeatmapSetInfo` to link to the pre-update set, and use that to handle the update case. + Schedule(() => + { + foreach (var set in removeableSets) + removeBeatmapSet(set); + }); } private void beatmapSetsChanged(IRealmCollection sender, ChangeSet? changes) From f09c6b8c1ba778d2a5a6f6f3c29de875bc3dc7a3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 18:20:02 +0900 Subject: [PATCH 0572/1118] Change default values of new object counts to `-1` to identify non-processed values --- osu.Game/BackgroundDataStoreProcessor.cs | 2 +- osu.Game/Beatmaps/BeatmapInfo.cs | 4 ++-- osu.Game/Beatmaps/IBeatmapInfo.cs | 3 +++ osu.Game/Database/RealmAccess.cs | 17 ++++++++++++++++- 4 files changed, 22 insertions(+), 4 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 33b66ecfc7..d4cd1ff6ea 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -196,7 +196,7 @@ namespace osu.Game realmAccess.Run(r => { - foreach (var b in r.All().Where(b => b.TotalObjectCount == 0)) + foreach (var b in r.All().Where(b => b.TotalObjectCount < 0 || b.EndTimeObjectCount < 0)) beatmapIds.Add(b.ID); }); diff --git a/osu.Game/Beatmaps/BeatmapInfo.cs b/osu.Game/Beatmaps/BeatmapInfo.cs index 2d04732f91..425fd98d27 100644 --- a/osu.Game/Beatmaps/BeatmapInfo.cs +++ b/osu.Game/Beatmaps/BeatmapInfo.cs @@ -120,9 +120,9 @@ namespace osu.Game.Beatmaps [JsonIgnore] public bool Hidden { get; set; } - public int EndTimeObjectCount { get; set; } + public int EndTimeObjectCount { get; set; } = -1; - public int TotalObjectCount { get; set; } + public int TotalObjectCount { get; set; } = -1; /// /// Reset any fetched online linking information (and history). diff --git a/osu.Game/Beatmaps/IBeatmapInfo.cs b/osu.Game/Beatmaps/IBeatmapInfo.cs index 9dcff5ce5e..04c2017ded 100644 --- a/osu.Game/Beatmaps/IBeatmapInfo.cs +++ b/osu.Game/Beatmaps/IBeatmapInfo.cs @@ -59,11 +59,13 @@ namespace osu.Game.Beatmaps /// /// The basic star rating for this beatmap (with no mods applied). + /// Defaults to -1 (meaning not-yet-calculated). /// double StarRating { get; } /// /// The number of hitobjects in the beatmap with a distinct end time. + /// Defaults to -1 (meaning not-yet-calculated). /// /// /// Canonically, these are hitobjects are either sliders or spinners. @@ -72,6 +74,7 @@ namespace osu.Game.Beatmaps /// /// The total number of hitobjects in the beatmap. + /// Defaults to -1 (meaning not-yet-calculated). /// int TotalObjectCount { get; } } diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index 191bb49b0c..ad61292c2e 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -89,8 +89,9 @@ namespace osu.Game.Database /// 35 2023-10-16 Clear key combinations of keybindings that are assigned to more than one action in a given settings section. /// 36 2023-10-26 Add LegacyOnlineID to ScoreInfo. Move osu_scores_*_high IDs stored in OnlineID to LegacyOnlineID. Reset anomalous OnlineIDs. /// 38 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapInfo. + /// 39 2023-12-19 Migrate any EndTimeObjectCount and TotalObjectCount values of 0 to -1 to better identify non-calculated values. /// - private const int schema_version = 38; + private const int schema_version = 39; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. @@ -1095,6 +1096,20 @@ namespace osu.Game.Database break; } + + case 39: + foreach (var b in migration.NewRealm.All()) + { + // Either actually no objects, or processing ran and failed. + // Reset to -1 so the next time they become zero we know that processing was attempted. + if (b.TotalObjectCount == 0 && b.EndTimeObjectCount == 0) + { + b.TotalObjectCount = -1; + b.EndTimeObjectCount = -1; + } + } + + break; } Logger.Log($"Migration completed in {stopwatch.ElapsedMilliseconds}ms"); From 372f930f8bd17b6ab4b928805fa4ee78a2100a48 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 18:27:44 +0900 Subject: [PATCH 0573/1118] Refactor usage of object counts for mania key count lookup to be a bit safer Protects against non-initialised values and also div-by-zero. --- .../Beatmaps/ManiaBeatmapConverter.cs | 25 +++++++++++-------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs index a496049729..def22608d6 100644 --- a/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs +++ b/osu.Game.Rulesets.Mania/Beatmaps/ManiaBeatmapConverter.cs @@ -59,23 +59,26 @@ namespace osu.Game.Rulesets.Mania.Beatmaps { double roundedCircleSize = Math.Round(difficulty.CircleSize); - if (new ManiaRuleset().RulesetInfo.Equals(difficulty.SourceRuleset)) + if (difficulty.SourceRuleset.ShortName == ManiaRuleset.SHORT_NAME) return (int)Math.Max(1, roundedCircleSize); double roundedOverallDifficulty = Math.Round(difficulty.OverallDifficulty); - int countSliderOrSpinner = difficulty.EndTimeObjectCount; + if (difficulty.TotalObjectCount > 0 && difficulty.EndTimeObjectCount >= 0) + { + int countSliderOrSpinner = difficulty.EndTimeObjectCount; - // In osu!stable, this division appears as if it happens on floats, but due to release-mode - // optimisations, it actually ends up happening on doubles. - double percentSpecialObjects = (double)countSliderOrSpinner / difficulty.TotalObjectCount; + // In osu!stable, this division appears as if it happens on floats, but due to release-mode + // optimisations, it actually ends up happening on doubles. + double percentSpecialObjects = (double)countSliderOrSpinner / difficulty.TotalObjectCount; - if (percentSpecialObjects < 0.2) - return 7; - if (percentSpecialObjects < 0.3 || roundedCircleSize >= 5) - return roundedOverallDifficulty > 5 ? 7 : 6; - if (percentSpecialObjects > 0.6) - return roundedOverallDifficulty > 4 ? 5 : 4; + if (percentSpecialObjects < 0.2) + return 7; + if (percentSpecialObjects < 0.3 || roundedCircleSize >= 5) + return roundedOverallDifficulty > 5 ? 7 : 6; + if (percentSpecialObjects > 0.6) + return roundedOverallDifficulty > 4 ? 5 : 4; + } return Math.Max(4, Math.Min((int)roundedOverallDifficulty + 1, 7)); } From 0a64d631e2af017dd042b8786201fd64de7b6e36 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 19:09:47 +0900 Subject: [PATCH 0574/1118] Add support for dual-column advanced stats --- .../Screens/Select/Details/AdvancedStats.cs | 48 ++++++++++++++----- 1 file changed, 35 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index ee805c2d12..3254ac4d99 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -64,21 +64,43 @@ namespace osu.Game.Screens.Select.Details } } - public AdvancedStats() + public AdvancedStats(int columns = 1) { - Child = new FillFlowContainer + switch (columns) { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Children = new[] - { - FirstValue = new StatisticRow(), // circle size/key amount - HpDrain = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsDrain }, - Accuracy = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAccuracy }, - ApproachRate = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAr }, - starDifficulty = new StatisticRow(10, true) { Title = BeatmapsetsStrings.ShowStatsStars }, - }, - }; + case 1: + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new[] + { + FirstValue = new StatisticRow(), // circle size/key amount + HpDrain = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsDrain }, + Accuracy = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAccuracy }, + ApproachRate = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAr }, + starDifficulty = new StatisticRow(10, true) { Title = BeatmapsetsStrings.ShowStatsStars }, + }, + }; + break; + + case 2: + Child = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Full, + Children = new[] + { + FirstValue = new StatisticRow { Width = 0.5f }, // circle size/key amount + HpDrain = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsDrain, Width = 0.5f }, + Accuracy = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAccuracy, Width = 0.5f }, + ApproachRate = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAr, Width = 0.5f }, + starDifficulty = new StatisticRow(10, true) { Title = BeatmapsetsStrings.ShowStatsStars, Width = 0.5f }, + }, + }; + break; + } } [BackgroundDependencyLoader] From 07bb0805e9b1980ed26212ab485193b28fa11baf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 19:09:56 +0900 Subject: [PATCH 0575/1118] Move advanced stats to a more permanent location on song select --- osu.Game/Screens/Select/BeatmapDetails.cs | 8 --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- osu.Game/Screens/Select/SongSelect.cs | 55 ++++++++++++++++++++- 3 files changed, 54 insertions(+), 11 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 179323176a..e1166b5abe 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -28,7 +28,6 @@ namespace osu.Game.Screens.Select private const float spacing = 10; private const float transition_duration = 250; - private readonly AdvancedStats advanced; private readonly UserRatings ratingsDisplay; private readonly MetadataSection description, source, tags; private readonly Container failRetryContainer; @@ -109,12 +108,6 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding { Right = spacing / 2 }, Children = new[] { - new DetailBox().WithChild(advanced = new AdvancedStats - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = spacing, Top = spacing * 2, Bottom = spacing }, - }), new DetailBox().WithChild(new OnlineViewContainer(string.Empty) { RelativeSizeAxes = Axes.X, @@ -180,7 +173,6 @@ namespace osu.Game.Screens.Select private void updateStatistics() { - advanced.BeatmapInfo = BeatmapInfo; description.Metadata = BeatmapInfo?.DifficultyName ?? string.Empty; source.Metadata = BeatmapInfo?.Metadata.Source ?? string.Empty; tags.Metadata = BeatmapInfo?.Metadata.Tags ?? string.Empty; diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 8bbf569566..a9ac30394f 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -305,7 +305,7 @@ namespace osu.Game.Screens.Select }, infoLabelContainer = new FillFlowContainer { - Margin = new MarginPadding { Top = 20 }, + Margin = new MarginPadding { Top = 8 }, Spacing = new Vector2(20, 0), AutoSizeAxes = Axes.Both, } diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index dfea4e3794..0f2b49f9bf 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -13,6 +13,8 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Bindings; @@ -35,6 +37,7 @@ using osu.Game.Screens.Backgrounds; using osu.Game.Screens.Edit; using osu.Game.Screens.Menu; using osu.Game.Screens.Play; +using osu.Game.Screens.Select.Details; using osu.Game.Screens.Select.Options; using osu.Game.Skinning; using osuTK; @@ -45,7 +48,7 @@ namespace osu.Game.Screens.Select { public abstract partial class SongSelect : ScreenWithBeatmapBackground, IKeyBindingHandler { - public static readonly float WEDGE_HEIGHT = 245; + public static readonly float WEDGE_HEIGHT = 200; protected const float BACKGROUND_BLUR = 20; private const float left_area_padding = 20; @@ -132,6 +135,8 @@ namespace osu.Game.Screens.Select private IDisposable? modSelectOverlayRegistration; + private AdvancedStats advancedStats = null!; + [Resolved] private MusicController music { get; set; } = null!; @@ -253,12 +258,56 @@ namespace osu.Game.Screens.Select }, }, new Container + { + RelativeSizeAxes = Axes.X, + Height = 90, + Padding = new MarginPadding(10) + { + Left = left_area_padding, + Right = left_area_padding * 2, + }, + Y = WEDGE_HEIGHT, + Children = new Drawable[] + { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 10, + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Hollow = true, + Colour = new Color4(130, 204, 255, 15), + Radius = 10, + }, + Children = new Drawable[] + { + new Box + { + Colour = new Color4(130, 204, 255, 40), + Blending = BlendingParameters.Additive, + RelativeSizeAxes = Axes.Both, + }, + advancedStats = new AdvancedStats(2) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Width = 0.8f, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }, + } + }, + } + }, + new Container { RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Bottom = Footer.HEIGHT, - Top = WEDGE_HEIGHT, + Top = WEDGE_HEIGHT + 70, Left = left_area_padding, Right = left_area_padding * 2, }, @@ -797,6 +846,8 @@ namespace osu.Game.Screens.Select ModSelect.Beatmap = beatmap; + advancedStats.BeatmapInfo = beatmap.BeatmapInfo; + bool beatmapSelected = beatmap is not DummyWorkingBeatmap; if (beatmapSelected) From 3b848e85037a28afcc9fa43d99603af1c5cf456f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 19:18:36 +0900 Subject: [PATCH 0576/1118] Tidy up visual look --- osu.Game/Screens/Select/BeatmapDetails.cs | 24 +++++++---- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 2 +- .../Screens/Select/Details/AdvancedStats.cs | 43 ++++++++++++------- osu.Game/Screens/Select/SongSelect.cs | 4 +- 4 files changed, 47 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index e1166b5abe..679ebfcf48 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -3,9 +3,9 @@ using System.Linq; using osu.Framework.Allocation; -using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -67,12 +67,24 @@ namespace osu.Game.Screens.Select public BeatmapDetails() { + CornerRadius = 10; + Masking = true; + + EdgeEffect = new EdgeEffectParameters + { + Type = EdgeEffectType.Glow, + Hollow = true, + Colour = new Color4(130, 204, 255, 15), + Radius = 10, + }; + Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), + Colour = new Color4(130, 204, 255, 40), + Blending = BlendingParameters.Additive, }, new Container { @@ -122,7 +134,8 @@ namespace osu.Game.Screens.Select }, new OsuScrollContainer { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + Height = 250, Width = 0.5f, ScrollbarVisible = false, Padding = new MarginPadding { Left = spacing / 2 }, @@ -271,11 +284,6 @@ namespace osu.Game.Screens.Select InternalChildren = new Drawable[] { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = Color4.Black.Opacity(0.5f), - }, content = new Container { RelativeSizeAxes = Axes.X, diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index a9ac30394f..2613857998 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -61,7 +61,7 @@ namespace osu.Game.Screens.Select { Type = EdgeEffectType.Glow, Colour = new Color4(130, 204, 255, 150), - Radius = 20, + Radius = 15, Roundness = 15, }; } diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 3254ac4d99..66a913fda1 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -311,23 +311,36 @@ namespace osu.Game.Screens.Select.Details Font = OsuFont.GetFont(size: 12) }, }, - bar = new Bar + new Container { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - Height = 5, - BackgroundColour = Color4.White.Opacity(0.5f), - Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 }, - }, - ModBar = new Bar - { - Origin = Anchor.CentreLeft, - Anchor = Anchor.CentreLeft, - RelativeSizeAxes = Axes.X, - Alpha = 0.5f, - Height = 5, + RelativeSizeAxes = Axes.Both, Padding = new MarginPadding { Left = name_width + 10, Right = value_width + 10 }, + Children = new Drawable[] + { + new Container + { + Origin = Anchor.CentreLeft, + Anchor = Anchor.CentreLeft, + RelativeSizeAxes = Axes.X, + Height = 5, + + CornerRadius = 2, + Masking = true, + Children = new Drawable[] + { + bar = new Bar + { + RelativeSizeAxes = Axes.Both, + BackgroundColour = Color4.White.Opacity(0.5f), + }, + ModBar = new Bar + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.5f, + }, + } + }, + } }, new Container { diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 0f2b49f9bf..208a8eb321 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -240,7 +240,7 @@ namespace osu.Game.Screens.Select Origin = Anchor.BottomLeft, Anchor = Anchor.BottomLeft, RelativeSizeAxes = Axes.Both, - Padding = new MarginPadding { Top = left_area_padding }, + Padding = new MarginPadding { Top = 5 }, Children = new Drawable[] { new LeftSideInteractionContainer(() => Carousel.ScrollToSelected()) @@ -264,7 +264,7 @@ namespace osu.Game.Screens.Select Padding = new MarginPadding(10) { Left = left_area_padding, - Right = left_area_padding * 2, + Right = left_area_padding * 2 + 5, }, Y = WEDGE_HEIGHT, Children = new Drawable[] From 2c5ca9c1c88b6c80cea70d47d5fdb121825e0146 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 19:31:05 +0900 Subject: [PATCH 0577/1118] Change default song select tab display to local leaderboard --- osu.Game/Configuration/OsuConfigManager.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index ea526c6d54..0df870655f 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -37,7 +37,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.Ruleset, string.Empty); SetDefault(OsuSetting.Skin, SkinInfo.ARGON_SKIN.ToString()); - SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Details); + SetDefault(OsuSetting.BeatmapDetailTab, PlayBeatmapDetailArea.TabType.Local); SetDefault(OsuSetting.BeatmapDetailModsFilter, false); SetDefault(OsuSetting.ShowConvertedBeatmaps, true); From 502e3edac3ea2c5082718ca49d87064fdea4e92e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 19:39:48 +0900 Subject: [PATCH 0578/1118] Add missing invalidation call --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 47cdbe34f4..fe7eee701f 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -290,6 +290,8 @@ namespace osu.Game.Screens.Select { foreach (var set in removeableSets) removeBeatmapSet(set); + + invalidateAfterChange(); }); } From c556475c2c3522729cc1cf17b02b1497730d6e66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 19:46:30 +0900 Subject: [PATCH 0579/1118] Revert to using a more manual approach to holding focus --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 23 +++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 30d7b6191e..baa7e594c1 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -132,6 +132,8 @@ namespace osu.Game.Overlays.Mods protected ShearedToggleButton? CustomisationButton { get; private set; } protected SelectAllModsButton? SelectAllModsButton { get; set; } + private bool textBoxShouldFocus; + private Sample? columnAppearSample; private WorkingBeatmap? beatmap; @@ -510,9 +512,9 @@ namespace osu.Game.Overlays.Mods TopLevelContent.MoveToY(-modAreaHeight, transition_duration, Easing.InOutCubic); if (customisationVisible.Value) - GetContainingInputManager().ChangeFocus(modSettingsArea); + SearchTextBox.KillFocus(); else - Scheduler.Add(() => GetContainingInputManager().ChangeFocus(null)); + setTextBoxFocus(textBoxShouldFocus); } /// @@ -626,8 +628,7 @@ namespace osu.Game.Overlays.Mods nonFilteredColumnCount += 1; } - if (textSearchStartsActive.Value) - SearchTextBox.HoldFocus = true; + setTextBoxFocus(textSearchStartsActive.Value); } protected override void PopOut() @@ -766,12 +767,20 @@ namespace osu.Game.Overlays.Mods return false; // TODO: should probably eventually support typical platform search shortcuts (`Ctrl-F`, `/`) - SearchTextBox.HoldFocus = !SearchTextBox.HoldFocus; - if (SearchTextBox.HoldFocus) - SearchTextBox.TakeFocus(); + setTextBoxFocus(!textBoxShouldFocus); return true; } + private void setTextBoxFocus(bool keepFocus) + { + textBoxShouldFocus = keepFocus; + + if (textBoxShouldFocus) + SearchTextBox.TakeFocus(); + else + SearchTextBox.KillFocus(); + } + #endregion #region Sample playback control From bbfdd6892ded1baf4e1d3da372e7678fe3e84d1e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 19 Dec 2023 19:58:49 +0900 Subject: [PATCH 0580/1118] Allow choosing "Edit" from any beatmap carousel item --- osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs | 2 +- .../Select/Carousel/CarouselGroupEagerSelect.cs | 2 +- .../Screens/Select/Carousel/DrawableCarouselBeatmap.cs | 2 +- .../Select/Carousel/DrawableCarouselBeatmapSet.cs | 10 +++++++++- osu.Game/Screens/Select/PlaySongSelect.cs | 7 ++++--- osu.Game/Screens/Select/SongSelect.cs | 6 +++--- 6 files changed, 19 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs index 67822a27ee..6d2e938fb7 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselBeatmapSet.cs @@ -45,7 +45,7 @@ namespace osu.Game.Screens.Select.Carousel .ForEach(AddItem); } - protected override CarouselItem? GetNextToSelect() + public override CarouselItem? GetNextToSelect() { if (LastSelected == null || LastSelected.Filtered.Value) { diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index 7f90e05744..b4313c1895 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -110,7 +110,7 @@ namespace osu.Game.Screens.Select.Carousel /// Finds the item this group would select next if it attempted selection /// /// An unfiltered item nearest to the last selected one or null if all items are filtered - protected virtual CarouselItem? GetNextToSelect() + public virtual CarouselItem? GetNextToSelect() { if (Items.Count == 0) return null; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 3c64df656d..baf0a14062 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -91,7 +91,7 @@ namespace osu.Game.Screens.Select.Carousel if (songSelect != null) { - mainMenuItems = songSelect.CreateForwardNavigationMenuItemsForBeatmap(beatmapInfo); + mainMenuItems = songSelect.CreateForwardNavigationMenuItemsForBeatmap(() => beatmapInfo); selectRequested = b => songSelect.FinaliseSelection(b); } diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index dd711b2513..f16e92a82a 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -44,6 +44,8 @@ namespace osu.Game.Screens.Select.Carousel private Task? beatmapsLoadTask; + private MenuItem[]? mainMenuItems; + [Resolved] private BeatmapManager manager { get; set; } = null!; @@ -57,8 +59,11 @@ namespace osu.Game.Screens.Select.Carousel } [BackgroundDependencyLoader] - private void load(BeatmapSetOverlay? beatmapOverlay) + private void load(BeatmapSetOverlay? beatmapOverlay, SongSelect? songSelect) { + if (songSelect != null) + mainMenuItems = songSelect.CreateForwardNavigationMenuItemsForBeatmap(() => (((CarouselBeatmapSet)Item!).GetNextToSelect() as CarouselBeatmap)!.BeatmapInfo); + restoreHiddenRequested = s => { foreach (var b in s.Beatmaps) @@ -222,6 +227,9 @@ namespace osu.Game.Screens.Select.Carousel if (Item?.State.Value == CarouselItemState.NotSelected) items.Add(new OsuMenuItem("Expand", MenuItemType.Highlighted, () => Item.State.Value = CarouselItemState.Selected)); + if (mainMenuItems != null) + items.AddRange(mainMenuItems); + if (beatmapSet.OnlineID > 0 && viewDetails != null) items.Add(new OsuMenuItem("Details...", MenuItemType.Standard, () => viewDetails(beatmapSet.OnlineID))); diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 86bebdc2ff..7b7b8857f3 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; @@ -34,10 +35,10 @@ namespace osu.Game.Screens.Select public override bool AllowExternalScreenChange => true; - public override MenuItem[] CreateForwardNavigationMenuItemsForBeatmap(BeatmapInfo beatmap) => new MenuItem[] + public override MenuItem[] CreateForwardNavigationMenuItemsForBeatmap(Func getBeatmap) => new MenuItem[] { - new OsuMenuItem(ButtonSystemStrings.Play.ToSentence(), MenuItemType.Highlighted, () => FinaliseSelection(beatmap)), - new OsuMenuItem(ButtonSystemStrings.Edit.ToSentence(), MenuItemType.Standard, () => Edit(beatmap)) + new OsuMenuItem(ButtonSystemStrings.Play.ToSentence(), MenuItemType.Highlighted, () => FinaliseSelection(getBeatmap())), + new OsuMenuItem(ButtonSystemStrings.Edit.ToSentence(), MenuItemType.Standard, () => Edit(getBeatmap())) }; protected override UserActivity InitialActivity => new UserActivity.ChoosingBeatmap(); diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index dfea4e3794..f4d1028747 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -89,11 +89,11 @@ namespace osu.Game.Screens.Select /// Creates any "action" menu items for the provided beatmap (ie. "Select", "Play", "Edit"). /// These will always be placed at the top of the context menu, with common items added below them. /// - /// The beatmap to create items for. + /// The beatmap to create items for. /// The menu items. - public virtual MenuItem[] CreateForwardNavigationMenuItemsForBeatmap(BeatmapInfo beatmap) => new MenuItem[] + public virtual MenuItem[] CreateForwardNavigationMenuItemsForBeatmap(Func getBeatmap) => new MenuItem[] { - new OsuMenuItem(@"Select", MenuItemType.Highlighted, () => FinaliseSelection(beatmap)) + new OsuMenuItem(@"Select", MenuItemType.Highlighted, () => FinaliseSelection(getBeatmap())) }; [Resolved] From d793d1cea16de4098c58fb5c50a3e20142a7a996 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Dec 2023 14:52:16 +0100 Subject: [PATCH 0581/1118] Add test coverage of desired input handling behaviour --- .../TestSceneModSelectOverlay.cs | 29 +++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 80be4412b3..4b101a52f4 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -572,7 +572,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestTextSearchActiveByDefault() { - configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, true); + AddStep("text search starts active", () => configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, true)); createScreen(); AddUntilStep("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus); @@ -587,7 +587,7 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestTextSearchNotActiveByDefault() { - configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, false); + AddStep("text search does not start active", () => configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, false)); createScreen(); AddUntilStep("search text box not focused", () => !modSelectOverlay.SearchTextBox.HasFocus); @@ -599,6 +599,31 @@ namespace osu.Game.Tests.Visual.UserInterface AddAssert("search text box unfocused", () => !modSelectOverlay.SearchTextBox.HasFocus); } + [Test] + public void TestTextSearchDoesNotBlockCustomisationPanelKeyboardInteractions() + { + AddStep("text search starts active", () => configManager.SetValue(OsuSetting.ModSelectTextSearchStartsActive, true)); + createScreen(); + + AddUntilStep("search text box focused", () => modSelectOverlay.SearchTextBox.HasFocus); + + AddStep("select DT", () => SelectedMods.Value = new Mod[] { new OsuModDoubleTime() }); + AddAssert("DT selected", () => modSelectOverlay.ChildrenOfType().Count(panel => panel.Active.Value), () => Is.EqualTo(1)); + + AddStep("open customisation area", () => modSelectOverlay.CustomisationButton!.TriggerClick()); + assertCustomisationToggleState(false, true); + AddStep("hover over mod settings slider", () => + { + var slider = modSelectOverlay.ChildrenOfType().Single().ChildrenOfType>().First(); + InputManager.MoveMouseTo(slider); + }); + AddStep("press right arrow", () => InputManager.PressKey(Key.Right)); + AddAssert("DT speed changed", () => !SelectedMods.Value.OfType().Single().SpeedChange.IsDefault); + + AddStep("close customisation area", () => InputManager.PressKey(Key.Escape)); + AddUntilStep("search text box reacquired focus", () => modSelectOverlay.SearchTextBox.HasFocus); + } + [Test] public void TestDeselectAllViaKey() { From 3f41c20ac6badc09efb5cf61205ecc106c1cabc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 19 Dec 2023 17:25:15 +0100 Subject: [PATCH 0582/1118] Use safer fix for now --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 11eacd1c6b..d2321f4fc4 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -305,11 +305,10 @@ namespace osu.Game.Database // The clamp from below to `comboPortionFromLongestComboInScoreV1` targets near-FC scores wherein // the player had bad accuracy at the end of their longest combo, which causes the division by accuracy // to underestimate the combo portion. - // The clamp from above to `maximumAchievableComboPortionInScoreV1` targets FC scores wherein - // the player had bad accuracy at the start of the map, which causes the division by accuracy - // to overestimate the combo portion. - double comboPortionInScoreV1 = Math.Clamp(maximumAchievableComboPortionInScoreV1 * comboProportion / score.Accuracy, - comboPortionFromLongestComboInScoreV1, maximumAchievableComboPortionInScoreV1); + // Ideally, this would be clamped from above to `maximumAchievableComboPortionInScoreV1` too, + // but in practice this appears to fail for some scores (https://github.com/ppy/osu/pull/25876#issuecomment-1862248413). + // TODO: investigate the above more closely + double comboPortionInScoreV1 = Math.Max(maximumAchievableComboPortionInScoreV1 * comboProportion / score.Accuracy, comboPortionFromLongestComboInScoreV1); // Calculate how many times the longest combo the user has achieved in the play can repeat // without exceeding the combo portion in score V1 as achieved by the player. From ec578e1d9f62697eee1b2b0b9f53c6a7727c0ca7 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 19 Dec 2023 21:20:21 +0100 Subject: [PATCH 0583/1118] fix near-zero length sliders n stuff being placeable --- .../Edit/Blueprints/BananaShowerPlacementBlueprint.cs | 3 ++- .../Edit/Blueprints/JuiceStreamPlacementBlueprint.cs | 3 ++- .../Edit/Blueprints/HoldNotePlacementBlueprint.cs | 3 ++- .../Edit/Blueprints/TaikoSpanPlacementBlueprint.cs | 3 ++- osu.Game/Rulesets/Objects/SliderPath.cs | 2 +- 5 files changed, 9 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs index 1e63d32c41..6902f78172 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/BananaShowerPlacementBlueprint.cs @@ -3,6 +3,7 @@ using System; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Edit; @@ -17,7 +18,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private double placementStartTime; private double placementEndTime; - protected override bool IsValidForPlacement => HitObject.Duration > 0; + protected override bool IsValidForPlacement => Precision.DefinitelyBigger(HitObject.Duration, 0); public BananaShowerPlacementBlueprint() { diff --git a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs index 9e50b5a80f..c8c8db1ebd 100644 --- a/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Catch/Edit/Blueprints/JuiceStreamPlacementBlueprint.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Input; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Rulesets.Catch.Edit.Blueprints.Components; using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Edit; @@ -24,7 +25,7 @@ namespace osu.Game.Rulesets.Catch.Edit.Blueprints private InputManager inputManager = null!; - protected override bool IsValidForPlacement => HitObject.Duration > 0; + protected override bool IsValidForPlacement => Precision.DefinitelyBigger(HitObject.Duration, 0); public JuiceStreamPlacementBlueprint() { diff --git a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs index 02ad1655b5..991b7f476c 100644 --- a/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs +++ b/osu.Game.Rulesets.Mania/Edit/Blueprints/HoldNotePlacementBlueprint.cs @@ -5,6 +5,7 @@ using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Mania.Edit.Blueprints.Components; using osu.Game.Rulesets.Mania.Objects; @@ -23,7 +24,7 @@ namespace osu.Game.Rulesets.Mania.Edit.Blueprints [Resolved] private IScrollingInfo scrollingInfo { get; set; } = null!; - protected override bool IsValidForPlacement => HitObject.Duration > 0; + protected override bool IsValidForPlacement => Precision.DefinitelyBigger(HitObject.Duration, 0); public HoldNotePlacementBlueprint() : base(new HoldNote()) diff --git a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs index bc4129c982..b0919417a4 100644 --- a/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Taiko/Edit/Blueprints/TaikoSpanPlacementBlueprint.cs @@ -6,6 +6,7 @@ using System; using osu.Framework.Graphics; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -25,7 +26,7 @@ namespace osu.Game.Rulesets.Taiko.Edit.Blueprints private readonly IHasDuration spanPlacementObject; - protected override bool IsValidForPlacement => spanPlacementObject.Duration > 0; + protected override bool IsValidForPlacement => Precision.DefinitelyBigger(spanPlacementObject.Duration, 0); public TaikoSpanPlacementBlueprint(HitObject hitObject) : base(hitObject) diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index e9a192669f..dc71608132 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -31,7 +31,7 @@ namespace osu.Game.Rulesets.Objects /// public readonly Bindable ExpectedDistance = new Bindable(); - public bool HasValidLength => Distance > 0; + public bool HasValidLength => Precision.DefinitelyBigger(Distance, 0); /// /// The control points of the path. From c167f10ad5ff9b82b26ee5f3eb3b709fd7467f82 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Tue, 19 Dec 2023 21:20:45 +0100 Subject: [PATCH 0584/1118] fix crash from dragging near zero-length repeating object in timeline --- .../Compose/Components/Timeline/TimelineHitObjectBlueprint.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs index 77afad2d4f..47dc3fb82e 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/TimelineHitObjectBlueprint.cs @@ -409,7 +409,7 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline double lengthOfOneRepeat = repeatHitObject.Duration / (repeatHitObject.RepeatCount + 1); int proposedCount = Math.Max(0, (int)Math.Round(proposedDuration / lengthOfOneRepeat) - 1); - if (proposedCount == repeatHitObject.RepeatCount || lengthOfOneRepeat == 0) + if (proposedCount == repeatHitObject.RepeatCount || Precision.AlmostEquals(lengthOfOneRepeat, 0)) return; repeatHitObject.RepeatCount = proposedCount; From 1b004dbebce337df0a38b6e3d4408758460d4399 Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Tue, 19 Dec 2023 12:20:44 -0800 Subject: [PATCH 0585/1118] Allow Relax to fail and remove failable mod exclusions Allows the Relax mod to fail, and remove NF/PF/SD mod exclusion ref: https://github.com/ppy/osu/discussions/13229 --- osu.Game/Rulesets/Mods/ModFailCondition.cs | 2 +- osu.Game/Rulesets/Mods/ModNoFail.cs | 2 +- osu.Game/Rulesets/Mods/ModRelax.cs | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index e671c065cf..a116826e32 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IApplicableFailOverride { - public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModRelax) }; + public override Type[] IncompatibleMods => new[] { typeof(ModBlockFail) }; [SettingSource("Restart on fail", "Automatically restarts when failed.")] public BindableBool Restart { get; } = new BindableBool(); diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 8c61d948a4..8fca98b018 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -16,6 +16,6 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyReduction; public override LocalisableString Description => "You can't fail, no matter what."; public override double ScoreMultiplier => 0.5; - public override Type[] IncompatibleMods => new[] { typeof(ModRelax), typeof(ModFailCondition) }; + public override Type[] IncompatibleMods => new[] { typeof(ModFailCondition) }; } } diff --git a/osu.Game/Rulesets/Mods/ModRelax.cs b/osu.Game/Rulesets/Mods/ModRelax.cs index 49c10339ee..3d672b5ef8 100644 --- a/osu.Game/Rulesets/Mods/ModRelax.cs +++ b/osu.Game/Rulesets/Mods/ModRelax.cs @@ -7,13 +7,13 @@ using osu.Game.Graphics; namespace osu.Game.Rulesets.Mods { - public abstract class ModRelax : ModBlockFail + public abstract class ModRelax : Mod { public override string Name => "Relax"; public override string Acronym => "RX"; public override IconUsage? Icon => OsuIcon.ModRelax; public override ModType Type => ModType.Automation; public override double ScoreMultiplier => 0.1; - public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModFailCondition) }; + public override Type[] IncompatibleMods => new[] { typeof(ModAutoplay) }; } } From 85e5d74a16eead4bc1dc01049698be56d8669de1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 12:42:06 +0900 Subject: [PATCH 0586/1118] Apply proposed changes --- osu.Game/Screens/Select/BeatmapDetails.cs | 13 +------ .../Screens/Select/Details/AdvancedStats.cs | 34 ++++++++++++++++--- osu.Game/Screens/Select/SongSelect.cs | 13 ++----- 3 files changed, 32 insertions(+), 28 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapDetails.cs b/osu.Game/Screens/Select/BeatmapDetails.cs index 679ebfcf48..dec2c1c1de 100644 --- a/osu.Game/Screens/Select/BeatmapDetails.cs +++ b/osu.Game/Screens/Select/BeatmapDetails.cs @@ -5,7 +5,6 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Game.Beatmaps; using osu.Game.Graphics; @@ -19,7 +18,6 @@ using osu.Game.Overlays.BeatmapSet; using osu.Game.Resources.Localisation.Web; using osu.Game.Screens.Select.Details; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Select { @@ -70,21 +68,12 @@ namespace osu.Game.Screens.Select CornerRadius = 10; Masking = true; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Hollow = true, - Colour = new Color4(130, 204, 255, 15), - Radius = 10, - }; - Children = new Drawable[] { new Box { RelativeSizeAxes = Axes.Both, - Colour = new Color4(130, 204, 255, 40), - Blending = BlendingParameters.Additive, + Colour = Colour4.Black.Opacity(0.3f), }, new Container { diff --git a/osu.Game/Screens/Select/Details/AdvancedStats.cs b/osu.Game/Screens/Select/Details/AdvancedStats.cs index 66a913fda1..0d68a0ec3c 100644 --- a/osu.Game/Screens/Select/Details/AdvancedStats.cs +++ b/osu.Game/Screens/Select/Details/AdvancedStats.cs @@ -92,11 +92,35 @@ namespace osu.Game.Screens.Select.Details Direction = FillDirection.Full, Children = new[] { - FirstValue = new StatisticRow { Width = 0.5f }, // circle size/key amount - HpDrain = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsDrain, Width = 0.5f }, - Accuracy = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAccuracy, Width = 0.5f }, - ApproachRate = new StatisticRow { Title = BeatmapsetsStrings.ShowStatsAr, Width = 0.5f }, - starDifficulty = new StatisticRow(10, true) { Title = BeatmapsetsStrings.ShowStatsStars, Width = 0.5f }, + FirstValue = new StatisticRow + { + Width = 0.5f, + Padding = new MarginPadding { Right = 5, Vertical = 2.5f }, + }, // circle size/key amount + HpDrain = new StatisticRow + { + Title = BeatmapsetsStrings.ShowStatsDrain, + Width = 0.5f, + Padding = new MarginPadding { Left = 5, Vertical = 2.5f }, + }, + Accuracy = new StatisticRow + { + Title = BeatmapsetsStrings.ShowStatsAccuracy, + Width = 0.5f, + Padding = new MarginPadding { Right = 5, Vertical = 2.5f }, + }, + ApproachRate = new StatisticRow + { + Title = BeatmapsetsStrings.ShowStatsAr, + Width = 0.5f, + Padding = new MarginPadding { Left = 5, Vertical = 2.5f }, + }, + starDifficulty = new StatisticRow(10, true) + { + Title = BeatmapsetsStrings.ShowStatsStars, + Width = 0.5f, + Padding = new MarginPadding { Right = 5, Vertical = 2.5f }, + }, }, }; break; diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 208a8eb321..0d302dc561 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -13,7 +13,6 @@ using osu.Framework.Bindables; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.UserInterface; @@ -274,28 +273,20 @@ namespace osu.Game.Screens.Select RelativeSizeAxes = Axes.Both, Masking = true, CornerRadius = 10, - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Hollow = true, - Colour = new Color4(130, 204, 255, 15), - Radius = 10, - }, Children = new Drawable[] { new Box { - Colour = new Color4(130, 204, 255, 40), - Blending = BlendingParameters.Additive, RelativeSizeAxes = Axes.Both, + Colour = Colour4.Black.Opacity(0.3f), }, advancedStats = new AdvancedStats(2) { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Width = 0.8f, Anchor = Anchor.Centre, Origin = Anchor.Centre, + Padding = new MarginPadding(10) }, } }, From 856c59f7f75d9322e03d98b874ab04691c5bc968 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 14:08:12 +0900 Subject: [PATCH 0587/1118] Fix thread safety of `OnlineMetadataClient.UserStates` Closes https://github.com/ppy/osu-framework/issues/6081. --- osu.Game/Online/Metadata/OnlineMetadataClient.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Online/Metadata/OnlineMetadataClient.cs b/osu.Game/Online/Metadata/OnlineMetadataClient.cs index 27093d7961..b916386b06 100644 --- a/osu.Game/Online/Metadata/OnlineMetadataClient.cs +++ b/osu.Game/Online/Metadata/OnlineMetadataClient.cs @@ -97,8 +97,11 @@ namespace osu.Game.Online.Metadata { if (!connected.NewValue) { - isWatchingUserPresence.Value = false; - userStates.Clear(); + Schedule(() => + { + isWatchingUserPresence.Value = false; + userStates.Clear(); + }); return; } @@ -187,13 +190,13 @@ namespace osu.Game.Online.Metadata public override Task UserPresenceUpdated(int userId, UserPresence? presence) { - lock (userStates) + Schedule(() => { if (presence != null) userStates[userId] = presence.Value; else userStates.Remove(userId); - } + }); return Task.CompletedTask; } @@ -215,8 +218,8 @@ namespace osu.Game.Online.Metadata if (connector?.IsConnected.Value != true) throw new OperationCanceledException(); - // must happen synchronously before any remote calls to avoid misordering. - userStates.Clear(); + // must happen synchronously before any remote calls to avoid mis-ordering. + Schedule(() => userStates.Clear()); Debug.Assert(connection != null); await connection.InvokeAsync(nameof(IMetadataServer.EndWatchingUserPresence)).ConfigureAwait(false); } From d7603e8021a0a0ae3cd09ee73344c76054cbcd8a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 14:32:38 +0900 Subject: [PATCH 0588/1118] Adjust "classic" mod multiplier to 0.96x Following discussions on discord, this seems like the most agreed upon value. Increasing this is important so that imported legacy scores don't lose too much value. --- osu.Game/Rulesets/Mods/ModClassic.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModClassic.cs b/osu.Game/Rulesets/Mods/ModClassic.cs index 42fdee0402..16cb928bd4 100644 --- a/osu.Game/Rulesets/Mods/ModClassic.cs +++ b/osu.Game/Rulesets/Mods/ModClassic.cs @@ -12,7 +12,7 @@ namespace osu.Game.Rulesets.Mods public override string Acronym => "CL"; - public override double ScoreMultiplier => 0.5; + public override double ScoreMultiplier => 0.96; public override IconUsage? Icon => FontAwesome.Solid.History; From 14d2d0d21538e4240211fa839ab6a34256cf797b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 14:50:21 +0900 Subject: [PATCH 0589/1118] Remove `ModBlockFail` Was only being used by `NoFail` now. --- osu.Game/Rulesets/Mods/ModBlockFail.cs | 31 ---------------------- osu.Game/Rulesets/Mods/ModFailCondition.cs | 2 +- osu.Game/Rulesets/Mods/ModNoFail.cs | 24 ++++++++++++++++- 3 files changed, 24 insertions(+), 33 deletions(-) delete mode 100644 osu.Game/Rulesets/Mods/ModBlockFail.cs diff --git a/osu.Game/Rulesets/Mods/ModBlockFail.cs b/osu.Game/Rulesets/Mods/ModBlockFail.cs deleted file mode 100644 index cdfb36ebbc..0000000000 --- a/osu.Game/Rulesets/Mods/ModBlockFail.cs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Bindables; -using osu.Game.Configuration; -using osu.Game.Screens.Play; - -namespace osu.Game.Rulesets.Mods -{ - public abstract class ModBlockFail : Mod, IApplicableFailOverride, IApplicableToHUD, IReadFromConfig - { - private readonly Bindable showHealthBar = new Bindable(); - - /// - /// We never fail, 'yo. - /// - public bool PerformFail() => false; - - public bool RestartOnFail => false; - - public void ReadFromConfig(OsuConfigManager config) - { - config.BindWith(OsuSetting.ShowHealthDisplayWhenCantFail, showHealthBar); - } - - public void ApplyToHUD(HUDOverlay overlay) - { - overlay.ShowHealthBar.BindTo(showHealthBar); - } - } -} diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index a116826e32..471c3bfe8d 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IApplicableFailOverride { - public override Type[] IncompatibleMods => new[] { typeof(ModBlockFail) }; + public override Type[] IncompatibleMods => new[] { typeof(ModNoFail) }; [SettingSource("Restart on fail", "Automatically restarts when failed.")] public BindableBool Restart { get; } = new BindableBool(); diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 8fca98b018..0cf81bf4c9 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -2,13 +2,16 @@ // See the LICENCE file in the repository root for full licence text. using System; +using osu.Framework.Bindables; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Configuration; using osu.Game.Graphics; +using osu.Game.Screens.Play; namespace osu.Game.Rulesets.Mods { - public abstract class ModNoFail : ModBlockFail + public abstract class ModNoFail : Mod, IApplicableFailOverride, IApplicableToHUD, IReadFromConfig { public override string Name => "No Fail"; public override string Acronym => "NF"; @@ -17,5 +20,24 @@ namespace osu.Game.Rulesets.Mods public override LocalisableString Description => "You can't fail, no matter what."; public override double ScoreMultiplier => 0.5; public override Type[] IncompatibleMods => new[] { typeof(ModFailCondition) }; + + private readonly Bindable showHealthBar = new Bindable(); + + /// + /// We never fail, 'yo. + /// + public bool PerformFail() => false; + + public bool RestartOnFail => false; + + public void ReadFromConfig(OsuConfigManager config) + { + config.BindWith(OsuSetting.ShowHealthDisplayWhenCantFail, showHealthBar); + } + + public void ApplyToHUD(HUDOverlay overlay) + { + overlay.ShowHealthBar.BindTo(showHealthBar); + } } } From b6f0c98a0907ad1869a19c4dc00c83cedbf4b03c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 14:56:52 +0900 Subject: [PATCH 0590/1118] Also apply to autopilot --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index 56bf0e08e9..bf74b741d5 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -16,7 +16,7 @@ using osu.Game.Rulesets.UI; namespace osu.Game.Rulesets.Osu.Mods { - public class OsuModAutopilot : Mod, IApplicableFailOverride, IUpdatableByPlayfield, IApplicableToDrawableRuleset + public class OsuModAutopilot : Mod, IUpdatableByPlayfield, IApplicableToDrawableRuleset { public override string Name => "Autopilot"; public override string Acronym => "AP"; @@ -37,10 +37,6 @@ namespace osu.Game.Rulesets.Osu.Mods typeof(ModTouchDevice) }; - public bool PerformFail() => false; - - public bool RestartOnFail => false; - private OsuInputManager inputManager = null!; private List replayFrames = null!; From 64f62e7d904a31a31df6b0926aba6284ef4f7087 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 17:31:08 +0900 Subject: [PATCH 0591/1118] Fix song select running updates when screen is not active MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Who would have guessed that `Schedule` calls were there for a reason! I've tidied things up. Most of the changes I've made here are not required – the schedule is the main thing here. The reason the sound was playing is because one-too-many schedules was removed causing beatmap updates to update carousel specifics while still at the player loader screen. Note that the selection sound still plays on returning to song select, but this is not a regression. I'm looking at fixing this in a separate PR because I'm in a good place as far as understanding the logic right now and it would be a waste to leave it broken. Closes https://github.com/ppy/osu/issues/25875. --- osu.Game/Screens/Select/BeatmapCarousel.cs | 100 ++++++++++++--------- 1 file changed, 58 insertions(+), 42 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index fe7eee701f..53529d592b 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -301,6 +301,9 @@ namespace osu.Game.Screens.Select if (loadedTestBeatmaps) return; + var setsRequiringUpdate = new HashSet(); + var setsRequiringRemoval = new HashSet(); + if (changes == null) { // During initial population, we must manually account for the fact that our original query was done on an async thread. @@ -314,67 +317,80 @@ namespace osu.Game.Screens.Select foreach (var id in realmSets) { if (!root.BeatmapSetsByID.ContainsKey(id)) - updateBeatmapSet(realm.Realm.Find(id)!.Detach()); + setsRequiringUpdate.Add(realm.Realm.Find(id)!.Detach()); } foreach (var id in root.BeatmapSetsByID.Keys) { if (!realmSets.Contains(id)) - removeBeatmapSet(id); + setsRequiringRemoval.Add(id); } + } + else + { + foreach (int i in changes.NewModifiedIndices) + setsRequiringUpdate.Add(sender[i].Detach()); - invalidateAfterChange(); - BeatmapSetsLoaded = true; - return; + foreach (int i in changes.InsertedIndices) + setsRequiringUpdate.Add(sender[i].Detach()); } - foreach (int i in changes.NewModifiedIndices) - updateBeatmapSet(sender[i].Detach()); - - foreach (int i in changes.InsertedIndices) - updateBeatmapSet(sender[i].Detach()); - - if (changes.DeletedIndices.Length > 0 && SelectedBeatmapInfo != null) + // All local operations must be scheduled. + // + // If we don't schedule, beatmaps getting changed while song select is suspended (ie. last played being updated) + // will cause unexpected sounds and operations to occur in the background. + Schedule(() => { - // If SelectedBeatmapInfo is non-null, the set should also be non-null. - Debug.Assert(SelectedBeatmapSet != null); - - // To handle the beatmap update flow, attempt to track selection changes across delete-insert transactions. - // When an update occurs, the previous beatmap set is either soft or hard deleted. - // Check if the current selection was potentially deleted by re-querying its validity. - bool selectedSetMarkedDeleted = sender.Realm.Find(SelectedBeatmapSet.ID)?.DeletePending != false; - - int[] modifiedAndInserted = changes.NewModifiedIndices.Concat(changes.InsertedIndices).ToArray(); - - if (selectedSetMarkedDeleted && modifiedAndInserted.Any()) + try { - // If it is no longer valid, make the bold assumption that an updated version will be available in the modified/inserted indices. - // This relies on the full update operation being in a single transaction, so please don't change that. - foreach (int i in modifiedAndInserted) + foreach (var set in setsRequiringRemoval) + removeBeatmapSet(set); + + foreach (var set in setsRequiringUpdate) + updateBeatmapSet(set); + + if (changes?.DeletedIndices.Length > 0 && SelectedBeatmapInfo != null) { - var beatmapSetInfo = sender[i]; + // If SelectedBeatmapInfo is non-null, the set should also be non-null. + Debug.Assert(SelectedBeatmapSet != null); - foreach (var beatmapInfo in beatmapSetInfo.Beatmaps) + // To handle the beatmap update flow, attempt to track selection changes across delete-insert transactions. + // When an update occurs, the previous beatmap set is either soft or hard deleted. + // Check if the current selection was potentially deleted by re-querying its validity. + bool selectedSetMarkedDeleted = realm.Run(r => r.Find(SelectedBeatmapSet.ID)?.DeletePending != false); + + if (selectedSetMarkedDeleted && setsRequiringUpdate.Any()) { - if (!((IBeatmapMetadataInfo)beatmapInfo.Metadata).Equals(SelectedBeatmapInfo.Metadata)) - continue; - - // Best effort matching. We can't use ID because in the update flow a new version will get its own GUID. - if (beatmapInfo.DifficultyName == SelectedBeatmapInfo.DifficultyName) + // If it is no longer valid, make the bold assumption that an updated version will be available in the modified/inserted indices. + // This relies on the full update operation being in a single transaction, so please don't change that. + foreach (var set in setsRequiringUpdate) { - SelectBeatmap(beatmapInfo); - return; + foreach (var beatmapInfo in set.Beatmaps) + { + if (!((IBeatmapMetadataInfo)beatmapInfo.Metadata).Equals(SelectedBeatmapInfo.Metadata)) + continue; + + // Best effort matching. We can't use ID because in the update flow a new version will get its own GUID. + if (beatmapInfo.DifficultyName == SelectedBeatmapInfo.DifficultyName) + { + SelectBeatmap(beatmapInfo); + return; + } + } } + + // If a direct selection couldn't be made, it's feasible that the difficulty name (or beatmap metadata) changed. + // Let's attempt to follow set-level selection anyway. + SelectBeatmap(setsRequiringUpdate.First().Beatmaps.First()); } } - - // If a direct selection couldn't be made, it's feasible that the difficulty name (or beatmap metadata) changed. - // Let's attempt to follow set-level selection anyway. - SelectBeatmap(sender[modifiedAndInserted.First()].Beatmaps.First()); } - } - - invalidateAfterChange(); + finally + { + BeatmapSetsLoaded = true; + invalidateAfterChange(); + } + }); } private void beatmapsChanged(IRealmCollection sender, ChangeSet? changes) From 104fbbde9460d42c10fe5d4d60db2dbfc4218421 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 20 Dec 2023 18:35:45 +0900 Subject: [PATCH 0592/1118] Change mania scoring to match ScoreV2 --- .../Objects/Drawables/DrawableNote.cs | 40 ----------------- .../Drawables/DrawableNotePerfectBonus.cs | 26 ----------- .../Scoring/ManiaScoreProcessor.cs | 43 +++++++++++++++++-- osu.Game.Rulesets.Mania/UI/Column.cs | 1 - 4 files changed, 40 insertions(+), 70 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNotePerfectBonus.cs diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs index c70dfcb761..680009bc4c 100644 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs +++ b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNote.cs @@ -13,8 +13,6 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Skinning.Default; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Screens.Edit; @@ -40,8 +38,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables private Drawable headPiece; - private DrawableNotePerfectBonus perfectBonus; - public DrawableNote() : this(null) { @@ -93,10 +89,7 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables if (!userTriggered) { if (!HitObject.HitWindows.CanBeHit(timeOffset)) - { - perfectBonus.TriggerResult(false); ApplyResult(r => r.Type = r.Judgement.MinResult); - } return; } @@ -107,16 +100,9 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables result = GetCappedResult(result); - perfectBonus.TriggerResult(result == HitResult.Perfect); ApplyResult(r => r.Type = result); } - public override void MissForcefully() - { - perfectBonus.TriggerResult(false); - base.MissForcefully(); - } - /// /// Some objects in mania may want to limit the max result. /// @@ -137,32 +123,6 @@ namespace osu.Game.Rulesets.Mania.Objects.Drawables { } - protected override void AddNestedHitObject(DrawableHitObject hitObject) - { - switch (hitObject) - { - case DrawableNotePerfectBonus bonus: - AddInternal(perfectBonus = bonus); - break; - } - } - - protected override void ClearNestedHitObjects() - { - RemoveInternal(perfectBonus, false); - } - - protected override DrawableHitObject CreateNestedHitObject(HitObject hitObject) - { - switch (hitObject) - { - case NotePerfectBonus bonus: - return new DrawableNotePerfectBonus(bonus); - } - - return base.CreateNestedHitObject(hitObject); - } - private void updateSnapColour() { if (beatmap == null || HitObject == null) return; diff --git a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNotePerfectBonus.cs b/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNotePerfectBonus.cs deleted file mode 100644 index 70ddb60296..0000000000 --- a/osu.Game.Rulesets.Mania/Objects/Drawables/DrawableNotePerfectBonus.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Rulesets.Mania.Objects.Drawables -{ - public partial class DrawableNotePerfectBonus : DrawableManiaHitObject - { - public override bool DisplayResult => false; - - public DrawableNotePerfectBonus() - : this(null!) - { - } - - public DrawableNotePerfectBonus(NotePerfectBonus hitObject) - : base(hitObject) - { - } - - /// - /// Apply a judgement result. - /// - /// Whether this tick was reached. - internal void TriggerResult(bool hit) => ApplyResult(r => r.Type = hit ? r.Judgement.MaxResult : r.Judgement.MinResult); - } -} diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index e5f9b33c6b..31435ddaad 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -26,13 +26,50 @@ namespace osu.Game.Rulesets.Mania.Scoring protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion) { - return 10000 * comboProgress - + 990000 * Math.Pow(Accuracy.Value, 2 + 2 * Accuracy.Value) * accuracyProgress + return 200000 * comboProgress + + 800000 * Math.Pow(Accuracy.Value, 2 + 2 * Accuracy.Value) * accuracyProgress + bonusPortion; } + protected override double GetNumericResultFor(JudgementResult result) + { + switch (result.Type) + { + case HitResult.Perfect: + return 305; + } + + return base.GetNumericResultFor(result); + } + + protected override double GetMaxNumericResultFor(JudgementResult result) + { + switch (result.Judgement.MaxResult) + { + case HitResult.Perfect: + return 305; + } + + return base.GetMaxNumericResultFor(result); + } + protected override double GetComboScoreChange(JudgementResult result) - => GetNumericResultFor(result) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); + { + double numericResult; + + switch (result.Type) + { + case HitResult.Perfect: + numericResult = 300; + break; + + default: + numericResult = GetNumericResultFor(result); + break; + } + + return numericResult * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); + } private class JudgementOrderComparer : IComparer { diff --git a/osu.Game.Rulesets.Mania/UI/Column.cs b/osu.Game.Rulesets.Mania/UI/Column.cs index 9489281176..6cd55bb099 100644 --- a/osu.Game.Rulesets.Mania/UI/Column.cs +++ b/osu.Game.Rulesets.Mania/UI/Column.cs @@ -109,7 +109,6 @@ namespace osu.Game.Rulesets.Mania.UI TopLevelContainer.Add(HitObjectArea.Explosions.CreateProxy()); RegisterPool(10, 50); - RegisterPool(10, 50); RegisterPool(10, 50); RegisterPool(10, 50); RegisterPool(10, 50); From 023bbda7dbb381255e66c84ac209c6510662380f Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 20 Dec 2023 18:43:06 +0900 Subject: [PATCH 0593/1118] Change mania to 85% acc / 15% combo --- osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index 31435ddaad..d5191c880a 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -26,8 +26,8 @@ namespace osu.Game.Rulesets.Mania.Scoring protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion) { - return 200000 * comboProgress - + 800000 * Math.Pow(Accuracy.Value, 2 + 2 * Accuracy.Value) * accuracyProgress + return 150000 * comboProgress + + 850000 * Math.Pow(Accuracy.Value, 2 + 2 * Accuracy.Value) * accuracyProgress + bonusPortion; } From e003462f7d4077d43513d233ae2b4143270be3e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 19:09:07 +0900 Subject: [PATCH 0594/1118] Fix beatmap updates causing one extra carousel selection --- osu.Game/Screens/Select/BeatmapCarousel.cs | 85 +++++++++++-------- .../Carousel/CarouselGroupEagerSelect.cs | 8 +- 2 files changed, 54 insertions(+), 39 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 53529d592b..a51b54b21d 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -455,30 +455,13 @@ namespace osu.Game.Screens.Select private void updateBeatmapSet(BeatmapSetInfo beatmapSet) { - Guid? previouslySelectedID = null; - originalBeatmapSetsDetached.RemoveAll(set => set.ID == beatmapSet.ID); originalBeatmapSetsDetached.Add(beatmapSet.Detach()); - // If the selected beatmap is about to be removed, store its ID so it can be re-selected if required - if (selectedBeatmapSet?.BeatmapSet.ID == beatmapSet.ID) - previouslySelectedID = selectedBeatmap?.BeatmapInfo.ID; - - var removedSets = root.RemoveItemsByID(beatmapSet.ID); - - foreach (var removedSet in removedSets) - { - // If we don't remove this here, it may remain in a hidden state until scrolled off screen. - // Doesn't really affect anything during actual user interaction, but makes testing annoying. - var removedDrawable = Scroll.FirstOrDefault(c => c.Item == removedSet); - if (removedDrawable != null) - expirePanelImmediately(removedDrawable); - } + var newSets = new List(); if (beatmapsSplitOut) { - var newSets = new List(); - foreach (var beatmap in beatmapSet.Beatmaps) { var newSet = createCarouselSet(new BeatmapSetInfo(new[] { beatmap }) @@ -489,18 +472,7 @@ namespace osu.Game.Screens.Select }); if (newSet != null) - { newSets.Add(newSet); - root.AddItem(newSet); - } - } - - // check if we can/need to maintain our current selection. - if (previouslySelectedID != null) - { - var toSelect = newSets.FirstOrDefault(s => s.Beatmaps.Any(b => b.BeatmapInfo.ID == previouslySelectedID)) - ?? newSets.FirstOrDefault(); - select(toSelect); } } else @@ -508,13 +480,18 @@ namespace osu.Game.Screens.Select var newSet = createCarouselSet(beatmapSet); if (newSet != null) - { - root.AddItem(newSet); + newSets.Add(newSet); + } - // check if we can/need to maintain our current selection. - if (previouslySelectedID != null) - select((CarouselItem?)newSet.Beatmaps.FirstOrDefault(b => b.BeatmapInfo.ID == previouslySelectedID) ?? newSet); - } + var removedSets = root.ReplaceItem(beatmapSet, newSets); + + // If we don't remove these here, it may remain in a hidden state until scrolled off screen. + // Doesn't really affect anything during actual user interaction, but makes testing annoying. + foreach (var removedSet in removedSets) + { + var removedDrawable = Scroll.FirstOrDefault(c => c.Item == removedSet); + if (removedDrawable != null) + expirePanelImmediately(removedDrawable); } } @@ -1207,6 +1184,44 @@ namespace osu.Game.Screens.Select base.AddItem(i); } + /// + /// A special method to handle replace operations (general for updating a beatmap). + /// Avoids event-driven selection flip-flopping during the remove/add process. + /// + /// The beatmap set to be replaced. + /// All new items to replace the removed beatmap set. + /// All removed items, for any further processing. + public IEnumerable ReplaceItem(BeatmapSetInfo oldItem, List newItems) + { + // Without doing this, the removal of the old beatmap will cause carousel's eager selection + // logic to invoke, causing one unnecessary selection. + DisableSelection = true; + var removedSets = RemoveItemsByID(oldItem.ID); + DisableSelection = false; + + foreach (var set in newItems) + AddItem(set); + + Guid? previouslySelectedID = null; + + var selectedBeatmap = (LastSelected as CarouselBeatmap)?.BeatmapInfo; + + // If the selected beatmap is about to be removed, store its ID so it can be re-selected if required + if (selectedBeatmap?.BeatmapSet?.ID == oldItem.ID) + previouslySelectedID = selectedBeatmap.ID; + + // check if we can/need to maintain our current selection. + if (previouslySelectedID != null) + { + var toSelect = newItems.FirstOrDefault(s => s.Beatmaps.Any(b => b.BeatmapInfo.ID == previouslySelectedID)) + ?? newItems.First(); + + toSelect.State.Value = CarouselItemState.Selected; + } + + return removedSets; + } + public IEnumerable RemoveItemsByID(Guid beatmapSetID) { if (BeatmapSetsByID.TryGetValue(beatmapSetID, out var carouselBeatmapSets)) diff --git a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs index b4313c1895..cf4ba5924f 100644 --- a/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs +++ b/osu.Game/Screens/Select/Carousel/CarouselGroupEagerSelect.cs @@ -36,13 +36,13 @@ namespace osu.Game.Screens.Select.Carousel /// items have been filtered. This bool will be true during the base /// operation. /// - private bool filteringItems; + protected bool DisableSelection; public override void Filter(FilterCriteria criteria) { - filteringItems = true; + DisableSelection = true; base.Filter(criteria); - filteringItems = false; + DisableSelection = false; attemptSelection(); } @@ -95,7 +95,7 @@ namespace osu.Game.Screens.Select.Carousel private void attemptSelection() { - if (filteringItems) return; + if (DisableSelection) return; // we only perform eager selection if we are a currently selected group. if (State.Value != CarouselItemState.Selected) return; From 7fa4dcf0fbd95c3f68de861a4985613c0ed29538 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 19:27:57 +0900 Subject: [PATCH 0595/1118] Fix selection retention logic copy paste failure --- osu.Game/Screens/Select/BeatmapCarousel.cs | 25 +++++++++++----------- 1 file changed, 12 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index a51b54b21d..370b559897 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -1193,6 +1193,12 @@ namespace osu.Game.Screens.Select /// All removed items, for any further processing. public IEnumerable ReplaceItem(BeatmapSetInfo oldItem, List newItems) { + var previousSelection = (LastSelected as CarouselBeatmapSet)?.Beatmaps + .FirstOrDefault(s => s.State.Value == CarouselItemState.Selected) + ?.BeatmapInfo; + + bool wasSelected = previousSelection?.BeatmapSet?.ID == oldItem.ID; + // Without doing this, the removal of the old beatmap will cause carousel's eager selection // logic to invoke, causing one unnecessary selection. DisableSelection = true; @@ -1202,21 +1208,14 @@ namespace osu.Game.Screens.Select foreach (var set in newItems) AddItem(set); - Guid? previouslySelectedID = null; - - var selectedBeatmap = (LastSelected as CarouselBeatmap)?.BeatmapInfo; - - // If the selected beatmap is about to be removed, store its ID so it can be re-selected if required - if (selectedBeatmap?.BeatmapSet?.ID == oldItem.ID) - previouslySelectedID = selectedBeatmap.ID; - - // check if we can/need to maintain our current selection. - if (previouslySelectedID != null) + // Check if we can/need to maintain our current selection. + if (wasSelected) { - var toSelect = newItems.FirstOrDefault(s => s.Beatmaps.Any(b => b.BeatmapInfo.ID == previouslySelectedID)) - ?? newItems.First(); + CarouselBeatmap? matchingBeatmap = newItems.SelectMany(s => s.Beatmaps) + .FirstOrDefault(b => b.BeatmapInfo.ID == previousSelection?.ID); - toSelect.State.Value = CarouselItemState.Selected; + if (matchingBeatmap != null) + matchingBeatmap.State.Value = CarouselItemState.Selected; } return removedSets; From 5284b95bb8ffe943f8e53c52b33c0275ac59b529 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 19:42:05 +0900 Subject: [PATCH 0596/1118] Schedule even more --- osu.Game/Online/Metadata/OnlineMetadataClient.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Metadata/OnlineMetadataClient.cs b/osu.Game/Online/Metadata/OnlineMetadataClient.cs index b916386b06..6d00ce7551 100644 --- a/osu.Game/Online/Metadata/OnlineMetadataClient.cs +++ b/osu.Game/Online/Metadata/OnlineMetadataClient.cs @@ -208,7 +208,7 @@ namespace osu.Game.Online.Metadata Debug.Assert(connection != null); await connection.InvokeAsync(nameof(IMetadataServer.BeginWatchingUserPresence)).ConfigureAwait(false); - isWatchingUserPresence.Value = true; + Schedule(() => isWatchingUserPresence.Value = true); } public override async Task EndWatchingUserPresence() @@ -218,14 +218,14 @@ namespace osu.Game.Online.Metadata if (connector?.IsConnected.Value != true) throw new OperationCanceledException(); - // must happen synchronously before any remote calls to avoid mis-ordering. + // must be scheduled before any remote calls to avoid mis-ordering. Schedule(() => userStates.Clear()); Debug.Assert(connection != null); await connection.InvokeAsync(nameof(IMetadataServer.EndWatchingUserPresence)).ConfigureAwait(false); } finally { - isWatchingUserPresence.Value = false; + Schedule(() => isWatchingUserPresence.Value = false); } } From e0c27510f23b71df4d13b78d7b4800135a3d16ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 19:47:36 +0900 Subject: [PATCH 0597/1118] Remove remaining usage of `NotePerfectBonus` --- osu.Game.Rulesets.Mania/Objects/Note.cs | 8 -------- .../Objects/NotePerfectBonus.cs | 20 ------------------- 2 files changed, 28 deletions(-) delete mode 100644 osu.Game.Rulesets.Mania/Objects/NotePerfectBonus.cs diff --git a/osu.Game.Rulesets.Mania/Objects/Note.cs b/osu.Game.Rulesets.Mania/Objects/Note.cs index 5914132624..0035960c63 100644 --- a/osu.Game.Rulesets.Mania/Objects/Note.cs +++ b/osu.Game.Rulesets.Mania/Objects/Note.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System.Threading; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Judgements; @@ -13,12 +12,5 @@ namespace osu.Game.Rulesets.Mania.Objects public class Note : ManiaHitObject { public override Judgement CreateJudgement() => new ManiaJudgement(); - - protected override void CreateNestedHitObjects(CancellationToken cancellationToken) - { - base.CreateNestedHitObjects(cancellationToken); - - AddNested(new NotePerfectBonus { StartTime = StartTime }); - } } } diff --git a/osu.Game.Rulesets.Mania/Objects/NotePerfectBonus.cs b/osu.Game.Rulesets.Mania/Objects/NotePerfectBonus.cs deleted file mode 100644 index def4c01268..0000000000 --- a/osu.Game.Rulesets.Mania/Objects/NotePerfectBonus.cs +++ /dev/null @@ -1,20 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Mania.Judgements; -using osu.Game.Rulesets.Scoring; - -namespace osu.Game.Rulesets.Mania.Objects -{ - public class NotePerfectBonus : ManiaHitObject - { - public override Judgement CreateJudgement() => new NotePerfectBonusJudgement(); - protected override HitWindows CreateHitWindows() => HitWindows.Empty; - - public class NotePerfectBonusJudgement : ManiaJudgement - { - public override HitResult MaxResult => HitResult.SmallBonus; - } - } -} From d417b156b25b9541ae88d1acc1d93aa571abbaf0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 19:57:42 +0900 Subject: [PATCH 0598/1118] Update test expectations --- .../Mods/TestSceneManiaModDoubleTime.cs | 7 ++++--- osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs | 6 +++--- osu.Game.Rulesets.Mania.Tests/TestSceneMaximumScore.cs | 4 ++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs index c717f03f51..975e43ec08 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModDoubleTime.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using NUnit.Framework; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Objects; @@ -25,8 +26,8 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods public void TestHitWindowWithoutDoubleTime() => CreateModTest(new ModTestData { PassCondition = () => Player.ScoreProcessor.JudgedHits > 0 - && Player.ScoreProcessor.Accuracy.Value == 1 - && Player.ScoreProcessor.TotalScore.Value == 1_000_000, + && Precision.AlmostEquals(Player.ScoreProcessor.Accuracy.Value, 0.9836, 0.01) + && Player.ScoreProcessor.TotalScore.Value == 946_049, Autoplay = false, Beatmap = new Beatmap { @@ -53,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods Mod = doubleTime, PassCondition = () => Player.ScoreProcessor.JudgedHits > 0 && Player.ScoreProcessor.Accuracy.Value == 1 - && Player.ScoreProcessor.TotalScore.Value == (long)(1_000_010 * doubleTime.ScoreMultiplier), + && Player.ScoreProcessor.TotalScore.Value == (long)(1_000_000 * doubleTime.ScoreMultiplier), Autoplay = false, Beatmap = new Beatmap { diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index 044ce37832..d752c443cc 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -201,11 +201,11 @@ namespace osu.Game.Rulesets.Mania.Tests assertHeadJudgement(HitResult.Perfect); // judgement combo offset by perfect bonus judgement. see logic in DrawableNote.CheckForResult. - assertComboAtJudgement(1, 1); + assertComboAtJudgement(0, 1); assertTailJudgement(HitResult.Meh); - assertComboAtJudgement(2, 0); + assertComboAtJudgement(1, 0); // judgement combo offset by perfect bonus judgement. see logic in DrawableNote.CheckForResult. - assertComboAtJudgement(4, 1); + assertComboAtJudgement(3, 1); } /// diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneMaximumScore.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneMaximumScore.cs index edf866952b..ee6d999932 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneMaximumScore.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneMaximumScore.cs @@ -54,7 +54,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddAssert("all objects perfectly judged", () => judgementResults.Select(result => result.Type), () => Is.EquivalentTo(judgementResults.Select(result => result.Judgement.MaxResult))); - AddAssert("score is correct", () => currentPlayer.ScoreProcessor.TotalScore.Value, () => Is.EqualTo(1_000_030)); + AddAssert("score is correct", () => currentPlayer.ScoreProcessor.TotalScore.Value, () => Is.EqualTo(1_000_000)); } [Test] @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Mania.Tests AddAssert("all objects perfectly judged", () => judgementResults.Select(result => result.Type), () => Is.EquivalentTo(judgementResults.Select(result => result.Judgement.MaxResult))); - AddAssert("score is correct", () => currentPlayer.ScoreProcessor.TotalScore.Value, () => Is.EqualTo(1_000_040)); + AddAssert("score is correct", () => currentPlayer.ScoreProcessor.TotalScore.Value, () => Is.EqualTo(1_000_000)); } private void performTest(List hitObjects, List frames) From 38d6b7f45b7d07bfb0b9e5bdac1163dda0dcc25b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 20 Dec 2023 20:03:13 +0900 Subject: [PATCH 0599/1118] Update total score conversion --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 6484500bcc..a9225050b7 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -367,8 +367,8 @@ namespace osu.Game.Database case 3: return (long)Math.Round(( - 990000 * comboProportion - + 10000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy) + 850000 * comboProportion + + 150000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy) + bonusProportion) * modMultiplier); default: From 9b383e3276c23ec9f070b3ab1ccd01df969c83a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 20:23:19 +0900 Subject: [PATCH 0600/1118] Add support for showing tick misses --- .../Objects/Drawables/DrawableSliderRepeat.cs | 2 - .../Objects/Drawables/DrawableSliderTail.cs | 5 -- .../Objects/Drawables/DrawableSliderTick.cs | 2 - .../Scoring/OsuHitWindows.cs | 2 + .../Skinning/Argon/ArgonJudgementPiece.cs | 30 +++++------ osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 5 +- osu.Game/Graphics/OsuColour.cs | 1 + .../Judgements/DefaultJudgementPiece.cs | 16 +++--- .../Rulesets/Judgements/DrawableJudgement.cs | 9 ++-- osu.Game/Rulesets/Scoring/HitResult.cs | 2 + osu.Game/Skinning/LegacyJudgementPieceNew.cs | 2 +- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 52 +++++++++---------- osu.Game/Skinning/LegacySkin.cs | 6 +-- 13 files changed, 62 insertions(+), 72 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index 0c8e5b765f..c6d4f7c4ca 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -34,8 +34,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Drawable scaleContainer; - public override bool DisplayResult => false; - public DrawableSliderRepeat() : base(null) { diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs index 60bad5d4a7..c4731118a1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTail.cs @@ -24,11 +24,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; - /// - /// The judgement text is provided by the . - /// - public override bool DisplayResult => false; - /// /// Whether the hit samples only play on successful hits. /// If false, the hit samples will also play on misses. diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs index cb323f4ac7..d64fb0bcc6 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderTick.cs @@ -20,8 +20,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private const float default_tick_size = 16; - public override bool DisplayResult => false; - protected DrawableSlider DrawableSlider => (DrawableSlider)ParentHitObject; private SkinnableDrawable scaleContainer; diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs index fd86e0eeda..5b2c95b536 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs @@ -28,6 +28,8 @@ namespace osu.Game.Rulesets.Osu.Scoring case HitResult.Ok: case HitResult.Meh: case HitResult.Miss: + case HitResult.LargeTickMiss: + case HitResult.IgnoreMiss: return true; } diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs index 6f55d93eff..94766cb077 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs @@ -62,25 +62,23 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon /// public virtual void PlayAnimation() { - switch (Result) + if (Result.IsHit()) { - default: - JudgementText - .FadeInFromZero(300, Easing.OutQuint) - .ScaleTo(Vector2.One) - .ScaleTo(new Vector2(1.2f), 1800, Easing.OutQuint); - break; + JudgementText + .FadeInFromZero(300, Easing.OutQuint) + .ScaleTo(Vector2.One) + .ScaleTo(new Vector2(1.2f), 1800, Easing.OutQuint); + } + else + { + this.ScaleTo(1.6f); + this.ScaleTo(1, 100, Easing.In); - case HitResult.Miss: - this.ScaleTo(1.6f); - this.ScaleTo(1, 100, Easing.In); + this.MoveTo(Vector2.Zero); + this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); - this.MoveTo(Vector2.Zero); - this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); - - this.RotateTo(0); - this.RotateTo(40, 800, Easing.InQuint); - break; + this.RotateTo(0); + this.RotateTo(40, 800, Easing.InQuint); } this.FadeOutFromOne(800); diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 15ca0a90de..3f60ce3610 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -170,7 +170,10 @@ namespace osu.Game.Rulesets.Osu.UI if (!judgedObject.DisplayResult || !DisplayJudgements.Value) return; - DrawableOsuJudgement explosion = poolDictionary[result.Type].Get(doj => doj.Apply(result, judgedObject)); + if (!poolDictionary.TryGetValue(result.Type, out var pool)) + return; + + DrawableOsuJudgement explosion = pool.Get(doj => doj.Apply(result, judgedObject)); judgementLayer.Add(explosion); diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index a417164e27..caa2037691 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -77,6 +77,7 @@ namespace osu.Game.Graphics { case HitResult.SmallTickMiss: case HitResult.LargeTickMiss: + case HitResult.IgnoreMiss: case HitResult.Miss: case HitResult.ComboBreak: return Red; diff --git a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs index d5f586dc35..3c5e37f91c 100644 --- a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs +++ b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs @@ -38,18 +38,16 @@ namespace osu.Game.Rulesets.Judgements /// public virtual void PlayAnimation() { - switch (Result) + if (Result != HitResult.None && !Result.IsHit()) { - case HitResult.Miss: - this.ScaleTo(1.6f); - this.ScaleTo(1, 100, Easing.In); + this.ScaleTo(1.6f); + this.ScaleTo(1, 100, Easing.In); - this.MoveTo(Vector2.Zero); - this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); + this.MoveTo(Vector2.Zero); + this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); - this.RotateTo(0); - this.RotateTo(40, 800, Easing.InQuint); - break; + this.RotateTo(0); + this.RotateTo(40, 800, Easing.InQuint); } this.FadeOutFromOne(800); diff --git a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs index 15434fcc04..b4686c52f3 100644 --- a/osu.Game/Rulesets/Judgements/DrawableJudgement.cs +++ b/osu.Game/Rulesets/Judgements/DrawableJudgement.cs @@ -133,12 +133,11 @@ namespace osu.Game.Rulesets.Judgements case HitResult.None: break; - case HitResult.Miss: - ApplyMissAnimations(); - break; - default: - ApplyHitAnimations(); + if (Result.Type.IsHit()) + ApplyHitAnimations(); + else + ApplyMissAnimations(); break; } diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 9705421571..b490501021 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -86,6 +86,7 @@ namespace osu.Game.Rulesets.Scoring /// Indicates a large tick miss. /// [EnumMember(Value = "large_tick_miss")] + [Description(@"x")] [Order(10)] LargeTickMiss, @@ -117,6 +118,7 @@ namespace osu.Game.Rulesets.Scoring /// Indicates a miss that should be ignored for scoring purposes. /// [EnumMember(Value = "ignore_miss")] + [Description("x")] [Order(13)] IgnoreMiss, diff --git a/osu.Game/Skinning/LegacyJudgementPieceNew.cs b/osu.Game/Skinning/LegacyJudgementPieceNew.cs index 9b1ff9b22f..a93c48ba63 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceNew.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceNew.cs @@ -50,7 +50,7 @@ namespace osu.Game.Skinning }); } - if (result != HitResult.Miss) + if (result.IsHit()) { //new judgement shows old as a temporary effect AddInternal(temporaryOldStyle = new LegacyJudgementPieceOld(result, createMainDrawable, 1.05f, true) diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index 082d0e4a67..5381cfc050 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -52,39 +52,35 @@ namespace osu.Game.Skinning if (animation?.FrameCount > 1 && !forceTransforms) return; - switch (result) + if (result.IsHit()) { - case HitResult.Miss: - this.ScaleTo(1.6f); - this.ScaleTo(1, 100, Easing.In); + this.ScaleTo(0.6f).Then() + .ScaleTo(1.1f, fade_in_length * 0.8f).Then() // t = 0.8 + .Delay(fade_in_length * 0.2f) // t = 1.0 + .ScaleTo(0.9f, fade_in_length * 0.2f).Then() // t = 1.2 + // stable dictates scale of 0.9->1 over time 1.0 to 1.4, but we are already at 1.2. + // so we need to force the current value to be correct at 1.2 (0.95) then complete the + // second half of the transform. + .ScaleTo(0.95f).ScaleTo(finalScale, fade_in_length * 0.2f); // t = 1.4 + } + else + { + this.ScaleTo(1.6f); + this.ScaleTo(1, 100, Easing.In); - decimal? legacyVersion = skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value; + decimal? legacyVersion = skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value; - if (legacyVersion >= 2.0m) - { - this.MoveTo(new Vector2(0, -5)); - this.MoveToOffset(new Vector2(0, 80), fade_out_delay + fade_out_length, Easing.In); - } + if (legacyVersion >= 2.0m) + { + this.MoveTo(new Vector2(0, -5)); + this.MoveToOffset(new Vector2(0, 80), fade_out_delay + fade_out_length, Easing.In); + } - float rotation = RNG.NextSingle(-8.6f, 8.6f); + float rotation = RNG.NextSingle(-8.6f, 8.6f); - this.RotateTo(0); - this.RotateTo(rotation, fade_in_length) - .Then().RotateTo(rotation * 2, fade_out_delay + fade_out_length - fade_in_length, Easing.In); - break; - - default: - - this.ScaleTo(0.6f).Then() - .ScaleTo(1.1f, fade_in_length * 0.8f).Then() // t = 0.8 - .Delay(fade_in_length * 0.2f) // t = 1.0 - .ScaleTo(0.9f, fade_in_length * 0.2f).Then() // t = 1.2 - - // stable dictates scale of 0.9->1 over time 1.0 to 1.4, but we are already at 1.2. - // so we need to force the current value to be correct at 1.2 (0.95) then complete the - // second half of the transform. - .ScaleTo(0.95f).ScaleTo(finalScale, fade_in_length * 0.2f); // t = 1.4 - break; + this.RotateTo(0); + this.RotateTo(rotation, fade_in_length) + .Then().RotateTo(rotation * 2, fade_out_delay + fade_out_length - fade_in_length, Easing.In); } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 9102231913..7516c73b68 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -453,11 +453,11 @@ namespace osu.Game.Skinning private Drawable? getJudgementAnimation(HitResult result) { + if (!result.IsHit()) + return this.GetAnimation("hit0", true, false); + switch (result) { - case HitResult.Miss: - return this.GetAnimation("hit0", true, false); - case HitResult.Meh: return this.GetAnimation("hit50", true, false); From d2716d855741c7461a33aa317965486a85ecd3d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 20:26:28 +0900 Subject: [PATCH 0601/1118] Improve animation for legacy skins --- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 32 +++++++++++++------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index 5381cfc050..6ad188bb47 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -65,22 +65,32 @@ namespace osu.Game.Skinning } else { - this.ScaleTo(1.6f); - this.ScaleTo(1, 100, Easing.In); + bool isTick = result != HitResult.Miss; - decimal? legacyVersion = skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value; - - if (legacyVersion >= 2.0m) + if (isTick) { - this.MoveTo(new Vector2(0, -5)); - this.MoveToOffset(new Vector2(0, 80), fade_out_delay + fade_out_length, Easing.In); + this.ScaleTo(0.6f); + this.ScaleTo(0.3f, 100, Easing.In); } + else + { + this.ScaleTo(1.6f); + this.ScaleTo(1, 100, Easing.In); - float rotation = RNG.NextSingle(-8.6f, 8.6f); + decimal? legacyVersion = skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value; - this.RotateTo(0); - this.RotateTo(rotation, fade_in_length) - .Then().RotateTo(rotation * 2, fade_out_delay + fade_out_length - fade_in_length, Easing.In); + if (legacyVersion >= 2.0m) + { + this.MoveTo(new Vector2(0, -5)); + this.MoveToOffset(new Vector2(0, 80), fade_out_delay + fade_out_length, Easing.In); + } + + float rotation = RNG.NextSingle(-8.6f, 8.6f); + + this.RotateTo(0); + this.RotateTo(rotation, fade_in_length) + .Then().RotateTo(rotation * 2, fade_out_delay + fade_out_length - fade_in_length, Easing.In); + } } } From d1ba2a4a64f7e5c51c5309d5366b9778b98fbdf4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 20 Dec 2023 20:45:18 +0900 Subject: [PATCH 0602/1118] Use orange for non-combo-breaking miss --- osu.Game/Graphics/OsuColour.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index caa2037691..0d11d2d4ef 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -75,10 +75,12 @@ namespace osu.Game.Graphics { switch (result) { - case HitResult.SmallTickMiss: - case HitResult.LargeTickMiss: case HitResult.IgnoreMiss: + case HitResult.SmallTickMiss: + return Orange1; + case HitResult.Miss: + case HitResult.LargeTickMiss: case HitResult.ComboBreak: return Red; From bff08d124ba7d925566a1a2445036d3ace99c071 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 20 Dec 2023 22:28:46 +0900 Subject: [PATCH 0603/1118] Remove mania mod multiplier for DT/NC --- osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs | 5 +++++ osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs index a841a8ab37..bea1a14110 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModDoubleTime.cs @@ -10,5 +10,10 @@ namespace osu.Game.Rulesets.Mania.Mods public class ManiaModDoubleTime : ModDoubleTime, IManiaRateAdjustmentMod { public HitWindows HitWindows { get; set; } = new ManiaHitWindows(); + + // For now, all rate-increasing mods should be given a 1x multiplier in mania because it doesn't always + // make the map harder and is more of a personal preference. + // In the future, we can consider adjusting this by experimenting with not applying the hitwindow leniency. + public override double ScoreMultiplier => 1; } } diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs index f64f7ae31a..7e5e80db6c 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModNightcore.cs @@ -11,5 +11,10 @@ namespace osu.Game.Rulesets.Mania.Mods public class ManiaModNightcore : ModNightcore, IManiaRateAdjustmentMod { public HitWindows HitWindows { get; set; } = new ManiaHitWindows(); + + // For now, all rate-increasing mods should be given a 1x multiplier in mania because it doesn't always + // make the map any harder and is more of a personal preference. + // In the future, we can consider adjusting this by experimenting with not applying the hitwindow leniency. + public override double ScoreMultiplier => 1; } } From b0da24176e5e2026173f25abb8a9e094f7d118ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Dec 2023 15:44:02 +0100 Subject: [PATCH 0604/1118] Remove outdated inline comments --- osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs index d752c443cc..5f299f419d 100644 --- a/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs +++ b/osu.Game.Rulesets.Mania.Tests/TestSceneHoldNoteInput.cs @@ -200,11 +200,9 @@ namespace osu.Game.Rulesets.Mania.Tests }); assertHeadJudgement(HitResult.Perfect); - // judgement combo offset by perfect bonus judgement. see logic in DrawableNote.CheckForResult. assertComboAtJudgement(0, 1); assertTailJudgement(HitResult.Meh); assertComboAtJudgement(1, 0); - // judgement combo offset by perfect bonus judgement. see logic in DrawableNote.CheckForResult. assertComboAtJudgement(3, 1); } From 9515f7dbfa778a22eef7414e2ec68ff732befa7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Dec 2023 15:58:26 +0100 Subject: [PATCH 0605/1118] Bump score version in order to recompute legacy scores --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 5c51e68d9f..fa930d77d3 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -33,9 +33,10 @@ namespace osu.Game.Scoring.Legacy /// 30000004: Fixed mod multipliers during legacy score conversion. Reconvert all scores. /// 30000005: Introduce combo exponent in the osu! gamemode. Reconvert all scores. /// 30000006: Fix edge cases in conversion after combo exponent introduction that lead to NaNs. Reconvert all scores. + /// 30000007: Adjust osu!mania combo and accuracy portions and judgement scoring values. Reconvert all scores. /// /// - public const int LATEST_VERSION = 30000006; + public const int LATEST_VERSION = 30000007; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From fcf47267fd3a38e53794ca8dadd0d6222c327b64 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2023 00:48:46 +0900 Subject: [PATCH 0606/1118] Revert change to `OsuHitWindows` and move logic local to pooling initialisation --- osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs | 2 -- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 18 +++++++++++++++--- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs index 5b2c95b536..fd86e0eeda 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHitWindows.cs @@ -28,8 +28,6 @@ namespace osu.Game.Rulesets.Osu.Scoring case HitResult.Ok: case HitResult.Meh: case HitResult.Miss: - case HitResult.LargeTickMiss: - case HitResult.IgnoreMiss: return true; } diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index 3f60ce3610..c94057cf6d 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -20,7 +20,6 @@ using osu.Game.Rulesets.Osu.Configuration; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Osu.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects.Drawables.Connections; -using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.UI.Cursor; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -66,8 +65,21 @@ namespace osu.Game.Rulesets.Osu.UI HitPolicy = new StartTimeOrderedHitPolicy(); - var hitWindows = new OsuHitWindows(); - foreach (var result in Enum.GetValues().Where(r => r > HitResult.None && hitWindows.IsHitResultAllowed(r))) + foreach (var result in Enum.GetValues().Where(r => + { + switch (r) + { + case HitResult.Great: + case HitResult.Ok: + case HitResult.Meh: + case HitResult.Miss: + case HitResult.LargeTickMiss: + case HitResult.IgnoreMiss: + return true; + } + + return false; + })) poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgementLoaded)); AddRangeInternal(poolDictionary.Values); From eb8fb8092d2effd3c4251f8247f0dcc5d8f52a93 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2023 00:58:43 +0900 Subject: [PATCH 0607/1118] Attempt to standardise miss handling logic --- .../Skinning/Argon/ArgonJudgementPiece.cs | 16 ++++++------- osu.Game/Rulesets/Scoring/HitResult.cs | 19 +++++++++++++++ osu.Game/Skinning/LegacyJudgementPieceNew.cs | 2 +- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 24 +++++++++---------- osu.Game/Skinning/LegacySkin.cs | 2 +- 5 files changed, 41 insertions(+), 22 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs index 94766cb077..edeece0293 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs @@ -62,14 +62,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon /// public virtual void PlayAnimation() { - if (Result.IsHit()) - { - JudgementText - .FadeInFromZero(300, Easing.OutQuint) - .ScaleTo(Vector2.One) - .ScaleTo(new Vector2(1.2f), 1800, Easing.OutQuint); - } - else + if (Result.IsMiss()) { this.ScaleTo(1.6f); this.ScaleTo(1, 100, Easing.In); @@ -80,6 +73,13 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon this.RotateTo(0); this.RotateTo(40, 800, Easing.InQuint); } + else + { + JudgementText + .FadeInFromZero(300, Easing.OutQuint) + .ScaleTo(Vector2.One) + .ScaleTo(new Vector2(1.2f), 1800, Easing.OutQuint); + } this.FadeOutFromOne(800); diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index b490501021..bdb2a9db23 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -269,6 +269,25 @@ namespace osu.Game.Rulesets.Scoring } } + /// + /// Whether a represents a miss of any type. + /// + public static bool IsMiss(this HitResult result) + { + switch (result) + { + case HitResult.IgnoreMiss: + case HitResult.Miss: + case HitResult.SmallTickMiss: + case HitResult.LargeTickMiss: + case HitResult.ComboBreak: + return true; + + default: + return false; + } + } + /// /// Whether a represents a successful hit. /// diff --git a/osu.Game/Skinning/LegacyJudgementPieceNew.cs b/osu.Game/Skinning/LegacyJudgementPieceNew.cs index a93c48ba63..5ff28726c0 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceNew.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceNew.cs @@ -50,7 +50,7 @@ namespace osu.Game.Skinning }); } - if (result.IsHit()) + if (!result.IsMiss()) { //new judgement shows old as a temporary effect AddInternal(temporaryOldStyle = new LegacyJudgementPieceOld(result, createMainDrawable, 1.05f, true) diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index 6ad188bb47..39697090d1 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -52,18 +52,7 @@ namespace osu.Game.Skinning if (animation?.FrameCount > 1 && !forceTransforms) return; - if (result.IsHit()) - { - this.ScaleTo(0.6f).Then() - .ScaleTo(1.1f, fade_in_length * 0.8f).Then() // t = 0.8 - .Delay(fade_in_length * 0.2f) // t = 1.0 - .ScaleTo(0.9f, fade_in_length * 0.2f).Then() // t = 1.2 - // stable dictates scale of 0.9->1 over time 1.0 to 1.4, but we are already at 1.2. - // so we need to force the current value to be correct at 1.2 (0.95) then complete the - // second half of the transform. - .ScaleTo(0.95f).ScaleTo(finalScale, fade_in_length * 0.2f); // t = 1.4 - } - else + if (result.IsMiss()) { bool isTick = result != HitResult.Miss; @@ -92,6 +81,17 @@ namespace osu.Game.Skinning .Then().RotateTo(rotation * 2, fade_out_delay + fade_out_length - fade_in_length, Easing.In); } } + else + { + this.ScaleTo(0.6f).Then() + .ScaleTo(1.1f, fade_in_length * 0.8f).Then() // t = 0.8 + .Delay(fade_in_length * 0.2f) // t = 1.0 + .ScaleTo(0.9f, fade_in_length * 0.2f).Then() // t = 1.2 + // stable dictates scale of 0.9->1 over time 1.0 to 1.4, but we are already at 1.2. + // so we need to force the current value to be correct at 1.2 (0.95) then complete the + // second half of the transform. + .ScaleTo(0.95f).ScaleTo(finalScale, fade_in_length * 0.2f); // t = 1.4 + } } public Drawable GetAboveHitObjectsProxiedContent() => CreateProxy(); diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 7516c73b68..a37a386889 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -453,7 +453,7 @@ namespace osu.Game.Skinning private Drawable? getJudgementAnimation(HitResult result) { - if (!result.IsHit()) + if (result.IsMiss()) return this.GetAnimation("hit0", true, false); switch (result) From ebbc8333e84509d39c5ed4afada29f07cd8d2d04 Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Tue, 19 Dec 2023 23:36:57 -0800 Subject: [PATCH 0608/1118] Prevent `ExportReplay` being spammed on fail by being held down This was already handled in ReplayDownloadButton (https://github.com/ppy/osu/blob/98efff0bd61ea6cc5d6a884aeda1614d81592b9d/osu.Game/Screens/Ranking/ReplayDownloadButton.cs#L114-L115) but seemingly missed for SaveFailedScoreButton --- osu.Game/Screens/Play/SaveFailedScoreButton.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/SaveFailedScoreButton.cs b/osu.Game/Screens/Play/SaveFailedScoreButton.cs index 0a2696339c..b97c140250 100644 --- a/osu.Game/Screens/Play/SaveFailedScoreButton.cs +++ b/osu.Game/Screens/Play/SaveFailedScoreButton.cs @@ -102,6 +102,9 @@ namespace osu.Game.Screens.Play public bool OnPressed(KeyBindingPressEvent e) { + if (e.Repeat) + return false; + switch (e.Action) { case GlobalAction.SaveReplay: From c5fb4d0f5c85714d207916d8035565c7a15a39c0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2023 01:52:40 +0900 Subject: [PATCH 0609/1118] Mark flaky test temporarily --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 518035fb82..218bf495e3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -420,6 +420,7 @@ namespace osu.Game.Tests.Visual.SongSelect } [Test] + [FlakyTest] // temporary while peppy investigates public void TestSelectionRetainedOnBeatmapUpdate() { createSongSelect(); From a763ad84730112774e89960d1c3afc115c3e1851 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Dec 2023 19:07:18 +0100 Subject: [PATCH 0610/1118] Add remarks to `Is{Hit,Miss}()` to explain their simultaneous existence --- osu.Game/Rulesets/Scoring/HitResult.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index bdb2a9db23..e174ebd00f 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -272,6 +272,9 @@ namespace osu.Game.Rulesets.Scoring /// /// Whether a represents a miss of any type. /// + /// + /// Of note, both and return for . + /// public static bool IsMiss(this HitResult result) { switch (result) @@ -291,6 +294,9 @@ namespace osu.Game.Rulesets.Scoring /// /// Whether a represents a successful hit. /// + /// + /// Of note, both and return for . + /// public static bool IsHit(this HitResult result) { switch (result) From 975bacaeb750daef25afa0c7c117e23baeb42586 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Dec 2023 19:07:26 +0100 Subject: [PATCH 0611/1118] Add back blank line to fix code quality inspection --- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index 39697090d1..ca2c8ce6bc 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -87,6 +87,7 @@ namespace osu.Game.Skinning .ScaleTo(1.1f, fade_in_length * 0.8f).Then() // t = 0.8 .Delay(fade_in_length * 0.2f) // t = 1.0 .ScaleTo(0.9f, fade_in_length * 0.2f).Then() // t = 1.2 + // stable dictates scale of 0.9->1 over time 1.0 to 1.4, but we are already at 1.2. // so we need to force the current value to be correct at 1.2 (0.95) then complete the // second half of the transform. From b6a331b2f7c64100874d2eebe0ecff069a7b9693 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Dec 2023 20:52:26 +0100 Subject: [PATCH 0612/1118] Fix argon pro not showing slider tick judgements Addresses https://github.com/ppy/osu/discussions/25968. --- osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs index f98a47097d..0f9c97059c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/OsuArgonSkinTransformer.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon { case GameplaySkinComponentLookup resultComponent: // This should eventually be moved to a skin setting, when supported. - if (Skin is ArgonProSkin && resultComponent.Component >= HitResult.Great) + if (Skin is ArgonProSkin && (resultComponent.Component == HitResult.Great || resultComponent.Component == HitResult.Perfect)) return Drawable.Empty(); return new ArgonJudgementPiece(resultComponent.Component); From 88e36eb08c353e8a65108995ad32718831eded66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 20 Dec 2023 21:46:18 +0100 Subject: [PATCH 0613/1118] Fix autopilot mod still declaring incompatibility with fail-preventing mods Closes https://github.com/ppy/osu/issues/25974. --- osu.Game.Rulesets.Osu/Mods/OsuModAccuracyChallenge.cs | 3 --- osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs | 2 -- osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs | 3 --- osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs | 3 --- osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs | 1 - 5 files changed, 12 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAccuracyChallenge.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAccuracyChallenge.cs index 5b79753632..e6daa3846f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAccuracyChallenge.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAccuracyChallenge.cs @@ -1,14 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModAccuracyChallenge : ModAccuracyChallenge { - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs index bf74b741d5..efcc728d55 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModAutopilot.cs @@ -29,8 +29,6 @@ namespace osu.Game.Rulesets.Osu.Mods { typeof(OsuModSpunOut), typeof(ModRelax), - typeof(ModFailCondition), - typeof(ModNoFail), typeof(ModAutoplay), typeof(OsuModMagnetised), typeof(OsuModRepel), diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs b/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs index 9f707a5aa6..53c67cd1c3 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModNoFail.cs @@ -1,14 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModNoFail : ModNoFail { - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(OsuModAutopilot)).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs b/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs index 33581405a6..da462eb6e8 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModPerfect.cs @@ -1,14 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Game.Rulesets.Mods; namespace osu.Game.Rulesets.Osu.Mods { public class OsuModPerfect : ModPerfect { - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(OsuModAutopilot) }).ToArray(); } } diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs index b4edb1581e..e661610fe7 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModSuddenDeath.cs @@ -11,7 +11,6 @@ namespace osu.Game.Rulesets.Osu.Mods { public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { - typeof(OsuModAutopilot), typeof(OsuModTargetPractice), }).ToArray(); } From 4e3b9941423591977dfe969d637661308dc7d9f6 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Wed, 20 Dec 2023 20:23:43 +0900 Subject: [PATCH 0614/1118] Relocate HitResult numeric score to ScoreProcessor --- .../Difficulty/CatchLegacyScoreSimulator.cs | 6 +- .../Scoring/CatchScoreProcessor.cs | 2 +- .../Scoring/ManiaScoreProcessor.cs | 31 ++--- .../TestSceneSpinnerRotation.cs | 11 +- .../Difficulty/OsuLegacyScoreSimulator.cs | 6 +- .../Objects/Drawables/DrawableSpinner.cs | 3 +- .../Difficulty/TaikoLegacyScoreSimulator.cs | 6 +- .../Scoring/TaikoScoreProcessor.cs | 19 +++- .../Gameplay/TestSceneScoreProcessor.cs | 2 +- .../StandardisedScoreMigrationTools.cs | 46 +++++++- osu.Game/Rulesets/Judgements/Judgement.cs | 59 +--------- .../Rulesets/Judgements/JudgementResult.cs | 2 +- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 107 +++++++++++++++--- osu.Game/Scoring/ScoreImporter.cs | 4 +- 14 files changed, 182 insertions(+), 122 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs index 7a84d9245d..52605ec0cc 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs @@ -7,7 +7,7 @@ using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Catch.Mods; using osu.Game.Rulesets.Catch.Objects; -using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Catch.Difficulty { internal class CatchLegacyScoreSimulator : ILegacyScoreSimulator { + private readonly ScoreProcessor scoreProcessor = new CatchScoreProcessor(); + private int legacyBonusScore; private int standardisedBonusScore; private int combo; @@ -134,7 +136,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (isBonus) { legacyBonusScore += scoreIncrease; - standardisedBonusScore += Judgement.ToNumericResult(bonusResult); + standardisedBonusScore += scoreProcessor.GetRawBonusScore(bonusResult); } else attributes.AccuracyScore += scoreIncrease; diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 503252df02..023369badc 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Scoring } protected override double GetComboScoreChange(JudgementResult result) - => GetNumericResultFor(result) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); + => GetRawComboScore(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); public override ScoreRank RankFromAccuracy(double accuracy) { diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index d5191c880a..d059d99d9f 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -31,45 +31,30 @@ namespace osu.Game.Rulesets.Mania.Scoring + bonusPortion; } - protected override double GetNumericResultFor(JudgementResult result) + public override int GetRawAccuracyScore(HitResult result) { - switch (result.Type) + switch (result) { case HitResult.Perfect: return 305; } - return base.GetNumericResultFor(result); + return base.GetRawAccuracyScore(result); } - protected override double GetMaxNumericResultFor(JudgementResult result) + public override int GetRawComboScore(HitResult result) { - switch (result.Judgement.MaxResult) + switch (result) { case HitResult.Perfect: - return 305; + return 300; } - return base.GetMaxNumericResultFor(result); + return base.GetRawComboScore(result); } protected override double GetComboScoreChange(JudgementResult result) - { - double numericResult; - - switch (result.Type) - { - case HitResult.Perfect: - numericResult = 300; - break; - - default: - numericResult = GetNumericResultFor(result); - break; - } - - return numericResult * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); - } + => GetRawComboScore(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); private class JudgementOrderComparer : IComparer { diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index 8711aa9c09..fd445bc1ac 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -58,10 +58,7 @@ namespace osu.Game.Rulesets.Osu.Tests double trackerRotationTolerance = 0; addSeekStep(5000); - AddStep("calculate rotation tolerance", () => - { - trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f); - }); + AddStep("calculate rotation tolerance", () => { trackerRotationTolerance = Math.Abs(drawableSpinner.RotationTracker.Rotation * 0.1f); }); AddAssert("is disc rotation not almost 0", () => drawableSpinner.RotationTracker.Rotation, () => Is.Not.EqualTo(0).Within(100)); AddAssert("is disc rotation absolute not almost 0", () => drawableSpinner.Result.TotalRotation, () => Is.Not.EqualTo(0).Within(100)); @@ -133,9 +130,11 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("player score matching expected bonus score", () => { + var scoreProcessor = ((ScoreExposedPlayer)Player).ScoreProcessor; + // multipled by 2 to nullify the score multiplier. (autoplay mod selected) - long totalScore = ((ScoreExposedPlayer)Player).ScoreProcessor.TotalScore.Value * 2; - return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * new SpinnerTick().CreateJudgement().MaxNumericResult; + long totalScore = scoreProcessor.TotalScore.Value * 2; + return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * scoreProcessor.GetRawBonusScore(new SpinnerTick().CreateJudgement().MaxResult); }); addSeekStep(0); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs index 28967cbf7e..6ac7c33275 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs @@ -5,12 +5,12 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring.Legacy; @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.Difficulty { internal class OsuLegacyScoreSimulator : ILegacyScoreSimulator { + private readonly ScoreProcessor scoreProcessor = new OsuScoreProcessor(); + private int legacyBonusScore; private int standardisedBonusScore; private int combo; @@ -171,7 +173,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (isBonus) { legacyBonusScore += scoreIncrease; - standardisedBonusScore += Judgement.ToNumericResult(bonusResult); + standardisedBonusScore += scoreProcessor.GetRawBonusScore(bonusResult); } else attributes.AccuracyScore += scoreIncrease; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index c0c135d145..787d5e5937 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Scoring; using osu.Game.Rulesets.Osu.Skinning; using osu.Game.Rulesets.Osu.Skinning.Default; using osu.Game.Rulesets.Scoring; @@ -312,7 +313,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables updateBonusScore(); } - private static readonly int score_per_tick = new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxNumericResult; + private static readonly int score_per_tick = new OsuScoreProcessor().GetRawBonusScore(new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxResult); private void updateBonusScore() { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index a8ed056c89..8b34c8c808 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using osu.Game.Beatmaps; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -13,11 +12,14 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring.Legacy; using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Scoring; namespace osu.Game.Rulesets.Taiko.Difficulty { internal class TaikoLegacyScoreSimulator : ILegacyScoreSimulator { + private readonly ScoreProcessor scoreProcessor = new TaikoScoreProcessor(); + private int legacyBonusScore; private int standardisedBonusScore; private int combo; @@ -191,7 +193,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (isBonus) { legacyBonusScore += scoreIncrease; - standardisedBonusScore += Judgement.ToNumericResult(bonusResult); + standardisedBonusScore += scoreProcessor.GetRawBonusScore(bonusResult); } else attributes.AccuracyScore += scoreIncrease; diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index bc1e42c5d1..e81ba324bd 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -28,20 +28,31 @@ namespace osu.Game.Rulesets.Taiko.Scoring protected override double GetComboScoreChange(JudgementResult result) { - return GetNumericResultFor(result) + return GetRawComboScore(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)) * strongScaleValue(result); } - protected override double GetNumericResultFor(JudgementResult result) + public override int GetRawAccuracyScore(HitResult result) { - switch (result.Type) + switch (result) { case HitResult.Ok: return 150; } - return base.GetNumericResultFor(result); + return base.GetRawAccuracyScore(result); + } + + public override int GetRawComboScore(HitResult result) + { + switch (result) + { + case HitResult.Ok: + return 150; + } + + return base.GetRawComboScore(result); } private double strongScaleValue(JudgementResult result) diff --git a/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs index 1cf72cf937..5d2ad8b363 100644 --- a/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Gameplay // Apply a judgement scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new TestJudgement(HitResult.LargeBonus)) { Type = HitResult.LargeBonus }); - Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(Judgement.LARGE_BONUS_SCORE)); + Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(scoreProcessor.GetRawBonusScore(HitResult.LargeBonus))); } [Test] diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 2e5ca390d7..5324d2569e 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -57,14 +57,14 @@ namespace osu.Game.Database // We are constructing a "best possible" score from the statistics provided because it's the best we can do. List sortedHits = score.Statistics .Where(kvp => kvp.Key.AffectsCombo()) - .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) + .OrderByDescending(kvp => processor.GetRawComboScore(kvp.Key)) .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value)) .ToList(); // Attempt to use maximum statistics from the database. var maximumJudgements = score.MaximumStatistics .Where(kvp => kvp.Key.AffectsCombo()) - .OrderByDescending(kvp => Judgement.ToNumericResult(kvp.Key)) + .OrderByDescending(kvp => processor.GetRawComboScore(kvp.Key)) .SelectMany(kvp => Enumerable.Repeat(new FakeJudgement(kvp.Key), kvp.Value)) .ToList(); @@ -169,10 +169,10 @@ namespace osu.Game.Database public static long GetOldStandardised(ScoreInfo score) { double accuracyScore = - (double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value) - / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value); + (double)score.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value) + / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value); double comboScore = (double)score.MaxCombo / score.MaximumStatistics.Where(kvp => kvp.Key.AffectsCombo()).Sum(kvp => kvp.Value); - double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => Judgement.ToNumericResult(kvp.Key) * kvp.Value); + double bonusScore = score.Statistics.Where(kvp => kvp.Key.IsBonus()).Sum(kvp => numericScoreFor(kvp.Key) * kvp.Value); double accuracyPortion = 0.3; @@ -193,6 +193,42 @@ namespace osu.Game.Database modMultiplier *= mod.ScoreMultiplier; return (long)Math.Round((1000000 * (accuracyPortion * accuracyScore + (1 - accuracyPortion) * comboScore) + bonusScore) * modMultiplier); + + static int numericScoreFor(HitResult result) + { + switch (result) + { + default: + return 0; + + case HitResult.SmallTickHit: + return 10; + + case HitResult.LargeTickHit: + return 30; + + case HitResult.Meh: + return 50; + + case HitResult.Ok: + return 100; + + case HitResult.Good: + return 200; + + case HitResult.Great: + return 300; + + case HitResult.Perfect: + return 315; + + case HitResult.SmallBonus: + return 10; + + case HitResult.LargeBonus: + return 50; + } + } } /// diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index cd1e81046d..27402d522c 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -11,16 +11,6 @@ namespace osu.Game.Rulesets.Judgements /// public class Judgement { - /// - /// The score awarded for a small bonus. - /// - public const int SMALL_BONUS_SCORE = 10; - - /// - /// The score awarded for a large bonus. - /// - public const int LARGE_BONUS_SCORE = 50; - /// /// The default health increase for a maximum judgement, as a proportion of total health. /// By default, each maximum judgement restores 5% of total health. @@ -91,23 +81,11 @@ namespace osu.Game.Rulesets.Judgements } } - /// - /// The numeric score representation for the maximum achievable result. - /// - public int MaxNumericResult => ToNumericResult(MaxResult); - /// /// The health increase for the maximum achievable result. /// public double MaxHealthIncrease => HealthIncreaseFor(MaxResult); - /// - /// Retrieves the numeric score representation of a . - /// - /// The to find the numeric score representation for. - /// The numeric score representation of . - public int NumericResultFor(JudgementResult result) => ToNumericResult(result.Type); - /// /// Retrieves the numeric health increase of a . /// @@ -165,41 +143,6 @@ namespace osu.Game.Rulesets.Judgements /// The numeric health increase of . public double HealthIncreaseFor(JudgementResult result) => HealthIncreaseFor(result.Type); - public override string ToString() => $"MaxResult:{MaxResult} MaxScore:{MaxNumericResult}"; - - public static int ToNumericResult(HitResult result) - { - switch (result) - { - default: - return 0; - - case HitResult.SmallTickHit: - return 10; - - case HitResult.LargeTickHit: - return 30; - - case HitResult.Meh: - return 50; - - case HitResult.Ok: - return 100; - - case HitResult.Good: - return 200; - - case HitResult.Great: - // Perfect doesn't actually give more score / accuracy directly. - case HitResult.Perfect: - return 300; - - case HitResult.SmallBonus: - return SMALL_BONUS_SCORE; - - case HitResult.LargeBonus: - return LARGE_BONUS_SCORE; - } - } + public override string ToString() => $"MaxResult:{MaxResult}"; } } diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index db621b4851..1b915d52b7 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -112,6 +112,6 @@ namespace osu.Game.Rulesets.Judgements RawTime = null; } - public override string ToString() => $"{Type} (Score:{Judgement.NumericResultFor(this)} HP:{Judgement.HealthIncreaseFor(this)} {Judgement})"; + public override string ToString() => $"{Type} ({Judgement})"; } } diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index aa8905c0b6..a80a118363 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -227,12 +227,12 @@ namespace osu.Game.Rulesets.Scoring if (result.Judgement.MaxResult.AffectsAccuracy()) { - currentMaximumBaseScore += GetMaxNumericResultFor(result); + currentMaximumBaseScore += GetRawAccuracyScore(result.Judgement.MaxResult); currentAccuracyJudgementCount++; } if (result.Type.AffectsAccuracy()) - currentBaseScore += GetNumericResultFor(result); + currentBaseScore += GetRawAccuracyScore(result.Type); if (result.Type.IsBonus()) currentBonusPortion += GetBonusScoreChange(result); @@ -276,12 +276,12 @@ namespace osu.Game.Rulesets.Scoring if (result.Judgement.MaxResult.AffectsAccuracy()) { - currentMaximumBaseScore -= GetMaxNumericResultFor(result); + currentMaximumBaseScore -= GetRawAccuracyScore(result.Judgement.MaxResult); currentAccuracyJudgementCount--; } if (result.Type.AffectsAccuracy()) - currentBaseScore -= GetNumericResultFor(result); + currentBaseScore -= GetRawAccuracyScore(result.Type); if (result.Type.IsBonus()) currentBonusPortion -= GetBonusScoreChange(result); @@ -297,21 +297,100 @@ namespace osu.Game.Rulesets.Scoring updateScore(); } - protected virtual double GetBonusScoreChange(JudgementResult result) => GetNumericResultFor(result); - - protected virtual double GetComboScoreChange(JudgementResult result) => GetMaxNumericResultFor(result) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); + /// + /// Gets the final score change to be applied to the bonus portion of the score. + /// + /// The judgement result. + protected virtual double GetBonusScoreChange(JudgementResult result) => GetRawBonusScore(result.Type); /// - /// Retrieves the numeric score representation for a . + /// Gets the final score change to be applied to the combo portion of the score. /// - /// The . - protected virtual double GetNumericResultFor(JudgementResult result) => result.Judgement.NumericResultFor(result); + /// The judgement result. + protected virtual double GetComboScoreChange(JudgementResult result) => GetRawComboScore(result.Judgement.MaxResult) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); /// - /// Retrieves the maximum numeric score representation for a . + /// Retrieves the raw score value for a hit result, in order to be applied to the combo portion. /// - /// The . - protected virtual double GetMaxNumericResultFor(JudgementResult result) => result.Judgement.MaxNumericResult; + /// The hit result. + public virtual int GetRawComboScore(HitResult result) + { + switch (result) + { + default: + return 0; + + case HitResult.SmallTickHit: + return 10; + + case HitResult.LargeTickHit: + return 30; + + case HitResult.Meh: + return 50; + + case HitResult.Ok: + return 100; + + case HitResult.Good: + return 200; + + case HitResult.Great: + case HitResult.Perfect: // Perfect doesn't actually give more score / accuracy directly. + return 300; + } + } + + /// + /// Retrieves the raw score value for a hit result, in order to be applied to the accuracy portion. + /// + /// The hit result. + public virtual int GetRawAccuracyScore(HitResult result) + { + switch (result) + { + default: + return 0; + + case HitResult.SmallTickHit: + return 10; + + case HitResult.LargeTickHit: + return 30; + + case HitResult.Meh: + return 50; + + case HitResult.Ok: + return 100; + + case HitResult.Good: + return 200; + + case HitResult.Great: + case HitResult.Perfect: // Perfect doesn't actually give more score / accuracy directly. + return 300; + } + } + + /// + /// Retrieves the raw score value for a hit result, in order to be applied to the bonus portion. + /// + /// The hit result. + public virtual int GetRawBonusScore(HitResult result) + { + switch (result) + { + default: + return 0; + + case HitResult.SmallBonus: + return 10; + + case HitResult.LargeBonus: + return 50; + } + } protected virtual void ApplyScoreChange(JudgementResult result) { @@ -540,7 +619,7 @@ namespace osu.Game.Rulesets.Scoring /// /// /// Used to compute accuracy. - /// See: and . + /// See: and . /// [Key(0)] public double BaseScore { get; set; } diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index b216c0897e..b1cba3756b 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -17,7 +17,6 @@ using osu.Game.Scoring.Legacy; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; -using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using Realms; @@ -125,13 +124,14 @@ namespace osu.Game.Scoring var beatmap = score.BeatmapInfo!.Detach(); var ruleset = score.Ruleset.Detach(); var rulesetInstance = ruleset.CreateInstance(); + var scoreProcessor = rulesetInstance.CreateScoreProcessor(); Debug.Assert(rulesetInstance != null); // Populate the maximum statistics. HitResult maxBasicResult = rulesetInstance.GetHitResults() .Select(h => h.result) - .Where(h => h.IsBasic()).MaxBy(Judgement.ToNumericResult); + .Where(h => h.IsBasic()).MaxBy(scoreProcessor.GetRawAccuracyScore); foreach ((HitResult result, int count) in score.Statistics) { From 6b4b2a57fc7d9652dd4726b388656175cec1e09b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Dec 2023 14:58:23 +0900 Subject: [PATCH 0615/1118] Expose only as one method --- .../Difficulty/CatchLegacyScoreSimulator.cs | 2 +- .../Scoring/CatchScoreProcessor.cs | 2 +- .../Scoring/ManiaScoreProcessor.cs | 16 +++-- .../TestSceneSpinnerRotation.cs | 2 +- .../Difficulty/OsuLegacyScoreSimulator.cs | 2 +- .../Objects/Drawables/DrawableSpinner.cs | 2 +- .../Difficulty/TaikoLegacyScoreSimulator.cs | 2 +- .../Scoring/TaikoScoreProcessor.cs | 17 +---- .../Gameplay/TestSceneScoreProcessor.cs | 2 +- .../StandardisedScoreMigrationTools.cs | 4 +- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 65 +++---------------- osu.Game/Scoring/ScoreImporter.cs | 2 +- 12 files changed, 30 insertions(+), 88 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs index 52605ec0cc..f65b6ef381 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs @@ -136,7 +136,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty if (isBonus) { legacyBonusScore += scoreIncrease; - standardisedBonusScore += scoreProcessor.GetRawBonusScore(bonusResult); + standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult); } else attributes.AccuracyScore += scoreIncrease; diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 023369badc..4b3d378889 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -33,7 +33,7 @@ namespace osu.Game.Rulesets.Catch.Scoring } protected override double GetComboScoreChange(JudgementResult result) - => GetRawComboScore(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); + => GetBaseScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); public override ScoreRank RankFromAccuracy(double accuracy) { diff --git a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs index d059d99d9f..1947d86a97 100644 --- a/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs +++ b/osu.Game.Rulesets.Mania/Scoring/ManiaScoreProcessor.cs @@ -31,7 +31,12 @@ namespace osu.Game.Rulesets.Mania.Scoring + bonusPortion; } - public override int GetRawAccuracyScore(HitResult result) + protected override double GetComboScoreChange(JudgementResult result) + { + return getBaseComboScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); + } + + public override int GetBaseScoreForResult(HitResult result) { switch (result) { @@ -39,10 +44,10 @@ namespace osu.Game.Rulesets.Mania.Scoring return 305; } - return base.GetRawAccuracyScore(result); + return base.GetBaseScoreForResult(result); } - public override int GetRawComboScore(HitResult result) + private int getBaseComboScoreForResult(HitResult result) { switch (result) { @@ -50,12 +55,9 @@ namespace osu.Game.Rulesets.Mania.Scoring return 300; } - return base.GetRawComboScore(result); + return GetBaseScoreForResult(result); } - protected override double GetComboScoreChange(JudgementResult result) - => GetRawComboScore(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)); - private class JudgementOrderComparer : IComparer { public static readonly JudgementOrderComparer DEFAULT = new JudgementOrderComparer(); diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs index fd445bc1ac..6706d20080 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSpinnerRotation.cs @@ -134,7 +134,7 @@ namespace osu.Game.Rulesets.Osu.Tests // multipled by 2 to nullify the score multiplier. (autoplay mod selected) long totalScore = scoreProcessor.TotalScore.Value * 2; - return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * scoreProcessor.GetRawBonusScore(new SpinnerTick().CreateJudgement().MaxResult); + return totalScore == (int)(drawableSpinner.Result.TotalRotation / 360) * scoreProcessor.GetBaseScoreForResult(new SpinnerTick().CreateJudgement().MaxResult); }); addSeekStep(0); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs index 6ac7c33275..a76054b42c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs @@ -173,7 +173,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty if (isBonus) { legacyBonusScore += scoreIncrease; - standardisedBonusScore += scoreProcessor.GetRawBonusScore(bonusResult); + standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult); } else attributes.AccuracyScore += scoreIncrease; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index 787d5e5937..f7c1437009 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -313,7 +313,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables updateBonusScore(); } - private static readonly int score_per_tick = new OsuScoreProcessor().GetRawBonusScore(new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxResult); + private static readonly int score_per_tick = new OsuScoreProcessor().GetBaseScoreForResult(new SpinnerBonusTick.OsuSpinnerBonusTickJudgement().MaxResult); private void updateBonusScore() { diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index 8b34c8c808..b20aa4f2b6 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -193,7 +193,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty if (isBonus) { legacyBonusScore += scoreIncrease; - standardisedBonusScore += scoreProcessor.GetRawBonusScore(bonusResult); + standardisedBonusScore += scoreProcessor.GetBaseScoreForResult(bonusResult); } else attributes.AccuracyScore += scoreIncrease; diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs index e81ba324bd..2fd9f070ec 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoScoreProcessor.cs @@ -28,12 +28,12 @@ namespace osu.Game.Rulesets.Taiko.Scoring protected override double GetComboScoreChange(JudgementResult result) { - return GetRawComboScore(result.Type) + return GetBaseScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(400, combo_base)) * strongScaleValue(result); } - public override int GetRawAccuracyScore(HitResult result) + public override int GetBaseScoreForResult(HitResult result) { switch (result) { @@ -41,18 +41,7 @@ namespace osu.Game.Rulesets.Taiko.Scoring return 150; } - return base.GetRawAccuracyScore(result); - } - - public override int GetRawComboScore(HitResult result) - { - switch (result) - { - case HitResult.Ok: - return 150; - } - - return base.GetRawComboScore(result); + return base.GetBaseScoreForResult(result); } private double strongScaleValue(JudgementResult result) diff --git a/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs b/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs index 5d2ad8b363..1a644ad600 100644 --- a/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs +++ b/osu.Game.Tests/Gameplay/TestSceneScoreProcessor.cs @@ -48,7 +48,7 @@ namespace osu.Game.Tests.Gameplay // Apply a judgement scoreProcessor.ApplyResult(new JudgementResult(new HitObject(), new TestJudgement(HitResult.LargeBonus)) { Type = HitResult.LargeBonus }); - Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(scoreProcessor.GetRawBonusScore(HitResult.LargeBonus))); + Assert.That(scoreProcessor.TotalScore.Value, Is.EqualTo(scoreProcessor.GetBaseScoreForResult(HitResult.LargeBonus))); } [Test] diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 5324d2569e..8a25a44b80 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -57,14 +57,14 @@ namespace osu.Game.Database // We are constructing a "best possible" score from the statistics provided because it's the best we can do. List sortedHits = score.Statistics .Where(kvp => kvp.Key.AffectsCombo()) - .OrderByDescending(kvp => processor.GetRawComboScore(kvp.Key)) + .OrderByDescending(kvp => processor.GetBaseScoreForResult(kvp.Key)) .SelectMany(kvp => Enumerable.Repeat(kvp.Key, kvp.Value)) .ToList(); // Attempt to use maximum statistics from the database. var maximumJudgements = score.MaximumStatistics .Where(kvp => kvp.Key.AffectsCombo()) - .OrderByDescending(kvp => processor.GetRawComboScore(kvp.Key)) + .OrderByDescending(kvp => processor.GetBaseScoreForResult(kvp.Key)) .SelectMany(kvp => Enumerable.Repeat(new FakeJudgement(kvp.Key), kvp.Value)) .ToList(); diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index a80a118363..5123668e54 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -227,12 +227,12 @@ namespace osu.Game.Rulesets.Scoring if (result.Judgement.MaxResult.AffectsAccuracy()) { - currentMaximumBaseScore += GetRawAccuracyScore(result.Judgement.MaxResult); + currentMaximumBaseScore += GetBaseScoreForResult(result.Judgement.MaxResult); currentAccuracyJudgementCount++; } if (result.Type.AffectsAccuracy()) - currentBaseScore += GetRawAccuracyScore(result.Type); + currentBaseScore += GetBaseScoreForResult(result.Type); if (result.Type.IsBonus()) currentBonusPortion += GetBonusScoreChange(result); @@ -276,12 +276,12 @@ namespace osu.Game.Rulesets.Scoring if (result.Judgement.MaxResult.AffectsAccuracy()) { - currentMaximumBaseScore -= GetRawAccuracyScore(result.Judgement.MaxResult); + currentMaximumBaseScore -= GetBaseScoreForResult(result.Judgement.MaxResult); currentAccuracyJudgementCount--; } if (result.Type.AffectsAccuracy()) - currentBaseScore -= GetRawAccuracyScore(result.Type); + currentBaseScore -= GetBaseScoreForResult(result.Type); if (result.Type.IsBonus()) currentBonusPortion -= GetBonusScoreChange(result); @@ -301,19 +301,15 @@ namespace osu.Game.Rulesets.Scoring /// Gets the final score change to be applied to the bonus portion of the score. /// /// The judgement result. - protected virtual double GetBonusScoreChange(JudgementResult result) => GetRawBonusScore(result.Type); + protected virtual double GetBonusScoreChange(JudgementResult result) => GetBaseScoreForResult(result.Type); /// /// Gets the final score change to be applied to the combo portion of the score. /// /// The judgement result. - protected virtual double GetComboScoreChange(JudgementResult result) => GetRawComboScore(result.Judgement.MaxResult) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); + protected virtual double GetComboScoreChange(JudgementResult result) => GetBaseScoreForResult(result.Judgement.MaxResult) * Math.Pow(result.ComboAfterJudgement, COMBO_EXPONENT); - /// - /// Retrieves the raw score value for a hit result, in order to be applied to the combo portion. - /// - /// The hit result. - public virtual int GetRawComboScore(HitResult result) + public virtual int GetBaseScoreForResult(HitResult result) { switch (result) { @@ -338,51 +334,6 @@ namespace osu.Game.Rulesets.Scoring case HitResult.Great: case HitResult.Perfect: // Perfect doesn't actually give more score / accuracy directly. return 300; - } - } - - /// - /// Retrieves the raw score value for a hit result, in order to be applied to the accuracy portion. - /// - /// The hit result. - public virtual int GetRawAccuracyScore(HitResult result) - { - switch (result) - { - default: - return 0; - - case HitResult.SmallTickHit: - return 10; - - case HitResult.LargeTickHit: - return 30; - - case HitResult.Meh: - return 50; - - case HitResult.Ok: - return 100; - - case HitResult.Good: - return 200; - - case HitResult.Great: - case HitResult.Perfect: // Perfect doesn't actually give more score / accuracy directly. - return 300; - } - } - - /// - /// Retrieves the raw score value for a hit result, in order to be applied to the bonus portion. - /// - /// The hit result. - public virtual int GetRawBonusScore(HitResult result) - { - switch (result) - { - default: - return 0; case HitResult.SmallBonus: return 10; @@ -619,7 +570,7 @@ namespace osu.Game.Rulesets.Scoring /// /// /// Used to compute accuracy. - /// See: and . + /// See: and . /// [Key(0)] public double BaseScore { get; set; } diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index b1cba3756b..3c7ee5c86b 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -131,7 +131,7 @@ namespace osu.Game.Scoring // Populate the maximum statistics. HitResult maxBasicResult = rulesetInstance.GetHitResults() .Select(h => h.result) - .Where(h => h.IsBasic()).MaxBy(scoreProcessor.GetRawAccuracyScore); + .Where(h => h.IsBasic()).MaxBy(scoreProcessor.GetBaseScoreForResult); foreach ((HitResult result, int count) in score.Statistics) { From eb072a1d242f0f1680183200310742fa4d850371 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Dec 2023 15:08:10 +0900 Subject: [PATCH 0616/1118] Add accuracy conversion, fix usages --- osu.Game/BackgroundDataStoreProcessor.cs | 5 +-- .../StandardisedScoreMigrationTools.cs | 42 +++++++++++++++++-- osu.Game/Scoring/ScoreImporter.cs | 2 +- 3 files changed, 41 insertions(+), 8 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 8f6dced350..a748a7422a 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -340,15 +340,12 @@ namespace osu.Game try { - var score = scoreManager.Query(s => s.ID == id); - long newTotalScore = StandardisedScoreMigrationTools.ConvertFromLegacyTotalScore(score, beatmapManager); - // Can't use async overload because we're not on the update thread. // ReSharper disable once MethodHasAsyncOverload realmAccess.Write(r => { ScoreInfo s = r.Find(id)!; - s.TotalScore = newTotalScore; + StandardisedScoreMigrationTools.UpdateFromLegacy(s, beatmapManager); s.TotalScoreVersion = LegacyScoreEncoder.LATEST_VERSION; }); diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 8a25a44b80..380bf63e63 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -231,13 +231,36 @@ namespace osu.Game.Database } } + /// + /// Updates a legacy to standardised scoring. + /// + /// The score to update. + /// A used for lookups. + public static void UpdateFromLegacy(ScoreInfo score, BeatmapManager beatmaps) + { + score.TotalScore = convertFromLegacyTotalScore(score, beatmaps); + score.Accuracy = ComputeAccuracy(score); + } + + /// + /// Updates a legacy to standardised scoring. + /// + /// The score to update. + /// The beatmap difficulty. + /// The legacy scoring attributes for the beatmap which the score was set on. + public static void UpdateFromLegacy(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes) + { + score.TotalScore = convertFromLegacyTotalScore(score, difficulty, attributes); + score.Accuracy = ComputeAccuracy(score); + } + /// /// Converts from to the new standardised scoring of . /// /// The score to convert the total score of. /// A used for lookups. /// The standardised total score. - public static long ConvertFromLegacyTotalScore(ScoreInfo score, BeatmapManager beatmaps) + private static long convertFromLegacyTotalScore(ScoreInfo score, BeatmapManager beatmaps) { if (!score.IsLegacyScore) return score.TotalScore; @@ -260,7 +283,7 @@ namespace osu.Game.Database ILegacyScoreSimulator sv1Simulator = legacyRuleset.CreateLegacyScoreSimulator(); LegacyScoreAttributes attributes = sv1Simulator.Simulate(beatmap, playableBeatmap); - return ConvertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes); + return convertFromLegacyTotalScore(score, LegacyBeatmapConversionDifficultyInfo.FromBeatmap(beatmap.Beatmap), attributes); } /// @@ -270,7 +293,7 @@ namespace osu.Game.Database /// The beatmap difficulty. /// The legacy scoring attributes for the beatmap which the score was set on. /// The standardised total score. - public static long ConvertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes) + private static long convertFromLegacyTotalScore(ScoreInfo score, LegacyBeatmapConversionDifficultyInfo difficulty, LegacyScoreAttributes attributes) { if (!score.IsLegacyScore) return score.TotalScore; @@ -422,6 +445,19 @@ namespace osu.Game.Database } } + public static double ComputeAccuracy(ScoreInfo scoreInfo) + { + Ruleset ruleset = scoreInfo.Ruleset.CreateInstance(); + ScoreProcessor scoreProcessor = ruleset.CreateScoreProcessor(); + + int baseScore = scoreInfo.Statistics.Where(kvp => kvp.Key.AffectsAccuracy()) + .Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key)); + int maxBaseScore = scoreInfo.MaximumStatistics.Where(kvp => kvp.Key.AffectsAccuracy()) + .Sum(kvp => kvp.Value * scoreProcessor.GetBaseScoreForResult(kvp.Key)); + + return maxBaseScore == 0 ? 1 : baseScore / (double)maxBaseScore; + } + /// /// Used to populate the model using data parsed from its corresponding replay file. /// diff --git a/osu.Game/Scoring/ScoreImporter.cs b/osu.Game/Scoring/ScoreImporter.cs index 3c7ee5c86b..8e28707107 100644 --- a/osu.Game/Scoring/ScoreImporter.cs +++ b/osu.Game/Scoring/ScoreImporter.cs @@ -106,7 +106,7 @@ namespace osu.Game.Scoring else if (model.IsLegacyScore) { model.LegacyTotalScore = model.TotalScore; - model.TotalScore = StandardisedScoreMigrationTools.ConvertFromLegacyTotalScore(model, beatmaps()); + StandardisedScoreMigrationTools.UpdateFromLegacy(model, beatmaps()); } } From c3c1752a5a315ae2d72b8c4b6b6a9436d5e579eb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Thu, 21 Dec 2023 15:32:34 +0900 Subject: [PATCH 0617/1118] Reconvert all legacy scores --- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index fa930d77d3..dbd9a106df 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -34,9 +34,10 @@ namespace osu.Game.Scoring.Legacy /// 30000005: Introduce combo exponent in the osu! gamemode. Reconvert all scores. /// 30000006: Fix edge cases in conversion after combo exponent introduction that lead to NaNs. Reconvert all scores. /// 30000007: Adjust osu!mania combo and accuracy portions and judgement scoring values. Reconvert all scores. + /// 30000008: Add accuracy conversion. Reconvert all scores. /// /// - public const int LATEST_VERSION = 30000007; + public const int LATEST_VERSION = 30000008; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From 0648201844a962429a99a2754a2c10e439882680 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2023 18:17:03 +0900 Subject: [PATCH 0618/1118] Cancel test more --- osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs index 218bf495e3..ce241f3676 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestScenePlaySongSelect.cs @@ -420,7 +420,7 @@ namespace osu.Game.Tests.Visual.SongSelect } [Test] - [FlakyTest] // temporary while peppy investigates + [Ignore("temporary while peppy investigates. probably realm batching related.")] public void TestSelectionRetainedOnBeatmapUpdate() { createSongSelect(); From a4baa0a716f05e6415e907824ff0bd06b66e2dfb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 21 Dec 2023 18:14:24 +0900 Subject: [PATCH 0619/1118] Add versioning of local scores For any potential future usage --- osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 2 ++ .../Visual/Gameplay/TestScenePlayerLocalScoreImport.cs | 4 ++++ osu.Game/Database/RealmAccess.cs | 3 ++- osu.Game/Scoring/ScoreInfo.cs | 6 ++++++ osu.Game/Screens/Play/Player.cs | 9 ++++++++- 5 files changed, 22 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index dd724d268e..85b4219792 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -196,6 +196,7 @@ namespace osu.Game.Tests.Scores.IO User = new APIUser { Username = "Test user" }, BeatmapInfo = beatmap.Beatmaps.First(), Ruleset = new OsuRuleset().RulesetInfo, + Version = "12345", Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, }; @@ -203,6 +204,7 @@ namespace osu.Game.Tests.Scores.IO Assert.IsTrue(imported.Mods.Any(m => m is OsuModHardRock)); Assert.IsTrue(imported.Mods.Any(m => m is OsuModDoubleTime)); + Assert.That(imported.Version, Is.EqualTo(toImport.Version)); } finally { diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs index 0dd544bb30..f7a5ec7562 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs @@ -41,6 +41,9 @@ namespace osu.Game.Tests.Visual.Gameplay private BeatmapSetInfo? importedSet; + [Resolved] + private OsuGameBase osu { get; set; } = null!; + [BackgroundDependencyLoader] private void load(GameHost host, AudioManager audio) { @@ -153,6 +156,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("results displayed", () => Player.GetChildScreen() is ResultsScreen); AddUntilStep("score in database", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID) != null)); + AddUntilStep("score has correct version", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID)!.Version), () => Is.EqualTo(osu.Version)); } [Test] diff --git a/osu.Game/Database/RealmAccess.cs b/osu.Game/Database/RealmAccess.cs index ad61292c2e..4bd7f36cdd 100644 --- a/osu.Game/Database/RealmAccess.cs +++ b/osu.Game/Database/RealmAccess.cs @@ -90,8 +90,9 @@ namespace osu.Game.Database /// 36 2023-10-26 Add LegacyOnlineID to ScoreInfo. Move osu_scores_*_high IDs stored in OnlineID to LegacyOnlineID. Reset anomalous OnlineIDs. /// 38 2023-12-10 Add EndTimeObjectCount and TotalObjectCount to BeatmapInfo. /// 39 2023-12-19 Migrate any EndTimeObjectCount and TotalObjectCount values of 0 to -1 to better identify non-calculated values. + /// 40 2023-12-21 Add ScoreInfo.Version to keep track of which build scores were set on. /// - private const int schema_version = 39; + private const int schema_version = 40; /// /// Lock object which is held during sections, blocking realm retrieval during blocking periods. diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 5545ba552e..4c00c73341 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -46,6 +46,12 @@ namespace osu.Game.Scoring /// public BeatmapInfo? BeatmapInfo { get; set; } + /// + /// The version of the client this score was set using. + /// Sourced from at the point of score submission. + /// + public string Version { get; set; } = string.Empty; + /// /// The at the point in time when the score was set. /// diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index cc08079d88..a432242046 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -109,6 +109,9 @@ namespace osu.Game.Screens.Play [Resolved] private MusicController musicController { get; set; } + [Resolved] + private OsuGameBase game { get; set; } + public GameplayState GameplayState { get; private set; } private Ruleset ruleset; @@ -1155,7 +1158,11 @@ namespace osu.Game.Screens.Play /// The . protected virtual Score CreateScore(IBeatmap beatmap) => new Score { - ScoreInfo = new ScoreInfo { User = api.LocalUser.Value }, + ScoreInfo = new ScoreInfo + { + User = api.LocalUser.Value, + Version = game.Version, + }, }; /// From 81bbdccee788daac7c7865859673eed1521191e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Dec 2023 12:56:19 +0100 Subject: [PATCH 0620/1118] Rename `ScoreInfo.{ -> Client}Version` --- osu.Game.Tests/Scores/IO/ImportScoreTest.cs | 4 ++-- .../Visual/Gameplay/TestScenePlayerLocalScoreImport.cs | 2 +- osu.Game/Scoring/ScoreInfo.cs | 2 +- osu.Game/Screens/Play/Player.cs | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs index 85b4219792..ebbc329b9d 100644 --- a/osu.Game.Tests/Scores/IO/ImportScoreTest.cs +++ b/osu.Game.Tests/Scores/IO/ImportScoreTest.cs @@ -196,7 +196,7 @@ namespace osu.Game.Tests.Scores.IO User = new APIUser { Username = "Test user" }, BeatmapInfo = beatmap.Beatmaps.First(), Ruleset = new OsuRuleset().RulesetInfo, - Version = "12345", + ClientVersion = "12345", Mods = new Mod[] { new OsuModHardRock(), new OsuModDoubleTime() }, }; @@ -204,7 +204,7 @@ namespace osu.Game.Tests.Scores.IO Assert.IsTrue(imported.Mods.Any(m => m is OsuModHardRock)); Assert.IsTrue(imported.Mods.Any(m => m is OsuModDoubleTime)); - Assert.That(imported.Version, Is.EqualTo(toImport.Version)); + Assert.That(imported.ClientVersion, Is.EqualTo(toImport.ClientVersion)); } finally { diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs index f7a5ec7562..fafd1330cc 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLocalScoreImport.cs @@ -156,7 +156,7 @@ namespace osu.Game.Tests.Visual.Gameplay AddUntilStep("results displayed", () => Player.GetChildScreen() is ResultsScreen); AddUntilStep("score in database", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID) != null)); - AddUntilStep("score has correct version", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID)!.Version), () => Is.EqualTo(osu.Version)); + AddUntilStep("score has correct version", () => Realm.Run(r => r.Find(Player.Score.ScoreInfo.ID)!.ClientVersion), () => Is.EqualTo(osu.Version)); } [Test] diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 4c00c73341..7071bd380e 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -50,7 +50,7 @@ namespace osu.Game.Scoring /// The version of the client this score was set using. /// Sourced from at the point of score submission. /// - public string Version { get; set; } = string.Empty; + public string ClientVersion { get; set; } = string.Empty; /// /// The at the point in time when the score was set. diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index a432242046..c9251b0a78 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1161,7 +1161,7 @@ namespace osu.Game.Screens.Play ScoreInfo = new ScoreInfo { User = api.LocalUser.Value, - Version = game.Version, + ClientVersion = game.Version, }, }; From 2baf579f7ce62429e83aa4781ec3d55320b92d09 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Dec 2023 12:58:08 +0100 Subject: [PATCH 0621/1118] Serialise and deserialise `ClientVersion` to replays --- osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs | 4 ++++ osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 1 + 2 files changed, 5 insertions(+) diff --git a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs index d34edf7bdf..2c5b91f10f 100644 --- a/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs +++ b/osu.Game/Scoring/Legacy/LegacyReplaySoloScoreInfo.cs @@ -35,12 +35,16 @@ namespace osu.Game.Scoring.Legacy [JsonProperty("maximum_statistics")] public Dictionary MaximumStatistics { get; set; } = new Dictionary(); + [JsonProperty("client_version")] + public string ClientVersion = string.Empty; + public static LegacyReplaySoloScoreInfo FromScore(ScoreInfo score) => new LegacyReplaySoloScoreInfo { OnlineID = score.OnlineID, Mods = score.APIMods, Statistics = score.Statistics.Where(kvp => kvp.Value != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value), MaximumStatistics = score.MaximumStatistics.Where(kvp => kvp.Value != 0).ToDictionary(kvp => kvp.Key, kvp => kvp.Value), + ClientVersion = score.ClientVersion, }; } } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index c5e6e3bcce..ed11691674 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -125,6 +125,7 @@ namespace osu.Game.Scoring.Legacy score.ScoreInfo.Statistics = readScore.Statistics; score.ScoreInfo.MaximumStatistics = readScore.MaximumStatistics; score.ScoreInfo.Mods = readScore.Mods.Select(m => m.ToMod(currentRuleset)).ToArray(); + score.ScoreInfo.ClientVersion = readScore.ClientVersion; }); } } From 5ff95db02c2af9ce6e7b1d707ee88440e3df9e6e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Dec 2023 13:06:42 +0100 Subject: [PATCH 0622/1118] Add test coverage of `ClientVersion` serialisation --- osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs index ab88be1511..4e281cf28e 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs @@ -219,6 +219,8 @@ namespace osu.Game.Tests.Beatmaps.Formats { new OsuModDoubleTime { SpeedChange = { Value = 1.1 } } }; + scoreInfo.OnlineID = 123123; + scoreInfo.ClientVersion = "2023.1221.0"; var beatmap = new TestBeatmap(ruleset); var score = new Score @@ -237,9 +239,11 @@ namespace osu.Game.Tests.Beatmaps.Formats Assert.Multiple(() => { + Assert.That(decodedAfterEncode.ScoreInfo.OnlineID, Is.EqualTo(123123)); Assert.That(decodedAfterEncode.ScoreInfo.Statistics, Is.EqualTo(scoreInfo.Statistics)); Assert.That(decodedAfterEncode.ScoreInfo.MaximumStatistics, Is.EqualTo(scoreInfo.MaximumStatistics)); Assert.That(decodedAfterEncode.ScoreInfo.Mods, Is.EqualTo(scoreInfo.Mods)); + Assert.That(decodedAfterEncode.ScoreInfo.ClientVersion, Is.EqualTo("2023.1221.0")); }); } From d4731e0830867868eec1050e51e90587bcfb330b Mon Sep 17 00:00:00 2001 From: Daniel Power Date: Thu, 21 Dec 2023 08:56:39 -0330 Subject: [PATCH 0623/1118] Fix scale of skin element bounding box --- osu.Game/Overlays/SkinEditor/SkinBlueprint.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs b/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs index 01cd3d97e0..8f8d899fad 100644 --- a/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs +++ b/osu.Game/Overlays/SkinEditor/SkinBlueprint.cs @@ -136,9 +136,10 @@ namespace osu.Game.Overlays.SkinEditor { base.Update(); + Vector2 scale = drawable.DrawInfo.MatrixInverse.ExtractScale().Xy; drawableQuad = drawable.ToScreenSpace( drawable.DrawRectangle - .Inflate(SkinSelectionHandler.INFLATE_SIZE)); + .Inflate(SkinSelectionHandler.INFLATE_SIZE * scale)); var localSpaceQuad = ToLocalSpace(drawableQuad); From b4e71a0787df19714a5cbf0a166a81d139d7cd23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 21 Dec 2023 13:58:12 +0100 Subject: [PATCH 0624/1118] Fix slider tick / end misses displaying with full size on legacy skins with animated misses Resolves https://github.com/ppy/osu/issues/25987. Structure is a bit clumsy but I'm not sure how to do better... --- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 21 +++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index ca2c8ce6bc..68274ffa2d 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -50,17 +50,16 @@ namespace osu.Game.Skinning // legacy judgements don't play any transforms if they are an animation.... UNLESS they are the temporary displayed judgement from new piece. if (animation?.FrameCount > 1 && !forceTransforms) + { + if (isMissedTick()) + applyMissedTickScaling(); return; + } if (result.IsMiss()) { - bool isTick = result != HitResult.Miss; - - if (isTick) - { - this.ScaleTo(0.6f); - this.ScaleTo(0.3f, 100, Easing.In); - } + if (isMissedTick()) + applyMissedTickScaling(); else { this.ScaleTo(1.6f); @@ -95,6 +94,14 @@ namespace osu.Game.Skinning } } + private bool isMissedTick() => result.IsMiss() && result != HitResult.Miss; + + private void applyMissedTickScaling() + { + this.ScaleTo(0.6f); + this.ScaleTo(0.3f, 100, Easing.In); + } + public Drawable GetAboveHitObjectsProxiedContent() => CreateProxy(); } } From 3f6dad5502d6aec67d9e7522fdb69d21299d5058 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 22 Dec 2023 01:33:50 +0900 Subject: [PATCH 0625/1118] Use classic HP values for non-classic osu! HP drain --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 + .../Scoring/OsuHealthProcessor.cs | 68 +++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index e53f20277b..35cbfa3790 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -48,6 +48,8 @@ namespace osu.Game.Rulesets.Osu public override ScoreProcessor CreateScoreProcessor() => new OsuScoreProcessor(); + public override HealthProcessor CreateHealthProcessor(double drainStartTime) => new OsuHealthProcessor(drainStartTime); + public override IBeatmapConverter CreateBeatmapConverter(IBeatmap beatmap) => new OsuBeatmapConverter(beatmap, this); public override IBeatmapProcessor CreateBeatmapProcessor(IBeatmap beatmap) => new OsuBeatmapProcessor(beatmap); diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs new file mode 100644 index 0000000000..784d3b934d --- /dev/null +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -0,0 +1,68 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.Osu.Scoring +{ + public partial class OsuHealthProcessor : DrainingHealthProcessor + { + public OsuHealthProcessor(double drainStartTime, double drainLenience = 0) + : base(drainStartTime, drainLenience) + { + } + + protected override double GetHealthIncreaseFor(JudgementResult result) + { + switch (result.Type) + { + case HitResult.SmallTickMiss: + return IBeatmapDifficultyInfo.DifficultyRange(Beatmap.Difficulty.DrainRate, -0.02, -0.075, -0.14); + + case HitResult.LargeTickMiss: + return IBeatmapDifficultyInfo.DifficultyRange(Beatmap.Difficulty.DrainRate, -0.02, -0.075, -0.14); + + case HitResult.Miss: + return IBeatmapDifficultyInfo.DifficultyRange(Beatmap.Difficulty.DrainRate, -0.03, -0.125, -0.2); + + case HitResult.SmallTickHit: + // When classic slider mechanics are enabled, this result comes from the tail. + return 0.02; + + case HitResult.LargeTickHit: + switch (result.HitObject) + { + case SliderTick: + return 0.015; + + case SliderHeadCircle: + case SliderTailCircle: + case SliderRepeat: + return 0.02; + } + + break; + + case HitResult.Meh: + return 0.002; + + case HitResult.Ok: + return 0.011; + + case HitResult.Great: + return 0.03; + + case HitResult.SmallBonus: + return 0.0085; + + case HitResult.LargeBonus: + return 0.01; + } + + return base.GetHealthIncreaseFor(result); + } + } +} From b31b9e96d0cf016e4f7b892116fd6951977a7853 Mon Sep 17 00:00:00 2001 From: Simon G <45692977+jeenyuhs@users.noreply.github.com> Date: Fri, 22 Dec 2023 03:04:48 +0100 Subject: [PATCH 0626/1118] adjust beatmap length and drain based on rate changing mods --- osu.Game/Screens/Select/BeatmapInfoWedge.cs | 25 ++++++++++++++------- 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapInfoWedge.cs b/osu.Game/Screens/Select/BeatmapInfoWedge.cs index 2613857998..c69cd6ead6 100644 --- a/osu.Game/Screens/Select/BeatmapInfoWedge.cs +++ b/osu.Game/Screens/Select/BeatmapInfoWedge.cs @@ -161,6 +161,7 @@ namespace osu.Game.Screens.Select private ILocalisedBindableString artistBinding; private FillFlowContainer infoLabelContainer; private Container bpmLabelContainer; + private Container lengthLabelContainer; private readonly WorkingBeatmap working; private readonly RulesetInfo ruleset; @@ -341,10 +342,10 @@ namespace osu.Game.Screens.Select { settingChangeTracker?.Dispose(); - refreshBPMLabel(); + refreshBPMAndLengthLabel(); settingChangeTracker = new ModSettingChangeTracker(m.NewValue); - settingChangeTracker.SettingChanged += _ => refreshBPMLabel(); + settingChangeTracker.SettingChanged += _ => refreshBPMAndLengthLabel(); }, true); } @@ -370,12 +371,10 @@ namespace osu.Game.Screens.Select infoLabelContainer.Children = new Drawable[] { - new InfoLabel(new BeatmapStatistic + lengthLabelContainer = new Container { - Name = BeatmapsetsStrings.ShowStatsTotalLength(playableBeatmap.CalculateDrainLength().ToFormattedDuration()), - CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), - Content = working.BeatmapInfo.Length.ToFormattedDuration().ToString(), - }), + AutoSizeAxes = Axes.Both, + }, bpmLabelContainer = new Container { AutoSizeAxes = Axes.Both, @@ -394,7 +393,7 @@ namespace osu.Game.Screens.Select } } - private void refreshBPMLabel() + private void refreshBPMAndLengthLabel() { var beatmap = working.Beatmap; @@ -420,6 +419,16 @@ namespace osu.Game.Screens.Select CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Bpm), Content = labelText }); + + double drainLength = Math.Round(beatmap.CalculateDrainLength() / rate); + double hitLength = Math.Round(beatmap.BeatmapInfo.Length / rate); + + lengthLabelContainer.Child = new InfoLabel(new BeatmapStatistic + { + Name = BeatmapsetsStrings.ShowStatsTotalLength(drainLength.ToFormattedDuration()), + CreateIcon = () => new BeatmapStatisticIcon(BeatmapStatisticsIconType.Length), + Content = hitLength.ToFormattedDuration().ToString(), + }); } private Drawable getMapper(BeatmapMetadata metadata) From 9c35e250368ca3d777e1b2f4394310008839d3b3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 22 Dec 2023 13:38:52 +0900 Subject: [PATCH 0627/1118] Add failing tests --- .../Mods/TestSceneManiaModPerfect.cs | 29 +++++++++++++++++++ .../Mods/TestSceneOsuModPerfect.cs | 29 +++++++++++++++++++ osu.Game/Tests/Visual/ModPerfectTestScene.cs | 8 ++--- osu.Game/Tests/Visual/ModTestScene.cs | 18 ++++++------ 4 files changed, 71 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs index 97a6ee28f4..a734979bf6 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs @@ -1,9 +1,14 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using NUnit.Framework; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Mania.Mods; using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Replays; using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Mania.Tests.Mods @@ -24,5 +29,29 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods [TestCase(false)] [TestCase(true)] public void TestHoldNote(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new HoldNote { StartTime = 1000, EndTime = 3000 }), shouldMiss); + + [Test] + public void TestBreakOnHoldNote() => CreateModTest(new ModTestData + { + Mod = new ManiaModPerfect(), + PassCondition = () => ((PerfectModTestPlayer)Player).CheckFailed(true) && Player.Results.Count == 2, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HoldNote + { + StartTime = 1000, + EndTime = 3000, + }, + }, + }, + ReplayFrames = new List + { + new ManiaReplayFrame(1000, ManiaAction.Key1), + new ManiaReplayFrame(2000) + } + }); } } diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs index 26c4133bc4..7030061e0e 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs @@ -1,11 +1,15 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; using NUnit.Framework; +using osu.Game.Beatmaps; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays; using osu.Game.Tests.Visual; using osuTK; @@ -50,5 +54,30 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods CreateHitObjectTest(new HitObjectTestData(spinner), shouldMiss); } + + [Test] + public void TestMissSliderTail() => CreateModTest(new ModTestData + { + Mod = new OsuModPerfect(), + PassCondition = () => ((PerfectModTestPlayer)Player).CheckFailed(true), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + Position = new Vector2(256, 192), + StartTime = 1000, + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), }) + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(1000, new Vector2(256, 192), OsuAction.LeftButton), + new OsuReplayFrame(1001, new Vector2(256, 192)), + } + }); } } diff --git a/osu.Game/Tests/Visual/ModPerfectTestScene.cs b/osu.Game/Tests/Visual/ModPerfectTestScene.cs index 164faa16aa..c6f4a27ec4 100644 --- a/osu.Game/Tests/Visual/ModPerfectTestScene.cs +++ b/osu.Game/Tests/Visual/ModPerfectTestScene.cs @@ -29,12 +29,12 @@ namespace osu.Game.Tests.Visual PassCondition = () => ((PerfectModTestPlayer)Player).CheckFailed(shouldMiss && testData.FailOnMiss) }); - protected override TestPlayer CreateModPlayer(Ruleset ruleset) => new PerfectModTestPlayer(); + protected override TestPlayer CreateModPlayer(Ruleset ruleset) => new PerfectModTestPlayer(CurrentTestData, AllowFail); - private partial class PerfectModTestPlayer : TestPlayer + protected partial class PerfectModTestPlayer : ModTestPlayer { - public PerfectModTestPlayer() - : base(showResults: false) + public PerfectModTestPlayer(ModTestData data, bool allowFail) + : base(data, allowFail) { } diff --git a/osu.Game/Tests/Visual/ModTestScene.cs b/osu.Game/Tests/Visual/ModTestScene.cs index aa5b506343..c2ebcdefac 100644 --- a/osu.Game/Tests/Visual/ModTestScene.cs +++ b/osu.Game/Tests/Visual/ModTestScene.cs @@ -20,35 +20,35 @@ namespace osu.Game.Tests.Visual { protected sealed override bool HasCustomSteps => true; - private ModTestData currentTestData; + protected ModTestData CurrentTestData { get; private set; } protected void CreateModTest(ModTestData testData) => CreateTest(() => { - AddStep("set test data", () => currentTestData = testData); + AddStep("set test data", () => CurrentTestData = testData); }); public override void TearDownSteps() { AddUntilStep("test passed", () => { - if (currentTestData == null) + if (CurrentTestData == null) return true; - return currentTestData.PassCondition?.Invoke() ?? false; + return CurrentTestData.PassCondition?.Invoke() ?? false; }); base.TearDownSteps(); } - protected sealed override IBeatmap CreateBeatmap(RulesetInfo ruleset) => currentTestData?.Beatmap ?? base.CreateBeatmap(ruleset); + protected sealed override IBeatmap CreateBeatmap(RulesetInfo ruleset) => CurrentTestData?.Beatmap ?? base.CreateBeatmap(ruleset); protected sealed override TestPlayer CreatePlayer(Ruleset ruleset) { var mods = new List(SelectedMods.Value); - if (currentTestData.Mods != null) - mods.AddRange(currentTestData.Mods); - if (currentTestData.Autoplay) + if (CurrentTestData.Mods != null) + mods.AddRange(CurrentTestData.Mods); + if (CurrentTestData.Autoplay) mods.Add(ruleset.GetAutoplayMod()); SelectedMods.Value = mods; @@ -56,7 +56,7 @@ namespace osu.Game.Tests.Visual return CreateModPlayer(ruleset); } - protected virtual TestPlayer CreateModPlayer(Ruleset ruleset) => new ModTestPlayer(currentTestData, AllowFail); + protected virtual TestPlayer CreateModPlayer(Ruleset ruleset) => new ModTestPlayer(CurrentTestData, AllowFail); protected partial class ModTestPlayer : TestPlayer { From ea778c6e0aa6bf52ae5e12f287b0fb7f131348ca Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 22 Dec 2023 13:39:41 +0900 Subject: [PATCH 0628/1118] Fix perfect/sudden death not working on slider tails --- osu.Game/Rulesets/Mods/ModPerfect.cs | 4 +++- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModPerfect.cs b/osu.Game/Rulesets/Mods/ModPerfect.cs index 6f0bb7ad3b..0ba40ba070 100644 --- a/osu.Game/Rulesets/Mods/ModPerfect.cs +++ b/osu.Game/Rulesets/Mods/ModPerfect.cs @@ -28,7 +28,9 @@ namespace osu.Game.Rulesets.Mods } protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) - => result.Type.AffectsAccuracy() + => (isRelevantResult(result.Judgement.MinResult) || isRelevantResult(result.Judgement.MaxResult) || isRelevantResult(result.Type)) && result.Type != result.Judgement.MaxResult; + + private bool isRelevantResult(HitResult result) => result.AffectsAccuracy() || result.AffectsCombo(); } } diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 4e4e8662e8..56420e5ff4 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModPerfect)).ToArray(); protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) - => result.Type.AffectsCombo() + => (result.Judgement.MinResult.AffectsCombo() || result.Judgement.MaxResult.AffectsCombo() || result.Type.AffectsCombo()) && !result.IsHit; } } From 93efa98d9b8c1ce647a5baca6770f3c0c3e39755 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 22 Dec 2023 16:19:36 +0900 Subject: [PATCH 0629/1118] Fix mania "Great" hits failing with perfect mod --- .../Mods/TestSceneManiaModPerfect.cs | 23 +++++++++++++++++++ .../Mods/ManiaModPerfect.cs | 15 ++++++++++++ 2 files changed, 38 insertions(+) diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs index a734979bf6..73d6fe357d 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs @@ -30,6 +30,29 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods [TestCase(true)] public void TestHoldNote(bool shouldMiss) => CreateHitObjectTest(new HitObjectTestData(new HoldNote { StartTime = 1000, EndTime = 3000 }), shouldMiss); + [Test] + public void TestGreatHit() => CreateModTest(new ModTestData + { + Mod = new ManiaModPerfect(), + PassCondition = () => ((PerfectModTestPlayer)Player).CheckFailed(false), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Note + { + StartTime = 1000, + } + }, + }, + ReplayFrames = new List + { + new ManiaReplayFrame(1020, ManiaAction.Key1), + new ManiaReplayFrame(2000) + } + }); + [Test] public void TestBreakOnHoldNote() => CreateModTest(new ModTestData { diff --git a/osu.Game.Rulesets.Mania/Mods/ManiaModPerfect.cs b/osu.Game.Rulesets.Mania/Mods/ManiaModPerfect.cs index 2e22e23dbd..b02a18c9f4 100644 --- a/osu.Game.Rulesets.Mania/Mods/ManiaModPerfect.cs +++ b/osu.Game.Rulesets.Mania/Mods/ManiaModPerfect.cs @@ -1,11 +1,26 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; namespace osu.Game.Rulesets.Mania.Mods { public class ManiaModPerfect : ModPerfect { + protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) + { + if (!isRelevantResult(result.Judgement.MinResult) && !isRelevantResult(result.Judgement.MaxResult) && !isRelevantResult(result.Type)) + return false; + + // Mania allows imperfect "Great" hits without failing. + if (result.Judgement.MaxResult == HitResult.Perfect) + return result.Type < HitResult.Great; + + return result.Type != result.Judgement.MaxResult; + } + + private bool isRelevantResult(HitResult result) => result.AffectsAccuracy() || result.AffectsCombo(); } } From 88a5ba8167facffe4c7c401ca32ddf9f003bf4df Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 22 Dec 2023 16:43:09 +0900 Subject: [PATCH 0630/1118] Add mania/osu sudden death mod tests --- .../Mods/TestSceneCatchModPerfect.cs | 2 +- .../Mods/TestSceneManiaModPerfect.cs | 6 +- .../Mods/TestSceneManiaModSuddenDeath.cs | 72 +++++++++++++++++ .../Mods/TestSceneOsuModPerfect.cs | 4 +- .../Mods/TestSceneOsuModSuddenDeath.cs | 77 +++++++++++++++++++ .../Mods/TestSceneTaikoModPerfect.cs | 2 +- ...tScene.cs => ModFailConditionTestScene.cs} | 14 ++-- 7 files changed, 163 insertions(+), 14 deletions(-) create mode 100644 osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs rename osu.Game/Tests/Visual/{ModPerfectTestScene.cs => ModFailConditionTestScene.cs} (74%) diff --git a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs index 45e7d7aa28..7d539f91e4 100644 --- a/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs +++ b/osu.Game.Rulesets.Catch.Tests/Mods/TestSceneCatchModPerfect.cs @@ -11,7 +11,7 @@ using osuTK; namespace osu.Game.Rulesets.Catch.Tests.Mods { - public partial class TestSceneCatchModPerfect : ModPerfectTestScene + public partial class TestSceneCatchModPerfect : ModFailConditionTestScene { protected override Ruleset CreatePlayerRuleset() => new CatchRuleset(); diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs index 73d6fe357d..51730e2b43 100644 --- a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModPerfect.cs @@ -13,7 +13,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Mania.Tests.Mods { - public partial class TestSceneManiaModPerfect : ModPerfectTestScene + public partial class TestSceneManiaModPerfect : ModFailConditionTestScene { protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); @@ -34,7 +34,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods public void TestGreatHit() => CreateModTest(new ModTestData { Mod = new ManiaModPerfect(), - PassCondition = () => ((PerfectModTestPlayer)Player).CheckFailed(false), + PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(false), Autoplay = false, Beatmap = new Beatmap { @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Mania.Tests.Mods public void TestBreakOnHoldNote() => CreateModTest(new ModTestData { Mod = new ManiaModPerfect(), - PassCondition = () => ((PerfectModTestPlayer)Player).CheckFailed(true) && Player.Results.Count == 2, + PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true) && Player.Results.Count == 2, Autoplay = false, Beatmap = new Beatmap { diff --git a/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs new file mode 100644 index 0000000000..619816a815 --- /dev/null +++ b/osu.Game.Rulesets.Mania.Tests/Mods/TestSceneManiaModSuddenDeath.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Mania.Mods; +using osu.Game.Rulesets.Mania.Objects; +using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Replays; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Mania.Tests.Mods +{ + public partial class TestSceneManiaModSuddenDeath : ModFailConditionTestScene + { + protected override Ruleset CreatePlayerRuleset() => new ManiaRuleset(); + + public TestSceneManiaModSuddenDeath() + : base(new ManiaModSuddenDeath()) + { + } + + [Test] + public void TestGreatHit() => CreateModTest(new ModTestData + { + Mod = new ManiaModSuddenDeath(), + PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(false), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Note + { + StartTime = 1000, + } + }, + }, + ReplayFrames = new List + { + new ManiaReplayFrame(1020, ManiaAction.Key1), + new ManiaReplayFrame(2000) + } + }); + + [Test] + public void TestBreakOnHoldNote() => CreateModTest(new ModTestData + { + Mod = new ManiaModSuddenDeath(), + PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true) && Player.Results.Count == 2, + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new HoldNote + { + StartTime = 1000, + EndTime = 3000, + }, + }, + }, + ReplayFrames = new List + { + new ManiaReplayFrame(1000, ManiaAction.Key1), + new ManiaReplayFrame(2000) + } + }); + } +} diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs index 7030061e0e..b01bbbfca1 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModPerfect.cs @@ -15,7 +15,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Tests.Mods { - public partial class TestSceneOsuModPerfect : ModPerfectTestScene + public partial class TestSceneOsuModPerfect : ModFailConditionTestScene { protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods public void TestMissSliderTail() => CreateModTest(new ModTestData { Mod = new OsuModPerfect(), - PassCondition = () => ((PerfectModTestPlayer)Player).CheckFailed(true), + PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true), Autoplay = false, Beatmap = new Beatmap { diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs new file mode 100644 index 0000000000..ea048aaa6e --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModSuddenDeath.cs @@ -0,0 +1,77 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays; +using osu.Game.Tests.Visual; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public partial class TestSceneOsuModSuddenDeath : ModFailConditionTestScene + { + protected override Ruleset CreatePlayerRuleset() => new OsuRuleset(); + + public TestSceneOsuModSuddenDeath() + : base(new OsuModSuddenDeath()) + { + } + + [Test] + public void TestMissTail() => CreateModTest(new ModTestData + { + Mod = new OsuModSuddenDeath(), + PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(false), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + Position = new Vector2(256, 192), + StartTime = 1000, + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(100, 0), }) + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(1000, new Vector2(256, 192), OsuAction.LeftButton), + new OsuReplayFrame(1001, new Vector2(256, 192)), + } + }); + + [Test] + public void TestMissTick() => CreateModTest(new ModTestData + { + Mod = new OsuModSuddenDeath(), + PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(true), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + Position = new Vector2(256, 192), + StartTime = 1000, + Path = new SliderPath(PathType.LINEAR, new[] { Vector2.Zero, new Vector2(200, 0), }) + }, + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(1000, new Vector2(256, 192), OsuAction.LeftButton), + new OsuReplayFrame(1001, new Vector2(256, 192)), + } + }); + } +} diff --git a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs index aed08f33e0..8a1157a7f8 100644 --- a/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs +++ b/osu.Game.Rulesets.Taiko.Tests/Mods/TestSceneTaikoModPerfect.cs @@ -10,7 +10,7 @@ using osu.Game.Tests.Visual; namespace osu.Game.Rulesets.Taiko.Tests.Mods { - public partial class TestSceneTaikoModPerfect : ModPerfectTestScene + public partial class TestSceneTaikoModPerfect : ModFailConditionTestScene { protected override Ruleset CreatePlayerRuleset() => new TestTaikoRuleset(); diff --git a/osu.Game/Tests/Visual/ModPerfectTestScene.cs b/osu.Game/Tests/Visual/ModFailConditionTestScene.cs similarity index 74% rename from osu.Game/Tests/Visual/ModPerfectTestScene.cs rename to osu.Game/Tests/Visual/ModFailConditionTestScene.cs index c6f4a27ec4..8f0dff055d 100644 --- a/osu.Game/Tests/Visual/ModPerfectTestScene.cs +++ b/osu.Game/Tests/Visual/ModFailConditionTestScene.cs @@ -8,11 +8,11 @@ using osu.Game.Rulesets.Objects; namespace osu.Game.Tests.Visual { - public abstract partial class ModPerfectTestScene : ModTestScene + public abstract partial class ModFailConditionTestScene : ModTestScene { - private readonly ModPerfect mod; + private readonly ModFailCondition mod; - protected ModPerfectTestScene(ModPerfect mod) + protected ModFailConditionTestScene(ModFailCondition mod) { this.mod = mod; } @@ -26,14 +26,14 @@ namespace osu.Game.Tests.Visual HitObjects = { testData.HitObject } }, Autoplay = !shouldMiss, - PassCondition = () => ((PerfectModTestPlayer)Player).CheckFailed(shouldMiss && testData.FailOnMiss) + PassCondition = () => ((ModFailConditionTestPlayer)Player).CheckFailed(shouldMiss && testData.FailOnMiss) }); - protected override TestPlayer CreateModPlayer(Ruleset ruleset) => new PerfectModTestPlayer(CurrentTestData, AllowFail); + protected override TestPlayer CreateModPlayer(Ruleset ruleset) => new ModFailConditionTestPlayer(CurrentTestData, AllowFail); - protected partial class PerfectModTestPlayer : ModTestPlayer + protected partial class ModFailConditionTestPlayer : ModTestPlayer { - public PerfectModTestPlayer(ModTestData data, bool allowFail) + public ModFailConditionTestPlayer(ModTestData data, bool allowFail) : base(data, allowFail) { } From 5703546d715687d58b832c25082386d35abadb09 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 22 Dec 2023 16:43:17 +0900 Subject: [PATCH 0631/1118] Revert change to ModSuddenDeath --- osu.Game/Rulesets/Mods/ModSuddenDeath.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs index 56420e5ff4..4e4e8662e8 100644 --- a/osu.Game/Rulesets/Mods/ModSuddenDeath.cs +++ b/osu.Game/Rulesets/Mods/ModSuddenDeath.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mods public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModPerfect)).ToArray(); protected override bool FailCondition(HealthProcessor healthProcessor, JudgementResult result) - => (result.Judgement.MinResult.AffectsCombo() || result.Judgement.MaxResult.AffectsCombo() || result.Type.AffectsCombo()) + => result.Type.AffectsCombo() && !result.IsHit; } } From a0185508b76af5f650534d575226d27fe3fc1f21 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 22 Dec 2023 17:55:06 +0900 Subject: [PATCH 0632/1118] Add basic consideration of density for HP drain --- .../Scoring/OsuHealthProcessor.cs | 4 ++ .../Scoring/DrainingHealthProcessor.cs | 55 +++++++++++++++++-- 2 files changed, 53 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 784d3b934d..5ae5766cda 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -3,6 +3,8 @@ using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; @@ -15,6 +17,8 @@ namespace osu.Game.Rulesets.Osu.Scoring { } + protected override int? GetDensityGroup(HitObject hitObject) => (hitObject as IHasComboInformation)?.ComboIndex; + protected override double GetHealthIncreaseFor(JudgementResult result) { switch (result.Type) diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index a2fc23ac2e..4f6f8598fb 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -61,7 +61,9 @@ namespace osu.Game.Rulesets.Scoring /// protected readonly double DrainLenience; - private readonly List<(double time, double health)> healthIncreases = new List<(double, double)>(); + private readonly List healthIncreases = new List(); + private readonly Dictionary densityMultiplierByGroup = new Dictionary(); + private double gameplayEndTime; private double targetMinimumHealth; @@ -133,14 +135,33 @@ namespace osu.Game.Rulesets.Scoring { base.ApplyResultInternal(result); - if (!result.Type.IsBonus()) - healthIncreases.Add((result.HitObject.GetEndTime() + result.TimeOffset, GetHealthIncreaseFor(result))); + if (IsSimulating && !result.Type.IsBonus()) + { + healthIncreases.Add(new HealthIncrease( + result.HitObject.GetEndTime() + result.TimeOffset, + GetHealthIncreaseFor(result), + GetDensityGroup(result.HitObject))); + } } + protected override double GetHealthIncreaseFor(JudgementResult result) => base.GetHealthIncreaseFor(result) * getDensityMultiplier(GetDensityGroup(result.HitObject)); + + private double getDensityMultiplier(int? group) + { + if (group == null) + return 1; + + return densityMultiplierByGroup.TryGetValue(group.Value, out double multiplier) ? multiplier : 1; + } + + protected virtual int? GetDensityGroup(HitObject hitObject) => null; + protected override void Reset(bool storeResults) { base.Reset(storeResults); + densityMultiplierByGroup.Clear(); + if (storeResults) DrainRate = ComputeDrainRate(); @@ -152,6 +173,24 @@ namespace osu.Game.Rulesets.Scoring if (healthIncreases.Count <= 1) return 0; + // Normalise the health gain during sections with higher densities. + (int group, double avgIncrease)[] avgIncreasesByGroup = healthIncreases + .Where(i => i.Group != null) + .GroupBy(i => i.Group) + .Select(g => ((int)g.Key!, g.Sum(i => i.Amount) / (g.Max(i => i.Time) - g.Min(i => i.Time) + 1))) + .ToArray(); + + if (avgIncreasesByGroup.Length > 1) + { + double overallAverageIncrease = avgIncreasesByGroup.Average(g => g.avgIncrease); + + foreach ((int group, double avgIncrease) in avgIncreasesByGroup) + { + // Reduce the health increase for groups that return more health than average. + densityMultiplierByGroup[group] = Math.Min(1, overallAverageIncrease / avgIncrease); + } + } + int adjustment = 1; double result = 1; @@ -165,8 +204,8 @@ namespace osu.Game.Rulesets.Scoring for (int i = 0; i < healthIncreases.Count; i++) { - double currentTime = healthIncreases[i].time; - double lastTime = i > 0 ? healthIncreases[i - 1].time : DrainStartTime; + double currentTime = healthIncreases[i].Time; + double lastTime = i > 0 ? healthIncreases[i - 1].Time : DrainStartTime; while (currentBreak < Beatmap.Breaks.Count && Beatmap.Breaks[currentBreak].EndTime <= currentTime) { @@ -177,10 +216,12 @@ namespace osu.Game.Rulesets.Scoring currentBreak++; } + double multiplier = getDensityMultiplier(healthIncreases[i].Group); + // Apply health adjustments currentHealth -= (currentTime - lastTime) * result; lowestHealth = Math.Min(lowestHealth, currentHealth); - currentHealth = Math.Min(1, currentHealth + healthIncreases[i].health); + currentHealth = Math.Min(1, currentHealth + healthIncreases[i].Amount * multiplier); // Common scenario for when the drain rate is definitely too harsh if (lowestHealth < 0) @@ -198,5 +239,7 @@ namespace osu.Game.Rulesets.Scoring return result; } + + private record struct HealthIncrease(double Time, double Amount, int? Group); } } From 7e557152fb430219ab484107d592311e983094f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Dec 2023 12:40:24 +0100 Subject: [PATCH 0633/1118] Fix relax mod not considering full follow area radius when automatically holding sliders Closes https://github.com/ppy/osu/issues/25947. Regressed in https://github.com/ppy/osu/pull/25776 with the changes to `DrawableSliderBall`. I would have liked to include tests, but relax mod is a bit untestable, because it disengages completely in the presence of a replay: https://github.com/ppy/osu/blob/7e09164d7084265536570ac7036d7244934b651c/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs#L49-L58 Additionally, `RulesetInputManager` disengages completely from parent inputs when there is a replay active: https://github.com/ppy/osu/blob/7e09164d7084265536570ac7036d7244934b651c/osu.Game/Rulesets/UI/RulesetInputManager.cs#L116 which means there is really no easy way to control positional input while still having relax logic work. So I'm hoping the fix could be considered obvious enough to not require test coverage. --- osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs | 2 +- .../Objects/Drawables/SliderInputManager.cs | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs index aaa7c70a8d..40fadfb77e 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModRelax.cs @@ -88,7 +88,7 @@ namespace osu.Game.Rulesets.Osu.Mods if (!slider.HeadCircle.IsHit) handleHitCircle(slider.HeadCircle); - requiresHold |= slider.Ball.IsHovered || h.IsHovered; + requiresHold |= slider.SliderInputManager.IsMouseInFollowArea(true); break; case DrawableSpinner spinner: diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs index 8aa982783e..95896c7c91 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/SliderInputManager.cs @@ -65,7 +65,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables protected override void Update() { base.Update(); - updateTracking(isMouseInFollowArea(Tracking)); + updateTracking(IsMouseInFollowArea(Tracking)); } public void PostProcessHeadJudgement(DrawableSliderHead head) @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (!head.Judged || !head.Result.IsHit) return; - if (!isMouseInFollowArea(true)) + if (!IsMouseInFollowArea(true)) return; Debug.Assert(screenSpaceMousePosition != null); @@ -129,7 +129,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables // If all ticks were hit so far, enable tracking the full extent. // If any ticks were missed, assume tracking would've broken at some point, and should only activate if the cursor is within the slider ball. // For the second case, this may be the last chance we have to enable tracking before other objects get judged, otherwise the same would normally happen via Update(). - updateTracking(allTicksInRange || isMouseInFollowArea(false)); + updateTracking(allTicksInRange || IsMouseInFollowArea(false)); } public void TryJudgeNestedObject(DrawableOsuHitObject nestedObject, double timeOffset) @@ -174,7 +174,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables /// Whether the mouse is currently in the follow area. /// /// Whether to test against the maximum area of the follow circle. - private bool isMouseInFollowArea(bool expanded) + public bool IsMouseInFollowArea(bool expanded) { if (screenSpaceMousePosition is not Vector2 pos) return false; From 6cb82310549a73ca2657efe65c9528da8367f1ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Dec 2023 13:22:26 +0100 Subject: [PATCH 0634/1118] Add test coverage for failure case --- .../Mods/TestSceneOsuModStrictTracking.cs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs new file mode 100644 index 0000000000..726b415977 --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModStrictTracking.cs @@ -0,0 +1,53 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using NUnit.Framework; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays; +using osuTK; + +namespace osu.Game.Rulesets.Osu.Tests.Mods +{ + public partial class TestSceneOsuModStrictTracking : OsuModTestScene + { + [Test] + public void TestSliderInput() => CreateModTest(new ModTestData + { + Mod = new OsuModStrictTracking(), + Autoplay = false, + Beatmap = new Beatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 1000, + Path = new SliderPath + { + ControlPoints = + { + new PathControlPoint(), + new PathControlPoint(new Vector2(0, 100)) + } + } + } + } + }, + ReplayFrames = new List + { + new OsuReplayFrame(0, new Vector2(), OsuAction.LeftButton), + new OsuReplayFrame(500, new Vector2(200, 0), OsuAction.LeftButton), + new OsuReplayFrame(501, new Vector2(200, 0)), + new OsuReplayFrame(1000, new Vector2(), OsuAction.LeftButton), + new OsuReplayFrame(1750, new Vector2(0, 100), OsuAction.LeftButton), + new OsuReplayFrame(1751, new Vector2(0, 100)), + }, + PassCondition = () => Player.ScoreProcessor.Combo.Value == 2 + }); + } +} From 30553dc7b839600145ac57bd789b7e9992c34dfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 22 Dec 2023 13:38:07 +0100 Subject: [PATCH 0635/1118] Fix strict tracking mod forcefully missing tail before slider start time Closes https://github.com/ppy/osu/issues/25816. Regressed in https://github.com/ppy/osu/pull/25748. The reason this broke is that allowing the state of `Tracking` to change before the slider's start time to support the early hit scenario causes strict tracking to consider loss of tracking before the slider's start time as an actual miss, and thus forcefully miss the tail (see test case in 6cb82310549a73ca2657efe65c9528da8367f1ab). --- osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs index c465ab8732..2c9292c58b 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModStrictTracking.cs @@ -36,6 +36,9 @@ namespace osu.Game.Rulesets.Osu.Mods { if (e.NewValue || slider.Judged) return; + if (slider.Time.Current < slider.HitObject.StartTime) + return; + var tail = slider.NestedHitObjects.OfType().First(); if (!tail.Judged) From 01cf4ee15a8456ccc56381f138dfdf7f210de121 Mon Sep 17 00:00:00 2001 From: Simon G <45692977+jeenyuhs@users.noreply.github.com> Date: Fri, 22 Dec 2023 18:11:37 +0100 Subject: [PATCH 0636/1118] add test for length updates --- .../SongSelect/TestSceneBeatmapInfoWedge.cs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 7cd4f06bce..4e100a37dc 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -3,6 +3,7 @@ #nullable disable +using System; using System.Collections.Generic; using System.Linq; using JetBrains.Annotations; @@ -194,6 +195,36 @@ namespace osu.Game.Tests.Visual.SongSelect }); } + [TestCase] + public void TestLengthUpdates() + { + IBeatmap beatmap = createTestBeatmap(new OsuRuleset().RulesetInfo); + double drain = beatmap.CalculateDrainLength(); + beatmap.BeatmapInfo.Length = drain; + + OsuModDoubleTime doubleTime = null; + + selectBeatmap(beatmap); + checkDisplayedLength(drain); + + AddStep("select DT", () => SelectedMods.Value = new[] { doubleTime = new OsuModDoubleTime() }); + checkDisplayedLength(Math.Round(drain / 1.5f)); + + AddStep("change DT rate", () => doubleTime.SpeedChange.Value = 2); + checkDisplayedLength(Math.Round(drain / 2)); + } + + private void checkDisplayedLength(double drain) + { + var displayedLength = drain.ToFormattedDuration(); + + AddUntilStep($"check map drain ({displayedLength})", () => + { + var label = infoWedge.DisplayedContent.ChildrenOfType().Single(l => l.Statistic.Name == BeatmapsetsStrings.ShowStatsTotalLength(displayedLength)); + return label.Statistic.Content == displayedLength.ToString(); + }); + } + private void setRuleset(RulesetInfo rulesetInfo) { Container containerBefore = null; From c5893f245ce7a89d1900dbb620390823702481fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Dec 2023 14:03:45 +0900 Subject: [PATCH 0637/1118] Change legacy version checks to account for users specifying incorrect versions --- osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs | 2 +- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs index d8d86d1802..ef616ae964 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyMainCirclePiece.cs @@ -160,7 +160,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy { decimal? legacyVersion = skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value; - if (legacyVersion >= 2.0m) + if (legacyVersion > 1.0m) // legacy skins of version 2.0 and newer only apply very short fade out to the number piece. hitCircleText.FadeOut(legacy_fade_duration / 4); else diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index 68274ffa2d..b8fe2f8d06 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -67,7 +67,7 @@ namespace osu.Game.Skinning decimal? legacyVersion = skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value; - if (legacyVersion >= 2.0m) + if (legacyVersion > 1.0m) { this.MoveTo(new Vector2(0, -5)); this.MoveToOffset(new Vector2(0, 80), fade_out_delay + fade_out_length, Easing.In); From 9f34dfa2baa7e649d43ad74a444fa32df7b172e3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Dec 2023 16:25:52 +0900 Subject: [PATCH 0638/1118] Add missing using statement --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs index 4e100a37dc..fd102da026 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapInfoWedge.cs @@ -15,6 +15,7 @@ using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; +using osu.Game.Extensions; using osu.Game.Graphics.Sprites; using osu.Game.Resources.Localisation.Web; using osu.Game.Rulesets; From 8349cb7bbe4f30777f73cbf7c18f071997bd185c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Dec 2023 17:03:57 +0900 Subject: [PATCH 0639/1118] Fix hard crash when attempting to change folder location during a large import Closes https://github.com/ppy/osu/issues/26067. --- osu.Game/OsuGameBase.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 2d8024a45a..48548dc1ef 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -527,14 +527,21 @@ namespace osu.Game { ManualResetEventSlim readyToRun = new ManualResetEventSlim(); + bool success = false; + Scheduler.Add(() => { - realmBlocker = realm.BlockAllOperations("migration"); + try + { + realmBlocker = realm.BlockAllOperations("migration"); + success = true; + } + catch { } readyToRun.Set(); }, false); - if (!readyToRun.Wait(30000)) + if (!readyToRun.Wait(30000) || !success) throw new TimeoutException("Attempting to block for migration took too long."); bool? cleanupSucceded = (Storage as OsuStorage)?.Migrate(Host.GetStorage(path)); From 27a9dcc5a1a375e7d0591c90880ccb09fd8c0042 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Dec 2023 19:55:05 +0900 Subject: [PATCH 0640/1118] Add basic hotkey offset adjust support (via existing offset control) --- .../Input/Bindings/GlobalActionContainer.cs | 8 ++ .../GlobalActionKeyBindingStrings.cs | 10 +++ .../PlayerSettings/BeatmapOffsetControl.cs | 73 +++++++++++++------ 3 files changed, 68 insertions(+), 23 deletions(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 947cd5f54f..5a39c02185 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -160,6 +160,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.Enter, GlobalAction.ToggleChatFocus), new KeyBinding(InputKey.F1, GlobalAction.SaveReplay), new KeyBinding(InputKey.F2, GlobalAction.ExportReplay), + new KeyBinding(InputKey.Plus, GlobalAction.IncreaseOffset), + new KeyBinding(InputKey.Minus, GlobalAction.DecreaseOffset), }; private static IEnumerable replayKeyBindings => new[] @@ -404,6 +406,12 @@ namespace osu.Game.Input.Bindings [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.EditorToggleRotateControl))] EditorToggleRotateControl, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.IncreaseOffset))] + IncreaseOffset, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseOffset))] + DecreaseOffset } public enum GlobalActionCategory diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index 8356c480dd..ca27d0ff95 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -344,6 +344,16 @@ namespace osu.Game.Localisation /// public static LocalisableString ExportReplay => new TranslatableString(getKey(@"export_replay"), @"Export replay"); + /// + /// "Increase offset" + /// + public static LocalisableString IncreaseOffset => new TranslatableString(getKey(@"increase_offset"), @"Increase offset"); + + /// + /// "Decrease offset" + /// + public static LocalisableString DecreaseOffset => new TranslatableString(getKey(@"decrease_offset"), @"Decrease offset"); + /// /// "Toggle rotate control" /// diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 840077eb7f..b4bb35377d 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -9,6 +9,8 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Bindings; +using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -16,6 +18,7 @@ using osu.Game.Database; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; +using osu.Game.Input.Bindings; using osu.Game.Localisation; using osu.Game.Overlays.Settings; using osu.Game.Rulesets.Mods; @@ -26,7 +29,7 @@ using osuTK; namespace osu.Game.Screens.Play.PlayerSettings { - public partial class BeatmapOffsetControl : CompositeDrawable + public partial class BeatmapOffsetControl : CompositeDrawable, IKeyBindingHandler { public Bindable ReferenceScore { get; } = new Bindable(); @@ -88,28 +91,6 @@ namespace osu.Game.Screens.Play.PlayerSettings }; } - public partial class OffsetSliderBar : PlayerSliderBar - { - protected override Drawable CreateControl() => new CustomSliderBar(); - - protected partial class CustomSliderBar : SliderBar - { - public override LocalisableString TooltipText => - Current.Value == 0 - ? LocalisableString.Interpolate($@"{base.TooltipText} ms") - : LocalisableString.Interpolate($@"{base.TooltipText} ms {getEarlyLateText(Current.Value)}"); - - private LocalisableString getEarlyLateText(double value) - { - Debug.Assert(value != 0); - - return value > 0 - ? BeatmapOffsetControlStrings.HitObjectsAppearEarlier - : BeatmapOffsetControlStrings.HitObjectsAppearLater; - } - } - } - protected override void LoadComplete() { base.LoadComplete(); @@ -243,5 +224,51 @@ namespace osu.Game.Screens.Play.PlayerSettings base.Dispose(isDisposing); beatmapOffsetSubscription?.Dispose(); } + + public bool OnPressed(KeyBindingPressEvent e) + { + double amount = e.AltPressed ? 1 : 5; + + switch (e.Action) + { + case GlobalAction.IncreaseOffset: + Current.Value += amount; + + return true; + + case GlobalAction.DecreaseOffset: + Current.Value -= amount; + + return true; + } + + return false; + } + + public void OnReleased(KeyBindingReleaseEvent e) + { + } + + public partial class OffsetSliderBar : PlayerSliderBar + { + protected override Drawable CreateControl() => new CustomSliderBar(); + + protected partial class CustomSliderBar : SliderBar + { + public override LocalisableString TooltipText => + Current.Value == 0 + ? LocalisableString.Interpolate($@"{base.TooltipText} ms") + : LocalisableString.Interpolate($@"{base.TooltipText} ms {getEarlyLateText(Current.Value)}"); + + private LocalisableString getEarlyLateText(double value) + { + Debug.Assert(value != 0); + + return value > 0 + ? BeatmapOffsetControlStrings.HitObjectsAppearEarlier + : BeatmapOffsetControlStrings.HitObjectsAppearLater; + } + } + } } } From 7e9522a722fd01ca872877e1e3b4bcfeb61c8a2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Dec 2023 20:46:12 +0900 Subject: [PATCH 0641/1118] Allow external use of offset text explanation --- .../PlayerSettings/BeatmapOffsetControl.cs | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index b4bb35377d..80549343a5 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -233,12 +233,10 @@ namespace osu.Game.Screens.Play.PlayerSettings { case GlobalAction.IncreaseOffset: Current.Value += amount; - return true; case GlobalAction.DecreaseOffset: Current.Value -= amount; - return true; } @@ -249,25 +247,29 @@ namespace osu.Game.Screens.Play.PlayerSettings { } + public static LocalisableString GetOffsetExplanatoryText(double offset) + { + return offset == 0 + ? LocalisableString.Interpolate($@"{offset:0.0} ms") + : LocalisableString.Interpolate($@"{offset:0.0} ms {getEarlyLateText(offset)}"); + + LocalisableString getEarlyLateText(double value) + { + Debug.Assert(value != 0); + + return value > 0 + ? BeatmapOffsetControlStrings.HitObjectsAppearEarlier + : BeatmapOffsetControlStrings.HitObjectsAppearLater; + } + } + public partial class OffsetSliderBar : PlayerSliderBar { protected override Drawable CreateControl() => new CustomSliderBar(); protected partial class CustomSliderBar : SliderBar { - public override LocalisableString TooltipText => - Current.Value == 0 - ? LocalisableString.Interpolate($@"{base.TooltipText} ms") - : LocalisableString.Interpolate($@"{base.TooltipText} ms {getEarlyLateText(Current.Value)}"); - - private LocalisableString getEarlyLateText(double value) - { - Debug.Assert(value != 0); - - return value > 0 - ? BeatmapOffsetControlStrings.HitObjectsAppearEarlier - : BeatmapOffsetControlStrings.HitObjectsAppearLater; - } + public override LocalisableString TooltipText => GetOffsetExplanatoryText(Current.Value); } } } From 6f11885d4bedc303859964ab1415a2f1959e9bda Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Dec 2023 20:46:22 +0900 Subject: [PATCH 0642/1118] Add control to allow changing offset from gameplay --- .../Screens/Play/GameplayOffsetControl.cs | 104 ++++++++++++++++++ osu.Game/Screens/Play/Player.cs | 6 + 2 files changed, 110 insertions(+) create mode 100644 osu.Game/Screens/Play/GameplayOffsetControl.cs diff --git a/osu.Game/Screens/Play/GameplayOffsetControl.cs b/osu.Game/Screens/Play/GameplayOffsetControl.cs new file mode 100644 index 0000000000..3f5a5bef2a --- /dev/null +++ b/osu.Game/Screens/Play/GameplayOffsetControl.cs @@ -0,0 +1,104 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Threading; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Overlays; +using osu.Game.Screens.Play.PlayerSettings; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Play +{ + /// + /// This provides the ability to change the offset while in gameplay. + /// Eventually this should be replaced with all settings from PlayerLoader being accessible from the game. + /// + internal partial class GameplayOffsetControl : VisibilityContainer + { + protected override bool StartHidden => true; + + public override bool PropagateNonPositionalInputSubTree => true; + + private BeatmapOffsetControl offsetControl = null!; + + private OsuTextFlowContainer text = null!; + + private ScheduledDelegate? hideOp; + + public GameplayOffsetControl() + { + AutoSizeAxes = Axes.Y; + Width = SettingsToolboxGroup.CONTAINER_WIDTH; + + Masking = true; + CornerRadius = 5; + + // Allow BeatmapOffsetControl to handle keyboard input. + AlwaysPresent = true; + + Anchor = Anchor.CentreRight; + Origin = Anchor.CentreRight; + + X = 100; + } + + [BackgroundDependencyLoader] + private void load(OverlayColourProvider? colourProvider) + { + InternalChildren = new Drawable[] + { + new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.8f, + Colour = colourProvider?.Background4 ?? Color4.Black, + }, + new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding(10), + Spacing = new Vector2(5), + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + offsetControl = new BeatmapOffsetControl(), + text = new OsuTextFlowContainer(cp => cp.Font = OsuFont.Default.With(weight: FontWeight.SemiBold)) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + TextAnchor = Anchor.TopCentre, + } + } + }, + }; + + offsetControl.Current.BindValueChanged(val => + { + text.Text = BeatmapOffsetControl.GetOffsetExplanatoryText(val.NewValue); + Show(); + + hideOp?.Cancel(); + hideOp = Scheduler.AddDelayed(Hide, 500); + }); + } + + protected override void PopIn() + { + this.FadeIn(500, Easing.OutQuint) + .MoveToX(0, 500, Easing.OutQuint); + } + + protected override void PopOut() + { + this.FadeOut(500, Easing.InQuint) + .MoveToX(100, 500, Easing.InQuint); + } + } +} diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c9251b0a78..c960ac357f 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -461,6 +461,12 @@ namespace osu.Game.Screens.Play OnRetry = () => Restart(), OnQuit = () => PerformExit(true), }, + new GameplayOffsetControl + { + Margin = new MarginPadding(20), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + } }, }; From a2e5f624789d852a23d555e12b5dd962ad6f7712 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Dec 2023 21:07:17 +0900 Subject: [PATCH 0643/1118] Fix user profile cover showing 1px line when contracted Addresses https://github.com/ppy/osu/discussions/26068. --- osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs index 36bd8a5af5..c9e5068b2a 100644 --- a/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/TopHeaderContainer.cs @@ -232,6 +232,14 @@ namespace osu.Game.Overlays.Profile.Header bool expanded = coverToggle.CoverExpanded.Value; cover.ResizeHeightTo(expanded ? 250 : 0, transition_duration, Easing.OutQuint); + + // Without this a very tiny slither of the cover will be visible even with a size of zero. + // Integer masking woes, no doubt. + if (expanded) + cover.FadeIn(transition_duration, Easing.OutQuint); + else + cover.FadeOut(transition_duration, Easing.InQuint); + avatar.ResizeTo(new Vector2(expanded ? 120 : content_height), transition_duration, Easing.OutQuint); avatar.TransformTo(nameof(avatar.CornerRadius), expanded ? 40f : 20f, transition_duration, Easing.OutQuint); flow.TransformTo(nameof(flow.Spacing), new Vector2(expanded ? 20f : 10f), transition_duration, Easing.OutQuint); From 007ea51e202d546e813725991221436066c3db74 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Dec 2023 13:07:29 +0100 Subject: [PATCH 0644/1118] Add extra safety against returning negative total score in conversion operation --- .../StandardisedScoreMigrationTools.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 380bf63e63..5cd2a2f29c 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -321,6 +321,8 @@ namespace osu.Game.Database double modMultiplier = score.Mods.Select(m => m.ScoreMultiplier).Aggregate(1.0, (c, n) => c * n); + long convertedTotalScore; + switch (score.Ruleset.OnlineID) { case 0: @@ -417,32 +419,42 @@ namespace osu.Game.Database double newComboScoreProportion = estimatedComboPortionInStandardisedScore / maximumAchievableComboPortionInStandardisedScore; - return (long)Math.Round(( + convertedTotalScore = (long)Math.Round(( 500000 * newComboScoreProportion * score.Accuracy + 500000 * Math.Pow(score.Accuracy, 5) + bonusProportion) * modMultiplier); + break; case 1: - return (long)Math.Round(( + convertedTotalScore = (long)Math.Round(( 250000 * comboProportion + 750000 * Math.Pow(score.Accuracy, 3.6) + bonusProportion) * modMultiplier); + break; case 2: - return (long)Math.Round(( + convertedTotalScore = (long)Math.Round(( 600000 * comboProportion + 400000 * score.Accuracy + bonusProportion) * modMultiplier); + break; case 3: - return (long)Math.Round(( + convertedTotalScore = (long)Math.Round(( 850000 * comboProportion + 150000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy) + bonusProportion) * modMultiplier); + break; default: - return score.TotalScore; + convertedTotalScore = score.TotalScore; + break; } + + if (convertedTotalScore < 0) + throw new InvalidOperationException($"Total score conversion operation returned invalid total of {convertedTotalScore}"); + + return convertedTotalScore; } public static double ComputeAccuracy(ScoreInfo scoreInfo) From 15a9740eb65f39dbc3e58c55ad146e3103d483a1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Dec 2023 21:12:47 +0900 Subject: [PATCH 0645/1118] Change "cinema" mod to never fail Addresses https://github.com/ppy/osu/discussions/26032. --- osu.Game/Rulesets/Mods/ModCinema.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index ae661c5f25..a942bcc678 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -23,7 +23,7 @@ namespace osu.Game.Rulesets.Mods } } - public class ModCinema : ModAutoplay, IApplicableToHUD, IApplicableToPlayer + public class ModCinema : ModAutoplay, IApplicableToHUD, IApplicableToPlayer, IApplicableFailOverride { public override string Name => "Cinema"; public override string Acronym => "CN"; @@ -45,5 +45,9 @@ namespace osu.Game.Rulesets.Mods player.BreakOverlay.Hide(); } + + public bool PerformFail() => false; + + public bool RestartOnFail => false; } } From 644c9816739b2aadfcdb9b5e14d438a0cae81313 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 23 Dec 2023 21:21:34 +0900 Subject: [PATCH 0646/1118] Fix "spectate" button not always being clickable in online users list --- osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs index fe3151398f..37ea3f9c38 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Collections.Specialized; using System.Diagnostics; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -218,6 +219,7 @@ namespace osu.Game.Overlays.Dashboard { panel.Anchor = Anchor.TopCentre; panel.Origin = Anchor.TopCentre; + panel.CanSpectate.Value = playingUsers.Contains(user.Id); }); public partial class OnlineUserPanel : CompositeDrawable, IFilterable From 3a2ed3677b75bbbf8ec1683be7ecd8eb6e0a3e5e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Dec 2023 13:25:20 +0100 Subject: [PATCH 0647/1118] Fix standardised score conversion failing for scores set with 0.0x mod mutliplier Closes https://github.com/ppy/osu/issues/26073. --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 8 ++++++-- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 5cd2a2f29c..66c816e796 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -312,8 +312,12 @@ namespace osu.Game.Database double legacyAccScore = maximumLegacyAccuracyScore * score.Accuracy; // We can not separate the ComboScore from the BonusScore, so we keep the bonus in the ratio. - double comboProportion = - ((double)score.LegacyTotalScore - legacyAccScore) / (maximumLegacyComboScore + maximumLegacyBonusScore); + // Note that `maximumLegacyComboScore + maximumLegacyBonusScore` can actually be 0 + // when playing a beatmap with no bonus objects, with mods that have a 0.0x multiplier on stable (relax/autopilot). + // In such cases, just assume 0. + double comboProportion = maximumLegacyComboScore + maximumLegacyBonusScore > 0 + ? ((double)score.LegacyTotalScore - legacyAccScore) / (maximumLegacyComboScore + maximumLegacyBonusScore) + : 0; // We assume the bonus proportion only makes up the rest of the score that exceeds maximumLegacyBaseScore. long maximumLegacyBaseScore = maximumLegacyAccuracyScore + maximumLegacyComboScore; diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index dbd9a106df..cf0a7bd54f 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -35,9 +35,10 @@ namespace osu.Game.Scoring.Legacy /// 30000006: Fix edge cases in conversion after combo exponent introduction that lead to NaNs. Reconvert all scores. /// 30000007: Adjust osu!mania combo and accuracy portions and judgement scoring values. Reconvert all scores. /// 30000008: Add accuracy conversion. Reconvert all scores. + /// 30000009: Fix edge cases in conversion for scores which have 0.0x mod multiplier on stable. Reconvert all scores. /// /// - public const int LATEST_VERSION = 30000008; + public const int LATEST_VERSION = 30000009; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From 32d8ee2d0c30b4922ac96bb172959ea375cd2e0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Dec 2023 13:42:19 +0100 Subject: [PATCH 0648/1118] Add test coverage --- .../Online/TestSceneCurrentlyOnlineDisplay.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyOnlineDisplay.cs b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyOnlineDisplay.cs index 7687cd195d..b696c5d8ca 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneCurrentlyOnlineDisplay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneCurrentlyOnlineDisplay.cs @@ -81,6 +81,21 @@ namespace osu.Game.Tests.Visual.Online AddStep("End watching user presence", () => metadataClient.EndWatchingUserPresence()); } + [Test] + public void TestUserWasPlayingBeforeWatchingUserPresence() + { + AddStep("User began playing", () => spectatorClient.SendStartPlay(streamingUser.Id, 0)); + AddStep("Begin watching user presence", () => metadataClient.BeginWatchingUserPresence()); + AddStep("Add online user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, new UserPresence { Status = UserStatus.Online, Activity = new UserActivity.ChoosingBeatmap() })); + AddUntilStep("Panel loaded", () => currentlyOnline.ChildrenOfType().FirstOrDefault()?.User.Id == 2); + AddAssert("Spectate button enabled", () => currentlyOnline.ChildrenOfType().First().Enabled.Value, () => Is.True); + + AddStep("User finished playing", () => spectatorClient.SendEndPlay(streamingUser.Id)); + AddAssert("Spectate button disabled", () => currentlyOnline.ChildrenOfType().First().Enabled.Value, () => Is.False); + AddStep("Remove playing user", () => metadataClient.UserPresenceUpdated(streamingUser.Id, null)); + AddStep("End watching user presence", () => metadataClient.EndWatchingUserPresence()); + } + internal partial class TestUserLookupCache : UserLookupCache { private static readonly string[] usernames = From 0cbf594a8c151e3699ca15431e29d3dac96f2016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Dec 2023 15:10:31 +0100 Subject: [PATCH 0649/1118] Make cinema mod incompatible with no fail --- osu.Game/Rulesets/Mods/ModCinema.cs | 2 +- osu.Game/Rulesets/Mods/ModNoFail.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index a942bcc678..dbb37e0af6 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => OsuIcon.ModCinema; public override LocalisableString Description => "Watch the video without visual distractions."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Append(typeof(ModAutoplay)).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ModAutoplay), typeof(ModNoFail) }).ToArray(); public void ApplyToHUD(HUDOverlay overlay) { diff --git a/osu.Game/Rulesets/Mods/ModNoFail.cs b/osu.Game/Rulesets/Mods/ModNoFail.cs index 0cf81bf4c9..cc451772b2 100644 --- a/osu.Game/Rulesets/Mods/ModNoFail.cs +++ b/osu.Game/Rulesets/Mods/ModNoFail.cs @@ -19,7 +19,7 @@ namespace osu.Game.Rulesets.Mods public override ModType Type => ModType.DifficultyReduction; public override LocalisableString Description => "You can't fail, no matter what."; public override double ScoreMultiplier => 0.5; - public override Type[] IncompatibleMods => new[] { typeof(ModFailCondition) }; + public override Type[] IncompatibleMods => new[] { typeof(ModFailCondition), typeof(ModCinema) }; private readonly Bindable showHealthBar = new Bindable(); From d1000b2e6c527f170e9fd1dd525d2ba296c31f17 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 23 Dec 2023 23:36:15 +0900 Subject: [PATCH 0650/1118] remove HP density --- .../Scoring/OsuHealthProcessor.cs | 2 - .../Scoring/DrainingHealthProcessor.cs | 42 ++----------------- 2 files changed, 3 insertions(+), 41 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 5ae5766cda..7463bb565c 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Osu.Scoring { } - protected override int? GetDensityGroup(HitObject hitObject) => (hitObject as IHasComboInformation)?.ComboIndex; - protected override double GetHealthIncreaseFor(JudgementResult result) { switch (result.Type) diff --git a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs index 4f6f8598fb..629a84ea62 100644 --- a/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/DrainingHealthProcessor.cs @@ -62,7 +62,6 @@ namespace osu.Game.Rulesets.Scoring protected readonly double DrainLenience; private readonly List healthIncreases = new List(); - private readonly Dictionary densityMultiplierByGroup = new Dictionary(); private double gameplayEndTime; private double targetMinimumHealth; @@ -139,29 +138,14 @@ namespace osu.Game.Rulesets.Scoring { healthIncreases.Add(new HealthIncrease( result.HitObject.GetEndTime() + result.TimeOffset, - GetHealthIncreaseFor(result), - GetDensityGroup(result.HitObject))); + GetHealthIncreaseFor(result))); } } - protected override double GetHealthIncreaseFor(JudgementResult result) => base.GetHealthIncreaseFor(result) * getDensityMultiplier(GetDensityGroup(result.HitObject)); - - private double getDensityMultiplier(int? group) - { - if (group == null) - return 1; - - return densityMultiplierByGroup.TryGetValue(group.Value, out double multiplier) ? multiplier : 1; - } - - protected virtual int? GetDensityGroup(HitObject hitObject) => null; - protected override void Reset(bool storeResults) { base.Reset(storeResults); - densityMultiplierByGroup.Clear(); - if (storeResults) DrainRate = ComputeDrainRate(); @@ -173,24 +157,6 @@ namespace osu.Game.Rulesets.Scoring if (healthIncreases.Count <= 1) return 0; - // Normalise the health gain during sections with higher densities. - (int group, double avgIncrease)[] avgIncreasesByGroup = healthIncreases - .Where(i => i.Group != null) - .GroupBy(i => i.Group) - .Select(g => ((int)g.Key!, g.Sum(i => i.Amount) / (g.Max(i => i.Time) - g.Min(i => i.Time) + 1))) - .ToArray(); - - if (avgIncreasesByGroup.Length > 1) - { - double overallAverageIncrease = avgIncreasesByGroup.Average(g => g.avgIncrease); - - foreach ((int group, double avgIncrease) in avgIncreasesByGroup) - { - // Reduce the health increase for groups that return more health than average. - densityMultiplierByGroup[group] = Math.Min(1, overallAverageIncrease / avgIncrease); - } - } - int adjustment = 1; double result = 1; @@ -216,12 +182,10 @@ namespace osu.Game.Rulesets.Scoring currentBreak++; } - double multiplier = getDensityMultiplier(healthIncreases[i].Group); - // Apply health adjustments currentHealth -= (currentTime - lastTime) * result; lowestHealth = Math.Min(lowestHealth, currentHealth); - currentHealth = Math.Min(1, currentHealth + healthIncreases[i].Amount * multiplier); + currentHealth = Math.Min(1, currentHealth + healthIncreases[i].Amount); // Common scenario for when the drain rate is definitely too harsh if (lowestHealth < 0) @@ -240,6 +204,6 @@ namespace osu.Game.Rulesets.Scoring return result; } - private record struct HealthIncrease(double Time, double Amount, int? Group); + private record struct HealthIncrease(double Time, double Amount); } } From 00090bc527989b6caa094a40caf67023aeb352b3 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 23 Dec 2023 23:36:24 +0900 Subject: [PATCH 0651/1118] Add combo end bonus to HP --- .../Scoring/OsuHealthProcessor.cs | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 7463bb565c..672a8c91ba 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; @@ -12,12 +14,68 @@ namespace osu.Game.Rulesets.Osu.Scoring { public partial class OsuHealthProcessor : DrainingHealthProcessor { + private ComboResult currentComboResult = ComboResult.Perfect; + public OsuHealthProcessor(double drainStartTime, double drainLenience = 0) : base(drainStartTime, drainLenience) { } protected override double GetHealthIncreaseFor(JudgementResult result) + { + if (IsSimulating) + return getHealthIncreaseFor(result); + + if (result.HitObject is not IHasComboInformation combo) + return getHealthIncreaseFor(result); + + if (combo.NewCombo) + currentComboResult = ComboResult.Perfect; + + switch (result.Type) + { + case HitResult.LargeTickMiss: + case HitResult.Ok: + setComboResult(ComboResult.Good); + break; + + case HitResult.Meh: + case HitResult.Miss: + setComboResult(ComboResult.None); + break; + } + + // The tail has a special IgnoreMiss judgement + if (result.HitObject is SliderTailCircle && !result.IsHit) + setComboResult(ComboResult.Good); + + if (combo.LastInCombo && result.Type.IsHit()) + { + switch (currentComboResult) + { + case ComboResult.Perfect: + return getHealthIncreaseFor(result) + 0.07; + + case ComboResult.Good: + return getHealthIncreaseFor(result) + 0.05; + + default: + return getHealthIncreaseFor(result) + 0.03; + } + } + + return getHealthIncreaseFor(result); + + void setComboResult(ComboResult comboResult) => currentComboResult = (ComboResult)Math.Min((int)currentComboResult, (int)comboResult); + } + + protected override void Reset(bool storeResults) + { + base.Reset(storeResults); + currentComboResult = ComboResult.Perfect; + } + + private double getHealthIncreaseFor(JudgementResult result) { switch (result.Type) { From 8b11bcc6ea617bbb689b793443a90b977ae2d5e9 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 24 Dec 2023 00:08:15 +0900 Subject: [PATCH 0652/1118] Remove unused using --- osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 672a8c91ba..bd6281a7d3 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -4,7 +4,6 @@ using System; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Osu.Objects; From 7437d21f498cca28084c7ed70341e714392e0690 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sun, 24 Dec 2023 00:45:22 +0900 Subject: [PATCH 0653/1118] Adjust comment regarding slider tail --- osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index bd6281a7d3..2eb257b3e6 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -44,7 +44,7 @@ namespace osu.Game.Rulesets.Osu.Scoring break; } - // The tail has a special IgnoreMiss judgement + // The slider tail has a special judgement that can't accurately be described above. if (result.HitObject is SliderTailCircle && !result.IsHit) setComboResult(ComboResult.Good); From 92b490f2e79d850980fa9e681f7c62c1ba1d5692 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Dec 2023 01:59:48 +0900 Subject: [PATCH 0654/1118] Don't bother with alt support for now --- osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 80549343a5..80003aeaba 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -227,7 +227,10 @@ namespace osu.Game.Screens.Play.PlayerSettings public bool OnPressed(KeyBindingPressEvent e) { - double amount = e.AltPressed ? 1 : 5; + // To match stable, this should adjust by 5 ms, or 1 ms when holding alt. + // But that is hard to make work with global actions due to the operating mode. + // Let's use the more precise as a default for now. + const double amount = 1; switch (e.Action) { From 72bec527fd06714bd0b88f56ff1d7538bbf37710 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Dec 2023 02:36:27 +0900 Subject: [PATCH 0655/1118] Add conditions to match stable offset adjust limitations --- .../PlayerSettings/BeatmapOffsetControl.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 80003aeaba..b0e7d08699 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -51,6 +51,12 @@ namespace osu.Game.Screens.Play.PlayerSettings [Resolved] private OsuColour colours { get; set; } = null!; + [Resolved] + private Player? player { get; set; } + + [Resolved] + private IGameplayClock? gameplayClock { get; set; } + private double lastPlayAverage; private double lastPlayBeatmapOffset; private HitEventTimingDistributionGraph? lastPlayGraph; @@ -227,6 +233,18 @@ namespace osu.Game.Screens.Play.PlayerSettings public bool OnPressed(KeyBindingPressEvent e) { + // General limitations to ensure players don't do anything too weird. + // These match stable for now. + if (player is SubmittingPlayer) + { + // TODO: the blocking conditions should probably display a message. + if (player?.IsBreakTime.Value == false && gameplayClock?.CurrentTime - gameplayClock?.StartTime > 10000) + return false; + + if (gameplayClock?.IsPaused.Value == true) + return false; + } + // To match stable, this should adjust by 5 ms, or 1 ms when holding alt. // But that is hard to make work with global actions due to the operating mode. // Let's use the more precise as a default for now. From 686b2a4394ac735ef309976910fed859f3f3b779 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Dec 2023 03:00:51 +0900 Subject: [PATCH 0656/1118] Disable positional interaction for now --- osu.Game/Screens/Play/GameplayOffsetControl.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/GameplayOffsetControl.cs b/osu.Game/Screens/Play/GameplayOffsetControl.cs index 3f5a5bef2a..2f0cb821ec 100644 --- a/osu.Game/Screens/Play/GameplayOffsetControl.cs +++ b/osu.Game/Screens/Play/GameplayOffsetControl.cs @@ -25,6 +25,9 @@ namespace osu.Game.Screens.Play public override bool PropagateNonPositionalInputSubTree => true; + // Disable interaction for now to avoid any funny business with slider bar dragging. + public override bool PropagatePositionalInputSubTree => false; + private BeatmapOffsetControl offsetControl = null!; private OsuTextFlowContainer text = null!; From 5b03dc8d0bb1d189dc68103971fccf41bfc2a008 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 24 Dec 2023 03:20:42 +0900 Subject: [PATCH 0657/1118] Use a realm subscription to avoid overhead when hovering a toolbar button Addresses https://github.com/ppy/osu/discussions/26018. --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 26 +++++++++++----------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 08bcb6bd8a..344f7b2018 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -157,6 +157,15 @@ namespace osu.Game.Overlays.Toolbar }; } + [BackgroundDependencyLoader] + private void load() + { + if (Hotkey != null) + { + realm.SubscribeToPropertyChanged(r => r.All().FirstOrDefault(rkb => rkb.RulesetName == null && rkb.ActionInt == (int)Hotkey.Value), kb => kb.KeyCombinationString, updateKeyBindingTooltip); + } + } + protected override bool OnMouseDown(MouseDownEvent e) => false; protected override bool OnClick(ClickEvent e) @@ -168,8 +177,6 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnHover(HoverEvent e) { - updateKeyBindingTooltip(); - HoverBackground.FadeIn(200); tooltipContainer.FadeIn(100); @@ -197,19 +204,12 @@ namespace osu.Game.Overlays.Toolbar { } - private void updateKeyBindingTooltip() + private void updateKeyBindingTooltip(string keyCombination) { - if (Hotkey == null) return; + string keyBindingString = keyCombinationProvider.GetReadableString(keyCombination); - var realmKeyBinding = realm.Realm.All().FirstOrDefault(rkb => rkb.RulesetName == null && rkb.ActionInt == (int)Hotkey.Value); - - if (realmKeyBinding != null) - { - string keyBindingString = keyCombinationProvider.GetReadableString(realmKeyBinding.KeyCombination); - - if (!string.IsNullOrEmpty(keyBindingString)) - keyBindingTooltip.Text = $" ({keyBindingString})"; - } + if (!string.IsNullOrEmpty(keyBindingString)) + keyBindingTooltip.Text = $" ({keyBindingString})"; } } From 68430d6ecdbf7810285a818e4f762c07477f6de6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 23 Dec 2023 19:39:17 +0100 Subject: [PATCH 0658/1118] Fix toolbar keybinding hint not clearing after unbinding the keybinding --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 344f7b2018..81d2de5acb 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -208,8 +208,9 @@ namespace osu.Game.Overlays.Toolbar { string keyBindingString = keyCombinationProvider.GetReadableString(keyCombination); - if (!string.IsNullOrEmpty(keyBindingString)) - keyBindingTooltip.Text = $" ({keyBindingString})"; + keyBindingTooltip.Text = !string.IsNullOrEmpty(keyBindingString) + ? $" ({keyBindingString})" + : string.Empty; } } From 19d02364185b72e97c157b42f1d668c2fa794dd8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 23 Dec 2023 22:11:00 +0300 Subject: [PATCH 0659/1118] Change mod acronym --- osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs index f71acf95b8..b70d607ca1 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModDepth.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Osu.Mods public class OsuModDepth : ModWithVisibilityAdjustment, IUpdatableByPlayfield, IApplicableToDrawableRuleset { public override string Name => "Depth"; - public override string Acronym => "DH"; + public override string Acronym => "DP"; public override IconUsage? Icon => FontAwesome.Solid.Cube; public override ModType Type => ModType.Fun; public override LocalisableString Description => "3D. Almost."; From b1d994b6ffb617f12a09aca20cf0482a3b5db2b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Dec 2023 17:17:23 +0900 Subject: [PATCH 0660/1118] Add classic skin sprites for slider tick and slider end misses --- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 16 +++++++++++++--- osu.Game/Skinning/LegacySkin.cs | 13 ++++++++++--- 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index b8fe2f8d06..1834a17279 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -58,14 +58,24 @@ namespace osu.Game.Skinning if (result.IsMiss()) { + decimal? legacyVersion = skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value; + + // missed ticks / slider end don't get the normal animation. if (isMissedTick()) - applyMissedTickScaling(); - else { this.ScaleTo(1.6f); this.ScaleTo(1, 100, Easing.In); - decimal? legacyVersion = skin.GetConfig(SkinConfiguration.LegacySetting.Version)?.Value; + if (legacyVersion > 1.0m) + { + this.MoveTo(new Vector2(0, -2f)); + this.MoveToOffset(new Vector2(0, 10), fade_out_delay + fade_out_length, Easing.In); + } + } + else + { + this.ScaleTo(1.6f); + this.ScaleTo(1, 100, Easing.In); if (legacyVersion > 1.0m) { diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index a37a386889..270abe2849 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -453,11 +453,18 @@ namespace osu.Game.Skinning private Drawable? getJudgementAnimation(HitResult result) { - if (result.IsMiss()) - return this.GetAnimation("hit0", true, false); - switch (result) { + case HitResult.Miss: + return this.GetAnimation("hit0", true, false); + + case HitResult.LargeTickMiss: + return this.GetAnimation("slidertickmiss", true, false); + + case HitResult.ComboBreak: + case HitResult.IgnoreMiss: + return this.GetAnimation("sliderendmiss", true, false); + case HitResult.Meh: return this.GetAnimation("hit50", true, false); From 02c771f540489eedaf56719632f500981b20da6a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Dec 2023 17:27:15 +0900 Subject: [PATCH 0661/1118] Add warning for linux users about to report a non-bug as a bug --- .github/ISSUE_TEMPLATE/bug-issue.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index 00a873f9c8..dfdcf8d320 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -11,6 +11,10 @@ body: - Current open `priority:0` issues, filterable [here](https://github.com/ppy/osu/issues?q=is%3Aissue+is%3Aopen+sort%3Aupdated-desc+label%3Apriority%3A0). - And most importantly, search for your issue both in the [issue listing](https://github.com/ppy/osu/issues) and the [Q&A discussion listing](https://github.com/ppy/osu/discussions/categories/q-a). If you find that it already exists, respond with a reaction or add any further information that may be helpful. + # ATTENTION LINUX USERS + + If you are having an issue and it is hardware related, **please open a [q&a discussion](https://github.com/ppy/osu/discussions/categories/q-a)** instead of an issue. There's a high chance your issue is due to your system configuration, and not our software. + - type: dropdown attributes: label: Type From 8e6ea2dd9b098aa41f32d54a3c953218aee459fd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 25 Dec 2023 17:32:30 +0900 Subject: [PATCH 0662/1118] Update argon and triangles to match display style --- .../Skinning/Argon/ArgonJudgementPiece.cs | 11 ++++++++++- .../Rulesets/Judgements/DefaultJudgementPiece.cs | 15 ++++++++++++++- osu.Game/Rulesets/Scoring/HitResult.cs | 4 ++-- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs index edeece0293..bb61bd37c1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs @@ -62,7 +62,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon /// public virtual void PlayAnimation() { - if (Result.IsMiss()) + if (Result == HitResult.IgnoreMiss || Result == HitResult.LargeTickMiss) + { + this.RotateTo(-45); + this.ScaleTo(1.8f); + this.ScaleTo(1.2f, 100, Easing.In); + + this.MoveTo(Vector2.Zero); + this.MoveToOffset(new Vector2(0, 10), 800, Easing.InQuint); + } + else if (Result.IsMiss()) { this.ScaleTo(1.6f); this.ScaleTo(1, 100, Easing.In); diff --git a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs index 3c5e37f91c..ada651b60e 100644 --- a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs +++ b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs @@ -38,7 +38,20 @@ namespace osu.Game.Rulesets.Judgements /// public virtual void PlayAnimation() { - if (Result != HitResult.None && !Result.IsHit()) + // TODO: make these better. currently they are using a text `-` and it's not centered properly. + // Should be an explicit drawable. + // + // When this is done, remove the [Description] attributes from HitResults which were added for this purpose. + if (Result == HitResult.IgnoreMiss || Result == HitResult.LargeTickMiss) + { + this.RotateTo(-45); + this.ScaleTo(1.8f); + this.ScaleTo(1.2f, 100, Easing.In); + + this.MoveTo(Vector2.Zero); + this.MoveToOffset(new Vector2(0, 10), 800, Easing.InQuint); + } + else if (Result.IsMiss()) { this.ScaleTo(1.6f); this.ScaleTo(1, 100, Easing.In); diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index e174ebd00f..2d55f1a649 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -86,7 +86,7 @@ namespace osu.Game.Rulesets.Scoring /// Indicates a large tick miss. /// [EnumMember(Value = "large_tick_miss")] - [Description(@"x")] + [Description("-")] [Order(10)] LargeTickMiss, @@ -118,7 +118,7 @@ namespace osu.Game.Rulesets.Scoring /// Indicates a miss that should be ignored for scoring purposes. /// [EnumMember(Value = "ignore_miss")] - [Description("x")] + [Description("-")] [Order(13)] IgnoreMiss, From 8142a7cb7e82d7053004cd6e4b2f62e74254ed57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Dec 2023 14:00:32 +0100 Subject: [PATCH 0663/1118] Remove incorrect spec --- osu.Game/Skinning/LegacySkin.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 270abe2849..8f0cd59b68 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -461,7 +461,6 @@ namespace osu.Game.Skinning case HitResult.LargeTickMiss: return this.GetAnimation("slidertickmiss", true, false); - case HitResult.ComboBreak: case HitResult.IgnoreMiss: return this.GetAnimation("sliderendmiss", true, false); From 4fa35d709cb163a1a93bc17c6772f2b034614e91 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Dec 2023 14:56:40 +0100 Subject: [PATCH 0664/1118] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 2ec954b3a5..fbcdb00cdb 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From f84b07e71a3298a5093eab2806a74c0ef2066679 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 25 Dec 2023 19:01:06 +0100 Subject: [PATCH 0665/1118] Do not attempt to stop preview tracks when arriving from a "track completed" sync This fixes an issue identified with the WASAPI implementation in https://github.com/ppy/osu-framework/pull/6088. It has no real effect on current `master`, but fixes a deadlock that occurs with the aforementioned framework branch when one lets a preview track play out to the end - at this point all audio will stop and an attempt to perform any synchronous BASS operation (playing another track, seeking) will result in a deadlock. It isn't terribly clear as to why this is happening precisely, but there does not appear to be any need to stop and seek at that point, so this feels like a decent workaround even if the actual issue is upstream (and will unblock pushing out WASAPI support to users). --- osu.Game/Audio/PreviewTrack.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index d625566ee7..6184ff85dd 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -96,10 +96,13 @@ namespace osu.Game.Audio hasStarted = false; - Track.Stop(); + if (!Track.HasCompleted) + { + Track.Stop(); - // Ensure the track is reset immediately on stopping, so the next time it is started it has a correct time value. - Track.Seek(0); + // Ensure the track is reset immediately on stopping, so the next time it is started it has a correct time value. + Track.Seek(0); + } Stopped?.Invoke(); } From 060bf8beff4f58060e9d60f41dce5850a20748ba Mon Sep 17 00:00:00 2001 From: Nathan Tran Date: Mon, 25 Dec 2023 15:09:39 -0800 Subject: [PATCH 0666/1118] Fix rewind backtracking --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 370b559897..89911c9a69 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -643,7 +643,7 @@ namespace osu.Game.Screens.Select while (randomSelectedBeatmaps.Any()) { var beatmap = randomSelectedBeatmaps[^1]; - randomSelectedBeatmaps.Remove(beatmap); + randomSelectedBeatmaps.RemoveAt(randomSelectedBeatmaps.Count - 1); if (!beatmap.Filtered.Value && beatmap.BeatmapInfo.BeatmapSet?.DeletePending != true) { From b18b5b99773bffbd5cea8f2b536db763ae983f80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Dec 2023 12:06:56 +0900 Subject: [PATCH 0667/1118] Add inline note about deadlock --- osu.Game/Audio/PreviewTrack.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Audio/PreviewTrack.cs b/osu.Game/Audio/PreviewTrack.cs index 6184ff85dd..961990a1bd 100644 --- a/osu.Game/Audio/PreviewTrack.cs +++ b/osu.Game/Audio/PreviewTrack.cs @@ -96,6 +96,7 @@ namespace osu.Game.Audio hasStarted = false; + // This pre-check is important, fixes a BASS deadlock in some scenarios. if (!Track.HasCompleted) { Track.Stop(); From 2ec6aa7fbb92aa286f1281c4043274f6d007e98b Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Tue, 26 Dec 2023 12:46:21 +0900 Subject: [PATCH 0668/1118] Make mania scroll speed independent of hit position --- .../UI/DrawableManiaRuleset.cs | 20 ++++++++++++++++++- .../Skinning/LegacyManiaSkinConfiguration.cs | 4 +++- 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs index 9169599798..bea536e4af 100644 --- a/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs +++ b/osu.Game.Rulesets.Mania/UI/DrawableManiaRuleset.cs @@ -19,12 +19,14 @@ using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Configuration; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Replays; +using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Scoring; +using osu.Game.Skinning; namespace osu.Game.Rulesets.Mania.UI { @@ -57,6 +59,9 @@ namespace osu.Game.Rulesets.Mania.UI // Stores the current speed adjustment active in gameplay. private readonly Track speedAdjustmentTrack = new TrackVirtual(0); + [Resolved] + private ISkinSource skin { get; set; } + public DrawableManiaRuleset(Ruleset ruleset, IBeatmap beatmap, IReadOnlyList mods = null) : base(ruleset, beatmap, mods) { @@ -104,7 +109,20 @@ namespace osu.Game.Rulesets.Mania.UI updateTimeRange(); } - private void updateTimeRange() => TimeRange.Value = smoothTimeRange * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value; + private void updateTimeRange() + { + float hitPosition = skin.GetConfig( + new ManiaSkinConfigurationLookup(LegacyManiaSkinConfigurationLookups.HitPosition))?.Value + ?? Stage.HIT_TARGET_POSITION; + + const float length_to_default_hit_position = 768 - LegacyManiaSkinConfiguration.DEFAULT_HIT_POSITION; + float lengthToHitPosition = 768 - hitPosition; + + // This scaling factor preserves the scroll speed as the scroll length varies from changes to the hit position. + float scale = lengthToHitPosition / length_to_default_hit_position; + + TimeRange.Value = smoothTimeRange * speedAdjustmentTrack.AggregateTempo.Value * speedAdjustmentTrack.AggregateFrequency.Value * scale; + } /// /// Computes a scroll time (in milliseconds) from a scroll speed in the range of 1-40. diff --git a/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs b/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs index 9acb29a793..042836984a 100644 --- a/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs +++ b/osu.Game/Skinning/LegacyManiaSkinConfiguration.cs @@ -21,6 +21,8 @@ namespace osu.Game.Skinning /// public const float DEFAULT_COLUMN_SIZE = 30 * POSITION_SCALE_FACTOR; + public const float DEFAULT_HIT_POSITION = (480 - 402) * POSITION_SCALE_FACTOR; + public readonly int Keys; public Dictionary CustomColours { get; } = new Dictionary(); @@ -35,7 +37,7 @@ namespace osu.Game.Skinning public readonly float[] ExplosionWidth; public readonly float[] HoldNoteLightWidth; - public float HitPosition = (480 - 402) * POSITION_SCALE_FACTOR; + public float HitPosition = DEFAULT_HIT_POSITION; public float LightPosition = (480 - 413) * POSITION_SCALE_FACTOR; public float ScorePosition = 300 * POSITION_SCALE_FACTOR; public bool ShowJudgementLine = true; From f9e47242db508d596fe92afef5e15ab5f6583c1a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Dec 2023 17:44:49 +0900 Subject: [PATCH 0669/1118] Add visual offset to better align editor waveforms with expectations --- .../Edit/Compose/Components/Timeline/Timeline.cs | 7 ++++++- osu.Game/Screens/Edit/Editor.cs | 13 +++++++++++++ .../Edit/Timing/WaveformComparisonDisplay.cs | 2 +- 3 files changed, 20 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 75de15fe56..83d34ab61a 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -141,7 +141,12 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline waveformOpacity = config.GetBindable(OsuSetting.EditorWaveformOpacity); track.BindTo(editorClock.Track); - track.BindValueChanged(_ => waveform.Waveform = beatmap.Value.Waveform, true); + track.BindValueChanged(_ => + { + waveform.Waveform = beatmap.Value.Waveform; + waveform.RelativePositionAxes = Axes.X; + waveform.X = -(float)(Editor.WAVEFORM_VISUAL_OFFSET / beatmap.Value.Track.Length); + }, true); Zoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom); } diff --git a/osu.Game/Screens/Edit/Editor.cs b/osu.Game/Screens/Edit/Editor.cs index a6c18bdf0e..c1f6c02301 100644 --- a/osu.Game/Screens/Edit/Editor.cs +++ b/osu.Game/Screens/Edit/Editor.cs @@ -60,6 +60,19 @@ namespace osu.Game.Screens.Edit [Cached] public partial class Editor : ScreenWithBeatmapBackground, IKeyBindingHandler, IKeyBindingHandler, IBeatSnapProvider, ISamplePlaybackDisabler, IBeatSyncProvider { + /// + /// An offset applied to waveform visuals to align them with expectations. + /// + /// + /// Historically, osu! beatmaps have an assumption of full system latency baked in. + /// This comes from a culmination of stable's platform offset, average hardware playback + /// latency, and users having their universal offsets tweaked to previous beatmaps. + /// + /// Coming to this value involved running various tests with existing users / beatmaps. + /// This included both visual and audible comparisons. Ballpark confidence is ≈2 ms. + /// + public const float WAVEFORM_VISUAL_OFFSET = 20; + public override float BackgroundParallaxAmount => 0.1f; public override bool AllowBackButton => false; diff --git a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs index 856bc7c303..b5315feccb 100644 --- a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs @@ -219,7 +219,7 @@ namespace osu.Game.Screens.Edit.Timing // offset to the required beat index. double time = selectedGroupStartTime + index * timingPoint.BeatLength; - float offset = (float)(time - visible_width / 2) / trackLength * scale; + float offset = (float)(time - visible_width / 2 + Editor.WAVEFORM_VISUAL_OFFSET) / trackLength * scale; row.Alpha = time < selectedGroupStartTime || time > selectedGroupEndTime ? 0.2f : 1; row.WaveformOffsetTo(-offset, animated); From 4e3bdb2b56bd0d8430ffeeb988498d42412d72ee Mon Sep 17 00:00:00 2001 From: Nathan Tran Date: Tue, 26 Dec 2023 00:57:06 -0800 Subject: [PATCH 0670/1118] Add test coverage --- .../SongSelect/TestSceneBeatmapCarousel.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index c509d40e07..41ea347ef3 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -454,6 +454,23 @@ namespace osu.Game.Tests.Visual.SongSelect AddStep("Un-filter", () => carousel.Filter(new FilterCriteria(), false)); } + [Test] + public void TestRewind() + { + const int local_set_count = 3; + const int random_select_count = local_set_count * 3; + loadBeatmaps(setCount: local_set_count); + + for (int i = 0; i < random_select_count; i++) + nextRandom(); + + for (int i = 0; i < random_select_count; i++) + { + prevRandom(); + AddAssert("correct random last selected", () => selectedSets.Peek() == carousel.SelectedBeatmapSet); + } + } + [Test] public void TestRewindToDeletedBeatmap() { From 225528d519cf65420cf54055ff0602edc3c211d0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Dec 2023 19:20:41 +0900 Subject: [PATCH 0671/1118] Bail from score submission if audio playback rate is too far from reality Closes https://github.com/ppy/osu/issues/23149. --- .../Play/MasterGameplayClockContainer.cs | 55 +++++++++++++++++++ osu.Game/Screens/Play/SubmittingPlayer.cs | 8 +++ 2 files changed, 63 insertions(+) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 54ed7ba626..2844d84f31 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -8,6 +8,7 @@ using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Beatmaps; using osu.Game.Beatmaps.ControlPoints; @@ -39,6 +40,14 @@ namespace osu.Game.Screens.Play Precision = 0.1, }; + /// + /// Whether the audio playback is within acceptable ranges. + /// Will become false if audio playback is not going as expected. + /// + public IBindable PlaybackRateValid => playbackRateValid; + + private readonly Bindable playbackRateValid = new Bindable(true); + private readonly WorkingBeatmap beatmap; private Track track; @@ -128,6 +137,7 @@ namespace osu.Game.Screens.Play { // Safety in case the clock is seeked while stopped. LastStopTime = null; + elapsedValidationTime = null; base.Seek(time); } @@ -197,6 +207,51 @@ namespace osu.Game.Screens.Play addAdjustmentsToTrack(); } + protected override void Update() + { + base.Update(); + checkPlaybackValidity(); + } + + #region Clock validation (ensure things are running correctly for local gameplay) + + private double elapsedGameplayClockTime; + private double? elapsedValidationTime; + private int playbackDiscrepancyCount; + + private const int allowed_playback_discrepancies = 5; + + private void checkPlaybackValidity() + { + if (GameplayClock.IsRunning) + { + elapsedGameplayClockTime += GameplayClock.ElapsedFrameTime; + + elapsedValidationTime ??= elapsedGameplayClockTime; + elapsedValidationTime += GameplayClock.Rate * Time.Elapsed; + + if (Math.Abs(elapsedGameplayClockTime - elapsedValidationTime!.Value) > 300) + { + if (playbackDiscrepancyCount++ > allowed_playback_discrepancies) + { + if (playbackRateValid.Value) + { + playbackRateValid.Value = false; + Logger.Log("System audio playback is not working as expected. Some online functionality will not work.\n\nPlease check your audio drivers.", level: LogLevel.Important); + } + } + else + { + Logger.Log($"Playback discrepancy detected ({playbackDiscrepancyCount} of allowed {allowed_playback_discrepancies}): {elapsedGameplayClockTime:N1} vs {elapsedValidationTime:N1}"); + } + + elapsedValidationTime = null; + } + } + } + + #endregion + private bool speedAdjustmentsApplied; private void addAdjustmentsToTrack() diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 785164178a..f88526b8f9 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -208,6 +208,14 @@ namespace osu.Game.Screens.Play private Task submitScore(Score score) { + var masterClock = GameplayClockContainer as MasterGameplayClockContainer; + + if (masterClock?.PlaybackRateValid.Value != true) + { + Logger.Log("Score submission cancelled due to audio playback rate discrepancy."); + return Task.CompletedTask; + } + // token may be null if the request failed but gameplay was still allowed (see HandleTokenRetrievalFailure). if (token == null) { From 30b5b36f1d72f9285971287d6d78c4628c3dde94 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Dec 2023 12:19:04 +0100 Subject: [PATCH 0672/1118] Fix code quality inspection --- osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs index 41ea347ef3..aa4c879468 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneBeatmapCarousel.cs @@ -467,7 +467,7 @@ namespace osu.Game.Tests.Visual.SongSelect for (int i = 0; i < random_select_count; i++) { prevRandom(); - AddAssert("correct random last selected", () => selectedSets.Peek() == carousel.SelectedBeatmapSet); + AddAssert("correct random last selected", () => selectedSets.Peek(), () => Is.EqualTo(carousel.SelectedBeatmapSet)); } } From f2c0e7cf2ecc04d5f3f01bd945623a61a8ab7f04 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Dec 2023 20:31:34 +0900 Subject: [PATCH 0673/1118] Fix editor's control point list refreshing multiple times for a single change --- osu.Game/Screens/Edit/Timing/ControlPointList.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/ControlPointList.cs b/osu.Game/Screens/Edit/Timing/ControlPointList.cs index 22e37b9efb..7cd1dbc630 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointList.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointList.cs @@ -109,8 +109,13 @@ namespace osu.Game.Screens.Edit.Timing controlPointGroups.BindTo(Beatmap.ControlPointInfo.Groups); controlPointGroups.BindCollectionChanged((_, _) => { - table.ControlGroups = controlPointGroups; - changeHandler?.SaveState(); + // This callback can happen many times in a change operation. It gets expensive. + // We really should be handling the `CollectionChanged` event properly. + Scheduler.AddOnce(() => + { + table.ControlGroups = controlPointGroups; + changeHandler?.SaveState(); + }); }, true); table.OnRowSelected += drawable => scroll.ScrollIntoView(drawable); From 1f2f749db68fe3a541150abb7acdae7e0aabf79e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Dec 2023 20:42:04 +0900 Subject: [PATCH 0674/1118] Fix selection not being retained in control point list when undoing / redoing --- osu.Game/Screens/Edit/EditorTable.cs | 30 ++++++++++++++++++- .../Screens/Edit/Timing/ControlPointTable.cs | 14 +++++---- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index b79d71b42b..5ccb21cf59 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; @@ -46,15 +47,42 @@ namespace osu.Game.Screens.Edit }); } - protected void SetSelectedRow(object? item) + protected int GetIndexForObject(object? item) { + for (int i = 0; i < BackgroundFlow.Count; i++) + { + if (BackgroundFlow[i].Item == item) + return i; + } + + return -1; + } + + protected bool SetSelectedRow(object? item) + { + bool foundSelection = false; + foreach (var b in BackgroundFlow) { b.Selected = ReferenceEquals(b.Item, item); if (b.Selected) + { + Debug.Assert(!foundSelection); OnRowSelected?.Invoke(b); + foundSelection = true; + } } + + return foundSelection; + } + + protected object? GetObjectAtIndex(int index) + { + if (index < 0 || index > BackgroundFlow.Count - 1) + return null; + + return BackgroundFlow[index].Item; } protected override Drawable CreateHeader(int index, TableColumn? column) => new HeaderText(column?.Header ?? default); diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index b078e3fa44..335077c6f0 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -21,7 +21,7 @@ namespace osu.Game.Screens.Edit.Timing public partial class ControlPointTable : EditorTable { [Resolved] - private Bindable selectedGroup { get; set; } = null!; + private Bindable selectedGroup { get; set; } = null!; [Resolved] private EditorClock clock { get; set; } = null!; @@ -32,6 +32,8 @@ namespace osu.Game.Screens.Edit.Timing { set { + int selectedIndex = GetIndexForObject(selectedGroup.Value); + Content = null; BackgroundFlow.Clear(); @@ -53,7 +55,11 @@ namespace osu.Game.Screens.Edit.Timing Columns = createHeaders(); Content = value.Select(createContent).ToArray().ToRectangular(); - updateSelectedGroup(); + if (!SetSelectedRow(selectedGroup.Value)) + { + // Some operations completely obliterate references, so best-effort reselect based on index. + selectedGroup.Value = GetObjectAtIndex(selectedIndex) as ControlPointGroup; + } } } @@ -61,11 +67,9 @@ namespace osu.Game.Screens.Edit.Timing { base.LoadComplete(); - selectedGroup.BindValueChanged(_ => updateSelectedGroup(), true); + selectedGroup.BindValueChanged(_ => SetSelectedRow(selectedGroup.Value), true); } - private void updateSelectedGroup() => SetSelectedRow(selectedGroup.Value); - private TableColumn[] createHeaders() { var columns = new List From 03e2463b06b14b66529627fc76941d901231001c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 26 Dec 2023 21:20:18 +0900 Subject: [PATCH 0675/1118] Add test coverage and refactor to better handle equality edge case --- .../Visual/Editing/TestSceneTimingScreen.cs | 43 +++++++++++++++++++ osu.Game/Screens/Edit/EditorTable.cs | 2 +- .../Screens/Edit/Timing/ControlPointTable.cs | 29 ++++++++++--- 3 files changed, 66 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs index 216c35de65..40aadc8164 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs @@ -17,6 +17,7 @@ using osu.Game.Rulesets.Edit; using osu.Game.Screens.Edit; using osu.Game.Screens.Edit.Timing; using osu.Game.Screens.Edit.Timing.RowAttributes; +using osuTK; using osuTK.Input; namespace osu.Game.Tests.Visual.Editing @@ -69,6 +70,48 @@ namespace osu.Game.Tests.Visual.Editing AddUntilStep("Wait for rows to load", () => Child.ChildrenOfType().Any()); } + [Test] + public void TestSelectedRetainedOverUndo() + { + AddStep("Select first timing point", () => + { + InputManager.MoveMouseTo(Child.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("Selection changed", () => timingScreen.SelectedGroup.Value.Time == 2170); + AddUntilStep("Ensure seeked to correct time", () => EditorClock.CurrentTimeAccurate == 2170); + + AddStep("Adjust offset", () => + { + InputManager.MoveMouseTo(timingScreen.ChildrenOfType().First().ScreenSpaceDrawQuad.Centre + new Vector2(20, 0)); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("wait for offset changed", () => + { + return timingScreen.SelectedGroup.Value.ControlPoints.Any(c => c is TimingControlPoint) && timingScreen.SelectedGroup.Value.Time > 2170; + }); + + AddStep("simulate undo", () => + { + var clone = editorBeatmap.ControlPointInfo.DeepClone(); + + editorBeatmap.ControlPointInfo.Clear(); + + foreach (var group in clone.Groups) + { + foreach (var cp in group.ControlPoints) + editorBeatmap.ControlPointInfo.Add(group.Time, cp); + } + }); + + AddUntilStep("selection retained", () => + { + return timingScreen.SelectedGroup.Value.ControlPoints.Any(c => c is TimingControlPoint) && timingScreen.SelectedGroup.Value.Time > 2170; + }); + } + [Test] public void TestTrackingCurrentTimeWhileRunning() { diff --git a/osu.Game/Screens/Edit/EditorTable.cs b/osu.Game/Screens/Edit/EditorTable.cs index 5ccb21cf59..e5dc540b06 100644 --- a/osu.Game/Screens/Edit/EditorTable.cs +++ b/osu.Game/Screens/Edit/EditorTable.cs @@ -58,7 +58,7 @@ namespace osu.Game.Screens.Edit return -1; } - protected bool SetSelectedRow(object? item) + protected virtual bool SetSelectedRow(object? item) { bool foundSelection = false; diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index 335077c6f0..7a27056da3 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Edit.Timing { Action = () => { - selectedGroup.Value = group; + SetSelectedRow(group); clock.SeekSmoothlyTo(group.Time); } }); @@ -55,11 +55,16 @@ namespace osu.Game.Screens.Edit.Timing Columns = createHeaders(); Content = value.Select(createContent).ToArray().ToRectangular(); - if (!SetSelectedRow(selectedGroup.Value)) - { - // Some operations completely obliterate references, so best-effort reselect based on index. - selectedGroup.Value = GetObjectAtIndex(selectedIndex) as ControlPointGroup; - } + // Attempt to retain selection. + if (SetSelectedRow(selectedGroup.Value)) + return; + + // Some operations completely obliterate references, so best-effort reselect based on index. + if (SetSelectedRow(GetObjectAtIndex(selectedIndex))) + return; + + // Selection could not be retained. + selectedGroup.Value = null; } } @@ -67,7 +72,17 @@ namespace osu.Game.Screens.Edit.Timing { base.LoadComplete(); - selectedGroup.BindValueChanged(_ => SetSelectedRow(selectedGroup.Value), true); + // Handle external selections. + selectedGroup.BindValueChanged(g => SetSelectedRow(g.NewValue), true); + } + + protected override bool SetSelectedRow(object? item) + { + if (!base.SetSelectedRow(item)) + return false; + + selectedGroup.Value = item as ControlPointGroup; + return true; } private TableColumn[] createHeaders() From d70fddb6fde18de0ef092c047509ee0a2efd2d16 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 00:11:22 +0900 Subject: [PATCH 0676/1118] Fix elapsed time being counted twice on first frame --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 2844d84f31..a475f4823f 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -227,8 +227,10 @@ namespace osu.Game.Screens.Play { elapsedGameplayClockTime += GameplayClock.ElapsedFrameTime; - elapsedValidationTime ??= elapsedGameplayClockTime; - elapsedValidationTime += GameplayClock.Rate * Time.Elapsed; + if (elapsedValidationTime == null) + elapsedValidationTime = elapsedGameplayClockTime; + else + elapsedValidationTime += GameplayClock.Rate * Time.Elapsed; if (Math.Abs(elapsedGameplayClockTime - elapsedValidationTime!.Value) > 300) { From c55458e49c6b5747a1a84e00ac05787829c81a2e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 02:32:47 +0900 Subject: [PATCH 0677/1118] Remove pointless intermediary class --- .../Containers/ExpandingButtonContainer.cs | 21 ------------------- osu.Game/Overlays/Settings/SettingsSidebar.cs | 2 +- 2 files changed, 1 insertion(+), 22 deletions(-) delete mode 100644 osu.Game/Graphics/Containers/ExpandingButtonContainer.cs diff --git a/osu.Game/Graphics/Containers/ExpandingButtonContainer.cs b/osu.Game/Graphics/Containers/ExpandingButtonContainer.cs deleted file mode 100644 index 5abb4096ac..0000000000 --- a/osu.Game/Graphics/Containers/ExpandingButtonContainer.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -namespace osu.Game.Graphics.Containers -{ - /// - /// An with a long hover expansion delay. - /// - /// - /// Mostly used for buttons with explanatory labels, in which the label would display after a "long hover". - /// - public partial class ExpandingButtonContainer : ExpandingContainer - { - protected ExpandingButtonContainer(float contractedWidth, float expandedWidth) - : base(contractedWidth, expandedWidth) - { - } - - protected override double HoverExpansionDelay => 400; - } -} diff --git a/osu.Game/Overlays/Settings/SettingsSidebar.cs b/osu.Game/Overlays/Settings/SettingsSidebar.cs index 06bc2fd788..7baf9a58ff 100644 --- a/osu.Game/Overlays/Settings/SettingsSidebar.cs +++ b/osu.Game/Overlays/Settings/SettingsSidebar.cs @@ -8,7 +8,7 @@ using osu.Game.Graphics.Containers; namespace osu.Game.Overlays.Settings { - public partial class SettingsSidebar : ExpandingButtonContainer + public partial class SettingsSidebar : ExpandingContainer { public const float DEFAULT_WIDTH = 70; public const int EXPANDED_WIDTH = 200; From 58476d5429985109a13240c481da362449489167 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 02:33:02 +0900 Subject: [PATCH 0678/1118] Allow `ExpandingContainer` to not auto expand on hover --- osu.Game/Graphics/Containers/ExpandingContainer.cs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/osu.Game/Graphics/Containers/ExpandingContainer.cs b/osu.Game/Graphics/Containers/ExpandingContainer.cs index 60b9e6a167..2abdb508ae 100644 --- a/osu.Game/Graphics/Containers/ExpandingContainer.cs +++ b/osu.Game/Graphics/Containers/ExpandingContainer.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -26,6 +24,8 @@ namespace osu.Game.Graphics.Containers /// protected virtual double HoverExpansionDelay => 0; + protected virtual bool ExpandOnHover => true; + protected override Container Content => FillFlow; protected FillFlowContainer FillFlow { get; } @@ -53,7 +53,7 @@ namespace osu.Game.Graphics.Containers }; } - private ScheduledDelegate hoverExpandEvent; + private ScheduledDelegate? hoverExpandEvent; protected override void LoadComplete() { @@ -93,6 +93,9 @@ namespace osu.Game.Graphics.Containers private void updateHoverExpansion() { + if (!ExpandOnHover) + return; + hoverExpandEvent?.Cancel(); if (IsHovered && !Expanded.Value) From 9a1a97180d7938bbc1dced8d887cc226f28deb46 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 02:33:17 +0900 Subject: [PATCH 0679/1118] Change settings overlay to always show expanded buttons --- osu.Game/Overlays/Settings/SettingsSidebar.cs | 3 +++ osu.Game/Overlays/SettingsOverlay.cs | 7 +++++-- osu.Game/Overlays/SettingsPanel.cs | 1 - 3 files changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSidebar.cs b/osu.Game/Overlays/Settings/SettingsSidebar.cs index 7baf9a58ff..fc5c6b07bb 100644 --- a/osu.Game/Overlays/Settings/SettingsSidebar.cs +++ b/osu.Game/Overlays/Settings/SettingsSidebar.cs @@ -13,9 +13,12 @@ namespace osu.Game.Overlays.Settings public const float DEFAULT_WIDTH = 70; public const int EXPANDED_WIDTH = 200; + protected override bool ExpandOnHover => false; + public SettingsSidebar() : base(DEFAULT_WIDTH, EXPANDED_WIDTH) { + Expanded.Value = true; } [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 746d451343..a779c3c263 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -72,16 +72,19 @@ namespace osu.Game.Overlays switch (state.NewValue) { case Visibility.Visible: - Sidebar?.FadeColour(Color4.DarkGray, 300, Easing.OutQuint); + Sidebar.Expanded.Value = false; + Sidebar.FadeColour(Color4.DarkGray, 300, Easing.OutQuint); SectionsContainer.FadeOut(300, Easing.OutQuint); ContentContainer.MoveToX(-PANEL_WIDTH, 500, Easing.OutQuint); lastOpenedSubPanel = panel; + break; case Visibility.Hidden: - Sidebar?.FadeColour(Color4.White, 300, Easing.OutQuint); + Sidebar.Expanded.Value = true; + Sidebar.FadeColour(Color4.White, 300, Easing.OutQuint); SectionsContainer.FadeIn(500, Easing.OutQuint); ContentContainer.MoveToX(0, 500, Easing.OutQuint); diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 3bac6c400f..339120fd84 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -285,7 +285,6 @@ namespace osu.Game.Overlays return; SectionsContainer.ScrollTo(section); - Sidebar.Expanded.Value = false; }, }; } From 5de8307918216ed5f18edb42bfc8eb28c6f310b5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 02:38:23 +0900 Subject: [PATCH 0680/1118] Reduce width of sidebar buttons --- osu.Game/Overlays/Settings/SettingsSidebar.cs | 2 +- osu.Game/Overlays/Settings/SidebarIconButton.cs | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSidebar.cs b/osu.Game/Overlays/Settings/SettingsSidebar.cs index fc5c6b07bb..c751f12003 100644 --- a/osu.Game/Overlays/Settings/SettingsSidebar.cs +++ b/osu.Game/Overlays/Settings/SettingsSidebar.cs @@ -11,7 +11,7 @@ namespace osu.Game.Overlays.Settings public partial class SettingsSidebar : ExpandingContainer { public const float DEFAULT_WIDTH = 70; - public const int EXPANDED_WIDTH = 200; + public const int EXPANDED_WIDTH = 170; protected override bool ExpandOnHover => false; diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs index 4e5b361460..bd9ac3cf97 100644 --- a/osu.Game/Overlays/Settings/SidebarIconButton.cs +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -69,18 +69,18 @@ namespace osu.Game.Overlays.Settings Colour = OsuColour.Gray(0.6f), Children = new Drawable[] { - headerText = new OsuSpriteText - { - Position = new Vector2(SettingsSidebar.DEFAULT_WIDTH + 10, 0), - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - }, iconContainer = new ConstrainedIconContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(20), }, + headerText = new OsuSpriteText + { + Position = new Vector2(60, 0), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, } }, selectionIndicator = new CircularContainer From 5b1deb7c4b1139aa55543094dc652b6763ddfcd2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 02:40:50 +0900 Subject: [PATCH 0681/1118] Give buttons a touch of padding to make click effect feel better --- osu.Game/Overlays/Settings/SidebarIconButton.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs index bd9ac3cf97..a8d27eb792 100644 --- a/osu.Game/Overlays/Settings/SidebarIconButton.cs +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -60,6 +60,8 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.X; Height = 46; + Padding = new MarginPadding(5); + AddRange(new Drawable[] { textIconContent = new Container From 8e13f65c5d388ba0494367dfedcb2cccdc066b84 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 02:46:21 +0900 Subject: [PATCH 0682/1118] Adjust hover effect slightly --- osu.Game/Overlays/Settings/SidebarIconButton.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs index a8d27eb792..041d19e8bf 100644 --- a/osu.Game/Overlays/Settings/SidebarIconButton.cs +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -111,10 +111,13 @@ namespace osu.Game.Overlays.Settings private void load() { selectionIndicator.Colour = ColourProvider.Highlight1; + Hover.Colour = ColourProvider.Light4; } protected override void UpdateState() { + Hover.FadeTo(IsHovered ? 0.1f : 0, FADE_DURATION, Easing.OutQuint); + if (Selected) { textIconContent.FadeColour(ColourProvider.Content1, FADE_DURATION, Easing.OutQuint); From 5d0b5247946e758fd28f8bad58ec16cf7c15ee18 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 02:54:24 +0900 Subject: [PATCH 0683/1118] Adjust back button to match style better --- osu.Game/Overlays/Settings/SidebarButton.cs | 7 ++++++- osu.Game/Overlays/Settings/SidebarIconButton.cs | 3 +-- osu.Game/Overlays/SettingsSubPanel.cs | 13 +++++++++---- 3 files changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game/Overlays/Settings/SidebarButton.cs b/osu.Game/Overlays/Settings/SidebarButton.cs index a63688762d..f58c2f41ef 100644 --- a/osu.Game/Overlays/Settings/SidebarButton.cs +++ b/osu.Game/Overlays/Settings/SidebarButton.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Input.Events; using osu.Game.Graphics.UserInterface; @@ -23,6 +24,7 @@ namespace osu.Game.Overlays.Settings private void load() { BackgroundColour = ColourProvider.Background5; + Hover.Colour = ColourProvider.Light4; } protected override void LoadComplete() @@ -40,6 +42,9 @@ namespace osu.Game.Overlays.Settings protected override void OnHoverLost(HoverLostEvent e) => UpdateState(); - protected abstract void UpdateState(); + protected virtual void UpdateState() + { + Hover.FadeTo(IsHovered ? 0.1f : 0, FADE_DURATION, Easing.OutQuint); + } } } diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs index 041d19e8bf..1bb3aa2921 100644 --- a/osu.Game/Overlays/Settings/SidebarIconButton.cs +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -111,12 +111,11 @@ namespace osu.Game.Overlays.Settings private void load() { selectionIndicator.Colour = ColourProvider.Highlight1; - Hover.Colour = ColourProvider.Light4; } protected override void UpdateState() { - Hover.FadeTo(IsHovered ? 0.1f : 0, FADE_DURATION, Easing.OutQuint); + base.UpdateState(); if (Selected) { diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index 1651975a74..1b9edeebc2 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -47,7 +47,9 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { - Size = new Vector2(SettingsSidebar.DEFAULT_WIDTH); + Size = new Vector2(SettingsSidebar.EXPANDED_WIDTH); + + Padding = new MarginPadding(5); AddRange(new Drawable[] { @@ -61,7 +63,8 @@ namespace osu.Game.Overlays { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(15), + Y = -5, + Size = new Vector2(30), Shadow = true, Icon = FontAwesome.Solid.ChevronLeft }, @@ -69,8 +72,8 @@ namespace osu.Game.Overlays { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Y = 15, - Font = OsuFont.GetFont(size: 12, weight: FontWeight.Bold), + Y = 30, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), Text = @"back", }, } @@ -80,6 +83,8 @@ namespace osu.Game.Overlays protected override void UpdateState() { + base.UpdateState(); + content.FadeColour(IsHovered ? ColourProvider.Light1 : ColourProvider.Light3, FADE_DURATION, Easing.OutQuint); } } From c087578e011c6ade4411dbccfcfce80da407dba3 Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Tue, 26 Dec 2023 10:07:21 -0800 Subject: [PATCH 0684/1118] Force minimum cursor size for `OsuResumeOverlay` On cursor sizes below 0.3x it becomes exceedingly difficult to quickly locate and then accurately click the resume cursor on the pause overlay as it could as big as a handful of pixels. This clamps the minimum cursor size to 1x for the resume overlay, which is way more comfortable and more closely resembles stable. --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 10 +++++----- osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs | 6 ++++++ 2 files changed, 11 insertions(+), 5 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index ba9fda25e4..18351c20ce 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -70,10 +70,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor }; userCursorScale = config.GetBindable(OsuSetting.GameplayCursorSize); - userCursorScale.ValueChanged += _ => calculateCursorScale(); + userCursorScale.ValueChanged += _ => cursorScale.Value = CalculateCursorScale(); autoCursorScale = config.GetBindable(OsuSetting.AutoCursorSize); - autoCursorScale.ValueChanged += _ => calculateCursorScale(); + autoCursorScale.ValueChanged += _ => cursorScale.Value = CalculateCursorScale(); cursorScale.BindValueChanged(e => cursorScaleContainer.Scale = new Vector2(e.NewValue), true); } @@ -81,10 +81,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor protected override void LoadComplete() { base.LoadComplete(); - calculateCursorScale(); + cursorScale.Value = CalculateCursorScale(); } - private void calculateCursorScale() + protected virtual float CalculateCursorScale() { float scale = userCursorScale.Value; @@ -94,7 +94,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor scale *= GetScaleForCircleSize(state.Beatmap.Difficulty.CircleSize); } - cursorScale.Value = scale; + return scale; } protected override void SkinChanged(ISkinSource skin) diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs index ea49836772..f5e83f46f2 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs @@ -71,6 +71,12 @@ namespace osu.Game.Rulesets.Osu.UI RelativePositionAxes = Axes.Both; } + protected override float CalculateCursorScale() + { + // Force minimum cursor size so it's easily clickable + return Math.Max(1f, base.CalculateCursorScale()); + } + protected override bool OnHover(HoverEvent e) { updateColour(); From c107bfcd3112cc8b1589d84f4634be1688cc684d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Dec 2023 20:09:34 +0100 Subject: [PATCH 0685/1118] Fix `TestSceneSideOverlays` test failure --- osu.Game/Overlays/Settings/SettingsSidebar.cs | 4 ++-- osu.Game/Overlays/Settings/SidebarIconButton.cs | 2 +- osu.Game/Overlays/SettingsPanel.cs | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/SettingsSidebar.cs b/osu.Game/Overlays/Settings/SettingsSidebar.cs index c751f12003..302c52fbd2 100644 --- a/osu.Game/Overlays/Settings/SettingsSidebar.cs +++ b/osu.Game/Overlays/Settings/SettingsSidebar.cs @@ -10,13 +10,13 @@ namespace osu.Game.Overlays.Settings { public partial class SettingsSidebar : ExpandingContainer { - public const float DEFAULT_WIDTH = 70; + public const float CONTRACTED_WIDTH = 70; public const int EXPANDED_WIDTH = 170; protected override bool ExpandOnHover => false; public SettingsSidebar() - : base(DEFAULT_WIDTH, EXPANDED_WIDTH) + : base(CONTRACTED_WIDTH, EXPANDED_WIDTH) { Expanded.Value = true; } diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs index 1bb3aa2921..e7ae4cc81d 100644 --- a/osu.Game/Overlays/Settings/SidebarIconButton.cs +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -66,7 +66,7 @@ namespace osu.Game.Overlays.Settings { textIconContent = new Container { - Width = SettingsSidebar.DEFAULT_WIDTH, + Width = SettingsSidebar.CONTRACTED_WIDTH, RelativeSizeAxes = Axes.Y, Colour = OsuColour.Gray(0.6f), Children = new Drawable[] diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 339120fd84..f4dfc7fa27 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -33,7 +33,7 @@ namespace osu.Game.Overlays public const float TRANSITION_LENGTH = 600; - private const float sidebar_width = SettingsSidebar.DEFAULT_WIDTH; + private const float sidebar_width = SettingsSidebar.EXPANDED_WIDTH; /// /// The width of the settings panel content, excluding the sidebar. From 9ac79782d25f3625d79d1d9096cb3eecacbdad21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Dec 2023 20:21:15 +0100 Subject: [PATCH 0686/1118] Add visibility toggle step to settings panel test scene --- osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 5d9c2f890c..8c4ca47fc3 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -41,6 +41,7 @@ namespace osu.Game.Tests.Visual.Settings public void TestBasic() { AddStep("do nothing", () => { }); + AddToggleStep("toggle visibility", visible => settings.State.Value = visible ? Visibility.Visible : Visibility.Hidden); } [Test] From af47f9fd701897a3096c785a5c2e31c2b87ee6f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Dec 2023 20:24:43 +0100 Subject: [PATCH 0687/1118] Fix sidebar button text becoming masked away during fadeout --- osu.Game/Overlays/Settings/SidebarIconButton.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Settings/SidebarIconButton.cs b/osu.Game/Overlays/Settings/SidebarIconButton.cs index e7ae4cc81d..f4b71207e3 100644 --- a/osu.Game/Overlays/Settings/SidebarIconButton.cs +++ b/osu.Game/Overlays/Settings/SidebarIconButton.cs @@ -66,16 +66,16 @@ namespace osu.Game.Overlays.Settings { textIconContent = new Container { - Width = SettingsSidebar.CONTRACTED_WIDTH, - RelativeSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(0.6f), Children = new Drawable[] { iconContainer = new ConstrainedIconContainer { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, Size = new Vector2(20), + Margin = new MarginPadding { Left = 25 } }, headerText = new OsuSpriteText { From 6f672b8cb302fb7aa5034b07a2b6f0f351df54aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 26 Dec 2023 20:36:12 +0100 Subject: [PATCH 0688/1118] Fix `TestSceneKeyBindingPanel` failures --- .../Visual/Settings/TestSceneKeyBindingPanel.cs | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs index 1c4e89e1a2..57c9770c9a 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneKeyBindingPanel.cs @@ -13,7 +13,6 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; using osu.Game.Overlays; -using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Rulesets.Taiko; using osuTK.Input; @@ -152,7 +151,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("click first row with two bindings", () => { multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); - InputManager.MoveMouseTo(multiBindingRow); + InputManager.MoveMouseTo(multiBindingRow.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); @@ -256,7 +255,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("click first row with two bindings", () => { multiBindingRow = panel.ChildrenOfType().First(row => row.Defaults.Count() > 1); - InputManager.MoveMouseTo(multiBindingRow); + InputManager.MoveMouseTo(multiBindingRow.ChildrenOfType().First()); InputManager.Click(MouseButton.Left); }); @@ -305,7 +304,6 @@ namespace osu.Game.Tests.Visual.Settings section.ChildrenOfType().Single().TriggerClick(); }); AddStep("move mouse to centre", () => InputManager.MoveMouseTo(panel.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for collapsed", () => panel.ChildrenOfType().Single().Expanded.Value, () => Is.False); scrollToAndStartBinding("Left (rim)"); AddStep("attempt to bind M1 to two keys", () => InputManager.Click(MouseButton.Left)); @@ -325,7 +323,6 @@ namespace osu.Game.Tests.Visual.Settings section.ChildrenOfType().Single().TriggerClick(); }); AddStep("move mouse to centre", () => InputManager.MoveMouseTo(panel.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for collapsed", () => panel.ChildrenOfType().Single().Expanded.Value, () => Is.False); scrollToAndStartBinding("Left (rim)"); AddStep("attempt to bind M1 to two keys", () => InputManager.Click(MouseButton.Left)); @@ -345,7 +342,6 @@ namespace osu.Game.Tests.Visual.Settings section.ChildrenOfType().Single().TriggerClick(); }); AddStep("move mouse to centre", () => InputManager.MoveMouseTo(panel.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for collapsed", () => panel.ChildrenOfType().Single().Expanded.Value, () => Is.False); scrollToAndStartBinding("Left (centre)"); AddStep("clear binding", () => { @@ -377,7 +373,6 @@ namespace osu.Game.Tests.Visual.Settings section.ChildrenOfType().Single().TriggerClick(); }); AddStep("move mouse to centre", () => InputManager.MoveMouseTo(panel.ScreenSpaceDrawQuad.Centre)); - AddUntilStep("wait for collapsed", () => panel.ChildrenOfType().Single().Expanded.Value, () => Is.False); scrollToAndStartBinding("Left (centre)"); AddStep("clear binding", () => { From 8cd240fbecebf7bd37ba2cc504fe751c5afe727a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 13:20:19 +0900 Subject: [PATCH 0689/1118] Reduce size and fix alignment of back button --- osu.Game/Overlays/SettingsSubPanel.cs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index 1b9edeebc2..4d1f8f45cc 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -37,7 +37,7 @@ namespace osu.Game.Overlays public partial class BackButton : SidebarButton { - private Container content; + private Drawable content; public BackButton() : base(HoverSampleSet.Default) @@ -49,30 +49,31 @@ namespace osu.Game.Overlays { Size = new Vector2(SettingsSidebar.EXPANDED_WIDTH); - Padding = new MarginPadding(5); + Padding = new MarginPadding(40); AddRange(new Drawable[] { - content = new Container + content = new FillFlowContainer { Anchor = Anchor.Centre, Origin = Anchor.Centre, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5), Children = new Drawable[] { new SpriteIcon { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = -5, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Size = new Vector2(30), Shadow = true, Icon = FontAwesome.Solid.ChevronLeft }, new OsuSpriteText { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Y = 30, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), Text = @"back", }, From 901674a1303a349185d5123f013a3d2e3d2d4fad Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 13:39:13 +0900 Subject: [PATCH 0690/1118] Move back button inside sidebar to fix weird animation --- .../Visual/Settings/TestSceneSettingsPanel.cs | 2 +- osu.Game/Overlays/Settings/SettingsSidebar.cs | 79 ++++++++++++++++++- osu.Game/Overlays/SettingsOverlay.cs | 2 +- osu.Game/Overlays/SettingsPanel.cs | 13 +-- osu.Game/Overlays/SettingsSubPanel.cs | 71 ----------------- 5 files changed, 87 insertions(+), 80 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs index 8c4ca47fc3..df0fc8de57 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsPanel.cs @@ -111,7 +111,7 @@ namespace osu.Game.Tests.Visual.Settings AddStep("Press back", () => settings .ChildrenOfType().FirstOrDefault()? - .ChildrenOfType().FirstOrDefault()?.TriggerClick()); + .ChildrenOfType().FirstOrDefault()?.TriggerClick()); AddUntilStep("top-level textbox focused", () => settings.SectionsContainer.ChildrenOfType().FirstOrDefault()?.HasFocus == true); } diff --git a/osu.Game/Overlays/Settings/SettingsSidebar.cs b/osu.Game/Overlays/Settings/SettingsSidebar.cs index 302c52fbd2..ddbcd60ef6 100644 --- a/osu.Game/Overlays/Settings/SettingsSidebar.cs +++ b/osu.Game/Overlays/Settings/SettingsSidebar.cs @@ -1,10 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; namespace osu.Game.Overlays.Settings { @@ -13,11 +20,16 @@ namespace osu.Game.Overlays.Settings public const float CONTRACTED_WIDTH = 70; public const int EXPANDED_WIDTH = 170; + public Action? BackButtonAction; + protected override bool ExpandOnHover => false; - public SettingsSidebar() + private readonly bool showBackButton; + + public SettingsSidebar(bool showBackButton) : base(CONTRACTED_WIDTH, EXPANDED_WIDTH) { + this.showBackButton = showBackButton; Expanded.Value = true; } @@ -30,6 +42,71 @@ namespace osu.Game.Overlays.Settings RelativeSizeAxes = Axes.Both, Depth = float.MaxValue }); + + if (showBackButton) + { + AddInternal(new BackButton + { + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Action = () => BackButtonAction?.Invoke(), + }); + } + } + + public partial class BackButton : SidebarButton + { + private Drawable content = null!; + + public BackButton() + : base(HoverSampleSet.Default) + { + } + + [BackgroundDependencyLoader] + private void load() + { + Size = new Vector2(SettingsSidebar.EXPANDED_WIDTH); + + Padding = new MarginPadding(40); + + AddRange(new[] + { + content = new FillFlowContainer + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Direction = FillDirection.Vertical, + AutoSizeAxes = Axes.Both, + Spacing = new Vector2(5), + Children = new Drawable[] + { + new SpriteIcon + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Size = new Vector2(30), + Shadow = true, + Icon = FontAwesome.Solid.ChevronLeft + }, + new OsuSpriteText + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), + Text = @"back", + }, + } + } + }); + } + + protected override void UpdateState() + { + base.UpdateState(); + + content.FadeColour(IsHovered ? ColourProvider.Light1 : ColourProvider.Light3, FADE_DURATION, Easing.OutQuint); + } } } } diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index a779c3c263..5735b1515a 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -49,7 +49,7 @@ namespace osu.Game.Overlays protected override Drawable CreateFooter() => new SettingsFooter(); public SettingsOverlay() - : base(true) + : base(false) { } diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index f4dfc7fa27..3861c5abc7 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -59,7 +59,7 @@ namespace osu.Game.Overlays protected override string PopInSampleName => "UI/settings-pop-in"; protected override double PopInOutSampleBalance => -OsuGameBase.SFX_STEREO_STRENGTH; - private readonly bool showSidebar; + private readonly bool showBackButton; private LoadingLayer loading; @@ -72,9 +72,9 @@ namespace osu.Game.Overlays [Cached] private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); - protected SettingsPanel(bool showSidebar) + protected SettingsPanel(bool showBackButton) { - this.showSidebar = showSidebar; + this.showBackButton = showBackButton; RelativeSizeAxes = Axes.Y; AutoSizeAxes = Axes.X; } @@ -146,10 +146,11 @@ namespace osu.Game.Overlays } }); - if (showSidebar) + AddInternal(Sidebar = new SettingsSidebar(showBackButton) { - AddInternal(Sidebar = new SettingsSidebar { Width = sidebar_width }); - } + BackButtonAction = Hide, + Width = sidebar_width + }); CreateSections()?.ForEach(AddSection); } diff --git a/osu.Game/Overlays/SettingsSubPanel.cs b/osu.Game/Overlays/SettingsSubPanel.cs index 4d1f8f45cc..440639f06b 100644 --- a/osu.Game/Overlays/SettingsSubPanel.cs +++ b/osu.Game/Overlays/SettingsSubPanel.cs @@ -1,17 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using osu.Framework.Allocation; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Game.Graphics; -using osu.Game.Graphics.Sprites; -using osu.Game.Graphics.UserInterface; -using osu.Game.Overlays.Settings; -using osuTK; namespace osu.Game.Overlays { @@ -25,69 +15,8 @@ namespace osu.Game.Overlays [BackgroundDependencyLoader] private void load() { - AddInternal(new BackButton - { - Anchor = Anchor.BottomLeft, - Origin = Anchor.BottomLeft, - Action = Hide - }); } protected override bool DimMainContent => false; // dimming is handled by main overlay - - public partial class BackButton : SidebarButton - { - private Drawable content; - - public BackButton() - : base(HoverSampleSet.Default) - { - } - - [BackgroundDependencyLoader] - private void load() - { - Size = new Vector2(SettingsSidebar.EXPANDED_WIDTH); - - Padding = new MarginPadding(40); - - AddRange(new Drawable[] - { - content = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Direction = FillDirection.Vertical, - AutoSizeAxes = Axes.Both, - Spacing = new Vector2(5), - Children = new Drawable[] - { - new SpriteIcon - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Size = new Vector2(30), - Shadow = true, - Icon = FontAwesome.Solid.ChevronLeft - }, - new OsuSpriteText - { - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular), - Text = @"back", - }, - } - } - }); - } - - protected override void UpdateState() - { - base.UpdateState(); - - content.FadeColour(IsHovered ? ColourProvider.Light1 : ColourProvider.Light3, FADE_DURATION, Easing.OutQuint); - } - } } } From 81ba46216f0951e30329c3e2779a5380ee25e743 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 13:49:00 +0900 Subject: [PATCH 0691/1118] Speed up fades in transition to avoid ugliness --- osu.Game/Overlays/SettingsPanel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/SettingsPanel.cs b/osu.Game/Overlays/SettingsPanel.cs index 3861c5abc7..748673035b 100644 --- a/osu.Game/Overlays/SettingsPanel.cs +++ b/osu.Game/Overlays/SettingsPanel.cs @@ -181,7 +181,7 @@ namespace osu.Game.Overlays Scheduler.AddDelayed(loadSections, TRANSITION_LENGTH / 3); Sidebar?.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); - this.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); + this.FadeTo(1, TRANSITION_LENGTH / 2, Easing.OutQuint); searchTextBox.TakeFocus(); searchTextBox.HoldFocus = true; @@ -197,7 +197,7 @@ namespace osu.Game.Overlays ContentContainer.MoveToX(-WIDTH + ExpandedPosition, TRANSITION_LENGTH, Easing.OutQuint); Sidebar?.MoveToX(-sidebar_width, TRANSITION_LENGTH, Easing.OutQuint); - this.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); + this.FadeTo(0, TRANSITION_LENGTH / 2, Easing.OutQuint); searchTextBox.HoldFocus = false; if (searchTextBox.HasFocus) From 768e10d55f5e3cc144856b47906b03187f162e13 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 13:50:47 +0900 Subject: [PATCH 0692/1118] Adjust `NotificationOverlay` fades to match --- osu.Game/Overlays/NotificationOverlay.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index c3ddb228ea..f56d09f2b2 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -227,7 +227,7 @@ namespace osu.Game.Overlays protected override void PopIn() { this.MoveToX(0, TRANSITION_LENGTH, Easing.OutQuint); - mainContent.FadeTo(1, TRANSITION_LENGTH, Easing.OutQuint); + mainContent.FadeTo(1, TRANSITION_LENGTH / 2, Easing.OutQuint); mainContent.FadeEdgeEffectTo(WaveContainer.SHADOW_OPACITY, WaveContainer.APPEAR_DURATION, Easing.Out); toastTray.FlushAllToasts(); @@ -240,7 +240,7 @@ namespace osu.Game.Overlays markAllRead(); this.MoveToX(WIDTH, TRANSITION_LENGTH, Easing.OutQuint); - mainContent.FadeTo(0, TRANSITION_LENGTH, Easing.OutQuint); + mainContent.FadeTo(0, TRANSITION_LENGTH / 2, Easing.OutQuint); mainContent.FadeEdgeEffectTo(0, WaveContainer.DISAPPEAR_DURATION, Easing.In); } From 8f7e0571f0693cfd8fefcc41f41a70239db1f21e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 10:51:05 +0100 Subject: [PATCH 0693/1118] Add failing test coverage of handling out-of-bounds catch objects --- .../TestSceneOutOfBoundsObjects.cs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 osu.Game.Rulesets.Catch.Tests/TestSceneOutOfBoundsObjects.cs diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneOutOfBoundsObjects.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneOutOfBoundsObjects.cs new file mode 100644 index 0000000000..951f5d1ca1 --- /dev/null +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneOutOfBoundsObjects.cs @@ -0,0 +1,72 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Collections.Generic; +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Catch.Objects; +using osu.Game.Rulesets.Catch.Objects.Drawables; +using osu.Game.Rulesets.Catch.UI; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Types; +using osuTK; + +namespace osu.Game.Rulesets.Catch.Tests +{ + public partial class TestSceneOutOfBoundsObjects : TestSceneCatchPlayer + { + protected override bool Autoplay => true; + + [Test] + public void TestNoOutOfBoundsObjects() + { + bool anyObjectOutOfBounds = false; + + AddStep("reset flag", () => anyObjectOutOfBounds = false); + + AddUntilStep("check for out-of-bounds objects", + () => + { + anyObjectOutOfBounds |= Player.ChildrenOfType().Any(dho => dho.X < 0 || dho.X > CatchPlayfield.WIDTH); + return Player.ScoreProcessor.HasCompleted.Value; + }); + + AddAssert("no out of bound objects found", () => !anyObjectOutOfBounds); + } + + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) => new Beatmap + { + BeatmapInfo = new BeatmapInfo + { + Ruleset = ruleset, + }, + HitObjects = new List + { + new Fruit { StartTime = 1000, X = -50 }, + new Fruit { StartTime = 1200, X = CatchPlayfield.WIDTH + 50 }, + new JuiceStream + { + StartTime = 1500, + X = 10, + Path = new SliderPath(PathType.LINEAR, new[] + { + Vector2.Zero, + new Vector2(-200, 0) + }) + }, + new JuiceStream + { + StartTime = 3000, + X = CatchPlayfield.WIDTH - 10, + Path = new SliderPath(PathType.LINEAR, new[] + { + Vector2.Zero, + new Vector2(200, 0) + }) + }, + } + }; + } +} From 2e8b49b93a7ec12ace3ea9e3f0713f8ba20545e7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 10:55:29 +0100 Subject: [PATCH 0694/1118] Fix catch drawable objects not being clamped to playfield bounds --- .../Objects/Drawables/DrawablePalpableCatchHitObject.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs index 4a9661f108..ade00918ab 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawablePalpableCatchHitObject.cs @@ -1,10 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.Catch.UI; using osuTK; using osuTK.Graphics; @@ -70,7 +72,10 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables private void updateXPosition(ValueChangedEvent _) { - X = OriginalXBindable.Value + XOffsetBindable.Value; + // same as `CatchHitObject.EffectiveX`. + // not using that property directly to support scenarios where `HitObject` may not necessarily be present + // for this pooled drawable. + X = Math.Clamp(OriginalXBindable.Value + XOffsetBindable.Value, 0, CatchPlayfield.WIDTH); } protected override void OnApply() From 1233533fb967390c2c433ea0f7feb0dadd682635 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 22:14:15 +0900 Subject: [PATCH 0695/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index cf01f2f99b..b179b8b837 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index c7b9d02b26..7e03ab50e2 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 14b37db3dd17d3be07a9dce5b088b59205d33815 Mon Sep 17 00:00:00 2001 From: Gabriel Del Nero <43073074+Gabixel@users.noreply.github.com> Date: Wed, 27 Dec 2023 14:35:19 +0100 Subject: [PATCH 0696/1118] Make `ModDaycore` sequential for `ModHalfTime` --- osu.Game/Overlays/Mods/Input/ClassicModHotkeyHandler.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/Input/ClassicModHotkeyHandler.cs b/osu.Game/Overlays/Mods/Input/ClassicModHotkeyHandler.cs index 59a631a7b5..bf58efc339 100644 --- a/osu.Game/Overlays/Mods/Input/ClassicModHotkeyHandler.cs +++ b/osu.Game/Overlays/Mods/Input/ClassicModHotkeyHandler.cs @@ -20,7 +20,7 @@ namespace osu.Game.Overlays.Mods.Input { [Key.Q] = new[] { typeof(ModEasy) }, [Key.W] = new[] { typeof(ModNoFail) }, - [Key.E] = new[] { typeof(ModHalfTime) }, + [Key.E] = new[] { typeof(ModHalfTime), typeof(ModDaycore) }, [Key.A] = new[] { typeof(ModHardRock) }, [Key.S] = new[] { typeof(ModSuddenDeath), typeof(ModPerfect) }, [Key.D] = new[] { typeof(ModDoubleTime), typeof(ModNightcore) }, From 13333f75756e587572411971524a65cda09eedab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 15:02:19 +0100 Subject: [PATCH 0697/1118] Make room for `OsuIcon` to accept new icons --- osu.Game/Graphics/OsuIcon.cs | 132 ++++++++++++++++++----------------- 1 file changed, 68 insertions(+), 64 deletions(-) diff --git a/osu.Game/Graphics/OsuIcon.cs b/osu.Game/Graphics/OsuIcon.cs index 15af8f000b..d1ad818300 100644 --- a/osu.Game/Graphics/OsuIcon.cs +++ b/osu.Game/Graphics/OsuIcon.cs @@ -7,90 +7,94 @@ namespace osu.Game.Graphics { public static class OsuIcon { - public static IconUsage Get(int icon) => new IconUsage((char)icon, "osuFont"); + #region Legacy spritesheet-based icons + + private static IconUsage get(int icon) => new IconUsage((char)icon, @"osuFont"); // ruleset icons in circles - public static IconUsage RulesetOsu => Get(0xe000); - public static IconUsage RulesetMania => Get(0xe001); - public static IconUsage RulesetCatch => Get(0xe002); - public static IconUsage RulesetTaiko => Get(0xe003); + public static IconUsage RulesetOsu => get(0xe000); + public static IconUsage RulesetMania => get(0xe001); + public static IconUsage RulesetCatch => get(0xe002); + public static IconUsage RulesetTaiko => get(0xe003); // ruleset icons without circles - public static IconUsage FilledCircle => Get(0xe004); - public static IconUsage CrossCircle => Get(0xe005); - public static IconUsage Logo => Get(0xe006); - public static IconUsage ChevronDownCircle => Get(0xe007); - public static IconUsage EditCircle => Get(0xe033); - public static IconUsage LeftCircle => Get(0xe034); - public static IconUsage RightCircle => Get(0xe035); - public static IconUsage Charts => Get(0xe036); - public static IconUsage Solo => Get(0xe037); - public static IconUsage Multi => Get(0xe038); - public static IconUsage Gear => Get(0xe039); + public static IconUsage FilledCircle => get(0xe004); + public static IconUsage CrossCircle => get(0xe005); + public static IconUsage Logo => get(0xe006); + public static IconUsage ChevronDownCircle => get(0xe007); + public static IconUsage EditCircle => get(0xe033); + public static IconUsage LeftCircle => get(0xe034); + public static IconUsage RightCircle => get(0xe035); + public static IconUsage Charts => get(0xe036); + public static IconUsage Solo => get(0xe037); + public static IconUsage Multi => get(0xe038); + public static IconUsage Gear => get(0xe039); // misc icons - public static IconUsage Bat => Get(0xe008); - public static IconUsage Bubble => Get(0xe009); - public static IconUsage BubblePop => Get(0xe02e); - public static IconUsage Dice => Get(0xe011); - public static IconUsage Heart => Get(0xe02f); - public static IconUsage HeartBreak => Get(0xe030); - public static IconUsage Hot => Get(0xe031); - public static IconUsage ListSearch => Get(0xe032); + public static IconUsage Bat => get(0xe008); + public static IconUsage Bubble => get(0xe009); + public static IconUsage BubblePop => get(0xe02e); + public static IconUsage Dice => get(0xe011); + public static IconUsage Heart => get(0xe02f); + public static IconUsage HeartBreak => get(0xe030); + public static IconUsage Hot => get(0xe031); + public static IconUsage ListSearch => get(0xe032); //osu! playstyles - public static IconUsage PlayStyleTablet => Get(0xe02a); - public static IconUsage PlayStyleMouse => Get(0xe029); - public static IconUsage PlayStyleKeyboard => Get(0xe02b); - public static IconUsage PlayStyleTouch => Get(0xe02c); + public static IconUsage PlayStyleTablet => get(0xe02a); + public static IconUsage PlayStyleMouse => get(0xe029); + public static IconUsage PlayStyleKeyboard => get(0xe02b); + public static IconUsage PlayStyleTouch => get(0xe02c); // osu! difficulties - public static IconUsage EasyOsu => Get(0xe015); - public static IconUsage NormalOsu => Get(0xe016); - public static IconUsage HardOsu => Get(0xe017); - public static IconUsage InsaneOsu => Get(0xe018); - public static IconUsage ExpertOsu => Get(0xe019); + public static IconUsage EasyOsu => get(0xe015); + public static IconUsage NormalOsu => get(0xe016); + public static IconUsage HardOsu => get(0xe017); + public static IconUsage InsaneOsu => get(0xe018); + public static IconUsage ExpertOsu => get(0xe019); // taiko difficulties - public static IconUsage EasyTaiko => Get(0xe01a); - public static IconUsage NormalTaiko => Get(0xe01b); - public static IconUsage HardTaiko => Get(0xe01c); - public static IconUsage InsaneTaiko => Get(0xe01d); - public static IconUsage ExpertTaiko => Get(0xe01e); + public static IconUsage EasyTaiko => get(0xe01a); + public static IconUsage NormalTaiko => get(0xe01b); + public static IconUsage HardTaiko => get(0xe01c); + public static IconUsage InsaneTaiko => get(0xe01d); + public static IconUsage ExpertTaiko => get(0xe01e); // fruits difficulties - public static IconUsage EasyFruits => Get(0xe01f); - public static IconUsage NormalFruits => Get(0xe020); - public static IconUsage HardFruits => Get(0xe021); - public static IconUsage InsaneFruits => Get(0xe022); - public static IconUsage ExpertFruits => Get(0xe023); + public static IconUsage EasyFruits => get(0xe01f); + public static IconUsage NormalFruits => get(0xe020); + public static IconUsage HardFruits => get(0xe021); + public static IconUsage InsaneFruits => get(0xe022); + public static IconUsage ExpertFruits => get(0xe023); // mania difficulties - public static IconUsage EasyMania => Get(0xe024); - public static IconUsage NormalMania => Get(0xe025); - public static IconUsage HardMania => Get(0xe026); - public static IconUsage InsaneMania => Get(0xe027); - public static IconUsage ExpertMania => Get(0xe028); + public static IconUsage EasyMania => get(0xe024); + public static IconUsage NormalMania => get(0xe025); + public static IconUsage HardMania => get(0xe026); + public static IconUsage InsaneMania => get(0xe027); + public static IconUsage ExpertMania => get(0xe028); // mod icons - public static IconUsage ModPerfect => Get(0xe049); - public static IconUsage ModAutopilot => Get(0xe03a); - public static IconUsage ModAuto => Get(0xe03b); - public static IconUsage ModCinema => Get(0xe03c); - public static IconUsage ModDoubleTime => Get(0xe03d); - public static IconUsage ModEasy => Get(0xe03e); - public static IconUsage ModFlashlight => Get(0xe03f); - public static IconUsage ModHalftime => Get(0xe040); - public static IconUsage ModHardRock => Get(0xe041); - public static IconUsage ModHidden => Get(0xe042); - public static IconUsage ModNightcore => Get(0xe043); - public static IconUsage ModNoFail => Get(0xe044); - public static IconUsage ModRelax => Get(0xe045); - public static IconUsage ModSpunOut => Get(0xe046); - public static IconUsage ModSuddenDeath => Get(0xe047); - public static IconUsage ModTarget => Get(0xe048); + public static IconUsage ModPerfect => get(0xe049); + public static IconUsage ModAutopilot => get(0xe03a); + public static IconUsage ModAuto => get(0xe03b); + public static IconUsage ModCinema => get(0xe03c); + public static IconUsage ModDoubleTime => get(0xe03d); + public static IconUsage ModEasy => get(0xe03e); + public static IconUsage ModFlashlight => get(0xe03f); + public static IconUsage ModHalftime => get(0xe040); + public static IconUsage ModHardRock => get(0xe041); + public static IconUsage ModHidden => get(0xe042); + public static IconUsage ModNightcore => get(0xe043); + public static IconUsage ModNoFail => get(0xe044); + public static IconUsage ModRelax => get(0xe045); + public static IconUsage ModSpunOut => get(0xe046); + public static IconUsage ModSuddenDeath => get(0xe047); + public static IconUsage ModTarget => get(0xe048); // Use "Icons/BeatmapDetails/mod-icon" instead // public static IconUsage ModBg => Get(0xe04a); + + #endregion } } From c9de6a383d71829eb3ec23c4a7571b72480459c1 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 27 Dec 2023 16:21:54 +0100 Subject: [PATCH 0698/1118] Add settings checkbox for `MinimiseOnFocusLossInFullscreen` Shown only on desktop platforms in fullscreen. "alt-tab" keyword also matches "alt" and "tab". --- osu.Game/Localisation/GraphicsSettingsStrings.cs | 5 +++++ .../Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 8 ++++++++ 2 files changed, 13 insertions(+) diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index 422704514f..fc29352ae2 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -155,6 +155,11 @@ namespace osu.Game.Localisation public static LocalisableString ChangeRendererConfirmation => new TranslatableString(getKey(@"change_renderer_configuration"), @"In order to change the renderer, the game will close. Please open it again."); + /// + /// "Minimise on focus loss" + /// + public static LocalisableString MinimiseOnFocusLoss => new TranslatableString(getKey(@"minimise_on_focus_loss"), @"Minimise on focus loss"); + private static string getKey(string key) => $"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index a3290bc81c..adb572e245 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -51,6 +51,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics private SettingsDropdown resolutionDropdown = null!; private SettingsDropdown displayDropdown = null!; private SettingsDropdown windowModeDropdown = null!; + private SettingsCheckbox minimiseOnFocusLossCheckbox = null!; private SettingsCheckbox safeAreaConsiderationsCheckbox = null!; private Bindable scalingPositionX = null!; @@ -106,6 +107,12 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics ItemSource = resolutions, Current = sizeFullscreen }, + minimiseOnFocusLossCheckbox = new SettingsCheckbox + { + LabelText = GraphicsSettingsStrings.MinimiseOnFocusLoss, + Current = config.GetBindable(FrameworkSetting.MinimiseOnFocusLossInFullscreen), + Keywords = new[] { "alt-tab" }, + }, safeAreaConsiderationsCheckbox = new SettingsCheckbox { LabelText = "Shrink game to avoid cameras and notches", @@ -255,6 +262,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { resolutionDropdown.CanBeShown.Value = resolutions.Count > 1 && windowModeDropdown.Current.Value == WindowMode.Fullscreen; displayDropdown.CanBeShown.Value = displayDropdown.Items.Count() > 1; + minimiseOnFocusLossCheckbox.CanBeShown.Value = RuntimeInfo.IsDesktop && windowModeDropdown.Current.Value == WindowMode.Fullscreen; safeAreaConsiderationsCheckbox.CanBeShown.Value = host.Window?.SafeAreaPadding.Value.Total != Vector2.Zero; } From 0fde9cd6ae676c3bef39cd34182fbe0cfe338526 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 27 Dec 2023 16:40:52 +0100 Subject: [PATCH 0699/1118] Override confine mouse mode only when clicking outside the window would minimise it --- osu.Game/Input/ConfineMouseTracker.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Input/ConfineMouseTracker.cs b/osu.Game/Input/ConfineMouseTracker.cs index de8660dbce..926f68df45 100644 --- a/osu.Game/Input/ConfineMouseTracker.cs +++ b/osu.Game/Input/ConfineMouseTracker.cs @@ -22,6 +22,7 @@ namespace osu.Game.Input { private Bindable frameworkConfineMode; private Bindable frameworkWindowMode; + private Bindable frameworkMinimiseOnFocusLossInFullscreen; private Bindable osuConfineMode; private IBindable localUserPlaying; @@ -31,7 +32,9 @@ namespace osu.Game.Input { frameworkConfineMode = frameworkConfigManager.GetBindable(FrameworkSetting.ConfineMouseMode); frameworkWindowMode = frameworkConfigManager.GetBindable(FrameworkSetting.WindowMode); + frameworkMinimiseOnFocusLossInFullscreen = frameworkConfigManager.GetBindable(FrameworkSetting.MinimiseOnFocusLossInFullscreen); frameworkWindowMode.BindValueChanged(_ => updateConfineMode()); + frameworkMinimiseOnFocusLossInFullscreen.BindValueChanged(_ => updateConfineMode()); osuConfineMode = osuConfigManager.GetBindable(OsuSetting.ConfineMouseMode); localUserPlaying = localUserInfo.IsPlaying.GetBoundCopy(); @@ -46,7 +49,8 @@ namespace osu.Game.Input if (frameworkConfineMode.Disabled) return; - if (frameworkWindowMode.Value == WindowMode.Fullscreen) + // override confine mode only when clicking outside the window minimises it. + if (frameworkWindowMode.Value == WindowMode.Fullscreen && frameworkMinimiseOnFocusLossInFullscreen.Value) { frameworkConfineMode.Value = ConfineMouseMode.Fullscreen; return; From a6bed04e50b6bcc7845ca85157bb2c6672c991d5 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Wed, 27 Dec 2023 16:54:01 +0100 Subject: [PATCH 0700/1118] Update confine mode settings checkbox to match the new `ConfineMouseTracker` logic Not too happy with the duplicated logic, but settings can't depend on `ConfineMouseTracker` for testability reasons. --- .../Settings/Sections/Input/MouseSettings.cs | 38 +++++++++++-------- 1 file changed, 23 insertions(+), 15 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs index 6bf06f4f98..7805ed5834 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/MouseSettings.cs @@ -28,6 +28,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input private Bindable localSensitivity; private Bindable windowMode; + private Bindable minimiseOnFocusLoss; private SettingsEnumDropdown confineMouseModeSetting; private Bindable relativeMode; @@ -47,6 +48,7 @@ namespace osu.Game.Overlays.Settings.Sections.Input relativeMode = mouseHandler.UseRelativeMode.GetBoundCopy(); windowMode = config.GetBindable(FrameworkSetting.WindowMode); + minimiseOnFocusLoss = config.GetBindable(FrameworkSetting.MinimiseOnFocusLossInFullscreen); Children = new Drawable[] { @@ -98,21 +100,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input localSensitivity.BindValueChanged(val => handlerSensitivity.Value = val.NewValue); - windowMode.BindValueChanged(mode => - { - bool isFullscreen = mode.NewValue == WindowMode.Fullscreen; - - if (isFullscreen) - { - confineMouseModeSetting.Current.Disabled = true; - confineMouseModeSetting.TooltipText = MouseSettingsStrings.NotApplicableFullscreen; - } - else - { - confineMouseModeSetting.Current.Disabled = false; - confineMouseModeSetting.TooltipText = string.Empty; - } - }, true); + windowMode.BindValueChanged(_ => updateConfineMouseModeSettingVisibility()); + minimiseOnFocusLoss.BindValueChanged(_ => updateConfineMouseModeSettingVisibility(), true); highPrecisionMouse.Current.BindValueChanged(highPrecision => { @@ -126,6 +115,25 @@ namespace osu.Game.Overlays.Settings.Sections.Input }, true); } + /// + /// Updates disabled state and tooltip of to match when is overriding the confine mode. + /// + private void updateConfineMouseModeSettingVisibility() + { + bool confineModeOverriden = windowMode.Value == WindowMode.Fullscreen && minimiseOnFocusLoss.Value; + + if (confineModeOverriden) + { + confineMouseModeSetting.Current.Disabled = true; + confineMouseModeSetting.TooltipText = MouseSettingsStrings.NotApplicableFullscreen; + } + else + { + confineMouseModeSetting.Current.Disabled = false; + confineMouseModeSetting.TooltipText = string.Empty; + } + } + public partial class SensitivitySetting : SettingsSlider { public SensitivitySetting() From 45143a6c17e4574aab8a910a7be27e568f91c34a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 15:17:39 +0100 Subject: [PATCH 0701/1118] Implement new icon store --- osu.Game/Graphics/OsuIcon.cs | 348 ++++++++++++++++++++++++++++++++++- osu.Game/OsuGameBase.cs | 1 + 2 files changed, 347 insertions(+), 2 deletions(-) diff --git a/osu.Game/Graphics/OsuIcon.cs b/osu.Game/Graphics/OsuIcon.cs index d1ad818300..3cd10b1315 100644 --- a/osu.Game/Graphics/OsuIcon.cs +++ b/osu.Game/Graphics/OsuIcon.cs @@ -1,7 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using osu.Framework.Extensions; using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Text; namespace osu.Game.Graphics { @@ -19,7 +28,6 @@ namespace osu.Game.Graphics // ruleset icons without circles public static IconUsage FilledCircle => get(0xe004); - public static IconUsage CrossCircle => get(0xe005); public static IconUsage Logo => get(0xe006); public static IconUsage ChevronDownCircle => get(0xe007); public static IconUsage EditCircle => get(0xe033); @@ -35,7 +43,6 @@ namespace osu.Game.Graphics public static IconUsage Bubble => get(0xe009); public static IconUsage BubblePop => get(0xe02e); public static IconUsage Dice => get(0xe011); - public static IconUsage Heart => get(0xe02f); public static IconUsage HeartBreak => get(0xe030); public static IconUsage Hot => get(0xe031); public static IconUsage ListSearch => get(0xe032); @@ -96,5 +103,342 @@ namespace osu.Game.Graphics // public static IconUsage ModBg => Get(0xe04a); #endregion + + #region New single-file-based icons + + public const string FONT_NAME = @"Icons"; + + public static IconUsage Audio => get(OsuIconMapping.Audio); + public static IconUsage Beatmap => get(OsuIconMapping.Beatmap); + public static IconUsage Calendar => get(OsuIconMapping.Calendar); + public static IconUsage ChangelogA => get(OsuIconMapping.ChangelogA); + public static IconUsage ChangelogB => get(OsuIconMapping.ChangelogB); + public static IconUsage Chat => get(OsuIconMapping.Chat); + public static IconUsage CheckCircle => get(OsuIconMapping.CheckCircle); + public static IconUsage CollapseA => get(OsuIconMapping.CollapseA); + public static IconUsage Collections => get(OsuIconMapping.Collections); + public static IconUsage Cross => get(OsuIconMapping.Cross); + public static IconUsage CrossCircle => get(OsuIconMapping.CrossCircle); + public static IconUsage Crown => get(OsuIconMapping.Crown); + public static IconUsage Debug => get(OsuIconMapping.Debug); + public static IconUsage Delete => get(OsuIconMapping.Delete); + public static IconUsage Details => get(OsuIconMapping.Details); + public static IconUsage Discord => get(OsuIconMapping.Discord); + public static IconUsage EllipsisHorizontal => get(OsuIconMapping.EllipsisHorizontal); + public static IconUsage EllipsisVertical => get(OsuIconMapping.EllipsisVertical); + public static IconUsage ExpandA => get(OsuIconMapping.ExpandA); + public static IconUsage ExpandB => get(OsuIconMapping.ExpandB); + public static IconUsage FeaturedArtist => get(OsuIconMapping.FeaturedArtist); + public static IconUsage FeaturedArtistCircle => get(OsuIconMapping.FeaturedArtistCircle); + public static IconUsage GameplayA => get(OsuIconMapping.GameplayA); + public static IconUsage GameplayB => get(OsuIconMapping.GameplayB); + public static IconUsage GameplayC => get(OsuIconMapping.GameplayC); + public static IconUsage Global => get(OsuIconMapping.Global); + public static IconUsage Graphics => get(OsuIconMapping.Graphics); + public static IconUsage Heart => get(OsuIconMapping.Heart); + public static IconUsage Home => get(OsuIconMapping.Home); + public static IconUsage Input => get(OsuIconMapping.Input); + public static IconUsage Maintenance => get(OsuIconMapping.Maintenance); + public static IconUsage Megaphone => get(OsuIconMapping.Megaphone); + public static IconUsage Music => get(OsuIconMapping.Music); + public static IconUsage News => get(OsuIconMapping.News); + public static IconUsage Next => get(OsuIconMapping.Next); + public static IconUsage NextCircle => get(OsuIconMapping.NextCircle); + public static IconUsage Notification => get(OsuIconMapping.Notification); + public static IconUsage Online => get(OsuIconMapping.Online); + public static IconUsage Play => get(OsuIconMapping.Play); + public static IconUsage Player => get(OsuIconMapping.Player); + public static IconUsage PlayerFollow => get(OsuIconMapping.PlayerFollow); + public static IconUsage Prev => get(OsuIconMapping.Prev); + public static IconUsage PrevCircle => get(OsuIconMapping.PrevCircle); + public static IconUsage Ranking => get(OsuIconMapping.Ranking); + public static IconUsage Rulesets => get(OsuIconMapping.Rulesets); + public static IconUsage Search => get(OsuIconMapping.Search); + public static IconUsage Settings => get(OsuIconMapping.Settings); + public static IconUsage SkinA => get(OsuIconMapping.SkinA); + public static IconUsage SkinB => get(OsuIconMapping.SkinB); + public static IconUsage Star => get(OsuIconMapping.Star); + public static IconUsage Storyboard => get(OsuIconMapping.Storyboard); + public static IconUsage Team => get(OsuIconMapping.Team); + public static IconUsage ThumbsUp => get(OsuIconMapping.ThumbsUp); + public static IconUsage Tournament => get(OsuIconMapping.Tournament); + public static IconUsage Twitter => get(OsuIconMapping.Twitter); + public static IconUsage UserInterface => get(OsuIconMapping.UserInterface); + public static IconUsage Wiki => get(OsuIconMapping.Wiki); + public static IconUsage EditorAddControlPoint => get(OsuIconMapping.EditorAddControlPoint); + public static IconUsage EditorConvertToStream => get(OsuIconMapping.EditorConvertToStream); + public static IconUsage EditorDistanceSnap => get(OsuIconMapping.EditorDistanceSnap); + public static IconUsage EditorFinish => get(OsuIconMapping.EditorFinish); + public static IconUsage EditorGridSnap => get(OsuIconMapping.EditorGridSnap); + public static IconUsage EditorNewComboA => get(OsuIconMapping.EditorNewComboA); + public static IconUsage EditorNewComboB => get(OsuIconMapping.EditorNewComboB); + public static IconUsage EditorSelect => get(OsuIconMapping.EditorSelect); + public static IconUsage EditorSound => get(OsuIconMapping.EditorSound); + public static IconUsage EditorWhistle => get(OsuIconMapping.EditorWhistle); + + private static IconUsage get(OsuIconMapping glyph) => new IconUsage((char)glyph, FONT_NAME); + + private enum OsuIconMapping + { + [Description(@"audio")] + Audio, + + [Description(@"beatmap")] + Beatmap, + + [Description(@"calendar")] + Calendar, + + [Description(@"changelog-a")] + ChangelogA, + + [Description(@"changelog-b")] + ChangelogB, + + [Description(@"chat")] + Chat, + + [Description(@"check-circle")] + CheckCircle, + + [Description(@"collapse-a")] + CollapseA, + + [Description(@"collections")] + Collections, + + [Description(@"cross")] + Cross, + + [Description(@"cross-circle")] + CrossCircle, + + [Description(@"crown")] + Crown, + + [Description(@"debug")] + Debug, + + [Description(@"delete")] + Delete, + + [Description(@"details")] + Details, + + [Description(@"discord")] + Discord, + + [Description(@"ellipsis-horizontal")] + EllipsisHorizontal, + + [Description(@"ellipsis-vertical")] + EllipsisVertical, + + [Description(@"expand-a")] + ExpandA, + + [Description(@"expand-b")] + ExpandB, + + [Description(@"featured-artist")] + FeaturedArtist, + + [Description(@"featured-artist-circle")] + FeaturedArtistCircle, + + [Description(@"gameplay-a")] + GameplayA, + + [Description(@"gameplay-b")] + GameplayB, + + [Description(@"gameplay-c")] + GameplayC, + + [Description(@"global")] + Global, + + [Description(@"graphics")] + Graphics, + + [Description(@"heart")] + Heart, + + [Description(@"home")] + Home, + + [Description(@"input")] + Input, + + [Description(@"maintenance")] + Maintenance, + + [Description(@"megaphone")] + Megaphone, + + [Description(@"music")] + Music, + + [Description(@"news")] + News, + + [Description(@"next")] + Next, + + [Description(@"next-circle")] + NextCircle, + + [Description(@"notification")] + Notification, + + [Description(@"online")] + Online, + + [Description(@"play")] + Play, + + [Description(@"player")] + Player, + + [Description(@"player-follow")] + PlayerFollow, + + [Description(@"prev")] + Prev, + + [Description(@"prev-circle")] + PrevCircle, + + [Description(@"ranking")] + Ranking, + + [Description(@"rulesets")] + Rulesets, + + [Description(@"search")] + Search, + + [Description(@"settings")] + Settings, + + [Description(@"skin-a")] + SkinA, + + [Description(@"skin-b")] + SkinB, + + [Description(@"star")] + Star, + + [Description(@"storyboard")] + Storyboard, + + [Description(@"team")] + Team, + + [Description(@"thumbs-up")] + ThumbsUp, + + [Description(@"tournament")] + Tournament, + + [Description(@"twitter")] + Twitter, + + [Description(@"user-interface")] + UserInterface, + + [Description(@"wiki")] + Wiki, + + [Description(@"Editor/add-control-point")] + EditorAddControlPoint = 1000, + + [Description(@"Editor/convert-to-stream")] + EditorConvertToStream, + + [Description(@"Editor/distance-snap")] + EditorDistanceSnap, + + [Description(@"Editor/finish")] + EditorFinish, + + [Description(@"Editor/grid-snap")] + EditorGridSnap, + + [Description(@"Editor/new-combo-a")] + EditorNewComboA, + + [Description(@"Editor/new-combo-b")] + EditorNewComboB, + + [Description(@"Editor/select")] + EditorSelect, + + [Description(@"Editor/sound")] + EditorSound, + + [Description(@"Editor/whistle")] + EditorWhistle, + } + + public class OsuIconStore : ITextureStore, ITexturedGlyphLookupStore + { + private readonly TextureStore textures; + + public OsuIconStore(TextureStore textures) + { + this.textures = textures; + } + + public ITexturedCharacterGlyph? Get(string? fontName, char character) + { + if (fontName == FONT_NAME) + return new Glyph(textures.Get($@"{fontName}/{((OsuIconMapping)character).GetDescription()}")); + + return null; + } + + public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); + + public Texture? Get(string name, WrapMode wrapModeS, WrapMode wrapModeT) => null; + + public Texture Get(string name) => throw new NotImplementedException(); + + public Task GetAsync(string name, CancellationToken cancellationToken = default) => throw new NotImplementedException(); + + public Stream GetStream(string name) => throw new NotImplementedException(); + + public IEnumerable GetAvailableResources() => throw new NotImplementedException(); + + public Task GetAsync(string name, WrapMode wrapModeS, WrapMode wrapModeT, CancellationToken cancellationToken = default) => throw new NotImplementedException(); + + public class Glyph : ITexturedCharacterGlyph + { + public float XOffset => default; + public float YOffset => default; + public float XAdvance => default; + public float Baseline => default; + public char Character => default; + + public float GetKerning(T lastGlyph) where T : ICharacterGlyph => throw new NotImplementedException(); + + public Texture Texture { get; } + public float Width => Texture.Width; + public float Height => Texture.Height; + + public Glyph(Texture texture) + { + Texture = texture; + } + } + + public void Dispose() + { + textures.Dispose(); + } + } + + #endregion } } diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 48548dc1ef..b4ad21f045 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -479,6 +479,7 @@ namespace osu.Game AddFont(Resources, @"Fonts/Venera/Venera-Black"); Fonts.AddStore(new HexaconsIcons.HexaconsStore(Textures)); + Fonts.AddStore(new OsuIcon.OsuIconStore(Textures)); } protected override IReadOnlyDependencyContainer CreateChildDependencies(IReadOnlyDependencyContainer parent) => From 69baabee627f55d87f6ba61f361e1b370bbfdd2a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 15:35:03 +0100 Subject: [PATCH 0702/1118] Replace hexacons in toolbar with new icons --- osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs | 2 +- osu.Game/Overlays/Changelog/ChangelogHeader.cs | 2 +- osu.Game/Overlays/ChatOverlay.cs | 2 +- osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs | 2 +- osu.Game/Overlays/News/NewsHeader.cs | 2 +- osu.Game/Overlays/NotificationOverlay.cs | 2 +- osu.Game/Overlays/NowPlayingOverlay.cs | 2 +- osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs | 2 +- osu.Game/Overlays/SettingsOverlay.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs | 2 +- osu.Game/Overlays/Wiki/WikiHeader.cs | 2 +- 11 files changed, 11 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs index 27fab82bf3..075dfd02b0 100644 --- a/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs +++ b/osu.Game/Overlays/BeatmapListing/BeatmapListingHeader.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.BeatmapListing { Title = PageTitleStrings.MainBeatmapsetsControllerIndex; Description = NamedOverlayComponentStrings.BeatmapListingDescription; - Icon = HexaconsIcons.Beatmap; + Icon = OsuIcon.Beatmap; } } } diff --git a/osu.Game/Overlays/Changelog/ChangelogHeader.cs b/osu.Game/Overlays/Changelog/ChangelogHeader.cs index 61ea9dc4db..f738d70370 100644 --- a/osu.Game/Overlays/Changelog/ChangelogHeader.cs +++ b/osu.Game/Overlays/Changelog/ChangelogHeader.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Changelog { Title = PageTitleStrings.MainChangelogControllerDefault; Description = NamedOverlayComponentStrings.ChangelogDescription; - Icon = HexaconsIcons.Devtools; + Icon = OsuIcon.ChangelogB; } } } diff --git a/osu.Game/Overlays/ChatOverlay.cs b/osu.Game/Overlays/ChatOverlay.cs index 4aa4c59471..8f3b7031c2 100644 --- a/osu.Game/Overlays/ChatOverlay.cs +++ b/osu.Game/Overlays/ChatOverlay.cs @@ -31,7 +31,7 @@ namespace osu.Game.Overlays { public partial class ChatOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent, IKeyBindingHandler { - public IconUsage Icon => HexaconsIcons.Messaging; + public IconUsage Icon => OsuIcon.Chat; public LocalisableString Title => ChatStrings.HeaderTitle; public LocalisableString Description => ChatStrings.HeaderDescription; diff --git a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs index 104f0943dc..8fd8f6b332 100644 --- a/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs +++ b/osu.Game/Overlays/Dashboard/DashboardOverlayHeader.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays.Dashboard { Title = PageTitleStrings.MainHomeControllerIndex; Description = NamedOverlayComponentStrings.DashboardDescription; - Icon = HexaconsIcons.Social; + Icon = OsuIcon.Global; } } } diff --git a/osu.Game/Overlays/News/NewsHeader.cs b/osu.Game/Overlays/News/NewsHeader.cs index f237ed66f2..92d71a21ef 100644 --- a/osu.Game/Overlays/News/NewsHeader.cs +++ b/osu.Game/Overlays/News/NewsHeader.cs @@ -69,7 +69,7 @@ namespace osu.Game.Overlays.News { Title = PageTitleStrings.MainNewsControllerDefault; Description = NamedOverlayComponentStrings.NewsDescription; - Icon = HexaconsIcons.News; + Icon = OsuIcon.News; } } } diff --git a/osu.Game/Overlays/NotificationOverlay.cs b/osu.Game/Overlays/NotificationOverlay.cs index f56d09f2b2..18a487a312 100644 --- a/osu.Game/Overlays/NotificationOverlay.cs +++ b/osu.Game/Overlays/NotificationOverlay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays { public partial class NotificationOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent, INotificationOverlay { - public IconUsage Icon => HexaconsIcons.Notification; + public IconUsage Icon => OsuIcon.Notification; public LocalisableString Title => NotificationsStrings.HeaderTitle; public LocalisableString Description => NotificationsStrings.HeaderDescription; diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 425ff0935d..7ec52364d4 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -29,7 +29,7 @@ namespace osu.Game.Overlays { public partial class NowPlayingOverlay : OsuFocusedOverlayContainer, INamedOverlayComponent { - public IconUsage Icon => HexaconsIcons.Music; + public IconUsage Icon => OsuIcon.Music; public LocalisableString Title => NowPlayingStrings.HeaderTitle; public LocalisableString Description => NowPlayingStrings.HeaderDescription; diff --git a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs index 63128fb73d..a23ec18afe 100644 --- a/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs +++ b/osu.Game/Overlays/Rankings/RankingsOverlayHeader.cs @@ -36,7 +36,7 @@ namespace osu.Game.Overlays.Rankings { Title = PageTitleStrings.MainRankingControllerDefault; Description = NamedOverlayComponentStrings.RankingsDescription; - Icon = HexaconsIcons.Rankings; + Icon = OsuIcon.Ranking; } } } diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 5735b1515a..9efd848035 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays { public partial class SettingsOverlay : SettingsPanel, INamedOverlayComponent { - public IconUsage Icon => HexaconsIcons.Settings; + public IconUsage Icon => OsuIcon.Settings; public LocalisableString Title => SettingsStrings.HeaderTitle; public LocalisableString Description => SettingsStrings.HeaderDescription; diff --git a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs index ded0229d67..70675c1b92 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs @@ -21,7 +21,7 @@ namespace osu.Game.Overlays.Toolbar { TooltipMain = ToolbarStrings.HomeHeaderTitle; TooltipSub = ToolbarStrings.HomeHeaderDescription; - SetIcon(HexaconsIcons.Home); + SetIcon(OsuIcon.Home); } } } diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 9e9e565684..24eddeb0c2 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -82,7 +82,7 @@ namespace osu.Game.Overlays.Wiki { Title = PageTitleStrings.MainWikiControllerDefault; Description = NamedOverlayComponentStrings.WikiDescription; - Icon = HexaconsIcons.Wiki; + Icon = OsuIcon.Wiki; } } } From 28d9145a4c182ecb66029e0b1e52f24f7cc16acf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 15:47:29 +0100 Subject: [PATCH 0703/1118] Add more spacing to toolbar icons --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 81d2de5acb..a547fda814 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -113,7 +113,7 @@ namespace osu.Game.Overlays.Toolbar { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Size = new Vector2(26), + Size = new Vector2(20), Alpha = 0, }, DrawableText = new OsuSpriteText From c45477bd1fff8447755f7afd54bf5156e1352833 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 15:40:48 +0100 Subject: [PATCH 0704/1118] Use new icons in main menu wherever feasible --- osu.Game/Screens/Menu/ButtonSystem.cs | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index ca5bef985e..b2b3fbd626 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -13,7 +13,6 @@ using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Logging; @@ -103,8 +102,8 @@ namespace osu.Game.Screens.Menu buttonArea.AddRange(new Drawable[] { - new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, FontAwesome.Solid.Cog, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), - backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.LeftCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, + new MainMenuButton(ButtonSystemStrings.Settings, string.Empty, OsuIcon.Settings, new Color4(85, 85, 85, 255), () => OnSettings?.Invoke(), -WEDGE_WIDTH, Key.O), + backButton = new MainMenuButton(ButtonSystemStrings.Back, @"back-to-top", OsuIcon.PrevCircle, new Color4(51, 58, 94, 255), () => State = ButtonSystemState.TopLevel, -WEDGE_WIDTH) { VisibleStateMin = ButtonSystemState.Play, @@ -128,18 +127,18 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(AudioManager audio, IdleTracker? idleTracker, GameHost host) { - buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", FontAwesome.Solid.User, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); - buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", FontAwesome.Solid.Users, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); - buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Charts, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); + buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Solo, @"button-default-select", OsuIcon.Player, new Color4(102, 68, 204, 255), () => OnSolo?.Invoke(), WEDGE_WIDTH, Key.P)); + buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Multi, @"button-default-select", OsuIcon.Online, new Color4(94, 63, 186, 255), onMultiplayer, 0, Key.M)); + buttonsPlay.Add(new MainMenuButton(ButtonSystemStrings.Playlists, @"button-default-select", OsuIcon.Tournament, new Color4(94, 63, 186, 255), onPlaylists, 0, Key.L)); buttonsPlay.ForEach(b => b.VisibleState = ButtonSystemState.Play); - buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", HexaconsIcons.Beatmap, new Color4(238, 170, 0, 255), () => OnEditBeatmap?.Invoke(), WEDGE_WIDTH, Key.B)); - buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", HexaconsIcons.Editor, new Color4(220, 160, 0, 255), () => OnEditSkin?.Invoke(), 0, Key.S)); + buttonsEdit.Add(new MainMenuButton(EditorStrings.BeatmapEditor.ToLower(), @"button-default-select", OsuIcon.Beatmap, new Color4(238, 170, 0, 255), () => OnEditBeatmap?.Invoke(), WEDGE_WIDTH, Key.B)); + buttonsEdit.Add(new MainMenuButton(SkinEditorStrings.SkinEditor.ToLower(), @"button-default-select", OsuIcon.SkinB, new Color4(220, 160, 0, 255), () => OnEditSkin?.Invoke(), 0, Key.S)); buttonsEdit.ForEach(b => b.VisibleState = ButtonSystemState.Edit); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Play, @"button-play-select", OsuIcon.Logo, new Color4(102, 68, 204, 255), () => State = ButtonSystemState.Play, WEDGE_WIDTH, Key.P)); buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Edit, @"button-play-select", OsuIcon.EditCircle, new Color4(238, 170, 0, 255), () => State = ButtonSystemState.Edit, 0, Key.E)); - buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.ChevronDownCircle, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D)); + buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Browse, @"button-default-select", OsuIcon.Beatmap, new Color4(165, 204, 0, 255), () => OnBeatmapListing?.Invoke(), 0, Key.B, Key.D)); if (host.CanExit) buttonsTopLevel.Add(new MainMenuButton(ButtonSystemStrings.Exit, string.Empty, OsuIcon.CrossCircle, new Color4(238, 51, 153, 255), () => OnExit?.Invoke(), 0, Key.Q)); From 2857322a8bc3257ae26ea35567b8f1741b7a45ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 15:53:01 +0100 Subject: [PATCH 0705/1118] Use new icons in settings --- osu.Game/Overlays/Settings/Sections/AudioSection.cs | 3 ++- osu.Game/Overlays/Settings/Sections/DebugSection.cs | 3 ++- osu.Game/Overlays/Settings/Sections/GameplaySection.cs | 3 ++- osu.Game/Overlays/Settings/Sections/GeneralSection.cs | 2 +- osu.Game/Overlays/Settings/Sections/GraphicsSection.cs | 3 ++- osu.Game/Overlays/Settings/Sections/InputSection.cs | 3 ++- osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs | 3 ++- osu.Game/Overlays/Settings/Sections/OnlineSection.cs | 3 ++- osu.Game/Overlays/Settings/Sections/RulesetSection.cs | 3 ++- osu.Game/Overlays/Settings/Sections/SkinSection.cs | 3 ++- osu.Game/Overlays/Settings/Sections/UserInterfaceSection.cs | 3 ++- 11 files changed, 21 insertions(+), 11 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/AudioSection.cs b/osu.Game/Overlays/Settings/Sections/AudioSection.cs index fb3d486776..1ab0d6c886 100644 --- a/osu.Game/Overlays/Settings/Sections/AudioSection.cs +++ b/osu.Game/Overlays/Settings/Sections/AudioSection.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Localisation; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Audio; @@ -17,7 +18,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.VolumeUp + Icon = OsuIcon.Audio }; public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "sound" }); diff --git a/osu.Game/Overlays/Settings/Sections/DebugSection.cs b/osu.Game/Overlays/Settings/Sections/DebugSection.cs index 33a6f4c673..b84c441057 100644 --- a/osu.Game/Overlays/Settings/Sections/DebugSection.cs +++ b/osu.Game/Overlays/Settings/Sections/DebugSection.cs @@ -5,6 +5,7 @@ using osu.Framework.Development; using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.DebugSettings; @@ -16,7 +17,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.Bug + Icon = OsuIcon.Debug }; public DebugSection() diff --git a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs index b60689b611..463b3d1d09 100644 --- a/osu.Game/Overlays/Settings/Sections/GameplaySection.cs +++ b/osu.Game/Overlays/Settings/Sections/GameplaySection.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Gameplay; @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Regular.DotCircle + Icon = OsuIcon.GameplayC }; public GameplaySection() diff --git a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs index 2b043d40bc..2aa1008b1d 100644 --- a/osu.Game/Overlays/Settings/Sections/GeneralSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GeneralSection.cs @@ -23,7 +23,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.Cog + Icon = OsuIcon.Settings }; [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs index 98f6908512..e1fa1eef9c 100644 --- a/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs +++ b/osu.Game/Overlays/Settings/Sections/GraphicsSection.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Graphics; @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.Laptop + Icon = OsuIcon.Graphics }; public GraphicsSection() diff --git a/osu.Game/Overlays/Settings/Sections/InputSection.cs b/osu.Game/Overlays/Settings/Sections/InputSection.cs index a8f19cc91d..0204aa5e64 100644 --- a/osu.Game/Overlays/Settings/Sections/InputSection.cs +++ b/osu.Game/Overlays/Settings/Sections/InputSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Handlers; using osu.Framework.Localisation; using osu.Framework.Platform; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Input; @@ -20,7 +21,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.Keyboard + Icon = OsuIcon.Input }; public InputSection(KeyBindingPanel keyConfig) diff --git a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs index bb0a952164..bd90e4c35d 100644 --- a/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs +++ b/osu.Game/Overlays/Settings/Sections/MaintenanceSection.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Maintenance; @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.Wrench + Icon = OsuIcon.Maintenance }; public MaintenanceSection() diff --git a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs index c8faa3b697..1484f2c756 100644 --- a/osu.Game/Overlays/Settings/Sections/OnlineSection.cs +++ b/osu.Game/Overlays/Settings/Sections/OnlineSection.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.Online; @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.GlobeAsia + Icon = OsuIcon.Online }; public OnlineSection() diff --git a/osu.Game/Overlays/Settings/Sections/RulesetSection.cs b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs index aaad1ec4e2..626264151f 100644 --- a/osu.Game/Overlays/Settings/Sections/RulesetSection.cs +++ b/osu.Game/Overlays/Settings/Sections/RulesetSection.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Logging; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Rulesets; @@ -18,7 +19,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.Chess + Icon = OsuIcon.Rulesets }; [BackgroundDependencyLoader] diff --git a/osu.Game/Overlays/Settings/Sections/SkinSection.cs b/osu.Game/Overlays/Settings/Sections/SkinSection.cs index 1d057f42c0..9b04f208a7 100644 --- a/osu.Game/Overlays/Settings/Sections/SkinSection.cs +++ b/osu.Game/Overlays/Settings/Sections/SkinSection.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; using osu.Framework.Logging; using osu.Game.Database; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Overlays.SkinEditor; @@ -31,7 +32,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.PaintBrush + Icon = OsuIcon.SkinB }; private static readonly Live random_skin_info = new SkinInfo diff --git a/osu.Game/Overlays/Settings/Sections/UserInterfaceSection.cs b/osu.Game/Overlays/Settings/Sections/UserInterfaceSection.cs index 2ec9e32ea9..953ede25e1 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterfaceSection.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterfaceSection.cs @@ -4,6 +4,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Localisation; using osu.Game.Overlays.Settings.Sections.UserInterface; @@ -15,7 +16,7 @@ namespace osu.Game.Overlays.Settings.Sections public override Drawable CreateIcon() => new SpriteIcon { - Icon = FontAwesome.Solid.LayerGroup + Icon = OsuIcon.UserInterface }; public UserInterfaceSection() From 288ac930e44bd8dc5b5ab8d5f5f342228625fc0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 16:03:57 +0100 Subject: [PATCH 0706/1118] Use new icons in editor Some that exist on figma are purposefully not used due to an editorial request from @peppy. --- osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs | 3 ++- osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs | 3 ++- osu.Game/Rulesets/Edit/Tools/SelectTool.cs | 3 ++- .../Edit/Compose/Components/ComposeBlueprintContainer.cs | 6 +++--- 4 files changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs index 8382317d70..448cfaf84c 100644 --- a/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs +++ b/osu.Game.Rulesets.Osu/Edit/OsuHitObjectComposer.cs @@ -17,6 +17,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Utils; using osu.Game.Beatmaps; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Edit.Tools; @@ -54,7 +55,7 @@ namespace osu.Game.Rulesets.Osu.Edit .Concat(DistanceSnapProvider.CreateTernaryButtons()) .Concat(new[] { - new TernaryButton(rectangularGridSnapToggle, "Grid Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Th }) + new TernaryButton(rectangularGridSnapToggle, "Grid Snap", () => new SpriteIcon { Icon = OsuIcon.EditorGridSnap }) }); private BindableList selectedHitObjects; diff --git a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs index ddf539771d..b3ca59a5b0 100644 --- a/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs +++ b/osu.Game/Rulesets/Edit/ComposerDistanceSnapProvider.cs @@ -16,6 +16,7 @@ using osu.Framework.Input.Events; using osu.Framework.Localisation; using osu.Framework.Utils; using osu.Game.Configuration; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Overlays; @@ -169,7 +170,7 @@ namespace osu.Game.Rulesets.Edit public IEnumerable CreateTernaryButtons() => new[] { - new TernaryButton(DistanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = FontAwesome.Solid.Ruler }) + new TernaryButton(DistanceSnapToggle, "Distance Snap", () => new SpriteIcon { Icon = OsuIcon.EditorDistanceSnap }) }; protected override bool OnKeyDown(KeyDownEvent e) diff --git a/osu.Game/Rulesets/Edit/Tools/SelectTool.cs b/osu.Game/Rulesets/Edit/Tools/SelectTool.cs index 9640830a09..a272e9f480 100644 --- a/osu.Game/Rulesets/Edit/Tools/SelectTool.cs +++ b/osu.Game/Rulesets/Edit/Tools/SelectTool.cs @@ -5,6 +5,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; namespace osu.Game.Rulesets.Edit.Tools { @@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Edit.Tools { } - public override Drawable CreateIcon() => new SpriteIcon { Icon = FontAwesome.Solid.MousePointer }; + public override Drawable CreateIcon() => new SpriteIcon { Icon = OsuIcon.EditorSelect }; public override PlacementBlueprint CreatePlacementBlueprint() => null; } diff --git a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs index c7c7c4aa83..4fba798a26 100644 --- a/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/ComposeBlueprintContainer.cs @@ -225,7 +225,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected virtual IEnumerable CreateTernaryButtons() { //TODO: this should only be enabled (visible?) for rulesets that provide combo-supporting HitObjects. - yield return new TernaryButton(NewCombo, "New combo", () => new SpriteIcon { Icon = FontAwesome.Regular.DotCircle }); + yield return new TernaryButton(NewCombo, "New combo", () => new SpriteIcon { Icon = OsuIcon.EditorNewComboA }); foreach (var kvp in SelectionHandler.SelectionSampleStates) yield return new TernaryButton(kvp.Value, kvp.Key.Replace("hit", string.Empty).Titleize(), () => getIconForSample(kvp.Key)); @@ -272,10 +272,10 @@ namespace osu.Game.Screens.Edit.Compose.Components return new SpriteIcon { Icon = FontAwesome.Solid.Hands }; case HitSampleInfo.HIT_WHISTLE: - return new SpriteIcon { Icon = FontAwesome.Solid.Bullhorn }; + return new SpriteIcon { Icon = OsuIcon.EditorWhistle }; case HitSampleInfo.HIT_FINISH: - return new SpriteIcon { Icon = FontAwesome.Solid.DrumSteelpan }; + return new SpriteIcon { Icon = OsuIcon.EditorFinish }; } return null; From 53766285ce6cd5b227e933fe45da8f32bc2f6cf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 15:55:19 +0100 Subject: [PATCH 0707/1118] Remove remaining hexacons usages --- osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs | 2 +- osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs | 2 +- osu.Game/Overlays/Chat/ChatOverlayTopBar.cs | 2 +- osu.Game/Overlays/Profile/ProfileHeader.cs | 2 +- osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs | 2 +- osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs index 55a04b129c..5db7223bdf 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneOverlayHeader.cs @@ -154,7 +154,7 @@ namespace osu.Game.Tests.Visual.UserInterface public TestTitle() { Title = "title"; - Icon = HexaconsIcons.Devtools; + Icon = OsuIcon.ChangelogB; } } } diff --git a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs index eced27f35e..1df246ae77 100644 --- a/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs +++ b/osu.Game/Overlays/BeatmapSet/BeatmapSetHeader.cs @@ -60,7 +60,7 @@ namespace osu.Game.Overlays.BeatmapSet public BeatmapHeaderTitle() { Title = PageTitleStrings.MainBeatmapsetsControllerShow; - Icon = HexaconsIcons.Beatmap; + Icon = OsuIcon.Beatmap; } } } diff --git a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs index 4fc9fbb6d5..3ecdb09976 100644 --- a/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs +++ b/osu.Game/Overlays/Chat/ChatOverlayTopBar.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Chat { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Icon = HexaconsIcons.Messaging, + Icon = OsuIcon.Chat, Size = new Vector2(24), }, // Placeholder text diff --git a/osu.Game/Overlays/Profile/ProfileHeader.cs b/osu.Game/Overlays/Profile/ProfileHeader.cs index 78343d08f1..42bec50022 100644 --- a/osu.Game/Overlays/Profile/ProfileHeader.cs +++ b/osu.Game/Overlays/Profile/ProfileHeader.cs @@ -87,7 +87,7 @@ namespace osu.Game.Overlays.Profile public ProfileHeaderTitle() { Title = PageTitleStrings.MainUsersControllerDefault; - Icon = HexaconsIcons.Profile; + Icon = OsuIcon.Player; } } } diff --git a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs index 5c77672d90..0e125d0ec0 100644 --- a/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs +++ b/osu.Game/Screens/Edit/Components/Menus/EditorMenuBar.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Edit.Components.Menus Size = new Vector2(26), Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - Icon = HexaconsIcons.Editor, + Icon = OsuIcon.EditCircle, }, text = new TextFlowContainer { diff --git a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs index 93448c4394..022da36abc 100644 --- a/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs +++ b/osu.Game/Screens/Edit/Setup/SetupScreenHeader.cs @@ -80,7 +80,7 @@ namespace osu.Game.Screens.Edit.Setup { Title = EditorSetupStrings.BeatmapSetup.ToLower(); Description = EditorSetupStrings.BeatmapSetupDescription; - Icon = HexaconsIcons.Social; + Icon = OsuIcon.Beatmap; } } From 89e2b6358a2937ee46246f3b05b4122c7528ae95 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 15:55:37 +0100 Subject: [PATCH 0708/1118] Remove hexacons --- osu.Game/Graphics/HexaconsIcons.cs | 131 ----------------------------- osu.Game/OsuGameBase.cs | 1 - 2 files changed, 132 deletions(-) delete mode 100644 osu.Game/Graphics/HexaconsIcons.cs diff --git a/osu.Game/Graphics/HexaconsIcons.cs b/osu.Game/Graphics/HexaconsIcons.cs deleted file mode 100644 index 3eee5d7197..0000000000 --- a/osu.Game/Graphics/HexaconsIcons.cs +++ /dev/null @@ -1,131 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System; -using System.Collections.Generic; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Graphics.Textures; -using osu.Framework.Text; - -namespace osu.Game.Graphics -{ - public static class HexaconsIcons - { - public const string FONT_NAME = "Icons/Hexacons"; - - public static IconUsage BeatmapPacks => get(HexaconsMapping.beatmap_packs); - public static IconUsage Beatmap => get(HexaconsMapping.beatmap); - public static IconUsage Calendar => get(HexaconsMapping.calendar); - public static IconUsage Chart => get(HexaconsMapping.chart); - public static IconUsage Community => get(HexaconsMapping.community); - public static IconUsage Contests => get(HexaconsMapping.contests); - public static IconUsage Devtools => get(HexaconsMapping.devtools); - public static IconUsage Download => get(HexaconsMapping.download); - public static IconUsage Editor => get(HexaconsMapping.editor); - public static IconUsage FeaturedArtist => get(HexaconsMapping.featured_artist); - public static IconUsage Home => get(HexaconsMapping.home); - public static IconUsage Messaging => get(HexaconsMapping.messaging); - public static IconUsage Music => get(HexaconsMapping.music); - public static IconUsage News => get(HexaconsMapping.news); - public static IconUsage Notification => get(HexaconsMapping.notification); - public static IconUsage Profile => get(HexaconsMapping.profile); - public static IconUsage Rankings => get(HexaconsMapping.rankings); - public static IconUsage Search => get(HexaconsMapping.search); - public static IconUsage Settings => get(HexaconsMapping.settings); - public static IconUsage Social => get(HexaconsMapping.social); - public static IconUsage Store => get(HexaconsMapping.store); - public static IconUsage Tournament => get(HexaconsMapping.tournament); - public static IconUsage Wiki => get(HexaconsMapping.wiki); - - private static IconUsage get(HexaconsMapping icon) => new IconUsage((char)icon, FONT_NAME); - - // Basically just converting to something we can use in a `char` lookup for FontStore/GlyphStore compatibility. - // Names should match filenames in resources. - private enum HexaconsMapping - { - beatmap_packs, - beatmap, - calendar, - chart, - community, - contests, - devtools, - download, - editor, - featured_artist, - home, - messaging, - music, - news, - notification, - profile, - rankings, - search, - settings, - social, - store, - tournament, - wiki, - } - - public class HexaconsStore : ITextureStore, ITexturedGlyphLookupStore - { - private readonly TextureStore textures; - - public HexaconsStore(TextureStore textures) - { - this.textures = textures; - } - - public void Dispose() - { - textures.Dispose(); - } - - public ITexturedCharacterGlyph? Get(string? fontName, char character) - { - if (fontName == FONT_NAME) - return new Glyph(textures.Get($"{fontName}/{((HexaconsMapping)character).ToString().Replace("_", "-")}")); - - return null; - } - - public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); - - public Texture? Get(string name, WrapMode wrapModeS, WrapMode wrapModeT) => null; - - public Texture Get(string name) => throw new NotImplementedException(); - - public Task GetAsync(string name, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - - public Stream GetStream(string name) => throw new NotImplementedException(); - - public IEnumerable GetAvailableResources() => throw new NotImplementedException(); - - public Task GetAsync(string name, WrapMode wrapModeS, WrapMode wrapModeT, CancellationToken cancellationToken = default) => throw new NotImplementedException(); - - public class Glyph : ITexturedCharacterGlyph - { - public float XOffset => default; - public float YOffset => default; - public float XAdvance => default; - public float Baseline => default; - public char Character => default; - - public float GetKerning(T lastGlyph) where T : ICharacterGlyph => throw new NotImplementedException(); - - public Texture Texture { get; } - public float Width => Texture.Width; - public float Height => Texture.Height; - - public Glyph(Texture texture) - { - Texture = texture; - } - } - } - } -} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index b4ad21f045..5b17dc13c2 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -478,7 +478,6 @@ namespace osu.Game AddFont(Resources, @"Fonts/Venera/Venera-Bold"); AddFont(Resources, @"Fonts/Venera/Venera-Black"); - Fonts.AddStore(new HexaconsIcons.HexaconsStore(Textures)); Fonts.AddStore(new OsuIcon.OsuIconStore(Textures)); } From 655528a5370fe83b05452e220d22103824bfecc0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 03:04:13 +0900 Subject: [PATCH 0709/1118] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index dd2393ff21..c7e0cc3808 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From 5f7f1f771d2404e05a7d152d73e893e3d1c54cc0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 03:09:20 +0900 Subject: [PATCH 0710/1118] Reword tooltip text for dashboard --- osu.Game/Localisation/NamedOverlayComponentStrings.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Localisation/NamedOverlayComponentStrings.cs b/osu.Game/Localisation/NamedOverlayComponentStrings.cs index 475bea2a4a..72e63d699a 100644 --- a/osu.Game/Localisation/NamedOverlayComponentStrings.cs +++ b/osu.Game/Localisation/NamedOverlayComponentStrings.cs @@ -20,12 +20,12 @@ namespace osu.Game.Localisation public static LocalisableString ChangelogDescription => new TranslatableString(getKey(@"changelog_description"), @"track recent dev updates in the osu! ecosystem"); /// - /// "view your friends and other information" + /// "view your friends and spectate other players" /// - public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard_description"), @"view your friends and other information"); + public static LocalisableString DashboardDescription => new TranslatableString(getKey(@"dashboard_description"), @"view your friends and spectate other players"); /// - /// "find out who's the best right now" + /// "find out who's the best right now" /// public static LocalisableString RankingsDescription => new TranslatableString(getKey(@"rankings_description"), @"find out who's the best right now"); @@ -39,6 +39,6 @@ namespace osu.Game.Localisation /// public static LocalisableString WikiDescription => new TranslatableString(getKey(@"wiki_description"), @"knowledge base"); - private static string getKey(string key) => $"{prefix}:{key}"; + private static string getKey(string key) => $@"{prefix}:{key}"; } } From 1f55ef211eda02b867327746313a32d599645d17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 03:11:27 +0900 Subject: [PATCH 0711/1118] Rearrange buttons --- osu.Game/Overlays/Toolbar/Toolbar.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index 93294a9d30..ec1238ad1f 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -164,11 +164,11 @@ namespace osu.Game.Overlays.Toolbar { new ToolbarNewsButton(), new ToolbarChangelogButton(), + new ToolbarWikiButton(), new ToolbarRankingsButton(), new ToolbarBeatmapListingButton(), new ToolbarChatButton(), new ToolbarSocialButton(), - new ToolbarWikiButton(), new ToolbarMusicButton(), //new ToolbarButton //{ From d4423d493364065baa7984acda63884e26e8a98d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 18:38:29 +0100 Subject: [PATCH 0712/1118] Store last set score to a `SessionStatic` --- osu.Game/Configuration/SessionStatics.cs | 7 +++++++ osu.Game/Scoring/ScoreInfo.cs | 1 + osu.Game/Screens/Play/SubmittingPlayer.cs | 5 +++++ 3 files changed, 13 insertions(+) diff --git a/osu.Game/Configuration/SessionStatics.cs b/osu.Game/Configuration/SessionStatics.cs index 8f0a60b23d..1548b781a7 100644 --- a/osu.Game/Configuration/SessionStatics.cs +++ b/osu.Game/Configuration/SessionStatics.cs @@ -9,6 +9,7 @@ using osu.Game.Input; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; using osu.Game.Overlays.Mods; +using osu.Game.Scoring; namespace osu.Game.Configuration { @@ -27,6 +28,7 @@ namespace osu.Game.Configuration SetDefault(Static.LastModSelectPanelSamplePlaybackTime, (double?)null); SetDefault(Static.SeasonalBackgrounds, null); SetDefault(Static.TouchInputActive, RuntimeInfo.IsMobile); + SetDefault(Static.LastLocalUserScore, null); } /// @@ -73,5 +75,10 @@ namespace osu.Game.Configuration /// Used in touchscreen detection scenarios (). /// TouchInputActive, + + /// + /// Stores the local user's last score (can be completed or aborted). + /// + LastLocalUserScore, } } diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 7071bd380e..44795c6fa7 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -207,6 +207,7 @@ namespace osu.Game.Scoring clone.Statistics = new Dictionary(clone.Statistics); clone.MaximumStatistics = new Dictionary(clone.MaximumStatistics); + clone.HitEvents = new List(clone.HitEvents); // Ensure we have fresh mods to avoid any references (ie. after gameplay). clone.clearAllMods(); diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index f88526b8f9..ff2c57bf35 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Database; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; @@ -37,6 +38,9 @@ namespace osu.Game.Screens.Play [Resolved] private SpectatorClient spectatorClient { get; set; } + [Resolved] + private SessionStatics statics { get; set; } + private TaskCompletionSource scoreSubmissionSource; protected SubmittingPlayer(PlayerConfiguration configuration = null) @@ -176,6 +180,7 @@ namespace osu.Game.Screens.Play { bool exiting = base.OnExiting(e); submitFromFailOrQuit(); + statics.SetValue(Static.LastLocalUserScore, Score.ScoreInfo.DeepClone()); return exiting; } From 1b7af989ec68e06b10c1a014e37c4a56f47cd1c6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 19:14:17 +0100 Subject: [PATCH 0713/1118] Migrate `BeatmapOffsetControl` to use session static directly --- .../Gameplay/TestSceneBeatmapOffsetControl.cs | 28 ++++++++++++++++--- osu.Game/Screens/Play/PlayerLoader.cs | 4 --- .../Play/PlayerSettings/AudioSettings.cs | 7 +++-- .../PlayerSettings/BeatmapOffsetControl.cs | 4 +++ 4 files changed, 32 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs index f3701b664c..83fc5c2013 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneBeatmapOffsetControl.cs @@ -12,6 +12,7 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play.PlayerSettings; +using osu.Game.Tests.Resources; using osu.Game.Tests.Visual.Ranking; namespace osu.Game.Tests.Visual.Gameplay @@ -44,7 +45,23 @@ namespace osu.Game.Tests.Visual.Gameplay { offsetControl.ReferenceScore.Value = new ScoreInfo { - HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(0, 2) + HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(0, 2), + BeatmapInfo = Beatmap.Value.BeatmapInfo, + }; + }); + + AddAssert("No calibration button", () => !offsetControl.ChildrenOfType().Any()); + } + + [Test] + public void TestScoreFromDifferentBeatmap() + { + AddStep("Set short reference score", () => + { + offsetControl.ReferenceScore.Value = new ScoreInfo + { + HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(10), + BeatmapInfo = TestResources.CreateTestBeatmapSetInfo().Beatmaps.First(), }; }); @@ -59,7 +76,8 @@ namespace osu.Game.Tests.Visual.Gameplay offsetControl.ReferenceScore.Value = new ScoreInfo { HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(10), - Mods = new Mod[] { new OsuModRelax() } + Mods = new Mod[] { new OsuModRelax() }, + BeatmapInfo = Beatmap.Value.BeatmapInfo, }; }); @@ -77,7 +95,8 @@ namespace osu.Game.Tests.Visual.Gameplay { offsetControl.ReferenceScore.Value = new ScoreInfo { - HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(average_error) + HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(average_error), + BeatmapInfo = Beatmap.Value.BeatmapInfo, }; }); @@ -105,7 +124,8 @@ namespace osu.Game.Tests.Visual.Gameplay { offsetControl.ReferenceScore.Value = new ScoreInfo { - HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(average_error) + HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(average_error), + BeatmapInfo = Beatmap.Value.BeatmapInfo, }; }); diff --git a/osu.Game/Screens/Play/PlayerLoader.cs b/osu.Game/Screens/Play/PlayerLoader.cs index 681189d184..232de53ac3 100644 --- a/osu.Game/Screens/Play/PlayerLoader.cs +++ b/osu.Game/Screens/Play/PlayerLoader.cs @@ -263,10 +263,6 @@ namespace osu.Game.Screens.Play Debug.Assert(CurrentPlayer != null); - var lastScore = CurrentPlayer.Score; - - AudioSettings.ReferenceScore.Value = lastScore?.ScoreInfo; - // prepare for a retry. CurrentPlayer = null; playerConsumed = false; diff --git a/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs b/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs index 010d8115fa..3c79721590 100644 --- a/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/AudioSettings.cs @@ -14,7 +14,7 @@ namespace osu.Game.Screens.Play.PlayerSettings { public partial class AudioSettings : PlayerSettingsGroup { - public Bindable ReferenceScore { get; } = new Bindable(); + private Bindable referenceScore { get; } = new Bindable(); private readonly PlayerCheckbox beatmapHitsoundsToggle; @@ -26,15 +26,16 @@ namespace osu.Game.Screens.Play.PlayerSettings beatmapHitsoundsToggle = new PlayerCheckbox { LabelText = SkinSettingsStrings.BeatmapHitsounds }, new BeatmapOffsetControl { - ReferenceScore = { BindTarget = ReferenceScore }, + ReferenceScore = { BindTarget = referenceScore }, }, }; } [BackgroundDependencyLoader] - private void load(OsuConfigManager config) + private void load(OsuConfigManager config, SessionStatics statics) { beatmapHitsoundsToggle.Current = config.GetBindable(OsuSetting.BeatmapHitsounds); + statics.BindWith(Static.LastLocalUserScore, referenceScore); } } } diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index b0e7d08699..3f0f0fd1df 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -7,6 +7,7 @@ using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Bindings; @@ -174,6 +175,9 @@ namespace osu.Game.Screens.Play.PlayerSettings if (score.NewValue == null) return; + if (!score.NewValue.BeatmapInfo.AsNonNull().Equals(beatmap.Value.BeatmapInfo)) + return; + if (score.NewValue.Mods.Any(m => !m.UserPlayable || m is IHasNoTimedInputs)) return; From 70aa067eb166477f29b31495da6b1a8121a0bf8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 03:23:04 +0900 Subject: [PATCH 0714/1118] Adjust gradient visibility and transition --- osu.Game/Overlays/Toolbar/Toolbar.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/Toolbar.cs b/osu.Game/Overlays/Toolbar/Toolbar.cs index ec1238ad1f..52fad2ba3b 100644 --- a/osu.Game/Overlays/Toolbar/Toolbar.cs +++ b/osu.Game/Overlays/Toolbar/Toolbar.cs @@ -224,9 +224,9 @@ namespace osu.Game.Overlays.Toolbar RelativeSizeAxes = Axes.X, Anchor = Anchor.BottomLeft, Alpha = 0, - Height = 100, + Height = 80, Colour = ColourInfo.GradientVertical( - OsuColour.Gray(0).Opacity(0.9f), OsuColour.Gray(0).Opacity(0)), + OsuColour.Gray(0f).Opacity(0.7f), OsuColour.Gray(0).Opacity(0)), }, }; } @@ -241,9 +241,9 @@ namespace osu.Game.Overlays.Toolbar private void updateState() { if (ShowGradient.Value) - gradientBackground.FadeIn(transition_time, Easing.OutQuint); + gradientBackground.FadeIn(2500, Easing.OutQuint); else - gradientBackground.FadeOut(transition_time, Easing.OutQuint); + gradientBackground.FadeOut(200, Easing.OutQuint); } } From 92c4c20a51e544a27b745932334e6442d5d5d994 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 03:43:38 +0900 Subject: [PATCH 0715/1118] Adjust paddings and fills of toolbar buttons --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 45 ++++++++++--------- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 34 +++++++++----- .../Toolbar/ToolbarOverlayToggleButton.cs | 2 +- .../Toolbar/ToolbarRulesetSelector.cs | 1 + .../Overlays/Toolbar/ToolbarUserButton.cs | 2 +- 5 files changed, 51 insertions(+), 33 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index a547fda814..bd5faf1588 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -7,7 +7,6 @@ using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.EnumExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input; @@ -74,6 +73,8 @@ namespace osu.Game.Overlays.Toolbar private readonly SpriteText keyBindingTooltip; protected FillFlowContainer Flow; + protected readonly Container BackgroundContent; + [Resolved] private RealmAccess realm { get; set; } = null!; @@ -82,21 +83,33 @@ namespace osu.Game.Overlays.Toolbar Width = Toolbar.HEIGHT; RelativeSizeAxes = Axes.Y; + Padding = new MarginPadding(3); + Children = new Drawable[] { - HoverBackground = new Box + BackgroundContent = new Container { RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(80).Opacity(180), - Blending = BlendingParameters.Additive, - Alpha = 0, - }, - flashBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Colour = Color4.White.Opacity(100), - Blending = BlendingParameters.Additive, + Masking = true, + CornerRadius = 6, + CornerExponent = 3f, + Children = new Drawable[] + { + HoverBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(80).Opacity(180), + Blending = BlendingParameters.Additive, + Alpha = 0, + }, + flashBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Colour = Color4.White.Opacity(100), + Blending = BlendingParameters.Additive, + }, + } }, Flow = new FillFlowContainer { @@ -219,14 +232,6 @@ namespace osu.Game.Overlays.Toolbar public OpaqueBackground() { RelativeSizeAxes = Axes.Both; - Masking = true; - MaskingSmoothness = 0; - EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(40), - Radius = 5, - }; Children = new Drawable[] { diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index f1310d8535..67688155ae 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -42,21 +42,33 @@ namespace osu.Game.Overlays.Toolbar clockDisplayMode = config.GetBindable(OsuSetting.ToolbarClockDisplayMode); prefer24HourTime = config.GetBindable(OsuSetting.Prefer24HourTime); + Padding = new MarginPadding(3); + Children = new Drawable[] { - hoverBackground = new Box + new Container { RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(80).Opacity(180), - Blending = BlendingParameters.Additive, - Alpha = 0, - }, - flashBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Colour = Color4.White.Opacity(100), - Blending = BlendingParameters.Additive, + Masking = true, + CornerRadius = 6, + CornerExponent = 3f, + Children = new Drawable[] + { + hoverBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(80).Opacity(180), + Blending = BlendingParameters.Additive, + Alpha = 0, + }, + flashBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Colour = Color4.White.Opacity(100), + Blending = BlendingParameters.Additive, + }, + } }, new FillFlowContainer { diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index 78c976111b..37038161e1 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -46,7 +46,7 @@ namespace osu.Game.Overlays.Toolbar public ToolbarOverlayToggleButton() { - Add(stateBackground = new Box + BackgroundContent.Add(stateBackground = new Box { RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(150).Opacity(180), diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 715076b368..63d11c1b9e 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -41,6 +41,7 @@ namespace osu.Game.Overlays.Toolbar new OpaqueBackground { Depth = 1, + Masking = true, }, ModeButtonLine = new Container { diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 028decea1e..7d1b6c7404 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -40,7 +40,7 @@ namespace osu.Game.Overlays.Toolbar [BackgroundDependencyLoader] private void load(OsuColour colours, IAPIProvider api, LoginOverlay? login) { - Add(new OpaqueBackground { Depth = 1 }); + BackgroundContent.Add(new OpaqueBackground { Depth = 1 }); Flow.Add(new Container { From cf5e3e886386737385fe75cf1642e75279c7ff16 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 04:06:29 +0900 Subject: [PATCH 0716/1118] Breathe some colour and life into the toolbar --- .../Toolbar/ToolbarOverlayToggleButton.cs | 8 ++-- .../Toolbar/ToolbarRulesetSelector.cs | 43 +++++++++---------- .../Toolbar/ToolbarRulesetTabButton.cs | 30 +++++++------ 3 files changed, 43 insertions(+), 38 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index 37038161e1..09b8df14a6 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -3,6 +3,7 @@ #nullable disable +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; @@ -14,7 +15,7 @@ namespace osu.Game.Overlays.Toolbar { public partial class ToolbarOverlayToggleButton : ToolbarButton { - private readonly Box stateBackground; + private Box stateBackground; private OverlayContainer stateContainer; @@ -44,12 +45,13 @@ namespace osu.Game.Overlays.Toolbar } } - public ToolbarOverlayToggleButton() + [BackgroundDependencyLoader] + private void load(OsuColour colours) { BackgroundContent.Add(stateBackground = new Box { RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(150).Opacity(180), + Colour = colours.Carmine.Opacity(180), Blending = BlendingParameters.Additive, Depth = 2, Alpha = 0, diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs index 63d11c1b9e..723c24597a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetSelector.cs @@ -4,20 +4,19 @@ #nullable disable using System.Collections.Generic; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; -using osuTK; -using osuTK.Graphics; -using osu.Framework.Graphics.Shapes; -using osu.Game.Rulesets; -using osu.Framework.Graphics.UserInterface; -using osu.Framework.Input.Events; -using osuTK.Input; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Sample; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Framework.Input.Events; +using osu.Game.Rulesets; +using osuTK; +using osuTK.Graphics; +using osuTK.Input; namespace osu.Game.Overlays.Toolbar { @@ -47,20 +46,18 @@ namespace osu.Game.Overlays.Toolbar { Size = new Vector2(Toolbar.HEIGHT, 3), Anchor = Anchor.BottomLeft, - Origin = Anchor.TopLeft, - Masking = true, - EdgeEffect = new EdgeEffectParameters + Origin = Anchor.BottomLeft, + Y = -1, + Children = new Drawable[] { - Type = EdgeEffectType.Glow, - Colour = new Color4(255, 194, 224, 100), - Radius = 15, - Roundness = 15, - }, - Child = new Box - { - RelativeSizeAxes = Axes.Both, + new Circle + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Size = new Vector2(18, 3), + } } - } + }, }); foreach (var ruleset in Rulesets.AvailableRulesets) @@ -90,7 +87,7 @@ namespace osu.Game.Overlays.Toolbar { if (SelectedTab != null) { - ModeButtonLine.MoveToX(SelectedTab.DrawPosition.X, !hasInitialPosition ? 0 : 200, Easing.OutQuint); + ModeButtonLine.MoveToX(SelectedTab.DrawPosition.X, !hasInitialPosition ? 0 : 500, Easing.OutElasticQuarter); if (hasInitialPosition) selectionSamples[SelectedTab.Value.ShortName]?.Play(); diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs index 74f76c7c89..5500f1c879 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs @@ -1,14 +1,16 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; +using osu.Game.Graphics; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Rulesets; -using osuTK.Graphics; namespace osu.Game.Overlays.Toolbar { @@ -41,27 +43,31 @@ namespace osu.Game.Overlays.Toolbar { protected override HoverSounds CreateHoverSounds(HoverSampleSet sampleSet) => new HoverSounds(); + [Resolved] + private OsuColour colours { get; set; } = null!; + + public RulesetButton() + { + Padding = new MarginPadding(3) + { + Bottom = 5 + }; + } + public bool Active { - set + set => Scheduler.AddOnce(() => { if (value) { - IconContainer.Colour = Color4.White; - IconContainer.EdgeEffect = new EdgeEffectParameters - { - Type = EdgeEffectType.Glow, - Colour = new Color4(255, 194, 224, 100), - Radius = 15, - Roundness = 15, - }; + IconContainer.Colour = Color4Extensions.FromHex("#00FFAA"); } else { - IconContainer.Colour = new Color4(255, 194, 224, 255); + IconContainer.Colour = colours.GrayF; IconContainer.EdgeEffect = new EdgeEffectParameters(); } - } + }); } protected override bool OnClick(ClickEvent e) From f51b5f5487e770b783cc12513865eb1d5a3462d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 19:43:14 +0100 Subject: [PATCH 0717/1118] Add components to track average hit errors across session --- osu.Game/OsuGameBase.cs | 2 + .../Audio/AudioOffsetAdjustControl.cs | 23 ++++++++ .../Audio/SessionAverageHitErrorTracker.cs | 55 +++++++++++++++++++ 3 files changed, 80 insertions(+) create mode 100644 osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs create mode 100644 osu.Game/Overlays/Settings/Sections/Audio/SessionAverageHitErrorTracker.cs diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 48548dc1ef..64f15efb15 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -55,6 +55,7 @@ using osu.Game.Online.Spectator; using osu.Game.Overlays; using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; +using osu.Game.Overlays.Settings.Sections.Audio; using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Resources; using osu.Game.Rulesets; @@ -349,6 +350,7 @@ namespace osu.Game dependencies.CacheAs(powerStatus); dependencies.Cache(SessionStatics = new SessionStatics()); + dependencies.Cache(new SessionAverageHitErrorTracker()); dependencies.Cache(Colours = new OsuColour()); RegisterImportHandler(BeatmapManager); diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs new file mode 100644 index 0000000000..045cd24fc0 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs @@ -0,0 +1,23 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Overlays.Settings.Sections.Audio +{ + public partial class AudioOffsetAdjustControl : SettingsItem + { + [BackgroundDependencyLoader] + private void load() + { + } + + protected override Drawable CreateControl() => new AudioOffsetPreview(); + + private partial class AudioOffsetPreview : CompositeDrawable + { + } + } +} diff --git a/osu.Game/Overlays/Settings/Sections/Audio/SessionAverageHitErrorTracker.cs b/osu.Game/Overlays/Settings/Sections/Audio/SessionAverageHitErrorTracker.cs new file mode 100644 index 0000000000..b714d49b89 --- /dev/null +++ b/osu.Game/Overlays/Settings/Sections/Audio/SessionAverageHitErrorTracker.cs @@ -0,0 +1,55 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Game.Configuration; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; + +namespace osu.Game.Overlays.Settings.Sections.Audio +{ + /// + /// Tracks the local user's average hit error during the ongoing play session. + /// + [Cached] + public partial class SessionAverageHitErrorTracker : Component + { + public IBindableList AverageHitErrorHistory => averageHitErrorHistory; + private readonly BindableList averageHitErrorHistory = new BindableList(); + + private readonly Bindable latestScore = new Bindable(); + + [BackgroundDependencyLoader] + private void load(SessionStatics statics) + { + statics.BindWith(Static.LastLocalUserScore, latestScore); + latestScore.BindValueChanged(score => calculateAverageHitError(score.NewValue), true); + } + + private void calculateAverageHitError(ScoreInfo? newScore) + { + if (newScore == null) + return; + + if (newScore.Mods.Any(m => !m.UserPlayable || m is IHasNoTimedInputs)) + return; + + if (newScore.HitEvents.Count < 10) + return; + + if (newScore.HitEvents.CalculateAverageHitError() is not double averageError) + return; + + // keep a sane maximum number of entries. + if (averageHitErrorHistory.Count >= 50) + averageHitErrorHistory.RemoveAt(0); + averageHitErrorHistory.Add(averageError); + } + + public void ClearHistory() => averageHitErrorHistory.Clear(); + } +} From 160342ceed4824a3214157c049ff981e2710568f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 21:14:37 +0100 Subject: [PATCH 0718/1118] Implement automatic suggestion of audio offset based on last plays --- .../TestSceneAudioOffsetAdjustControl.cs | 63 ++++++++ osu.Game/OsuGameBase.cs | 5 +- .../Audio/AudioOffsetAdjustControl.cs | 138 +++++++++++++++++- .../Settings/Sections/Audio/OffsetSettings.cs | 5 +- 4 files changed, 205 insertions(+), 6 deletions(-) create mode 100644 osu.Game.Tests/Visual/Settings/TestSceneAudioOffsetAdjustControl.cs diff --git a/osu.Game.Tests/Visual/Settings/TestSceneAudioOffsetAdjustControl.cs b/osu.Game.Tests/Visual/Settings/TestSceneAudioOffsetAdjustControl.cs new file mode 100644 index 0000000000..efb65bb0a8 --- /dev/null +++ b/osu.Game.Tests/Visual/Settings/TestSceneAudioOffsetAdjustControl.cs @@ -0,0 +1,63 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; +using osu.Game.Configuration; +using osu.Game.Overlays.Settings.Sections.Audio; +using osu.Game.Scoring; +using osu.Game.Tests.Visual.Ranking; + +namespace osu.Game.Tests.Visual.Settings +{ + public partial class TestSceneAudioOffsetAdjustControl : OsuTestScene + { + [Resolved] + private SessionStatics statics { get; set; } = null!; + + [Cached] + private SessionAverageHitErrorTracker tracker = new SessionAverageHitErrorTracker(); + + private Container content = null!; + protected override Container Content => content; + + [BackgroundDependencyLoader] + private void load() + { + base.Content.AddRange(new Drawable[] + { + tracker, + content = new Container + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Width = 400, + AutoSizeAxes = Axes.Y + } + }); + } + + [Test] + public void TestBehaviour() + { + AddStep("create control", () => Child = new AudioOffsetAdjustControl + { + Current = new BindableDouble + { + MinValue = -500, + MaxValue = 500 + } + }); + AddStep("set new score", () => statics.SetValue(Static.LastLocalUserScore, new ScoreInfo + { + HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(RNG.NextDouble(-100, 100)), + BeatmapInfo = Beatmap.Value.BeatmapInfo, + })); + AddStep("clear history", () => tracker.ClearHistory()); + } + } +} diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 64f15efb15..0d8a2fbb97 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -201,6 +201,8 @@ namespace osu.Game private RulesetConfigCache rulesetConfigCache; + private SessionAverageHitErrorTracker hitErrorTracker; + protected SpectatorClient SpectatorClient { get; private set; } protected MultiplayerClient MultiplayerClient { get; private set; } @@ -350,7 +352,7 @@ namespace osu.Game dependencies.CacheAs(powerStatus); dependencies.Cache(SessionStatics = new SessionStatics()); - dependencies.Cache(new SessionAverageHitErrorTracker()); + dependencies.Cache(hitErrorTracker = new SessionAverageHitErrorTracker()); dependencies.Cache(Colours = new OsuColour()); RegisterImportHandler(BeatmapManager); @@ -410,6 +412,7 @@ namespace osu.Game }); base.Content.Add(new TouchInputInterceptor()); + base.Content.Add(hitErrorTracker); KeyBindingStore = new RealmKeyBindingStore(realm, keyCombinationProvider); KeyBindingStore.Register(globalBindings, RulesetStore.AvailableRulesets); diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs index 045cd24fc0..0023c60c61 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs @@ -1,9 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.UserInterface; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Graphics.UserInterface; +using osu.Game.Graphics.UserInterfaceV2; +using osu.Game.Localisation; +using osuTK; namespace osu.Game.Overlays.Settings.Sections.Audio { @@ -12,12 +26,134 @@ namespace osu.Game.Overlays.Settings.Sections.Audio [BackgroundDependencyLoader] private void load() { + LabelText = AudioSettingsStrings.AudioOffset; } protected override Drawable CreateControl() => new AudioOffsetPreview(); - private partial class AudioOffsetPreview : CompositeDrawable + private partial class AudioOffsetPreview : CompositeDrawable, IHasCurrentValue { + public Bindable Current + { + get => current.Current; + set => current.Current = value; + } + + private readonly BindableNumberWithCurrent current = new BindableNumberWithCurrent(); + + private readonly IBindableList averageHitErrorHistory = new BindableList(); + + private readonly Bindable suggestedOffset = new Bindable(); + + private Container notchContainer = null!; + private TextFlowContainer hintText = null!; + private RoundedButton applySuggestion = null!; + + [BackgroundDependencyLoader] + private void load(SessionAverageHitErrorTracker hitErrorTracker) + { + averageHitErrorHistory.BindTo(hitErrorTracker.AverageHitErrorHistory); + + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + InternalChild = new FillFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Spacing = new Vector2(10), + Direction = FillDirection.Vertical, + Children = new Drawable[] + { + new TimeSlider + { + RelativeSizeAxes = Axes.X, + Current = { BindTarget = Current }, + KeyboardStep = 1, + }, + notchContainer = new Container + { + RelativeSizeAxes = Axes.X, + Height = 10, + Padding = new MarginPadding { Horizontal = Nub.DEFAULT_EXPANDED_SIZE / 2 }, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + hintText = new OsuTextFlowContainer(t => t.Font = OsuFont.Default.With(size: 16)) + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + }, + applySuggestion = new RoundedButton + { + RelativeSizeAxes = Axes.X, + Text = "Apply suggested offset", + Action = () => + { + if (suggestedOffset.Value.HasValue) + current.Value = suggestedOffset.Value.Value; + hitErrorTracker.ClearHistory(); + } + } + } + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + averageHitErrorHistory.BindCollectionChanged(updateDisplay, true); + suggestedOffset.BindValueChanged(_ => updateHintText(), true); + } + + private void updateDisplay(object? _, NotifyCollectionChangedEventArgs e) + { + switch (e.Action) + { + case NotifyCollectionChangedAction.Add: + foreach (double average in e.NewItems!) + { + notchContainer.ForEach(n => n.Alpha *= 0.95f); + notchContainer.Add(new Box + { + RelativeSizeAxes = Axes.Y, + Width = 2, + RelativePositionAxes = Axes.X, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + X = getXPositionForAverage(average) + }); + } + + break; + + case NotifyCollectionChangedAction.Remove: + foreach (double average in e.OldItems!) + { + var notch = notchContainer.FirstOrDefault(n => n.X == getXPositionForAverage(average)); + Debug.Assert(notch != null); + notchContainer.Remove(notch, true); + } + + break; + + case NotifyCollectionChangedAction.Reset: + notchContainer.Clear(); + break; + } + + suggestedOffset.Value = averageHitErrorHistory.Count < 3 ? null : -averageHitErrorHistory.Average(); + } + + private float getXPositionForAverage(double average) => (float)(Math.Clamp(-average, current.MinValue, current.MaxValue) / (2 * current.MaxValue)); + + private void updateHintText() + { + hintText.Text = suggestedOffset.Value == null + ? @"Play a few beatmaps to receive a suggested offset!" + : $@"Based on the last {averageHitErrorHistory.Count} plays, the suggested offset is {suggestedOffset.Value:N0} ms."; + applySuggestion.Enabled.Value = suggestedOffset.Value != null; + } } } } diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index 6b5c769853..af15d310fc 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Localisation; using osu.Game.Configuration; -using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; namespace osu.Game.Overlays.Settings.Sections.Audio @@ -23,11 +22,9 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { Children = new Drawable[] { - new SettingsSlider + new AudioOffsetAdjustControl { - LabelText = AudioSettingsStrings.AudioOffset, Current = config.GetBindable(OsuSetting.AudioOffset), - KeyboardStep = 1f }, new SettingsButton { From cf39bb7a18877055a58324d542b9c4feb83d1611 Mon Sep 17 00:00:00 2001 From: rushiiMachine <33725716+rushiiMachine@users.noreply.github.com> Date: Wed, 27 Dec 2023 12:55:20 -0800 Subject: [PATCH 0719/1118] Fix spinner max bonus not respecting ISamplePlaybackDisabler The spinner max bonus was loaded through SkinnableSound instead of PausableSkinnableSound, leading to it not respecting the case where sample playback is globally disabled through ISamplePlaybackDisabler, and can be easily heard in situations like during the catchup period after seeking using the ArgonSongProgressBar with song volume at 0 --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs index f7c1437009..bf4b07eaab 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSpinner.cs @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private const float spinning_sample_initial_frequency = 1.0f; private const float spinning_sample_modulated_base_frequency = 0.5f; - private SkinnableSound maxBonusSample; + private PausableSkinnableSound maxBonusSample; /// /// The amount of bonus score gained from spinning after the required number of spins, for display purposes. @@ -114,7 +114,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Looping = true, Frequency = { Value = spinning_sample_initial_frequency } }, - maxBonusSample = new SkinnableSound + maxBonusSample = new PausableSkinnableSound { MinimumSampleVolume = MINIMUM_SAMPLE_VOLUME, } From d9299a8a55bff6757358db2d46b38806817f974a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 23:07:17 +0100 Subject: [PATCH 0720/1118] Implement visual appearance of "system title" message in main menu --- .../Visual/Menus/TestSceneMainMenu.cs | 30 ++++++ .../API/Requests/Responses/APISystemTitle.cs | 16 +++ osu.Game/OsuGame.cs | 5 +- osu.Game/Screens/Menu/MainMenu.cs | 15 +++ osu.Game/Screens/Menu/SystemTitle.cs | 101 ++++++++++++++++++ 5 files changed, 166 insertions(+), 1 deletion(-) create mode 100644 osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs create mode 100644 osu.Game/Online/API/Requests/Responses/APISystemTitle.cs create mode 100644 osu.Game/Screens/Menu/SystemTitle.cs diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs new file mode 100644 index 0000000000..85aa14f33e --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs @@ -0,0 +1,30 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.Menus +{ + public partial class TestSceneMainMenu : OsuGameTestScene + { + [Test] + public void TestSystemTitle() + { + AddStep("set system title", () => Game.ChildrenOfType().Single().Current.Value = new APISystemTitle + { + Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png", + Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023", + }); + AddStep("set another title", () => Game.ChildrenOfType().Single().Current.Value = new APISystemTitle + { + Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", + Url = @"https://osu.ppy.sh/community/contests/189", + }); + AddStep("unset system title", () => Game.ChildrenOfType().Single().Current.Value = null); + } + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs b/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs new file mode 100644 index 0000000000..3a2342cc4c --- /dev/null +++ b/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs @@ -0,0 +1,16 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using Newtonsoft.Json; + +namespace osu.Game.Online.API.Requests.Responses +{ + public class APISystemTitle + { + [JsonProperty(@"image")] + public string Image { get; set; } = string.Empty; + + [JsonProperty(@"url")] + public string Url { get; set; } = string.Empty; + } +} diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index e7ff99ef01..37996e8832 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -995,7 +995,10 @@ namespace osu.Game }, topMostOverlayContent.Add); if (!args?.Any(a => a == @"--no-version-overlay") ?? true) - loadComponentSingleFile(versionManager = new VersionManager { Depth = int.MinValue }, ScreenContainer.Add); + { + dependencies.Cache(versionManager = new VersionManager { Depth = int.MinValue }); + loadComponentSingleFile(versionManager, ScreenContainer.Add); + } loadComponentSingleFile(osuLogo, _ => { diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index c25e62d69e..0739689daa 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -76,6 +76,9 @@ namespace osu.Game.Screens.Menu [Resolved(canBeNull: true)] private IDialogOverlay dialogOverlay { get; set; } + [Resolved(canBeNull: true)] + private VersionManager versionManager { get; set; } + protected override BackgroundScreen CreateBackground() => new BackgroundScreenDefault(); protected override bool PlayExitSound => false; @@ -91,6 +94,7 @@ namespace osu.Game.Screens.Menu private ParallaxContainer buttonsContainer; private SongTicker songTicker; private Container logoTarget; + private SystemTitle systemTitle; private Sample reappearSampleSwoosh; @@ -153,6 +157,7 @@ namespace osu.Game.Screens.Menu Margin = new MarginPadding { Right = 15, Top = 5 } }, new KiaiMenuFountains(), + systemTitle = new SystemTitle(), holdToExitGameOverlay?.CreateProxy() ?? Empty() }); @@ -263,6 +268,16 @@ namespace osu.Game.Screens.Menu } } + protected override void Update() + { + base.Update(); + + systemTitle.Margin = new MarginPadding + { + Bottom = (versionManager?.DrawHeight + 5) ?? 0 + }; + } + protected override void LogoSuspending(OsuLogo logo) { var seq = logo.FadeOut(300, Easing.InSine) diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs new file mode 100644 index 0000000000..6ca9aab6c7 --- /dev/null +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -0,0 +1,101 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Threading; +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; +using osu.Framework.Input.Events; +using osu.Framework.Platform; +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Screens.Menu +{ + public partial class SystemTitle : CompositeDrawable + { + internal Bindable Current { get; } = new Bindable(); + + private Container content = null!; + private CancellationTokenSource? cancellationTokenSource; + private SystemTitleImage? currentImage; + + [BackgroundDependencyLoader] + private void load(GameHost? gameHost) + { + Anchor = Anchor.BottomCentre; + Origin = Anchor.BottomCentre; + AutoSizeAxes = Axes.Both; + + InternalChild = content = new ClickableContainer + { + AutoSizeAxes = Axes.Both, + Action = () => + { + if (!string.IsNullOrEmpty(Current.Value?.Url)) + gameHost?.OpenUrlExternally(Current.Value.Url); + } + }; + } + + protected override bool OnHover(HoverEvent e) + { + content.ScaleTo(1.1f, 500, Easing.OutBounce); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + content.ScaleTo(1f, 500, Easing.OutBounce); + base.OnHoverLost(e); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + Current.BindValueChanged(_ => loadNewImage(), true); + } + + private void loadNewImage() + { + cancellationTokenSource?.Cancel(); + cancellationTokenSource = null; + currentImage?.FadeOut(500, Easing.OutQuint).Expire(); + + if (string.IsNullOrEmpty(Current.Value?.Image)) + return; + + LoadComponentAsync(new SystemTitleImage(Current.Value), loaded => + { + if (loaded.SystemTitle != Current.Value) + loaded.Dispose(); + + loaded.FadeInFromZero(500, Easing.OutQuint); + content.Add(currentImage = loaded); + }, (cancellationTokenSource ??= new CancellationTokenSource()).Token); + } + + [LongRunningLoad] + private partial class SystemTitleImage : Sprite + { + public readonly APISystemTitle SystemTitle; + + public SystemTitleImage(APISystemTitle systemTitle) + { + SystemTitle = systemTitle; + } + + [BackgroundDependencyLoader] + private void load(LargeTextureStore textureStore) + { + var texture = textureStore.Get(SystemTitle.Image); + if (SystemTitle.Image.Contains(@"@2x")) + texture.ScaleAdjust *= 2; + Texture = texture; + } + } + } +} From a3f720bc627f27d6bcc9fe36c011190010930b0d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 23:31:14 +0100 Subject: [PATCH 0721/1118] Retrieve system title from online source --- .../API/Requests/GetSystemTitleRequest.cs | 15 ++++++++++++ .../API/Requests/Responses/APISystemTitle.cs | 2 +- osu.Game/Screens/Menu/SystemTitle.cs | 23 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Online/API/Requests/GetSystemTitleRequest.cs diff --git a/osu.Game/Online/API/Requests/GetSystemTitleRequest.cs b/osu.Game/Online/API/Requests/GetSystemTitleRequest.cs new file mode 100644 index 0000000000..52ca0c11eb --- /dev/null +++ b/osu.Game/Online/API/Requests/GetSystemTitleRequest.cs @@ -0,0 +1,15 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Game.Online.API.Requests.Responses; + +namespace osu.Game.Online.API.Requests +{ + public class GetSystemTitleRequest : OsuJsonWebRequest + { + public GetSystemTitleRequest() + : base(@"https://assets.ppy.sh/lazer-status.json") + { + } + } +} diff --git a/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs b/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs index 3a2342cc4c..97bdc8355a 100644 --- a/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs +++ b/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs @@ -5,7 +5,7 @@ using Newtonsoft.Json; namespace osu.Game.Online.API.Requests.Responses { - public class APISystemTitle + public record APISystemTitle { [JsonProperty(@"image")] public string Image { get; set; } = string.Empty; diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index 6ca9aab6c7..0867fc4748 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -1,7 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Threading; +using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -10,6 +12,7 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Framework.Platform; +using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Screens.Menu @@ -57,6 +60,26 @@ namespace osu.Game.Screens.Menu base.LoadComplete(); Current.BindValueChanged(_ => loadNewImage(), true); + + checkForUpdates(); + Scheduler.AddDelayed(checkForUpdates, TimeSpan.FromMinutes(15).TotalMilliseconds, true); + } + + private void checkForUpdates() + { + var request = new GetSystemTitleRequest(); + Task.Run(() => request.Perform()) + .ContinueWith(r => + { + if (r.IsCompletedSuccessfully) + Schedule(() => Current.Value = request.ResponseObject); + + // if the request failed, "observe" the exception. + // it isn't very important why this failed, as it's only for display. + // the inner error will be logged by framework mechanisms anyway. + if (r.IsFaulted) + _ = r.Exception; + }); } private void loadNewImage() From ac449131edcee95d9ca61fef538aeea7010c128d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 27 Dec 2023 23:47:37 +0100 Subject: [PATCH 0722/1118] CodeFileSanity does not like records in standalone files --- .../API/Requests/Responses/APISystemTitle.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs b/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs index 97bdc8355a..6d5d91f8f3 100644 --- a/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs +++ b/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs @@ -1,16 +1,29 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using Newtonsoft.Json; namespace osu.Game.Online.API.Requests.Responses { - public record APISystemTitle + public class APISystemTitle : IEquatable { [JsonProperty(@"image")] public string Image { get; set; } = string.Empty; [JsonProperty(@"url")] public string Url { get; set; } = string.Empty; + + public bool Equals(APISystemTitle? other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + + return Image == other.Image && Url == other.Url; + } + + public override bool Equals(object? obj) => obj is APISystemTitle other && Equals(other); + + public override int GetHashCode() => HashCode.Combine(Image, Url); } } From ef3975981376e7308f4156024929f404f9240504 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Dec 2023 00:09:51 +0100 Subject: [PATCH 0723/1118] More code quality inspections --- osu.Game/Online/API/Requests/Responses/APISystemTitle.cs | 1 + osu.Game/Screens/Menu/SystemTitle.cs | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs b/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs index 6d5d91f8f3..bfa5c1043b 100644 --- a/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs +++ b/osu.Game/Online/API/Requests/Responses/APISystemTitle.cs @@ -24,6 +24,7 @@ namespace osu.Game.Online.API.Requests.Responses public override bool Equals(object? obj) => obj is APISystemTitle other && Equals(other); + // ReSharper disable NonReadonlyMemberInGetHashCode public override int GetHashCode() => HashCode.Combine(Image, Url); } } diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index 0867fc4748..6b976a8ed6 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -93,7 +93,7 @@ namespace osu.Game.Screens.Menu LoadComponentAsync(new SystemTitleImage(Current.Value), loaded => { - if (loaded.SystemTitle != Current.Value) + if (!loaded.SystemTitle.Equals(Current.Value)) loaded.Dispose(); loaded.FadeInFromZero(500, Easing.OutQuint); From cbfcda79299e2ed2d42e720b81dcf6cd6f55984f Mon Sep 17 00:00:00 2001 From: Felipe Marins Date: Thu, 28 Dec 2023 00:10:01 -0300 Subject: [PATCH 0724/1118] Expose `SelectAll()` method on `ShearedSearchTextBox` --- osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs index 131041e706..c3a9f8a586 100644 --- a/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs +++ b/osu.Game/Graphics/UserInterface/ShearedSearchTextBox.cs @@ -48,6 +48,8 @@ namespace osu.Game.Graphics.UserInterface public void KillFocus() => textBox.KillFocus(); + public bool SelectAll() => textBox.SelectAll(); + public ShearedSearchTextBox() { Height = 42; From dce9204731972d81aaeedb42002de243b9c0742c Mon Sep 17 00:00:00 2001 From: Felipe Marins Date: Thu, 28 Dec 2023 00:10:44 -0300 Subject: [PATCH 0725/1118] Select search box text on ModSelectOverlay when mod selection changes --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index baa7e594c1..43f44d682d 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -565,6 +565,9 @@ namespace osu.Game.Overlays.Mods .ToArray(); SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection); + + if (SearchTextBox.HasFocus) + SearchTextBox.SelectAll(); } #region Transition handling From f8d6b8e347c18e2bf0bec85cdf76aac6f0f5d7a7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 14:10:52 +0900 Subject: [PATCH 0726/1118] Adjust toolbar animations / layering to feel better --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index bd5faf1588..03a1cfc005 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -183,7 +183,7 @@ namespace osu.Game.Overlays.Toolbar protected override bool OnClick(ClickEvent e) { - flashBackground.FadeOutFromOne(800, Easing.OutQuint); + flashBackground.FadeIn(50).Then().FadeOutFromOne(800, Easing.OutQuint); tooltipContainer.FadeOut(100); return base.OnClick(e); } diff --git a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs index 09b8df14a6..06755a9da9 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarOverlayToggleButton.cs @@ -53,7 +53,7 @@ namespace osu.Game.Overlays.Toolbar RelativeSizeAxes = Axes.Both, Colour = colours.Carmine.Opacity(180), Blending = BlendingParameters.Additive, - Depth = 2, + Depth = float.MaxValue, Alpha = 0, }); @@ -65,11 +65,11 @@ namespace osu.Game.Overlays.Toolbar switch (state.NewValue) { case Visibility.Hidden: - stateBackground.FadeOut(200); + stateBackground.FadeOut(200, Easing.OutQuint); break; case Visibility.Visible: - stateBackground.FadeIn(200); + stateBackground.FadeIn(200, Easing.OutQuint); break; } } From ffc8778d673dd54a650066b9dc47d6252367da15 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 14:13:35 +0900 Subject: [PATCH 0727/1118] Fix song select leaderboard tab ordering not matching stable --- .../Select/Leaderboards/BeatmapLeaderboardScope.cs | 6 +++--- osu.Game/Screens/Select/PlayBeatmapDetailArea.cs | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs index 5bcb4c27a7..e2e3404877 100644 --- a/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs +++ b/osu.Game/Screens/Select/Leaderboards/BeatmapLeaderboardScope.cs @@ -12,12 +12,12 @@ namespace osu.Game.Screens.Select.Leaderboards [Description("Local Ranking")] Local, - [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowScoreboardCountry))] - Country, - [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowScoreboardGlobal))] Global, + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowScoreboardCountry))] + Country, + [LocalisableDescription(typeof(BeatmapsetsStrings), nameof(BeatmapsetsStrings.ShowScoreboardFriend))] Friend, } diff --git a/osu.Game/Screens/Select/PlayBeatmapDetailArea.cs b/osu.Game/Screens/Select/PlayBeatmapDetailArea.cs index 8a1b9ef3e1..deb1100dfc 100644 --- a/osu.Game/Screens/Select/PlayBeatmapDetailArea.cs +++ b/osu.Game/Screens/Select/PlayBeatmapDetailArea.cs @@ -80,8 +80,8 @@ namespace osu.Game.Screens.Select protected override BeatmapDetailAreaTabItem[] CreateTabItems() => base.CreateTabItems().Concat(new BeatmapDetailAreaTabItem[] { new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Local), - new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Country), new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Global), + new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Country), new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Friend), }).ToArray(); @@ -95,12 +95,12 @@ namespace osu.Game.Screens.Select case TabType.Local: return new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Local); - case TabType.Country: - return new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Country); - case TabType.Global: return new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Global); + case TabType.Country: + return new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Country); + case TabType.Friends: return new BeatmapDetailAreaLeaderboardTabItem(BeatmapLeaderboardScope.Friend); From 93a8afe96e101a63f65e6bcca92d9707c1fde767 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 14:40:10 +0900 Subject: [PATCH 0728/1118] Add very simple cache-busting (30 minutes) --- osu.Game/Online/API/Requests/GetSystemTitleRequest.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/GetSystemTitleRequest.cs b/osu.Game/Online/API/Requests/GetSystemTitleRequest.cs index 52ca0c11eb..659e46bb11 100644 --- a/osu.Game/Online/API/Requests/GetSystemTitleRequest.cs +++ b/osu.Game/Online/API/Requests/GetSystemTitleRequest.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Online.API.Requests @@ -8,7 +9,7 @@ namespace osu.Game.Online.API.Requests public class GetSystemTitleRequest : OsuJsonWebRequest { public GetSystemTitleRequest() - : base(@"https://assets.ppy.sh/lazer-status.json") + : base($@"https://assets.ppy.sh/lazer-status.json?{DateTimeOffset.UtcNow.ToUnixTimeSeconds() / 1800}") { } } From c70e7d340da905d081eb87863ea9588021816453 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 14:48:17 +0900 Subject: [PATCH 0729/1118] Adjust animation and add delay to URL open --- osu.Game/Screens/Menu/SystemTitle.cs | 37 ++++++++++++++++++++++++---- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index 6b976a8ed6..2c8cb90b96 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -12,6 +12,8 @@ using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; using osu.Framework.Platform; +using osu.Framework.Threading; +using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; @@ -25,6 +27,8 @@ namespace osu.Game.Screens.Menu private CancellationTokenSource? cancellationTokenSource; private SystemTitleImage? currentImage; + private ScheduledDelegate? openUrlAction; + [BackgroundDependencyLoader] private void load(GameHost? gameHost) { @@ -32,29 +36,52 @@ namespace osu.Game.Screens.Menu Origin = Anchor.BottomCentre; AutoSizeAxes = Axes.Both; - InternalChild = content = new ClickableContainer + InternalChild = content = new OsuClickableContainer { AutoSizeAxes = Axes.Both, Action = () => { - if (!string.IsNullOrEmpty(Current.Value?.Url)) - gameHost?.OpenUrlExternally(Current.Value.Url); + // Delay slightly to allow animation to play out. + openUrlAction?.Cancel(); + openUrlAction = Scheduler.AddDelayed(() => + { + if (!string.IsNullOrEmpty(Current.Value?.Url)) + gameHost?.OpenUrlExternally(Current.Value.Url); + }, 250); } }; } protected override bool OnHover(HoverEvent e) { - content.ScaleTo(1.1f, 500, Easing.OutBounce); + content.ScaleTo(1.05f, 2000, Easing.OutQuint); return base.OnHover(e); } protected override void OnHoverLost(HoverLostEvent e) { - content.ScaleTo(1f, 500, Easing.OutBounce); + content.ScaleTo(1f, 500, Easing.OutQuint); base.OnHoverLost(e); } + protected override bool OnClick(ClickEvent e) + { + //hover.FlashColour(FlashColour, 800, Easing.OutQuint); + return base.OnClick(e); + } + + protected override bool OnMouseDown(MouseDownEvent e) + { + content.ScaleTo(0.95f, 500, Easing.OutQuint); + return base.OnMouseDown(e); + } + + protected override void OnMouseUp(MouseUpEvent e) + { + content.ScaleTo(1, 500, Easing.OutElastic); + base.OnMouseUp(e); + } + protected override void LoadComplete() { base.LoadComplete(); From 289e0f00f98ea570fd0a3f6b644b4399c0e4b5ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 14:58:05 +0900 Subject: [PATCH 0730/1118] Add flash on click --- osu.Game/Screens/Menu/SystemTitle.cs | 37 +++++++++++++++++++++------- 1 file changed, 28 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index 2c8cb90b96..667dc6e947 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -41,6 +41,8 @@ namespace osu.Game.Screens.Menu AutoSizeAxes = Axes.Both, Action = () => { + currentImage?.Flash(); + // Delay slightly to allow animation to play out. openUrlAction?.Cancel(); openUrlAction = Scheduler.AddDelayed(() => @@ -64,12 +66,6 @@ namespace osu.Game.Screens.Menu base.OnHoverLost(e); } - protected override bool OnClick(ClickEvent e) - { - //hover.FlashColour(FlashColour, 800, Easing.OutQuint); - return base.OnClick(e); - } - protected override bool OnMouseDown(MouseDownEvent e) { content.ScaleTo(0.95f, 500, Easing.OutQuint); @@ -78,7 +74,9 @@ namespace osu.Game.Screens.Menu protected override void OnMouseUp(MouseUpEvent e) { - content.ScaleTo(1, 500, Easing.OutElastic); + content + .ScaleTo(0.95f) + .ScaleTo(1, 500, Easing.OutElastic); base.OnMouseUp(e); } @@ -129,10 +127,12 @@ namespace osu.Game.Screens.Menu } [LongRunningLoad] - private partial class SystemTitleImage : Sprite + private partial class SystemTitleImage : CompositeDrawable { public readonly APISystemTitle SystemTitle; + private Sprite flash = null!; + public SystemTitleImage(APISystemTitle systemTitle) { SystemTitle = systemTitle; @@ -144,7 +144,26 @@ namespace osu.Game.Screens.Menu var texture = textureStore.Get(SystemTitle.Image); if (SystemTitle.Image.Contains(@"@2x")) texture.ScaleAdjust *= 2; - Texture = texture; + + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + new Sprite { Texture = texture }, + flash = new Sprite + { + Texture = texture, + Blending = BlendingParameters.Additive, + Alpha = 0, + }, + }; + } + + public void Flash() + { + flash.FadeInFromZero(50) + .Then() + .FadeOut(500, Easing.OutQuint); } } } From 481a25178658e80891a59bcdd7ad1720576854fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 15:10:05 +0900 Subject: [PATCH 0731/1118] Use `HandleLink` to allow potentially opening wiki or otherwise --- osu.Game/Screens/Menu/SystemTitle.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index 667dc6e947..967b928b43 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -11,7 +11,6 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Graphics.Textures; using osu.Framework.Input.Events; -using osu.Framework.Platform; using osu.Framework.Threading; using osu.Game.Graphics.Containers; using osu.Game.Online.API.Requests; @@ -30,7 +29,7 @@ namespace osu.Game.Screens.Menu private ScheduledDelegate? openUrlAction; [BackgroundDependencyLoader] - private void load(GameHost? gameHost) + private void load(OsuGame? game) { Anchor = Anchor.BottomCentre; Origin = Anchor.BottomCentre; @@ -48,7 +47,7 @@ namespace osu.Game.Screens.Menu openUrlAction = Scheduler.AddDelayed(() => { if (!string.IsNullOrEmpty(Current.Value?.Url)) - gameHost?.OpenUrlExternally(Current.Value.Url); + game?.HandleLink(Current.Value.Url); }, 250); } }; From 972234b1e5c1a505f23fe1491d509ab343636b4c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 15:12:44 +0900 Subject: [PATCH 0732/1118] Move re-schedule inside continuation --- osu.Game/Screens/Menu/SystemTitle.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index 967b928b43..b8510dde87 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -86,7 +86,6 @@ namespace osu.Game.Screens.Menu Current.BindValueChanged(_ => loadNewImage(), true); checkForUpdates(); - Scheduler.AddDelayed(checkForUpdates, TimeSpan.FromMinutes(15).TotalMilliseconds, true); } private void checkForUpdates() @@ -103,6 +102,8 @@ namespace osu.Game.Screens.Menu // the inner error will be logged by framework mechanisms anyway. if (r.IsFaulted) _ = r.Exception; + + Scheduler.AddDelayed(checkForUpdates, TimeSpan.FromMinutes(15).TotalMilliseconds); }); } From 0ea62d0c5d52f98c19062e8baba5e225daea8c5c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 15:16:42 +0900 Subject: [PATCH 0733/1118] Add initial additive blending on fade in --- osu.Game/Screens/Menu/SystemTitle.cs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index b8510dde87..8b243e8f7f 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -121,7 +121,6 @@ namespace osu.Game.Screens.Menu if (!loaded.SystemTitle.Equals(Current.Value)) loaded.Dispose(); - loaded.FadeInFromZero(500, Easing.OutQuint); content.Add(currentImage = loaded); }, (cancellationTokenSource ??= new CancellationTokenSource()).Token); } @@ -154,16 +153,25 @@ namespace osu.Game.Screens.Menu { Texture = texture, Blending = BlendingParameters.Additive, - Alpha = 0, }, }; } - public void Flash() + protected override void LoadComplete() + { + base.LoadComplete(); + + this.FadeInFromZero(500, Easing.OutQuint); + flash.FadeOutFromOne(4000, Easing.OutQuint); + } + + public Drawable Flash() { flash.FadeInFromZero(50) .Then() .FadeOut(500, Easing.OutQuint); + + return this; } } } From 6684987289a6dcbd0ab918cd9976909bead57dc1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 15:18:21 +0900 Subject: [PATCH 0734/1118] Reduce refresh interval slightly --- osu.Game/Screens/Menu/SystemTitle.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index 8b243e8f7f..bb623cacdf 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.Menu if (r.IsFaulted) _ = r.Exception; - Scheduler.AddDelayed(checkForUpdates, TimeSpan.FromMinutes(15).TotalMilliseconds); + Scheduler.AddDelayed(checkForUpdates, TimeSpan.FromMinutes(5).TotalMilliseconds); }); } From a1867afbb43307ca775e56586bb6c86af038cc81 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 16:05:20 +0900 Subject: [PATCH 0735/1118] Move menu tips to main menu In preparation for removing the disclaimer screen. --- osu.Game/Configuration/OsuConfigManager.cs | 2 + osu.Game/Localisation/UserInterfaceStrings.cs | 7 +- .../UserInterface/MainMenuSettings.cs | 5 ++ osu.Game/Screens/Menu/Disclaimer.cs | 30 ------- osu.Game/Screens/Menu/MainMenu.cs | 38 +++++++- osu.Game/Screens/Menu/MenuTip.cs | 89 +++++++++++++++++++ osu.Game/Screens/Menu/SystemTitle.cs | 2 - 7 files changed, 138 insertions(+), 35 deletions(-) create mode 100644 osu.Game/Screens/Menu/MenuTip.cs diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 0df870655f..d4162b76d6 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -96,6 +96,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.MenuVoice, true); SetDefault(OsuSetting.MenuMusic, true); + SetDefault(OsuSetting.MenuTips, true); SetDefault(OsuSetting.AudioOffset, 0, -500.0, 500.0, 1); @@ -350,6 +351,7 @@ namespace osu.Game.Configuration VolumeInactive, MenuMusic, MenuVoice, + MenuTips, CursorRotation, MenuParallax, Prefer24HourTime, diff --git a/osu.Game/Localisation/UserInterfaceStrings.cs b/osu.Game/Localisation/UserInterfaceStrings.cs index 68c5c3ccbc..dceedca05c 100644 --- a/osu.Game/Localisation/UserInterfaceStrings.cs +++ b/osu.Game/Localisation/UserInterfaceStrings.cs @@ -24,6 +24,11 @@ namespace osu.Game.Localisation /// public static LocalisableString MenuCursorSize => new TranslatableString(getKey(@"menu_cursor_size"), @"Menu cursor size"); + /// + /// "Menu tips" + /// + public static LocalisableString ShowMenuTips => new TranslatableString(getKey(@"show_menu_tips"), @"Menu tips"); + /// /// "Parallax" /// @@ -154,6 +159,6 @@ namespace osu.Game.Localisation /// public static LocalisableString TrueRandom => new TranslatableString(getKey(@"true_random"), @"True Random"); - private static string getKey(string key) => $"{prefix}:{key}"; + private static string getKey(string key) => $@"{prefix}:{key}"; } } diff --git a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs index 4577fadb01..5e42c3035c 100644 --- a/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/UserInterface/MainMenuSettings.cs @@ -29,6 +29,11 @@ namespace osu.Game.Overlays.Settings.Sections.UserInterface Children = new Drawable[] { + new SettingsCheckbox + { + LabelText = UserInterfaceStrings.ShowMenuTips, + Current = config.GetBindable(OsuSetting.MenuTips) + }, new SettingsCheckbox { LabelText = UserInterfaceStrings.InterfaceVoices, diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs index 539d58d2d7..1afef66313 100644 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ b/osu.Game/Screens/Menu/Disclaimer.cs @@ -12,7 +12,6 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; using osu.Framework.Screens; -using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; @@ -122,10 +121,6 @@ namespace osu.Game.Screens.Menu textFlow.NewParagraph(); textFlow.NewParagraph(); - textFlow.AddParagraph("today's tip:", formatSemiBold); - textFlow.AddParagraph(getRandomTip(), formatRegular); - textFlow.NewParagraph(); - textFlow.NewParagraph(); iconColour = colours.Yellow; @@ -228,30 +223,5 @@ namespace osu.Game.Screens.Menu this.Push(nextScreen); }); } - - private string getRandomTip() - { - string[] tips = - { - "You can press Ctrl-T anywhere in the game to toggle the toolbar!", - "You can press Ctrl-O anywhere in the game to access options!", - "All settings are dynamic and take effect in real-time. Try pausing and changing the skin while playing!", - "New features are coming online every update. Make sure to stay up-to-date!", - "If you find the UI too large or small, try adjusting UI scale in settings!", - "Try adjusting the \"Screen Scaling\" mode to change your gameplay or UI area, even in fullscreen!", - "What used to be \"osu!direct\" is available to all users just like on the website. You can access it anywhere using Ctrl-B!", - "Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen!", - "Multithreading support means that even with low \"FPS\" your input and judgements will be accurate!", - "Try scrolling down in the mod select panel to find a bunch of new fun mods!", - "Most of the web content (profiles, rankings, etc.) are available natively in-game from the icons on the toolbar!", - "Get more details, hide or delete a beatmap by right-clicking on its panel at song select!", - "All delete operations are temporary until exiting. Restore accidentally deleted content from the maintenance settings!", - "Check out the \"playlists\" system, which lets users create their own custom and permanent leaderboards!", - "Toggle advanced frame / thread statistics with Ctrl-F11!", - "Take a look under the hood at performance counters and enable verbose performance logging with Ctrl-F2!", - }; - - return tips[RNG.Next(0, tips.Length)]; - } } } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 0739689daa..cde7da53ce 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -95,6 +95,8 @@ namespace osu.Game.Screens.Menu private SongTicker songTicker; private Container logoTarget; private SystemTitle systemTitle; + private MenuTip menuTip; + private FillFlowContainer bottomElementsFlow; private Sample reappearSampleSwoosh; @@ -157,7 +159,27 @@ namespace osu.Game.Screens.Menu Margin = new MarginPadding { Right = 15, Top = 5 } }, new KiaiMenuFountains(), - systemTitle = new SystemTitle(), + bottomElementsFlow = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Vertical, + Anchor = Anchor.BottomCentre, + Origin = Anchor.BottomCentre, + Spacing = new Vector2(15), + Children = new Drawable[] + { + menuTip = new MenuTip + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + }, + systemTitle = new SystemTitle + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + } + } + }, holdToExitGameOverlay?.CreateProxy() ?? Empty() }); @@ -220,6 +242,8 @@ namespace osu.Game.Screens.Menu if (storage is OsuStorage osuStorage && osuStorage.Error != OsuStorageError.None) dialogOverlay?.Push(new StorageErrorDialog(osuStorage, osuStorage.Error)); + + menuTip.ShowNextTip(); } [CanBeNull] @@ -272,7 +296,7 @@ namespace osu.Game.Screens.Menu { base.Update(); - systemTitle.Margin = new MarginPadding + bottomElementsFlow.Margin = new MarginPadding { Bottom = (versionManager?.DrawHeight + 5) ?? 0 }; @@ -314,6 +338,10 @@ namespace osu.Game.Screens.Menu buttonsContainer.MoveTo(new Vector2(-800, 0), FADE_OUT_DURATION, Easing.InSine); sideFlashes.FadeOut(64, Easing.OutQuint); + + bottomElementsFlow + .ScaleTo(0.9f, 1000, Easing.OutQuint) + .FadeOut(500, Easing.OutQuint); } public override void OnResuming(ScreenTransitionEvent e) @@ -330,6 +358,12 @@ namespace osu.Game.Screens.Menu preloadSongSelect(); musicController.EnsurePlayingSomething(); + + menuTip.ShowNextTip(); + + bottomElementsFlow + .ScaleTo(1, 1000, Easing.OutQuint) + .FadeIn(1000, Easing.OutQuint); } public override bool OnExiting(ScreenExitEvent e) diff --git a/osu.Game/Screens/Menu/MenuTip.cs b/osu.Game/Screens/Menu/MenuTip.cs new file mode 100644 index 0000000000..c9c8fea1c1 --- /dev/null +++ b/osu.Game/Screens/Menu/MenuTip.cs @@ -0,0 +1,89 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Utils; +using osu.Game.Configuration; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osuTK; + +namespace osu.Game.Screens.Menu +{ + public partial class MenuTip : CompositeDrawable + { + [Resolved] + private OsuConfigManager config { get; set; } = null!; + + private LinkFlowContainer textFlow = null!; + + [BackgroundDependencyLoader] + private void load() + { + AutoSizeAxes = Axes.Both; + + InternalChildren = new Drawable[] + { + textFlow = new LinkFlowContainer + { + Width = 700, + AutoSizeAxes = Axes.Y, + TextAnchor = Anchor.TopCentre, + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Spacing = new Vector2(0, 2), + }, + }; + } + + public void ShowNextTip() + { + if (!config.Get(OsuSetting.MenuTips)) return; + + static void formatRegular(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular); + static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold); + + string tip = getRandomTip(); + + AutoSizeAxes = Axes.Both; + + textFlow.Clear(); + textFlow.AddParagraph("a tip for you:", formatSemiBold); + textFlow.AddParagraph(tip, formatRegular); + + this.FadeInFromZero(200, Easing.OutQuint) + .Delay(1000 + 80 * tip.Length) + .Then() + .FadeOutFromOne(2000, Easing.OutQuint) + .Finally(_ => AutoSizeAxes = Axes.X); + } + + private string getRandomTip() + { + string[] tips = + { + "You can press Ctrl-T anywhere in the game to toggle the toolbar!", + "You can press Ctrl-O anywhere in the game to access options!", + "All settings are dynamic and take effect in real-time. Try changing the skin while watching autoplay!", + "New features are coming online every update. Make sure to stay up-to-date!", + "If you find the UI too large or small, try adjusting UI scale in settings!", + "Try adjusting the \"Screen Scaling\" mode to change your gameplay or UI area, even in fullscreen!", + "What used to be \"osu!direct\" is available to all users just like on the website. You can access it anywhere using Ctrl-B!", + "Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen!", + "Multithreading support means that even with low \"FPS\" your input and judgements will be accurate!", + "Try scrolling down in the mod select panel to find a bunch of new fun mods!", + "Most of the web content (profiles, rankings, etc.) are available natively in-game from the icons on the toolbar!", + "Get more details, hide or delete a beatmap by right-clicking on its panel at song select!", + "All delete operations are temporary until exiting. Restore accidentally deleted content from the maintenance settings!", + "Check out the \"playlists\" system, which lets users create their own custom and permanent leaderboards!", + "Toggle advanced frame / thread statistics with Ctrl-F11!", + "Take a look under the hood at performance counters and enable verbose performance logging with Ctrl-F2!", + }; + + return tips[RNG.Next(0, tips.Length)]; + } + } +} diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index bb623cacdf..060e426f8c 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -31,8 +31,6 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(OsuGame? game) { - Anchor = Anchor.BottomCentre; - Origin = Anchor.BottomCentre; AutoSizeAxes = Axes.Both; InternalChild = content = new OsuClickableContainer From 222459d921f886a7dd621057752a94442bc703d6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 16:16:27 +0900 Subject: [PATCH 0736/1118] Add background and improve layout --- osu.Game/Screens/Menu/MainMenu.cs | 2 +- osu.Game/Screens/Menu/MenuTip.cs | 32 ++++++++++++++++++++++--------- 2 files changed, 24 insertions(+), 10 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index cde7da53ce..e8554d077b 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -165,7 +165,7 @@ namespace osu.Game.Screens.Menu Direction = FillDirection.Vertical, Anchor = Anchor.BottomCentre, Origin = Anchor.BottomCentre, - Spacing = new Vector2(15), + Spacing = new Vector2(5), Children = new Drawable[] { menuTip = new MenuTip diff --git a/osu.Game/Screens/Menu/MenuTip.cs b/osu.Game/Screens/Menu/MenuTip.cs index c9c8fea1c1..ac23162bd0 100644 --- a/osu.Game/Screens/Menu/MenuTip.cs +++ b/osu.Game/Screens/Menu/MenuTip.cs @@ -4,12 +4,14 @@ using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osuTK; +using osuTK.Graphics; namespace osu.Game.Screens.Menu { @@ -27,14 +29,29 @@ namespace osu.Game.Screens.Menu InternalChildren = new Drawable[] { + new Container + { + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerExponent = 2.5f, + CornerRadius = 15, + Children = new Drawable[] + { + new Box + { + Colour = Color4.Black, + RelativeSizeAxes = Axes.Both, + Alpha = 0.4f, + }, + } + }, textFlow = new LinkFlowContainer { - Width = 700, + Width = 600, AutoSizeAxes = Axes.Y, TextAnchor = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, Spacing = new Vector2(0, 2), + Margin = new MarginPadding(10) }, }; } @@ -43,13 +60,11 @@ namespace osu.Game.Screens.Menu { if (!config.Get(OsuSetting.MenuTips)) return; - static void formatRegular(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular); - static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold); + static void formatRegular(SpriteText t) => t.Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular); + static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: 16, weight: FontWeight.SemiBold); string tip = getRandomTip(); - AutoSizeAxes = Axes.Both; - textFlow.Clear(); textFlow.AddParagraph("a tip for you:", formatSemiBold); textFlow.AddParagraph(tip, formatRegular); @@ -57,8 +72,7 @@ namespace osu.Game.Screens.Menu this.FadeInFromZero(200, Easing.OutQuint) .Delay(1000 + 80 * tip.Length) .Then() - .FadeOutFromOne(2000, Easing.OutQuint) - .Finally(_ => AutoSizeAxes = Axes.X); + .FadeOutFromOne(2000, Easing.OutQuint); } private string getRandomTip() From 932d03a4f8aebf8b76d43fad4c7b8ffa17e0ae95 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 16:19:12 +0900 Subject: [PATCH 0737/1118] Make toggle more immediately hide/show tips --- osu.Game/Screens/Menu/MainMenu.cs | 3 +-- osu.Game/Screens/Menu/MenuTip.cs | 17 ++++++++++++++++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e8554d077b..e3fc69dc2f 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -242,8 +242,6 @@ namespace osu.Game.Screens.Menu if (storage is OsuStorage osuStorage && osuStorage.Error != OsuStorageError.None) dialogOverlay?.Push(new StorageErrorDialog(osuStorage, osuStorage.Error)); - - menuTip.ShowNextTip(); } [CanBeNull] @@ -359,6 +357,7 @@ namespace osu.Game.Screens.Menu musicController.EnsurePlayingSomething(); + // Cycle tip on resuming menuTip.ShowNextTip(); bottomElementsFlow diff --git a/osu.Game/Screens/Menu/MenuTip.cs b/osu.Game/Screens/Menu/MenuTip.cs index ac23162bd0..325fb07767 100644 --- a/osu.Game/Screens/Menu/MenuTip.cs +++ b/osu.Game/Screens/Menu/MenuTip.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; @@ -22,6 +23,8 @@ namespace osu.Game.Screens.Menu private LinkFlowContainer textFlow = null!; + private Bindable showMenuTips = null!; + [BackgroundDependencyLoader] private void load() { @@ -56,9 +59,21 @@ namespace osu.Game.Screens.Menu }; } + protected override void LoadComplete() + { + base.LoadComplete(); + + showMenuTips = config.GetBindable(OsuSetting.MenuTips); + showMenuTips.BindValueChanged(_ => ShowNextTip(), true); + } + public void ShowNextTip() { - if (!config.Get(OsuSetting.MenuTips)) return; + if (!showMenuTips.Value) + { + this.FadeOut(100, Easing.OutQuint); + return; + } static void formatRegular(SpriteText t) => t.Font = OsuFont.GetFont(size: 16, weight: FontWeight.Regular); static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: 16, weight: FontWeight.SemiBold); From 0ad6ac8b2a2866d5b26cb044110122cd4702cd8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 16:48:17 +0900 Subject: [PATCH 0738/1118] Remove unused variable --- osu.Game/Screens/Menu/MainMenu.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index e3fc69dc2f..a90d9151b5 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -94,7 +94,6 @@ namespace osu.Game.Screens.Menu private ParallaxContainer buttonsContainer; private SongTicker songTicker; private Container logoTarget; - private SystemTitle systemTitle; private MenuTip menuTip; private FillFlowContainer bottomElementsFlow; @@ -173,7 +172,7 @@ namespace osu.Game.Screens.Menu Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, - systemTitle = new SystemTitle + new SystemTitle { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, From b19f72481b5d3cd1ad81458601aca0404a09d8fe Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 17:19:41 +0900 Subject: [PATCH 0739/1118] Fade out quickly on game exit sequence --- osu.Game/Screens/Menu/MainMenu.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index a90d9151b5..bc19e9cb63 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -399,6 +399,10 @@ namespace osu.Game.Screens.Menu songTicker.Hide(); this.FadeOut(3000); + + bottomElementsFlow + .FadeOut(500, Easing.OutQuint); + return base.OnExiting(e); } From 1f2339244ef739341e3429d88678e512430a47d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 17:14:16 +0900 Subject: [PATCH 0740/1118] Add supporter display to main menu --- .../Visual/Menus/TestSceneSupporterDisplay.cs | 37 +++++ osu.Game/Screens/Menu/MainMenu.cs | 13 ++ osu.Game/Screens/Menu/SupporterDisplay.cs | 126 ++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 osu.Game.Tests/Visual/Menus/TestSceneSupporterDisplay.cs create mode 100644 osu.Game/Screens/Menu/SupporterDisplay.cs diff --git a/osu.Game.Tests/Visual/Menus/TestSceneSupporterDisplay.cs b/osu.Game.Tests/Visual/Menus/TestSceneSupporterDisplay.cs new file mode 100644 index 0000000000..8b18adbe0d --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneSupporterDisplay.cs @@ -0,0 +1,37 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Framework.Graphics; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.Menus +{ + public partial class TestSceneSupporterDisplay : OsuTestScene + { + [Test] + public void TestBasic() + { + AddStep("create display", () => + { + Child = new SupporterDisplay + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + }; + }); + + AddStep("toggle support", () => + { + ((DummyAPIAccess)API).LocalUser.Value = new APIUser + { + Username = API.LocalUser.Value.Username, + Id = API.LocalUser.Value.Id + 1, + IsSupporter = !API.LocalUser.Value.IsSupporter, + }; + }); + } + } +} diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index bc19e9cb63..401460a498 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -96,6 +96,7 @@ namespace osu.Game.Screens.Menu private Container logoTarget; private MenuTip menuTip; private FillFlowContainer bottomElementsFlow; + private SupporterDisplay supporterDisplay; private Sample reappearSampleSwoosh; @@ -179,6 +180,12 @@ namespace osu.Game.Screens.Menu } } }, + supporterDisplay = new SupporterDisplay + { + Margin = new MarginPadding(5), + Anchor = Anchor.BottomLeft, + Origin = Anchor.BottomLeft, + }, holdToExitGameOverlay?.CreateProxy() ?? Empty() }); @@ -339,6 +346,9 @@ namespace osu.Game.Screens.Menu bottomElementsFlow .ScaleTo(0.9f, 1000, Easing.OutQuint) .FadeOut(500, Easing.OutQuint); + + supporterDisplay + .FadeOut(500, Easing.OutQuint); } public override void OnResuming(ScreenTransitionEvent e) @@ -403,6 +413,9 @@ namespace osu.Game.Screens.Menu bottomElementsFlow .FadeOut(500, Easing.OutQuint); + supporterDisplay + .FadeOut(500, Easing.OutQuint); + return base.OnExiting(e); } diff --git a/osu.Game/Screens/Menu/SupporterDisplay.cs b/osu.Game/Screens/Menu/SupporterDisplay.cs new file mode 100644 index 0000000000..d8ba21cd88 --- /dev/null +++ b/osu.Game/Screens/Menu/SupporterDisplay.cs @@ -0,0 +1,126 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Bindables; +using osu.Framework.Extensions.IEnumerableExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osu.Game.Graphics; +using osu.Game.Graphics.Containers; +using osu.Game.Online.API; +using osu.Game.Online.API.Requests.Responses; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Menu +{ + public partial class SupporterDisplay : CompositeDrawable + { + private LinkFlowContainer supportFlow = null!; + + private Drawable heart = null!; + + private readonly IBindable currentUser = new Bindable(); + + private Box backgroundBox = null!; + + [Resolved] + private IAPIProvider api { get; set; } = null!; + + [Resolved] + private OsuColour colours { get; set; } = null!; + + [BackgroundDependencyLoader] + private void load() + { + Height = 40; + + AutoSizeAxes = Axes.X; + AutoSizeDuration = 1000; + AutoSizeEasing = Easing.OutQuint; + + Masking = true; + CornerExponent = 2.5f; + CornerRadius = 15; + + InternalChildren = new Drawable[] + { + backgroundBox = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0.4f, + }, + supportFlow = new LinkFlowContainer + { + AutoSizeAxes = Axes.Both, + Padding = new MarginPadding(10), + Spacing = new Vector2(0, 2), + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + }; + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + const float font_size = 14; + + static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: font_size, weight: FontWeight.SemiBold); + + currentUser.BindTo(api.LocalUser); + currentUser.BindValueChanged(e => + { + supportFlow.Children.ForEach(d => d.FadeOut().Expire()); + + if (e.NewValue.IsSupporter) + { + supportFlow.AddText("Eternal thanks to you for supporting osu!", formatSemiBold); + + backgroundBox.FadeColour(colours.Pink, 250); + } + else + { + supportFlow.AddText("Consider becoming an ", formatSemiBold); + supportFlow.AddLink("osu!supporter", "https://osu.ppy.sh/home/support", formatSemiBold); + supportFlow.AddText(" to help support osu!'s development", formatSemiBold); + + backgroundBox.FadeColour(colours.Pink4, 250); + } + + supportFlow.AddIcon(FontAwesome.Solid.Heart, t => + { + heart = t; + + t.Padding = new MarginPadding { Left = 5, Top = 1 }; + t.Font = t.Font.With(size: font_size); + t.Origin = Anchor.Centre; + t.Colour = colours.Pink; + + Schedule(() => + { + heart?.FlashColour(Color4.White, 750, Easing.OutQuint).Loop(); + }); + }); + }, true); + + this + .FadeOut() + .Delay(1000) + .FadeInFromZero(800, Easing.OutQuint); + + Scheduler.AddDelayed(() => + { + AutoSizeEasing = Easing.In; + supportFlow.BypassAutoSizeAxes = Axes.X; + this + .Delay(200) + .FadeOut(750, Easing.Out); + }, 6000); + } + } +} From 7dc50b9baf5e6268c84b4177d3f6b14bbaa99514 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 17:18:47 +0900 Subject: [PATCH 0741/1118] Don't dismiss on hover, and allow dismissing via click --- osu.Game/Screens/Menu/SupporterDisplay.cs | 43 ++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/SupporterDisplay.cs b/osu.Game/Screens/Menu/SupporterDisplay.cs index d8ba21cd88..6639300f4a 100644 --- a/osu.Game/Screens/Menu/SupporterDisplay.cs +++ b/osu.Game/Screens/Menu/SupporterDisplay.cs @@ -8,6 +8,8 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; +using osu.Framework.Input.Events; +using osu.Framework.Threading; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Online.API; @@ -113,8 +115,47 @@ namespace osu.Game.Screens.Menu .Delay(1000) .FadeInFromZero(800, Easing.OutQuint); - Scheduler.AddDelayed(() => + scheduleDismissal(); + } + + protected override bool OnClick(ClickEvent e) + { + dismissalDelegate?.Cancel(); + + supportFlow.BypassAutoSizeAxes = Axes.X; + this.FadeOut(500, Easing.OutQuint); + return base.OnClick(e); + } + + protected override bool OnHover(HoverEvent e) + { + backgroundBox.FadeTo(0.6f, 500, Easing.OutQuint); + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + backgroundBox.FadeTo(0.4f, 500, Easing.OutQuint); + base.OnHoverLost(e); + } + + private ScheduledDelegate? dismissalDelegate; + + private void scheduleDismissal() + { + dismissalDelegate?.Cancel(); + dismissalDelegate = Scheduler.AddDelayed(() => { + // If the user is hovering they may want to interact with the link. + // Give them more time. + if (IsHovered) + { + scheduleDismissal(); + return; + } + + dismissalDelegate?.Cancel(); + AutoSizeEasing = Easing.In; supportFlow.BypassAutoSizeAxes = Axes.X; this From bd0e2b4dde99cd20d990959ba52965cc73099977 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 17:21:29 +0900 Subject: [PATCH 0742/1118] Remove disclaimer screen completely --- .../Visual/Menus/TestSceneDisclaimer.cs | 29 --- osu.Game/Screens/Loader.cs | 13 +- osu.Game/Screens/Menu/Disclaimer.cs | 227 ------------------ 3 files changed, 2 insertions(+), 267 deletions(-) delete mode 100644 osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs delete mode 100644 osu.Game/Screens/Menu/Disclaimer.cs diff --git a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs b/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs deleted file mode 100644 index fb82b0df80..0000000000 --- a/osu.Game.Tests/Visual/Menus/TestSceneDisclaimer.cs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osu.Framework.Allocation; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; -using osu.Game.Screens.Menu; - -namespace osu.Game.Tests.Visual.Menus -{ - public partial class TestSceneDisclaimer : ScreenTestScene - { - [BackgroundDependencyLoader] - private void load() - { - AddStep("load disclaimer", () => LoadScreen(new Disclaimer())); - - AddStep("toggle support", () => - { - ((DummyAPIAccess)API).LocalUser.Value = new APIUser - { - Username = API.LocalUser.Value.Username, - Id = API.LocalUser.Value.Id + 1, - IsSupporter = !API.LocalUser.Value.IsSupporter, - }; - }); - } - } -} diff --git a/osu.Game/Screens/Loader.cs b/osu.Game/Screens/Loader.cs index 372cfe748e..4dba512cbd 100644 --- a/osu.Game/Screens/Loader.cs +++ b/osu.Game/Screens/Loader.cs @@ -21,8 +21,6 @@ namespace osu.Game.Screens { public partial class Loader : StartupScreen { - private bool showDisclaimer; - public Loader() { ValidForResume = false; @@ -35,13 +33,7 @@ namespace osu.Game.Screens private LoadingSpinner spinner; private ScheduledDelegate spinnerShow; - protected virtual OsuScreen CreateLoadableScreen() - { - if (showDisclaimer) - return new Disclaimer(getIntroSequence()); - - return getIntroSequence(); - } + protected virtual OsuScreen CreateLoadableScreen() => getIntroSequence(); private IntroScreen getIntroSequence() { @@ -107,9 +99,8 @@ namespace osu.Game.Screens } [BackgroundDependencyLoader] - private void load(OsuGameBase game, OsuConfigManager config) + private void load(OsuConfigManager config) { - showDisclaimer = game.IsDeployedBuild; introSequence = config.Get(OsuSetting.IntroSequence); } diff --git a/osu.Game/Screens/Menu/Disclaimer.cs b/osu.Game/Screens/Menu/Disclaimer.cs deleted file mode 100644 index 1afef66313..0000000000 --- a/osu.Game/Screens/Menu/Disclaimer.cs +++ /dev/null @@ -1,227 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using System.Collections.Generic; -using System.Linq; -using osu.Framework.Allocation; -using osu.Framework.Bindables; -using osu.Framework.Extensions.IEnumerableExtensions; -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Screens; -using osu.Game.Graphics; -using osu.Game.Graphics.Containers; -using osu.Game.Online.API; -using osu.Game.Online.API.Requests.Responses; -using osuTK; -using osuTK.Graphics; - -namespace osu.Game.Screens.Menu -{ - public partial class Disclaimer : StartupScreen - { - private SpriteIcon icon; - private Color4 iconColour; - private LinkFlowContainer textFlow; - private LinkFlowContainer supportFlow; - - private Drawable heart; - - private const float icon_y = -85; - private const float icon_size = 30; - - private readonly OsuScreen nextScreen; - - private readonly Bindable currentUser = new Bindable(); - private FillFlowContainer fill; - - private readonly List expendableText = new List(); - - public Disclaimer(OsuScreen nextScreen = null) - { - this.nextScreen = nextScreen; - ValidForResume = false; - } - - [Resolved] - private IAPIProvider api { get; set; } - - [BackgroundDependencyLoader] - private void load(OsuColour colours) - { - InternalChildren = new Drawable[] - { - icon = new SpriteIcon - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Icon = OsuIcon.Logo, - Size = new Vector2(icon_size), - Y = icon_y, - }, - fill = new FillFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - Y = icon_y, - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, - Children = new Drawable[] - { - textFlow = new LinkFlowContainer - { - Width = 680, - AutoSizeAxes = Axes.Y, - TextAnchor = Anchor.TopCentre, - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Spacing = new Vector2(0, 2), - }, - } - }, - supportFlow = new LinkFlowContainer - { - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - TextAnchor = Anchor.BottomCentre, - Anchor = Anchor.BottomCentre, - Origin = Anchor.BottomCentre, - Padding = new MarginPadding(20), - Alpha = 0, - Spacing = new Vector2(0, 2), - }, - }; - - textFlow.AddText("this is osu!", t => t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular)); - - expendableText.Add(textFlow.AddText("lazer", t => - { - t.Font = t.Font.With(Typeface.Torus, 30, FontWeight.Regular); - t.Colour = colours.PinkLight; - })); - - static void formatRegular(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.Regular); - static void formatSemiBold(SpriteText t) => t.Font = OsuFont.GetFont(size: 20, weight: FontWeight.SemiBold); - - textFlow.NewParagraph(); - - textFlow.AddText("the next ", formatRegular); - textFlow.AddText("major update", t => - { - t.Font = t.Font.With(Typeface.Torus, 20, FontWeight.SemiBold); - t.Colour = colours.Pink; - }); - expendableText.Add(textFlow.AddText(" coming to osu!", formatRegular)); - textFlow.AddText(".", formatRegular); - - textFlow.NewParagraph(); - textFlow.NewParagraph(); - - textFlow.NewParagraph(); - - iconColour = colours.Yellow; - - // manually transfer the user once, but only do the final bind in LoadComplete to avoid thread woes (API scheduler could run while this screen is still loading). - // the manual transfer is here to ensure all text content is loaded ahead of time as this is very early in the game load process and we want to avoid stutters. - currentUser.Value = api.LocalUser.Value; - currentUser.BindValueChanged(e => - { - supportFlow.Children.ForEach(d => d.FadeOut().Expire()); - - if (e.NewValue.IsSupporter) - { - supportFlow.AddText("Eternal thanks to you for supporting osu!", formatSemiBold); - } - else - { - supportFlow.AddText("Consider becoming an ", formatSemiBold); - supportFlow.AddLink("osu!supporter", "https://osu.ppy.sh/home/support", formatSemiBold); - supportFlow.AddText(" to help support osu!'s development", formatSemiBold); - } - - supportFlow.AddIcon(FontAwesome.Solid.Heart, t => - { - heart = t; - - t.Padding = new MarginPadding { Left = 5, Top = 3 }; - t.Font = t.Font.With(size: 20); - t.Origin = Anchor.Centre; - t.Colour = colours.Pink; - - Schedule(() => heart?.FlashColour(Color4.White, 750, Easing.OutQuint).Loop()); - }); - - if (supportFlow.IsPresent) - supportFlow.FadeInFromZero(500); - }, true); - } - - protected override void LoadComplete() - { - base.LoadComplete(); - if (nextScreen != null) - LoadComponentAsync(nextScreen); - - ((IBindable)currentUser).BindTo(api.LocalUser); - } - - public override void OnSuspending(ScreenTransitionEvent e) - { - base.OnSuspending(e); - - // Once this screen has finished being displayed, we don't want to unnecessarily handle user change events. - currentUser.UnbindAll(); - } - - public override void OnEntering(ScreenTransitionEvent e) - { - base.OnEntering(e); - - icon.RotateTo(10); - icon.FadeOut(); - icon.ScaleTo(0.5f); - - icon.Delay(500).FadeIn(500).ScaleTo(1, 500, Easing.OutQuint); - - using (BeginDelayedSequence(3000)) - { - icon.FadeColour(iconColour, 200, Easing.OutQuint); - icon.MoveToY(icon_y * 1.3f, 500, Easing.OutCirc) - .RotateTo(-360, 520, Easing.OutQuint) - .Then() - .MoveToY(icon_y, 160, Easing.InQuart) - .FadeColour(Color4.White, 160); - - using (BeginDelayedSequence(520 + 160)) - { - fill.MoveToOffset(new Vector2(0, 15), 160, Easing.OutQuart); - Schedule(() => expendableText.SelectMany(t => t.Drawables).ForEach(t => - { - t.FadeOut(100); - t.ScaleTo(new Vector2(0, 1), 100, Easing.OutQuart); - })); - } - } - - supportFlow.FadeOut().Delay(2000).FadeIn(500); - double delay = 500; - foreach (var c in textFlow.Children) - c.FadeTo(0.001f).Delay(delay += 20).FadeIn(500); - - this - .FadeInFromZero(500) - .Then(5500) - .FadeOut(250) - .ScaleTo(0.9f, 250, Easing.InQuint) - .Finally(_ => - { - if (nextScreen != null) - this.Push(nextScreen); - }); - } - } -} From 2ec9343868693f45f7650e2209271b157222d973 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 17:35:10 +0900 Subject: [PATCH 0743/1118] Add the ability to spectate a user by right clicking their user panel --- osu.Game/Localisation/ContextMenuStrings.cs | 9 +++-- osu.Game/Users/UserPanel.cs | 39 +++++++++++++-------- 2 files changed, 31 insertions(+), 17 deletions(-) diff --git a/osu.Game/Localisation/ContextMenuStrings.cs b/osu.Game/Localisation/ContextMenuStrings.cs index 029fba67d8..cb18a2159c 100644 --- a/osu.Game/Localisation/ContextMenuStrings.cs +++ b/osu.Game/Localisation/ContextMenuStrings.cs @@ -20,9 +20,14 @@ namespace osu.Game.Localisation public static LocalisableString ViewBeatmap => new TranslatableString(getKey(@"view_beatmap"), @"View beatmap"); /// - /// "Invite player" + /// "Invite to room" /// - public static LocalisableString InvitePlayer => new TranslatableString(getKey(@"invite_player"), @"Invite player"); + public static LocalisableString InvitePlayer => new TranslatableString(getKey(@"invite_player"), @"Invite to room"); + + /// + /// "Spectate" + /// + public static LocalisableString SpectatePlayer => new TranslatableString(getKey(@"spectate_player"), @"Spectate"); private static string getKey(string key) => $@"{prefix}:{key}"; } diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index 273faf9bd1..b6a77e754d 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -13,6 +13,7 @@ using osu.Game.Overlays; using osu.Framework.Graphics.UserInterface; using osu.Game.Graphics.UserInterface; using osu.Framework.Graphics.Cursor; +using osu.Framework.Screens; using osu.Game.Graphics.Containers; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; @@ -20,6 +21,8 @@ using osu.Game.Online.Chat; using osu.Game.Resources.Localisation.Web; using osu.Game.Localisation; using osu.Game.Online.Multiplayer; +using osu.Game.Screens; +using osu.Game.Screens.Play; namespace osu.Game.Users { @@ -60,6 +63,9 @@ namespace osu.Game.Users [Resolved] protected OverlayColourProvider? ColourProvider { get; private set; } + [Resolved] + private IPerformFromScreenRunner? performer { get; set; } + [Resolved] protected OsuColour Colours { get; private set; } = null!; @@ -113,23 +119,26 @@ namespace osu.Game.Users new OsuMenuItem(ContextMenuStrings.ViewProfile, MenuItemType.Highlighted, ViewProfile) }; - if (!User.Equals(api.LocalUser.Value)) - { - items.Add(new OsuMenuItem(UsersStrings.CardSendMessage, MenuItemType.Standard, () => - { - channelManager?.OpenPrivateChannel(User); - chatOverlay?.Show(); - })); - } + if (User.Equals(api.LocalUser.Value)) + return items.ToArray(); - if ( - // TODO: uncomment this once lazer / osu-web is updating online states - // User.IsOnline && - multiplayerClient?.Room != null && - multiplayerClient.Room.Users.All(u => u.UserID != User.Id) - ) + items.Add(new OsuMenuItem(UsersStrings.CardSendMessage, MenuItemType.Standard, () => { - items.Add(new OsuMenuItem(ContextMenuStrings.InvitePlayer, MenuItemType.Standard, () => multiplayerClient.InvitePlayer(User.Id))); + channelManager?.OpenPrivateChannel(User); + chatOverlay?.Show(); + })); + + if (User.IsOnline) + { + items.Add(new OsuMenuItem(ContextMenuStrings.SpectatePlayer, MenuItemType.Standard, () => + { + performer?.PerformFromScreen(s => s.Push(new SoloSpectatorScreen(User))); + })); + + if (multiplayerClient?.Room?.Users.All(u => u.UserID != User.Id) == true) + { + items.Add(new OsuMenuItem(ContextMenuStrings.InvitePlayer, MenuItemType.Standard, () => multiplayerClient.InvitePlayer(User.Id))); + } } return items.ToArray(); From 22eced33006f959af2bc17198eafd46779a4923b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 17:40:07 +0900 Subject: [PATCH 0744/1118] Show local user in online users --- osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs | 3 --- 1 file changed, 3 deletions(-) diff --git a/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs b/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs index 37ea3f9c38..ee277ff538 100644 --- a/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs +++ b/osu.Game/Overlays/Dashboard/CurrentlyOnlineDisplay.cs @@ -131,9 +131,6 @@ namespace osu.Game.Overlays.Dashboard { int userId = kvp.Key; - if (userId == api.LocalUser.Value.Id) - continue; - users.GetUserAsync(userId).ContinueWith(task => { APIUser user = task.GetResultSafely(); From 28e220ca501592f854aab01b294981ca4d83154e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 19:04:19 +0900 Subject: [PATCH 0745/1118] Update popup dialog design Had to be done. I hated the old ones so much. As usual, disclaimer that this is an iterative design and will probably be replaced in the future. --- .../UserInterface/TestSceneDialogOverlay.cs | 19 ++--- .../Graphics/UserInterface/DialogButton.cs | 14 ++-- osu.Game/Overlays/Dialog/PopupDialog.cs | 77 +++++++++++-------- osu.Game/Overlays/DialogOverlay.cs | 12 +-- 4 files changed, 68 insertions(+), 54 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index 81b692004b..3b38343f04 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -9,6 +9,7 @@ using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; +using osu.Framework.Testing; using osu.Game.Overlays; using osu.Game.Overlays.Dialog; @@ -19,11 +20,15 @@ namespace osu.Game.Tests.Visual.UserInterface { private DialogOverlay overlay; + [SetUpSteps] + public void SetUpSteps() + { + AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay()); + } + [Test] public void TestBasic() { - AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay()); - TestPopupDialog firstDialog = null; TestPopupDialog secondDialog = null; @@ -84,7 +89,7 @@ namespace osu.Game.Tests.Visual.UserInterface })); AddAssert("second dialog displayed", () => overlay.CurrentDialog == secondDialog); - AddAssert("first dialog is not part of hierarchy", () => firstDialog.Parent == null); + AddUntilStep("first dialog is not part of hierarchy", () => firstDialog.Parent == null); } [Test] @@ -92,7 +97,7 @@ namespace osu.Game.Tests.Visual.UserInterface { PopupDialog dialog = null; - AddStep("create dialog overlay", () => overlay = new SlowLoadingDialogOverlay()); + AddStep("create slow loading dialog overlay", () => overlay = new SlowLoadingDialogOverlay()); AddStep("start loading overlay", () => LoadComponentAsync(overlay, Add)); @@ -128,8 +133,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestDismissBeforePush() { - AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay()); - TestPopupDialog testDialog = null; AddStep("dismissed dialog push", () => { @@ -146,8 +149,6 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestDismissBeforePushViaButtonPress() { - AddStep("create dialog overlay", () => Child = overlay = new DialogOverlay()); - TestPopupDialog testDialog = null; AddStep("dismissed dialog push", () => { @@ -163,7 +164,7 @@ namespace osu.Game.Tests.Visual.UserInterface }); AddAssert("no dialog pushed", () => overlay.CurrentDialog == null); - AddAssert("dialog is not part of hierarchy", () => testDialog.Parent == null); + AddUntilStep("dialog is not part of hierarchy", () => testDialog.Parent == null); } private partial class TestPopupDialog : PopupDialog diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index db81bc991d..c920597a95 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -25,7 +25,7 @@ namespace osu.Game.Graphics.UserInterface private const float idle_width = 0.8f; private const float hover_width = 0.9f; - private const float hover_duration = 500; + private const float hover_duration = 300; private const float click_duration = 200; public event Action? StateChanged; @@ -54,7 +54,7 @@ namespace osu.Game.Graphics.UserInterface private readonly Box rightGlow; private readonly Box background; private readonly SpriteText spriteText; - private Vector2 hoverSpacing => new Vector2(3f, 0f); + private Vector2 hoverSpacing => new Vector2(1.4f, 0f); public DialogButton(HoverSampleSet sampleSet = HoverSampleSet.Button) : base(sampleSet) @@ -279,15 +279,15 @@ namespace osu.Game.Graphics.UserInterface if (newState == SelectionState.Selected) { - spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutElastic); - ColourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutElastic); + spriteText.TransformSpacingTo(hoverSpacing, hover_duration, Easing.OutQuint); + ColourContainer.ResizeWidthTo(hover_width, hover_duration, Easing.OutQuint); glowContainer.FadeIn(hover_duration, Easing.OutQuint); } else { - ColourContainer.ResizeWidthTo(idle_width, hover_duration, Easing.OutElastic); - spriteText.TransformSpacingTo(Vector2.Zero, hover_duration, Easing.OutElastic); - glowContainer.FadeOut(hover_duration, Easing.OutQuint); + ColourContainer.ResizeWidthTo(idle_width, hover_duration / 2, Easing.OutQuint); + spriteText.TransformSpacingTo(Vector2.Zero, hover_duration / 2, Easing.OutQuint); + glowContainer.FadeOut(hover_duration / 2, Easing.OutQuint); } } diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 36a9baac67..663c2e78ce 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -14,6 +14,7 @@ using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.Sprites; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Game.Graphics; using osu.Game.Graphics.Backgrounds; using osu.Game.Graphics.Containers; using osuTK; @@ -25,11 +26,10 @@ namespace osu.Game.Overlays.Dialog public abstract partial class PopupDialog : VisibilityContainer { public const float ENTER_DURATION = 500; - public const float EXIT_DURATION = 200; + public const float EXIT_DURATION = 500; private readonly Vector2 ringSize = new Vector2(100f); private readonly Vector2 ringMinifiedSize = new Vector2(20f); - private readonly Vector2 buttonsEnterSpacing = new Vector2(0f, 50f); private readonly Box flashLayer; private Sample flashSample = null!; @@ -108,13 +108,20 @@ namespace osu.Game.Overlays.Dialog protected PopupDialog() { - RelativeSizeAxes = Axes.Both; + RelativeSizeAxes = Axes.X; + AutoSizeAxes = Axes.Y; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; Children = new Drawable[] { content = new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, Alpha = 0f, Children = new Drawable[] { @@ -122,11 +129,13 @@ namespace osu.Game.Overlays.Dialog { RelativeSizeAxes = Axes.Both, Masking = true, + CornerRadius = 20, + CornerExponent = 2.5f, EdgeEffect = new EdgeEffectParameters { Type = EdgeEffectType.Shadow, - Colour = Color4.Black.Opacity(0.5f), - Radius = 8, + Colour = Color4.Black.Opacity(0.2f), + Radius = 14, }, Children = new Drawable[] { @@ -142,23 +151,29 @@ namespace osu.Game.Overlays.Dialog ColourDark = Color4Extensions.FromHex(@"1e171e"), TriangleScale = 4, }, + flashLayer = new Box + { + Alpha = 0, + RelativeSizeAxes = Axes.Both, + Blending = BlendingParameters.Additive, + Colour = Color4Extensions.FromHex(@"221a21"), + }, }, }, new FillFlowContainer { - Anchor = Anchor.Centre, - Origin = Anchor.BottomCentre, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, Direction = FillDirection.Vertical, Spacing = new Vector2(0f, 10f), - Padding = new MarginPadding { Bottom = 10 }, + Padding = new MarginPadding { Vertical = 60 }, Children = new Drawable[] { new Container { Origin = Anchor.TopCentre, Anchor = Anchor.TopCentre, + Padding = new MarginPadding { Bottom = 30 }, Size = ringSize, Children = new Drawable[] { @@ -181,6 +196,7 @@ namespace osu.Game.Overlays.Dialog Origin = Anchor.Centre, Anchor = Anchor.Centre, Icon = FontAwesome.Solid.TimesCircle, + Y = -2, Size = new Vector2(50), }, }, @@ -202,25 +218,18 @@ namespace osu.Game.Overlays.Dialog TextAnchor = Anchor.TopCentre, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding(5), + }, + buttonsContainer = new FillFlowContainer + { + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Vertical, + Padding = new MarginPadding { Top = 30 }, }, }, }, - buttonsContainer = new FillFlowContainer - { - Anchor = Anchor.Centre, - Origin = Anchor.TopCentre, - RelativeSizeAxes = Axes.X, - AutoSizeAxes = Axes.Y, - Direction = FillDirection.Vertical, - }, - flashLayer = new Box - { - Alpha = 0, - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - Colour = Color4Extensions.FromHex(@"221a21"), - }, }, }, }; @@ -231,7 +240,7 @@ namespace osu.Game.Overlays.Dialog } [BackgroundDependencyLoader] - private void load(AudioManager audio) + private void load(AudioManager audio, OsuColour colours) { flashSample = audio.Samples.Get(@"UI/default-select-disabled"); } @@ -288,15 +297,15 @@ namespace osu.Game.Overlays.Dialog // Reset various animations but only if the dialog animation fully completed if (content.Alpha == 0) { - buttonsContainer.TransformSpacingTo(buttonsEnterSpacing); - buttonsContainer.MoveToY(buttonsEnterSpacing.Y); + content.ScaleTo(0.7f); ring.ResizeTo(ringMinifiedSize); } - content.FadeIn(ENTER_DURATION, Easing.OutQuint); - ring.ResizeTo(ringSize, ENTER_DURATION, Easing.OutQuint); - buttonsContainer.TransformSpacingTo(Vector2.Zero, ENTER_DURATION, Easing.OutQuint); - buttonsContainer.MoveToY(0, ENTER_DURATION, Easing.OutQuint); + content + .ScaleTo(1, 750, Easing.OutElasticHalf) + .FadeIn(ENTER_DURATION, Easing.OutQuint); + + ring.ResizeTo(ringSize, ENTER_DURATION * 1.5f, Easing.OutQuint); } protected override void PopOut() @@ -306,7 +315,9 @@ namespace osu.Game.Overlays.Dialog // This is presumed to always be a sane default "cancel" action. buttonsContainer.Last().TriggerClick(); - content.FadeOut(EXIT_DURATION, Easing.InSine); + content + .ScaleTo(0.7f, EXIT_DURATION, Easing.Out) + .FadeOut(EXIT_DURATION, Easing.OutQuint); } private void pressButtonAtIndex(int index) diff --git a/osu.Game/Overlays/DialogOverlay.cs b/osu.Game/Overlays/DialogOverlay.cs index 005162bbcc..a85f1ecbcd 100644 --- a/osu.Game/Overlays/DialogOverlay.cs +++ b/osu.Game/Overlays/DialogOverlay.cs @@ -29,16 +29,18 @@ namespace osu.Game.Overlays public DialogOverlay() { - RelativeSizeAxes = Axes.Both; + AutoSizeAxes = Axes.Y; Child = dialogContainer = new Container { - RelativeSizeAxes = Axes.Both, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, }; - Width = 0.4f; - Anchor = Anchor.BottomCentre; - Origin = Anchor.BottomCentre; + Width = 500; + + Anchor = Anchor.Centre; + Origin = Anchor.Centre; } [BackgroundDependencyLoader] From b7f3c83514deec96959b51bcbb227ec0ee4f8801 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 20:01:29 +0900 Subject: [PATCH 0746/1118] Expose the ability to update global offset from the player loader screen --- osu.Game/Overlays/SettingsOverlay.cs | 29 +++++++++++++++---- .../PlayerSettings/BeatmapOffsetControl.cs | 19 ++++++++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/osu.Game/Overlays/SettingsOverlay.cs b/osu.Game/Overlays/SettingsOverlay.cs index 5735b1515a..cf76ed8781 100644 --- a/osu.Game/Overlays/SettingsOverlay.cs +++ b/osu.Game/Overlays/SettingsOverlay.cs @@ -3,19 +3,21 @@ #nullable disable +using System.Collections.Generic; +using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Localisation; +using osu.Framework.Testing; +using osu.Game.Graphics; +using osu.Game.Localisation; using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; using osu.Game.Overlays.Settings.Sections.Input; using osuTK.Graphics; -using System.Collections.Generic; -using osu.Framework.Bindables; -using osu.Framework.Graphics.Sprites; -using osu.Framework.Localisation; -using osu.Game.Graphics; -using osu.Game.Localisation; namespace osu.Game.Overlays { @@ -55,6 +57,21 @@ namespace osu.Game.Overlays public override bool AcceptsFocus => lastOpenedSubPanel == null || lastOpenedSubPanel.State.Value == Visibility.Hidden; + public void ShowAtControl() + where T : Drawable + { + Show(); + + // wait for load of sections + if (!SectionsContainer.Any()) + { + Scheduler.Add(ShowAtControl); + return; + } + + SectionsContainer.ScrollTo(SectionsContainer.ChildrenOfType().Single()); + } + private T createSubPanel(T subPanel) where T : SettingsSubPanel { diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 3f0f0fd1df..8c6f1a8f7f 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -21,7 +21,9 @@ using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; using osu.Game.Input.Bindings; using osu.Game.Localisation; +using osu.Game.Overlays; using osu.Game.Overlays.Settings; +using osu.Game.Overlays.Settings.Sections.Audio; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -213,6 +215,8 @@ namespace osu.Game.Screens.Play.PlayerSettings lastPlayAverage = average; lastPlayBeatmapOffset = Current.Value; + LinkFlowContainer globalOffsetText; + referenceScoreContainer.AddRange(new Drawable[] { lastPlayGraph = new HitEventTimingDistributionGraph(hitEvents) @@ -226,9 +230,24 @@ namespace osu.Game.Screens.Play.PlayerSettings Text = BeatmapOffsetControlStrings.CalibrateUsingLastPlay, Action = () => Current.Value = lastPlayBeatmapOffset - lastPlayAverage }, + globalOffsetText = new LinkFlowContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + } }); + + if (settings != null) + { + globalOffsetText.AddText("You can also "); + globalOffsetText.AddLink("adjust the global offset", () => settings.ShowAtControl()); + globalOffsetText.AddText(" based off this play."); + } } + [Resolved] + private SettingsOverlay? settings { get; set; } + protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); From 91af94086c6ba2905779c07ebb35dc011979993f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 20:02:06 +0900 Subject: [PATCH 0747/1118] Remove offset wizard button for now --- osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs index af15d310fc..e05d20a5db 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/OffsetSettings.cs @@ -15,7 +15,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { protected override LocalisableString Header => AudioSettingsStrings.OffsetHeader; - public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "universal", "uo", "timing", "delay", "latency" }); + public override IEnumerable FilterTerms => base.FilterTerms.Concat(new LocalisableString[] { "universal", "uo", "timing", "delay", "latency", "wizard" }); [BackgroundDependencyLoader] private void load(OsuConfigManager config) @@ -26,10 +26,6 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { Current = config.GetBindable(OsuSetting.AudioOffset), }, - new SettingsButton - { - Text = AudioSettingsStrings.OffsetWizard - } }; } } From e1a376c0a742d958399734683c8eb79631f37add Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 20:13:19 +0900 Subject: [PATCH 0748/1118] Fix using "Back" binding at spectator fail screen not working --- osu.Game/Screens/Play/GameplayMenuOverlay.cs | 10 +++++++++- osu.Game/Screens/Play/PauseOverlay.cs | 8 +++++++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/GameplayMenuOverlay.cs b/osu.Game/Screens/Play/GameplayMenuOverlay.cs index 0680842891..440b8d37b9 100644 --- a/osu.Game/Screens/Play/GameplayMenuOverlay.cs +++ b/osu.Game/Screens/Play/GameplayMenuOverlay.cs @@ -44,7 +44,15 @@ namespace osu.Game.Screens.Play /// /// Action that is invoked when is triggered. /// - protected virtual Action BackAction => () => InternalButtons.LastOrDefault()?.TriggerClick(); + protected virtual Action BackAction => () => + { + // We prefer triggering the button click as it will animate... + // but sometimes buttons aren't present (see FailOverlay's constructor as an example). + if (Buttons.Any()) + Buttons.Last().TriggerClick(); + else + OnQuit?.Invoke(); + }; /// /// Action that is invoked when is triggered. diff --git a/osu.Game/Screens/Play/PauseOverlay.cs b/osu.Game/Screens/Play/PauseOverlay.cs index 88561ada71..2aa2793fd4 100644 --- a/osu.Game/Screens/Play/PauseOverlay.cs +++ b/osu.Game/Screens/Play/PauseOverlay.cs @@ -29,7 +29,13 @@ namespace osu.Game.Screens.Play private SkinnableSound pauseLoop; - protected override Action BackAction => () => InternalButtons.First().TriggerClick(); + protected override Action BackAction => () => + { + if (Buttons.Any()) + Buttons.First().TriggerClick(); + else + OnResume?.Invoke(); + }; [BackgroundDependencyLoader] private void load(OsuColour colours) From f8347288c1a5cdc5ab6587a49e3980075eec4872 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 20:29:56 +0900 Subject: [PATCH 0749/1118] Add padding around text in dialogs --- .../UserInterface/TestSceneDialogOverlay.cs | 24 +++++++++++++++++++ osu.Game/Overlays/Dialog/PopupDialog.cs | 2 ++ 2 files changed, 26 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs index 3b38343f04..f2313022ec 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneDialogOverlay.cs @@ -92,6 +92,30 @@ namespace osu.Game.Tests.Visual.UserInterface AddUntilStep("first dialog is not part of hierarchy", () => firstDialog.Parent == null); } + [Test] + public void TestTooMuchText() + { + AddStep("dialog #1", () => overlay.Push(new TestPopupDialog + { + Icon = FontAwesome.Regular.TrashAlt, + HeaderText = @"Confirm deletion ofConfirm deletion ofConfirm deletion ofConfirm deletion ofConfirm deletion ofConfirm deletion of", + BodyText = @"Ayase Rie - Yuima-ru*World TVver.Ayase Rie - Yuima-ru*World TVver.Ayase Rie - Yuima-ru*World TVver.Ayase Rie - Yuima-ru*World TVver.Ayase Rie - Yuima-ru*World TVver.Ayase Rie - Yuima-ru*World TVver.Ayase Rie - Yuima-ru*World TVver.Ayase Rie - Yuima-ru*World TVver.Ayase Rie - Yuima-ru*World TVver. ", + Buttons = new PopupDialogButton[] + { + new PopupDialogOkButton + { + Text = @"I never want to see this again.", + Action = () => Console.WriteLine(@"OK"), + }, + new PopupDialogCancelButton + { + Text = @"Firetruck, I still want quick ranks!", + Action = () => Console.WriteLine(@"Cancel"), + }, + }, + })); + } + [Test] public void TestPushBeforeLoad() { diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 663c2e78ce..4048b35e78 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -210,6 +210,7 @@ namespace osu.Game.Overlays.Dialog RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, TextAnchor = Anchor.TopCentre, + Padding = new MarginPadding { Horizontal = 5 }, }, body = new OsuTextFlowContainer(t => t.Font = t.Font.With(size: 18)) { @@ -218,6 +219,7 @@ namespace osu.Game.Overlays.Dialog TextAnchor = Anchor.TopCentre, RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Horizontal = 5 }, }, buttonsContainer = new FillFlowContainer { From 6bc0f02e2d46296fe2838cf97d57a3905d739928 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 28 Dec 2023 20:40:51 +0900 Subject: [PATCH 0750/1118] Fix test initialising dialogs without cleaning up --- .../Visual/UserInterface/TestScenePopupDialog.cs | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs index 9537ab63be..37ff66aa35 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs @@ -1,10 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using NUnit.Framework; -using osu.Framework.Graphics; using osu.Framework.Graphics.Sprites; using osu.Framework.Testing; using osu.Game.Overlays.Dialog; @@ -15,18 +12,17 @@ namespace osu.Game.Tests.Visual.UserInterface { public partial class TestScenePopupDialog : OsuManualInputManagerTestScene { - private TestPopupDialog dialog; + private TestPopupDialog dialog = null!; [SetUpSteps] public void SetUpSteps() { AddStep("new popup", () => { - Add(dialog = new TestPopupDialog + Child = dialog = new TestPopupDialog { - RelativeSizeAxes = Axes.Both, State = { Value = Framework.Graphics.Containers.Visibility.Visible }, - }); + }; }); } From bb0737837b537fd7d97f276e48aaae871f0abc5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Dec 2023 13:58:00 +0100 Subject: [PATCH 0751/1118] Fix test failing due to querying button position during transform --- osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs index 37ff66aa35..96d19911bd 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestScenePopupDialog.cs @@ -29,6 +29,8 @@ namespace osu.Game.Tests.Visual.UserInterface [Test] public void TestDangerousButton([Values(false, true)] bool atEdge) { + AddStep("finish transforms", () => dialog.FinishTransforms(true)); + if (atEdge) { AddStep("move mouse to button edge", () => From e6f1d7db4446e9e0c2308b5d17c937c65a4f7a27 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Dec 2023 14:10:03 +0100 Subject: [PATCH 0752/1118] Move `SessionAverageHitErrorTracker` to more general namespace --- .../Audio => Configuration}/SessionAverageHitErrorTracker.cs | 3 +-- .../Settings/Sections/Audio/AudioOffsetAdjustControl.cs | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) rename osu.Game/{Overlays/Settings/Sections/Audio => Configuration}/SessionAverageHitErrorTracker.cs (95%) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/SessionAverageHitErrorTracker.cs b/osu.Game/Configuration/SessionAverageHitErrorTracker.cs similarity index 95% rename from osu.Game/Overlays/Settings/Sections/Audio/SessionAverageHitErrorTracker.cs rename to osu.Game/Configuration/SessionAverageHitErrorTracker.cs index b714d49b89..13b1ea7d37 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/SessionAverageHitErrorTracker.cs +++ b/osu.Game/Configuration/SessionAverageHitErrorTracker.cs @@ -5,12 +5,11 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; -using osu.Game.Configuration; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; -namespace osu.Game.Overlays.Settings.Sections.Audio +namespace osu.Game.Configuration { /// /// Tracks the local user's average hit error during the ongoing play session. diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs index 0023c60c61..e10de58a6c 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs @@ -12,6 +12,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; From 6718de01ecc7397f3804954f0c01aaa81d1fffdb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Dec 2023 14:11:08 +0100 Subject: [PATCH 0753/1118] Suggest audio adjust after one play --- .../Settings/Sections/Audio/AudioOffsetAdjustControl.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs index e10de58a6c..98acea1f2e 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs @@ -143,7 +143,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio break; } - suggestedOffset.Value = averageHitErrorHistory.Count < 3 ? null : -averageHitErrorHistory.Average(); + suggestedOffset.Value = averageHitErrorHistory.Any() ? -averageHitErrorHistory.Average() : null; } private float getXPositionForAverage(double average) => (float)(Math.Clamp(-average, current.MinValue, current.MaxValue) / (2 * current.MaxValue)); @@ -152,7 +152,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { hintText.Text = suggestedOffset.Value == null ? @"Play a few beatmaps to receive a suggested offset!" - : $@"Based on the last {averageHitErrorHistory.Count} plays, the suggested offset is {suggestedOffset.Value:N0} ms."; + : $@"Based on the last {averageHitErrorHistory.Count} play(s), the suggested offset is {suggestedOffset.Value:N0} ms."; applySuggestion.Enabled.Value = suggestedOffset.Value != null; } } From 619b0cc69b1998fdc8b1b1fcb69bcddaefc08004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Dec 2023 14:12:28 +0100 Subject: [PATCH 0754/1118] Fix code quality inspection --- .../Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 8c6f1a8f7f..8efb80e771 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -160,11 +160,11 @@ namespace osu.Game.Screens.Play.PlayerSettings // Apply to all difficulties in a beatmap set for now (they generally always share timing). foreach (var b in setInfo.Beatmaps) { - BeatmapUserSettings settings = b.UserSettings; + BeatmapUserSettings userSettings = b.UserSettings; double val = Current.Value; - if (settings.Offset != val) - settings.Offset = val; + if (userSettings.Offset != val) + userSettings.Offset = val; } }); } From 6d124513e7d88aba2849b5494783820eeb476777 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Dec 2023 14:15:15 +0100 Subject: [PATCH 0755/1118] Fix test failures due to player bailing early after failing to load beatmap --- osu.Game/Screens/Play/SubmittingPlayer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index ff2c57bf35..83adf1f960 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -180,7 +180,7 @@ namespace osu.Game.Screens.Play { bool exiting = base.OnExiting(e); submitFromFailOrQuit(); - statics.SetValue(Static.LastLocalUserScore, Score.ScoreInfo.DeepClone()); + statics.SetValue(Static.LastLocalUserScore, Score?.ScoreInfo.DeepClone()); return exiting; } From 93c7ebdae3f61e95e36cc61c014d71fb67e8a3b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Dec 2023 14:30:11 +0100 Subject: [PATCH 0756/1118] Remove unused using --- osu.Game/OsuGameBase.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/OsuGameBase.cs b/osu.Game/OsuGameBase.cs index 90c88b2b33..4e465f59df 100644 --- a/osu.Game/OsuGameBase.cs +++ b/osu.Game/OsuGameBase.cs @@ -55,7 +55,6 @@ using osu.Game.Online.Spectator; using osu.Game.Overlays; using osu.Game.Overlays.Settings; using osu.Game.Overlays.Settings.Sections; -using osu.Game.Overlays.Settings.Sections.Audio; using osu.Game.Overlays.Settings.Sections.Input; using osu.Game.Resources; using osu.Game.Rulesets; From 68402bfd113b3c4051aead283fe3cf57d5b9ca78 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Dec 2023 20:32:33 +0100 Subject: [PATCH 0757/1118] Add test coverage of failure scenario --- osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs index 85aa14f33e..46570e7cdf 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs @@ -24,6 +24,11 @@ namespace osu.Game.Tests.Visual.Menus Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", Url = @"https://osu.ppy.sh/community/contests/189", }); + AddStep("set title with nonexistent image", () => Game.ChildrenOfType().Single().Current.Value = new APISystemTitle + { + Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2 + Url = @"https://osu.ppy.sh/community/contests/189", + }); AddStep("unset system title", () => Game.ChildrenOfType().Single().Current.Value = null); } } From 7a10e132eaea162febb65d03baa16067a1244e80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 28 Dec 2023 20:33:56 +0100 Subject: [PATCH 0758/1118] Fix crash when retrieval of system title image fails Closes https://github.com/ppy/osu/issues/26194. --- osu.Game/Screens/Menu/SystemTitle.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index 060e426f8c..7d49a1dd5f 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -138,8 +138,8 @@ namespace osu.Game.Screens.Menu [BackgroundDependencyLoader] private void load(LargeTextureStore textureStore) { - var texture = textureStore.Get(SystemTitle.Image); - if (SystemTitle.Image.Contains(@"@2x")) + Texture? texture = textureStore.Get(SystemTitle.Image); + if (texture != null && SystemTitle.Image.Contains(@"@2x")) texture.ScaleAdjust *= 2; AutoSizeAxes = Axes.Both; From 637119f7d4ae7142d9cef29e8a2cd22000c95fb1 Mon Sep 17 00:00:00 2001 From: iminlikewithyou Date: Thu, 28 Dec 2023 17:15:23 -0600 Subject: [PATCH 0759/1118] increase the base size of button icons --- osu.Game/Screens/Menu/MainMenuButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index bc638b44ac..a9927541f1 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -125,7 +125,7 @@ namespace osu.Game.Screens.Menu Shadow = true, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(30), + Size = new Vector2(32), Position = new Vector2(0, 0), Icon = symbol }, From 4760c6aaee2bef3a47329158c612d9ea231d4e3b Mon Sep 17 00:00:00 2001 From: iminlikewithyou Date: Thu, 28 Dec 2023 17:17:24 -0600 Subject: [PATCH 0760/1118] move icon upwards to be visually centered --- osu.Game/Screens/Menu/MainMenuButton.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index a9927541f1..87136675a2 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -126,7 +126,7 @@ namespace osu.Game.Screens.Menu Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(32), - Position = new Vector2(0, 0), + Position = new Vector2(0, -4), Icon = symbol }, new OsuSpriteText @@ -186,7 +186,7 @@ namespace osu.Game.Screens.Menu { icon.ClearTransforms(); icon.RotateTo(0, 500, Easing.Out); - icon.MoveTo(Vector2.Zero, 500, Easing.Out); + icon.MoveTo(new Vector2(0, -4), 500, Easing.Out); icon.ScaleTo(Vector2.One, 200, Easing.Out); if (State == ButtonState.Expanded) From f1f1221e0e4f5e83f89b74df1c6e1b1c3ce4e04d Mon Sep 17 00:00:00 2001 From: iminlikewithyou Date: Thu, 28 Dec 2023 17:18:41 -0600 Subject: [PATCH 0761/1118] move text left to be visually centered in the skewed rectangle --- osu.Game/Screens/Menu/MainMenuButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index 87136675a2..69dbaa8ed8 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -135,7 +135,7 @@ namespace osu.Game.Screens.Menu AllowMultiline = false, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Position = new Vector2(0, 35), + Position = new Vector2(-3, 35), Text = text } } From 51d26d2d715f463a13b47d4692daac3d13a9319d Mon Sep 17 00:00:00 2001 From: iminlikewithyou Date: Thu, 28 Dec 2023 17:20:44 -0600 Subject: [PATCH 0762/1118] make the hover scale bigger as a consequence, the rotation needs to be tweaked to be lower --- osu.Game/Screens/Menu/MainMenuButton.cs | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index 69dbaa8ed8..3d56a75ca8 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -1,4 +1,4 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. using System; @@ -31,6 +31,9 @@ namespace osu.Game.Screens.Menu /// public partial class MainMenuButton : BeatSyncedContainer, IStateful { + public const float BOUNCE_COMPRESSION = 0.9f; + public const float HOVER_SCALE = 1.2f; + public const float BOUNCE_ROTATION = 8; public event Action? StateChanged; public readonly Key[] TriggerKeys; @@ -153,14 +156,14 @@ namespace osu.Game.Screens.Menu double duration = timingPoint.BeatLength / 2; - icon.RotateTo(rightward ? 10 : -10, duration * 2, Easing.InOutSine); + icon.RotateTo(rightward ? BOUNCE_ROTATION : -BOUNCE_ROTATION, duration * 2, Easing.InOutSine); icon.Animate( i => i.MoveToY(-10, duration, Easing.Out), - i => i.ScaleTo(1, duration, Easing.Out) + i => i.ScaleTo(HOVER_SCALE, duration, Easing.Out) ).Then( i => i.MoveToY(0, duration, Easing.In), - i => i.ScaleTo(new Vector2(1, 0.9f), duration, Easing.In) + i => i.ScaleTo(new Vector2(HOVER_SCALE, HOVER_SCALE * BOUNCE_COMPRESSION), duration, Easing.In) ); rightward = !rightward; @@ -177,8 +180,8 @@ namespace osu.Game.Screens.Menu double duration = TimeUntilNextBeat; icon.ClearTransforms(); - icon.RotateTo(rightward ? -10 : 10, duration, Easing.InOutSine); - icon.ScaleTo(new Vector2(1, 0.9f), duration, Easing.Out); + icon.RotateTo(rightward ? -BOUNCE_ROTATION : BOUNCE_ROTATION, duration, Easing.InOutSine); + icon.ScaleTo(new Vector2(HOVER_SCALE, HOVER_SCALE * BOUNCE_COMPRESSION), duration, Easing.Out); return true; } From c68a850325e36b8585b2d60f25be81deef52565a Mon Sep 17 00:00:00 2001 From: Nitrous Date: Fri, 29 Dec 2023 08:51:54 +0800 Subject: [PATCH 0763/1118] Make menu tip about mod select more up to date. --- osu.Game/Screens/Menu/MenuTip.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MenuTip.cs b/osu.Game/Screens/Menu/MenuTip.cs index 325fb07767..ab09c07e64 100644 --- a/osu.Game/Screens/Menu/MenuTip.cs +++ b/osu.Game/Screens/Menu/MenuTip.cs @@ -103,7 +103,7 @@ namespace osu.Game.Screens.Menu "What used to be \"osu!direct\" is available to all users just like on the website. You can access it anywhere using Ctrl-B!", "Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen!", "Multithreading support means that even with low \"FPS\" your input and judgements will be accurate!", - "Try scrolling down in the mod select panel to find a bunch of new fun mods!", + "Try scrolling right in mod select to find a bunch of new fun mods!", "Most of the web content (profiles, rankings, etc.) are available natively in-game from the icons on the toolbar!", "Get more details, hide or delete a beatmap by right-clicking on its panel at song select!", "All delete operations are temporary until exiting. Restore accidentally deleted content from the maintenance settings!", From 150bf670649e61ac071796fa4056aa372ff9023b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Dec 2023 05:30:50 +0300 Subject: [PATCH 0764/1118] Fix dropdown colour not updating correctly on enabled state changes --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index 632036fef9..c99a21ac28 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -363,6 +363,7 @@ namespace osu.Game.Graphics.UserInterface base.LoadComplete(); SearchBar.State.ValueChanged += _ => updateColour(); + Enabled.BindValueChanged(_ => updateColour()); updateColour(); } From c147ec0a98b22bec0a4635e1fc65cc32c5b34ddf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Dec 2023 05:31:13 +0300 Subject: [PATCH 0765/1118] Update dropdown disabled state to match with other components --- osu.Game/Graphics/UserInterface/OsuDropdown.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Graphics/UserInterface/OsuDropdown.cs b/osu.Game/Graphics/UserInterface/OsuDropdown.cs index c99a21ac28..2dc701dc9d 100644 --- a/osu.Game/Graphics/UserInterface/OsuDropdown.cs +++ b/osu.Game/Graphics/UserInterface/OsuDropdown.cs @@ -384,6 +384,9 @@ namespace osu.Game.Graphics.UserInterface var hoveredColour = colourProvider?.Light4 ?? colours.PinkDarker; var unhoveredColour = colourProvider?.Background5 ?? Color4.Black.Opacity(0.5f); + Colour = Color4.White; + Alpha = Enabled.Value ? 1 : 0.3f; + if (SearchBar.State.Value == Visibility.Visible) { Icon.Colour = hovered ? hoveredColour.Lighten(0.5f) : Colour4.White; From a6313c4ee8b78361d052304a3469fc9bf229edfb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Fri, 29 Dec 2023 17:16:16 +0900 Subject: [PATCH 0766/1118] Expose `Mod.UsesDefaultConfiguration` --- osu.Game/Rulesets/Mods/Mod.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Mods/Mod.cs b/osu.Game/Rulesets/Mods/Mod.cs index d635dccab1..0500b49513 100644 --- a/osu.Game/Rulesets/Mods/Mod.cs +++ b/osu.Game/Rulesets/Mods/Mod.cs @@ -195,7 +195,7 @@ namespace osu.Game.Rulesets.Mods /// /// Whether all settings in this mod are set to their default state. /// - protected virtual bool UsesDefaultConfiguration => SettingsBindables.All(s => s.IsDefault); + public virtual bool UsesDefaultConfiguration => SettingsBindables.All(s => s.IsDefault); /// /// Creates a copy of this initialised to a default state. From 9bb0663b3bb3b7f717a5cedbcd62a0051062eacc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Dec 2023 10:36:52 +0100 Subject: [PATCH 0767/1118] Add test coverage of failure case --- .../Visual/Editing/TestSceneEditorBeatmapCreation.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs index 7a2ed23cae..db87987815 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneEditorBeatmapCreation.cs @@ -25,6 +25,7 @@ using osu.Game.Rulesets.Osu.UI; using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko.Objects; using osu.Game.Screens.Edit; +using osu.Game.Screens.Edit.Compose.Components.Timeline; using osu.Game.Screens.Edit.Setup; using osu.Game.Storyboards; using osu.Game.Tests.Resources; @@ -94,8 +95,11 @@ namespace osu.Game.Tests.Visual.Editing [Test] public void TestAddAudioTrack() { - AddAssert("track is virtual", () => Beatmap.Value.Track is TrackVirtual); + AddStep("enter compose mode", () => InputManager.Key(Key.F1)); + AddUntilStep("wait for timeline load", () => Editor.ChildrenOfType().FirstOrDefault()?.IsLoaded == true); + AddStep("enter setup mode", () => InputManager.Key(Key.F4)); + AddAssert("track is virtual", () => Beatmap.Value.Track is TrackVirtual); AddAssert("switch track to real track", () => { var setup = Editor.ChildrenOfType().First(); From cd1f6b46c40639443c49cb03bc84289fcdd47f68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Dec 2023 10:24:48 +0100 Subject: [PATCH 0768/1118] Fix crash after changing audio track in editor Closes https://github.com/ppy/osu/issues/26213. Reproduction scenario: switch audio track in editor after timeline loads. Happens because `beatmap.Value.Track.Length` is 0 immediately after a track switch, until BASS computes the actual track length on the audio thread. Yes this is a hack. No I have no better immediate ideas how to address this otherwise. --- .../Screens/Edit/Compose/Components/Timeline/Timeline.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 83d34ab61a..03a868b0f3 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -141,12 +141,13 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline waveformOpacity = config.GetBindable(OsuSetting.EditorWaveformOpacity); track.BindTo(editorClock.Track); - track.BindValueChanged(_ => + // schedule added as without it, `beatmap.Value.Track.Length` can be 0 immediately after a track switch. + track.BindValueChanged(_ => Schedule(() => { waveform.Waveform = beatmap.Value.Waveform; waveform.RelativePositionAxes = Axes.X; waveform.X = -(float)(Editor.WAVEFORM_VISUAL_OFFSET / beatmap.Value.Track.Length); - }, true); + }), true); Zoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom); } From 99cddb631702f2aebe23056424ae86428369a5d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Dec 2023 11:07:45 +0100 Subject: [PATCH 0769/1118] Use alternative workaround --- .../Compose/Components/Timeline/Timeline.cs | 22 ++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs index 03a868b0f3..a2704e550c 100644 --- a/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs +++ b/osu.Game/Screens/Edit/Compose/Components/Timeline/Timeline.cs @@ -141,17 +141,29 @@ namespace osu.Game.Screens.Edit.Compose.Components.Timeline waveformOpacity = config.GetBindable(OsuSetting.EditorWaveformOpacity); track.BindTo(editorClock.Track); - // schedule added as without it, `beatmap.Value.Track.Length` can be 0 immediately after a track switch. - track.BindValueChanged(_ => Schedule(() => + track.BindValueChanged(_ => { waveform.Waveform = beatmap.Value.Waveform; - waveform.RelativePositionAxes = Axes.X; - waveform.X = -(float)(Editor.WAVEFORM_VISUAL_OFFSET / beatmap.Value.Track.Length); - }), true); + Scheduler.AddOnce(applyVisualOffset, beatmap); + }, true); Zoom = (float)(defaultTimelineZoom * editorBeatmap.BeatmapInfo.TimelineZoom); } + private void applyVisualOffset(IBindable beatmap) + { + waveform.RelativePositionAxes = Axes.X; + + if (beatmap.Value.Track.Length > 0) + waveform.X = -(float)(Editor.WAVEFORM_VISUAL_OFFSET / beatmap.Value.Track.Length); + else + { + // sometimes this can be the case immediately after a track switch. + // reschedule with the hope that the track length eventually populates. + Scheduler.AddOnce(applyVisualOffset, beatmap); + } + } + protected override void LoadComplete() { base.LoadComplete(); From 25fa76a1b2bbb8d96794466e28024842aff9e71d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Dec 2023 11:14:28 +0100 Subject: [PATCH 0770/1118] Do not show main menu version display on deployed builds See https://discord.com/channels/188630481301012481/188630652340404224/1190028102525530202. --- osu.Game/OsuGame.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 37996e8832..0d6eb1ea44 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -994,7 +994,7 @@ namespace osu.Game Margin = new MarginPadding(5), }, topMostOverlayContent.Add); - if (!args?.Any(a => a == @"--no-version-overlay") ?? true) + if (!IsDeployedBuild) { dependencies.Cache(versionManager = new VersionManager { Depth = int.MinValue }); loadComponentSingleFile(versionManager, ScreenContainer.Add); From db78d73fa511b647e45286e805a6ae7784ee4fce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Dec 2023 11:50:30 +0100 Subject: [PATCH 0771/1118] Do not display system title in inital menu state Addresses https://github.com/ppy/osu/discussions/26199. --- osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs | 15 +++++++++++---- osu.Game/Screens/Menu/MainMenu.cs | 5 ++++- osu.Game/Screens/Menu/SystemTitle.cs | 12 +++++++++++- 3 files changed, 26 insertions(+), 6 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs index 46570e7cdf..7053a9d544 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneMainMenu.cs @@ -3,33 +3,40 @@ using System.Linq; using NUnit.Framework; +using osu.Framework.Graphics.Containers; using osu.Framework.Testing; using osu.Game.Online.API.Requests.Responses; using osu.Game.Screens.Menu; +using osuTK.Input; namespace osu.Game.Tests.Visual.Menus { public partial class TestSceneMainMenu : OsuGameTestScene { + private SystemTitle systemTitle => Game.ChildrenOfType().Single(); + [Test] public void TestSystemTitle() { - AddStep("set system title", () => Game.ChildrenOfType().Single().Current.Value = new APISystemTitle + AddStep("set system title", () => systemTitle.Current.Value = new APISystemTitle { Image = @"https://assets.ppy.sh/main-menu/project-loved-2@2x.png", Url = @"https://osu.ppy.sh/home/news/2023-12-21-project-loved-december-2023", }); - AddStep("set another title", () => Game.ChildrenOfType().Single().Current.Value = new APISystemTitle + AddAssert("system title not visible", () => systemTitle.State.Value, () => Is.EqualTo(Visibility.Hidden)); + AddStep("enter menu", () => InputManager.Key(Key.Enter)); + AddUntilStep("system title visible", () => systemTitle.State.Value, () => Is.EqualTo(Visibility.Visible)); + AddStep("set another title", () => systemTitle.Current.Value = new APISystemTitle { Image = @"https://assets.ppy.sh/main-menu/wf2023-vote@2x.png", Url = @"https://osu.ppy.sh/community/contests/189", }); - AddStep("set title with nonexistent image", () => Game.ChildrenOfType().Single().Current.Value = new APISystemTitle + AddStep("set title with nonexistent image", () => systemTitle.Current.Value = new APISystemTitle { Image = @"https://test.invalid/@2x", // .invalid TLD reserved by https://datatracker.ietf.org/doc/html/rfc2606#section-2 Url = @"https://osu.ppy.sh/community/contests/189", }); - AddStep("unset system title", () => Game.ChildrenOfType().Single().Current.Value = null); + AddStep("unset system title", () => systemTitle.Current.Value = null); } } } diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 401460a498..516b090a16 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -94,6 +94,7 @@ namespace osu.Game.Screens.Menu private ParallaxContainer buttonsContainer; private SongTicker songTicker; private Container logoTarget; + private SystemTitle systemTitle; private MenuTip menuTip; private FillFlowContainer bottomElementsFlow; private SupporterDisplay supporterDisplay; @@ -173,7 +174,7 @@ namespace osu.Game.Screens.Menu Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, }, - new SystemTitle + systemTitle = new SystemTitle { Anchor = Anchor.TopCentre, Origin = Anchor.TopCentre, @@ -196,10 +197,12 @@ namespace osu.Game.Screens.Menu case ButtonSystemState.Initial: case ButtonSystemState.Exit: ApplyToBackground(b => b.FadeColour(Color4.White, 500, Easing.OutSine)); + systemTitle.State.Value = Visibility.Hidden; break; default: ApplyToBackground(b => b.FadeColour(OsuColour.Gray(0.8f), 500, Easing.OutSine)); + systemTitle.State.Value = Visibility.Visible; break; } }; diff --git a/osu.Game/Screens/Menu/SystemTitle.cs b/osu.Game/Screens/Menu/SystemTitle.cs index 7d49a1dd5f..813a470ed6 100644 --- a/osu.Game/Screens/Menu/SystemTitle.cs +++ b/osu.Game/Screens/Menu/SystemTitle.cs @@ -18,10 +18,12 @@ using osu.Game.Online.API.Requests.Responses; namespace osu.Game.Screens.Menu { - public partial class SystemTitle : CompositeDrawable + public partial class SystemTitle : VisibilityContainer { internal Bindable Current { get; } = new Bindable(); + private const float transition_duration = 500; + private Container content = null!; private CancellationTokenSource? cancellationTokenSource; private SystemTitleImage? currentImage; @@ -32,9 +34,13 @@ namespace osu.Game.Screens.Menu private void load(OsuGame? game) { AutoSizeAxes = Axes.Both; + AutoSizeDuration = transition_duration; + AutoSizeEasing = Easing.OutQuint; InternalChild = content = new OsuClickableContainer { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, AutoSizeAxes = Axes.Both, Action = () => { @@ -51,6 +57,10 @@ namespace osu.Game.Screens.Menu }; } + protected override void PopIn() => content.FadeInFromZero(transition_duration, Easing.OutQuint); + + protected override void PopOut() => content.FadeOut(transition_duration, Easing.OutQuint); + protected override bool OnHover(HoverEvent e) { content.ScaleTo(1.05f, 2000, Easing.OutQuint); From cdcb3ef4aa7401243d6a97e151d9cc41cd284060 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Dec 2023 12:20:14 +0100 Subject: [PATCH 0772/1118] Fix global audio offset suggestion feature not taking previous offset value into account See https://osu.ppy.sh/comments/2989193 etc. --- .../SessionAverageHitErrorTracker.cs | 25 ++++++++++++++++--- .../Audio/AudioOffsetAdjustControl.cs | 14 +++++------ 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/osu.Game/Configuration/SessionAverageHitErrorTracker.cs b/osu.Game/Configuration/SessionAverageHitErrorTracker.cs index 13b1ea7d37..cd21eb6fa8 100644 --- a/osu.Game/Configuration/SessionAverageHitErrorTracker.cs +++ b/osu.Game/Configuration/SessionAverageHitErrorTracker.cs @@ -17,11 +17,14 @@ namespace osu.Game.Configuration [Cached] public partial class SessionAverageHitErrorTracker : Component { - public IBindableList AverageHitErrorHistory => averageHitErrorHistory; - private readonly BindableList averageHitErrorHistory = new BindableList(); + public IBindableList AverageHitErrorHistory => averageHitErrorHistory; + private readonly BindableList averageHitErrorHistory = new BindableList(); private readonly Bindable latestScore = new Bindable(); + [Resolved] + private OsuConfigManager configManager { get; set; } = null!; + [BackgroundDependencyLoader] private void load(SessionStatics statics) { @@ -46,9 +49,25 @@ namespace osu.Game.Configuration // keep a sane maximum number of entries. if (averageHitErrorHistory.Count >= 50) averageHitErrorHistory.RemoveAt(0); - averageHitErrorHistory.Add(averageError); + + double globalOffset = configManager.Get(OsuSetting.AudioOffset); + averageHitErrorHistory.Add(new DataPoint(averageError, globalOffset)); } public void ClearHistory() => averageHitErrorHistory.Clear(); + + public readonly struct DataPoint + { + public double AverageHitError { get; } + public double GlobalAudioOffset { get; } + + public double SuggestedGlobalAudioOffset => GlobalAudioOffset - AverageHitError; + + public DataPoint(double averageHitError, double globalOffset) + { + AverageHitError = averageHitError; + GlobalAudioOffset = globalOffset; + } + } } } diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs index 98acea1f2e..08bf4b0dad 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs @@ -42,7 +42,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio private readonly BindableNumberWithCurrent current = new BindableNumberWithCurrent(); - private readonly IBindableList averageHitErrorHistory = new BindableList(); + private readonly IBindableList averageHitErrorHistory = new BindableList(); private readonly Bindable suggestedOffset = new Bindable(); @@ -112,7 +112,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio switch (e.Action) { case NotifyCollectionChangedAction.Add: - foreach (double average in e.NewItems!) + foreach (SessionAverageHitErrorTracker.DataPoint dataPoint in e.NewItems!) { notchContainer.ForEach(n => n.Alpha *= 0.95f); notchContainer.Add(new Box @@ -122,16 +122,16 @@ namespace osu.Game.Overlays.Settings.Sections.Audio RelativePositionAxes = Axes.X, Anchor = Anchor.Centre, Origin = Anchor.Centre, - X = getXPositionForAverage(average) + X = getXPositionForOffset(dataPoint.SuggestedGlobalAudioOffset) }); } break; case NotifyCollectionChangedAction.Remove: - foreach (double average in e.OldItems!) + foreach (SessionAverageHitErrorTracker.DataPoint dataPoint in e.OldItems!) { - var notch = notchContainer.FirstOrDefault(n => n.X == getXPositionForAverage(average)); + var notch = notchContainer.FirstOrDefault(n => n.X == getXPositionForOffset(dataPoint.SuggestedGlobalAudioOffset)); Debug.Assert(notch != null); notchContainer.Remove(notch, true); } @@ -143,10 +143,10 @@ namespace osu.Game.Overlays.Settings.Sections.Audio break; } - suggestedOffset.Value = averageHitErrorHistory.Any() ? -averageHitErrorHistory.Average() : null; + suggestedOffset.Value = averageHitErrorHistory.Any() ? -averageHitErrorHistory.Average(dataPoint => dataPoint.SuggestedGlobalAudioOffset) : null; } - private float getXPositionForAverage(double average) => (float)(Math.Clamp(-average, current.MinValue, current.MaxValue) / (2 * current.MaxValue)); + private float getXPositionForOffset(double offset) => (float)(Math.Clamp(offset, current.MinValue, current.MaxValue) / (2 * current.MaxValue)); private void updateHintText() { From 17753b82356c4e4f51fb6e55dbff4e8b604c6ede Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 29 Dec 2023 13:48:49 +0100 Subject: [PATCH 0773/1118] Remove opaque background from toolbar user button Would close https://github.com/ppy/osu/issues/26223, and generally seems more consistent with the rest of toolbar anyhow? --- osu.Game/Overlays/Toolbar/ToolbarUserButton.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 7d1b6c7404..19b98628e6 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -40,8 +40,6 @@ namespace osu.Game.Overlays.Toolbar [BackgroundDependencyLoader] private void load(OsuColour colours, IAPIProvider api, LoginOverlay? login) { - BackgroundContent.Add(new OpaqueBackground { Depth = 1 }); - Flow.Add(new Container { Masking = true, From d00b7c9cdf23b204f4843523dbd7a5ae0883faaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Dec 2023 22:14:51 +0900 Subject: [PATCH 0774/1118] Add some new menu tips (and reword some others) --- osu.Game/Screens/Menu/MenuTip.cs | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/MenuTip.cs b/osu.Game/Screens/Menu/MenuTip.cs index ab09c07e64..e31281ea20 100644 --- a/osu.Game/Screens/Menu/MenuTip.cs +++ b/osu.Game/Screens/Menu/MenuTip.cs @@ -94,14 +94,14 @@ namespace osu.Game.Screens.Menu { string[] tips = { - "You can press Ctrl-T anywhere in the game to toggle the toolbar!", - "You can press Ctrl-O anywhere in the game to access options!", + "Press Ctrl-T anywhere in the game to toggle the toolbar!", + "Press Ctrl-O anywhere in the game to access options!", "All settings are dynamic and take effect in real-time. Try changing the skin while watching autoplay!", "New features are coming online every update. Make sure to stay up-to-date!", "If you find the UI too large or small, try adjusting UI scale in settings!", "Try adjusting the \"Screen Scaling\" mode to change your gameplay or UI area, even in fullscreen!", "What used to be \"osu!direct\" is available to all users just like on the website. You can access it anywhere using Ctrl-B!", - "Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen!", + "Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen or by using the left and right arrow keys!", "Multithreading support means that even with low \"FPS\" your input and judgements will be accurate!", "Try scrolling right in mod select to find a bunch of new fun mods!", "Most of the web content (profiles, rankings, etc.) are available natively in-game from the icons on the toolbar!", @@ -110,6 +110,18 @@ namespace osu.Game.Screens.Menu "Check out the \"playlists\" system, which lets users create their own custom and permanent leaderboards!", "Toggle advanced frame / thread statistics with Ctrl-F11!", "Take a look under the hood at performance counters and enable verbose performance logging with Ctrl-F2!", + "You can pause during a replay by pressing Space!", + "Most of the hotkeys in the game are configurable and can be changed to anything you want. Check the bindings panel under input settings!", + "When your gameplay HUD is hidden, you can press and hold Ctrl to view it temporarily!", + "Your gameplay HUD can be customized by using the skin layout editor. Open at any time via Ctrl-Shift-S!", + "Drag and drop any image into the skin editor to load it in quickly!", + "You can create mod presets to make toggling your favorite mod combinations easier!", + "Many mods have customisation settings that drastically change how they function. Click the Mod Customisation button in mod select to view settings!", + "Press Ctrl-Shift-R to switch to a random skin!", + "Press Ctrl-Shift-F to toggle the FPS Counter. But make sure not to pay too much attention to it!", + "While watching a reply replay, press Ctrl-H to toggle replay settings!", + "You can easily copy the mods from scores on a leaderboard by right-clicking on them!", + "Ctrl-Enter at song select will start a beatmap in autoplay mode!" }; return tips[RNG.Next(0, tips.Length)]; From 9f0fe6c6cab17dc1395e6874e57bf29a58b39a8c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Dec 2023 22:20:29 +0900 Subject: [PATCH 0775/1118] Fix common typos MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Bartłomiej Dach --- osu.Game/Screens/Menu/MenuTip.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Menu/MenuTip.cs b/osu.Game/Screens/Menu/MenuTip.cs index e31281ea20..0f250768f1 100644 --- a/osu.Game/Screens/Menu/MenuTip.cs +++ b/osu.Game/Screens/Menu/MenuTip.cs @@ -113,13 +113,13 @@ namespace osu.Game.Screens.Menu "You can pause during a replay by pressing Space!", "Most of the hotkeys in the game are configurable and can be changed to anything you want. Check the bindings panel under input settings!", "When your gameplay HUD is hidden, you can press and hold Ctrl to view it temporarily!", - "Your gameplay HUD can be customized by using the skin layout editor. Open at any time via Ctrl-Shift-S!", + "Your gameplay HUD can be customized by using the skin layout editor. Open it at any time via Ctrl-Shift-S!", "Drag and drop any image into the skin editor to load it in quickly!", "You can create mod presets to make toggling your favorite mod combinations easier!", "Many mods have customisation settings that drastically change how they function. Click the Mod Customisation button in mod select to view settings!", "Press Ctrl-Shift-R to switch to a random skin!", "Press Ctrl-Shift-F to toggle the FPS Counter. But make sure not to pay too much attention to it!", - "While watching a reply replay, press Ctrl-H to toggle replay settings!", + "While watching a replay, press Ctrl-H to toggle replay settings!", "You can easily copy the mods from scores on a leaderboard by right-clicking on them!", "Ctrl-Enter at song select will start a beatmap in autoplay mode!" }; From 61c46b78bd380189944107782533915f9ec37995 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 29 Dec 2023 22:24:16 +0900 Subject: [PATCH 0776/1118] Update MenuTip.cs Co-authored-by: Salman Ahmed --- osu.Game/Screens/Menu/MenuTip.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MenuTip.cs b/osu.Game/Screens/Menu/MenuTip.cs index 0f250768f1..da349373c3 100644 --- a/osu.Game/Screens/Menu/MenuTip.cs +++ b/osu.Game/Screens/Menu/MenuTip.cs @@ -101,7 +101,7 @@ namespace osu.Game.Screens.Menu "If you find the UI too large or small, try adjusting UI scale in settings!", "Try adjusting the \"Screen Scaling\" mode to change your gameplay or UI area, even in fullscreen!", "What used to be \"osu!direct\" is available to all users just like on the website. You can access it anywhere using Ctrl-B!", - "Seeking in replays is available by dragging on the difficulty bar at the bottom of the screen or by using the left and right arrow keys!", + "Seeking in replays is available by dragging on the progress bar at the bottom of the screen or by using the left and right arrow keys!", "Multithreading support means that even with low \"FPS\" your input and judgements will be accurate!", "Try scrolling right in mod select to find a bunch of new fun mods!", "Most of the web content (profiles, rankings, etc.) are available natively in-game from the icons on the toolbar!", From 8df9a1ee1f8b8253e1438909f245aa709bd0be46 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Dec 2023 20:43:00 +0300 Subject: [PATCH 0777/1118] Fix argon miss judgement on mania placed on a different position --- osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs index 4ce3c50f7c..6b1d8a7fcd 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs @@ -76,7 +76,6 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon this.ScaleTo(1.6f); this.ScaleTo(1, 100, Easing.In); - this.MoveTo(Vector2.Zero); this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); this.RotateTo(0); From 08d88ec2fa94b8d6e855246e40d7eb23e632773a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Fri, 29 Dec 2023 20:47:18 +0300 Subject: [PATCH 0778/1118] Add back initial position transform to ensure correctness --- .../Skinning/Argon/ArgonJudgementPiece.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs index 6b1d8a7fcd..a191dee1ca 100644 --- a/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs +++ b/osu.Game.Rulesets.Mania/Skinning/Argon/ArgonJudgementPiece.cs @@ -19,6 +19,8 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon { public partial class ArgonJudgementPiece : JudgementPiece, IAnimatableJudgement { + private const float judgement_y_position = 160; + private RingExplosion? ringExplosion; [Resolved] @@ -30,7 +32,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon AutoSizeAxes = Axes.Both; Origin = Anchor.Centre; - Y = 160; + Y = judgement_y_position; } [BackgroundDependencyLoader] @@ -76,6 +78,7 @@ namespace osu.Game.Rulesets.Mania.Skinning.Argon this.ScaleTo(1.6f); this.ScaleTo(1, 100, Easing.In); + this.MoveToY(judgement_y_position); this.MoveToOffset(new Vector2(0, 100), 800, Easing.InQuint); this.RotateTo(0); From 807443b6481223fc14574b6c0829e4f197dee6cb Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 30 Dec 2023 10:21:59 +0900 Subject: [PATCH 0779/1118] Add HitResult.SliderTailHit --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 8 ++++++++ osu.Game/Rulesets/Judgements/Judgement.cs | 2 ++ osu.Game/Rulesets/Scoring/HitResult.cs | 15 +++++++++++++++ osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 3 +++ 4 files changed, 28 insertions(+) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index d883b91abc..567bf6968f 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -84,6 +84,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(ScoringMode.Standardised, HitResult.SmallTickHit, HitResult.SmallTickHit, 493_652)] [TestCase(ScoringMode.Standardised, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] [TestCase(ScoringMode.Standardised, HitResult.LargeTickHit, HitResult.LargeTickHit, 326_963)] + [TestCase(ScoringMode.Standardised, HitResult.SliderTailHit, HitResult.SliderTailHit, 326_963)] [TestCase(ScoringMode.Standardised, HitResult.SmallBonus, HitResult.SmallBonus, 1_000_030)] [TestCase(ScoringMode.Standardised, HitResult.LargeBonus, HitResult.LargeBonus, 1_000_150)] [TestCase(ScoringMode.Classic, HitResult.Miss, HitResult.Great, 0)] @@ -96,6 +97,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(ScoringMode.Classic, HitResult.SmallTickHit, HitResult.SmallTickHit, 49_365)] [TestCase(ScoringMode.Classic, HitResult.LargeTickMiss, HitResult.LargeTickHit, 0)] [TestCase(ScoringMode.Classic, HitResult.LargeTickHit, HitResult.LargeTickHit, 32_696)] + [TestCase(ScoringMode.Classic, HitResult.SliderTailHit, HitResult.SliderTailHit, 32_696)] [TestCase(ScoringMode.Classic, HitResult.SmallBonus, HitResult.SmallBonus, 100_003)] [TestCase(ScoringMode.Classic, HitResult.LargeBonus, HitResult.LargeBonus, 100_015)] public void TestFourVariousResultsOneMiss(ScoringMode scoringMode, HitResult hitResult, HitResult maxResult, int expectedScore) @@ -167,6 +169,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(HitResult.Perfect, HitResult.Miss)] [TestCase(HitResult.SmallTickHit, HitResult.SmallTickMiss)] [TestCase(HitResult.LargeTickHit, HitResult.LargeTickMiss)] + [TestCase(HitResult.SliderTailHit, HitResult.LargeTickMiss)] [TestCase(HitResult.SmallBonus, HitResult.IgnoreMiss)] [TestCase(HitResult.LargeBonus, HitResult.IgnoreMiss)] public void TestMinResults(HitResult hitResult, HitResult expectedMinResult) @@ -187,6 +190,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(HitResult.SmallTickHit, false)] [TestCase(HitResult.LargeTickMiss, true)] [TestCase(HitResult.LargeTickHit, true)] + [TestCase(HitResult.SliderTailHit, true)] [TestCase(HitResult.SmallBonus, false)] [TestCase(HitResult.LargeBonus, false)] public void TestAffectsCombo(HitResult hitResult, bool expectedReturnValue) @@ -207,6 +211,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(HitResult.SmallTickHit, true)] [TestCase(HitResult.LargeTickMiss, true)] [TestCase(HitResult.LargeTickHit, true)] + [TestCase(HitResult.SliderTailHit, true)] [TestCase(HitResult.SmallBonus, false)] [TestCase(HitResult.LargeBonus, false)] public void TestAffectsAccuracy(HitResult hitResult, bool expectedReturnValue) @@ -227,6 +232,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(HitResult.SmallTickHit, false)] [TestCase(HitResult.LargeTickMiss, false)] [TestCase(HitResult.LargeTickHit, false)] + [TestCase(HitResult.SliderTailHit, false)] [TestCase(HitResult.SmallBonus, true)] [TestCase(HitResult.LargeBonus, true)] public void TestIsBonus(HitResult hitResult, bool expectedReturnValue) @@ -247,6 +253,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(HitResult.SmallTickHit, true)] [TestCase(HitResult.LargeTickMiss, false)] [TestCase(HitResult.LargeTickHit, true)] + [TestCase(HitResult.SliderTailHit, true)] [TestCase(HitResult.SmallBonus, true)] [TestCase(HitResult.LargeBonus, true)] public void TestIsHit(HitResult hitResult, bool expectedReturnValue) @@ -267,6 +274,7 @@ namespace osu.Game.Tests.Rulesets.Scoring [TestCase(HitResult.SmallTickHit, true)] [TestCase(HitResult.LargeTickMiss, true)] [TestCase(HitResult.LargeTickHit, true)] + [TestCase(HitResult.SliderTailHit, true)] [TestCase(HitResult.SmallBonus, true)] [TestCase(HitResult.LargeBonus, true)] public void TestIsScorable(HitResult hitResult, bool expectedReturnValue) diff --git a/osu.Game/Rulesets/Judgements/Judgement.cs b/osu.Game/Rulesets/Judgements/Judgement.cs index 27402d522c..93386de483 100644 --- a/osu.Game/Rulesets/Judgements/Judgement.cs +++ b/osu.Game/Rulesets/Judgements/Judgement.cs @@ -73,6 +73,7 @@ namespace osu.Game.Rulesets.Judgements return HitResult.SmallTickMiss; case HitResult.LargeTickHit: + case HitResult.SliderTailHit: return HitResult.LargeTickMiss; default: @@ -104,6 +105,7 @@ namespace osu.Game.Rulesets.Judgements case HitResult.SmallTickMiss: return -DEFAULT_MAX_HEALTH_INCREASE * 0.5; + case HitResult.SliderTailHit: case HitResult.LargeTickHit: return DEFAULT_MAX_HEALTH_INCREASE; diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index 2d55f1a649..f307344347 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -139,6 +139,13 @@ namespace osu.Game.Rulesets.Scoring [Order(15)] ComboBreak, + /// + /// A special judgement similar to that's used to increase the valuation of the final tick of a slider. + /// + [EnumMember(Value = "slider_tail_hit")] + [Order(16)] + SliderTailHit, + /// /// A special result used as a padding value for legacy rulesets. It is a hit type and affects combo, but does not affect the base score (does not affect accuracy). /// @@ -188,6 +195,7 @@ namespace osu.Game.Rulesets.Scoring case HitResult.LargeTickMiss: case HitResult.LegacyComboIncrease: case HitResult.ComboBreak: + case HitResult.SliderTailHit: return true; default: @@ -246,6 +254,7 @@ namespace osu.Game.Rulesets.Scoring case HitResult.LargeTickMiss: case HitResult.SmallTickHit: case HitResult.SmallTickMiss: + case HitResult.SliderTailHit: return true; default: @@ -329,6 +338,9 @@ namespace osu.Game.Rulesets.Scoring case HitResult.ComboBreak: return true; + case HitResult.SliderTailHit: + return true; + default: // Note that IgnoreHit and IgnoreMiss are excluded as they do not affect score. return result >= HitResult.Miss && result < HitResult.IgnoreMiss; @@ -383,6 +395,9 @@ namespace osu.Game.Rulesets.Scoring if (minResult == HitResult.IgnoreMiss) return; + if (maxResult == HitResult.SliderTailHit && minResult != HitResult.LargeTickMiss) + throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.LargeTickMiss} is the only valid minimum result for a {maxResult} judgement."); + if (maxResult == HitResult.LargeTickHit && minResult != HitResult.LargeTickMiss) throw new ArgumentOutOfRangeException(nameof(minResult), $"{HitResult.LargeTickMiss} is the only valid minimum result for a {maxResult} judgement."); diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 5123668e54..837bb4080e 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -322,6 +322,9 @@ namespace osu.Game.Rulesets.Scoring case HitResult.LargeTickHit: return 30; + case HitResult.SliderTailHit: + return 150; + case HitResult.Meh: return 50; From 17a531209cf4ca6d2e2386f736779ad084a9fa44 Mon Sep 17 00:00:00 2001 From: Dan Balasescu Date: Sat, 30 Dec 2023 10:24:59 +0900 Subject: [PATCH 0780/1118] Use SliderTailHit result for slider tails (non-classic-mod) --- .../TestSceneSliderEarlyHitJudgement.cs | 2 +- .../TestSceneSliderInput.cs | 4 ++-- .../TestSceneSliderLateHitJudgement.cs | 14 +++++++------- osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs | 2 +- .../Scoring/OsuHealthProcessor.cs | 1 + .../Scoring/OsuLegacyHealthProcessor.cs | 1 + 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs index 4ea21e51f6..19883060a0 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderEarlyHitJudgement.cs @@ -51,7 +51,7 @@ namespace osu.Game.Rulesets.Osu.Tests assertHeadJudgement(HitResult.Meh); assertTickJudgement(HitResult.LargeTickHit); - assertTailJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.SliderTailHit); assertSliderJudgement(HitResult.IgnoreHit); } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs index 716d2e0756..12be74c4cc 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderInput.cs @@ -467,13 +467,13 @@ namespace osu.Game.Rulesets.Osu.Tests private void assertHeadMissTailTracked() { - AddAssert("Tracking retained", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.LargeTickHit)); + AddAssert("Tracking retained", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.SliderTailHit)); AddAssert("Slider head missed", () => judgementResults.First().IsHit, () => Is.False); } private void assertMidSliderJudgements() { - AddAssert("Tracking acquired", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.LargeTickHit)); + AddAssert("Tracking acquired", () => judgementResults[^2].Type, () => Is.EqualTo(HitResult.SliderTailHit)); } private void assertMidSliderJudgementFail() diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs index 6ba9c723da..1ba4a60b75 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderLateHitJudgement.cs @@ -57,7 +57,7 @@ namespace osu.Game.Rulesets.Osu.Tests }); assertHeadJudgement(HitResult.Ok); - assertTailJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.SliderTailHit); assertSliderJudgement(HitResult.IgnoreHit); } @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Osu.Tests assertTickJudgement(1, HitResult.LargeTickHit); assertTickJudgement(2, HitResult.LargeTickHit); assertTickJudgement(3, HitResult.LargeTickHit); - assertTailJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.SliderTailHit); assertSliderJudgement(HitResult.IgnoreHit); } @@ -182,7 +182,7 @@ namespace osu.Game.Rulesets.Osu.Tests assertHeadJudgement(HitResult.Meh); assertAllTickJudgements(HitResult.LargeTickHit); assertRepeatJudgement(HitResult.LargeTickHit); - assertTailJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.SliderTailHit); assertSliderJudgement(HitResult.IgnoreHit); } @@ -210,7 +210,7 @@ namespace osu.Game.Rulesets.Osu.Tests assertHeadJudgement(HitResult.Meh); assertRepeatJudgement(HitResult.LargeTickHit); - assertTailJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.SliderTailHit); assertSliderJudgement(HitResult.IgnoreHit); } @@ -245,7 +245,7 @@ namespace osu.Game.Rulesets.Osu.Tests assertAllTickJudgements(HitResult.LargeTickMiss); // This particular test actually starts tracking the slider just before the end, so the tail should be hit because of its leniency. - assertTailJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.SliderTailHit); assertSliderJudgement(HitResult.IgnoreHit); } @@ -276,7 +276,7 @@ namespace osu.Game.Rulesets.Osu.Tests assertHeadJudgement(HitResult.Meh); assertTickJudgement(0, HitResult.LargeTickMiss); - assertTailJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.SliderTailHit); assertSliderJudgement(HitResult.IgnoreHit); } @@ -307,7 +307,7 @@ namespace osu.Game.Rulesets.Osu.Tests assertHeadJudgement(HitResult.Meh); assertTickJudgement(0, HitResult.LargeTickMiss); assertTickJudgement(1, HitResult.LargeTickMiss); - assertTailJudgement(HitResult.LargeTickHit); + assertTailJudgement(HitResult.SliderTailHit); assertSliderJudgement(HitResult.IgnoreHit); } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index 357476ed30..ceee513412 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -29,7 +29,7 @@ namespace osu.Game.Rulesets.Osu.Objects public class TailJudgement : SliderEndJudgement { - public override HitResult MaxResult => HitResult.LargeTickHit; + public override HitResult MaxResult => HitResult.SliderTailHit; public override HitResult MinResult => HitResult.IgnoreMiss; } } diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs index 2eb257b3e6..fe6da9af35 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuHealthProcessor.cs @@ -91,6 +91,7 @@ namespace osu.Game.Rulesets.Osu.Scoring // When classic slider mechanics are enabled, this result comes from the tail. return 0.02; + case HitResult.SliderTailHit: case HitResult.LargeTickHit: switch (result.HitObject) { diff --git a/osu.Game.Rulesets.Osu/Scoring/OsuLegacyHealthProcessor.cs b/osu.Game.Rulesets.Osu/Scoring/OsuLegacyHealthProcessor.cs index e383e82b86..57d2f64e2c 100644 --- a/osu.Game.Rulesets.Osu/Scoring/OsuLegacyHealthProcessor.cs +++ b/osu.Game.Rulesets.Osu/Scoring/OsuLegacyHealthProcessor.cs @@ -57,6 +57,7 @@ namespace osu.Game.Rulesets.Osu.Scoring increase = 0.02; break; + case HitResult.SliderTailHit: case HitResult.LargeTickHit: // This result comes from either a slider tick or repeat. increase = hitObject is SliderTick ? 0.015 : 0.02; From bca060048266e1b124699def72b8f86e00b79c99 Mon Sep 17 00:00:00 2001 From: CaffeeLake Date: Wed, 27 Dec 2023 22:11:54 +0900 Subject: [PATCH 0781/1118] Use 0.99x or 1.01x Signed-off-by: CaffeeLake --- osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs | 10 ++++++++-- osu.Game/Screens/Select/FooterButtonMods.cs | 12 +++++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs index c758632392..85bcf6f0b2 100644 --- a/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs @@ -10,6 +10,7 @@ using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; +using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -108,13 +109,13 @@ namespace osu.Game.Overlays.Mods Current.BindValueChanged(e => { - if (e.NewValue > Current.Default) + if (Precision.DefinitelyBigger(e.NewValue, Current.Default)) { MainBackground .FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); counter.FadeColour(ColourProvider.Background5, transition_duration, Easing.OutQuint); } - else if (e.NewValue < Current.Default) + else if (Precision.DefinitelyBigger(Current.Default, e.NewValue)) { MainBackground .FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); @@ -131,6 +132,11 @@ namespace osu.Game.Overlays.Mods .FadeTo(0.15f, 60, Easing.OutQuint) .Then().FadeOut(500, Easing.OutQuint); + if (Precision.DefinitelyBigger(1.0, Current.Value) && Current.Value >= 0.995) + Current.Value = 0.99; + if (Precision.DefinitelyBigger(Current.Value, 1.0) && Current.Value < 1.005) + Current.Value = 1.01; + const float move_amount = 4; if (e.NewValue > e.OldValue) counter.MoveToY(Math.Max(-move_amount * 2, counter.Y - move_amount)).Then().MoveToY(0, transition_duration * 2, Easing.OutQuint); diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs index 9a84f9a0aa..cfbd17be01 100644 --- a/osu.Game/Screens/Select/FooterButtonMods.cs +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -19,6 +19,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; using osu.Game.Input.Bindings; +using osu.Framework.Utils; namespace osu.Game.Screens.Select { @@ -88,11 +89,16 @@ namespace osu.Game.Screens.Select { double multiplier = Current.Value?.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier) ?? 1; - MultiplierText.Text = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x"; + if (Precision.DefinitelyBigger(1.0, multiplier) && multiplier >= 0.995) + MultiplierText.Text = $"{0.99:N2}x"; + else if (Precision.DefinitelyBigger(multiplier, 1.0) && multiplier < 1.005) + MultiplierText.Text = $"{1.01:N2}x"; + else + MultiplierText.Text = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x"; - if (multiplier > 1.0) + if (Precision.DefinitelyBigger(multiplier, 1.0)) MultiplierText.FadeColour(highMultiplierColour, 200); - else if (multiplier < 1.0) + else if (Precision.DefinitelyBigger(1.0, multiplier)) MultiplierText.FadeColour(lowMultiplierColour, 200); else MultiplierText.FadeColour(Color4.White, 200); From cc89390ea89e6b1f8127adef2717e4d6438b7b46 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 30 Dec 2023 21:33:03 +0300 Subject: [PATCH 0782/1118] Expose `SuggestedOffset` bindable for testing purposes --- .../Sections/Audio/AudioOffsetAdjustControl.cs | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs index 08bf4b0dad..e46dc602eb 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs @@ -24,6 +24,8 @@ namespace osu.Game.Overlays.Settings.Sections.Audio { public partial class AudioOffsetAdjustControl : SettingsItem { + public IBindable SuggestedOffset => ((AudioOffsetPreview)Control).SuggestedOffset; + [BackgroundDependencyLoader] private void load() { @@ -44,7 +46,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio private readonly IBindableList averageHitErrorHistory = new BindableList(); - private readonly Bindable suggestedOffset = new Bindable(); + public readonly Bindable SuggestedOffset = new Bindable(); private Container notchContainer = null!; private TextFlowContainer hintText = null!; @@ -90,8 +92,8 @@ namespace osu.Game.Overlays.Settings.Sections.Audio Text = "Apply suggested offset", Action = () => { - if (suggestedOffset.Value.HasValue) - current.Value = suggestedOffset.Value.Value; + if (SuggestedOffset.Value.HasValue) + current.Value = SuggestedOffset.Value.Value; hitErrorTracker.ClearHistory(); } } @@ -104,7 +106,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio base.LoadComplete(); averageHitErrorHistory.BindCollectionChanged(updateDisplay, true); - suggestedOffset.BindValueChanged(_ => updateHintText(), true); + SuggestedOffset.BindValueChanged(_ => updateHintText(), true); } private void updateDisplay(object? _, NotifyCollectionChangedEventArgs e) @@ -143,17 +145,17 @@ namespace osu.Game.Overlays.Settings.Sections.Audio break; } - suggestedOffset.Value = averageHitErrorHistory.Any() ? -averageHitErrorHistory.Average(dataPoint => dataPoint.SuggestedGlobalAudioOffset) : null; + SuggestedOffset.Value = averageHitErrorHistory.Any() ? -averageHitErrorHistory.Average(dataPoint => dataPoint.SuggestedGlobalAudioOffset) : null; } private float getXPositionForOffset(double offset) => (float)(Math.Clamp(offset, current.MinValue, current.MaxValue) / (2 * current.MaxValue)); private void updateHintText() { - hintText.Text = suggestedOffset.Value == null + hintText.Text = SuggestedOffset.Value == null ? @"Play a few beatmaps to receive a suggested offset!" - : $@"Based on the last {averageHitErrorHistory.Count} play(s), the suggested offset is {suggestedOffset.Value:N0} ms."; - applySuggestion.Enabled.Value = suggestedOffset.Value != null; + : $@"Based on the last {averageHitErrorHistory.Count} play(s), the suggested offset is {SuggestedOffset.Value:N0} ms."; + applySuggestion.Enabled.Value = SuggestedOffset.Value != null; } } } From 68dd103c89e25619bf4aea6aea65e61d369396d6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 30 Dec 2023 21:33:22 +0300 Subject: [PATCH 0783/1118] Add failing test cases --- .../TestSceneAudioOffsetAdjustControl.cs | 88 ++++++++++++++++--- 1 file changed, 78 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneAudioOffsetAdjustControl.cs b/osu.Game.Tests/Visual/Settings/TestSceneAudioOffsetAdjustControl.cs index efb65bb0a8..85cde966b1 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneAudioOffsetAdjustControl.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneAudioOffsetAdjustControl.cs @@ -3,7 +3,7 @@ using NUnit.Framework; using osu.Framework.Allocation; -using osu.Framework.Bindables; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Utils; @@ -25,9 +25,15 @@ namespace osu.Game.Tests.Visual.Settings private Container content = null!; protected override Container Content => content; + private OsuConfigManager localConfig = null!; + private AudioOffsetAdjustControl adjustControl = null!; + [BackgroundDependencyLoader] private void load() { + localConfig = new OsuConfigManager(LocalStorage); + Dependencies.CacheAs(localConfig); + base.Content.AddRange(new Drawable[] { tracker, @@ -41,17 +47,21 @@ namespace osu.Game.Tests.Visual.Settings }); } - [Test] - public void TestBehaviour() + [SetUp] + public void SetUp() => Schedule(() => { - AddStep("create control", () => Child = new AudioOffsetAdjustControl + Child = adjustControl = new AudioOffsetAdjustControl { - Current = new BindableDouble - { - MinValue = -500, - MaxValue = 500 - } - }); + Current = localConfig.GetBindable(OsuSetting.AudioOffset), + }; + + localConfig.SetValue(OsuSetting.AudioOffset, 0.0); + tracker.ClearHistory(); + }); + + [Test] + public void TestDisplay() + { AddStep("set new score", () => statics.SetValue(Static.LastLocalUserScore, new ScoreInfo { HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(RNG.NextDouble(-100, 100)), @@ -59,5 +69,63 @@ namespace osu.Game.Tests.Visual.Settings })); AddStep("clear history", () => tracker.ClearHistory()); } + + [Test] + public void TestBehaviour() + { + AddStep("set score with -20ms", () => setScore(-20)); + AddAssert("suggested global offset is 20ms", () => adjustControl.SuggestedOffset.Value, () => Is.EqualTo(20)); + AddStep("clear history", () => tracker.ClearHistory()); + + AddStep("set score with 40ms", () => setScore(40)); + AddAssert("suggested global offset is -40ms", () => adjustControl.SuggestedOffset.Value, () => Is.EqualTo(-40)); + AddStep("clear history", () => tracker.ClearHistory()); + } + + [Test] + public void TestNonZeroGlobalOffset() + { + AddStep("set global offset to -20ms", () => localConfig.SetValue(OsuSetting.AudioOffset, -20.0)); + AddStep("set score with -20ms", () => setScore(-20)); + AddAssert("suggested global offset is 0ms", () => adjustControl.SuggestedOffset.Value, () => Is.EqualTo(0)); + AddStep("clear history", () => tracker.ClearHistory()); + + AddStep("set global offset to 20ms", () => localConfig.SetValue(OsuSetting.AudioOffset, 20.0)); + AddStep("set score with 40ms", () => setScore(40)); + AddAssert("suggested global offset is -20ms", () => adjustControl.SuggestedOffset.Value, () => Is.EqualTo(-20)); + AddStep("clear history", () => tracker.ClearHistory()); + } + + [Test] + public void TestMultiplePlays() + { + AddStep("set score with -20ms", () => setScore(-20)); + AddStep("set score with -10ms", () => setScore(-10)); + AddAssert("suggested global offset is 15ms", () => adjustControl.SuggestedOffset.Value, () => Is.EqualTo(15)); + AddStep("clear history", () => tracker.ClearHistory()); + + AddStep("set score with -20ms", () => setScore(-20)); + AddStep("set global offset to 30ms", () => localConfig.SetValue(OsuSetting.AudioOffset, 30.0)); + AddStep("set score with 10ms", () => setScore(10)); + AddAssert("suggested global offset is 20ms", () => adjustControl.SuggestedOffset.Value, () => Is.EqualTo(20)); + AddStep("clear history", () => tracker.ClearHistory()); + } + + private void setScore(double averageHitError) + { + statics.SetValue(Static.LastLocalUserScore, new ScoreInfo + { + HitEvents = TestSceneHitEventTimingDistributionGraph.CreateDistributedHitEvents(averageHitError), + BeatmapInfo = Beatmap.Value.BeatmapInfo, + }); + } + + protected override void Dispose(bool isDisposing) + { + if (localConfig.IsNotNull()) + localConfig.Dispose(); + + base.Dispose(isDisposing); + } } } From e6fe631625643a23b18641a132c6e67c1062b9ee Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 30 Dec 2023 21:34:37 +0300 Subject: [PATCH 0784/1118] Fix suggested value in audio offset adjust control being opposite in signs --- .../Settings/Sections/Audio/AudioOffsetAdjustControl.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs index e46dc602eb..90f5a59215 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs @@ -145,7 +145,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio break; } - SuggestedOffset.Value = averageHitErrorHistory.Any() ? -averageHitErrorHistory.Average(dataPoint => dataPoint.SuggestedGlobalAudioOffset) : null; + SuggestedOffset.Value = averageHitErrorHistory.Any() ? averageHitErrorHistory.Average(dataPoint => dataPoint.SuggestedGlobalAudioOffset) : null; } private float getXPositionForOffset(double offset) => (float)(Math.Clamp(offset, current.MinValue, current.MaxValue) / (2 * current.MaxValue)); From 452f201f0673426df7e88b78f720293863e8d660 Mon Sep 17 00:00:00 2001 From: iminlikewithyou Date: Sat, 30 Dec 2023 12:56:38 -0600 Subject: [PATCH 0785/1118] use margins isntead of moving the position of the sprite --- osu.Game/Screens/Menu/MainMenuButton.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/MainMenuButton.cs b/osu.Game/Screens/Menu/MainMenuButton.cs index 3d56a75ca8..422599a4a8 100644 --- a/osu.Game/Screens/Menu/MainMenuButton.cs +++ b/osu.Game/Screens/Menu/MainMenuButton.cs @@ -129,7 +129,8 @@ namespace osu.Game.Screens.Menu Anchor = Anchor.Centre, Origin = Anchor.Centre, Size = new Vector2(32), - Position = new Vector2(0, -4), + Position = new Vector2(0, 0), + Margin = new MarginPadding { Top = -4 }, Icon = symbol }, new OsuSpriteText @@ -138,7 +139,8 @@ namespace osu.Game.Screens.Menu AllowMultiline = false, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Position = new Vector2(-3, 35), + Position = new Vector2(0, 35), + Margin = new MarginPadding { Left = -3 }, Text = text } } @@ -189,7 +191,7 @@ namespace osu.Game.Screens.Menu { icon.ClearTransforms(); icon.RotateTo(0, 500, Easing.Out); - icon.MoveTo(new Vector2(0, -4), 500, Easing.Out); + icon.MoveTo(Vector2.Zero, 500, Easing.Out); icon.ScaleTo(Vector2.One, 200, Easing.Out); if (State == ButtonState.Expanded) From 535177ab97c06c291aaba6860b0499096413c708 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Sat, 30 Dec 2023 11:40:40 -0800 Subject: [PATCH 0786/1118] Remove outdated main menu version reference on issue template --- .github/ISSUE_TEMPLATE/bug-issue.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-issue.yml b/.github/ISSUE_TEMPLATE/bug-issue.yml index dfdcf8d320..a8a5d5e64b 100644 --- a/.github/ISSUE_TEMPLATE/bug-issue.yml +++ b/.github/ISSUE_TEMPLATE/bug-issue.yml @@ -42,7 +42,7 @@ body: - type: input attributes: label: Version - description: The version you encountered this bug on. This is shown at the bottom of the main menu and also at the end of the settings screen. + description: The version you encountered this bug on. This is shown at the end of the settings overlay. validations: required: true - type: markdown From 922b6ccb83b0e83d9b2be6f63ee7e8366e500c73 Mon Sep 17 00:00:00 2001 From: Gabriel Del Nero <43073074+Gabixel@users.noreply.github.com> Date: Sun, 31 Dec 2023 00:36:55 +0100 Subject: [PATCH 0787/1118] Use `FontAwesome` solid heart icon instead of `OsuIcon`'s --- osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs index 08a816930e..85751e7457 100644 --- a/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs +++ b/osu.Game/Overlays/Profile/Header/BottomHeaderContainer.cs @@ -145,7 +145,7 @@ namespace osu.Game.Overlays.Profile.Header bool anyInfoAdded = false; anyInfoAdded |= tryAddInfo(FontAwesome.Solid.MapMarker, user.Location); - anyInfoAdded |= tryAddInfo(OsuIcon.Heart, user.Interests); + anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Heart, user.Interests); anyInfoAdded |= tryAddInfo(FontAwesome.Solid.Suitcase, user.Occupation); if (anyInfoAdded) From 7cfb786b1a1dae5ef00c5d0359de336c4725c757 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 31 Dec 2023 04:48:06 +0300 Subject: [PATCH 0788/1118] Add helper method for properly formatting score multiplier in `ModUtils` --- osu.Game/Utils/ModUtils.cs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 1bd60fcdde..dad8c72aa1 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -5,6 +5,8 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Linq; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Localisation; using osu.Game.Online.API; using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; @@ -226,5 +228,21 @@ namespace osu.Game.Utils return proposedWereValid; } + + /// + /// Given a value of a score multiplier, returns a string version with special handling for a value near 1.00x. + /// + /// The value of the score multiplier. + /// A formatted score multiplier with a trailing "x" symbol + public static LocalisableString FormatScoreMultiplier(double scoreMultiplier) + { + // Round multiplier values away from 1.00x to two significant digits. + if (scoreMultiplier > 1) + scoreMultiplier = Math.Ceiling(scoreMultiplier * 100) / 100; + else + scoreMultiplier = Math.Floor(scoreMultiplier * 100) / 100; + + return scoreMultiplier.ToLocalisableString("0.00x"); + } } } From 4dc11c4c48d49104a2d5e504a58441f1752e6d7b Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 31 Dec 2023 05:18:07 +0300 Subject: [PATCH 0789/1118] Update existing code to use helper method --- osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs | 14 ++++---------- osu.Game/Screens/Select/FooterButtonMods.cs | 14 ++++---------- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs index 85bcf6f0b2..b599b53082 100644 --- a/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs @@ -4,18 +4,17 @@ using System; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Framework.Localisation; -using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Localisation; using osu.Game.Rulesets.Mods; +using osu.Game.Utils; using osuTK; namespace osu.Game.Overlays.Mods @@ -109,13 +108,13 @@ namespace osu.Game.Overlays.Mods Current.BindValueChanged(e => { - if (Precision.DefinitelyBigger(e.NewValue, Current.Default)) + if (e.NewValue > Current.Default) { MainBackground .FadeColour(colours.ForModType(ModType.DifficultyIncrease), transition_duration, Easing.OutQuint); counter.FadeColour(ColourProvider.Background5, transition_duration, Easing.OutQuint); } - else if (Precision.DefinitelyBigger(Current.Default, e.NewValue)) + else if (e.NewValue < Current.Default) { MainBackground .FadeColour(colours.ForModType(ModType.DifficultyReduction), transition_duration, Easing.OutQuint); @@ -132,11 +131,6 @@ namespace osu.Game.Overlays.Mods .FadeTo(0.15f, 60, Easing.OutQuint) .Then().FadeOut(500, Easing.OutQuint); - if (Precision.DefinitelyBigger(1.0, Current.Value) && Current.Value >= 0.995) - Current.Value = 0.99; - if (Precision.DefinitelyBigger(Current.Value, 1.0) && Current.Value < 1.005) - Current.Value = 1.01; - const float move_amount = 4; if (e.NewValue > e.OldValue) counter.MoveToY(Math.Max(-move_amount * 2, counter.Y - move_amount)).Then().MoveToY(0, transition_duration * 2, Easing.OutQuint); @@ -153,7 +147,7 @@ namespace osu.Game.Overlays.Mods { protected override double RollingDuration => 500; - protected override LocalisableString FormatCount(double count) => count.ToLocalisableString(@"0.00x"); + protected override LocalisableString FormatCount(double count) => ModUtils.FormatScoreMultiplier(count); protected override OsuSpriteText CreateSpriteText() => new OsuSpriteText { diff --git a/osu.Game/Screens/Select/FooterButtonMods.cs b/osu.Game/Screens/Select/FooterButtonMods.cs index cfbd17be01..69782c25bb 100644 --- a/osu.Game/Screens/Select/FooterButtonMods.cs +++ b/osu.Game/Screens/Select/FooterButtonMods.cs @@ -19,7 +19,7 @@ using osu.Game.Graphics.Sprites; using osuTK; using osuTK.Graphics; using osu.Game.Input.Bindings; -using osu.Framework.Utils; +using osu.Game.Utils; namespace osu.Game.Screens.Select { @@ -88,17 +88,11 @@ namespace osu.Game.Screens.Select private void updateMultiplierText() => Schedule(() => { double multiplier = Current.Value?.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier) ?? 1; + MultiplierText.Text = multiplier == 1 ? string.Empty : ModUtils.FormatScoreMultiplier(multiplier); - if (Precision.DefinitelyBigger(1.0, multiplier) && multiplier >= 0.995) - MultiplierText.Text = $"{0.99:N2}x"; - else if (Precision.DefinitelyBigger(multiplier, 1.0) && multiplier < 1.005) - MultiplierText.Text = $"{1.01:N2}x"; - else - MultiplierText.Text = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x"; - - if (Precision.DefinitelyBigger(multiplier, 1.0)) + if (multiplier > 1) MultiplierText.FadeColour(highMultiplierColour, 200); - else if (Precision.DefinitelyBigger(1.0, multiplier)) + else if (multiplier < 1) MultiplierText.FadeColour(lowMultiplierColour, 200); else MultiplierText.FadeColour(Color4.White, 200); From cd9250d08ccc415b5d94bacf8e0c31eb0d273ffb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 31 Dec 2023 05:18:10 +0300 Subject: [PATCH 0790/1118] Add test coverage --- osu.Game.Tests/Mods/ModUtilsTest.cs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index 9107ddd1ae..4f10a0ea54 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -310,6 +310,13 @@ namespace osu.Game.Tests.Mods Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid)); } + [Test] + public void TestFormatScoreMultiplier() + { + Assert.AreEqual(ModUtils.FormatScoreMultiplier(0.9999).ToString(), "0.99x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.0001).ToString(), "1.01x"); + } + public abstract class CustomMod1 : Mod, IModCompatibilitySpecification { } @@ -339,6 +346,16 @@ namespace osu.Game.Tests.Mods public override bool ValidForMultiplayerAsFreeMod => false; } + public class EditableMod : Mod + { + public override string Name => string.Empty; + public override LocalisableString Description => string.Empty; + public override string Acronym => string.Empty; + public override double ScoreMultiplier => Multiplier; + + public double Multiplier = 1; + } + public interface IModCompatibilitySpecification { } From 5ae5d7f92dc7b002e5b685922707c9cef61edbf9 Mon Sep 17 00:00:00 2001 From: iminlikewithyou Date: Sat, 30 Dec 2023 23:59:47 -0600 Subject: [PATCH 0791/1118] change floor to round --- osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs index b5315feccb..45213b7bdb 100644 --- a/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs +++ b/osu.Game/Screens/Edit/Timing/WaveformComparisonDisplay.cs @@ -224,7 +224,7 @@ namespace osu.Game.Screens.Edit.Timing row.Alpha = time < selectedGroupStartTime || time > selectedGroupEndTime ? 0.2f : 1; row.WaveformOffsetTo(-offset, animated); row.WaveformScale = new Vector2(scale, 1); - row.BeatIndex = (int)Math.Floor(index); + row.BeatIndex = (int)Math.Round(index); index++; } From 0c85fd496f9d555e1ec1c0b07fbebd2f9e8fd998 Mon Sep 17 00:00:00 2001 From: wooster0 Date: Sun, 31 Dec 2023 22:10:49 +0900 Subject: [PATCH 0792/1118] Don't leave scores screen empty if no scores are present yet Addresses https://github.com/ppy/osu/discussions/23787 I originally wanted to set `allowShowingResults` to false if there are no results but because this involves an API request to fetch the scores that would mean all the scores would have to be fetched all at once so that it knows whether to hide or show the "View results" button on a beatmap. Because that would slow things down and be very inefficient, this still allows the user to view the scores screen but if there aren't any scores, it shows a text, which I think is at least for now better than nothing. As for the testing of this, I wasn't sure how to not generate scores only for one specific test so I opted into not using `SetUpSteps` and doing it that way. --- .../TestScenePlaylistsResultsScreen.cs | 47 ++++++++++++++----- osu.Game/Screens/Ranking/ResultsScreen.cs | 35 ++++++++++---- osu.Game/Screens/Ranking/ScorePanelList.cs | 2 + 3 files changed, 63 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index cb422d8c06..e36e7f54ba 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -42,15 +42,15 @@ namespace osu.Game.Tests.Visual.Playlists private int totalCount; private ScoreInfo userScore; - [SetUpSteps] - public override void SetUpSteps() + // We don't use SetUpSteps for this because one of the tests shouldn't receive any scores. + public void InitialiseUserScoresAndBeatmap(bool noScores = false) { base.SetUpSteps(); // Previous test instances of the results screen may still exist at this point so wait for // those screens to be cleaned up by the base SetUpSteps before re-initialising test state. - // The the screen also holds a leased Beatmap bindable so reassigning it must happen after - // the screen as been exited. + // The screen also holds a leased Beatmap bindable so reassigning it must happen after + // the screen has been exited. AddStep("initialise user scores and beatmap", () => { lowestScoreId = 1; @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Playlists userScore.Statistics = new Dictionary(); userScore.MaximumStatistics = new Dictionary(); - bindHandler(); + bindHandler(noScores: noScores); // Beatmap is required to be an actual beatmap so the scores can get their scores correctly // calculated for standardised scoring, else the tests that rely on ordering will fall over. @@ -74,6 +74,8 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowWithUserScore() { + InitialiseUserScoresAndBeatmap(); + AddStep("bind user score info handler", () => bindHandler(userScore: userScore)); createResults(() => userScore); @@ -86,6 +88,8 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowNullUserScore() { + InitialiseUserScoresAndBeatmap(); + createResults(); AddAssert("top score selected", () => this.ChildrenOfType().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded); @@ -94,6 +98,8 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowUserScoreWithDelay() { + InitialiseUserScoresAndBeatmap(); + AddStep("bind user score info handler", () => bindHandler(true, userScore)); createResults(() => userScore); @@ -105,6 +111,8 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowNullUserScoreWithDelay() { + InitialiseUserScoresAndBeatmap(); + AddStep("bind delayed handler", () => bindHandler(true)); createResults(); @@ -115,6 +123,8 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestFetchWhenScrolledToTheRight() { + InitialiseUserScoresAndBeatmap(); + createResults(); AddStep("bind delayed handler", () => bindHandler(true)); @@ -137,6 +147,8 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestFetchWhenScrolledToTheLeft() { + InitialiseUserScoresAndBeatmap(); + AddStep("bind user score info handler", () => bindHandler(userScore: userScore)); createResults(() => userScore); @@ -158,7 +170,16 @@ namespace osu.Game.Tests.Visual.Playlists } } - private void createResults(Func getScore = null) + [Test] + public void TestShowWithNoScores() + { + InitialiseUserScoresAndBeatmap(noScores: true); + + createResults(noScores: true); + AddAssert("no scores visible", () => resultsScreen.ScorePanelList.GetScorePanels().Count() == 0); + } + + private void createResults(Func getScore = null, bool noScores = false) { AddStep("load results", () => { @@ -169,11 +190,13 @@ namespace osu.Game.Tests.Visual.Playlists }); AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded); - waitForDisplay(); + waitForDisplay(noScores); } - private void waitForDisplay() + private void waitForDisplay(bool noScores = false) { + if (noScores) return; + AddUntilStep("wait for scores loaded", () => requestComplete // request handler may need to fire more than once to get scores. @@ -183,7 +206,7 @@ namespace osu.Game.Tests.Visual.Playlists AddWaitStep("wait for display", 5); } - private void bindHandler(bool delayed = false, ScoreInfo userScore = null, bool failRequests = false) => ((DummyAPIAccess)API).HandleRequest = request => + private void bindHandler(bool delayed = false, ScoreInfo userScore = null, bool failRequests = false, bool noScores = false) => ((DummyAPIAccess)API).HandleRequest = request => { // pre-check for requests we should be handling (as they are scheduled below). switch (request) @@ -219,7 +242,7 @@ namespace osu.Game.Tests.Visual.Playlists break; case IndexPlaylistScoresRequest i: - triggerSuccess(i, createIndexResponse(i)); + triggerSuccess(i, createIndexResponse(i, noScores)); break; } }, delay); @@ -301,10 +324,12 @@ namespace osu.Game.Tests.Visual.Playlists return multiplayerUserScore; } - private IndexedMultiplayerScores createIndexResponse(IndexPlaylistScoresRequest req) + private IndexedMultiplayerScores createIndexResponse(IndexPlaylistScoresRequest req, bool noScores = false) { var result = new IndexedMultiplayerScores(); + if (noScores) return result; + string sort = req.IndexParams?.Properties["sort"].ToObject() ?? "score_desc"; for (int i = 1; i <= scores_per_result; i++) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index e3d19725da..e18ab63706 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -19,6 +19,7 @@ using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; +using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; using osu.Game.Online.API; @@ -204,17 +205,31 @@ namespace osu.Game.Screens.Ranking if (lastFetchCompleted) { - APIRequest nextPageRequest = null; - - if (ScorePanelList.IsScrolledToStart) - nextPageRequest = FetchNextPage(-1, fetchScoresCallback); - else if (ScorePanelList.IsScrolledToEnd) - nextPageRequest = FetchNextPage(1, fetchScoresCallback); - - if (nextPageRequest != null) + if (ScorePanelList.IsEmpty) { - lastFetchCompleted = false; - api.Queue(nextPageRequest); + // This can happen if a beatmap part of a playlist hasn't been played yet. + VerticalScrollContent.Add(new OsuSpriteText + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Font = OsuFont.GetFont(size: 32, weight: FontWeight.Regular), + Text = "no scores yet!", + }); + } + else + { + APIRequest nextPageRequest = null; + + if (ScorePanelList.IsScrolledToStart) + nextPageRequest = FetchNextPage(-1, fetchScoresCallback); + else if (ScorePanelList.IsScrolledToEnd) + nextPageRequest = FetchNextPage(1, fetchScoresCallback); + + if (nextPageRequest != null) + { + lastFetchCompleted = false; + api.Queue(nextPageRequest); + } } } } diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index b75f3d86ff..95c90e35a0 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -49,6 +49,8 @@ namespace osu.Game.Screens.Ranking public bool AllPanelsVisible => flow.All(p => p.IsPresent); + public bool IsEmpty => flow.Count == 0; + /// /// The current scroll position. /// From 4a94cfd35d74060daf691aabf6cf6235a1282344 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sun, 31 Dec 2023 16:47:10 +0300 Subject: [PATCH 0793/1118] Fix failing test case --- .../Visual/UserInterface/TestSceneFooterButtonMods.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs index 4e1bf1390a..b98b91e8e5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs @@ -5,10 +5,12 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; +using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Screens.Select; +using osu.Game.Utils; namespace osu.Game.Tests.Visual.UserInterface { @@ -74,7 +76,7 @@ namespace osu.Game.Tests.Visual.UserInterface private bool assertModsMultiplier(IEnumerable mods) { double multiplier = mods.Aggregate(1.0, (current, mod) => current * mod.ScoreMultiplier); - string expectedValue = multiplier.Equals(1.0) ? string.Empty : $"{multiplier:N2}x"; + string expectedValue = multiplier == 1 ? string.Empty : ModUtils.FormatScoreMultiplier(multiplier).ToString(); return expectedValue == footerButtonMods.MultiplierText.Current.Value; } From ca0f09733ab7741a1ee583d710bafead4fe86039 Mon Sep 17 00:00:00 2001 From: CaffeeLake Date: Sun, 31 Dec 2023 23:35:42 +0900 Subject: [PATCH 0794/1118] Remove unnecessary using directives Signed-off-by: CaffeeLake --- osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs index b98b91e8e5..a95bb2c9e3 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneFooterButtonMods.cs @@ -5,7 +5,6 @@ using System; using System.Collections.Generic; using System.Linq; using NUnit.Framework; -using osu.Framework.Localisation; using osu.Game.Graphics.Sprites; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu.Mods; From ad4b5f6deddc8cab855be638d7c77a358e6bf05a Mon Sep 17 00:00:00 2001 From: CaffeeLake Date: Mon, 1 Jan 2024 08:32:21 +0900 Subject: [PATCH 0795/1118] Fix: floating point errors Signed-off-by: CaffeeLake --- osu.Game/Utils/ModUtils.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index dad8c72aa1..252579a186 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -238,9 +238,9 @@ namespace osu.Game.Utils { // Round multiplier values away from 1.00x to two significant digits. if (scoreMultiplier > 1) - scoreMultiplier = Math.Ceiling(scoreMultiplier * 100) / 100; + scoreMultiplier = Math.Ceiling(Math.Round(scoreMultiplier * 100, 12)) / 100; else - scoreMultiplier = Math.Floor(scoreMultiplier * 100) / 100; + scoreMultiplier = Math.Floor(Math.Round(scoreMultiplier * 100, 12)) / 100; return scoreMultiplier.ToLocalisableString("0.00x"); } From 94531807e476c774c42df3ef0ef745608eb80534 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 2 Jan 2024 17:01:32 +0900 Subject: [PATCH 0796/1118] Make slider ends show on results screen again --- osu.Game.Rulesets.Osu/OsuRuleset.cs | 2 ++ osu.Game/Scoring/ScoreInfo.cs | 1 + 2 files changed, 3 insertions(+) diff --git a/osu.Game.Rulesets.Osu/OsuRuleset.cs b/osu.Game.Rulesets.Osu/OsuRuleset.cs index 5959576b9d..0496d1f680 100644 --- a/osu.Game.Rulesets.Osu/OsuRuleset.cs +++ b/osu.Game.Rulesets.Osu/OsuRuleset.cs @@ -277,6 +277,7 @@ namespace osu.Game.Rulesets.Osu HitResult.LargeTickHit, HitResult.SmallTickHit, + HitResult.SliderTailHit, HitResult.SmallBonus, HitResult.LargeBonus, }; @@ -289,6 +290,7 @@ namespace osu.Game.Rulesets.Osu case HitResult.LargeTickHit: return "slider tick"; + case HitResult.SliderTailHit: case HitResult.SmallTickHit: return "slider end"; diff --git a/osu.Game/Scoring/ScoreInfo.cs b/osu.Game/Scoring/ScoreInfo.cs index 44795c6fa7..32e4bbbf29 100644 --- a/osu.Game/Scoring/ScoreInfo.cs +++ b/osu.Game/Scoring/ScoreInfo.cs @@ -350,6 +350,7 @@ namespace osu.Game.Scoring { case HitResult.SmallTickHit: case HitResult.LargeTickHit: + case HitResult.SliderTailHit: case HitResult.LargeBonus: case HitResult.SmallBonus: if (MaximumStatistics.TryGetValue(r.result, out int count) && count > 0) From b0cfea491675a012694576168396ba1c433133e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jan 2024 10:11:49 +0100 Subject: [PATCH 0797/1118] Fix standardised score conversion failing for some taiko scores due to overestimating accuracy portion Standardised score conversion would return a negative total score for https://osu.ppy.sh/scores/taiko/182230346. The underlying reason for this is that the estimation of the accuracy portion for a score can be above the actual accuracy portion in the taiko ruleset. When calculating the maximum accuracy portion achievable, `TaikoLegacyScoreSimulator` will include the extra 300 points from a double hit on a strong hit, per strong hit. However, this double hit is not factored into accuracy. Both of the aforementioned facts mean that in taiko maximumLegacyAccuracyScore * score.Accuracy - which normally in other rulesets can be used pretty reliably as the exact number of points gained from the accuracy portion - is an estimate in the case of taiko, and an _upper_ estimate at that, because it implicitly assumes that the user has also hit `score.Accuracy` percent of all double hits possible in the beatmap. If this assumption is not upheld, then the user will have earned _less_ points than that from the accuracy portion, which means that the combo proportion estimate will go below zero. It is possible that this has happened on other scores before, but did not result in the total score going negative as the accuracy portion gained would have counteracted the effect of that due to being larger in magnitude than the score loss incurred from the negative combo portion. In the case of the score in question this was not the case due to very low accuracy _and_ very low max combo. --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 66c816e796..9cfb9ea957 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -316,7 +316,7 @@ namespace osu.Game.Database // when playing a beatmap with no bonus objects, with mods that have a 0.0x multiplier on stable (relax/autopilot). // In such cases, just assume 0. double comboProportion = maximumLegacyComboScore + maximumLegacyBonusScore > 0 - ? ((double)score.LegacyTotalScore - legacyAccScore) / (maximumLegacyComboScore + maximumLegacyBonusScore) + ? Math.Max((double)score.LegacyTotalScore - legacyAccScore, 0) / (maximumLegacyComboScore + maximumLegacyBonusScore) : 0; // We assume the bonus proportion only makes up the rest of the score that exceeds maximumLegacyBaseScore. From 3c5e9ac9a9cfc224905f8f8c6da66ed143cc7e54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jan 2024 10:49:09 +0100 Subject: [PATCH 0798/1118] Fix possible double score submission when auto-retrying via perfect mod Closes https://github.com/ppy/osu/issues/26035. `submitOnFailOrQuit()`, as the name suggests, can be called both when the player has failed, or when the player screen is being exited from. Notably, when perfect mod with auto-retry is active, the two happen almost simultaneously. This double call exposes a data race in `submitScore()` concerning the handling of `scoreSubmissionSource`. The race could be experimentally confirmed by applying the following patch: diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 83adf1f960..76dd29bbdb 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -228,6 +228,7 @@ private Task submitScore(Score score) return Task.CompletedTask; } + Logger.Log($"{nameof(scoreSubmissionSource)} is {(scoreSubmissionSource == null ? "null" : "not null")}"); if (scoreSubmissionSource != null) return scoreSubmissionSource.Task; @@ -237,6 +238,7 @@ private Task submitScore(Score score) Logger.Log($"Beginning score submission (token:{token.Value})..."); + Logger.Log($"creating new {nameof(scoreSubmissionSource)}"); scoreSubmissionSource = new TaskCompletionSource(); var request = CreateSubmissionRequest(score, token.Value); which would result in the following log output: [runtime] 2024-01-02 09:54:13 [verbose]: scoreSubmissionSource is null [runtime] 2024-01-02 09:54:13 [verbose]: scoreSubmissionSource is null [runtime] 2024-01-02 09:54:13 [verbose]: Beginning score submission (token:36780)... [runtime] 2024-01-02 09:54:13 [verbose]: creating new scoreSubmissionSource [runtime] 2024-01-02 09:54:13 [verbose]: Beginning score submission (token:36780)... [runtime] 2024-01-02 09:54:13 [verbose]: creating new scoreSubmissionSource [network] 2024-01-02 09:54:13 [verbose]: Performing request osu.Game.Online.Solo.SubmitSoloScoreRequest [network] 2024-01-02 09:54:14 [verbose]: Request to https://dev.ppy.sh/api/v2/beatmaps/869310/solo/scores/36780 successfully completed! [network] 2024-01-02 09:54:14 [verbose]: SubmitSoloScoreRequest finished with response size of 639 bytes [network] 2024-01-02 09:54:14 [verbose]: Performing request osu.Game.Online.Solo.SubmitSoloScoreRequest [runtime] 2024-01-02 09:54:14 [verbose]: Score submission completed! (token:36780 id:20247) [network] 2024-01-02 09:54:14 [verbose]: Request to https://dev.ppy.sh/api/v2/beatmaps/869310/solo/scores/36780 successfully completed! [network] 2024-01-02 09:54:14 [verbose]: SubmitSoloScoreRequest finished with response size of 639 bytes [runtime] 2024-01-02 09:54:14 [error]: An unhandled error has occurred. [runtime] 2024-01-02 09:54:14 [error]: System.InvalidOperationException: An attempt was made to transition a task to a final state when it had already completed. [runtime] 2024-01-02 09:54:14 [error]: at osu.Game.Screens.Play.SubmittingPlayer.<>c__DisplayClass30_0.b__0(MultiplayerScore s) in /home/dachb/Documents/opensource/osu/osu.Game/Screens/Play/SubmittingPlayer.cs:line 250 The intention of the submission logic was to only ever create one `scoreSubmissionSource`, and then reuse this one if a redundant submission request was made. However, because of the temporal proximity of fail and quit in this particular case, combined with the fact that the calls to `submitScore()` are taking place on TPL threads, means that there is a read-write data race on `scoreSubmissionSource`, wherein the source can be actually created twice. This leads to two concurrent score submission requests, which, upon completion, attempt to transition only _the second_ `scoreSubmissionSource` to a final state (this is because the API success/failure request callbacks capture `this`, i.e. the entire `SubmittingPlayer` instance, rather than the `scoreSubmissionSource` reference specifically). To fix, ensure correct synchronisation on the read-write critical section, which should prevent the `scoreSubmissionSource` from being created multiple times. --- osu.Game/Screens/Play/SubmittingPlayer.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 83adf1f960..fb3481cbc4 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -41,6 +41,7 @@ namespace osu.Game.Screens.Play [Resolved] private SessionStatics statics { get; set; } + private readonly object scoreSubmissionLock = new object(); private TaskCompletionSource scoreSubmissionSource; protected SubmittingPlayer(PlayerConfiguration configuration = null) @@ -228,16 +229,19 @@ namespace osu.Game.Screens.Play return Task.CompletedTask; } - if (scoreSubmissionSource != null) - return scoreSubmissionSource.Task; + lock (scoreSubmissionLock) + { + if (scoreSubmissionSource != null) + return scoreSubmissionSource.Task; + + scoreSubmissionSource = new TaskCompletionSource(); + } // if the user never hit anything, this score should not be counted in any way. if (!score.ScoreInfo.Statistics.Any(s => s.Key.IsHit() && s.Value > 0)) return Task.CompletedTask; Logger.Log($"Beginning score submission (token:{token.Value})..."); - - scoreSubmissionSource = new TaskCompletionSource(); var request = CreateSubmissionRequest(score, token.Value); request.Success += s => From 72e502e6156b58474c8a6c9517b6f4c73ad420b3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jan 2024 11:55:59 +0100 Subject: [PATCH 0799/1118] Fix backwards z-ordering of fruits in juice streams and banana showers Closes https://github.com/ppy/osu/issues/25827. The logic cannot be easily abstracted out (because `CompareReverseChildID()` is protected and non-static on `CompositeDrawable`), so a local copy was applied instead. No testing as any testing would have been purely visual anyways. --- .../Objects/Drawables/DrawableBananaShower.cs | 2 +- .../Objects/Drawables/DrawableJuiceStream.cs | 2 +- .../Objects/Drawables/NestedFruitContainer.cs | 26 +++++++++++++++++++ 3 files changed, 28 insertions(+), 2 deletions(-) create mode 100644 osu.Game.Rulesets.Catch/Objects/Drawables/NestedFruitContainer.cs diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs index 03adbce885..9ee4a15182 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableBananaShower.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables RelativeSizeAxes = Axes.X; Origin = Anchor.BottomLeft; - AddInternal(bananaContainer = new Container { RelativeSizeAxes = Axes.Both }); + AddInternal(bananaContainer = new NestedFruitContainer { RelativeSizeAxes = Axes.Both }); } protected override void AddNestedHitObject(DrawableHitObject hitObject) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs index 41ecf59276..677b61df47 100644 --- a/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/DrawableJuiceStream.cs @@ -22,7 +22,7 @@ namespace osu.Game.Rulesets.Catch.Objects.Drawables RelativeSizeAxes = Axes.X; Origin = Anchor.BottomLeft; - AddInternal(dropletContainer = new Container { RelativeSizeAxes = Axes.Both, }); + AddInternal(dropletContainer = new NestedFruitContainer { RelativeSizeAxes = Axes.Both, }); } protected override void AddNestedHitObject(DrawableHitObject hitObject) diff --git a/osu.Game.Rulesets.Catch/Objects/Drawables/NestedFruitContainer.cs b/osu.Game.Rulesets.Catch/Objects/Drawables/NestedFruitContainer.cs new file mode 100644 index 0000000000..90bdb0237e --- /dev/null +++ b/osu.Game.Rulesets.Catch/Objects/Drawables/NestedFruitContainer.cs @@ -0,0 +1,26 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Game.Rulesets.UI; + +namespace osu.Game.Rulesets.Catch.Objects.Drawables +{ + public partial class NestedFruitContainer : Container + { + /// + /// This comparison logic is a copy of comparison logic, + /// which can't be easily extracted to a more common place. + /// + /// + protected override int Compare(Drawable x, Drawable y) + { + if (x is not DrawableCatchHitObject xObj || y is not DrawableCatchHitObject yObj) + return base.Compare(x, y); + + int result = yObj.HitObject.StartTime.CompareTo(xObj.HitObject.StartTime); + return result == 0 ? CompareReverseChildID(x, y) : result; + } + } +} From f9f03ebc0f066dc8d7c23764fc3d88e4d6278bc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jan 2024 14:04:40 +0100 Subject: [PATCH 0800/1118] Store user online state in config for next launch Closes remainder of https://github.com/ppy/osu/issues/12635. --- osu.Game/Configuration/OsuConfigManager.cs | 3 +++ osu.Game/Online/API/APIAccess.cs | 15 +++++++++++++-- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index d4162b76d6..23686db1f8 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -20,6 +20,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; using osu.Game.Skinning; +using osu.Game.Users; namespace osu.Game.Configuration { @@ -193,6 +194,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.LastProcessedMetadataId, -1); SetDefault(OsuSetting.ComboColourNormalisationAmount, 0.2f, 0f, 1f, 0.01f); + SetDefault(OsuSetting.UserOnlineStatus, null); } protected override bool CheckLookupContainsPrivateInformation(OsuSetting lookup) @@ -420,5 +422,6 @@ namespace osu.Game.Configuration EditorShowSpeedChanges, TouchDisableGameplayTaps, ModSelectTextSearchStartsActive, + UserOnlineStatus, } } diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 21107d61fc..be5bdeca77 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -62,6 +62,9 @@ namespace osu.Game.Online.API private Bindable activity { get; } = new Bindable(); + private Bindable configStatus { get; } = new Bindable(); + private Bindable localUserStatus { get; } = new Bindable(); + protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password)); private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); @@ -85,12 +88,20 @@ namespace osu.Game.Online.API authentication.TokenString = config.Get(OsuSetting.Token); authentication.Token.ValueChanged += onTokenChanged; + config.BindWith(OsuSetting.UserOnlineStatus, configStatus); + localUser.BindValueChanged(u => { u.OldValue?.Activity.UnbindFrom(activity); u.NewValue.Activity.BindTo(activity); + + if (u.OldValue != null) + localUserStatus.UnbindFrom(u.OldValue.Status); + localUserStatus.BindTo(u.NewValue.Status); }, true); + localUserStatus.BindValueChanged(val => configStatus.Value = val.NewValue); + var thread = new Thread(run) { Name = "APIAccess", @@ -200,6 +211,7 @@ namespace osu.Game.Online.API setLocalUser(new APIUser { Username = ProvidedUsername, + Status = { Value = configStatus.Value ?? UserStatus.Online } }); } @@ -246,8 +258,7 @@ namespace osu.Game.Online.API }; userReq.Success += user => { - // todo: save/pull from settings - user.Status.Value = UserStatus.Online; + user.Status.Value = configStatus.Value ?? UserStatus.Online; setLocalUser(user); From d4e917448d98e6409982719231f8b53e4340e11a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jan 2024 14:07:04 +0100 Subject: [PATCH 0801/1118] Fix login panel dropdown forcing user online It was sort of assuming that the user can't be anything but online when opening, thus forcing the status to online via the immediately-run value change callback. --- osu.Game/Overlays/Login/LoginPanel.cs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs index 19af95459f..b0af2fa273 100644 --- a/osu.Game/Overlays/Login/LoginPanel.cs +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -143,6 +143,8 @@ namespace osu.Game.Overlays.Login panel.Status.BindTo(api.LocalUser.Value.Status); panel.Activity.BindTo(api.LocalUser.Value.Activity); + panel.Status.BindValueChanged(_ => updateDropdownCurrent(), true); + dropdown.Current.BindValueChanged(action => { switch (action.NewValue) @@ -174,6 +176,24 @@ namespace osu.Game.Overlays.Login ScheduleAfterChildren(() => GetContainingInputManager()?.ChangeFocus(form)); }); + private void updateDropdownCurrent() + { + switch (panel.Status.Value) + { + case UserStatus.Online: + dropdown.Current.Value = UserAction.Online; + break; + + case UserStatus.DoNotDisturb: + dropdown.Current.Value = UserAction.DoNotDisturb; + break; + + case UserStatus.Offline: + dropdown.Current.Value = UserAction.AppearOffline; + break; + } + } + public override bool AcceptsFocus => true; protected override bool OnClick(ClickEvent e) => true; From 09b2a4e3b42defa1ead3a156c2f8c890ed175473 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jan 2024 14:07:59 +0100 Subject: [PATCH 0802/1118] Fix users blipping online briefly before their online status is known --- osu.Game/Online/Metadata/OnlineMetadataClient.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/osu.Game/Online/Metadata/OnlineMetadataClient.cs b/osu.Game/Online/Metadata/OnlineMetadataClient.cs index 6d00ce7551..c42c3378b7 100644 --- a/osu.Game/Online/Metadata/OnlineMetadataClient.cs +++ b/osu.Game/Online/Metadata/OnlineMetadataClient.cs @@ -23,7 +23,6 @@ namespace osu.Game.Online.Metadata public override IBindable IsWatchingUserPresence => isWatchingUserPresence; private readonly BindableBool isWatchingUserPresence = new BindableBool(); - // ReSharper disable once InconsistentlySynchronizedField public override IBindableDictionary UserStates => userStates; private readonly BindableDictionary userStates = new BindableDictionary(); @@ -192,7 +191,7 @@ namespace osu.Game.Online.Metadata { Schedule(() => { - if (presence != null) + if (presence?.Status != null) userStates[userId] = presence.Value; else userStates.Remove(userId); From 5eaf5fca2ad8d162835212151b8c1c530f7ca0f1 Mon Sep 17 00:00:00 2001 From: StanR Date: Tue, 2 Jan 2024 20:33:36 +0600 Subject: [PATCH 0803/1118] Add user card with global/country ranks for login overlay --- osu.Game/Overlays/Login/LoginPanel.cs | 7 +- osu.Game/Users/ExtendedUserPanel.cs | 9 -- osu.Game/Users/UserPanel.cs | 41 +++-- osu.Game/Users/UserRankPanel.cs | 212 ++++++++++++++++++++++++++ 4 files changed, 241 insertions(+), 28 deletions(-) create mode 100644 osu.Game/Users/UserRankPanel.cs diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs index 19af95459f..892d613ea4 100644 --- a/osu.Game/Overlays/Login/LoginPanel.cs +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -30,7 +30,7 @@ namespace osu.Game.Overlays.Login [Resolved] private OsuColour colours { get; set; } = null!; - private UserGridPanel panel = null!; + private UserRankPanel panel = null!; private UserDropdown dropdown = null!; /// @@ -131,7 +131,7 @@ namespace osu.Game.Overlays.Login Text = LoginPanelStrings.SignedIn, Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold), }, - panel = new UserGridPanel(api.LocalUser.Value) + panel = new UserRankPanel(api.LocalUser.Value) { RelativeSizeAxes = Axes.X, Action = RequestHide @@ -140,9 +140,6 @@ namespace osu.Game.Overlays.Login }, }; - panel.Status.BindTo(api.LocalUser.Value.Status); - panel.Activity.BindTo(api.LocalUser.Value.Activity); - dropdown.Current.BindValueChanged(action => { switch (action.NewValue) diff --git a/osu.Game/Users/ExtendedUserPanel.cs b/osu.Game/Users/ExtendedUserPanel.cs index 1359f5d792..e33fb7a44e 100644 --- a/osu.Game/Users/ExtendedUserPanel.cs +++ b/osu.Game/Users/ExtendedUserPanel.cs @@ -3,7 +3,6 @@ #nullable disable -using osuTK; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions; @@ -53,14 +52,6 @@ namespace osu.Game.Users statusIcon.FinishTransforms(); } - protected UpdateableAvatar CreateAvatar() => new UpdateableAvatar(User, false); - - protected UpdateableFlag CreateFlag() => new UpdateableFlag(User.CountryCode) - { - Size = new Vector2(36, 26), - Action = Action, - }; - protected Container CreateStatusIcon() => statusIcon = new StatusIcon(); protected FillFlowContainer CreateStatusMessage(bool rightAlignedChildren) diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index b6a77e754d..d38f5d8ccc 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -23,6 +23,8 @@ using osu.Game.Localisation; using osu.Game.Online.Multiplayer; using osu.Game.Screens; using osu.Game.Screens.Play; +using osu.Game.Users.Drawables; +using osuTK; namespace osu.Game.Users { @@ -77,23 +79,18 @@ namespace osu.Game.Users { Masking = true; - AddRange(new[] + Add(new Box { - new Box - { - RelativeSizeAxes = Axes.Both, - Colour = ColourProvider?.Background5 ?? Colours.Gray1 - }, - Background = new UserCoverBackground - { - RelativeSizeAxes = Axes.Both, - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - User = User, - }, - CreateLayout() + RelativeSizeAxes = Axes.Both, + Colour = ColourProvider?.Background5 ?? Colours.Gray1 }); + var background = CreateBackground(); + if (background != null) + Add(background); + + Add(CreateLayout()); + base.Action = ViewProfile = () => { Action?.Invoke(); @@ -110,6 +107,22 @@ namespace osu.Game.Users Text = User.Username, }; + protected virtual Drawable? CreateBackground() => Background = new UserCoverBackground + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + User = User + }; + + protected UpdateableAvatar CreateAvatar() => new UpdateableAvatar(User, false); + + protected UpdateableFlag CreateFlag() => new UpdateableFlag(User.CountryCode) + { + Size = new Vector2(36, 26), + Action = Action, + }; + public MenuItem[] ContextMenuItems { get diff --git a/osu.Game/Users/UserRankPanel.cs b/osu.Game/Users/UserRankPanel.cs new file mode 100644 index 0000000000..272d2eecb4 --- /dev/null +++ b/osu.Game/Users/UserRankPanel.cs @@ -0,0 +1,212 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Allocation; +using osu.Framework.Extensions.LocalisationExtensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; +using osu.Framework.Input.Events; +using osu.Framework.Localisation; +using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays.Profile.Header.Components; +using osu.Game.Resources.Localisation.Web; +using osuTK; + +namespace osu.Game.Users +{ + /// + /// User card that shows user's global and country ranks in the bottom. + /// Meant to be used in the toolbar login overlay. + /// + public partial class UserRankPanel : UserPanel + { + private const int padding = 10; + private const int main_content_height = 80; + + public UserRankPanel(APIUser user) + : base(user) + { + AutoSizeAxes = Axes.Y; + CornerRadius = 10; + } + + [BackgroundDependencyLoader] + private void load() + { + BorderColour = ColourProvider?.Light1 ?? Colours.GreyVioletLighter; + } + + protected override Drawable CreateLayout() + { + FillFlowContainer details; + + var layout = new Container + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + new Container + { + Name = "Main content", + RelativeSizeAxes = Axes.X, + Height = main_content_height, + CornerRadius = 10, + Masking = true, + Children = new Drawable[] + { + new UserCoverBackground + { + RelativeSizeAxes = Axes.Both, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + User = User, + Alpha = 0.3f + }, + new GridContainer + { + AutoSizeAxes = Axes.Y, + RelativeSizeAxes = Axes.X, + ColumnDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, padding), + new Dimension(GridSizeMode.AutoSize), + new Dimension(), + new Dimension(GridSizeMode.Absolute, padding), + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.Absolute, padding), + new Dimension(GridSizeMode.AutoSize), + }, + Content = new[] + { + new[] + { + Empty(), + Empty(), + Empty(), + Empty() + }, + new[] + { + Empty(), + CreateAvatar().With(avatar => + { + avatar.Size = new Vector2(60); + avatar.Masking = true; + avatar.CornerRadius = 6; + }), + new Container + { + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding { Left = padding }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.Both, + ColumnDimensions = new[] + { + new Dimension() + }, + RowDimensions = new[] + { + new Dimension(GridSizeMode.AutoSize), + new Dimension() + }, + Content = new[] + { + new Drawable[] + { + details = new FillFlowContainer + { + AutoSizeAxes = Axes.Both, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(6), + Children = new Drawable[] + { + CreateFlag(), + } + } + }, + new Drawable[] + { + CreateUsername().With(username => + { + username.Anchor = Anchor.CentreLeft; + username.Origin = Anchor.CentreLeft; + }) + } + } + } + }, + Empty() + } + } + } + } + }, + new Container + { + Name = "Bottom content", + Margin = new MarginPadding { Top = main_content_height }, + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Padding = new MarginPadding { Left = 80, Vertical = padding }, + Child = new GridContainer + { + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + ColumnDimensions = new[] + { + new Dimension(), + new Dimension() + }, + RowDimensions = new[] { new Dimension(GridSizeMode.AutoSize) }, + Content = new[] + { + new Drawable[] + { + new ProfileValueDisplay(true) + { + Title = UsersStrings.ShowRankGlobalSimple, + Content = User.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-" + }, + new ProfileValueDisplay(true) + { + Title = UsersStrings.ShowRankCountrySimple, + Content = User.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-" + } + } + } + } + } + } + }; + + if (User.IsSupporter) + { + details.Add(new SupporterIcon + { + Height = 26, + SupportLevel = User.SupportLevel + }); + } + + return layout; + } + + protected override bool OnHover(HoverEvent e) + { + BorderThickness = 2; + return base.OnHover(e); + } + + protected override void OnHoverLost(HoverLostEvent e) + { + BorderThickness = 0; + base.OnHoverLost(e); + } + + protected override Drawable? CreateBackground() => null; + } +} From 17656e9b9cfe4e6ce3fa2627bb1b3e34694ad305 Mon Sep 17 00:00:00 2001 From: Lena Date: Tue, 2 Jan 2024 18:38:25 +0100 Subject: [PATCH 0804/1118] update the current activity when the multiplayer room updates --- .../Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 7c12e6eab5..56256bb15c 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -349,6 +349,8 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer addItemButton.Alpha = localUserCanAddItem ? 1 : 0; Scheduler.AddOnce(UpdateMods); + + Activity.Value = new UserActivity.InLobby(Room); } private bool localUserCanAddItem => client.IsHost || Room.QueueMode.Value != QueueMode.HostOnly; From c4be6fa9741c5fdcf8c347bdec9bf5fe3641d3fb Mon Sep 17 00:00:00 2001 From: StanR Date: Wed, 3 Jan 2024 00:37:24 +0600 Subject: [PATCH 0805/1118] Fix code quality, add new cards to the test scene --- .../Visual/Online/TestSceneUserPanel.cs | 25 +++++++++++++++++-- osu.Game/Overlays/Login/LoginPanel.cs | 3 +-- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs index b3b8fd78d3..81e17cd1b8 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs @@ -11,6 +11,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Overlays; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; using osu.Game.Scoring; @@ -29,6 +30,9 @@ namespace osu.Game.Tests.Visual.Online private UserGridPanel boundPanel1; private TestUserListPanel boundPanel2; + [Cached] + private OverlayColourProvider colourProvider = new OverlayColourProvider(OverlayColourScheme.Purple); + [Resolved] private IRulesetStore rulesetStore { get; set; } @@ -85,8 +89,25 @@ namespace osu.Game.Tests.Visual.Online CoverUrl = @"https://assets.ppy.sh/user-profile-covers/8195163/4a8e2ad5a02a2642b631438cfa6c6bd7e2f9db289be881cb27df18331f64144c.jpeg", IsOnline = false, LastVisit = DateTimeOffset.Now - }) - }, + }), + new UserRankPanel(new APIUser + { + Username = @"flyte", + Id = 3103765, + CountryCode = CountryCode.JP, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c6.jpg", + Statistics = new UserStatistics { GlobalRank = 12345, CountryRank = 1234 } + }) { Width = 300 }, + new UserRankPanel(new APIUser + { + Username = @"peppy", + Id = 2, + Colour = "99EB47", + CountryCode = CountryCode.AU, + CoverUrl = @"https://osu.ppy.sh/images/headers/profile-covers/c3.jpg", + Statistics = new UserStatistics { GlobalRank = null, CountryRank = null } + }) { Width = 300 } + } }; boundPanel1.Status.BindTo(status); diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs index 892d613ea4..57a9f4035c 100644 --- a/osu.Game/Overlays/Login/LoginPanel.cs +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -30,7 +30,6 @@ namespace osu.Game.Overlays.Login [Resolved] private OsuColour colours { get; set; } = null!; - private UserRankPanel panel = null!; private UserDropdown dropdown = null!; /// @@ -131,7 +130,7 @@ namespace osu.Game.Overlays.Login Text = LoginPanelStrings.SignedIn, Font = OsuFont.GetFont(size: 18, weight: FontWeight.Bold), }, - panel = new UserRankPanel(api.LocalUser.Value) + new UserRankPanel(api.LocalUser.Value) { RelativeSizeAxes = Axes.X, Action = RequestHide From 88509f28ad66110f0b8b7fb79e69aeccce7e5929 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jan 2024 20:51:22 +0100 Subject: [PATCH 0806/1118] Adjust assertion to match new juice stream child ordering --- osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs index 419a846ec3..825e8c697c 100644 --- a/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs +++ b/osu.Game.Rulesets.Catch.Tests/TestSceneCatchModHidden.cs @@ -39,7 +39,7 @@ namespace osu.Game.Rulesets.Catch.Tests Mod = new CatchModHidden(), PassCondition = () => Player.Results.Count > 0 && Player.ChildrenOfType().Single().Alpha > 0 - && Player.ChildrenOfType().Last().Alpha > 0 + && Player.ChildrenOfType().First().Alpha > 0 }); } From bdfaa4b583912ebe4bba70a0d48c13ef1e79b919 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jan 2024 13:34:49 +0900 Subject: [PATCH 0807/1118] Fix crash when dragging rotation control in editor with both mouse buttons Closes https://github.com/ppy/osu/issues/26325. --- .../Edit/Compose/Components/SelectionBoxRotationHandle.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs index 024749a701..5270162189 100644 --- a/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs +++ b/osu.Game/Screens/Edit/Compose/Components/SelectionBoxRotationHandle.cs @@ -62,6 +62,9 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnDragStart(DragStartEvent e) { + if (e.Button != MouseButton.Left) + return false; + if (rotationHandler == null) return false; rotationHandler.Begin(); From 9e8d07d3144bd4b072d28bd9bd0e255fee410de0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 27 Dec 2023 15:47:05 +0900 Subject: [PATCH 0808/1118] Add NVAPI and force thread optimisations on This undoes what stable does to force this setting off. --- osu.Desktop/NVAPI.cs | 737 +++++++++++++++++++++++++++++++++++++++++ osu.Desktop/Program.cs | 2 + 2 files changed, 739 insertions(+) create mode 100644 osu.Desktop/NVAPI.cs diff --git a/osu.Desktop/NVAPI.cs b/osu.Desktop/NVAPI.cs new file mode 100644 index 0000000000..9648ea1072 --- /dev/null +++ b/osu.Desktop/NVAPI.cs @@ -0,0 +1,737 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using osu.Framework.Logging; + +namespace osu.Desktop +{ + [SuppressMessage("ReSharper", "InconsistentNaming")] + internal static class NVAPI + { + private const string osu_filename = "osu!.exe"; + + // This is a good reference: + // https://github.com/errollw/Warp-and-Blend-Quadros/blob/master/WarpBlend-Quadros/UnwarpAll-Quadros/include/nvapi.h + // Note our Stride == their VERSION (e.g. NVDRS_SETTING_VER) + + public const int MAX_PHYSICAL_GPUS = 64; + public const int UNICODE_STRING_MAX = 2048; + + public const string APPLICATION_NAME = @"osu!"; + public const string PROFILE_NAME = @"osu!"; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus EnumPhysicalGPUsDelegate([Out] IntPtr[] gpuHandles, out int gpuCount); + + public static readonly EnumPhysicalGPUsDelegate EnumPhysicalGPUs; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus EnumLogicalGPUsDelegate([Out] IntPtr[] gpuHandles, out int gpuCount); + + public static readonly EnumLogicalGPUsDelegate EnumLogicalGPUs; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus GetSystemTypeDelegate(IntPtr gpuHandle, out NvSystemType systemType); + + public static readonly GetSystemTypeDelegate GetSystemType; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus GetGPUTypeDelegate(IntPtr gpuHandle, out NvGpuType gpuType); + + public static readonly GetGPUTypeDelegate GetGPUType; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus CreateSessionDelegate(out IntPtr sessionHandle); + + public static CreateSessionDelegate CreateSession; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus LoadSettingsDelegate(IntPtr sessionHandle); + + public static LoadSettingsDelegate LoadSettings; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus FindApplicationByNameDelegate(IntPtr sessionHandle, [MarshalAs(UnmanagedType.BStr)] string appName, out IntPtr profileHandle, ref NvApplication application); + + public static FindApplicationByNameDelegate FindApplicationByName; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus GetCurrentGlobalProfileDelegate(IntPtr sessionHandle, out IntPtr profileHandle); + + public static GetCurrentGlobalProfileDelegate GetCurrentGlobalProfile; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus GetProfileInfoDelegate(IntPtr sessionHandle, IntPtr profileHandle, ref NvProfile profile); + + public static GetProfileInfoDelegate GetProfileInfo; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + public delegate NvStatus GetSettingDelegate(IntPtr sessionHandle, IntPtr profileHandle, NvSettingID settingID, ref NvSetting setting); + + public static GetSettingDelegate GetSetting; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate NvStatus CreateProfileDelegate(IntPtr sessionHandle, ref NvProfile profile, out IntPtr profileHandle); + + private static readonly CreateProfileDelegate CreateProfile; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate NvStatus SetSettingDelegate(IntPtr sessionHandle, IntPtr profileHandle, ref NvSetting setting); + + private static readonly SetSettingDelegate SetSetting; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate NvStatus EnumApplicationsDelegate(IntPtr sessionHandle, IntPtr profileHandle, uint startIndex, ref uint appCount, [In, Out, MarshalAs(UnmanagedType.LPArray)] NvApplication[] applications); + + private static readonly EnumApplicationsDelegate EnumApplications; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate NvStatus CreateApplicationDelegate(IntPtr sessionHandle, IntPtr profileHandle, ref NvApplication application); + + private static readonly CreateApplicationDelegate CreateApplication; + + [UnmanagedFunctionPointer(CallingConvention.Cdecl)] + private delegate NvStatus SaveSettingsDelegate(IntPtr sessionHandle); + + private static readonly SaveSettingsDelegate SaveSettings; + + public static NvStatus Status { get; private set; } = NvStatus.OK; + public static bool Available { get; private set; } + + private static IntPtr sessionHandle; + + public static bool IsUsingOptimusDedicatedGpu + { + get + { + if (!Available) + return false; + + if (!IsLaptop) + return false; + + IntPtr profileHandle; + if (!getProfile(out profileHandle, out _, out bool _)) + return false; + + // Get the optimus setting + NvSetting setting; + if (!getSetting(NvSettingID.SHIM_RENDERING_MODE_ID, profileHandle, out setting)) + return false; + + return (setting.U32CurrentValue & (uint)NvShimSetting.SHIM_RENDERING_MODE_ENABLE) > 0; + } + } + + public static bool IsLaptop + { + get + { + if (!Available) + return false; + + // Make sure that this is a laptop. + var gpus = new IntPtr[64]; + if (checkError(EnumPhysicalGPUs(gpus, out int gpuCount))) + return false; + + for (int i = 0; i < gpuCount; i++) + { + if (checkError(GetSystemType(gpus[i], out var type))) + return false; + + if (type == NvSystemType.LAPTOP) + return true; + } + + return false; + } + } + + public static bool ThreadedOptimisations + { + get + { + if (!Available) + return false; + + IntPtr profileHandle; + if (!getProfile(out profileHandle, out _, out bool _)) + return false; + + // Get the threaded optimisations setting + NvSetting setting; + if (!getSetting(NvSettingID.OGL_THREAD_CONTROL_ID, profileHandle, out setting)) + return false; + + return setting.U32CurrentValue != (uint)NvThreadControlSetting.OGL_THREAD_CONTROL_DISABLE; + } + set + { + if (!Available) + return; + + bool success = setSetting(NvSettingID.OGL_THREAD_CONTROL_ID, (uint)(value ? NvThreadControlSetting.OGL_THREAD_CONTROL_ENABLE : NvThreadControlSetting.OGL_THREAD_CONTROL_DISABLE)); + + Logger.Log(success ? $"Threaded optimizations set to \"{value}\"!" : "Threaded optimizations set failed!"); + } + } + + /// + /// Checks if the profile contains the current application. + /// + /// If the profile contains the current application. + private static bool containsApplication(IntPtr profileHandle, NvProfile profile, out NvApplication application) + { + application = new NvApplication + { + Version = NvApplication.Stride + }; + + if (profile.NumOfApps == 0) + return false; + + NvApplication[] applications = new NvApplication[profile.NumOfApps]; + applications[0].Version = NvApplication.Stride; + + uint numApps = profile.NumOfApps; + + if (checkError(EnumApplications(sessionHandle, profileHandle, 0, ref numApps, applications))) + return false; + + for (uint i = 0; i < numApps; i++) + { + if (applications[i].AppName == osu_filename) + { + application = applications[i]; + return true; + } + } + + return false; + } + + /// + /// Retrieves the profile of the current application. + /// + /// The profile handle. + /// The current application description. + /// If this profile is not a global (default) profile. + /// If the operation succeeded. + private static bool getProfile(out IntPtr profileHandle, out NvApplication application, out bool isApplicationSpecific) + { + application = new NvApplication + { + Version = NvApplication.Stride + }; + + isApplicationSpecific = true; + + if (checkError(FindApplicationByName(sessionHandle, osu_filename, out profileHandle, ref application))) + { + isApplicationSpecific = false; + if (checkError(GetCurrentGlobalProfile(sessionHandle, out profileHandle))) + return false; + } + + return true; + } + + /// + /// Creates a profile. + /// + /// The profile handle. + /// If the operation succeeded. + private static bool createProfile(out IntPtr profileHandle) + { + NvProfile newProfile = new NvProfile + { + Version = NvProfile.Stride, + IsPredefined = 0, + ProfileName = PROFILE_NAME, + GPUSupport = new uint[32] + }; + + newProfile.GPUSupport[0] = 1; + + if (checkError(CreateProfile(sessionHandle, ref newProfile, out profileHandle))) + return false; + + return true; + } + + /// + /// Retrieves a setting from the profile. + /// + /// The setting to retrieve. + /// The profile handle to retrieve the setting from. + /// The setting. + /// If the operation succeeded. + private static bool getSetting(NvSettingID settingId, IntPtr profileHandle, out NvSetting setting) + { + setting = new NvSetting + { + Version = NvSetting.Stride, + SettingID = settingId + }; + + if (checkError(GetSetting(sessionHandle, profileHandle, settingId, ref setting))) + return false; + + return true; + } + + private static bool setSetting(NvSettingID settingId, uint settingValue) + { + NvApplication application; + IntPtr profileHandle; + bool isApplicationSpecific; + if (!getProfile(out profileHandle, out application, out isApplicationSpecific)) + return false; + + if (!isApplicationSpecific) + { + // We don't want to interfere with the user's other settings, so let's create a separate config for osu! + if (!createProfile(out profileHandle)) + return false; + } + + NvSetting newSetting = new NvSetting + { + Version = NvSetting.Stride, + SettingID = settingId, + U32CurrentValue = settingValue + }; + + // Set the thread state + if (checkError(SetSetting(sessionHandle, profileHandle, ref newSetting))) + return false; + + // Get the profile (needed to check app count) + NvProfile profile = new NvProfile + { + Version = NvProfile.Stride + }; + if (checkError(GetProfileInfo(sessionHandle, profileHandle, ref profile))) + return false; + + if (!containsApplication(profileHandle, profile, out application)) + { + // Need to add the current application to the profile + application.IsPredefined = 0; + + application.AppName = osu_filename; + application.UserFriendlyName = APPLICATION_NAME; + + if (checkError(CreateApplication(sessionHandle, profileHandle, ref application))) + return false; + } + + // Save! + return !checkError(SaveSettings(sessionHandle)); + } + + /// + /// Creates a session to access the driver configuration. + /// + /// If the operation succeeded. + private static bool createSession() + { + if (checkError(CreateSession(out sessionHandle))) + return false; + + // Load settings into session + if (checkError(LoadSettings(sessionHandle))) + return false; + + return true; + } + + private static bool checkError(NvStatus status) + { + Status = status; + return status != NvStatus.OK; + } + + static NVAPI() + { + // TODO: check whether gpu vendor contains NVIDIA before attempting load? + + try + { + // Try to load NVAPI + if ((IntPtr.Size == 4 && loadLibrary(@"nvapi.dll") == IntPtr.Zero) + || (IntPtr.Size == 8 && loadLibrary(@"nvapi64.dll") == IntPtr.Zero)) + { + return; + } + + InitializeDelegate initialize; + getDelegate(0x0150E828, out initialize); + + if (initialize?.Invoke() == NvStatus.OK) + { + // IDs can be found here: https://github.com/jNizM/AHK_NVIDIA_NvAPI/blob/master/info/NvAPI_IDs.txt + + getDelegate(0xE5AC921F, out EnumPhysicalGPUs); + getDelegate(0x48B3EA59, out EnumLogicalGPUs); + getDelegate(0xBAAABFCC, out GetSystemType); + getDelegate(0xC33BAEB1, out GetGPUType); + getDelegate(0x0694D52E, out CreateSession); + getDelegate(0x375DBD6B, out LoadSettings); + getDelegate(0xEEE566B2, out FindApplicationByName); + getDelegate(0x617BFF9F, out GetCurrentGlobalProfile); + getDelegate(0x577DD202, out SetSetting); + getDelegate(0x61CD6FD6, out GetProfileInfo); + getDelegate(0x73BF8338, out GetSetting); + getDelegate(0xCC176068, out CreateProfile); + getDelegate(0x7FA2173A, out EnumApplications); + getDelegate(0x4347A9DE, out CreateApplication); + getDelegate(0xFCBC7E14, out SaveSettings); + } + + if (createSession()) + Available = true; + } + catch { } + } + + private static void getDelegate(uint id, out T newDelegate) where T : class + { + IntPtr ptr = IntPtr.Size == 4 ? queryInterface32(id) : queryInterface64(id); + newDelegate = ptr == IntPtr.Zero ? null : Marshal.GetDelegateForFunctionPointer(ptr, typeof(T)) as T; + } + + [DllImport("kernel32.dll", EntryPoint = "LoadLibrary")] + private static extern IntPtr loadLibrary(string dllToLoad); + + [DllImport(@"nvapi.dll", EntryPoint = "nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr queryInterface32(uint id); + + [DllImport(@"nvapi64.dll", EntryPoint = "nvapi_QueryInterface", CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr queryInterface64(uint id); + + private delegate NvStatus InitializeDelegate(); + } + + [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] + internal struct NvSetting + { + public uint Version; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)] + public string SettingName; + + public NvSettingID SettingID; + public uint SettingType; + public uint SettingLocation; + public uint IsCurrentPredefined; + public uint IsPredefinedValid; + + public uint U32PredefinedValue; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)] + public string StringPredefinedValue; + + public uint U32CurrentValue; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)] + public string StringCurrentValue; + + public static uint Stride => (uint)Marshal.SizeOf(typeof(NvSetting)) | (1 << 16); + } + + [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] + internal struct NvProfile + { + public uint Version; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)] + public string ProfileName; + + [MarshalAs(UnmanagedType.ByValArray)] + public uint[] GPUSupport; + + public uint IsPredefined; + public uint NumOfApps; + public uint NumOfSettings; + + public static uint Stride => (uint)Marshal.SizeOf(typeof(NvProfile)) | (1 << 16); + } + + [StructLayout(LayoutKind.Sequential, Pack = 8, CharSet = CharSet.Unicode)] + internal struct NvApplication + { + public uint Version; + public uint IsPredefined; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)] + public string AppName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)] + public string UserFriendlyName; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)] + public string Launcher; + + [MarshalAs(UnmanagedType.ByValTStr, SizeConst = NVAPI.UNICODE_STRING_MAX)] + public string FileInFolder; + + public static uint Stride => (uint)Marshal.SizeOf(typeof(NvApplication)) | (2 << 16); + } + + internal enum NvStatus + { + OK = 0, // Success. Request is completed. + ERROR = -1, // Generic error + LIBRARY_NOT_FOUND = -2, // NVAPI support library cannot be loaded. + NO_IMPLEMENTATION = -3, // not implemented in current driver installation + API_NOT_INITIALIZED = -4, // Initialize has not been called (successfully) + INVALID_ARGUMENT = -5, // The argument/parameter value is not valid or NULL. + NVIDIA_DEVICE_NOT_FOUND = -6, // No NVIDIA display driver, or NVIDIA GPU driving a display, was found. + END_ENUMERATION = -7, // No more items to enumerate + INVALID_HANDLE = -8, // Invalid handle + INCOMPATIBLE_STRUCT_VERSION = -9, // An argument's structure version is not supported + HANDLE_INVALIDATED = -10, // The handle is no longer valid (likely due to GPU or display re-configuration) + OPENGL_CONTEXT_NOT_CURRENT = -11, // No NVIDIA OpenGL context is current (but needs to be) + INVALID_POINTER = -14, // An invalid pointer, usually NULL, was passed as a parameter + NO_GL_EXPERT = -12, // OpenGL Expert is not supported by the current drivers + INSTRUMENTATION_DISABLED = -13, // OpenGL Expert is supported, but driver instrumentation is currently disabled + NO_GL_NSIGHT = -15, // OpenGL does not support Nsight + + EXPECTED_LOGICAL_GPU_HANDLE = -100, // Expected a logical GPU handle for one or more parameters + EXPECTED_PHYSICAL_GPU_HANDLE = -101, // Expected a physical GPU handle for one or more parameters + EXPECTED_DISPLAY_HANDLE = -102, // Expected an NV display handle for one or more parameters + INVALID_COMBINATION = -103, // The combination of parameters is not valid. + NOT_SUPPORTED = -104, // Requested feature is not supported in the selected GPU + PORTID_NOT_FOUND = -105, // No port ID was found for the I2C transaction + EXPECTED_UNATTACHED_DISPLAY_HANDLE = -106, // Expected an unattached display handle as one of the input parameters. + INVALID_PERF_LEVEL = -107, // Invalid perf level + DEVICE_BUSY = -108, // Device is busy; request not fulfilled + NV_PERSIST_FILE_NOT_FOUND = -109, // NV persist file is not found + PERSIST_DATA_NOT_FOUND = -110, // NV persist data is not found + EXPECTED_TV_DISPLAY = -111, // Expected a TV output display + EXPECTED_TV_DISPLAY_ON_DCONNECTOR = -112, // Expected a TV output on the D Connector - HDTV_EIAJ4120. + NO_ACTIVE_SLI_TOPOLOGY = -113, // SLI is not active on this device. + SLI_RENDERING_MODE_NOTALLOWED = -114, // Setup of SLI rendering mode is not possible right now. + EXPECTED_DIGITAL_FLAT_PANEL = -115, // Expected a digital flat panel. + ARGUMENT_EXCEED_MAX_SIZE = -116, // Argument exceeds the expected size. + DEVICE_SWITCHING_NOT_ALLOWED = -117, // Inhibit is ON due to one of the flags in NV_GPU_DISPLAY_CHANGE_INHIBIT or SLI active. + TESTING_CLOCKS_NOT_SUPPORTED = -118, // Testing of clocks is not supported. + UNKNOWN_UNDERSCAN_CONFIG = -119, // The specified underscan config is from an unknown source (e.g. INF) + TIMEOUT_RECONFIGURING_GPU_TOPO = -120, // Timeout while reconfiguring GPUs + DATA_NOT_FOUND = -121, // Requested data was not found + EXPECTED_ANALOG_DISPLAY = -122, // Expected an analog display + NO_VIDLINK = -123, // No SLI video bridge is present + REQUIRES_REBOOT = -124, // NVAPI requires a reboot for the settings to take effect + INVALID_HYBRID_MODE = -125, // The function is not supported with the current Hybrid mode. + MIXED_TARGET_TYPES = -126, // The target types are not all the same + SYSWOW64_NOT_SUPPORTED = -127, // The function is not supported from 32-bit on a 64-bit system. + IMPLICIT_SET_GPU_TOPOLOGY_CHANGE_NOT_ALLOWED = -128, // There is no implicit GPU topology active. Use SetHybridMode to change topology. + REQUEST_USER_TO_CLOSE_NON_MIGRATABLE_APPS = -129, // Prompt the user to close all non-migratable applications. + OUT_OF_MEMORY = -130, // Could not allocate sufficient memory to complete the call. + WAS_STILL_DRAWING = -131, // The previous operation that is transferring information to or from this surface is incomplete. + FILE_NOT_FOUND = -132, // The file was not found. + TOO_MANY_UNIQUE_STATE_OBJECTS = -133, // There are too many unique instances of a particular type of state object. + INVALID_CALL = -134, // The method call is invalid. For example, a method's parameter may not be a valid pointer. + D3D10_1_LIBRARY_NOT_FOUND = -135, // d3d10_1.dll cannot be loaded. + FUNCTION_NOT_FOUND = -136, // Couldn't find the function in the loaded DLL. + INVALID_USER_PRIVILEGE = -137, // Current User is not Admin. + EXPECTED_NON_PRIMARY_DISPLAY_HANDLE = -138, // The handle corresponds to GDIPrimary. + EXPECTED_COMPUTE_GPU_HANDLE = -139, // Setting Physx GPU requires that the GPU is compute-capable. + STEREO_NOT_INITIALIZED = -140, // The Stereo part of NVAPI failed to initialize completely. Check if the stereo driver is installed. + STEREO_REGISTRY_ACCESS_FAILED = -141, // Access to stereo-related registry keys or values has failed. + STEREO_REGISTRY_PROFILE_TYPE_NOT_SUPPORTED = -142, // The given registry profile type is not supported. + STEREO_REGISTRY_VALUE_NOT_SUPPORTED = -143, // The given registry value is not supported. + STEREO_NOT_ENABLED = -144, // Stereo is not enabled and the function needed it to execute completely. + STEREO_NOT_TURNED_ON = -145, // Stereo is not turned on and the function needed it to execute completely. + STEREO_INVALID_DEVICE_INTERFACE = -146, // Invalid device interface. + STEREO_PARAMETER_OUT_OF_RANGE = -147, // Separation percentage or JPEG image capture quality is out of [0-100] range. + STEREO_FRUSTUM_ADJUST_MODE_NOT_SUPPORTED = -148, // The given frustum adjust mode is not supported. + TOPO_NOT_POSSIBLE = -149, // The mosaic topology is not possible given the current state of the hardware. + MODE_CHANGE_FAILED = -150, // An attempt to do a display resolution mode change has failed. + D3D11_LIBRARY_NOT_FOUND = -151, // d3d11.dll/d3d11_beta.dll cannot be loaded. + INVALID_ADDRESS = -152, // Address is outside of valid range. + STRING_TOO_SMALL = -153, // The pre-allocated string is too small to hold the result. + MATCHING_DEVICE_NOT_FOUND = -154, // The input does not match any of the available devices. + DRIVER_RUNNING = -155, // Driver is running. + DRIVER_NOTRUNNING = -156, // Driver is not running. + ERROR_DRIVER_RELOAD_REQUIRED = -157, // A driver reload is required to apply these settings. + SET_NOT_ALLOWED = -158, // Intended setting is not allowed. + ADVANCED_DISPLAY_TOPOLOGY_REQUIRED = -159, // Information can't be returned due to "advanced display topology". + SETTING_NOT_FOUND = -160, // Setting is not found. + SETTING_SIZE_TOO_LARGE = -161, // Setting size is too large. + TOO_MANY_SETTINGS_IN_PROFILE = -162, // There are too many settings for a profile. + PROFILE_NOT_FOUND = -163, // Profile is not found. + PROFILE_NAME_IN_USE = -164, // Profile name is duplicated. + PROFILE_NAME_EMPTY = -165, // Profile name is empty. + EXECUTABLE_NOT_FOUND = -166, // Application not found in the Profile. + EXECUTABLE_ALREADY_IN_USE = -167, // Application already exists in the other profile. + DATATYPE_MISMATCH = -168, // Data Type mismatch + PROFILE_REMOVED = -169, // The profile passed as parameter has been removed and is no longer valid. + UNREGISTERED_RESOURCE = -170, // An unregistered resource was passed as a parameter. + ID_OUT_OF_RANGE = -171, // The DisplayId corresponds to a display which is not within the normal outputId range. + DISPLAYCONFIG_VALIDATION_FAILED = -172, // Display topology is not valid so the driver cannot do a mode set on this configuration. + DPMST_CHANGED = -173, // Display Port Multi-Stream topology has been changed. + INSUFFICIENT_BUFFER = -174, // Input buffer is insufficient to hold the contents. + ACCESS_DENIED = -175, // No access to the caller. + MOSAIC_NOT_ACTIVE = -176, // The requested action cannot be performed without Mosaic being enabled. + SHARE_RESOURCE_RELOCATED = -177, // The surface is relocated away from video memory. + REQUEST_USER_TO_DISABLE_DWM = -178, // The user should disable DWM before calling NvAPI. + D3D_DEVICE_LOST = -179, // D3D device status is D3DERR_DEVICELOST or D3DERR_DEVICENOTRESET - the user has to reset the device. + INVALID_CONFIGURATION = -180, // The requested action cannot be performed in the current state. + STEREO_HANDSHAKE_NOT_DONE = -181, // Call failed as stereo handshake not completed. + EXECUTABLE_PATH_IS_AMBIGUOUS = -182, // The path provided was too short to determine the correct NVDRS_APPLICATION + DEFAULT_STEREO_PROFILE_IS_NOT_DEFINED = -183, // Default stereo profile is not currently defined + DEFAULT_STEREO_PROFILE_DOES_NOT_EXIST = -184, // Default stereo profile does not exist + CLUSTER_ALREADY_EXISTS = -185, // A cluster is already defined with the given configuration. + DPMST_DISPLAY_ID_EXPECTED = -186, // The input display id is not that of a multi stream enabled connector or a display device in a multi stream topology + INVALID_DISPLAY_ID = -187, // The input display id is not valid or the monitor associated to it does not support the current operation + STREAM_IS_OUT_OF_SYNC = -188, // While playing secure audio stream, stream goes out of sync + INCOMPATIBLE_AUDIO_DRIVER = -189, // Older audio driver version than required + VALUE_ALREADY_SET = -190, // Value already set, setting again not allowed. + TIMEOUT = -191, // Requested operation timed out + GPU_WORKSTATION_FEATURE_INCOMPLETE = -192, // The requested workstation feature set has incomplete driver internal allocation resources + STEREO_INIT_ACTIVATION_NOT_DONE = -193, // Call failed because InitActivation was not called. + SYNC_NOT_ACTIVE = -194, // The requested action cannot be performed without Sync being enabled. + SYNC_MASTER_NOT_FOUND = -195, // The requested action cannot be performed without Sync Master being enabled. + INVALID_SYNC_TOPOLOGY = -196, // Invalid displays passed in the NV_GSYNC_DISPLAY pointer. + ECID_SIGN_ALGO_UNSUPPORTED = -197, // The specified signing algorithm is not supported. Either an incorrect value was entered or the current installed driver/hardware does not support the input value. + ECID_KEY_VERIFICATION_FAILED = -198, // The encrypted public key verification has failed. + FIRMWARE_OUT_OF_DATE = -199, // The device's firmware is out of date. + FIRMWARE_REVISION_NOT_SUPPORTED = -200, // The device's firmware is not supported. + } + + internal enum NvSystemType + { + UNKNOWN = 0, + LAPTOP = 1, + DESKTOP = 2 + } + + internal enum NvGpuType + { + UNKNOWN = 0, + IGPU = 1, // Integrated + DGPU = 2, // Discrete + } + + internal enum NvSettingID : uint + { + OGL_AA_LINE_GAMMA_ID = 0x2089BF6C, + OGL_DEEP_COLOR_SCANOUT_ID = 0x2097C2F6, + OGL_DEFAULT_SWAP_INTERVAL_ID = 0x206A6582, + OGL_DEFAULT_SWAP_INTERVAL_FRACTIONAL_ID = 0x206C4581, + OGL_DEFAULT_SWAP_INTERVAL_SIGN_ID = 0x20655CFA, + OGL_EVENT_LOG_SEVERITY_THRESHOLD_ID = 0x209DF23E, + OGL_EXTENSION_STRING_VERSION_ID = 0x20FF7493, + OGL_FORCE_BLIT_ID = 0x201F619F, + OGL_FORCE_STEREO_ID = 0x204D9A0C, + OGL_IMPLICIT_GPU_AFFINITY_ID = 0x20D0F3E6, + OGL_MAX_FRAMES_ALLOWED_ID = 0x208E55E3, + OGL_MULTIMON_ID = 0x200AEBFC, + OGL_OVERLAY_PIXEL_TYPE_ID = 0x209AE66F, + OGL_OVERLAY_SUPPORT_ID = 0x206C28C4, + OGL_QUALITY_ENHANCEMENTS_ID = 0x20797D6C, + OGL_SINGLE_BACKDEPTH_BUFFER_ID = 0x20A29055, + OGL_THREAD_CONTROL_ID = 0x20C1221E, + OGL_TRIPLE_BUFFER_ID = 0x20FDD1F9, + OGL_VIDEO_EDITING_MODE_ID = 0x20EE02B4, + AA_BEHAVIOR_FLAGS_ID = 0x10ECDB82, + AA_MODE_ALPHATOCOVERAGE_ID = 0x10FC2D9C, + AA_MODE_GAMMACORRECTION_ID = 0x107D639D, + AA_MODE_METHOD_ID = 0x10D773D2, + AA_MODE_REPLAY_ID = 0x10D48A85, + AA_MODE_SELECTOR_ID = 0x107EFC5B, + AA_MODE_SELECTOR_SLIAA_ID = 0x107AFC5B, + ANISO_MODE_LEVEL_ID = 0x101E61A9, + ANISO_MODE_SELECTOR_ID = 0x10D2BB16, + APPLICATION_PROFILE_NOTIFICATION_TIMEOUT_ID = 0x104554B6, + APPLICATION_STEAM_ID_ID = 0x107CDDBC, + CPL_HIDDEN_PROFILE_ID = 0x106D5CFF, + CUDA_EXCLUDED_GPUS_ID = 0x10354FF8, + D3DOGL_GPU_MAX_POWER_ID = 0x10D1EF29, + EXPORT_PERF_COUNTERS_ID = 0x108F0841, + FXAA_ALLOW_ID = 0x1034CB89, + FXAA_ENABLE_ID = 0x1074C972, + FXAA_INDICATOR_ENABLE_ID = 0x1068FB9C, + MCSFRSHOWSPLIT_ID = 0x10287051, + OPTIMUS_MAXAA_ID = 0x10F9DC83, + PHYSXINDICATOR_ID = 0x1094F16F, + PREFERRED_PSTATE_ID = 0x1057EB71, + PREVENT_UI_AF_OVERRIDE_ID = 0x103BCCB5, + PS_FRAMERATE_LIMITER_ID = 0x10834FEE, + PS_FRAMERATE_LIMITER_GPS_CTRL_ID = 0x10834F01, + SHIM_MAXRES_ID = 0x10F9DC82, + SHIM_MCCOMPAT_ID = 0x10F9DC80, + SHIM_RENDERING_MODE_ID = 0x10F9DC81, + SHIM_RENDERING_OPTIONS_ID = 0x10F9DC84, + SLI_GPU_COUNT_ID = 0x1033DCD1, + SLI_PREDEFINED_GPU_COUNT_ID = 0x1033DCD2, + SLI_PREDEFINED_GPU_COUNT_DX10_ID = 0x1033DCD3, + SLI_PREDEFINED_MODE_ID = 0x1033CEC1, + SLI_PREDEFINED_MODE_DX10_ID = 0x1033CEC2, + SLI_RENDERING_MODE_ID = 0x1033CED1, + VRRFEATUREINDICATOR_ID = 0x1094F157, + VRROVERLAYINDICATOR_ID = 0x1095F16F, + VRRREQUESTSTATE_ID = 0x1094F1F7, + VSYNCSMOOTHAFR_ID = 0x101AE763, + VSYNCVRRCONTROL_ID = 0x10A879CE, + VSYNC_BEHAVIOR_FLAGS_ID = 0x10FDEC23, + WKS_API_STEREO_EYES_EXCHANGE_ID = 0x11AE435C, + WKS_API_STEREO_MODE_ID = 0x11E91A61, + WKS_MEMORY_ALLOCATION_POLICY_ID = 0x11112233, + WKS_STEREO_DONGLE_SUPPORT_ID = 0x112493BD, + WKS_STEREO_SUPPORT_ID = 0x11AA9E99, + WKS_STEREO_SWAP_MODE_ID = 0x11333333, + AO_MODE_ID = 0x00667329, + AO_MODE_ACTIVE_ID = 0x00664339, + AUTO_LODBIASADJUST_ID = 0x00638E8F, + ICAFE_LOGO_CONFIG_ID = 0x00DB1337, + LODBIASADJUST_ID = 0x00738E8F, + PRERENDERLIMIT_ID = 0x007BA09E, + PS_DYNAMIC_TILING_ID = 0x00E5C6C0, + PS_SHADERDISKCACHE_ID = 0x00198FFF, + PS_TEXFILTER_ANISO_OPTS2_ID = 0x00E73211, + PS_TEXFILTER_BILINEAR_IN_ANISO_ID = 0x0084CD70, + PS_TEXFILTER_DISABLE_TRILIN_SLOPE_ID = 0x002ECAF2, + PS_TEXFILTER_NO_NEG_LODBIAS_ID = 0x0019BB68, + QUALITY_ENHANCEMENTS_ID = 0x00CE2691, + REFRESH_RATE_OVERRIDE_ID = 0x0064B541, + SET_POWER_THROTTLE_FOR_PCIe_COMPLIANCE_ID = 0x00AE785C, + SET_VAB_DATA_ID = 0x00AB8687, + VSYNCMODE_ID = 0x00A879CF, + VSYNCTEARCONTROL_ID = 0x005A375C, + TOTAL_DWORD_SETTING_NUM = 80, + TOTAL_WSTRING_SETTING_NUM = 4, + TOTAL_SETTING_NUM = 84, + INVALID_SETTING_ID = 0xFFFFFFFF + } + + internal enum NvShimSetting : uint + { + SHIM_RENDERING_MODE_INTEGRATED = 0x00000000, + SHIM_RENDERING_MODE_ENABLE = 0x00000001, + SHIM_RENDERING_MODE_USER_EDITABLE = 0x00000002, + SHIM_RENDERING_MODE_MASK = 0x00000003, + SHIM_RENDERING_MODE_VIDEO_MASK = 0x00000004, + SHIM_RENDERING_MODE_VARYING_BIT = 0x00000008, + SHIM_RENDERING_MODE_AUTO_SELECT = 0x00000010, + SHIM_RENDERING_MODE_OVERRIDE_BIT = 0x80000000, + SHIM_RENDERING_MODE_NUM_VALUES = 8, + SHIM_RENDERING_MODE_DEFAULT = SHIM_RENDERING_MODE_AUTO_SELECT + } + + internal enum NvThreadControlSetting : uint + { + OGL_THREAD_CONTROL_ENABLE = 0x00000001, + OGL_THREAD_CONTROL_DISABLE = 0x00000002, + OGL_THREAD_CONTROL_NUM_VALUES = 2, + OGL_THREAD_CONTROL_DEFAULT = 0 + } +} diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index a33e845f5b..b37b5cf6ca 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -30,6 +30,8 @@ namespace osu.Desktop [STAThread] public static void Main(string[] args) { + NVAPI.ThreadedOptimisations = true; + // run Squirrel first, as the app may exit after these run if (OperatingSystem.IsWindows()) { From d38f8d9c783049f0c6a577383f3097a923ba635c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 3 Jan 2024 15:37:24 +0900 Subject: [PATCH 0809/1118] Change threaded optimisations setting to "Auto" on startup --- osu.Desktop/NVAPI.cs | 12 ++++++------ osu.Desktop/Program.cs | 5 ++++- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/osu.Desktop/NVAPI.cs b/osu.Desktop/NVAPI.cs index 9648ea1072..e1c5971e28 100644 --- a/osu.Desktop/NVAPI.cs +++ b/osu.Desktop/NVAPI.cs @@ -153,30 +153,30 @@ namespace osu.Desktop } } - public static bool ThreadedOptimisations + public static NvThreadControlSetting ThreadedOptimisations { get { if (!Available) - return false; + return NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT; IntPtr profileHandle; if (!getProfile(out profileHandle, out _, out bool _)) - return false; + return NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT; // Get the threaded optimisations setting NvSetting setting; if (!getSetting(NvSettingID.OGL_THREAD_CONTROL_ID, profileHandle, out setting)) - return false; + return NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT; - return setting.U32CurrentValue != (uint)NvThreadControlSetting.OGL_THREAD_CONTROL_DISABLE; + return (NvThreadControlSetting)setting.U32CurrentValue; } set { if (!Available) return; - bool success = setSetting(NvSettingID.OGL_THREAD_CONTROL_ID, (uint)(value ? NvThreadControlSetting.OGL_THREAD_CONTROL_ENABLE : NvThreadControlSetting.OGL_THREAD_CONTROL_DISABLE)); + bool success = setSetting(NvSettingID.OGL_THREAD_CONTROL_ID, (uint)value); Logger.Log(success ? $"Threaded optimizations set to \"{value}\"!" : "Threaded optimizations set failed!"); } diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index b37b5cf6ca..a652e31f62 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -30,7 +30,10 @@ namespace osu.Desktop [STAThread] public static void Main(string[] args) { - NVAPI.ThreadedOptimisations = true; + // NVIDIA profiles are based on the executable name of a process. + // Lazer and stable share the same executable name. + // Stable sets this setting to "Off", which may not be what we want, so let's force it back to the default "Auto" on startup. + NVAPI.ThreadedOptimisations = NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT; // run Squirrel first, as the app may exit after these run if (OperatingSystem.IsWindows()) From e686a6a1dddbe20c3c22dd1ec8cb572d7a9d8aef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jan 2024 09:17:01 +0100 Subject: [PATCH 0810/1118] Fix player submission test intermittent failures due to audio playback discrepancy logic kicking in See https://github.com/ppy/osu/actions/runs/7384457927/job/20087439457#step:5:133. --- .../Visual/Gameplay/TestScenePlayerScoreSubmission.cs | 6 ++++++ osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 9 +++++++++ 2 files changed, 15 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index f75a2656ef..96cfcd61c3 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -21,6 +21,7 @@ using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko; using osu.Game.Scoring; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Tests.Beatmaps; @@ -359,6 +360,11 @@ namespace osu.Game.Tests.Visual.Gameplay AllowImportCompletion = new SemaphoreSlim(1); } + protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) => new MasterGameplayClockContainer(beatmap, gameplayStart) + { + ShouldValidatePlaybackRate = false, + }; + protected override async Task ImportScore(Score score) { ScoreImportStarted = true; diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index a475f4823f..0d60ec4713 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -40,6 +40,12 @@ namespace osu.Game.Screens.Play Precision = 0.1, }; + /// + /// Whether the audio playback rate should be validated. + /// Mostly disabled for tests. + /// + internal bool ShouldValidatePlaybackRate { get; init; } + /// /// Whether the audio playback is within acceptable ranges. /// Will become false if audio playback is not going as expected. @@ -223,6 +229,9 @@ namespace osu.Game.Screens.Play private void checkPlaybackValidity() { + if (!ShouldValidatePlaybackRate) + return; + if (GameplayClock.IsRunning) { elapsedGameplayClockTime += GameplayClock.ElapsedFrameTime; From 3df7430d2e278cbd3dacc173270053e6a415af07 Mon Sep 17 00:00:00 2001 From: StanR Date: Wed, 3 Jan 2024 14:32:32 +0600 Subject: [PATCH 0811/1118] Bind `UserRankPanel` values to `Statistics` bindable in `APIAccess` --- .../Visual/Online/TestSceneUserPanel.cs | 18 ++++++++++++++++++ osu.Game/Users/UserRankPanel.cs | 17 +++++++++++++++-- 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs index 81e17cd1b8..4df34e6244 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneUserPanel.cs @@ -9,6 +9,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; @@ -157,6 +158,23 @@ namespace osu.Game.Tests.Visual.Online AddAssert("visit message is not visible", () => !boundPanel2.LastVisitMessage.IsPresent); } + [Test] + public void TestUserStatisticsChange() + { + AddStep("update statistics", () => + { + API.UpdateStatistics(new UserStatistics + { + GlobalRank = RNG.Next(100000), + CountryRank = RNG.Next(100000) + }); + }); + AddStep("set statistics to empty", () => + { + API.UpdateStatistics(new UserStatistics()); + }); + } + private UserActivity soloGameStatusForRuleset(int rulesetId) => new UserActivity.InSoloGame(new BeatmapInfo(), rulesetStore.GetRuleset(rulesetId)!); private ScoreInfo createScore(string name) => new ScoreInfo(new TestBeatmap(Ruleset.Value).BeatmapInfo) diff --git a/osu.Game/Users/UserRankPanel.cs b/osu.Game/Users/UserRankPanel.cs index 272d2eecb4..74ff0a06dd 100644 --- a/osu.Game/Users/UserRankPanel.cs +++ b/osu.Game/Users/UserRankPanel.cs @@ -7,6 +7,7 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; using osu.Framework.Localisation; +using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Profile.Header.Components; using osu.Game.Resources.Localisation.Web; @@ -23,6 +24,12 @@ namespace osu.Game.Users private const int padding = 10; private const int main_content_height = 80; + [Resolved] + private IAPIProvider api { get; set; } = null!; + + private ProfileValueDisplay globalRankDisplay = null!; + private ProfileValueDisplay countryRankDisplay = null!; + public UserRankPanel(APIUser user) : base(user) { @@ -34,6 +41,12 @@ namespace osu.Game.Users private void load() { BorderColour = ColourProvider?.Light1 ?? Colours.GreyVioletLighter; + + api.Statistics.ValueChanged += e => + { + globalRankDisplay.Content = e.NewValue?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + countryRankDisplay.Content = e.NewValue?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; + }; } protected override Drawable CreateLayout() @@ -166,12 +179,12 @@ namespace osu.Game.Users { new Drawable[] { - new ProfileValueDisplay(true) + globalRankDisplay = new ProfileValueDisplay(true) { Title = UsersStrings.ShowRankGlobalSimple, Content = User.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-" }, - new ProfileValueDisplay(true) + countryRankDisplay = new ProfileValueDisplay(true) { Title = UsersStrings.ShowRankCountrySimple, Content = User.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-" From d34f30f6ad1a2c5071eddb2ed1763465be3cc70b Mon Sep 17 00:00:00 2001 From: StanR Date: Wed, 3 Jan 2024 14:37:57 +0600 Subject: [PATCH 0812/1118] Add `Statistics` bindable to `IAPIProvider` and update it from `SoloStatisticsWatcher` --- osu.Game/Online/API/APIAccess.cs | 14 +++++++++++++- osu.Game/Online/API/DummyAPIAccess.cs | 14 ++++++++++++++ osu.Game/Online/API/IAPIProvider.cs | 10 ++++++++++ osu.Game/Online/Solo/SoloStatisticsWatcher.cs | 2 ++ 4 files changed, 39 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index be5bdeca77..3910f1efea 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -53,6 +53,7 @@ namespace osu.Game.Online.API public IBindable LocalUser => localUser; public IBindableList Friends => friends; public IBindable Activity => activity; + public IBindable Statistics => statistics; public Language Language => game.CurrentLanguage.Value; @@ -65,6 +66,8 @@ namespace osu.Game.Online.API private Bindable configStatus { get; } = new Bindable(); private Bindable localUserStatus { get; } = new Bindable(); + private Bindable statistics { get; } = new Bindable(); + protected bool HasLogin => authentication.Token.Value != null || (!string.IsNullOrEmpty(ProvidedUsername) && !string.IsNullOrEmpty(password)); private readonly CancellationTokenSource cancellationToken = new CancellationTokenSource(); @@ -517,9 +520,18 @@ namespace osu.Game.Online.API flushQueue(); } + public void UpdateStatistics(UserStatistics newStatistics) + { + statistics.Value = newStatistics; + } + private static APIUser createGuestUser() => new GuestUser(); - private void setLocalUser(APIUser user) => Scheduler.Add(() => localUser.Value = user, false); + private void setLocalUser(APIUser user) => Scheduler.Add(() => + { + localUser.Value = user; + statistics.Value = user.Statistics; + }, false); protected override void Dispose(bool isDisposing) { diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index d585124db6..aa8658cc83 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -28,6 +28,8 @@ namespace osu.Game.Online.API public Bindable Activity { get; } = new Bindable(); + public Bindable Statistics { get; } = new Bindable(); + public Language Language => Language.en; public string AccessToken => "token"; @@ -115,6 +117,12 @@ namespace osu.Game.Online.API Id = DUMMY_USER_ID, }; + Statistics.Value = new UserStatistics + { + GlobalRank = 1, + CountryRank = 1 + }; + state.Value = APIState.Online; } @@ -126,6 +134,11 @@ namespace osu.Game.Online.API LocalUser.Value = new GuestUser(); } + public void UpdateStatistics(UserStatistics newStatistics) + { + Statistics.Value = newStatistics; + } + public IHubClientConnector? GetHubConnector(string clientName, string endpoint, bool preferMessagePack) => null; public NotificationsClientConnector GetNotificationsConnector() => new PollingNotificationsClientConnector(this); @@ -141,6 +154,7 @@ namespace osu.Game.Online.API IBindable IAPIProvider.LocalUser => LocalUser; IBindableList IAPIProvider.Friends => Friends; IBindable IAPIProvider.Activity => Activity; + IBindable IAPIProvider.Statistics => Statistics; /// /// During the next simulated login, the process will fail immediately. diff --git a/osu.Game/Online/API/IAPIProvider.cs b/osu.Game/Online/API/IAPIProvider.cs index a1d7006c8c..b58d4a363a 100644 --- a/osu.Game/Online/API/IAPIProvider.cs +++ b/osu.Game/Online/API/IAPIProvider.cs @@ -28,6 +28,11 @@ namespace osu.Game.Online.API /// IBindable Activity { get; } + /// + /// The current user's online statistics. + /// + IBindable Statistics { get; } + /// /// The language supplied by this provider to API requests. /// @@ -111,6 +116,11 @@ namespace osu.Game.Online.API /// void Logout(); + /// + /// Sets Statistics bindable. + /// + void UpdateStatistics(UserStatistics newStatistics); + /// /// Constructs a new . May be null if not supported. /// diff --git a/osu.Game/Online/Solo/SoloStatisticsWatcher.cs b/osu.Game/Online/Solo/SoloStatisticsWatcher.cs index 46449fea73..55b27fb364 100644 --- a/osu.Game/Online/Solo/SoloStatisticsWatcher.cs +++ b/osu.Game/Online/Solo/SoloStatisticsWatcher.cs @@ -127,6 +127,8 @@ namespace osu.Game.Online.Solo { string rulesetName = callback.Score.Ruleset.ShortName; + api.UpdateStatistics(updatedStatistics); + if (latestStatistics == null) return; From 474c416bd9985aab49f895054189ea5b61b40949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jan 2024 10:05:11 +0100 Subject: [PATCH 0813/1118] Make `dotnet format` shut up about naming --- osu.Desktop/NVAPI.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Desktop/NVAPI.cs b/osu.Desktop/NVAPI.cs index e1c5971e28..bb3a59cc7f 100644 --- a/osu.Desktop/NVAPI.cs +++ b/osu.Desktop/NVAPI.cs @@ -3,6 +3,8 @@ #nullable disable +#pragma warning disable IDE1006 // Naming rule violation + using System; using System.Diagnostics.CodeAnalysis; using System.Runtime.InteropServices; From 04147eb68988e4ce7f94f5d36def2c8669821508 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jan 2024 11:46:26 +0100 Subject: [PATCH 0814/1118] Fix lack of correct default value spec --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 0d60ec4713..8b8bf87436 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Play /// Whether the audio playback rate should be validated. /// Mostly disabled for tests. /// - internal bool ShouldValidatePlaybackRate { get; init; } + internal bool ShouldValidatePlaybackRate { get; init; } = true; /// /// Whether the audio playback is within acceptable ranges. From 4312e9e9a9c70bd7a139833108edca8dd4ff63e3 Mon Sep 17 00:00:00 2001 From: CaffeeLake Date: Wed, 3 Jan 2024 20:17:05 +0900 Subject: [PATCH 0815/1118] Add Test: floating point errors --- osu.Game.Tests/Mods/ModUtilsTest.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index 4f10a0ea54..f1db146f3a 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -315,6 +315,10 @@ namespace osu.Game.Tests.Mods { Assert.AreEqual(ModUtils.FormatScoreMultiplier(0.9999).ToString(), "0.99x"); Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.0001).ToString(), "1.01x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(0.899999999999999).ToString(), "0.90x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.099999999999999).ToString(), "1.10x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(0.900000000000001).ToString(), "0.90x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.100000000000001).ToString(), "1.10x"); } public abstract class CustomMod1 : Mod, IModCompatibilitySpecification From 7262fef67f5c0abc51b78f8aa643c3b68d9bbbe3 Mon Sep 17 00:00:00 2001 From: StanR Date: Wed, 3 Jan 2024 17:39:48 +0600 Subject: [PATCH 0816/1118] Add comments --- osu.Game/Users/UserGridPanel.cs | 2 ++ osu.Game/Users/UserPanel.cs | 17 ++++++++++------- osu.Game/Users/UserRankPanel.cs | 6 ++++-- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/osu.Game/Users/UserGridPanel.cs b/osu.Game/Users/UserGridPanel.cs index aac2315b2f..fe3435c248 100644 --- a/osu.Game/Users/UserGridPanel.cs +++ b/osu.Game/Users/UserGridPanel.cs @@ -91,6 +91,7 @@ namespace osu.Game.Users Children = new Drawable[] { CreateFlag(), + // supporter icon is being added later } } }, @@ -108,6 +109,7 @@ namespace osu.Game.Users }, new[] { + // padding Empty(), Empty() }, diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index d38f5d8ccc..ef5c95c422 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -100,13 +100,9 @@ namespace osu.Game.Users protected abstract Drawable CreateLayout(); - protected OsuSpriteText CreateUsername() => new OsuSpriteText - { - Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), - Shadow = false, - Text = User.Username, - }; - + /// + /// Panel background container. Can be null if a panel doesn't want a background under it's layout + /// protected virtual Drawable? CreateBackground() => Background = new UserCoverBackground { RelativeSizeAxes = Axes.Both, @@ -115,6 +111,13 @@ namespace osu.Game.Users User = User }; + protected OsuSpriteText CreateUsername() => new OsuSpriteText + { + Font = OsuFont.GetFont(size: 16, weight: FontWeight.Bold), + Shadow = false, + Text = User.Username, + }; + protected UpdateableAvatar CreateAvatar() => new UpdateableAvatar(User, false); protected UpdateableFlag CreateFlag() => new UpdateableFlag(User.CountryCode) diff --git a/osu.Game/Users/UserRankPanel.cs b/osu.Game/Users/UserRankPanel.cs index 74ff0a06dd..a2e234cf82 100644 --- a/osu.Game/Users/UserRankPanel.cs +++ b/osu.Game/Users/UserRankPanel.cs @@ -96,6 +96,7 @@ namespace osu.Game.Users { new[] { + // padding Empty(), Empty(), Empty(), @@ -103,7 +104,7 @@ namespace osu.Game.Users }, new[] { - Empty(), + Empty(), // padding CreateAvatar().With(avatar => { avatar.Size = new Vector2(60); @@ -138,6 +139,7 @@ namespace osu.Game.Users Children = new Drawable[] { CreateFlag(), + // supporter icon is being added later } } }, @@ -152,7 +154,7 @@ namespace osu.Game.Users } } }, - Empty() + Empty() // padding } } } From e240443c467436a470c12fd77b3035b7720a8cdd Mon Sep 17 00:00:00 2001 From: StanR Date: Wed, 3 Jan 2024 18:15:32 +0600 Subject: [PATCH 0817/1118] Update `LocalUser` statistics, add test --- .../Online/TestSceneSoloStatisticsWatcher.cs | 20 +++++++++++++++++++ osu.Game/Online/API/APIAccess.cs | 3 +++ osu.Game/Online/API/DummyAPIAccess.cs | 3 +++ 3 files changed, 26 insertions(+) diff --git a/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs b/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs index e62e53bd02..be819afa3e 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneSoloStatisticsWatcher.cs @@ -268,6 +268,26 @@ namespace osu.Game.Tests.Visual.Online AddAssert("update not received", () => update == null); } + [Test] + public void TestGlobalStatisticsUpdatedAfterRegistrationAddedAndScoreProcessed() + { + int userId = getUserId(); + long scoreId = getScoreId(); + setUpUser(userId); + + var ruleset = new OsuRuleset().RulesetInfo; + + SoloStatisticsUpdate? update = null; + registerForUpdates(scoreId, ruleset, receivedUpdate => update = receivedUpdate); + + feignScoreProcessing(userId, ruleset, 5_000_000); + + AddStep("signal score processed", () => ((ISpectatorClient)spectatorClient).UserScoreProcessed(userId, scoreId)); + AddUntilStep("update received", () => update != null); + AddAssert("local user values are correct", () => dummyAPI.LocalUser.Value.Statistics.TotalScore, () => Is.EqualTo(5_000_000)); + AddAssert("statistics values are correct", () => dummyAPI.Statistics.Value!.TotalScore, () => Is.EqualTo(5_000_000)); + } + private int nextUserId = 2000; private long nextScoreId = 50000; diff --git a/osu.Game/Online/API/APIAccess.cs b/osu.Game/Online/API/APIAccess.cs index 3910f1efea..17bf8bcc37 100644 --- a/osu.Game/Online/API/APIAccess.cs +++ b/osu.Game/Online/API/APIAccess.cs @@ -523,6 +523,9 @@ namespace osu.Game.Online.API public void UpdateStatistics(UserStatistics newStatistics) { statistics.Value = newStatistics; + + if (IsLoggedIn) + localUser.Value.Statistics = newStatistics; } private static APIUser createGuestUser() => new GuestUser(); diff --git a/osu.Game/Online/API/DummyAPIAccess.cs b/osu.Game/Online/API/DummyAPIAccess.cs index aa8658cc83..4b4f8061e0 100644 --- a/osu.Game/Online/API/DummyAPIAccess.cs +++ b/osu.Game/Online/API/DummyAPIAccess.cs @@ -137,6 +137,9 @@ namespace osu.Game.Online.API public void UpdateStatistics(UserStatistics newStatistics) { Statistics.Value = newStatistics; + + if (IsLoggedIn) + LocalUser.Value.Statistics = newStatistics; } public IHubClientConnector? GetHubConnector(string clientName, string endpoint, bool preferMessagePack) => null; From 2c64db06287b40811e742f785664eafac09093c5 Mon Sep 17 00:00:00 2001 From: wooster0 Date: Thu, 4 Jan 2024 12:15:48 +0900 Subject: [PATCH 0818/1118] Use already existing message placeholder + localized string --- osu.Game/Screens/Ranking/ResultsScreen.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index e18ab63706..8823e4d320 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -19,10 +19,11 @@ using osu.Framework.Input.Events; using osu.Framework.Screens; using osu.Game.Graphics; using osu.Game.Graphics.Containers; -using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Input.Bindings; +using osu.Game.Localisation; using osu.Game.Online.API; +using osu.Game.Online.Placeholders; using osu.Game.Scoring; using osu.Game.Screens.Play; using osu.Game.Screens.Ranking.Statistics; @@ -208,13 +209,7 @@ namespace osu.Game.Screens.Ranking if (ScorePanelList.IsEmpty) { // This can happen if a beatmap part of a playlist hasn't been played yet. - VerticalScrollContent.Add(new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Font = OsuFont.GetFont(size: 32, weight: FontWeight.Regular), - Text = "no scores yet!", - }); + VerticalScrollContent.Add(new MessagePlaceholder(LeaderboardStrings.NoRecordsYet)); } else { From 92c45662874d2c4e3992124a8aeabcca90fa1862 Mon Sep 17 00:00:00 2001 From: wooster0 Date: Thu, 4 Jan 2024 12:20:05 +0900 Subject: [PATCH 0819/1118] Fix typo --- osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index d4b6bc2b91..6c87553971 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -275,7 +275,7 @@ Phasellus eu nunc nec ligula semper fringilla. Aliquam magna neque, placerat sed AddStep("set content", () => { markdownContainer.Text = @" -This is a paragraph containing `inline code` synatax. +This is a paragraph containing `inline code` syntax. Oh wow I do love the `WikiMarkdownContainer`, it is very cool! This is a line before the fenced code block: From 659118c04353bca73182a82e6d48c533779f6777 Mon Sep 17 00:00:00 2001 From: wooster0 Date: Thu, 4 Jan 2024 12:20:51 +0900 Subject: [PATCH 0820/1118] Fix wiki link path inconsistencies If I access https://osu.ppy.sh/wiki/en/MAIN_PAGE or use any other capitalization my browser always redirects me to https://osu.ppy.sh/wiki/en/Main_page so I think Main_page is the correct capitalization. This might slightly reduce loading time? No idea though. Probably negligible if so. --- osu.Game/Overlays/Wiki/WikiHeader.cs | 2 +- osu.Game/Overlays/WikiOverlay.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 24eddeb0c2..55be05ed7a 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -17,7 +17,7 @@ namespace osu.Game.Overlays.Wiki { public partial class WikiHeader : BreadcrumbControlOverlayHeader { - private const string index_path = "Main_Page"; + private const string index_path = "Main_page"; public static LocalisableString IndexPageString => LayoutStrings.HeaderHelpIndex; diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index c816eca776..440e451201 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -19,7 +19,7 @@ namespace osu.Game.Overlays { public partial class WikiOverlay : OnlineOverlay { - private const string index_path = @"main_page"; + private const string index_path = "Main_page"; public string CurrentPath => path.Value; @@ -161,7 +161,7 @@ namespace osu.Game.Overlays path.Value = "error"; LoadDisplay(articlePage = new WikiArticlePage($@"{api.WebsiteRootUrl}/wiki/", - $"Something went wrong when trying to fetch page \"{originalPath}\".\n\n[Return to the main page](Main_Page).")); + $"Something went wrong when trying to fetch page \"{originalPath}\".\n\n[Return to the main page](Main_page).")); } private void showParentPage() From ddc8a647640ee1c6f4c2dcf005cb7dad0b9e4700 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 14:55:52 +0900 Subject: [PATCH 0821/1118] Reduce spinner glow It was horrible --- osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs index 88769442a1..76afeeb2c4 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonSpinnerProgressArc.cs @@ -59,7 +59,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon RelativeSizeAxes = Axes.Both, InnerRadius = arc_radius, RoundedCaps = true, - GlowColour = new Color4(171, 255, 255, 255) + GlowColour = new Color4(171, 255, 255, 180) } }; } From cf5f0a2bdc79e50b178f9b41d08a7ab52b1b5a9f Mon Sep 17 00:00:00 2001 From: wooster0 Date: Thu, 4 Jan 2024 15:01:27 +0900 Subject: [PATCH 0822/1118] Make chat commands case-insensitive Would be nice if I accidentally have caps lock enabled and write "/HELP" it still works. --- .../Chat/TestSceneChannelManager.cs | 19 ++++++++++++++++++- osu.Game/Online/Chat/ChannelManager.cs | 2 +- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Chat/TestSceneChannelManager.cs b/osu.Game.Tests/Chat/TestSceneChannelManager.cs index 3a4c55c65c..eae12edebd 100644 --- a/osu.Game.Tests/Chat/TestSceneChannelManager.cs +++ b/osu.Game.Tests/Chat/TestSceneChannelManager.cs @@ -112,7 +112,7 @@ namespace osu.Game.Tests.Chat }); AddStep("post message", () => channelManager.PostMessage("Something interesting")); - AddUntilStep("message postesd", () => !channel.Messages.Any(m => m is LocalMessage)); + AddUntilStep("message posted", () => !channel.Messages.Any(m => m is LocalMessage)); AddStep("post /help command", () => channelManager.PostCommand("help", channel)); AddStep("post /me command with no action", () => channelManager.PostCommand("me", channel)); @@ -146,6 +146,23 @@ namespace osu.Game.Tests.Chat AddAssert("channel has no more messages", () => channel.Messages, () => Is.Empty); } + [Test] + public void TestCommandNameCaseInsensitivity() + { + Channel channel = null; + + AddStep("join channel and select it", () => + { + channelManager.JoinChannel(channel = createChannel(1, ChannelType.Public)); + channelManager.CurrentChannel.Value = channel; + }); + + AddStep("post /me command", () => channelManager.PostCommand("ME DANCES")); + AddUntilStep("/me command received", () => channel.Messages.Last().Content.Contains("DANCES")); + AddStep("post /help command", () => channelManager.PostCommand("HeLp")); + AddUntilStep("/help command received", () => channel.Messages.Last().Content.Contains("Supported commands")); + } + private void handlePostMessageRequest(PostMessageRequest request) { var message = new Message(++currentMessageId) diff --git a/osu.Game/Online/Chat/ChannelManager.cs b/osu.Game/Online/Chat/ChannelManager.cs index e95bc128c8..23989caae2 100644 --- a/osu.Game/Online/Chat/ChannelManager.cs +++ b/osu.Game/Online/Chat/ChannelManager.cs @@ -247,7 +247,7 @@ namespace osu.Game.Online.Chat string command = parameters[0]; string content = parameters.Length == 2 ? parameters[1] : string.Empty; - switch (command) + switch (command.ToLowerInvariant()) { case "np": AddInternal(new NowPlayingCommand(target)); From cd9bf0c753c0d4e6ca86c4ccc17fe1a668654e8e Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 3 Jan 2024 21:30:46 -0800 Subject: [PATCH 0823/1118] Flash blocking ongoing operations dialog when trying to force quit --- osu.Game/Screens/Menu/MainMenu.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 516b090a16..14c950d726 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -5,6 +5,7 @@ using System; using System.Diagnostics; +using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Audio; @@ -25,6 +26,7 @@ using osu.Game.Input.Bindings; using osu.Game.IO; using osu.Game.Online.API; using osu.Game.Overlays; +using osu.Game.Overlays.Dialog; using osu.Game.Overlays.SkinEditor; using osu.Game.Rulesets; using osu.Game.Screens.Backgrounds; @@ -390,7 +392,12 @@ namespace osu.Game.Screens.Menu if (requiresConfirmation) { if (dialogOverlay.CurrentDialog is ConfirmExitDialog exitDialog) - exitDialog.PerformOkAction(); + { + if (exitDialog.Buttons.OfType().FirstOrDefault() != null) + exitDialog.PerformOkAction(); + else + exitDialog.Flash(); + } else { dialogOverlay.Push(new ConfirmExitDialog(() => From ea714c86d427cb6fa498b1d76a02e6ee389dae6f Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Wed, 3 Jan 2024 22:22:25 -0800 Subject: [PATCH 0824/1118] Fix potential null reference with flash sample when exiting rapidly Fixes `TestForceExitWithOperationInProgress()`. --- osu.Game/Overlays/Dialog/PopupDialog.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 4048b35e78..4ac37a63e2 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -32,7 +32,7 @@ namespace osu.Game.Overlays.Dialog private readonly Vector2 ringMinifiedSize = new Vector2(20f); private readonly Box flashLayer; - private Sample flashSample = null!; + private Sample? flashSample; private readonly Container content; private readonly Container ring; @@ -267,7 +267,7 @@ namespace osu.Game.Overlays.Dialog flashLayer.FadeInFromZero(80, Easing.OutQuint) .Then() .FadeOutFromOne(1500, Easing.OutQuint); - flashSample.Play(); + flashSample?.Play(); } protected override bool OnKeyDown(KeyDownEvent e) From 1beb3f5462329262b902472227a3b6c097714092 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 15:45:13 +0900 Subject: [PATCH 0825/1118] Add failing test coverage of fast streams not working in editor --- .../TestSceneEditorAutoplayFastStreams.cs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneEditorAutoplayFastStreams.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneEditorAutoplayFastStreams.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneEditorAutoplayFastStreams.cs new file mode 100644 index 0000000000..cf5cd809ef --- /dev/null +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneEditorAutoplayFastStreams.cs @@ -0,0 +1,51 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Beatmaps; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Objects.Drawables; +using osu.Game.Tests.Beatmaps; +using osu.Game.Tests.Visual; + +namespace osu.Game.Rulesets.Osu.Tests.Editor +{ + /// + /// This test covers autoplay working correctly in the editor on fast streams. + /// Might seem like a weird test, but frame stability being toggled can cause autoplay to operation incorrectly. + /// This is clearly a bug with the autoplay algorithm, but is worked around at an editor level for now. + /// + public partial class TestSceneEditorAutoplayFastStreams : EditorTestScene + { + protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) + { + var testBeatmap = new TestBeatmap(ruleset, false); + testBeatmap.HitObjects.AddRange(new[] + { + new HitCircle { StartTime = 500 }, + new HitCircle { StartTime = 530 }, + new HitCircle { StartTime = 560 }, + new HitCircle { StartTime = 590 }, + new HitCircle { StartTime = 620 }, + }); + + return testBeatmap; + } + + protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); + + [Test] + public void TestAllHit() + { + AddStep("start playback", () => EditorClock.Start()); + AddUntilStep("wait for all hit", () => + { + DrawableHitCircle[] hitCircles = Editor.ChildrenOfType().OrderBy(s => s.HitObject.StartTime).ToArray(); + + return hitCircles.Length == 5 && hitCircles.All(h => h.IsHit); + }); + } + } +} From 65c29b4f09d49d772cfe2c9934c4d1ee65e9b384 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 13:18:41 +0900 Subject: [PATCH 0826/1118] Make editor remain frame stable during normal playback --- .../Edit/DrawableEditorRulesetWrapper.cs | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs index 174b278d89..ebf06bcc4e 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; @@ -26,6 +27,9 @@ namespace osu.Game.Rulesets.Edit [Resolved] private EditorBeatmap beatmap { get; set; } = null!; + [Resolved] + private EditorClock editorClock { get; set; } = null!; + public DrawableEditorRulesetWrapper(DrawableRuleset drawableRuleset) { this.drawableRuleset = drawableRuleset; @@ -38,7 +42,6 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load() { - drawableRuleset.FrameStablePlayback = false; Playfield.DisplayJudgements.Value = false; } @@ -65,6 +68,22 @@ namespace osu.Game.Rulesets.Edit Scheduler.AddOnce(regenerateAutoplay); } + protected override void Update() + { + base.Update(); + + // Whenever possible, we want to stay in frame stability playback. + // Without doing so, we run into bugs with some gameplay elements not behaving as expected. + // + // Note that this is not using EditorClock.IsSeeking as that would exit frame stability + // on all seeks. The intention here is to retain frame stability for small seeks. + // + // I still think no gameplay elements should require frame stability in the first place, but maybe that ship has sailed already.. + bool shouldBypassFrameStability = Math.Abs(drawableRuleset.FrameStableClock.CurrentTime - editorClock.CurrentTime) > 1000; + + drawableRuleset.FrameStablePlayback = !shouldBypassFrameStability; + } + private void regenerateAutoplay() { var autoplayMod = drawableRuleset.Mods.OfType().Single(); From 3419c59b062ba0ac9670d9279029d6fbb062aac0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 15:33:18 +0900 Subject: [PATCH 0827/1118] Fix failing tests due to frame stable seeks taking longer --- .../Editor/TestSceneOsuComposerSelection.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs index 623cefff6b..366f17daee 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs @@ -46,10 +46,12 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor moveMouseToObject(() => slider); AddStep("seek after end", () => EditorClock.Seek(750)); + AddUntilStep("wait for seek", () => !EditorClock.IsSeeking); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddAssert("slider not selected", () => EditorBeatmap.SelectedHitObjects.Count == 0); AddStep("seek to visible", () => EditorClock.Seek(650)); + AddUntilStep("wait for seek", () => !EditorClock.IsSeeking); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddUntilStep("slider selected", () => EditorBeatmap.SelectedHitObjects.Single() == slider); } From b1813b17a2f84b5ee318a6db89fdff588356678b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 16:39:50 +0900 Subject: [PATCH 0828/1118] Few new rider inspection --- osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs index 69070b0b64..76ed5063b0 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneDifficultySwitching.cs @@ -104,7 +104,7 @@ namespace osu.Game.Tests.Visual.Editing if (sameRuleset) { AddUntilStep("prompt for save dialog shown", () => DialogOverlay.CurrentDialog is PromptForSaveDialog); - AddStep("discard changes", () => ((PromptForSaveDialog)DialogOverlay.CurrentDialog).PerformOkAction()); + AddStep("discard changes", () => ((PromptForSaveDialog)DialogOverlay.CurrentDialog)?.PerformOkAction()); } // ensure editor loader didn't resume. From df99a37254b930b7369c754d09b3ad2ce9d115c3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 17:04:36 +0900 Subject: [PATCH 0829/1118] Fix another realm null inspection --- osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index bb4e06654a..015c2c62ed 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -132,8 +132,8 @@ namespace osu.Game.Tests.Beatmaps public AudioManager AudioManager => Audio; public IResourceStore Files => userSkinResourceStore; public new IResourceStore Resources => base.Resources; - public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null; - RealmAccess IStorageResourceProvider.RealmAccess => null; + public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => throw new NotImplementedException(); + RealmAccess IStorageResourceProvider.RealmAccess => throw new NotImplementedException(); #endregion From f0aeeeea966f06add12cf2bca3dd48dac8573e82 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 17:13:21 +0900 Subject: [PATCH 0830/1118] ...in a safer way --- osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs index 015c2c62ed..1f491be7e3 100644 --- a/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs +++ b/osu.Game/Tests/Beatmaps/HitObjectSampleTest.cs @@ -132,8 +132,8 @@ namespace osu.Game.Tests.Beatmaps public AudioManager AudioManager => Audio; public IResourceStore Files => userSkinResourceStore; public new IResourceStore Resources => base.Resources; - public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => throw new NotImplementedException(); - RealmAccess IStorageResourceProvider.RealmAccess => throw new NotImplementedException(); + public IResourceStore CreateTextureLoaderStore(IResourceStore underlyingStore) => null!; + RealmAccess IStorageResourceProvider.RealmAccess => null!; #endregion From 0bbc27e380b7fb2d430322edba27e8b0122f583f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 16:41:52 +0900 Subject: [PATCH 0831/1118] Add a gameplay configuration flag to disable fail animation --- .../Multiplayer/MultiplayerPlayer.cs | 4 +- osu.Game/Screens/Play/GameplayState.cs | 2 +- osu.Game/Screens/Play/Player.cs | 57 +++++++++++-------- osu.Game/Screens/Play/PlayerConfiguration.cs | 6 ++ 4 files changed, 40 insertions(+), 29 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs index e6d9dd4cd0..d9043df1d5 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerPlayer.cs @@ -26,9 +26,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { protected override bool PauseOnFocusLost => false; - // Disallow fails in multiplayer for now. - protected override bool CheckModsAllowFailure() => false; - protected override UserActivity InitialActivity => new UserActivity.InMultiplayerGame(Beatmap.Value.BeatmapInfo, Ruleset.Value); [Resolved] @@ -55,6 +52,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { AllowPause = false, AllowRestart = false, + AllowFailAnimation = false, AllowSkipping = room.AutoSkip.Value, AutomaticallySkipIntro = room.AutoSkip.Value, AlwaysShowLeaderboard = true, diff --git a/osu.Game/Screens/Play/GameplayState.cs b/osu.Game/Screens/Play/GameplayState.cs index c2162d4df2..cc399a0fbe 100644 --- a/osu.Game/Screens/Play/GameplayState.cs +++ b/osu.Game/Screens/Play/GameplayState.cs @@ -46,7 +46,7 @@ namespace osu.Game.Screens.Play public bool HasPassed { get; set; } /// - /// Whether the user failed during gameplay. + /// Whether the user failed during gameplay. This is only set when the gameplay session has completed due to the fail. /// public bool HasFailed { get; set; } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index c960ac357f..df50e35986 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -735,7 +735,7 @@ namespace osu.Game.Screens.Play } // Only show the completion screen if the player hasn't failed - if (HealthProcessor.HasFailed) + if (GameplayState.HasFailed) return; GameplayState.HasPassed = true; @@ -924,37 +924,44 @@ namespace osu.Game.Screens.Play if (!CheckModsAllowFailure()) return false; - Debug.Assert(!GameplayState.HasFailed); - Debug.Assert(!GameplayState.HasPassed); - Debug.Assert(!GameplayState.HasQuit); + if (Configuration.AllowFailAnimation) + { + Debug.Assert(!GameplayState.HasFailed); + Debug.Assert(!GameplayState.HasPassed); + Debug.Assert(!GameplayState.HasQuit); - GameplayState.HasFailed = true; + GameplayState.HasFailed = true; - updateGameplayState(); + updateGameplayState(); - // There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer) - // could process an extra frame after the GameplayClock is stopped. - // In such cases we want the fail state to precede a user triggered pause. - if (PauseOverlay.State.Value == Visibility.Visible) - PauseOverlay.Hide(); + // There is a chance that we could be in a paused state as the ruleset's internal clock (see FrameStabilityContainer) + // could process an extra frame after the GameplayClock is stopped. + // In such cases we want the fail state to precede a user triggered pause. + if (PauseOverlay.State.Value == Visibility.Visible) + PauseOverlay.Hide(); - failAnimationContainer.Start(); + failAnimationContainer.Start(); - // Failures can be triggered either by a judgement, or by a mod. - // - // For the case of a judgement, due to ordering considerations, ScoreProcessor will not have received - // the final judgement which triggered the failure yet (see DrawableRuleset.NewResult handling above). - // - // A schedule here ensures that any lingering judgements from the current frame are applied before we - // finalise the score as "failed". - Schedule(() => + // Failures can be triggered either by a judgement, or by a mod. + // + // For the case of a judgement, due to ordering considerations, ScoreProcessor will not have received + // the final judgement which triggered the failure yet (see DrawableRuleset.NewResult handling above). + // + // A schedule here ensures that any lingering judgements from the current frame are applied before we + // finalise the score as "failed". + Schedule(() => + { + ScoreProcessor.FailScore(Score.ScoreInfo); + OnFail(); + + if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail)) + Restart(true); + }); + } + else { ScoreProcessor.FailScore(Score.ScoreInfo); - OnFail(); - - if (GameplayState.Mods.OfType().Any(m => m.RestartOnFail)) - Restart(true); - }); + } return true; } diff --git a/osu.Game/Screens/Play/PlayerConfiguration.cs b/osu.Game/Screens/Play/PlayerConfiguration.cs index 122e25f406..466a691118 100644 --- a/osu.Game/Screens/Play/PlayerConfiguration.cs +++ b/osu.Game/Screens/Play/PlayerConfiguration.cs @@ -15,6 +15,12 @@ namespace osu.Game.Screens.Play /// public bool ShowResults { get; set; } = true; + /// + /// Whether the fail animation / screen should be triggered on failing. + /// If false, the score will still be marked as failed but gameplay will continue. + /// + public bool AllowFailAnimation { get; set; } = true; + /// /// Whether the player should be allowed to trigger a restart. /// From 705f25e4b990c0ab726c29d6541121f7eb47b034 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 16:42:05 +0900 Subject: [PATCH 0832/1118] Make `ScoreProcessor.Rank` read-only --- osu.Game/Rulesets/Mods/ModFlashlight.cs | 3 --- osu.Game/Rulesets/Mods/ModHidden.cs | 2 -- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 14 ++++++++------ osu.Game/Screens/Play/BreakOverlay.cs | 4 +++- osu.Game/Screens/Play/Player.cs | 2 -- 5 files changed, 11 insertions(+), 14 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModFlashlight.cs b/osu.Game/Rulesets/Mods/ModFlashlight.cs index 95406cc9e6..dc2ad6f47e 100644 --- a/osu.Game/Rulesets/Mods/ModFlashlight.cs +++ b/osu.Game/Rulesets/Mods/ModFlashlight.cs @@ -56,9 +56,6 @@ namespace osu.Game.Rulesets.Mods public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { Combo.BindTo(scoreProcessor.Combo); - - // Default value of ScoreProcessor's Rank in Flashlight Mod should be SS+ - scoreProcessor.Rank.Value = ScoreRank.XH; } public ScoreRank AdjustRank(ScoreRank rank, double accuracy) diff --git a/osu.Game/Rulesets/Mods/ModHidden.cs b/osu.Game/Rulesets/Mods/ModHidden.cs index 5a8226115f..8b25768575 100644 --- a/osu.Game/Rulesets/Mods/ModHidden.cs +++ b/osu.Game/Rulesets/Mods/ModHidden.cs @@ -17,8 +17,6 @@ namespace osu.Game.Rulesets.Mods public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) { - // Default value of ScoreProcessor's Rank in Hidden Mod should be SS+ - scoreProcessor.Rank.Value = ScoreRank.XH; } public ScoreRank AdjustRank(ScoreRank rank, double accuracy) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 837bb4080e..4ef65c55ab 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -86,7 +86,9 @@ namespace osu.Game.Rulesets.Scoring /// /// The current rank. /// - public readonly Bindable Rank = new Bindable(ScoreRank.X); + public IBindable Rank => rank; + + private readonly Bindable rank = new Bindable(ScoreRank.X); /// /// The highest combo achieved by this score. @@ -186,9 +188,9 @@ namespace osu.Game.Rulesets.Scoring Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue); Accuracy.ValueChanged += accuracy => { - Rank.Value = RankFromAccuracy(accuracy.NewValue); + rank.Value = RankFromAccuracy(accuracy.NewValue); foreach (var mod in Mods.Value.OfType()) - Rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue); + rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue); }; Mods.ValueChanged += mods => @@ -411,8 +413,8 @@ namespace osu.Game.Rulesets.Scoring TotalScore.Value = 0; Accuracy.Value = 1; Combo.Value = 0; - Rank.Disabled = false; - Rank.Value = ScoreRank.X; + rank.Disabled = false; + rank.Value = ScoreRank.X; HighestCombo.Value = 0; } @@ -448,7 +450,7 @@ namespace osu.Game.Rulesets.Scoring return; score.Passed = false; - Rank.Value = ScoreRank.F; + rank.Value = ScoreRank.F; PopulateScore(score); } diff --git a/osu.Game/Screens/Play/BreakOverlay.cs b/osu.Game/Screens/Play/BreakOverlay.cs index 3ca82ec00b..e18612c955 100644 --- a/osu.Game/Screens/Play/BreakOverlay.cs +++ b/osu.Game/Screens/Play/BreakOverlay.cs @@ -4,12 +4,14 @@ #nullable disable using System.Collections.Generic; +using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; using osu.Game.Beatmaps.Timing; using osu.Game.Rulesets.Scoring; +using osu.Game.Scoring; using osu.Game.Screens.Play.Break; namespace osu.Game.Screens.Play @@ -113,7 +115,7 @@ namespace osu.Game.Screens.Play if (scoreProcessor != null) { info.AccuracyDisplay.Current.BindTo(scoreProcessor.Accuracy); - info.GradeDisplay.Current.BindTo(scoreProcessor.Rank); + ((IBindable)info.GradeDisplay.Current).BindTo(scoreProcessor.Rank); } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index df50e35986..b87306b9a2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -801,8 +801,6 @@ namespace osu.Game.Screens.Play // This player instance may already be in the process of exiting. return; - Debug.Assert(ScoreProcessor.Rank.Value != ScoreRank.F); - this.Push(CreateResults(prepareScoreForDisplayTask.GetResultSafely())); }, Time.Current + delay, 50); From a4dee1a01af5c572f4fdd48c58caa754e542a32b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 16:51:25 +0900 Subject: [PATCH 0833/1118] Don't unset `Disabled` on rank (never actually disabled?) --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 4ef65c55ab..6d2b43a3e7 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -413,7 +413,6 @@ namespace osu.Game.Rulesets.Scoring TotalScore.Value = 0; Accuracy.Value = 1; Combo.Value = 0; - rank.Disabled = false; rank.Value = ScoreRank.X; HighestCombo.Value = 0; } From b12011d501ab28a34932176ab72854f4ed19fa52 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 17:04:45 +0900 Subject: [PATCH 0834/1118] Avoid rank updates after failing --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 6d2b43a3e7..13c5d523da 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -188,6 +188,10 @@ namespace osu.Game.Rulesets.Scoring Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue); Accuracy.ValueChanged += accuracy => { + // Once failed, we shouldn't update the rank anymore. + if (rank.Value == ScoreRank.F) + return; + rank.Value = RankFromAccuracy(accuracy.NewValue); foreach (var mod in Mods.Value.OfType()) rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue); From 44d35020d16a1f445abd65758dd37a939de44b3b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 17:54:52 +0900 Subject: [PATCH 0835/1118] Add test coverage of failed multiplayer score --- osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 16030d568b..462286335a 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -29,6 +29,7 @@ using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Scoring; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; @@ -690,6 +691,13 @@ namespace osu.Game.Tests.Visual.Multiplayer } AddUntilStep("wait for results", () => multiplayerComponents.CurrentScreen is ResultsScreen); + + AddAssert("check is fail", () => + { + var scoreInfo = ((ResultsScreen)multiplayerComponents.CurrentScreen).Score; + + return !scoreInfo.Passed && scoreInfo.Rank == ScoreRank.F; + }); } [Test] From fc1a2c594cbf53322b8e10aea3ce173dd6d9d82d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 18:02:37 +0900 Subject: [PATCH 0836/1118] Add a bit more test coverage --- osu.Game.Tests/Mods/ModUtilsTest.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index f1db146f3a..13da69871e 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -314,11 +314,20 @@ namespace osu.Game.Tests.Mods public void TestFormatScoreMultiplier() { Assert.AreEqual(ModUtils.FormatScoreMultiplier(0.9999).ToString(), "0.99x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.0).ToString(), "1.00x"); Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.0001).ToString(), "1.01x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(0.899999999999999).ToString(), "0.90x"); - Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.099999999999999).ToString(), "1.10x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(0.9).ToString(), "0.90x"); Assert.AreEqual(ModUtils.FormatScoreMultiplier(0.900000000000001).ToString(), "0.90x"); + + Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.099999999999999).ToString(), "1.10x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.1).ToString(), "1.10x"); Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.100000000000001).ToString(), "1.10x"); + + Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.045).ToString(), "1.05x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.05).ToString(), "1.05x"); + Assert.AreEqual(ModUtils.FormatScoreMultiplier(1.055).ToString(), "1.06x"); } public abstract class CustomMod1 : Mod, IModCompatibilitySpecification From adac3b65cea7a896907ed5451abedc1e241e866f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 18:48:13 +0900 Subject: [PATCH 0837/1118] Fix beatmap carousel not preloading panels when off-screen --- osu.Game/Screens/Select/BeatmapCarousel.cs | 4 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 73 +++++++++++++------ 2 files changed, 51 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 89911c9a69..3353aeed7d 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -96,12 +96,12 @@ namespace osu.Game.Screens.Select /// /// Extend the range to retain already loaded pooled drawables. /// - private const float distance_offscreen_before_unload = 1024; + private const float distance_offscreen_before_unload = 2048; /// /// Extend the range to update positions / retrieve pooled drawables outside of visible range. /// - private const float distance_offscreen_to_preload = 512; // todo: adjust this appropriately once we can make set panel contents load while off-screen. + private const float distance_offscreen_to_preload = 768; /// /// Whether carousel items have completed asynchronously loaded. diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index f16e92a82a..c24e09582e 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -5,6 +5,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; +using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -46,6 +47,8 @@ namespace osu.Game.Screens.Select.Carousel private MenuItem[]? mainMenuItems; + private double timeSinceUnpool; + [Resolved] private BeatmapManager manager { get; set; } = null!; @@ -54,6 +57,7 @@ namespace osu.Game.Screens.Select.Carousel base.FreeAfterUse(); Item = null; + timeSinceUnpool = 0; ClearTransforms(); } @@ -92,13 +96,21 @@ namespace osu.Game.Screens.Select.Carousel // algorithm for this is taken from ScrollContainer. // while it doesn't necessarily need to match 1:1, as we are emulating scroll in some cases this feels most correct. Y = (float)Interpolation.Lerp(targetY, Y, Math.Exp(-0.01 * Time.Elapsed)); + + loadContentIfRequired(); } + private CancellationTokenSource? loadCancellation; + protected override void UpdateItem() { + loadCancellation?.Cancel(); + loadCancellation = null; + base.UpdateItem(); Content.Clear(); + Header.Clear(); beatmapContainer = null; beatmapsLoadTask = null; @@ -107,32 +119,8 @@ namespace osu.Game.Screens.Select.Carousel return; beatmapSet = ((CarouselBeatmapSet)Item).BeatmapSet; - - DelayedLoadWrapper background; - DelayedLoadWrapper mainFlow; - - Header.Children = new Drawable[] - { - // Choice of background image matches BSS implementation (always uses the lowest `beatmap_id` from the set). - background = new DelayedLoadWrapper(() => new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) - { - RelativeSizeAxes = Axes.Both, - }, 200) - { - RelativeSizeAxes = Axes.Both - }, - mainFlow = new DelayedLoadWrapper(() => new SetPanelContent((CarouselBeatmapSet)Item), 50) - { - RelativeSizeAxes = Axes.Both - }, - }; - - background.DelayedLoadComplete += fadeContentIn; - mainFlow.DelayedLoadComplete += fadeContentIn; } - private void fadeContentIn(Drawable d) => d.FadeInFromZero(150); - protected override void Deselected() { base.Deselected(); @@ -190,6 +178,43 @@ namespace osu.Game.Screens.Select.Carousel } } + private void loadContentIfRequired() + { + // Using DelayedLoadWrappers would only allow us to load content when on screen, but we want to preload while off-screen + // to provide a better user experience. + + // This is tracking time that this drawable is updating since the last pool. + // This is intended to provide a debounce so very fast scrolls (from one end to the other of the carousel) + // don't cause huge overheads. + const double time_updating_before_load = 150; + + Debug.Assert(Item != null); + + if (loadCancellation == null && (timeSinceUnpool += Time.Elapsed) > time_updating_before_load) + { + loadCancellation = new CancellationTokenSource(); + + LoadComponentAsync(new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) + { + RelativeSizeAxes = Axes.Both, + }, background => + { + Header.Add(background); + background.FadeInFromZero(150); + }, loadCancellation.Token); + + LoadComponentAsync(new SetPanelContent((CarouselBeatmapSet)Item) + { + Depth = float.MinValue, + RelativeSizeAxes = Axes.Both, + }, mainFlow => + { + Header.Add(mainFlow); + mainFlow.FadeInFromZero(150); + }, loadCancellation.Token); + } + } + private void updateBeatmapYPositions() { if (beatmapContainer == null) From 81c6fd5589751e125a8728ad57eb286dc4b146f1 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 19:13:36 +0900 Subject: [PATCH 0838/1118] Load items closer to the centre of the screen as a priority --- osu.Game/Screens/Select/BeatmapCarousel.cs | 3 +- .../Carousel/DrawableCarouselBeatmapSet.cs | 41 +++++++++++-------- 2 files changed, 26 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 3353aeed7d..4408634787 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -108,6 +108,7 @@ namespace osu.Game.Screens.Select /// public bool BeatmapSetsLoaded { get; private set; } + [Cached] protected readonly CarouselScrollContainer Scroll; private readonly NoResultsPlaceholder noResultsPlaceholder; @@ -1251,7 +1252,7 @@ namespace osu.Game.Screens.Select } } - protected partial class CarouselScrollContainer : UserTrackingScrollContainer + public partial class CarouselScrollContainer : UserTrackingScrollContainer { private bool rightMouseScrollBlocked; diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index c24e09582e..61658526db 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -8,9 +8,11 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using osu.Framework.Allocation; +using osu.Framework.Extensions.IEnumerableExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; using osu.Framework.Utils; using osu.Game.Beatmaps; @@ -178,39 +180,44 @@ namespace osu.Game.Screens.Select.Carousel } } + [Resolved] + private BeatmapCarousel.CarouselScrollContainer scrollContainer { get; set; } = null!; + private void loadContentIfRequired() { + Quad containingSsdq = scrollContainer.ScreenSpaceDrawQuad; + // Using DelayedLoadWrappers would only allow us to load content when on screen, but we want to preload while off-screen // to provide a better user experience. // This is tracking time that this drawable is updating since the last pool. // This is intended to provide a debounce so very fast scrolls (from one end to the other of the carousel) // don't cause huge overheads. - const double time_updating_before_load = 150; + // + // We increase the delay based on distance from centre, so the beatmaps the user is currently looking at load first. + float timeUpdatingBeforeLoad = 50 + Math.Abs(containingSsdq.Centre.Y - ScreenSpaceDrawQuad.Centre.Y) / containingSsdq.Height * 100; Debug.Assert(Item != null); - if (loadCancellation == null && (timeSinceUnpool += Time.Elapsed) > time_updating_before_load) + if (loadCancellation == null && (timeSinceUnpool += Time.Elapsed) > timeUpdatingBeforeLoad) { loadCancellation = new CancellationTokenSource(); - LoadComponentAsync(new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) + LoadComponentsAsync(new CompositeDrawable[] { - RelativeSizeAxes = Axes.Both, - }, background => + new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) + { + RelativeSizeAxes = Axes.Both, + }, + new SetPanelContent((CarouselBeatmapSet)Item) + { + Depth = float.MinValue, + RelativeSizeAxes = Axes.Both, + } + }, drawables => { - Header.Add(background); - background.FadeInFromZero(150); - }, loadCancellation.Token); - - LoadComponentAsync(new SetPanelContent((CarouselBeatmapSet)Item) - { - Depth = float.MinValue, - RelativeSizeAxes = Axes.Both, - }, mainFlow => - { - Header.Add(mainFlow); - mainFlow.FadeInFromZero(150); + Header.AddRange(drawables); + drawables.ForEach(d => d.FadeInFromZero(150)); }, loadCancellation.Token); } } From 085f5acd1abaf8cf309c5e2ccb12df367a19cc33 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 4 Jan 2024 19:26:28 +0900 Subject: [PATCH 0839/1118] Fix another rider inspection (why do these keep coming up at random) --- osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs index 8c32135cfd..8691f46605 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneBeatmapListingOverlay.cs @@ -368,7 +368,7 @@ namespace osu.Game.Tests.Visual.Online { var cardContainer = this.ChildrenOfType>().Single().Parent; var expandedContent = this.ChildrenOfType().Single(); - return expandedContent.ScreenSpaceDrawQuad.GetVertices().ToArray().All(v => cardContainer.ScreenSpaceDrawQuad.Contains(v)); + return expandedContent.ScreenSpaceDrawQuad.GetVertices().ToArray().All(v => cardContainer!.ScreenSpaceDrawQuad.Contains(v)); }); } From 9b734bac2515c5a8cf61ca3cc7576bf1e012a4b9 Mon Sep 17 00:00:00 2001 From: Zachary Date: Fri, 5 Jan 2024 01:14:34 +1000 Subject: [PATCH 0840/1118] Allow track control after intro screen finishes. --- osu.Game/Overlays/MusicController.cs | 2 +- osu.Game/Screens/Menu/MainMenu.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 0986c0513c..01086d3e33 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays /// /// Whether user control of the global track should be allowed. /// - public readonly BindableBool AllowTrackControl = new BindableBool(true); + public readonly BindableBool AllowTrackControl = new BindableBool(false); /// /// Fired when the global has changed. diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index 516b090a16..ffb68367c2 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -241,6 +241,8 @@ namespace osu.Game.Screens.Menu { var track = musicController.CurrentTrack; + musicController.AllowTrackControl.Value = true; + // presume the track is the current beatmap's track. not sure how correct this assumption is but it has worked until now. if (!track.IsRunning) { From 91bb3f6c57d7263876d5de57bab01c5d4b444b9c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jan 2024 01:24:00 +0900 Subject: [PATCH 0841/1118] Cache argon character glyph lookups to reduce string allocations --- .../Play/HUD/ArgonCounterTextComponent.cs | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index 2a3f4365cb..266dfb3301 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; @@ -137,33 +139,48 @@ namespace osu.Game.Screens.Play.HUD [BackgroundDependencyLoader] private void load(TextureStore textures) { + const string font_name = @"argon-counter"; + Spacing = new Vector2(-2f, 0f); - Font = new FontUsage(@"argon-counter", 1); - glyphStore = new GlyphStore(textures, getLookup); + Font = new FontUsage(font_name, 1); + glyphStore = new GlyphStore(font_name, textures, getLookup); } protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); private class GlyphStore : ITexturedGlyphLookupStore { + private readonly string fontName; private readonly TextureStore textures; private readonly Func getLookup; - public GlyphStore(TextureStore textures, Func getLookup) + private readonly Dictionary cache = new Dictionary(); + + public GlyphStore(string fontName, TextureStore textures, Func getLookup) { + this.fontName = fontName; this.textures = textures; this.getLookup = getLookup; } public ITexturedCharacterGlyph? Get(string? fontName, char character) { + // We only service one font. + Debug.Assert(fontName == this.fontName); + + if (cache.TryGetValue(character, out var cached)) + return cached; + string lookup = getLookup(character); var texture = textures.Get($"Gameplay/Fonts/{fontName}-{lookup}"); - if (texture == null) - return null; + TexturedCharacterGlyph? glyph = null; - return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.125f); + if (texture != null) + glyph = new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 0.125f); + + cache[character] = glyph; + return glyph; } public Task GetAsync(string fontName, char character) => Task.Run(() => Get(fontName, character)); From b190333c17f3e0209e7f04ad1463b272b957c774 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 4 Jan 2024 09:00:24 -0800 Subject: [PATCH 0842/1118] Use repeat step for more delay between the two exits --- .../Visual/Navigation/TestSceneScreenNavigation.cs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index d8dc512787..a0069f55c7 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -799,11 +799,7 @@ namespace osu.Game.Tests.Visual.Navigation }); }); - AddStep("attempt exit", () => - { - for (int i = 0; i < 2; ++i) - Game.ScreenStack.CurrentScreen.Exit(); - }); + AddRepeatStep("attempt force exit", () => Game.ScreenStack.CurrentScreen.Exit(), 2); AddUntilStep("stopped at exit confirm", () => Game.ChildrenOfType().Single().CurrentDialog is ConfirmExitDialog); } From 5b55ca66920b40b394b0403f92b7f371c35cf6b9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jan 2024 02:26:26 +0900 Subject: [PATCH 0843/1118] Cache legacy skin character glyph lookups to reduce string allocations --- osu.Game/Skinning/LegacySpriteText.cs | 35 +++++++++++++++++++++------ 1 file changed, 27 insertions(+), 8 deletions(-) diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs index 8aefa50252..81db5fdf36 100644 --- a/osu.Game/Skinning/LegacySpriteText.cs +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -2,6 +2,8 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; +using System.Diagnostics; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; @@ -44,10 +46,11 @@ namespace osu.Game.Skinning [BackgroundDependencyLoader] private void load(ISkinSource skin) { - base.Font = new FontUsage(skin.GetFontPrefix(font), 1, fixedWidth: FixedWidth); + string fontPrefix = skin.GetFontPrefix(font); + base.Font = new FontUsage(fontPrefix, 1, fixedWidth: FixedWidth); Spacing = new Vector2(-skin.GetFontOverlap(font), 0); - glyphStore = new LegacyGlyphStore(skin, MaxSizePerGlyph); + glyphStore = new LegacyGlyphStore(fontPrefix, skin, MaxSizePerGlyph); } protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); @@ -57,25 +60,41 @@ namespace osu.Game.Skinning private readonly ISkin skin; private readonly Vector2? maxSize; - public LegacyGlyphStore(ISkin skin, Vector2? maxSize) + private readonly string fontName; + + private readonly Dictionary cache = new Dictionary(); + + public LegacyGlyphStore(string fontName, ISkin skin, Vector2? maxSize) { + this.fontName = fontName; this.skin = skin; this.maxSize = maxSize; } public ITexturedCharacterGlyph? Get(string? fontName, char character) { + // We only service one font. + Debug.Assert(fontName == this.fontName); + + if (cache.TryGetValue(character, out var cached)) + return cached; + string lookup = getLookupName(character); var texture = skin.GetTexture($"{fontName}-{lookup}"); - if (texture == null) - return null; + TexturedCharacterGlyph? glyph = null; - if (maxSize != null) - texture = texture.WithMaximumSize(maxSize.Value); + if (texture != null) + { + if (maxSize != null) + texture = texture.WithMaximumSize(maxSize.Value); - return new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 1f / texture.ScaleAdjust); + glyph = new TexturedCharacterGlyph(new CharacterGlyph(character, 0, 0, texture.Width, texture.Height, null), texture, 1f / texture.ScaleAdjust); + } + + cache[character] = glyph; + return glyph; } private static string getLookupName(char character) From e9289cfbe78f318d00c9b77065daca6fa824cb09 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jan 2024 01:51:20 +0900 Subject: [PATCH 0844/1118] Reduce precision of audio balance adjustments during slider sliding --- osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs index 1daaa24d57..bce28361cb 100644 --- a/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs +++ b/osu.Game/Rulesets/Objects/Drawables/DrawableHitObject.cs @@ -599,7 +599,9 @@ namespace osu.Game.Rulesets.Objects.Drawables float balanceAdjustAmount = positionalHitsoundsLevel.Value * 2; double returnedValue = balanceAdjustAmount * (position - 0.5f); - return returnedValue; + // Rounded to reduce the overhead of audio adjustments (which are currently bindable heavy). + // Balance is very hard to perceive in small increments anyways. + return Math.Round(returnedValue, 2); } /// From 091241634c08aaea3b6d3e34762fb2f1cb459295 Mon Sep 17 00:00:00 2001 From: Zachary Date: Fri, 5 Jan 2024 23:55:17 +1000 Subject: [PATCH 0845/1118] Make IntroScreen set `AllowTrackControl` to false instead --- osu.Game/Overlays/MusicController.cs | 2 +- osu.Game/Screens/Menu/IntroScreen.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/MusicController.cs b/osu.Game/Overlays/MusicController.cs index 01086d3e33..0986c0513c 100644 --- a/osu.Game/Overlays/MusicController.cs +++ b/osu.Game/Overlays/MusicController.cs @@ -43,7 +43,7 @@ namespace osu.Game.Overlays /// /// Whether user control of the global track should be allowed. /// - public readonly BindableBool AllowTrackControl = new BindableBool(false); + public readonly BindableBool AllowTrackControl = new BindableBool(true); /// /// Fired when the global has changed. diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index de7732dd5e..a81e24ee88 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -109,6 +109,8 @@ namespace osu.Game.Screens.Menu // prevent user from changing beatmap while the intro is still running. beatmap = Beatmap.BeginLease(false); + musicController.AllowTrackControl.Value = false; + MenuVoice = config.GetBindable(OsuSetting.MenuVoice); MenuMusic = config.GetBindable(OsuSetting.MenuMusic); From 8295ad1feb19c30ae944e2f8ea9bd3c5df6d04ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 2 Jan 2024 21:58:44 +0100 Subject: [PATCH 0846/1118] Change catch scoring to match score V2 --- .../Scoring/CatchScoreProcessor.cs | 60 ++++++++++++++++++- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 24 ++++---- 2 files changed, 69 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs index 4b3d378889..161a59c5fd 100644 --- a/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs +++ b/osu.Game.Rulesets.Catch/Scoring/CatchScoreProcessor.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Collections.Generic; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Scoring; @@ -20,20 +21,73 @@ namespace osu.Game.Rulesets.Catch.Scoring private const int combo_cap = 200; private const double combo_base = 4; + private double fruitTinyScale; + public CatchScoreProcessor() : base(new CatchRuleset()) { } + protected override void Reset(bool storeResults) + { + base.Reset(storeResults); + + // large ticks are *purposefully* not counted to match stable + int fruitTinyScaleDivisor = MaximumResultCounts.GetValueOrDefault(HitResult.SmallTickHit) + MaximumResultCounts.GetValueOrDefault(HitResult.Great); + fruitTinyScale = fruitTinyScaleDivisor == 0 + ? 0 + : (double)MaximumResultCounts.GetValueOrDefault(HitResult.SmallTickHit) / fruitTinyScaleDivisor; + } + protected override double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion) { - return 600000 * comboProgress - + 400000 * Accuracy.Value * accuracyProgress + const int max_tiny_droplets_portion = 400000; + + double comboPortion = 1000000 - max_tiny_droplets_portion + max_tiny_droplets_portion * (1 - fruitTinyScale); + double dropletsPortion = max_tiny_droplets_portion * fruitTinyScale; + double dropletsHit = MaximumResultCounts.GetValueOrDefault(HitResult.SmallTickHit) == 0 + ? 0 + : (double)ScoreResultCounts.GetValueOrDefault(HitResult.SmallTickHit) / MaximumResultCounts.GetValueOrDefault(HitResult.SmallTickHit); + + return comboPortion * comboProgress + + dropletsPortion * dropletsHit + bonusPortion; } + public override int GetBaseScoreForResult(HitResult result) + { + switch (result) + { + // dirty hack to emulate accuracy on stable weighting every object equally in accuracy portion + case HitResult.Great: + case HitResult.LargeTickHit: + case HitResult.SmallTickHit: + return 300; + + case HitResult.LargeBonus: + return 200; + } + + return base.GetBaseScoreForResult(result); + } + protected override double GetComboScoreChange(JudgementResult result) - => GetBaseScoreForResult(result.Type) * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); + { + double baseIncrease = 0; + + switch (result.Type) + { + case HitResult.Great: + baseIncrease = 300; + break; + + case HitResult.LargeTickHit: + baseIncrease = 100; + break; + } + + return baseIncrease * Math.Min(Math.Max(0.5, Math.Log(result.ComboAfterJudgement, combo_base)), Math.Log(combo_cap, combo_base)); + } public override ScoreRank RankFromAccuracy(double accuracy) { diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 837bb4080e..869ad2c4ae 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -167,14 +167,14 @@ namespace osu.Game.Rulesets.Scoring if (!beatmapApplied) throw new InvalidOperationException($"Cannot access maximum statistics before calling {nameof(ApplyBeatmap)}."); - return new Dictionary(maximumResultCounts); + return new Dictionary(MaximumResultCounts); } } private bool beatmapApplied; - private readonly Dictionary scoreResultCounts = new Dictionary(); - private readonly Dictionary maximumResultCounts = new Dictionary(); + protected readonly Dictionary ScoreResultCounts = new Dictionary(); + protected readonly Dictionary MaximumResultCounts = new Dictionary(); private readonly List hitEvents = new List(); private HitObject? lastHitObject; @@ -216,7 +216,7 @@ namespace osu.Game.Rulesets.Scoring if (result.FailedAtJudgement) return; - scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) + 1; + ScoreResultCounts[result.Type] = ScoreResultCounts.GetValueOrDefault(result.Type) + 1; if (result.Type.IncreasesCombo()) Combo.Value++; @@ -272,7 +272,7 @@ namespace osu.Game.Rulesets.Scoring if (result.FailedAtJudgement) return; - scoreResultCounts[result.Type] = scoreResultCounts.GetValueOrDefault(result.Type) - 1; + ScoreResultCounts[result.Type] = ScoreResultCounts.GetValueOrDefault(result.Type) - 1; if (result.Judgement.MaxResult.AffectsAccuracy()) { @@ -394,13 +394,13 @@ namespace osu.Game.Rulesets.Scoring maximumComboPortion = currentComboPortion; maximumAccuracyJudgementCount = currentAccuracyJudgementCount; - maximumResultCounts.Clear(); - maximumResultCounts.AddRange(scoreResultCounts); + MaximumResultCounts.Clear(); + MaximumResultCounts.AddRange(ScoreResultCounts); MaximumTotalScore = TotalScore.Value; } - scoreResultCounts.Clear(); + ScoreResultCounts.Clear(); currentBaseScore = 0; currentMaximumBaseScore = 0; @@ -430,10 +430,10 @@ namespace osu.Game.Rulesets.Scoring score.MaximumStatistics.Clear(); foreach (var result in HitResultExtensions.ALL_TYPES) - score.Statistics[result] = scoreResultCounts.GetValueOrDefault(result); + score.Statistics[result] = ScoreResultCounts.GetValueOrDefault(result); foreach (var result in HitResultExtensions.ALL_TYPES) - score.MaximumStatistics[result] = maximumResultCounts.GetValueOrDefault(result); + score.MaximumStatistics[result] = MaximumResultCounts.GetValueOrDefault(result); // Populate total score after everything else. score.TotalScore = TotalScore.Value; @@ -464,8 +464,8 @@ namespace osu.Game.Rulesets.Scoring HighestCombo.Value = frame.Header.MaxCombo; TotalScore.Value = frame.Header.TotalScore; - scoreResultCounts.Clear(); - scoreResultCounts.AddRange(frame.Header.Statistics); + ScoreResultCounts.Clear(); + ScoreResultCounts.AddRange(frame.Header.Statistics); SetScoreProcessorStatistics(frame.Header.ScoreProcessorStatistics); From ea7078fab52bc1db38f4311c8822e99fa6e82e0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 3 Jan 2024 15:03:06 +0100 Subject: [PATCH 0847/1118] Implement approximate score conversion algorithm matching score V2 --- .../StandardisedScoreMigrationTools.cs | 113 +++++++++++++++++- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 +- 2 files changed, 113 insertions(+), 3 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 9cfb9ea957..f029d85aed 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -437,9 +437,30 @@ namespace osu.Game.Database break; case 2: + // compare logic in `CatchScoreProcessor`. + + // this could technically be slightly incorrect in the case of stable scores. + // because large droplet misses are counted as full misses in stable scores, + // `score.MaximumStatistics.GetValueOrDefault(Great)` will be equal to the count of fruits *and* large droplets + // rather than just fruits (which was the intent). + // this is not fixable without introducing an extra legacy score attribute dedicated for catch, + // and this is a ballpark conversion process anyway, so attempt to trudge on. + int fruitTinyScaleDivisor = score.MaximumStatistics.GetValueOrDefault(HitResult.SmallTickHit) + score.MaximumStatistics.GetValueOrDefault(HitResult.Great); + double fruitTinyScale = fruitTinyScaleDivisor == 0 + ? 0 + : (double)score.MaximumStatistics.GetValueOrDefault(HitResult.SmallTickHit) / fruitTinyScaleDivisor; + + const int max_tiny_droplets_portion = 400000; + + double comboPortion = 1000000 - max_tiny_droplets_portion + max_tiny_droplets_portion * (1 - fruitTinyScale); + double dropletsPortion = max_tiny_droplets_portion * fruitTinyScale; + double dropletsHit = score.MaximumStatistics.GetValueOrDefault(HitResult.SmallTickHit) == 0 + ? 0 + : (double)score.Statistics.GetValueOrDefault(HitResult.SmallTickHit) / score.MaximumStatistics.GetValueOrDefault(HitResult.SmallTickHit); + convertedTotalScore = (long)Math.Round(( - 600000 * comboProportion - + 400000 * score.Accuracy + comboPortion * estimateComboProportionForCatch(attributes.MaxCombo, score.MaxCombo, score.Statistics.GetValueOrDefault(HitResult.Miss)) + + dropletsPortion * dropletsHit + bonusProportion) * modMultiplier); break; @@ -461,6 +482,94 @@ namespace osu.Game.Database return convertedTotalScore; } + /// + /// + /// For catch, the general method of calculating the combo proportion used for other rulesets is generally useless. + /// This is because in stable score V1, catch has quadratic score progression, + /// while in stable score V2, score progression is logarithmic up to 200 combo and then linear. + /// + /// + /// This means that applying the naive rescale method to scores with lots of short combos (think 10x 100-long combos on a 1000-object map) + /// by linearly rescaling the combo portion as given by score V1 leads to horribly underestimating it. + /// Therefore this method attempts to counteract this by calculating the best case estimate for the combo proportion that takes all of the above into account. + /// + /// + /// The general idea is that aside from the which the player is known to have hit, + /// the remaining misses are evenly distributed across the rest of the objects that give combo. + /// This is therefore a worst-case estimate. + /// + /// + private static double estimateComboProportionForCatch(int beatmapMaxCombo, int scoreMaxCombo, int scoreMissCount) + { + if (beatmapMaxCombo == 0) + return 1; + + if (scoreMaxCombo == 0) + return 0; + + if (beatmapMaxCombo == scoreMaxCombo) + return 1; + + double estimatedBestCaseTotal = estimateBestCaseComboTotal(beatmapMaxCombo); + + int remainingCombo = beatmapMaxCombo - (scoreMaxCombo + scoreMissCount); + double totalDroppedScore = 0; + + int assumedLengthOfRemainingCombos = (int)Math.Floor((double)remainingCombo / scoreMissCount); + + if (assumedLengthOfRemainingCombos > 0) + { + while (remainingCombo > 0) + { + int comboLength = Math.Min(assumedLengthOfRemainingCombos, remainingCombo); + + remainingCombo -= comboLength; + totalDroppedScore += estimateDroppedComboScoreAfterMiss(comboLength); + } + } + else + { + // there are so many misses that attempting to evenly divide remaining combo results in 0 length per combo, + // i.e. all remaining judgements are combo breaks. + // in that case, presume every single remaining object is a miss and did not give any combo score. + totalDroppedScore = estimatedBestCaseTotal - estimateBestCaseComboTotal(scoreMaxCombo); + } + + return estimatedBestCaseTotal == 0 + ? 1 + : 1 - Math.Clamp(totalDroppedScore / estimatedBestCaseTotal, 0, 1); + + double estimateBestCaseComboTotal(int maxCombo) + { + if (maxCombo == 0) + return 1; + + double estimatedTotal = 0.5 * Math.Min(maxCombo, 2); + + if (maxCombo <= 2) + return estimatedTotal; + + // int_2^x log_4(t) dt + estimatedTotal += (Math.Min(maxCombo, 200) * (Math.Log(Math.Min(maxCombo, 200)) - 1) + 2 - Math.Log(4)) / Math.Log(4); + + if (maxCombo <= 200) + return estimatedTotal; + + estimatedTotal += (maxCombo - 200) * Math.Log(200) / Math.Log(4); + return estimatedTotal; + } + + double estimateDroppedComboScoreAfterMiss(int lengthOfComboAfterMiss) + { + if (lengthOfComboAfterMiss >= 200) + lengthOfComboAfterMiss = 200; + + // int_0^x (log_4(200) - log_4(t)) dt + // note that this is an pessimistic estimate, i.e. it may subtract too much if the miss happened before reaching 200 combo + return lengthOfComboAfterMiss * (1 + Math.Log(200) - Math.Log(lengthOfComboAfterMiss)) / Math.Log(4); + } + } + public static double ComputeAccuracy(ScoreInfo scoreInfo) { Ruleset ruleset = scoreInfo.Ruleset.CreateInstance(); diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index cf0a7bd54f..495edaf49c 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -36,9 +36,10 @@ namespace osu.Game.Scoring.Legacy /// 30000007: Adjust osu!mania combo and accuracy portions and judgement scoring values. Reconvert all scores. /// 30000008: Add accuracy conversion. Reconvert all scores. /// 30000009: Fix edge cases in conversion for scores which have 0.0x mod multiplier on stable. Reconvert all scores. + /// 30000010: Re-do catch scoring to mirror stable Score V2 as closely as feasible. Reconvert all scores. /// /// - public const int LATEST_VERSION = 30000009; + public const int LATEST_VERSION = 30000010; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From e2769dbda14b21e550c3bafa9446af1e406e2966 Mon Sep 17 00:00:00 2001 From: Zachary Date: Sat, 6 Jan 2024 19:29:41 +1000 Subject: [PATCH 0848/1118] Attempt at creating a test. --- .../TestSceneIntroMusicActionHandling.cs | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs new file mode 100644 index 0000000000..cc2b16a842 --- /dev/null +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs @@ -0,0 +1,47 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System.Linq; +using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Input.Bindings; +using osu.Game.Screens.Menu; + +namespace osu.Game.Tests.Visual.Menus +{ + public partial class TestSceneIntroMusicActionHandling : OsuTestScene + { + private OsuGameTestScene.TestOsuGame? game; + + private GlobalActionContainer globalActionContainer => game.ChildrenOfType().First(); + + [Test] + public void TestPauseDuringIntro() + { + AddStep("Create new game instance", () => + { + if (game?.Parent != null) + Remove(game, true); + + RecycleLocalStorage(false); + + AddGame(game = new OsuGameTestScene.TestOsuGame(LocalStorage, API)); + }); + + AddUntilStep("Wait for load", () => game?.IsLoaded ?? false); + AddUntilStep("Wait for intro", () => game?.ScreenStack.CurrentScreen is IntroScreen); + AddUntilStep("Wait for music", () => game?.MusicController.IsPlaying == true); + + // Check that pause dosesn't work during intro sequence. + AddStep("Toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay)); + AddAssert("Still playing before menu", () => game?.MusicController.IsPlaying == true); + AddUntilStep("Wait for main menu", () => game?.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded); + + // Check that toggling after intro still works. + AddStep("Toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay)); + AddUntilStep("Music paused", () => game?.MusicController.IsPlaying == false && game?.MusicController.UserPauseRequested == true); + AddStep("Toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay)); + AddUntilStep("Music resumed", () => game?.MusicController.IsPlaying == true && game?.MusicController.UserPauseRequested == false); + } + } +} From 14a43375a70104094f7a9d23cf091c18d1223ad3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 6 Jan 2024 20:25:03 +0900 Subject: [PATCH 0849/1118] Fix overall ranking text overlapping at some aspect ratios Can't confirm on the actual ranking screen due to stuff not working. Maybe it'll work tomorrow. Closes https://github.com/ppy/osu/issues/26341. --- osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs b/osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs index 73b9897096..762be61853 100644 --- a/osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs +++ b/osu.Game/Screens/Ranking/Statistics/SoloStatisticsPanel.cs @@ -37,7 +37,6 @@ namespace osu.Game.Screens.Ranking.Statistics RelativeSizeAxes = Axes.X, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Width = 0.5f, StatisticsUpdate = { BindTarget = StatisticsUpdate } })).ToArray(); } From d3710f0bfd0e8fb457ffbde1fb14dce853ad4f14 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 6 Jan 2024 20:44:07 +0900 Subject: [PATCH 0850/1118] Remove scores from song select leaderboard when leaving the screen --- osu.Game/Online/Leaderboards/Leaderboard.cs | 13 ++++++++++--- osu.Game/Screens/Select/PlaySongSelect.cs | 8 ++++++++ 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 93aa0b95a7..67f2590ad8 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -152,6 +152,15 @@ namespace osu.Game.Online.Leaderboards /// public void RefetchScores() => Scheduler.AddOnce(refetchScores); + /// + /// Clear all scores from the display. + /// + public void ClearScores() + { + cancelPendingWork(); + SetScores(null); + } + /// /// Call when a retrieval or display failure happened to show a relevant message to the user. /// @@ -220,9 +229,7 @@ namespace osu.Game.Online.Leaderboards { Debug.Assert(ThreadSafety.IsUpdateThread); - cancelPendingWork(); - - SetScores(null); + ClearScores(); setState(LeaderboardState.Retrieving); currentFetchCancellationSource = new CancellationTokenSource(); diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 7b7b8857f3..4951504ff5 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -146,6 +146,14 @@ namespace osu.Game.Screens.Select } } + public override void OnSuspending(ScreenTransitionEvent e) + { + // Scores will be refreshed on arriving at this screen. + // Clear them to avoid animation overload on returning to song select. + playBeatmapDetailArea.Leaderboard.ClearScores(); + base.OnSuspending(e); + } + public override void OnResuming(ScreenTransitionEvent e) { base.OnResuming(e); From b809d4c068f77558e30e80e0a9a8846cd41af5c9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jan 2024 02:14:03 +0900 Subject: [PATCH 0851/1118] Remove delegate overhead from argon health display's animation updates --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 16 ++++++++++++---- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 8acc43c091..7721f9c0c0 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -6,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; +using osu.Framework.Caching; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; @@ -68,11 +69,11 @@ namespace osu.Game.Screens.Play.HUD get => glowBarValue; set { - if (glowBarValue == value) + if (Precision.AlmostEquals(glowBarValue, value, 0.0001)) return; glowBarValue = value; - Scheduler.AddOnce(updatePathVertices); + pathVerticesCache.Invalidate(); } } @@ -83,11 +84,11 @@ namespace osu.Game.Screens.Play.HUD get => healthBarValue; set { - if (healthBarValue == value) + if (Precision.AlmostEquals(healthBarValue, value, 0.0001)) return; healthBarValue = value; - Scheduler.AddOnce(updatePathVertices); + pathVerticesCache.Invalidate(); } } @@ -100,6 +101,8 @@ namespace osu.Game.Screens.Play.HUD private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize); + private readonly Cached pathVerticesCache = new Cached(); + public ArgonHealthDisplay() { AddLayout(drawSizeLayout); @@ -208,6 +211,9 @@ namespace osu.Game.Screens.Play.HUD drawSizeLayout.Validate(); } + if (!pathVerticesCache.IsValid) + updatePathVertices(); + mainBar.Alpha = (float)Interpolation.DampContinuously(mainBar.Alpha, Current.Value > 0 ? 1 : 0, 40, Time.Elapsed); glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed); } @@ -346,6 +352,8 @@ namespace osu.Game.Screens.Play.HUD mainBar.Vertices = healthBarVertices.Select(v => v - healthBarVertices[0]).ToList(); mainBar.Position = healthBarVertices[0]; + + pathVerticesCache.Validate(); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 7747036826..54aba6b8da 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -30,7 +30,7 @@ namespace osu.Game.Screens.Play.HUD public Bindable Current { get; } = new BindableDouble { MinValue = 0, - MaxValue = 1 + MaxValue = 1, }; private BindableNumber health = null!; From 9d9e6fcfdbc3c21faa7e637375ac4a9e4cdf0c51 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jan 2024 03:22:19 +0900 Subject: [PATCH 0852/1118] Remove LINQ calls in hot paths --- osu.Game/Skinning/SkinnableSound.cs | 28 ++++++++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/SkinnableSound.cs b/osu.Game/Skinning/SkinnableSound.cs index f866a4f8ec..f153f4f8d3 100644 --- a/osu.Game/Skinning/SkinnableSound.cs +++ b/osu.Game/Skinning/SkinnableSound.cs @@ -194,9 +194,33 @@ namespace osu.Game.Skinning /// /// Whether any samples are currently playing. /// - public bool IsPlaying => samplesContainer.Any(s => s.Playing); + public bool IsPlaying + { + get + { + foreach (PoolableSkinnableSample s in samplesContainer) + { + if (s.Playing) + return true; + } - public bool IsPlayed => samplesContainer.Any(s => s.Played); + return false; + } + } + + public bool IsPlayed + { + get + { + foreach (PoolableSkinnableSample s in samplesContainer) + { + if (s.Played) + return true; + } + + return false; + } + } public IBindable AggregateVolume => samplesContainer.AggregateVolume; From 35eff639cb936188f73c34dc71e67ea4d93a061d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jan 2024 03:25:00 +0900 Subject: [PATCH 0853/1118] Remove unnecessary second iteration over `NestedHitObjects` --- .../Objects/Drawables/DrawableSlider.cs | 7 ++----- .../Objects/Drawables/DrawableSliderRepeat.cs | 2 +- .../Objects/Drawables/ITrackSnaking.cs | 15 --------------- 3 files changed, 3 insertions(+), 21 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index b306fd38c1..0f8c9a4d36 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -246,11 +246,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Ball.UpdateProgress(completionProgress); SliderBody?.UpdateProgress(HeadCircle.IsHit ? completionProgress : 0); - foreach (DrawableHitObject hitObject in NestedHitObjects) - { - if (hitObject is ITrackSnaking s) - s.UpdateSnakingPosition(HitObject.Path.PositionAt(SliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(SliderBody?.SnakedEnd ?? 0)); - } + foreach (DrawableSliderRepeat repeat in repeatContainer) + repeat.UpdateSnakingPosition(HitObject.Path.PositionAt(SliderBody?.SnakedStart ?? 0), HitObject.Path.PositionAt(SliderBody?.SnakedEnd ?? 0)); Size = SliderBody?.Size ?? Vector2.Zero; OriginPosition = SliderBody?.PathOffset ?? Vector2.Zero; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs index c6d4f7c4ca..3239565528 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSliderRepeat.cs @@ -17,7 +17,7 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Objects.Drawables { - public partial class DrawableSliderRepeat : DrawableOsuHitObject, ITrackSnaking + public partial class DrawableSliderRepeat : DrawableOsuHitObject { public new SliderRepeat HitObject => (SliderRepeat)base.HitObject; diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs deleted file mode 100644 index cae2a7c36d..0000000000 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/ITrackSnaking.cs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using osuTK; - -namespace osu.Game.Rulesets.Osu.Objects.Drawables -{ - /// - /// A component which tracks the current end snaking position of a slider. - /// - public interface ITrackSnaking - { - void UpdateSnakingPosition(Vector2 start, Vector2 end); - } -} From 5cc4a586acf40fd3d9be3425f875e54406a38c4d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Fri, 5 Jan 2024 03:26:58 +0900 Subject: [PATCH 0854/1118] Avoid iteration over `NestedHitObjects` in silder's `Update` unless necessary --- .../Objects/Drawables/DrawableSlider.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 0f8c9a4d36..c5194025c1 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -36,6 +36,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private ShakeContainer shakeContainer; + private Vector2? childAnchorPosition; + protected override IEnumerable DimmablePieces => new Drawable[] { HeadCircle, @@ -254,10 +256,15 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables if (DrawSize != Vector2.Zero) { - var childAnchorPosition = Vector2.Divide(OriginPosition, DrawSize); - foreach (var obj in NestedHitObjects) - obj.RelativeAnchorPosition = childAnchorPosition; - Ball.RelativeAnchorPosition = childAnchorPosition; + Vector2 pos = Vector2.Divide(OriginPosition, DrawSize); + + if (pos != childAnchorPosition) + { + childAnchorPosition = pos; + foreach (var obj in NestedHitObjects) + obj.RelativeAnchorPosition = pos; + Ball.RelativeAnchorPosition = pos; + } } } From 16ea7f9b7787462aa1506bac4ecb0c57090c630d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 Jan 2024 13:10:50 +0900 Subject: [PATCH 0855/1118] Avoid completely unnecessary string allocations in `ArgonCounterTextComponent` --- .../Screens/Play/HUD/ArgonCounterTextComponent.cs | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index 266dfb3301..9d364acd59 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; @@ -35,14 +34,7 @@ namespace osu.Game.Screens.Play.HUD public LocalisableString Text { get => textPart.Text; - set - { - int remainingCount = RequiredDisplayDigits.Value - value.ToString().Count(char.IsDigit); - string remainingText = remainingCount > 0 ? new string('#', remainingCount) : string.Empty; - - wireframesPart.Text = remainingText + value; - textPart.Text = value; - } + set => textPart.Text = value; } public ArgonCounterTextComponent(Anchor anchor, LocalisableString? label = null) @@ -83,6 +75,8 @@ namespace osu.Game.Screens.Play.HUD } } }; + + RequiredDisplayDigits.BindValueChanged(digits => wireframesPart.Text = new string('#', digits.NewValue)); } private string textLookup(char c) From dc31c66f6229445dd0ebd0d6a6a52c7241da59f0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 Jan 2024 19:49:42 +0900 Subject: [PATCH 0856/1118] Return null on font lookup failure instead of asserting Fallback weirdness. --- osu.Game/Skinning/LegacySpriteText.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs index 81db5fdf36..581e7534e4 100644 --- a/osu.Game/Skinning/LegacySpriteText.cs +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Graphics.Sprites; @@ -74,7 +73,8 @@ namespace osu.Game.Skinning public ITexturedCharacterGlyph? Get(string? fontName, char character) { // We only service one font. - Debug.Assert(fontName == this.fontName); + if (fontName != this.fontName) + return null; if (cache.TryGetValue(character, out var cached)) return cached; From 962c8ba4acfc8920633f99c022824d4d58564ef9 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 7 Jan 2024 20:55:28 +0900 Subject: [PATCH 0857/1118] Reset child anchor position cache on hitobject position change --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index c5194025c1..6cc8b8e935 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -121,7 +121,11 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } }); - PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); + PositionBindable.BindValueChanged(_ => + { + Position = HitObject.StackedPosition; + childAnchorPosition = null; + }); StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); ScaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue)); From 7b663a27bd4d37334e428f1afc3179bc8f9da7d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 10:47:22 +0100 Subject: [PATCH 0858/1118] Fix score conversion incorrectly assuming zero combo score in certain cases --- .../StandardisedScoreMigrationTools.cs | 23 +++++++++++++------ 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 9cfb9ea957..24fe147593 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -311,13 +311,22 @@ namespace osu.Game.Database long maximumLegacyBonusScore = attributes.BonusScore; double legacyAccScore = maximumLegacyAccuracyScore * score.Accuracy; - // We can not separate the ComboScore from the BonusScore, so we keep the bonus in the ratio. - // Note that `maximumLegacyComboScore + maximumLegacyBonusScore` can actually be 0 - // when playing a beatmap with no bonus objects, with mods that have a 0.0x multiplier on stable (relax/autopilot). - // In such cases, just assume 0. - double comboProportion = maximumLegacyComboScore + maximumLegacyBonusScore > 0 - ? Math.Max((double)score.LegacyTotalScore - legacyAccScore, 0) / (maximumLegacyComboScore + maximumLegacyBonusScore) - : 0; + + double comboProportion; + + if (maximumLegacyComboScore + maximumLegacyBonusScore > 0) + { + // We can not separate the ComboScore from the BonusScore, so we keep the bonus in the ratio. + comboProportion = Math.Max((double)score.LegacyTotalScore - legacyAccScore, 0) / (maximumLegacyComboScore + maximumLegacyBonusScore); + } + else + { + // Two possible causes: + // the beatmap has no bonus objects *AND* + // either the active mods have a zero mod multiplier, in which case assume 0, + // or the *beatmap* has a zero `difficultyPeppyStars` (or just no combo-giving objects), in which case assume 1. + comboProportion = legacyModMultiplier == 0 ? 0 : 1; + } // We assume the bonus proportion only makes up the rest of the score that exceeds maximumLegacyBaseScore. long maximumLegacyBaseScore = maximumLegacyAccuracyScore + maximumLegacyComboScore; From 50eba9ebdb92790c10fda1904219525029abe0a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 12:52:14 +0100 Subject: [PATCH 0859/1118] Reduce code duplication in test --- .../TestSceneIntroMusicActionHandling.cs | 32 +++++++------------ osu.Game/Tests/Visual/OsuGameTestScene.cs | 8 +++-- 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs index cc2b16a842..00c14dc797 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs @@ -9,39 +9,31 @@ using osu.Game.Screens.Menu; namespace osu.Game.Tests.Visual.Menus { - public partial class TestSceneIntroMusicActionHandling : OsuTestScene + public partial class TestSceneIntroMusicActionHandling : OsuGameTestScene { - private OsuGameTestScene.TestOsuGame? game; + private GlobalActionContainer globalActionContainer => Game.ChildrenOfType().First(); - private GlobalActionContainer globalActionContainer => game.ChildrenOfType().First(); + public override void SetUpSteps() + { + CreateNewGame(); + // we do not want to progress to main menu immediately, hence the override and lack of `ConfirmAtMainMenu()` call here. + } [Test] public void TestPauseDuringIntro() { - AddStep("Create new game instance", () => - { - if (game?.Parent != null) - Remove(game, true); - - RecycleLocalStorage(false); - - AddGame(game = new OsuGameTestScene.TestOsuGame(LocalStorage, API)); - }); - - AddUntilStep("Wait for load", () => game?.IsLoaded ?? false); - AddUntilStep("Wait for intro", () => game?.ScreenStack.CurrentScreen is IntroScreen); - AddUntilStep("Wait for music", () => game?.MusicController.IsPlaying == true); + AddUntilStep("Wait for music", () => Game?.MusicController.IsPlaying == true); // Check that pause dosesn't work during intro sequence. AddStep("Toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay)); - AddAssert("Still playing before menu", () => game?.MusicController.IsPlaying == true); - AddUntilStep("Wait for main menu", () => game?.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded); + AddAssert("Still playing before menu", () => Game?.MusicController.IsPlaying == true); + AddUntilStep("Wait for main menu", () => Game?.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded); // Check that toggling after intro still works. AddStep("Toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay)); - AddUntilStep("Music paused", () => game?.MusicController.IsPlaying == false && game?.MusicController.UserPauseRequested == true); + AddUntilStep("Music paused", () => Game?.MusicController.IsPlaying == false && Game?.MusicController.UserPauseRequested == true); AddStep("Toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay)); - AddUntilStep("Music resumed", () => game?.MusicController.IsPlaying == true && game?.MusicController.UserPauseRequested == false); + AddUntilStep("Music resumed", () => Game?.MusicController.IsPlaying == true && Game?.MusicController.UserPauseRequested == false); } } } diff --git a/osu.Game/Tests/Visual/OsuGameTestScene.cs b/osu.Game/Tests/Visual/OsuGameTestScene.cs index 94be4a375d..947305439e 100644 --- a/osu.Game/Tests/Visual/OsuGameTestScene.cs +++ b/osu.Game/Tests/Visual/OsuGameTestScene.cs @@ -58,6 +58,12 @@ namespace osu.Game.Tests.Visual [SetUpSteps] public virtual void SetUpSteps() + { + CreateNewGame(); + ConfirmAtMainMenu(); + } + + protected void CreateNewGame() { AddStep("Create new game instance", () => { @@ -71,8 +77,6 @@ namespace osu.Game.Tests.Visual AddUntilStep("Wait for load", () => Game.IsLoaded); AddUntilStep("Wait for intro", () => Game.ScreenStack.CurrentScreen is IntroScreen); - - ConfirmAtMainMenu(); } [TearDownSteps] From b869be2f463b41781bfaefd2926c54925a33ac03 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 12:52:23 +0100 Subject: [PATCH 0860/1118] Fix typo --- .../Visual/Menus/TestSceneIntroMusicActionHandling.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs b/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs index 00c14dc797..01aeaff1db 100644 --- a/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs +++ b/osu.Game.Tests/Visual/Menus/TestSceneIntroMusicActionHandling.cs @@ -24,7 +24,7 @@ namespace osu.Game.Tests.Visual.Menus { AddUntilStep("Wait for music", () => Game?.MusicController.IsPlaying == true); - // Check that pause dosesn't work during intro sequence. + // Check that pause doesn't work during intro sequence. AddStep("Toggle playback", () => globalActionContainer.TriggerPressed(GlobalAction.MusicPlay)); AddAssert("Still playing before menu", () => Game?.MusicController.IsPlaying == true); AddUntilStep("Wait for main menu", () => Game?.ScreenStack.CurrentScreen is MainMenu menu && menu.IsLoaded); From b6ce57b777983af3808b9736d86d1aba4b6faf80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 12:54:16 +0100 Subject: [PATCH 0861/1118] Use override that was intended to steer global track control rather than local sets --- osu.Game/Screens/Menu/IntroScreen.cs | 4 ++-- osu.Game/Screens/Menu/MainMenu.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Menu/IntroScreen.cs b/osu.Game/Screens/Menu/IntroScreen.cs index a81e24ee88..ac7dffc241 100644 --- a/osu.Game/Screens/Menu/IntroScreen.cs +++ b/osu.Game/Screens/Menu/IntroScreen.cs @@ -95,6 +95,8 @@ namespace osu.Game.Screens.Menu Colour = Color4.Black }; + public override bool? AllowGlobalTrackControl => false; + protected IntroScreen([CanBeNull] Func createNextScreen = null) { this.createNextScreen = createNextScreen; @@ -109,8 +111,6 @@ namespace osu.Game.Screens.Menu // prevent user from changing beatmap while the intro is still running. beatmap = Beatmap.BeginLease(false); - musicController.AllowTrackControl.Value = false; - MenuVoice = config.GetBindable(OsuSetting.MenuVoice); MenuMusic = config.GetBindable(OsuSetting.MenuMusic); diff --git a/osu.Game/Screens/Menu/MainMenu.cs b/osu.Game/Screens/Menu/MainMenu.cs index ffb68367c2..b264341cc5 100644 --- a/osu.Game/Screens/Menu/MainMenu.cs +++ b/osu.Game/Screens/Menu/MainMenu.cs @@ -49,6 +49,8 @@ namespace osu.Game.Screens.Menu public override bool AllowExternalScreenChange => true; + public override bool? AllowGlobalTrackControl => true; + private Screen songSelect; private MenuSideFlashes sideFlashes; @@ -241,8 +243,6 @@ namespace osu.Game.Screens.Menu { var track = musicController.CurrentTrack; - musicController.AllowTrackControl.Value = true; - // presume the track is the current beatmap's track. not sure how correct this assumption is but it has worked until now. if (!track.IsRunning) { From 8c82bb006cc5f842f943c513d70ab33d9d7234f7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 09:43:53 +0100 Subject: [PATCH 0862/1118] Fix mania score conversion using score V1 accuracy Partially addresses https://github.com/ppy/osu/discussions/26416 As pointed out in the discussion thread above, the total score conversion process for mania was using accuracy directly from the replay. In mania accuracy is calculated differently in score V1 than in score V2, which meant that scores coming from stable were treated more favourably (due to weighting GREAT and PERFECT equally). To fix, recompute accuracy locally and use that for the accuracy portion. Note that this will still not be (and cannot be made) 100% accurate, as in stable score V2, as well as in lazer, hold notes are *two* judgements, not one as in stable score V1, meaning that full and correct score statistics are not available without playing back the replay. The effects of the change can be previewed on the following spreadsheet: https://docs.google.com/spreadsheets/d/1wxD4UwLjwcr7n9y5Yq7EN0lgiLBN93kpd4gBnAlG-E0/edit#gid=1711190356 Top 5 changed scores with replays: | score | master | this PR | replay | | :------------------------------------------------------------------------------------------------------------------------------- | ------: | ------: | ------: | | [Outlasted on Uwa!! So Holiday by toby fox [[4K] easy] (0.71\*)](https://osu.ppy.sh/scores/mania/460404716) | 935,917 | 927,269 | 920,579 | | [ag0 on Emotional Uplifting Orchestral by bradbreeck [[4K] Rocket's Normal] (0.76\*)](https://osu.ppy.sh/scores/mania/453133066) | 921,636 | 913,535 | 875,549 | | [rlarkgus on Zen Zen Zense by Gom (HoneyWorks) [[5K] Normal] (1.68\*)](https://osu.ppy.sh/scores/mania/458368312) | 934,340 | 926,787 | 918,855 | | [YuJJun on Harumachi Clover by R3 Music Box [4K Catastrophe] (1.80\*)](https://osu.ppy.sh/scores/mania/548215786) | 918,606 | 911,111 | 885,454 | | [Fritte on 45-byou by respon feat. Hatsune Miku & Megpoid [[5K] Normal] (1.52\*)](https://osu.ppy.sh/scores/mania/516079410) | 900,024 | 892,569 | 907,456 | --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 7 ++++++- osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 3 ++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index 9cfb9ea957..1ef7355027 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -444,9 +444,14 @@ namespace osu.Game.Database break; case 3: + // in the mania case accuracy actually changes between score V1 and score V2 / standardised + // (PERFECT weighting changes from 300 to 305), + // so for better accuracy recompute accuracy locally based on hit statistics and use that instead, + double scoreV2Accuracy = ComputeAccuracy(score); + convertedTotalScore = (long)Math.Round(( 850000 * comboProportion - + 150000 * Math.Pow(score.Accuracy, 2 + 2 * score.Accuracy) + + 150000 * Math.Pow(scoreV2Accuracy, 2 + 2 * scoreV2Accuracy) + bonusProportion) * modMultiplier); break; diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index cf0a7bd54f..95f2ee0552 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -36,9 +36,10 @@ namespace osu.Game.Scoring.Legacy /// 30000007: Adjust osu!mania combo and accuracy portions and judgement scoring values. Reconvert all scores. /// 30000008: Add accuracy conversion. Reconvert all scores. /// 30000009: Fix edge cases in conversion for scores which have 0.0x mod multiplier on stable. Reconvert all scores. + /// 30000010: Fix mania score V1 conversion using score V1 accuracy rather than V2 accuracy. Reconvert all scores. /// /// - public const int LATEST_VERSION = 30000009; + public const int LATEST_VERSION = 30000010; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From e77d203a24088174a47810a70112e2a4cda46fc7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 01:08:17 +0900 Subject: [PATCH 0863/1118] Refactor delayed load logic to hopefully read better --- .../Carousel/DrawableCarouselBeatmapSet.cs | 43 +++++++++++-------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 61658526db..b70278c9bb 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -199,27 +199,34 @@ namespace osu.Game.Screens.Select.Carousel Debug.Assert(Item != null); - if (loadCancellation == null && (timeSinceUnpool += Time.Elapsed) > timeUpdatingBeforeLoad) - { - loadCancellation = new CancellationTokenSource(); + // A load is already in progress if the cancellation token is non-null. + if (loadCancellation != null) + return; - LoadComponentsAsync(new CompositeDrawable[] + timeSinceUnpool += Time.Elapsed; + + // We only trigger a load after this set has been in an updating state for a set amount of time. + if (timeSinceUnpool <= timeUpdatingBeforeLoad) + return; + + loadCancellation = new CancellationTokenSource(); + + LoadComponentsAsync(new CompositeDrawable[] + { + new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) { - new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) - { - RelativeSizeAxes = Axes.Both, - }, - new SetPanelContent((CarouselBeatmapSet)Item) - { - Depth = float.MinValue, - RelativeSizeAxes = Axes.Both, - } - }, drawables => + RelativeSizeAxes = Axes.Both, + }, + new SetPanelContent((CarouselBeatmapSet)Item) { - Header.AddRange(drawables); - drawables.ForEach(d => d.FadeInFromZero(150)); - }, loadCancellation.Token); - } + Depth = float.MinValue, + RelativeSizeAxes = Axes.Both, + } + }, drawables => + { + Header.AddRange(drawables); + drawables.ForEach(d => d.FadeInFromZero(150)); + }, loadCancellation.Token); } private void updateBeatmapYPositions() From 51bd32bf7e94e74967d6852ead980b71bed6a8e6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 01:08:47 +0900 Subject: [PATCH 0864/1118] Restore comment regarding usage of `MinBy` --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index b70278c9bb..369db37e63 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -213,6 +213,7 @@ namespace osu.Game.Screens.Select.Carousel LoadComponentsAsync(new CompositeDrawable[] { + // Choice of background image matches BSS implementation (always uses the lowest `beatmap_id` from the set). new SetPanelBackground(manager.GetWorkingBeatmap(beatmapSet.Beatmaps.MinBy(b => b.OnlineID))) { RelativeSizeAxes = Axes.Both, From c4ac53002c04567f29b9bd0efe9a7ac16e72a4e0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 19:49:22 +0100 Subject: [PATCH 0865/1118] Remove loop in combo score loss estimation calculation --- osu.Game/Database/StandardisedScoreMigrationTools.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/osu.Game/Database/StandardisedScoreMigrationTools.cs b/osu.Game/Database/StandardisedScoreMigrationTools.cs index f029d85aed..07b4fe7f40 100644 --- a/osu.Game/Database/StandardisedScoreMigrationTools.cs +++ b/osu.Game/Database/StandardisedScoreMigrationTools.cs @@ -519,13 +519,13 @@ namespace osu.Game.Database if (assumedLengthOfRemainingCombos > 0) { - while (remainingCombo > 0) - { - int comboLength = Math.Min(assumedLengthOfRemainingCombos, remainingCombo); + int assumedCombosCount = (int)Math.Floor((double)remainingCombo / assumedLengthOfRemainingCombos); + totalDroppedScore += assumedCombosCount * estimateDroppedComboScoreAfterMiss(assumedLengthOfRemainingCombos); - remainingCombo -= comboLength; - totalDroppedScore += estimateDroppedComboScoreAfterMiss(comboLength); - } + remainingCombo -= assumedCombosCount * assumedLengthOfRemainingCombos; + + if (remainingCombo > 0) + totalDroppedScore += estimateDroppedComboScoreAfterMiss(remainingCombo); } else { From 8a87301c55262f888017cb2ce5ed23d04429ab05 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 21:33:25 +0100 Subject: [PATCH 0866/1118] Add test for crashing scenario --- .../Navigation/TestSceneScreenNavigation.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index a0069f55c7..8cb993eff2 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -938,6 +938,35 @@ namespace osu.Game.Tests.Visual.Navigation AddUntilStep("touch device mod still active", () => Game.SelectedMods.Value, () => Has.One.InstanceOf()); } + [Test] + public void TestExitSongSelectAndImmediatelyClickLogo() + { + Screens.Select.SongSelect songSelect = null; + PushAndConfirm(() => songSelect = new TestPlaySongSelect()); + AddUntilStep("wait for song select", () => songSelect.BeatmapSetsLoaded); + + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + + AddUntilStep("wait for selected", () => !Game.Beatmap.IsDefault); + + AddStep("press escape and then click logo immediately", () => + { + InputManager.Key(Key.Escape); + clickLogoWhenNotCurrent(); + }); + + void clickLogoWhenNotCurrent() + { + if (songSelect.IsCurrentScreen()) + Scheduler.AddOnce(clickLogoWhenNotCurrent); + else + { + InputManager.MoveMouseTo(Game.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + } + } + } + private Func playToResults() { var player = playToCompletion(); From 58db39ec3206a34e1b8a0cbdbe196059dd4b8306 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 21:37:25 +0100 Subject: [PATCH 0867/1118] Fix crash when clicking osu! logo in song select immediately after exiting Closes https://github.com/ppy/osu/issues/26415. The crash report with incomplete log was backwards, the exit comes first. Sentry events and the reproducing test in 8a87301c55262f888017cb2ce5ed23d04429ab05 confirm this. --- osu.Game/Screens/Select/PlaySongSelect.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 4951504ff5..3cf8de5267 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -92,6 +92,9 @@ namespace osu.Game.Screens.Select { if (playerLoader != null) return false; + if (!this.IsCurrentScreen()) + return false; + modsAtGameplayStart = Mods.Value; // Ctrl+Enter should start map with autoplay enabled. From 67df7b33fb6c355ad41382fae8323c5c9e3d0c86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 21:51:01 +0100 Subject: [PATCH 0868/1118] Add failing test coverage for not attempting to upgrade custom ruleset scores --- .../BackgroundDataStoreProcessorTests.cs | 30 +++++++++++++++++++ osu.Game/BackgroundDataStoreProcessor.cs | 4 ++- 2 files changed, 33 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs index 43ce7200d2..9dbfde7bce 100644 --- a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs +++ b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -182,9 +183,38 @@ namespace osu.Game.Tests.Database AddAssert("Score version not upgraded", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreVersion), () => Is.EqualTo(30000002)); } + [Test] + public void TestCustomRulesetScoreNotSubjectToUpgrades([Values] bool available) + { + RulesetInfo rulesetInfo = null!; + ScoreInfo scoreInfo = null!; + TestBackgroundDataStoreProcessor processor = null!; + + AddStep("Add unavailable ruleset", () => Realm.Write(r => r.Add(rulesetInfo = new RulesetInfo + { + ShortName = Guid.NewGuid().ToString(), + Available = available + }))); + + AddStep("Add score for unavailable ruleset", () => Realm.Write(r => r.Add(scoreInfo = new ScoreInfo( + ruleset: rulesetInfo, + beatmap: r.All().First()) + { + TotalScoreVersion = 30000001 + }))); + + AddStep("Run background processor", () => Add(processor = new TestBackgroundDataStoreProcessor())); + AddUntilStep("Wait for completion", () => processor.Completed); + + AddAssert("Score not marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.BackgroundReprocessingFailed), () => Is.False); + AddAssert("Score version not upgraded", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreVersion), () => Is.EqualTo(30000001)); + } + public partial class TestBackgroundDataStoreProcessor : BackgroundDataStoreProcessor { protected override int TimeToSleepDuringGameplay => 10; + + public bool Completed => ProcessingTask.IsCompleted; } } } diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index a748a7422a..969062be57 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -28,6 +28,8 @@ namespace osu.Game /// public partial class BackgroundDataStoreProcessor : Component { + protected Task ProcessingTask = null!; + [Resolved] private RulesetStore rulesetStore { get; set; } = null!; @@ -61,7 +63,7 @@ namespace osu.Game { base.LoadComplete(); - Task.Factory.StartNew(() => + ProcessingTask = Task.Factory.StartNew(() => { Logger.Log("Beginning background data store processing.."); From 388f6599e0264d640d4da1fda0e27441212ca389 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 21:54:10 +0100 Subject: [PATCH 0869/1118] Add failing test for not attempting to upgrade non-legacy scores This was a source of confusion for users previously, wondering why their non-legacy (non-stable) scores weren't being converted in line with new scoring changes, when it was never actually our intention to support anything of the sort. --- .../BackgroundDataStoreProcessorTests.cs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs index 9dbfde7bce..8b066f860f 100644 --- a/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs +++ b/osu.Game.Tests/Database/BackgroundDataStoreProcessorTests.cs @@ -210,6 +210,31 @@ namespace osu.Game.Tests.Database AddAssert("Score version not upgraded", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreVersion), () => Is.EqualTo(30000001)); } + [Test] + public void TestNonLegacyScoreNotSubjectToUpgrades() + { + ScoreInfo scoreInfo = null!; + TestBackgroundDataStoreProcessor processor = null!; + + AddStep("Add score which requires upgrade (and has beatmap)", () => + { + Realm.Write(r => + { + r.Add(scoreInfo = new ScoreInfo(ruleset: r.All().First(), beatmap: r.All().First()) + { + TotalScoreVersion = 30000005, + LegacyTotalScore = 123456, + }); + }); + }); + + AddStep("Run background processor", () => Add(processor = new TestBackgroundDataStoreProcessor())); + AddUntilStep("Wait for completion", () => processor.Completed); + + AddAssert("Score not marked as failed", () => Realm.Run(r => r.Find(scoreInfo.ID)!.BackgroundReprocessingFailed), () => Is.False); + AddAssert("Score version not upgraded", () => Realm.Run(r => r.Find(scoreInfo.ID)!.TotalScoreVersion), () => Is.EqualTo(30000005)); + } + public partial class TestBackgroundDataStoreProcessor : BackgroundDataStoreProcessor { protected override int TimeToSleepDuringGameplay => 10; From aa83b84bb225781adccc15f4276a74901ed03397 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 22:34:41 +0100 Subject: [PATCH 0870/1118] Fix Cinema mod being compatible with mods that can force failure Addresses https://github.com/ppy/osu/pull/26080#issuecomment-1868833214. --- osu.Game/Rulesets/Mods/ModCinema.cs | 2 +- osu.Game/Rulesets/Mods/ModFailCondition.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Rulesets/Mods/ModCinema.cs b/osu.Game/Rulesets/Mods/ModCinema.cs index dbb37e0af6..7c88a8a588 100644 --- a/osu.Game/Rulesets/Mods/ModCinema.cs +++ b/osu.Game/Rulesets/Mods/ModCinema.cs @@ -30,7 +30,7 @@ namespace osu.Game.Rulesets.Mods public override IconUsage? Icon => OsuIcon.ModCinema; public override LocalisableString Description => "Watch the video without visual distractions."; - public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ModAutoplay), typeof(ModNoFail) }).ToArray(); + public override Type[] IncompatibleMods => base.IncompatibleMods.Concat(new[] { typeof(ModAutoplay), typeof(ModNoFail), typeof(ModFailCondition) }).ToArray(); public void ApplyToHUD(HUDOverlay overlay) { diff --git a/osu.Game/Rulesets/Mods/ModFailCondition.cs b/osu.Game/Rulesets/Mods/ModFailCondition.cs index 471c3bfe8d..0b229766c1 100644 --- a/osu.Game/Rulesets/Mods/ModFailCondition.cs +++ b/osu.Game/Rulesets/Mods/ModFailCondition.cs @@ -11,7 +11,7 @@ namespace osu.Game.Rulesets.Mods { public abstract class ModFailCondition : Mod, IApplicableToHealthProcessor, IApplicableFailOverride { - public override Type[] IncompatibleMods => new[] { typeof(ModNoFail) }; + public override Type[] IncompatibleMods => new[] { typeof(ModNoFail), typeof(ModCinema) }; [SettingSource("Restart on fail", "Automatically restarts when failed.")] public BindableBool Restart { get; } = new BindableBool(); From 4f7dcb3a5022cb533cc467d974a7f382d1cf285e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 8 Jan 2024 22:07:33 +0100 Subject: [PATCH 0871/1118] Do not attempt to recalculate non-legacy scores or scores set on custom rulesets Addresses discussions such as https://github.com/ppy/osu/discussions/26407 or https://github.com/ppy/osu/discussions/25914 wherein: - the game would attempt to convert scores for custom rulesets, which makes no sense, especially so when they're not there, - the game would also "recalculate" lazer scores, but that was never the intention or was never supported; the game would just increment the score version on those but still include them in the converted tally. --- osu.Game/BackgroundDataStoreProcessor.cs | 18 +++++++++++++----- 1 file changed, 13 insertions(+), 5 deletions(-) diff --git a/osu.Game/BackgroundDataStoreProcessor.cs b/osu.Game/BackgroundDataStoreProcessor.cs index 969062be57..fc7db13d41 100644 --- a/osu.Game/BackgroundDataStoreProcessor.cs +++ b/osu.Game/BackgroundDataStoreProcessor.cs @@ -13,6 +13,7 @@ using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Game.Beatmaps; using osu.Game.Database; +using osu.Game.Extensions; using osu.Game.Online.API; using osu.Game.Overlays; using osu.Game.Overlays.Notifications; @@ -28,7 +29,7 @@ namespace osu.Game /// public partial class BackgroundDataStoreProcessor : Component { - protected Task ProcessingTask = null!; + protected Task ProcessingTask { get; private set; } = null!; [Resolved] private RulesetStore rulesetStore { get; set; } = null!; @@ -316,10 +317,17 @@ namespace osu.Game { Logger.Log("Querying for scores that need total score conversion..."); - HashSet scoreIds = realmAccess.Run(r => new HashSet(r.All() - .Where(s => !s.BackgroundReprocessingFailed && s.BeatmapInfo != null - && s.TotalScoreVersion < LegacyScoreEncoder.LATEST_VERSION) - .AsEnumerable().Select(s => s.ID))); + HashSet scoreIds = realmAccess.Run(r => new HashSet( + r.All() + .Where(s => !s.BackgroundReprocessingFailed + && s.BeatmapInfo != null + && s.IsLegacyScore + && s.TotalScoreVersion < LegacyScoreEncoder.LATEST_VERSION) + .AsEnumerable() + // must be done after materialisation, as realm doesn't want to support + // nested property predicates + .Where(s => s.Ruleset.IsLegacyRuleset()) + .Select(s => s.ID))); Logger.Log($"Found {scoreIds.Count} scores which require total score conversion."); From 58619f168458ead922914536a651951595f33875 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 8 Jan 2024 14:16:05 -0800 Subject: [PATCH 0872/1118] Fix wiki main page not displaying custom layout --- osu.Game/Overlays/WikiOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 440e451201..a8d9cdcdb2 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -137,7 +137,7 @@ namespace osu.Game.Overlays wikiData.Value = response; path.Value = response.Path; - if (response.Layout == index_path) + if (response.Layout == index_path.ToLowerInvariant()) { LoadDisplay(new WikiMainPage { From d6ba7a9c6eeab7c2c4889249df4f7280f28515d2 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 8 Jan 2024 14:24:56 -0800 Subject: [PATCH 0873/1118] Centralise `INDEX_PATH` to `WikiOverlay` --- osu.Game/Overlays/Wiki/WikiHeader.cs | 4 +--- osu.Game/Overlays/WikiOverlay.cs | 10 +++++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/osu.Game/Overlays/Wiki/WikiHeader.cs b/osu.Game/Overlays/Wiki/WikiHeader.cs index 55be05ed7a..d64d6b934a 100644 --- a/osu.Game/Overlays/Wiki/WikiHeader.cs +++ b/osu.Game/Overlays/Wiki/WikiHeader.cs @@ -17,8 +17,6 @@ namespace osu.Game.Overlays.Wiki { public partial class WikiHeader : BreadcrumbControlOverlayHeader { - private const string index_path = "Main_page"; - public static LocalisableString IndexPageString => LayoutStrings.HeaderHelpIndex; public readonly Bindable WikiPageData = new Bindable(); @@ -45,7 +43,7 @@ namespace osu.Game.Overlays.Wiki TabControl.AddItem(IndexPageString); - if (e.NewValue.Path == index_path) + if (e.NewValue.Path == WikiOverlay.INDEX_PATH) { Current.Value = IndexPageString; return; diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index a8d9cdcdb2..3777e83cde 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -19,11 +19,11 @@ namespace osu.Game.Overlays { public partial class WikiOverlay : OnlineOverlay { - private const string index_path = "Main_page"; + public const string INDEX_PATH = @"Main_page"; public string CurrentPath => path.Value; - private readonly Bindable path = new Bindable(index_path); + private readonly Bindable path = new Bindable(INDEX_PATH); private readonly Bindable wikiData = new Bindable(); @@ -43,7 +43,7 @@ namespace osu.Game.Overlays { } - public void ShowPage(string pagePath = index_path) + public void ShowPage(string pagePath = INDEX_PATH) { path.Value = pagePath.Trim('/'); Show(); @@ -137,7 +137,7 @@ namespace osu.Game.Overlays wikiData.Value = response; path.Value = response.Path; - if (response.Layout == index_path.ToLowerInvariant()) + if (response.Layout == INDEX_PATH.ToLowerInvariant()) { LoadDisplay(new WikiMainPage { @@ -161,7 +161,7 @@ namespace osu.Game.Overlays path.Value = "error"; LoadDisplay(articlePage = new WikiArticlePage($@"{api.WebsiteRootUrl}/wiki/", - $"Something went wrong when trying to fetch page \"{originalPath}\".\n\n[Return to the main page](Main_page).")); + $"Something went wrong when trying to fetch page \"{originalPath}\".\n\n[Return to the main page]({INDEX_PATH}).")); } private void showParentPage() From b03813d3b40e0a279bb8ef6800cd587162adf0f2 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Mon, 8 Jan 2024 14:36:08 -0800 Subject: [PATCH 0874/1118] Update casing of hardcoded "Main_page" string in tests --- osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs | 4 ++-- osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs | 2 +- .../Visual/Online/TestSceneWikiMarkdownContainer.cs | 4 ++-- osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs | 6 +++--- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs index 4e71c5977e..40eda3f3dc 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -24,8 +24,8 @@ namespace osu.Game.Tests.Visual.Online [Cached] private readonly Bindable wikiPageData = new Bindable(new APIWikiPage { - Title = "Main Page", - Path = "Main_Page", + Title = "Main page", + Path = "Main_page", }); private TestHeader header; diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs index 9967be73e8..7b4eadd46d 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMainPage.cs @@ -36,7 +36,7 @@ namespace osu.Game.Tests.Visual.Online }; } - // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page + // From https://osu.ppy.sh/api/v2/wiki/en/Main_page private const string main_page_markdown = "---\nlayout: main_page\n---\n\n\n\n
\nWelcome to the osu! wiki, a project containing a wide range of osu! related information.\n
\n\n
\n
\n\n# Getting started\n\n[Welcome](/wiki/Welcome) • [Installation](/wiki/Installation) • [Registration](/wiki/Registration) • [Help Centre](/wiki/Help_Centre) • [FAQ](/wiki/FAQ)\n\n
\n
\n\n# Game client\n\n[Interface](/wiki/Interface) • [Options](/wiki/Options) • [Visual settings](/wiki/Visual_Settings) • [Shortcut key reference](/wiki/Shortcut_key_reference) • [Configuration file](/wiki/osu!_Program_Files/User_Configuration_File) • [Program files](/wiki/osu!_Program_Files)\n\n[File formats](/wiki/osu!_File_Formats): [.osz](/wiki/osu!_File_Formats/Osz_(file_format)) • [.osk](/wiki/osu!_File_Formats/Osk_(file_format)) • [.osr](/wiki/osu!_File_Formats/Osr_(file_format)) • [.osu](/wiki/osu!_File_Formats/Osu_(file_format)) • [.osb](/wiki/osu!_File_Formats/Osb_(file_format)) • [.db](/wiki/osu!_File_Formats/Db_(file_format))\n\n
\n
\n\n# Gameplay\n\n[Game modes](/wiki/Game_mode): [osu!](/wiki/Game_mode/osu!) • [osu!taiko](/wiki/Game_mode/osu!taiko) • [osu!catch](/wiki/Game_mode/osu!catch) • [osu!mania](/wiki/Game_mode/osu!mania)\n\n[Beatmap](/wiki/Beatmap) • [Hit object](/wiki/Hit_object) • [Mods](/wiki/Game_modifier) • [Score](/wiki/Score) • [Replay](/wiki/Replay) • [Multi](/wiki/Multi)\n\n
\n
\n\n# [Beatmap editor](/wiki/Beatmap_Editor)\n\nSections: [Compose](/wiki/Beatmap_Editor/Compose) • [Design](/wiki/Beatmap_Editor/Design) • [Timing](/wiki/Beatmap_Editor/Timing) • [Song setup](/wiki/Beatmap_Editor/Song_Setup)\n\nComponents: [AiMod](/wiki/Beatmap_Editor/AiMod) • [Beat snap divisor](/wiki/Beatmap_Editor/Beat_Snap_Divisor) • [Distance snap](/wiki/Beatmap_Editor/Distance_Snap) • [Menu](/wiki/Beatmap_Editor/Menu) • [SB load](/wiki/Beatmap_Editor/SB_Load) • [Timelines](/wiki/Beatmap_Editor/Timelines)\n\n[Beatmapping](/wiki/Beatmapping) • [Difficulty](/wiki/Beatmap/Difficulty) • [Mapping techniques](/wiki/Mapping_Techniques) • [Storyboarding](/wiki/Storyboarding)\n\n
\n
\n\n# Beatmap submission and ranking\n\n[Submission](/wiki/Submission) • [Modding](/wiki/Modding) • [Ranking procedure](/wiki/Beatmap_ranking_procedure) • [Mappers' Guild](/wiki/Mappers_Guild) • [Project Loved](/wiki/Project_Loved)\n\n[Ranking criteria](/wiki/Ranking_Criteria): [osu!](/wiki/Ranking_Criteria/osu!) • [osu!taiko](/wiki/Ranking_Criteria/osu!taiko) • [osu!catch](/wiki/Ranking_Criteria/osu!catch) • [osu!mania](/wiki/Ranking_Criteria/osu!mania)\n\n
\n
\n\n# Community\n\n[Tournaments](/wiki/Tournaments) • [Skinning](/wiki/Skinning) • [Projects](/wiki/Projects) • [Guides](/wiki/Guides) • [osu!dev Discord server](/wiki/osu!dev_Discord_server) • [How you can help](/wiki/How_You_Can_Help!) • [Glossary](/wiki/Glossary)\n\n
\n
\n\n# People\n\n[The Team](/wiki/People/The_Team): [Developers](/wiki/People/The_Team/Developers) • [Global Moderation Team](/wiki/People/The_Team/Global_Moderation_Team) • [Support Team](/wiki/People/The_Team/Support_Team) • [Nomination Assessment Team](/wiki/People/The_Team/Nomination_Assessment_Team) • [Beatmap Nominators](/wiki/People/The_Team/Beatmap_Nominators) • [osu! Alumni](/wiki/People/The_Team/osu!_Alumni) • [Project Loved Team](/wiki/People/The_Team/Project_Loved_Team)\n\nOrganisations: [osu! UCI](/wiki/Organisations/osu!_UCI)\n\n[Community Contributors](/wiki/People/Community_Contributors) • [Users with unique titles](/wiki/People/Users_with_unique_titles)\n\n
\n
\n\n# For developers\n\n[API](/wiki/osu!api) • [Bot account](/wiki/Bot_account) • [Brand identity guidelines](/wiki/Brand_identity_guidelines)\n\n
\n
\n\n# About the wiki\n\n[Sitemap](/wiki/Sitemap) • [Contribution guide](/wiki/osu!_wiki_Contribution_Guide) • [Article styling criteria](/wiki/Article_Styling_Criteria) • [News styling criteria](/wiki/News_Styling_Criteria)\n\n
\n
\n"; } diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs index 6c87553971..8909305602 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiMarkdownContainer.cs @@ -69,8 +69,8 @@ namespace osu.Game.Tests.Visual.Online { AddStep("set current path", () => markdownContainer.CurrentPath = $"{API.WebsiteRootUrl}/wiki/Article_styling_criteria/"); - AddStep("set '/wiki/Main_Page''", () => markdownContainer.Text = "[wiki main page](/wiki/Main_Page)"); - AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Main_Page"); + AddStep("set '/wiki/Main_page''", () => markdownContainer.Text = "[wiki main page](/wiki/Main_page)"); + AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/Main_page"); AddStep("set '../FAQ''", () => markdownContainer.Text = "[FAQ](../FAQ)"); AddAssert("check url", () => markdownContainer.Link.Url == $"{API.WebsiteRootUrl}/wiki/FAQ"); diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 79c7e3a22e..352f100f3b 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -107,12 +107,12 @@ namespace osu.Game.Tests.Visual.Online }; }); - // From https://osu.ppy.sh/api/v2/wiki/en/Main_Page + // From https://osu.ppy.sh/api/v2/wiki/en/Main_page private APIWikiPage responseMainPage => new APIWikiPage { - Title = "Main Page", + Title = "Main page", Layout = "main_page", - Path = "Main_Page", + Path = "Main_page", Locale = "en", Subtitle = null, Markdown = From 7d57a668aba2032be4c000561be60f17674c4732 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 13:12:54 +0900 Subject: [PATCH 0875/1118] Use main page constant in more places --- osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs | 2 +- osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs index 40eda3f3dc..d259322d4a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiHeader.cs @@ -25,7 +25,7 @@ namespace osu.Game.Tests.Visual.Online private readonly Bindable wikiPageData = new Bindable(new APIWikiPage { Title = "Main page", - Path = "Main_page", + Path = WikiOverlay.INDEX_PATH, }); private TestHeader header; diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 352f100f3b..8765d8485a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -11,6 +11,7 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; +using osu.Game.Overlays.Wiki; namespace osu.Game.Tests.Visual.Online { @@ -111,8 +112,8 @@ namespace osu.Game.Tests.Visual.Online private APIWikiPage responseMainPage => new APIWikiPage { Title = "Main page", - Layout = "main_page", - Path = "Main_page", + Layout = WikiOverlay.INDEX_PATH.ToLowerInvariant(), // custom classes are always lower snake. + Path = WikiOverlay.INDEX_PATH, Locale = "en", Subtitle = null, Markdown = From 172fe53099bf4e2e26e7acc9dc0dc7708b0ea39d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 13:13:32 +0900 Subject: [PATCH 0876/1118] Use better method of ignore case comparison --- osu.Game/Overlays/WikiOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/WikiOverlay.cs b/osu.Game/Overlays/WikiOverlay.cs index 3777e83cde..ffbc168fb7 100644 --- a/osu.Game/Overlays/WikiOverlay.cs +++ b/osu.Game/Overlays/WikiOverlay.cs @@ -137,7 +137,7 @@ namespace osu.Game.Overlays wikiData.Value = response; path.Value = response.Path; - if (response.Layout == INDEX_PATH.ToLowerInvariant()) + if (response.Layout.Equals(INDEX_PATH, StringComparison.OrdinalIgnoreCase)) { LoadDisplay(new WikiMainPage { From bb2b7d3c313ab8c2e78ca9a790de290f874c0b6d Mon Sep 17 00:00:00 2001 From: Nitrous Date: Tue, 9 Jan 2024 12:23:01 +0800 Subject: [PATCH 0877/1118] Add playback controls. --- .../Play/PlayerSettings/PlaybackSettings.cs | 117 ++++++++++++++++-- 1 file changed, 105 insertions(+), 12 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 4753effdb0..69bfe666ee 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -1,11 +1,17 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; +using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; +using osu.Game.Graphics.UserInterface; +using osuTK; namespace osu.Game.Screens.Play.PlayerSettings { @@ -24,34 +30,115 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly OsuSpriteText multiplierText; + private readonly IconButton play; + + [Resolved] + private GameplayClockContainer gameplayClock { get; set; } = null!; + + [Resolved] + private GameplayState gameplayState { get; set; } = null!; + public PlaybackSettings() : base("playback") { + const double seek_amount = 5000; + const double seek_fast_amount = 10000; + Children = new Drawable[] { - new Container + new FillFlowContainer { RelativeSizeAxes = Axes.X, AutoSizeAxes = Axes.Y, - Padding = new MarginPadding { Horizontal = padding }, + Direction = FillDirection.Vertical, + Spacing = new Vector2(0, padding), Children = new Drawable[] { - new OsuSpriteText + new FillFlowContainer { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Text = "Playback speed", + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5, 0), + Children = new Drawable[] + { + new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.FastBackward, + Action = () => seek(-1, seek_fast_amount), + }, + new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.Backward, + Action = () => seek(-1, seek_amount), + }, + play = new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Scale = new Vector2(1.4f), + IconScale = new Vector2(1.4f), + Icon = FontAwesome.Regular.PlayCircle, + Action = () => + { + if (gameplayClock != null) + { + if (gameplayClock.IsRunning) + gameplayClock.Stop(); + else + gameplayClock.Start(); + } + } + }, + new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.Forward, + Action = () => seek(1, seek_amount), + }, + new IconButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.FastForward, + Action = () => seek(1, seek_fast_amount), + }, + }, }, - multiplierText = new OsuSpriteText + new Container { - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - Font = OsuFont.GetFont(weight: FontWeight.Bold), - } + RelativeSizeAxes = Axes.X, + AutoSizeAxes = Axes.Y, + Children = new Drawable[] + { + rateSlider = new PlayerSliderBar + { + LabelText = "Playback speed", + Current = UserPlaybackRate, + }, + multiplierText = new OsuSpriteText + { + Anchor = Anchor.TopRight, + Origin = Anchor.TopRight, + Font = OsuFont.GetFont(weight: FontWeight.Bold), + Margin = new MarginPadding { Right = 20 }, + } + }, + }, }, }, - rateSlider = new PlayerSliderBar { Current = UserPlaybackRate } }; + + void seek(int direction, double amount) + { + double target = Math.Clamp((gameplayClock?.CurrentTime ?? 0) + (direction * amount), 0, gameplayState.Beatmap.GetLastObjectTime()); + gameplayClock?.Seek(target); + } } protected override void LoadComplete() @@ -59,5 +146,11 @@ namespace osu.Game.Screens.Play.PlayerSettings base.LoadComplete(); rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.0}x", true); } + + protected override void Update() + { + base.Update(); + play.Icon = gameplayClock.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle; + } } } From 2e041823a15b2720c90c0f2fbcdf8afcee8417df Mon Sep 17 00:00:00 2001 From: Nitrous Date: Tue, 9 Jan 2024 12:24:09 +0800 Subject: [PATCH 0878/1118] Perform null check on gameplay state. --- osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 69bfe666ee..3662b7ddc8 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -136,7 +136,7 @@ namespace osu.Game.Screens.Play.PlayerSettings void seek(int direction, double amount) { - double target = Math.Clamp((gameplayClock?.CurrentTime ?? 0) + (direction * amount), 0, gameplayState.Beatmap.GetLastObjectTime()); + double target = Math.Clamp((gameplayClock?.CurrentTime ?? 0) + (direction * amount), 0, gameplayState?.Beatmap.GetLastObjectTime() ?? 0); gameplayClock?.Seek(target); } } From afa808695bf9c6060f2f706c15172861e1c56269 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Tue, 9 Jan 2024 12:48:11 +0800 Subject: [PATCH 0879/1118] Make resolved properties nullable. --- osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 3662b7ddc8..2a8701d4d5 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -33,10 +33,10 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly IconButton play; [Resolved] - private GameplayClockContainer gameplayClock { get; set; } = null!; + private GameplayClockContainer? gameplayClock { get; set; } [Resolved] - private GameplayState gameplayState { get; set; } = null!; + private GameplayState? gameplayState { get; set; } public PlaybackSettings() : base("playback") @@ -150,7 +150,7 @@ namespace osu.Game.Screens.Play.PlayerSettings protected override void Update() { base.Update(); - play.Icon = gameplayClock.IsRunning ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle; + play.Icon = gameplayClock?.IsRunning == true ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle; } } } From 3f5899dae041e66d8292acc757e25556a2172926 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 14:05:59 +0900 Subject: [PATCH 0880/1118] Fix incorrect implementation of wireframe digits --- .../Play/HUD/ArgonCounterTextComponent.cs | 1 - .../Screens/Play/HUD/ArgonScoreCounter.cs | 36 +++++++++++++++++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index 9d364acd59..f88874f872 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Diagnostics; using System.Threading.Tasks; using osu.Framework.Allocation; using osu.Framework.Bindables; diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index 005f7e36a7..f7ca218767 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; @@ -15,6 +16,8 @@ namespace osu.Game.Screens.Play.HUD { public partial class ArgonScoreCounter : GameplayScoreCounter, ISerialisableDrawable { + private ArgonScoreTextComponent scoreText = null!; + protected override double RollingDuration => 500; protected override Easing RollingEasing => Easing.OutQuint; @@ -33,13 +36,42 @@ namespace osu.Game.Screens.Play.HUD protected override LocalisableString FormatCount(long count) => count.ToLocalisableString(); - protected override IHasText CreateText() => new ArgonScoreTextComponent(Anchor.TopRight, BeatmapsetsStrings.ShowScoreboardHeadersScore.ToUpper()) + protected override IHasText CreateText() => scoreText = new ArgonScoreTextComponent(Anchor.TopRight, BeatmapsetsStrings.ShowScoreboardHeadersScore.ToUpper()) { - RequiredDisplayDigits = { BindTarget = RequiredDisplayDigits }, WireframeOpacity = { BindTarget = WireframeOpacity }, ShowLabel = { BindTarget = ShowLabel }, }; + public ArgonScoreCounter() + { + RequiredDisplayDigits.BindValueChanged(_ => updateWireframe()); + } + + public override long DisplayedCount + { + get => base.DisplayedCount; + set + { + base.DisplayedCount = value; + updateWireframe(); + } + } + + private void updateWireframe() + { + scoreText.RequiredDisplayDigits.Value = + Math.Max(RequiredDisplayDigits.Value, getDigitsRequiredForDisplayCount()); + } + + private int getDigitsRequiredForDisplayCount() + { + int digitsRequired = 1; + long c = DisplayedCount; + while ((c /= 10) > 0) + digitsRequired++; + return digitsRequired; + } + private partial class ArgonScoreTextComponent : ArgonCounterTextComponent { public ArgonScoreTextComponent(Anchor anchor, LocalisableString? label = null) From 765d41faa9940e8ae5878babe326cc76f39ba930 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 14:06:59 +0900 Subject: [PATCH 0881/1118] Change second occurrence of debug.assert with early return for fallback safety --- osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index f88874f872..a11f2f01cd 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -159,7 +159,8 @@ namespace osu.Game.Screens.Play.HUD public ITexturedCharacterGlyph? Get(string? fontName, char character) { // We only service one font. - Debug.Assert(fontName == this.fontName); + if (fontName != this.fontName) + return null; if (cache.TryGetValue(character, out var cached)) return cached; From 5970a68e2d12f4f8fc3a118cf68da7a69c1f1fcf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 14:17:33 +0900 Subject: [PATCH 0882/1118] Use invalidation based logic for child anchor position updpates in `DrawableSlider` --- .../Objects/Drawables/DrawableSlider.cs | 25 ++++++++----------- 1 file changed, 10 insertions(+), 15 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 6cc8b8e935..4099d47d61 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -11,6 +11,7 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Layout; using osu.Game.Audio; using osu.Game.Graphics.Containers; using osu.Game.Rulesets.Objects; @@ -36,8 +37,6 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private ShakeContainer shakeContainer; - private Vector2? childAnchorPosition; - protected override IEnumerable DimmablePieces => new Drawable[] { HeadCircle, @@ -68,6 +67,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Container repeatContainer; private PausableSkinnableSound slidingSample; + private readonly LayoutValue drawSizeLayout; + public DrawableSlider() : this(null) { @@ -84,6 +85,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true, Alpha = 0 }; + AddLayout(drawSizeLayout = new LayoutValue(Invalidation.DrawSize | Invalidation.MiscGeometry)); } [BackgroundDependencyLoader] @@ -121,11 +123,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables } }); - PositionBindable.BindValueChanged(_ => - { - Position = HitObject.StackedPosition; - childAnchorPosition = null; - }); + PositionBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); StackHeightBindable.BindValueChanged(_ => Position = HitObject.StackedPosition); ScaleBindable.BindValueChanged(scale => Ball.Scale = new Vector2(scale.NewValue)); @@ -258,17 +256,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = SliderBody?.Size ?? Vector2.Zero; OriginPosition = SliderBody?.PathOffset ?? Vector2.Zero; - if (DrawSize != Vector2.Zero) + if (!drawSizeLayout.IsValid) { Vector2 pos = Vector2.Divide(OriginPosition, DrawSize); + foreach (var obj in NestedHitObjects) + obj.RelativeAnchorPosition = pos; + Ball.RelativeAnchorPosition = pos; - if (pos != childAnchorPosition) - { - childAnchorPosition = pos; - foreach (var obj in NestedHitObjects) - obj.RelativeAnchorPosition = pos; - Ball.RelativeAnchorPosition = pos; - } + drawSizeLayout.Validate(); } } From 1f6e1cbe56d5f0dce5c3fe60f779bdd66c5fe655 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 14:45:21 +0900 Subject: [PATCH 0883/1118] Allow interacting with playlist item buttons when not selected --- .../Screens/OnlinePlay/DrawableRoomPlaylist.cs | 6 +++++- .../OnlinePlay/DrawableRoomPlaylistItem.cs | 16 +++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs index 8abdec9ade..5a1648c91f 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylist.cs @@ -165,7 +165,11 @@ namespace osu.Game.Screens.OnlinePlay { d.SelectedItem.BindTarget = SelectedItem; d.RequestDeletion = i => RequestDeletion?.Invoke(i); - d.RequestResults = i => RequestResults?.Invoke(i); + d.RequestResults = i => + { + SelectedItem.Value = i; + RequestResults?.Invoke(i); + }; d.RequestEdit = i => RequestEdit?.Invoke(i); d.AllowReordering = AllowReordering; d.AllowDeletion = AllowDeletion; diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 8f405399a7..823f6ea308 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -118,8 +118,6 @@ namespace osu.Game.Screens.OnlinePlay [Resolved(CanBeNull = true)] private ManageCollectionsDialog manageCollectionsDialog { get; set; } - protected override bool ShouldBeConsideredForInput(Drawable child) => AllowReordering || AllowDeletion || !AllowSelection || SelectedItem.Value == Model; - public DrawableRoomPlaylistItem(PlaylistItem item) : base(item) { @@ -367,7 +365,7 @@ namespace osu.Game.Screens.OnlinePlay AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Left = 8, Right = 8 }, }, - mainFillFlow = new FillFlowContainer + mainFillFlow = new MainFlow(() => SelectedItem.Value == Model) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -670,5 +668,17 @@ namespace osu.Game.Screens.OnlinePlay public LocalisableString TooltipText => avatar.TooltipText; } } + + public partial class MainFlow : FillFlowContainer + { + private readonly Func isSelected; + + public override bool PropagatePositionalInputSubTree => isSelected(); + + public MainFlow(Func isSelected) + { + this.isSelected = isSelected; + } + } } } From 79ff767eba341cba9e4c81e3410eacf75eb4cd07 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 14:51:30 +0900 Subject: [PATCH 0884/1118] Remove unused using --- osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs index 8765d8485a..e70d35f74a 100644 --- a/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs +++ b/osu.Game.Tests/Visual/Online/TestSceneWikiOverlay.cs @@ -11,7 +11,6 @@ using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays; -using osu.Game.Overlays.Wiki; namespace osu.Game.Tests.Visual.Online { From 19d1fff5362b3652eb70c366f7ea361f89af4737 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 15:37:29 +0900 Subject: [PATCH 0885/1118] Use native query to avoid huge overheads when cleaning up realm files --- osu.Game/Database/RealmFileStore.cs | 9 +++------ osu.Game/Models/RealmFile.cs | 4 ++++ 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Database/RealmFileStore.cs b/osu.Game/Database/RealmFileStore.cs index 1da64d5be8..f1ed3f4b63 100644 --- a/osu.Game/Database/RealmFileStore.cs +++ b/osu.Game/Database/RealmFileStore.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Diagnostics; using System.IO; using System.Linq; using osu.Framework.Extensions; @@ -98,15 +99,11 @@ namespace osu.Game.Database // can potentially be run asynchronously, although we will need to consider operation order for disk deletion vs realm removal. realm.Write(r => { - // TODO: consider using a realm native query to avoid iterating all files (https://github.com/realm/realm-dotnet/issues/2659#issuecomment-927823707) - var files = r.All().ToList(); - - foreach (var file in files) + foreach (var file in r.All().Filter("Usages.@count = 0")) { totalFiles++; - if (file.BacklinksCount > 0) - continue; + Debug.Assert(file.BacklinksCount == 0); try { diff --git a/osu.Game/Models/RealmFile.cs b/osu.Game/Models/RealmFile.cs index 2faa3f0ca6..4d1642fb5f 100644 --- a/osu.Game/Models/RealmFile.cs +++ b/osu.Game/Models/RealmFile.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Game.IO; using Realms; @@ -11,5 +12,8 @@ namespace osu.Game.Models { [PrimaryKey] public string Hash { get; set; } = string.Empty; + + [Backlink(nameof(RealmNamedFileUsage.File))] + public IQueryable Usages { get; } = null!; } } From 99c76854956f1d6f3116a462fa6af4f0320500c6 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Tue, 9 Jan 2024 15:18:21 +0800 Subject: [PATCH 0886/1118] use `GameplayClock.IsPaused` bindable instead of polling in `Update` --- .../Screens/Play/PlayerSettings/PlaybackSettings.cs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 2a8701d4d5..a0fafb821c 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -32,6 +32,8 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly IconButton play; + private readonly BindableBool isPaused = new BindableBool(); + [Resolved] private GameplayClockContainer? gameplayClock { get; set; } @@ -134,6 +136,8 @@ namespace osu.Game.Screens.Play.PlayerSettings }, }; + isPaused.BindValueChanged(e => play.Icon = e.NewValue ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle, true); + void seek(int direction, double amount) { double target = Math.Clamp((gameplayClock?.CurrentTime ?? 0) + (direction * amount), 0, gameplayState?.Beatmap.GetLastObjectTime() ?? 0); @@ -145,12 +149,7 @@ namespace osu.Game.Screens.Play.PlayerSettings { base.LoadComplete(); rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.0}x", true); - } - - protected override void Update() - { - base.Update(); - play.Icon = gameplayClock?.IsRunning == true ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle; + gameplayClock?.IsPaused.BindTo(isPaused); } } } From bdecac6d797661836279482a031ee97d3aae955a Mon Sep 17 00:00:00 2001 From: Nitrous Date: Tue, 9 Jan 2024 15:19:54 +0800 Subject: [PATCH 0887/1118] Inverse check. --- osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index a0fafb821c..7974b07438 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -136,7 +136,7 @@ namespace osu.Game.Screens.Play.PlayerSettings }, }; - isPaused.BindValueChanged(e => play.Icon = e.NewValue ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle, true); + isPaused.BindValueChanged(e => play.Icon = !e.NewValue ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle, true); void seek(int direction, double amount) { From 1837b31f9b5e1d3a2b829fe97105a0abcc882e8b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 16:38:02 +0900 Subject: [PATCH 0888/1118] Remove usage of `HealthDisplay.BindValueChanged` Health updates very often when using HP drain. Let's avoid bindable overheads. --- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 29 ++++++++--- osu.Game/Skinning/LegacyHealthDisplay.cs | 56 ++++++++++------------ 2 files changed, 48 insertions(+), 37 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index 7747036826..a20121b20b 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -56,13 +56,6 @@ namespace osu.Game.Screens.Play.HUD // Don't bind directly so we can animate the startup procedure. health = HealthProcessor.Health.GetBoundCopy(); - health.BindValueChanged(h => - { - if (initialIncrease != null) - FinishInitialAnimation(h.OldValue); - - Current.Value = h.NewValue; - }); if (hudOverlay != null) showHealthBar.BindTo(hudOverlay.ShowHealthBar); @@ -76,6 +69,28 @@ namespace osu.Game.Screens.Play.HUD Current.Value = health.Value; } + protected override void Update() + { + base.Update(); + + // Health changes every frame in draining situations. + // Manually handle value changes to avoid bindable event flow overhead. + if (health.Value != Current.Value) + { + if (initialIncrease != null) + FinishInitialAnimation(Current.Value); + + Current.Value = health.Value; + + if (health.Value > Current.Value) + HealthIncreased(); + } + } + + protected virtual void HealthIncreased() + { + } + private void startInitialAnimation() { if (Current.Value >= health.Value) diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index 845fc77394..00e19c4f76 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -79,7 +79,13 @@ namespace osu.Game.Skinning marker.Position = fill.Position + new Vector2(fill.DrawWidth, isNewStyle ? fill.DrawHeight / 2 : 0); } - protected override void Flash() => marker.Flash(); + protected override void HealthIncreased() + { + marker.Bulge(); + base.HealthIncreased(); + } + + protected override void Flash() => marker.Flash(Current.Value >= epic_cutoff); private static Texture getTexture(ISkin skin, string name) => skin?.GetTexture($"scorebar-{name}"); @@ -113,19 +119,16 @@ namespace osu.Game.Skinning Origin = Anchor.Centre, }; - protected override void LoadComplete() + protected override void Update() { - base.LoadComplete(); + base.Update(); - Current.BindValueChanged(hp => - { - if (hp.NewValue < 0.2f) - Main.Texture = superDangerTexture; - else if (hp.NewValue < epic_cutoff) - Main.Texture = dangerTexture; - else - Main.Texture = normalTexture; - }); + if (Current.Value < 0.2f) + Main.Texture = superDangerTexture; + else if (Current.Value < epic_cutoff) + Main.Texture = dangerTexture; + else + Main.Texture = normalTexture; } } @@ -226,37 +229,30 @@ namespace osu.Game.Skinning public abstract Sprite CreateSprite(); - protected override void LoadComplete() + public override void Flash(bool isEpic) { - base.LoadComplete(); - - Current.BindValueChanged(val => - { - if (val.NewValue > val.OldValue) - bulgeMain(); - }); - } - - public override void Flash() - { - bulgeMain(); - - bool isEpic = Current.Value >= epic_cutoff; - + Bulge(); explode.Blending = isEpic ? BlendingParameters.Additive : BlendingParameters.Inherit; explode.ScaleTo(1).Then().ScaleTo(isEpic ? 2 : 1.6f, 120); explode.FadeOutFromOne(120); } - private void bulgeMain() => + public override void Bulge() + { + base.Bulge(); Main.ScaleTo(1.4f).Then().ScaleTo(1, 200, Easing.Out); + } } public partial class LegacyHealthPiece : CompositeDrawable { public Bindable Current { get; } = new Bindable(); - public virtual void Flash() + public virtual void Bulge() + { + } + + public virtual void Flash(bool isEpic) { } } From d83b8dbdaf489320b1ef2632e7b9fba5a3d7c26a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 16:50:27 +0900 Subject: [PATCH 0889/1118] Refactor `ArgonHealthDisplay` to user interpolation and less bindable events --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 39 +++++++------------ 1 file changed, 13 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 8acc43c091..f15789601c 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -158,7 +158,6 @@ namespace osu.Game.Screens.Play.HUD base.LoadComplete(); HealthProcessor.NewJudgement += onNewJudgement; - Current.BindValueChanged(onCurrentChanged, true); // we're about to set `RelativeSizeAxes` depending on the value of `UseRelativeSize`. // setting `RelativeSizeAxes` internally transforms absolute sizing to relative and back to keep the size the same, @@ -173,31 +172,6 @@ namespace osu.Game.Screens.Play.HUD private void onNewJudgement(JudgementResult result) => pendingMissAnimation |= !result.IsHit; - private void onCurrentChanged(ValueChangedEvent valueChangedEvent) - // schedule display updates one frame later to ensure we know the judgement result causing this change (if there is one). - => Scheduler.AddOnce(updateDisplay); - - private void updateDisplay() - { - double newHealth = Current.Value; - - if (newHealth >= GlowBarValue) - finishMissDisplay(); - - double time = newHealth > GlowBarValue ? 500 : 250; - - // TODO: this should probably use interpolation in update. - this.TransformTo(nameof(HealthBarValue), newHealth, time, Easing.OutQuint); - - if (pendingMissAnimation && newHealth < GlowBarValue) - triggerMissDisplay(); - - pendingMissAnimation = false; - - if (!displayingMiss) - this.TransformTo(nameof(GlowBarValue), newHealth, time, Easing.OutQuint); - } - protected override void Update() { base.Update(); @@ -210,6 +184,19 @@ namespace osu.Game.Screens.Play.HUD mainBar.Alpha = (float)Interpolation.DampContinuously(mainBar.Alpha, Current.Value > 0 ? 1 : 0, 40, Time.Elapsed); glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed); + + double newHealth = Current.Value; + + if (newHealth >= GlowBarValue) + finishMissDisplay(); + + if (pendingMissAnimation && newHealth < GlowBarValue) + triggerMissDisplay(); + pendingMissAnimation = false; + + HealthBarValue = Interpolation.DampContinuously(HealthBarValue, newHealth, 50, Time.Elapsed); + if (!displayingMiss) + GlowBarValue = Interpolation.DampContinuously(GlowBarValue, newHealth, 50, Time.Elapsed); } protected override void FinishInitialAnimation(double value) From 63961ea276a282f681e1b57866ad36dbb3d472e1 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Tue, 9 Jan 2024 16:08:29 +0800 Subject: [PATCH 0890/1118] use `RepeatingButtonBehavior` for seek buttons --- .../Play/PlayerSettings/PlaybackSettings.cs | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 7974b07438..6e1ce39d9b 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -11,6 +11,7 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Screens.Edit.Timing; using osuTK; namespace osu.Game.Screens.Play.PlayerSettings @@ -64,14 +65,14 @@ namespace osu.Game.Screens.Play.PlayerSettings Spacing = new Vector2(5, 0), Children = new Drawable[] { - new IconButton + new SeekButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.FastBackward, Action = () => seek(-1, seek_fast_amount), }, - new IconButton + new SeekButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -96,14 +97,14 @@ namespace osu.Game.Screens.Play.PlayerSettings } } }, - new IconButton + new SeekButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.Forward, Action = () => seek(1, seek_amount), }, - new IconButton + new SeekButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -151,5 +152,13 @@ namespace osu.Game.Screens.Play.PlayerSettings rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.0}x", true); gameplayClock?.IsPaused.BindTo(isPaused); } + + private partial class SeekButton : IconButton + { + public SeekButton() + { + AddInternal(new RepeatingButtonBehaviour(this)); + } + } } } From f1b4c305b02df1d0495f30f2390bd168cdbf8249 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 17:09:19 +0900 Subject: [PATCH 0891/1118] Change skinnable health test scene to drain --- .../Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index 15a7b48323..f215a5d528 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -35,6 +35,13 @@ namespace osu.Game.Tests.Visual.Gameplay }); } + protected override void Update() + { + base.Update(); + + healthProcessor.Health.Value -= 0.0001f * Time.Elapsed; + } + [Test] public void TestHealthDisplayIncrementing() { From b3533d270c0c9ad70cb89919a3c55d3899be2fd3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 17:20:49 +0900 Subject: [PATCH 0892/1118] Remove delegate overhead of `HealthBarValue`/`GlowBarValue` --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 44 +++++-------------- 1 file changed, 10 insertions(+), 34 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index f15789601c..43de99b4b5 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -63,34 +63,8 @@ namespace osu.Game.Screens.Play.HUD private double glowBarValue; - public double GlowBarValue - { - get => glowBarValue; - set - { - if (glowBarValue == value) - return; - - glowBarValue = value; - Scheduler.AddOnce(updatePathVertices); - } - } - private double healthBarValue; - public double HealthBarValue - { - get => healthBarValue; - set - { - if (healthBarValue == value) - return; - - healthBarValue = value; - Scheduler.AddOnce(updatePathVertices); - } - } - public const float MAIN_PATH_RADIUS = 10f; private const float curve_start_offset = 70; @@ -183,27 +157,29 @@ namespace osu.Game.Screens.Play.HUD } mainBar.Alpha = (float)Interpolation.DampContinuously(mainBar.Alpha, Current.Value > 0 ? 1 : 0, 40, Time.Elapsed); - glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, GlowBarValue > 0 ? 1 : 0, 40, Time.Elapsed); + glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, glowBarValue > 0 ? 1 : 0, 40, Time.Elapsed); double newHealth = Current.Value; - if (newHealth >= GlowBarValue) + if (newHealth >= glowBarValue) finishMissDisplay(); - if (pendingMissAnimation && newHealth < GlowBarValue) + if (pendingMissAnimation && newHealth < glowBarValue) triggerMissDisplay(); pendingMissAnimation = false; - HealthBarValue = Interpolation.DampContinuously(HealthBarValue, newHealth, 50, Time.Elapsed); + healthBarValue = Interpolation.DampContinuously(healthBarValue, newHealth, 50, Time.Elapsed); if (!displayingMiss) - GlowBarValue = Interpolation.DampContinuously(GlowBarValue, newHealth, 50, Time.Elapsed); + glowBarValue = Interpolation.DampContinuously(glowBarValue, newHealth, 50, Time.Elapsed); + + updatePathVertices(); } protected override void FinishInitialAnimation(double value) { base.FinishInitialAnimation(value); - this.TransformTo(nameof(HealthBarValue), value, 500, Easing.OutQuint); - this.TransformTo(nameof(GlowBarValue), value, 250, Easing.OutQuint); + this.TransformTo(nameof(healthBarValue), value, 500, Easing.OutQuint); + this.TransformTo(nameof(glowBarValue), value, 250, Easing.OutQuint); } protected override void Flash() @@ -232,7 +208,7 @@ namespace osu.Game.Screens.Play.HUD this.Delay(500).Schedule(() => { - this.TransformTo(nameof(GlowBarValue), Current.Value, 300, Easing.OutQuint); + this.TransformTo(nameof(glowBarValue), Current.Value, 300, Easing.OutQuint); finishMissDisplay(); }, out resetMissBarDelegate); From c081ca21451baa684c343c8a019b90432a4c1336 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Tue, 9 Jan 2024 16:30:48 +0800 Subject: [PATCH 0893/1118] Make field to a local. --- osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 6e1ce39d9b..e208be6095 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -31,8 +31,6 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly OsuSpriteText multiplierText; - private readonly IconButton play; - private readonly BindableBool isPaused = new BindableBool(); [Resolved] @@ -47,6 +45,8 @@ namespace osu.Game.Screens.Play.PlayerSettings const double seek_amount = 5000; const double seek_fast_amount = 10000; + IconButton play; + Children = new Drawable[] { new FillFlowContainer From 12a59eb34cb44322315ec3608de8c112b2b217ac Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 17:30:13 +0900 Subject: [PATCH 0894/1118] Remove vertex update overheads --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 50 ++++++++++--------- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 9 ++-- osu.Game/Skinning/LegacyHealthDisplay.cs | 7 +-- 3 files changed, 35 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 43de99b4b5..e22f45b40d 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; @@ -58,8 +57,7 @@ namespace osu.Game.Screens.Play.HUD private bool displayingMiss => resetMissBarDelegate != null; - private readonly List missBarVertices = new List(); - private readonly List healthBarVertices = new List(); + private readonly List vertices = new List(); private double glowBarValue; @@ -156,23 +154,27 @@ namespace osu.Game.Screens.Play.HUD drawSizeLayout.Validate(); } + healthBarValue = Interpolation.DampContinuously(healthBarValue, Current.Value, 50, Time.Elapsed); + if (!displayingMiss) + glowBarValue = Interpolation.DampContinuously(glowBarValue, Current.Value, 50, Time.Elapsed); + mainBar.Alpha = (float)Interpolation.DampContinuously(mainBar.Alpha, Current.Value > 0 ? 1 : 0, 40, Time.Elapsed); glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, glowBarValue > 0 ? 1 : 0, 40, Time.Elapsed); - double newHealth = Current.Value; + updatePathVertices(); + } - if (newHealth >= glowBarValue) + protected override void HealthChanged(bool increase) + { + if (Current.Value >= glowBarValue) finishMissDisplay(); - if (pendingMissAnimation && newHealth < glowBarValue) + if (pendingMissAnimation && Current.Value < glowBarValue) triggerMissDisplay(); + pendingMissAnimation = false; - healthBarValue = Interpolation.DampContinuously(healthBarValue, newHealth, 50, Time.Elapsed); - if (!displayingMiss) - glowBarValue = Interpolation.DampContinuously(glowBarValue, newHealth, 50, Time.Elapsed); - - updatePathVertices(); + base.HealthChanged(increase); } protected override void FinishInitialAnimation(double value) @@ -265,7 +267,6 @@ namespace osu.Game.Screens.Play.HUD if (DrawWidth - padding < rescale_cutoff) rescalePathProportionally(); - List vertices = new List(); barPath.GetPathToProgress(vertices, 0.0, 1.0); background.Vertices = vertices; @@ -295,20 +296,23 @@ namespace osu.Game.Screens.Play.HUD private void updatePathVertices() { - barPath.GetPathToProgress(healthBarVertices, 0.0, healthBarValue); - barPath.GetPathToProgress(missBarVertices, healthBarValue, Math.Max(glowBarValue, healthBarValue)); + barPath.GetPathToProgress(vertices, 0.0, healthBarValue); + if (vertices.Count == 0) vertices.Add(Vector2.Zero); + Vector2 initialVertex = vertices[0]; + for (int i = 0; i < vertices.Count; i++) + vertices[i] -= initialVertex; - if (healthBarVertices.Count == 0) - healthBarVertices.Add(Vector2.Zero); + mainBar.Vertices = vertices; + mainBar.Position = initialVertex; - if (missBarVertices.Count == 0) - missBarVertices.Add(Vector2.Zero); + barPath.GetPathToProgress(vertices, healthBarValue, Math.Max(glowBarValue, healthBarValue)); + if (vertices.Count == 0) vertices.Add(Vector2.Zero); + initialVertex = vertices[0]; + for (int i = 0; i < vertices.Count; i++) + vertices[i] -= initialVertex; - glowBar.Vertices = missBarVertices.Select(v => v - missBarVertices[0]).ToList(); - glowBar.Position = missBarVertices[0]; - - mainBar.Vertices = healthBarVertices.Select(v => v - healthBarVertices[0]).ToList(); - mainBar.Position = healthBarVertices[0]; + glowBar.Vertices = vertices; + glowBar.Position = initialVertex; } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index a20121b20b..d5b238cd50 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -8,6 +8,7 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Threading; +using osu.Framework.Utils; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; @@ -75,19 +76,17 @@ namespace osu.Game.Screens.Play.HUD // Health changes every frame in draining situations. // Manually handle value changes to avoid bindable event flow overhead. - if (health.Value != Current.Value) + if (!Precision.AlmostEquals(health.Value, Current.Value, 0.001f)) { if (initialIncrease != null) FinishInitialAnimation(Current.Value); + HealthChanged(Current.Value > health.Value); Current.Value = health.Value; - - if (health.Value > Current.Value) - HealthIncreased(); } } - protected virtual void HealthIncreased() + protected virtual void HealthChanged(bool increase) { } diff --git a/osu.Game/Skinning/LegacyHealthDisplay.cs b/osu.Game/Skinning/LegacyHealthDisplay.cs index 00e19c4f76..9c06cbbfb5 100644 --- a/osu.Game/Skinning/LegacyHealthDisplay.cs +++ b/osu.Game/Skinning/LegacyHealthDisplay.cs @@ -79,10 +79,11 @@ namespace osu.Game.Skinning marker.Position = fill.Position + new Vector2(fill.DrawWidth, isNewStyle ? fill.DrawHeight / 2 : 0); } - protected override void HealthIncreased() + protected override void HealthChanged(bool increase) { - marker.Bulge(); - base.HealthIncreased(); + if (increase) + marker.Bulge(); + base.HealthChanged(increase); } protected override void Flash() => marker.Flash(Current.Value >= epic_cutoff); From b6505ba0634887d1d48db6aaa880a55622b3f715 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 18:16:53 +0900 Subject: [PATCH 0895/1118] Reduce colour tween overhead and mark other calls of concern --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index e22f45b40d..f1653c8742 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -188,15 +188,9 @@ namespace osu.Game.Screens.Play.HUD { base.Flash(); - mainBar.TransformTo(nameof(BarPath.GlowColour), main_bar_glow_colour.Opacity(0.8f)) - .TransformTo(nameof(BarPath.GlowColour), main_bar_glow_colour, 300, Easing.OutQuint); - if (!displayingMiss) { - glowBar.TransformTo(nameof(BarPath.BarColour), Colour4.White, 30, Easing.OutQuint) - .Then() - .TransformTo(nameof(BarPath.BarColour), main_bar_colour, 1000, Easing.OutQuint); - + // TODO: REMOVE THIS. It's recreating textures. glowBar.TransformTo(nameof(BarPath.GlowColour), Colour4.White, 30, Easing.OutQuint) .Then() .TransformTo(nameof(BarPath.GlowColour), main_bar_glow_colour, 300, Easing.OutQuint); @@ -214,9 +208,11 @@ namespace osu.Game.Screens.Play.HUD finishMissDisplay(); }, out resetMissBarDelegate); + // TODO: REMOVE THIS. It's recreating textures. glowBar.TransformTo(nameof(BarPath.BarColour), new Colour4(255, 147, 147, 255), 100, Easing.OutQuint).Then() .TransformTo(nameof(BarPath.BarColour), new Colour4(255, 93, 93, 255), 800, Easing.OutQuint); + // TODO: REMOVE THIS. It's recreating textures. glowBar.TransformTo(nameof(BarPath.GlowColour), new Colour4(253, 0, 0, 255).Lighten(0.2f)) .TransformTo(nameof(BarPath.GlowColour), new Colour4(253, 0, 0, 255), 800, Easing.OutQuint); } @@ -228,6 +224,7 @@ namespace osu.Game.Screens.Play.HUD if (Current.Value > 0) { + // TODO: REMOVE THIS. It's recreating textures. glowBar.TransformTo(nameof(BarPath.BarColour), main_bar_colour, 300, Easing.In); glowBar.TransformTo(nameof(BarPath.GlowColour), main_bar_glow_colour, 300, Easing.In); } From 80892f316754c4004112f9be1dbe4dc4a8723191 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 18:18:11 +0900 Subject: [PATCH 0896/1118] Fix misses not displaying properly --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index f1653c8742..78720ec087 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -169,10 +169,11 @@ namespace osu.Game.Screens.Play.HUD if (Current.Value >= glowBarValue) finishMissDisplay(); - if (pendingMissAnimation && Current.Value < glowBarValue) + if (pendingMissAnimation) + { triggerMissDisplay(); - - pendingMissAnimation = false; + pendingMissAnimation = false; + } base.HealthChanged(increase); } From 9c7e555237b29e241e22b0c40e14e94afdbec4bc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 18:27:37 +0900 Subject: [PATCH 0897/1118] Fix initial animation not playing correctly --- osu.Game/Screens/Play/HUD/HealthDisplay.cs | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HealthDisplay.cs b/osu.Game/Screens/Play/HUD/HealthDisplay.cs index d5b238cd50..cd4d050b52 100644 --- a/osu.Game/Screens/Play/HUD/HealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/HealthDisplay.cs @@ -36,6 +36,8 @@ namespace osu.Game.Screens.Play.HUD private BindableNumber health = null!; + protected bool InitialAnimationPlaying => initialIncrease != null; + private ScheduledDelegate? initialIncrease; /// @@ -64,25 +66,35 @@ namespace osu.Game.Screens.Play.HUD // this probably shouldn't be operating on `this.` showHealthBar.BindValueChanged(healthBar => this.FadeTo(healthBar.NewValue ? 1 : 0, HUDOverlay.FADE_DURATION, HUDOverlay.FADE_EASING), true); + initialHealthValue = health.Value; + if (PlayInitialIncreaseAnimation) startInitialAnimation(); else Current.Value = health.Value; } + private double lastValue; + private double initialHealthValue; + protected override void Update() { base.Update(); - // Health changes every frame in draining situations. - // Manually handle value changes to avoid bindable event flow overhead. - if (!Precision.AlmostEquals(health.Value, Current.Value, 0.001f)) + if (!InitialAnimationPlaying || health.Value != initialHealthValue) { + Current.Value = health.Value; + if (initialIncrease != null) FinishInitialAnimation(Current.Value); + } - HealthChanged(Current.Value > health.Value); - Current.Value = health.Value; + // Health changes every frame in draining situations. + // Manually handle value changes to avoid bindable event flow overhead. + if (!Precision.AlmostEquals(lastValue, Current.Value, 0.001f)) + { + HealthChanged(Current.Value > lastValue); + lastValue = Current.Value; } } From 6ac1c799bde83a8fb49ad44e37de703421a4097c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 18:34:20 +0900 Subject: [PATCH 0898/1118] Fix `SettingsToolboxGroup` allocating excessively due to missing cache validation --- osu.Game/Overlays/SettingsToolboxGroup.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Overlays/SettingsToolboxGroup.cs b/osu.Game/Overlays/SettingsToolboxGroup.cs index c0948c1eab..de13bd96d4 100644 --- a/osu.Game/Overlays/SettingsToolboxGroup.cs +++ b/osu.Game/Overlays/SettingsToolboxGroup.cs @@ -151,9 +151,12 @@ namespace osu.Game.Overlays base.Update(); if (!headerTextVisibilityCache.IsValid) + { // These toolbox grouped may be contracted to only show icons. // For now, let's hide the header to avoid text truncation weirdness in such cases. headerText.FadeTo(headerText.DrawWidth < DrawWidth ? 1 : 0, 150, Easing.OutQuint); + headerTextVisibilityCache.Validate(); + } } protected override bool OnInvalidate(Invalidation invalidation, InvalidationSource source) From 66b3945cd644abd7704adde5284211768981a36e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 9 Jan 2024 10:44:30 +0100 Subject: [PATCH 0899/1118] Move current screen check to better place --- osu.Game/Screens/Select/PlaySongSelect.cs | 3 --- osu.Game/Screens/Select/SongSelect.cs | 3 ++- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 3cf8de5267..4951504ff5 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -92,9 +92,6 @@ namespace osu.Game.Screens.Select { if (playerLoader != null) return false; - if (!this.IsCurrentScreen()) - return false; - modsAtGameplayStart = Mods.Value; // Ctrl+Enter should start map with autoplay enabled. diff --git a/osu.Game/Screens/Select/SongSelect.cs b/osu.Game/Screens/Select/SongSelect.cs index 2d5c44e5a5..bf1724995a 100644 --- a/osu.Game/Screens/Select/SongSelect.cs +++ b/osu.Game/Screens/Select/SongSelect.cs @@ -660,7 +660,8 @@ namespace osu.Game.Screens.Select logo.Action = () => { - FinaliseSelection(); + if (this.IsCurrentScreen()) + FinaliseSelection(); return false; }; } From cac0b0de6dba025e99692d208cd56f545bb05660 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 9 Jan 2024 11:38:01 +0100 Subject: [PATCH 0900/1118] Remove unused using directive --- osu.Game/Database/RealmFileStore.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Database/RealmFileStore.cs b/osu.Game/Database/RealmFileStore.cs index f1ed3f4b63..1bd22af4c7 100644 --- a/osu.Game/Database/RealmFileStore.cs +++ b/osu.Game/Database/RealmFileStore.cs @@ -4,7 +4,6 @@ using System; using System.Diagnostics; using System.IO; -using System.Linq; using osu.Framework.Extensions; using osu.Framework.IO.Stores; using osu.Framework.Logging; From a8a70be04ab13f0ad4c565b86ee206c19ba01d02 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 9 Jan 2024 11:49:42 +0100 Subject: [PATCH 0901/1118] Reference property via `nameof` rather than hardcoding --- osu.Game/Database/RealmFileStore.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Database/RealmFileStore.cs b/osu.Game/Database/RealmFileStore.cs index 1bd22af4c7..9683baec69 100644 --- a/osu.Game/Database/RealmFileStore.cs +++ b/osu.Game/Database/RealmFileStore.cs @@ -98,7 +98,7 @@ namespace osu.Game.Database // can potentially be run asynchronously, although we will need to consider operation order for disk deletion vs realm removal. realm.Write(r => { - foreach (var file in r.All().Filter("Usages.@count = 0")) + foreach (var file in r.All().Filter(@$"{nameof(RealmFile.Usages)}.@count = 0")) { totalFiles++; From 4110adc4c0a9fdc763ec73061169a5e4e8952a7c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 9 Jan 2024 20:16:27 +0900 Subject: [PATCH 0902/1118] Fix missing wireframe on argon combo counter --- .../Screens/Play/HUD/ArgonAccuracyCounter.cs | 1 + .../Screens/Play/HUD/ArgonComboCounter.cs | 24 +++++++++++++++++++ osu.Game/Screens/Play/HUD/ComboCounter.cs | 5 ---- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs index 521ad63426..5284e3167a 100644 --- a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs @@ -83,6 +83,7 @@ namespace osu.Game.Screens.Play.HUD }, fractionPart = new ArgonCounterTextComponent(Anchor.TopLeft) { + RequiredDisplayDigits = { Value = 2 }, WireframeOpacity = { BindTarget = WireframeOpacity }, Scale = new Vector2(0.5f), }, diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index 5ea7fd0b82..af884aa441 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs @@ -57,6 +57,30 @@ namespace osu.Game.Screens.Play.HUD }); } + public override int DisplayedCount + { + get => base.DisplayedCount; + set + { + base.DisplayedCount = value; + updateWireframe(); + } + } + + private void updateWireframe() + { + text.RequiredDisplayDigits.Value = getDigitsRequiredForDisplayCount(); + } + + private int getDigitsRequiredForDisplayCount() + { + int digitsRequired = 1; + long c = DisplayedCount; + while ((c /= 10) > 0) + digitsRequired++; + return digitsRequired; + } + protected override LocalisableString FormatCount(int count) => $@"{count}x"; protected override IHasText CreateText() => text = new ArgonCounterTextComponent(Anchor.TopLeft, MatchesStrings.MatchScoreStatsCombo.ToUpper()) diff --git a/osu.Game/Screens/Play/HUD/ComboCounter.cs b/osu.Game/Screens/Play/HUD/ComboCounter.cs index 17531281aa..93802e11c2 100644 --- a/osu.Game/Screens/Play/HUD/ComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ComboCounter.cs @@ -11,11 +11,6 @@ namespace osu.Game.Screens.Play.HUD { public bool UsesFixedAnchor { get; set; } - protected ComboCounter() - { - Current.Value = DisplayedCount = 0; - } - protected override double GetProportionalDuration(int currentValue, int newValue) { return Math.Abs(currentValue - newValue) * RollingDuration * 100.0f; From 8ad697ff4cea857bec7f18a94723b098c6e036ef Mon Sep 17 00:00:00 2001 From: wooster0 Date: Tue, 9 Jan 2024 21:28:46 +0900 Subject: [PATCH 0903/1118] apply some suggestions/corrections --- .../TestScenePlaylistsResultsScreen.cs | 16 ++++++---- osu.Game/Screens/Ranking/ResultsScreen.cs | 32 +++++++++---------- 2 files changed, 24 insertions(+), 24 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index e36e7f54ba..98aeb66428 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -79,6 +79,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("bind user score info handler", () => bindHandler(userScore: userScore)); createResults(() => userScore); + waitForDisplay(); AddAssert("user score selected", () => this.ChildrenOfType().Single(p => p.Score.OnlineID == userScore.OnlineID).State == PanelState.Expanded); AddAssert($"score panel position is {real_user_position}", @@ -91,6 +92,7 @@ namespace osu.Game.Tests.Visual.Playlists InitialiseUserScoresAndBeatmap(); createResults(); + waitForDisplay(); AddAssert("top score selected", () => this.ChildrenOfType().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded); } @@ -103,6 +105,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("bind user score info handler", () => bindHandler(true, userScore)); createResults(() => userScore); + waitForDisplay(); AddAssert("more than 1 panel displayed", () => this.ChildrenOfType().Count() > 1); AddAssert("user score selected", () => this.ChildrenOfType().Single(p => p.Score.OnlineID == userScore.OnlineID).State == PanelState.Expanded); @@ -116,6 +119,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("bind delayed handler", () => bindHandler(true)); createResults(); + waitForDisplay(); AddAssert("top score selected", () => this.ChildrenOfType().OrderByDescending(p => p.Score.TotalScore).First().State == PanelState.Expanded); } @@ -126,6 +130,7 @@ namespace osu.Game.Tests.Visual.Playlists InitialiseUserScoresAndBeatmap(); createResults(); + waitForDisplay(); AddStep("bind delayed handler", () => bindHandler(true)); @@ -152,6 +157,7 @@ namespace osu.Game.Tests.Visual.Playlists AddStep("bind user score info handler", () => bindHandler(userScore: userScore)); createResults(() => userScore); + waitForDisplay(); AddStep("bind delayed handler", () => bindHandler(true)); @@ -174,12 +180,11 @@ namespace osu.Game.Tests.Visual.Playlists public void TestShowWithNoScores() { InitialiseUserScoresAndBeatmap(noScores: true); - - createResults(noScores: true); + createResults(); AddAssert("no scores visible", () => resultsScreen.ScorePanelList.GetScorePanels().Count() == 0); } - private void createResults(Func getScore = null, bool noScores = false) + private void createResults(Func getScore = null) { AddStep("load results", () => { @@ -190,13 +195,10 @@ namespace osu.Game.Tests.Visual.Playlists }); AddUntilStep("wait for screen to load", () => resultsScreen.IsLoaded); - waitForDisplay(noScores); } - private void waitForDisplay(bool noScores = false) + private void waitForDisplay() { - if (noScores) return; - AddUntilStep("wait for scores loaded", () => requestComplete // request handler may need to fire more than once to get scores. diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 8823e4d320..697d62ad6e 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -206,25 +206,17 @@ namespace osu.Game.Screens.Ranking if (lastFetchCompleted) { - if (ScorePanelList.IsEmpty) - { - // This can happen if a beatmap part of a playlist hasn't been played yet. - VerticalScrollContent.Add(new MessagePlaceholder(LeaderboardStrings.NoRecordsYet)); - } - else - { - APIRequest nextPageRequest = null; + APIRequest nextPageRequest = null; - if (ScorePanelList.IsScrolledToStart) - nextPageRequest = FetchNextPage(-1, fetchScoresCallback); - else if (ScorePanelList.IsScrolledToEnd) - nextPageRequest = FetchNextPage(1, fetchScoresCallback); + if (ScorePanelList.IsScrolledToStart) + nextPageRequest = FetchNextPage(-1, fetchScoresCallback); + else if (ScorePanelList.IsScrolledToEnd) + nextPageRequest = FetchNextPage(1, fetchScoresCallback); - if (nextPageRequest != null) - { - lastFetchCompleted = false; - api.Queue(nextPageRequest); - } + if (nextPageRequest != null) + { + lastFetchCompleted = false; + api.Queue(nextPageRequest); } } } @@ -255,6 +247,12 @@ namespace osu.Game.Screens.Ranking addScore(s); lastFetchCompleted = true; + + if (ScorePanelList.IsEmpty) + { + // This can happen if for example a beatmap that is part of a playlist hasn't been played yet. + VerticalScrollContent.Add(new MessagePlaceholder(LeaderboardStrings.NoRecordsYet)); + } }); public override void OnEntering(ScreenTransitionEvent e) From ea37c70e0b0e032e84a9fa1ee7c902c1a7cb2c2e Mon Sep 17 00:00:00 2001 From: wooster0 Date: Tue, 9 Jan 2024 21:34:40 +0900 Subject: [PATCH 0904/1118] apply suggestion --- .../TestScenePlaylistsResultsScreen.cs | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 98aeb66428..02bd7f950f 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -42,8 +42,8 @@ namespace osu.Game.Tests.Visual.Playlists private int totalCount; private ScoreInfo userScore; - // We don't use SetUpSteps for this because one of the tests shouldn't receive any scores. - public void InitialiseUserScoresAndBeatmap(bool noScores = false) + [SetUpSteps] + public override void SetUpSteps() { base.SetUpSteps(); @@ -63,18 +63,24 @@ namespace osu.Game.Tests.Visual.Playlists userScore.Statistics = new Dictionary(); userScore.MaximumStatistics = new Dictionary(); - bindHandler(noScores: noScores); - // Beatmap is required to be an actual beatmap so the scores can get their scores correctly // calculated for standardised scoring, else the tests that rely on ordering will fall over. Beatmap.Value = CreateWorkingBeatmap(Ruleset.Value); }); } + public void SetUpRequestHandler(bool noScores = false) + { + AddStep("set up request handler", () => + { + bindHandler(noScores: noScores); + }); + } + [Test] public void TestShowWithUserScore() { - InitialiseUserScoresAndBeatmap(); + SetUpRequestHandler(); AddStep("bind user score info handler", () => bindHandler(userScore: userScore)); @@ -89,7 +95,7 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowNullUserScore() { - InitialiseUserScoresAndBeatmap(); + SetUpRequestHandler(); createResults(); waitForDisplay(); @@ -100,7 +106,7 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowUserScoreWithDelay() { - InitialiseUserScoresAndBeatmap(); + SetUpRequestHandler(); AddStep("bind user score info handler", () => bindHandler(true, userScore)); @@ -114,7 +120,7 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowNullUserScoreWithDelay() { - InitialiseUserScoresAndBeatmap(); + SetUpRequestHandler(); AddStep("bind delayed handler", () => bindHandler(true)); @@ -127,7 +133,7 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestFetchWhenScrolledToTheRight() { - InitialiseUserScoresAndBeatmap(); + SetUpRequestHandler(); createResults(); waitForDisplay(); @@ -152,7 +158,7 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestFetchWhenScrolledToTheLeft() { - InitialiseUserScoresAndBeatmap(); + SetUpRequestHandler(); AddStep("bind user score info handler", () => bindHandler(userScore: userScore)); @@ -179,7 +185,7 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowWithNoScores() { - InitialiseUserScoresAndBeatmap(noScores: true); + SetUpRequestHandler(true); createResults(); AddAssert("no scores visible", () => resultsScreen.ScorePanelList.GetScorePanels().Count() == 0); } From d5fdd0c0f9bb4a52630e9fead6b27ab44e472ca0 Mon Sep 17 00:00:00 2001 From: wooster0 Date: Tue, 9 Jan 2024 21:44:05 +0900 Subject: [PATCH 0905/1118] make each test bind the handler only once, depending on its need --- .../TestScenePlaylistsResultsScreen.cs | 24 +++---------------- 1 file changed, 3 insertions(+), 21 deletions(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 02bd7f950f..727d9da50b 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -69,19 +69,9 @@ namespace osu.Game.Tests.Visual.Playlists }); } - public void SetUpRequestHandler(bool noScores = false) - { - AddStep("set up request handler", () => - { - bindHandler(noScores: noScores); - }); - } - [Test] public void TestShowWithUserScore() { - SetUpRequestHandler(); - AddStep("bind user score info handler", () => bindHandler(userScore: userScore)); createResults(() => userScore); @@ -95,7 +85,7 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowNullUserScore() { - SetUpRequestHandler(); + AddStep("bind user score info handler", () => bindHandler()); createResults(); waitForDisplay(); @@ -106,8 +96,6 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowUserScoreWithDelay() { - SetUpRequestHandler(); - AddStep("bind user score info handler", () => bindHandler(true, userScore)); createResults(() => userScore); @@ -120,8 +108,6 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowNullUserScoreWithDelay() { - SetUpRequestHandler(); - AddStep("bind delayed handler", () => bindHandler(true)); createResults(); @@ -133,13 +119,11 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestFetchWhenScrolledToTheRight() { - SetUpRequestHandler(); + AddStep("bind delayed handler", () => bindHandler(true)); createResults(); waitForDisplay(); - AddStep("bind delayed handler", () => bindHandler(true)); - for (int i = 0; i < 2; i++) { int beforePanelCount = 0; @@ -158,8 +142,6 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestFetchWhenScrolledToTheLeft() { - SetUpRequestHandler(); - AddStep("bind user score info handler", () => bindHandler(userScore: userScore)); createResults(() => userScore); @@ -185,7 +167,7 @@ namespace osu.Game.Tests.Visual.Playlists [Test] public void TestShowWithNoScores() { - SetUpRequestHandler(true); + AddStep("bind user score info handler", () => bindHandler(noScores: true)); createResults(); AddAssert("no scores visible", () => resultsScreen.ScorePanelList.GetScorePanels().Count() == 0); } From 77bf6e3244111b026f930a75fbb35dd497d9c921 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 9 Jan 2024 13:59:27 +0100 Subject: [PATCH 0906/1118] Fix missing wireframe behind "x" sign on combo counter display --- osu.Game/Screens/Play/HUD/ArgonComboCounter.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index af884aa441..1d6ca3c893 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs @@ -74,7 +74,8 @@ namespace osu.Game.Screens.Play.HUD private int getDigitsRequiredForDisplayCount() { - int digitsRequired = 1; + // one for the single presumed starting digit, one for the "x" at the end. + int digitsRequired = 2; long c = DisplayedCount; while ((c /= 10) > 0) digitsRequired++; From 92ba77031403632ad03b86efcbd2754a47b6c646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 9 Jan 2024 14:00:58 +0100 Subject: [PATCH 0907/1118] Fix missing wireframe behind percent sign on accuracy counter --- osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs index 5284e3167a..171aa3f44b 100644 --- a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs @@ -90,6 +90,7 @@ namespace osu.Game.Screens.Play.HUD percentText = new ArgonCounterTextComponent(Anchor.TopLeft) { Text = @"%", + RequiredDisplayDigits = { Value = 1 }, WireframeOpacity = { BindTarget = WireframeOpacity } }, } From 484e9e8ee636d9d1ec4aaf549e7615fb18682226 Mon Sep 17 00:00:00 2001 From: Nitrous Date: Tue, 9 Jan 2024 22:09:20 +0800 Subject: [PATCH 0908/1118] Fix binding order of `IsPaused` bindable and disable playback controls in spectator mode. --- .../Screens/Play/PlayerSettings/PlaybackSettings.cs | 13 +++++++++++-- osu.Game/Screens/Play/SpectatorPlayer.cs | 2 ++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index e208be6095..e2859868bd 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -27,6 +27,8 @@ namespace osu.Game.Screens.Play.PlayerSettings Precision = 0.1, }; + public readonly Bindable AllowControls = new BindableBool(true); + private readonly PlayerSliderBar rateSlider; private readonly OsuSpriteText multiplierText; @@ -71,6 +73,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.FastBackward, Action = () => seek(-1, seek_fast_amount), + Enabled = { BindTarget = AllowControls }, }, new SeekButton { @@ -78,6 +81,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.Backward, Action = () => seek(-1, seek_amount), + Enabled = { BindTarget = AllowControls }, }, play = new IconButton { @@ -95,7 +99,8 @@ namespace osu.Game.Screens.Play.PlayerSettings else gameplayClock.Start(); } - } + }, + Enabled = { BindTarget = AllowControls }, }, new SeekButton { @@ -103,6 +108,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.Forward, Action = () => seek(1, seek_amount), + Enabled = { BindTarget = AllowControls }, }, new SeekButton { @@ -110,6 +116,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.FastForward, Action = () => seek(1, seek_fast_amount), + Enabled = { BindTarget = AllowControls }, }, }, }, @@ -150,7 +157,9 @@ namespace osu.Game.Screens.Play.PlayerSettings { base.LoadComplete(); rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.0}x", true); - gameplayClock?.IsPaused.BindTo(isPaused); + + if (gameplayClock != null) + isPaused.BindTarget = gameplayClock.IsPaused; } private partial class SeekButton : IconButton diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index 2faead0ee1..dd7418d563 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -68,6 +68,8 @@ namespace osu.Game.Screens.Play master.UserPlaybackRate.Value = 1; } }, true); + + HUDOverlay.PlayerSettingsOverlay.PlaybackSettings.AllowControls.Value = false; } /// From 91677158a058e03bae2bace8d236c1ef12a5e66e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jan 2024 22:33:00 +0900 Subject: [PATCH 0909/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index b179b8b837..56931bbcb4 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 7e03ab50e2..c180baeab7 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 49d13cda6b80fcd439a7d40723e2635c7792eee2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jan 2024 23:09:43 +0900 Subject: [PATCH 0910/1118] Fix failing test by setting health on source of truth --- .../Visual/Gameplay/TestSceneFailingLayer.cs | 18 ++++++++++-------- osu.Game/Screens/Play/HUD/FailingLayer.cs | 4 ++-- 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs index 235ada2d63..684d263a58 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFailingLayer.cs @@ -22,6 +22,8 @@ namespace osu.Game.Tests.Visual.Gameplay private readonly Bindable showHealth = new Bindable(); + private HealthProcessor healthProcessor; + [Resolved] private OsuConfigManager config { get; set; } @@ -29,7 +31,7 @@ namespace osu.Game.Tests.Visual.Gameplay { AddStep("create layer", () => { - Child = new HealthProcessorContainer(healthProcessor) + Child = new HealthProcessorContainer(this.healthProcessor = healthProcessor) { RelativeSizeAxes = Axes.Both, Child = layer = new FailingLayer() @@ -50,12 +52,12 @@ namespace osu.Game.Tests.Visual.Gameplay AddSliderStep("current health", 0.0, 1.0, 1.0, val => { if (layer != null) - layer.Current.Value = val; + healthProcessor.Health.Value = val; }); - AddStep("set health to 0.10", () => layer.Current.Value = 0.1); + AddStep("set health to 0.10", () => healthProcessor.Health.Value = 0.1); AddUntilStep("layer fade is visible", () => layer.ChildrenOfType().First().Alpha > 0.1f); - AddStep("set health to 1", () => layer.Current.Value = 1f); + AddStep("set health to 1", () => healthProcessor.Health.Value = 1f); AddUntilStep("layer fade is invisible", () => !layer.ChildrenOfType().First().IsPresent); } @@ -65,7 +67,7 @@ namespace osu.Game.Tests.Visual.Gameplay create(new DrainingHealthProcessor(0)); AddUntilStep("layer is visible", () => layer.IsPresent); AddStep("disable layer", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false)); - AddStep("set health to 0.10", () => layer.Current.Value = 0.1); + AddStep("set health to 0.10", () => healthProcessor.Health.Value = 0.1); AddUntilStep("layer is not visible", () => !layer.IsPresent); } @@ -74,7 +76,7 @@ namespace osu.Game.Tests.Visual.Gameplay { create(new AccumulatingHealthProcessor(1)); AddUntilStep("layer is not visible", () => !layer.IsPresent); - AddStep("set health to 0.10", () => layer.Current.Value = 0.1); + AddStep("set health to 0.10", () => healthProcessor.Health.Value = 0.1); AddUntilStep("layer is not visible", () => !layer.IsPresent); } @@ -82,7 +84,7 @@ namespace osu.Game.Tests.Visual.Gameplay public void TestLayerVisibilityWithDrainingProcessor() { create(new DrainingHealthProcessor(0)); - AddStep("set health to 0.10", () => layer.Current.Value = 0.1); + AddStep("set health to 0.10", () => healthProcessor.Health.Value = 0.1); AddWaitStep("wait for potential fade", 10); AddAssert("layer is still visible", () => layer.IsPresent); } @@ -92,7 +94,7 @@ namespace osu.Game.Tests.Visual.Gameplay { create(new DrainingHealthProcessor(0)); - AddStep("set health to 0.10", () => layer.Current.Value = 0.1); + AddStep("set health to 0.10", () => healthProcessor.Health.Value = 0.1); AddStep("don't show health", () => showHealth.Value = false); AddStep("disable FadePlayfieldWhenHealthLow", () => config.SetValue(OsuSetting.FadePlayfieldWhenHealthLow, false)); diff --git a/osu.Game/Screens/Play/HUD/FailingLayer.cs b/osu.Game/Screens/Play/HUD/FailingLayer.cs index 3954e23cbe..2bac7660b3 100644 --- a/osu.Game/Screens/Play/HUD/FailingLayer.cs +++ b/osu.Game/Screens/Play/HUD/FailingLayer.cs @@ -100,11 +100,11 @@ namespace osu.Game.Screens.Play.HUD protected override void Update() { + base.Update(); + double target = Math.Clamp(max_alpha * (1 - Current.Value / low_health_threshold), 0, max_alpha); boxes.Alpha = (float)Interpolation.Lerp(boxes.Alpha, target, Clock.ElapsedFrameTime * 0.01f); - - base.Update(); } } } From 84f704a6c2747360d89b5c241fa09ecb5b3a60e8 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 10 Jan 2024 17:15:27 +0300 Subject: [PATCH 0911/1118] Add failing test case --- .../Resources/mania-skin-broken-array.ini | 3 +++ .../Skins/LegacyManiaSkinDecoderTest.cs | 19 +++++++++++++++++++ 2 files changed, 22 insertions(+) create mode 100644 osu.Game.Tests/Resources/mania-skin-broken-array.ini diff --git a/osu.Game.Tests/Resources/mania-skin-broken-array.ini b/osu.Game.Tests/Resources/mania-skin-broken-array.ini new file mode 100644 index 0000000000..5a6d37eef6 --- /dev/null +++ b/osu.Game.Tests/Resources/mania-skin-broken-array.ini @@ -0,0 +1,3 @@ +[Mania] +Keys: 4 +ColumnLineWidth: 3,,3,3,3 \ No newline at end of file diff --git a/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs index b96bf09255..c4117014db 100644 --- a/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs @@ -114,5 +114,24 @@ namespace osu.Game.Tests.Skins Assert.That(configs[0].MinimumColumnWidth, Is.EqualTo(16)); } } + + [Test] + public void TestParseArrayWithSomeEmptyElements() + { + var decoder = new LegacyManiaSkinDecoder(); + + using (var resStream = TestResources.OpenResource("mania-skin-broken-array.ini")) + using (var stream = new LineBufferedReader(resStream)) + { + var configs = decoder.Decode(stream); + + Assert.That(configs.Count, Is.EqualTo(1)); + Assert.That(configs[0].ColumnLineWidth[0], Is.EqualTo(3)); + Assert.That(configs[0].ColumnLineWidth[1], Is.EqualTo(0)); // malformed entry, should be parsed as zero + Assert.That(configs[0].ColumnLineWidth[2], Is.EqualTo(3)); + Assert.That(configs[0].ColumnLineWidth[3], Is.EqualTo(3)); + Assert.That(configs[0].ColumnLineWidth[4], Is.EqualTo(3)); + } + } } } From 698ae66a494a899bfeb4159d72db665a7f514a8a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 10 Jan 2024 17:41:58 +0300 Subject: [PATCH 0912/1118] Fix mania skin array decoder not handling malformed entries rigorously --- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index b472afb74f..720b05a0b1 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -155,7 +155,15 @@ namespace osu.Game.Skinning if (i >= output.Length) break; - output[i] = float.Parse(values[i], CultureInfo.InvariantCulture) * (applyScaleFactor ? LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR : 1); + if (!float.TryParse(values[i], NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out float parsedValue)) + // some skins may provide incorrect entries in array values. to match stable behaviour, read such entries as zero. + // see: https://github.com/ppy/osu/issues/26464, stable code: https://github.com/peppy/osu-stable-reference/blob/3ea48705eb67172c430371dcfc8a16a002ed0d3d/osu!/Graphics/Skinning/Components/Section.cs#L134-L137 + parsedValue = 0; + + if (applyScaleFactor) + parsedValue *= LegacyManiaSkinConfiguration.POSITION_SCALE_FACTOR; + + output[i] = parsedValue; } } } From 7ca4d854416ae663825bfc7d84c5df0288a7ce41 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 10 Jan 2024 17:48:47 +0300 Subject: [PATCH 0913/1118] Remove unnecessary `AllowThousands` flag The flag is there to match `float.Parse` behaviour, but it's too illogical and unnecessary to have it. --- osu.Game/Skinning/LegacyManiaSkinDecoder.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs index 720b05a0b1..ff6e7fc38e 100644 --- a/osu.Game/Skinning/LegacyManiaSkinDecoder.cs +++ b/osu.Game/Skinning/LegacyManiaSkinDecoder.cs @@ -155,7 +155,7 @@ namespace osu.Game.Skinning if (i >= output.Length) break; - if (!float.TryParse(values[i], NumberStyles.Float | NumberStyles.AllowThousands, CultureInfo.InvariantCulture, out float parsedValue)) + if (!float.TryParse(values[i], NumberStyles.Float, CultureInfo.InvariantCulture, out float parsedValue)) // some skins may provide incorrect entries in array values. to match stable behaviour, read such entries as zero. // see: https://github.com/ppy/osu/issues/26464, stable code: https://github.com/peppy/osu-stable-reference/blob/3ea48705eb67172c430371dcfc8a16a002ed0d3d/osu!/Graphics/Skinning/Components/Section.cs#L134-L137 parsedValue = 0; From 7b848e1458c484cad520ce8ceee4d4fab64fa726 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 10 Jan 2024 17:51:27 +0300 Subject: [PATCH 0914/1118] Assert column line width length for extra safety --- osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs b/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs index c4117014db..d577e0fedf 100644 --- a/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs +++ b/osu.Game.Tests/Skins/LegacyManiaSkinDecoderTest.cs @@ -126,6 +126,7 @@ namespace osu.Game.Tests.Skins var configs = decoder.Decode(stream); Assert.That(configs.Count, Is.EqualTo(1)); + Assert.That(configs[0].ColumnLineWidth.Length, Is.EqualTo(5)); Assert.That(configs[0].ColumnLineWidth[0], Is.EqualTo(3)); Assert.That(configs[0].ColumnLineWidth[1], Is.EqualTo(0)); // malformed entry, should be parsed as zero Assert.That(configs[0].ColumnLineWidth[2], Is.EqualTo(3)); From c2706ca91be7293a47142c0ae184774a17b263f6 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jan 2024 23:48:16 +0900 Subject: [PATCH 0915/1118] Also show drain on argon health display test --- .../Visual/Gameplay/TestSceneArgonHealthDisplay.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs index 30fb4412f4..06d199513c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs @@ -159,5 +159,11 @@ namespace osu.Game.Tests.Visual.Gameplay Type = HitResult.Perfect }); } + + protected override void Update() + { + base.Update(); + healthProcessor.Health.Value -= 0.0001f * Time.Elapsed; + } } } From 5d6f767dbdc613ca54bbfdb98880c33517769b1c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 10 Jan 2024 23:52:39 +0900 Subject: [PATCH 0916/1118] Reduce excessive `Color4` allocations during path colour updates --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index afc64b0b84..236bd3366d 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -328,14 +328,17 @@ namespace osu.Game.Screens.Play.HUD private partial class BackgroundPath : SmoothPath { + private static readonly Color4 colour_white = Color4.White.Opacity(0.8f); + private static readonly Color4 colour_black = Color4.Black.Opacity(0.2f); + protected override Color4 ColourAt(float position) { if (position <= 0.16f) - return Color4.White.Opacity(0.8f); + return colour_white; return Interpolation.ValueAt(position, - Color4.White.Opacity(0.8f), - Color4.Black.Opacity(0.2f), + colour_white, + colour_black, -0.5f, 1f, Easing.OutQuint); } } @@ -374,12 +377,14 @@ namespace osu.Game.Screens.Play.HUD public float GlowPortion { get; init; } + private static readonly Colour4 transparent_black = Colour4.Black.Opacity(0.0f); + protected override Color4 ColourAt(float position) { if (position >= GlowPortion) return BarColour; - return Interpolation.ValueAt(position, Colour4.Black.Opacity(0.0f), GlowColour, 0.0, GlowPortion, Easing.InQuint); + return Interpolation.ValueAt(position, transparent_black, GlowColour, 0.0, GlowPortion, Easing.InQuint); } } } From 7c9adc7ad3c1d4c72620d92cb44bd665c6e8a28c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 10 Jan 2024 16:51:40 +0100 Subject: [PATCH 0917/1118] Fix incorrect score conversion on selected beatmaps due to incorrect `difficultyPeppyStars` rounding Fixes issue that occurs on *about* 246 beatmaps and was first described by me on discord: https://discord.com/channels/188630481301012481/188630652340404224/1154367700378865715 and then rediscovered again during work on https://github.com/ppy/osu/pull/26405: https://gist.github.com/bdach/414d5289f65b0399fa8f9732245a4f7c#venenog-on-ultmate-end-by-blacky-overdose-631 It so happens that in stable, due to .NET Framework internals, float math would be performed using x87 registers and opcodes. .NET (Core) however uses SSE instructions on 32- and 64-bit words. x87 registers are _80 bits_ wide. Which is notably wider than _both_ float and double. Therefore, on a significant number of beatmaps, the rounding would not produce correct values due to insufficient precision. See following gist for corroboration of the above: https://gist.github.com/bdach/dcde58d5a3607b0408faa3aa2b67bf10 Thus, to crudely - but, seemingly accurately, after checking across all ranked maps - emulate this, use `decimal`, which is slow, but has bigger precision than `double`. The single known exception beatmap in whose case this results in an incorrect result is https://osu.ppy.sh/beatmapsets/1156087#osu/2625853 which is considered an "acceptable casualty" of sorts. Doing this requires some fooling of the compiler / runtime (see second inline comment in new method). To corroborate that this is required, you can try the following code snippet: Console.WriteLine(string.Join(' ', BitConverter.GetBytes(1.3f).Select(x => x.ToString("X2")))); Console.WriteLine(string.Join(' ', BitConverter.GetBytes(1.3).Select(x => x.ToString("X2")))); Console.WriteLine(); decimal d1 = (decimal)1.3f; decimal d2 = (decimal)1.3; decimal d3 = (decimal)(double)1.3f; Console.WriteLine(string.Join(' ', decimal.GetBits(d1).SelectMany(BitConverter.GetBytes).Select(x => x.ToString("X2")))); Console.WriteLine(string.Join(' ', decimal.GetBits(d2).SelectMany(BitConverter.GetBytes).Select(x => x.ToString("X2")))); Console.WriteLine(string.Join(' ', decimal.GetBits(d3).SelectMany(BitConverter.GetBytes).Select(x => x.ToString("X2")))); which will print 66 66 A6 3F CD CC CC CC CC CC F4 3F 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 0D 00 00 00 00 00 00 00 00 00 00 00 00 00 01 00 8C 5D 89 FB 3B 76 00 00 00 00 00 00 00 00 0E 00 Note that despite `d1` being converted from a less-precise floating- -point value than `d2`, it still is represented 100% accurately as a decimal number. After applying this change, recomputation of legacy scoring attributes for *all* rulesets will be required. --- .../Difficulty/CatchLegacyScoreSimulator.cs | 9 ++--- .../Difficulty/OsuLegacyScoreSimulator.cs | 9 ++--- .../Difficulty/TaikoLegacyScoreSimulator.cs | 7 ++-- .../Objects/Legacy/LegacyRulesetExtensions.cs | 35 +++++++++++++++++++ osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs | 7 +++- 5 files changed, 47 insertions(+), 20 deletions(-) diff --git a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs index f65b6ef381..f931795ff2 100644 --- a/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Catch/Difficulty/CatchLegacyScoreSimulator.cs @@ -10,6 +10,7 @@ using osu.Game.Rulesets.Catch.Objects; using osu.Game.Rulesets.Catch.Scoring; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring.Legacy; @@ -62,13 +63,7 @@ namespace osu.Game.Rulesets.Catch.Difficulty drainLength = ((int)Math.Round(baseBeatmap.HitObjects[^1].StartTime) - (int)Math.Round(baseBeatmap.HitObjects[0].StartTime) - breakLength) / 1000; } - int difficultyPeppyStars = (int)Math.Round( - (baseBeatmap.Difficulty.DrainRate - + baseBeatmap.Difficulty.OverallDifficulty - + baseBeatmap.Difficulty.CircleSize - + Math.Clamp((float)objectCount / drainLength * 8, 0, 16)) / 38 * 5); - - scoreMultiplier = difficultyPeppyStars; + scoreMultiplier = LegacyRulesetExtensions.CalculateDifficultyPeppyStars(baseBeatmap.Difficulty, objectCount, drainLength); LegacyScoreAttributes attributes = new LegacyScoreAttributes(); diff --git a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs index a76054b42c..b808deab5c 100644 --- a/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Osu/Difficulty/OsuLegacyScoreSimulator.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; @@ -62,13 +63,7 @@ namespace osu.Game.Rulesets.Osu.Difficulty drainLength = ((int)Math.Round(baseBeatmap.HitObjects[^1].StartTime) - (int)Math.Round(baseBeatmap.HitObjects[0].StartTime) - breakLength) / 1000; } - int difficultyPeppyStars = (int)Math.Round( - (baseBeatmap.Difficulty.DrainRate - + baseBeatmap.Difficulty.OverallDifficulty - + baseBeatmap.Difficulty.CircleSize - + Math.Clamp((float)objectCount / drainLength * 8, 0, 16)) / 38 * 5); - - scoreMultiplier = difficultyPeppyStars; + scoreMultiplier = LegacyRulesetExtensions.CalculateDifficultyPeppyStars(baseBeatmap.Difficulty, objectCount, drainLength); LegacyScoreAttributes attributes = new LegacyScoreAttributes(); diff --git a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs index b20aa4f2b6..66ff0fc3d9 100644 --- a/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs +++ b/osu.Game.Rulesets.Taiko/Difficulty/TaikoLegacyScoreSimulator.cs @@ -7,6 +7,7 @@ using System.Linq; using osu.Game.Beatmaps; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Objects.Types; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Scoring.Legacy; @@ -65,11 +66,7 @@ namespace osu.Game.Rulesets.Taiko.Difficulty drainLength = ((int)Math.Round(baseBeatmap.HitObjects[^1].StartTime) - (int)Math.Round(baseBeatmap.HitObjects[0].StartTime) - breakLength) / 1000; } - difficultyPeppyStars = (int)Math.Round( - (baseBeatmap.Difficulty.DrainRate - + baseBeatmap.Difficulty.OverallDifficulty - + baseBeatmap.Difficulty.CircleSize - + Math.Clamp((float)objectCount / drainLength * 8, 0, 16)) / 38 * 5); + difficultyPeppyStars = LegacyRulesetExtensions.CalculateDifficultyPeppyStars(baseBeatmap.Difficulty, objectCount, drainLength); LegacyScoreAttributes attributes = new LegacyScoreAttributes(); diff --git a/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs index 53cf835248..2a5a11161b 100644 --- a/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs +++ b/osu.Game/Rulesets/Objects/Legacy/LegacyRulesetExtensions.cs @@ -57,5 +57,40 @@ namespace osu.Game.Rulesets.Objects.Legacy return (float)(1.0f - 0.7f * IBeatmapDifficultyInfo.DifficultyRange(circleSize)) / 2 * (applyFudge ? broken_gamefield_rounding_allowance : 1); } + + public static int CalculateDifficultyPeppyStars(BeatmapDifficulty difficulty, int objectCount, int drainLength) + { + /* + * WARNING: DO NOT TOUCH IF YOU DO NOT KNOW WHAT YOU ARE DOING + * + * It so happens that in stable, due to .NET Framework internals, float math would be performed + * using x87 registers and opcodes. + * .NET (Core) however uses SSE instructions on 32- and 64-bit words. + * x87 registers are _80 bits_ wide. Which is notably wider than _both_ float and double. + * Therefore, on a significant number of beatmaps, the rounding would not produce correct values. + * + * Thus, to crudely - but, seemingly *mostly* accurately, after checking across all ranked maps - emulate this, + * use `decimal`, which is slow, but has bigger precision than `double`. + * At the time of writing, there is _one_ ranked exception to this - namely https://osu.ppy.sh/beatmapsets/1156087#osu/2625853 - + * but it is considered an "acceptable casualty", since in that case scores aren't inflated by _that_ much compared to others. + */ + + decimal objectToDrainRatio = drainLength != 0 + ? Math.Clamp((decimal)objectCount / drainLength * 8, 0, 16) + : 16; + + /* + * Notably, THE `double` CASTS BELOW ARE IMPORTANT AND MUST REMAIN. + * Their goal is to trick the compiler / runtime into NOT promoting from single-precision float, as doing so would prompt it + * to attempt to "silently" fix the single-precision values when converting to decimal, + * which is NOT what the x87 FPU does. + */ + + decimal drainRate = (decimal)(double)difficulty.DrainRate; + decimal overallDifficulty = (decimal)(double)difficulty.OverallDifficulty; + decimal circleSize = (decimal)(double)difficulty.CircleSize; + + return (int)Math.Round((drainRate + overallDifficulty + circleSize + objectToDrainRatio) / 38 * 5); + } } } diff --git a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs index 110cf63e5c..389b20b5c8 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreEncoder.cs @@ -13,6 +13,7 @@ using osu.Game.Extensions; using osu.Game.IO.Legacy; using osu.Game.IO.Serialization; using osu.Game.Replays.Legacy; +using osu.Game.Rulesets.Objects.Legacy; using osu.Game.Rulesets.Replays; using osu.Game.Rulesets.Replays.Types; using SharpCompress.Compressors.LZMA; @@ -38,9 +39,13 @@ namespace osu.Game.Scoring.Legacy /// 30000009: Fix edge cases in conversion for scores which have 0.0x mod multiplier on stable. Reconvert all scores. /// 30000010: Fix mania score V1 conversion using score V1 accuracy rather than V2 accuracy. Reconvert all scores. /// 30000011: Re-do catch scoring to mirror stable Score V2 as closely as feasible. Reconvert all scores. + /// + /// 30000012: Fix incorrect total score conversion on selected beatmaps after implementing the more correct + /// method. Reconvert all scores. + /// /// /// - public const int LATEST_VERSION = 30000011; + public const int LATEST_VERSION = 30000012; /// /// The first stable-compatible YYYYMMDD format version given to lazer usage of replays. From 861080d3ae42de1b57e4471abc2f4f43a3026fc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 11 Jan 2024 10:04:37 +0100 Subject: [PATCH 0918/1118] Move simulated drain to separate test case Having it on at all times was causing other tests to fail. --- .../Visual/Gameplay/TestSceneArgonHealthDisplay.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs index 06d199513c..5d2921107e 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneArgonHealthDisplay.cs @@ -7,6 +7,7 @@ using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Shapes; using osu.Framework.Testing; +using osu.Framework.Threading; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu.Judgements; @@ -160,10 +161,14 @@ namespace osu.Game.Tests.Visual.Gameplay }); } - protected override void Update() + [Test] + public void TestSimulateDrain() { - base.Update(); - healthProcessor.Health.Value -= 0.0001f * Time.Elapsed; + ScheduledDelegate del = null!; + + AddStep("simulate drain", () => del = Scheduler.AddDelayed(() => healthProcessor.Health.Value -= 0.00025f * Time.Elapsed, 0, true)); + AddUntilStep("wait until zero", () => healthProcessor.Health.Value == 0); + AddStep("cancel drain", () => del.Cancel()); } } } From 600e4b6ef3c930ad8dc82f513c870582747e5bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Thu, 11 Jan 2024 10:17:32 +0100 Subject: [PATCH 0919/1118] Adjust skinnable health display test scene for usability --- .../Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs index f215a5d528..1849e8abd0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSkinnableHealthDisplay.cs @@ -21,7 +21,7 @@ namespace osu.Game.Tests.Visual.Gameplay [Cached(typeof(HealthProcessor))] private HealthProcessor healthProcessor = new DrainingHealthProcessor(0); - protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f), Width = 1f }; + protected override Drawable CreateArgonImplementation() => new ArgonHealthDisplay { Scale = new Vector2(0.6f), Width = 600, UseRelativeSize = { Value = false } }; protected override Drawable CreateDefaultImplementation() => new DefaultHealthDisplay { Scale = new Vector2(0.6f) }; protected override Drawable CreateLegacyImplementation() => new LegacyHealthDisplay { Scale = new Vector2(0.6f) }; From 90ab306a96e17b3d7e21011fb75acd9d753ba071 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 11 Jan 2024 14:55:04 +0300 Subject: [PATCH 0920/1118] Implement ArgonHealthDisplayBackground --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 11 +- .../ArgonHealthDisplayBackground.cs | 168 ++++++++++++++++++ 2 files changed, 178 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 236bd3366d..868ef9283d 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -19,6 +19,7 @@ using osu.Game.Configuration; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; +using osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; @@ -100,6 +101,14 @@ namespace osu.Game.Screens.Play.HUD background = new BackgroundPath { PathRadius = MAIN_PATH_RADIUS, + Alpha = 0, + AlwaysPresent = true + }, + new ArgonHealthDisplayBackground + { + RelativeSizeAxes = Axes.Both, + PathRadius = MAIN_PATH_RADIUS, + PathPadding = MAIN_PATH_RADIUS }, glowBar = new BarPath { @@ -121,7 +130,7 @@ namespace osu.Game.Screens.Play.HUD GlowColour = main_bar_glow_colour, PathRadius = MAIN_PATH_RADIUS, GlowPortion = 0.6f, - }, + } } }; } diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs new file mode 100644 index 0000000000..a819fb6f30 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs @@ -0,0 +1,168 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + +using System; +using System.Runtime.InteropServices; +using osu.Framework.Allocation; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osuTK; + +namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts +{ + public partial class ArgonHealthDisplayBackground : Box + { + private float endProgress = 1f; + + public float EndProgress + { + get => endProgress; + set + { + if (endProgress == value) + return; + + endProgress = value; + Invalidate(Invalidation.DrawNode); + } + } + + private float startProgress = 0f; + + public float StartProgress + { + get => startProgress; + set + { + if (startProgress == value) + return; + + startProgress = value; + Invalidate(Invalidation.DrawNode); + } + } + + private float radius = 10f; + + public float PathRadius + { + get => radius; + set + { + if (radius == value) + return; + + radius = value; + Invalidate(Invalidation.DrawNode); + } + } + + private float padding = 10f; + + public float PathPadding + { + get => padding; + set + { + if (padding == value) + return; + + padding = value; + Invalidate(Invalidation.DrawNode); + } + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders) + { + TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "ArgonBarPathBackground"); + } + + protected override void Update() + { + base.Update(); + Invalidate(Invalidation.DrawNode); + } + + protected override DrawNode CreateDrawNode() => new ArgonBarPathDrawNode(this); + + private class ArgonBarPathDrawNode : SpriteDrawNode + { + protected new ArgonHealthDisplayBackground Source => (ArgonHealthDisplayBackground)base.Source; + + public ArgonBarPathDrawNode(ArgonHealthDisplayBackground source) + : base(source) + { + } + + private Vector2 size; + private float startProgress; + private float endProgress; + private float pathRadius; + private float padding; + + public override void ApplyState() + { + base.ApplyState(); + + size = Source.DrawSize; + endProgress = Source.endProgress; + startProgress = Math.Min(Source.startProgress, endProgress); + pathRadius = Source.PathRadius; + padding = Source.PathPadding; + } + + protected override void Draw(IRenderer renderer) + { + if (pathRadius == 0) + return; + + base.Draw(renderer); + } + + private IUniformBuffer parametersBuffer; + + protected override void BindUniformResources(IShader shader, IRenderer renderer) + { + base.BindUniformResources(shader, renderer); + + parametersBuffer ??= renderer.CreateUniformBuffer(); + parametersBuffer.Data = new ArgonBarPathBackgroundParameters + { + Size = size, + StartProgress = startProgress, + EndProgress = endProgress, + PathRadius = pathRadius, + Padding = padding + }; + + shader.BindUniformBlock("m_ArgonBarPathBackgroundParameters", parametersBuffer); + } + + protected override bool CanDrawOpaqueInterior => false; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + parametersBuffer?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct ArgonBarPathBackgroundParameters + { + public UniformVector2 Size; + public UniformFloat StartProgress; + public UniformFloat EndProgress; + public UniformFloat PathRadius; + public UniformFloat Padding; + private UniformPadding8 pad; + } + } + } +} From f1db7db2592403d5783a63335362cf07a243bda7 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 11 Jan 2024 16:10:06 +0300 Subject: [PATCH 0921/1118] Implement ArgonHealthDisplayBar --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 211 +++------------- .../ArgonHealthDisplayBar.cs | 227 ++++++++++++++++++ 2 files changed, 265 insertions(+), 173 deletions(-) create mode 100644 osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 868ef9283d..9b91252a06 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -2,23 +2,18 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Caching; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Lines; using osu.Framework.Layout; using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Configuration; using osu.Game.Rulesets.Judgements; -using osu.Game.Rulesets.Objects; -using osu.Game.Rulesets.Objects.Types; using osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts; using osu.Game.Skinning; using osuTK; @@ -41,16 +36,14 @@ namespace osu.Game.Screens.Play.HUD [SettingSource("Use relative size")] public BindableBool UseRelativeSize { get; } = new BindableBool(true); - private BarPath mainBar = null!; + private ArgonHealthDisplayBar mainBar = null!; /// /// Used to show a glow at the end of the main bar, or red "damage" area when missing. /// - private BarPath glowBar = null!; + private ArgonHealthDisplayBar glowBar = null!; - private BackgroundPath background = null!; - - private SliderPath barPath = null!; + private Container content = null!; private static readonly Colour4 main_bar_colour = Colour4.White; private static readonly Colour4 main_bar_glow_colour = Color4Extensions.FromHex("#7ED7FD").Opacity(0.5f); @@ -59,23 +52,15 @@ namespace osu.Game.Screens.Play.HUD private bool displayingMiss => resetMissBarDelegate != null; - private readonly List vertices = new List(); - private double glowBarValue; private double healthBarValue; public const float MAIN_PATH_RADIUS = 10f; - - private const float curve_start_offset = 70; - private const float curve_end_offset = 40; private const float padding = MAIN_PATH_RADIUS * 2; - private const float curve_smoothness = 10; private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize); - private readonly Cached pathVerticesCache = new Cached(); - public ArgonHealthDisplay() { AddLayout(drawSizeLayout); @@ -93,42 +78,40 @@ namespace osu.Game.Screens.Play.HUD { AutoSizeAxes = Axes.Y; - InternalChild = new Container + InternalChild = content = new Container { - AutoSizeAxes = Axes.Both, Children = new Drawable[] { - background = new BackgroundPath - { - PathRadius = MAIN_PATH_RADIUS, - Alpha = 0, - AlwaysPresent = true - }, new ArgonHealthDisplayBackground { RelativeSizeAxes = Axes.Both, PathRadius = MAIN_PATH_RADIUS, PathPadding = MAIN_PATH_RADIUS }, - glowBar = new BarPath + new Container { - BarColour = Color4.White, - GlowColour = main_bar_glow_colour, - Blending = BlendingParameters.Additive, - Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White), - PathRadius = 40f, - // Kinda hacky, but results in correct positioning with increased path radius. - Margin = new MarginPadding(-30f), - GlowPortion = 0.9f, + RelativeSizeAxes = Axes.Both, + Padding = new MarginPadding(-30f), + Child = glowBar = new ArgonHealthDisplayBar + { + RelativeSizeAxes = Axes.Both, + BarColour = Color4.White, + GlowColour = main_bar_glow_colour, + Blending = BlendingParameters.Additive, + Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White), + PathRadius = 40f, + PathPadding = 40f, + GlowPortion = 0.9f, + } }, - mainBar = new BarPath + mainBar = new ArgonHealthDisplayBar { - AutoSizeAxes = Axes.None, RelativeSizeAxes = Axes.Both, Blending = BlendingParameters.Additive, BarColour = main_bar_colour, GlowColour = main_bar_glow_colour, PathRadius = MAIN_PATH_RADIUS, + PathPadding = MAIN_PATH_RADIUS, GlowPortion = 0.6f, } } @@ -151,7 +134,7 @@ namespace osu.Game.Screens.Play.HUD UseRelativeSize.BindValueChanged(v => RelativeSizeAxes = v.NewValue ? Axes.X : Axes.None, true); Width = previousWidth; - BarHeight.BindValueChanged(_ => updatePath(), true); + BarHeight.BindValueChanged(_ => updateContentSize(), true); } private void onNewJudgement(JudgementResult result) => pendingMissAnimation |= !result.IsHit; @@ -162,7 +145,7 @@ namespace osu.Game.Screens.Play.HUD if (!drawSizeLayout.IsValid) { - updatePath(); + updateContentSize(); drawSizeLayout.Validate(); } @@ -173,7 +156,7 @@ namespace osu.Game.Screens.Play.HUD mainBar.Alpha = (float)Interpolation.DampContinuously(mainBar.Alpha, Current.Value > 0 ? 1 : 0, 40, Time.Elapsed); glowBar.Alpha = (float)Interpolation.DampContinuously(glowBar.Alpha, glowBarValue > 0 ? 1 : 0, 40, Time.Elapsed); - updatePathVertices(); + updatePathProgress(); } protected override void HealthChanged(bool increase) @@ -203,10 +186,9 @@ namespace osu.Game.Screens.Play.HUD if (!displayingMiss) { - // TODO: REMOVE THIS. It's recreating textures. - glowBar.TransformTo(nameof(BarPath.GlowColour), Colour4.White, 30, Easing.OutQuint) + glowBar.TransformTo(nameof(ArgonHealthDisplayBar.GlowColour), Colour4.White, 30, Easing.OutQuint) .Then() - .TransformTo(nameof(BarPath.GlowColour), main_bar_glow_colour, 300, Easing.OutQuint); + .TransformTo(nameof(ArgonHealthDisplayBar.GlowColour), main_bar_glow_colour, 300, Easing.OutQuint); } } @@ -221,13 +203,11 @@ namespace osu.Game.Screens.Play.HUD finishMissDisplay(); }, out resetMissBarDelegate); - // TODO: REMOVE THIS. It's recreating textures. - glowBar.TransformTo(nameof(BarPath.BarColour), new Colour4(255, 147, 147, 255), 100, Easing.OutQuint).Then() - .TransformTo(nameof(BarPath.BarColour), new Colour4(255, 93, 93, 255), 800, Easing.OutQuint); + glowBar.TransformTo(nameof(ArgonHealthDisplayBar.BarColour), new Colour4(255, 147, 147, 255), 100, Easing.OutQuint).Then() + .TransformTo(nameof(ArgonHealthDisplayBar.BarColour), new Colour4(255, 93, 93, 255), 800, Easing.OutQuint); - // TODO: REMOVE THIS. It's recreating textures. - glowBar.TransformTo(nameof(BarPath.GlowColour), new Colour4(253, 0, 0, 255).Lighten(0.2f)) - .TransformTo(nameof(BarPath.GlowColour), new Colour4(253, 0, 0, 255), 800, Easing.OutQuint); + glowBar.TransformTo(nameof(ArgonHealthDisplayBar.GlowColour), new Colour4(253, 0, 0, 255).Lighten(0.2f)) + .TransformTo(nameof(ArgonHealthDisplayBar.GlowColour), new Colour4(253, 0, 0, 255), 800, Easing.OutQuint); } private void finishMissDisplay() @@ -237,53 +217,22 @@ namespace osu.Game.Screens.Play.HUD if (Current.Value > 0) { - // TODO: REMOVE THIS. It's recreating textures. - glowBar.TransformTo(nameof(BarPath.BarColour), main_bar_colour, 300, Easing.In); - glowBar.TransformTo(nameof(BarPath.GlowColour), main_bar_glow_colour, 300, Easing.In); + glowBar.TransformTo(nameof(ArgonHealthDisplayBar.BarColour), main_bar_colour, 300, Easing.In); + glowBar.TransformTo(nameof(ArgonHealthDisplayBar.GlowColour), main_bar_glow_colour, 300, Easing.In); } resetMissBarDelegate?.Cancel(); resetMissBarDelegate = null; } - private void updatePath() + private void updateContentSize() { float usableWidth = DrawWidth - padding; if (usableWidth < 0) enforceMinimumWidth(); - // the display starts curving at `curve_start_offset` units from the right and ends curving at `curve_end_offset`. - // to ensure that the curve is symmetric when it starts being narrow enough, add a `curve_end_offset` to the left side too. - const float rescale_cutoff = curve_start_offset + curve_end_offset; - - float barLength = Math.Max(DrawWidth - padding, rescale_cutoff); - float curveStart = barLength - curve_start_offset; - float curveEnd = barLength - curve_end_offset; - - Vector2 diagonalDir = (new Vector2(curveEnd, BarHeight.Value) - new Vector2(curveStart, 0)).Normalized(); - - barPath = new SliderPath(new[] - { - new PathControlPoint(new Vector2(0, 0), PathType.LINEAR), - new PathControlPoint(new Vector2(curveStart - curve_smoothness, 0), PathType.BEZIER), - new PathControlPoint(new Vector2(curveStart, 0)), - new PathControlPoint(new Vector2(curveStart, 0) + diagonalDir * curve_smoothness, PathType.LINEAR), - new PathControlPoint(new Vector2(curveEnd, BarHeight.Value) - diagonalDir * curve_smoothness, PathType.BEZIER), - new PathControlPoint(new Vector2(curveEnd, BarHeight.Value)), - new PathControlPoint(new Vector2(curveEnd + curve_smoothness, BarHeight.Value), PathType.LINEAR), - new PathControlPoint(new Vector2(barLength, BarHeight.Value)), - }); - - if (DrawWidth - padding < rescale_cutoff) - rescalePathProportionally(); - - barPath.GetPathToProgress(vertices, 0.0, 1.0); - - background.Vertices = vertices; - mainBar.Vertices = vertices; - glowBar.Vertices = vertices; - - updatePathVertices(); + content.Size = new Vector2(DrawWidth, BarHeight.Value + padding); + updatePathProgress(); void enforceMinimumWidth() { @@ -296,35 +245,13 @@ namespace osu.Game.Screens.Play.HUD RelativeSizeAxes = relativeAxes; } - - void rescalePathProportionally() - { - foreach (var point in barPath.ControlPoints) - point.Position = new Vector2(point.Position.X / barLength * (DrawWidth - padding), point.Position.Y); - } } - private void updatePathVertices() + private void updatePathProgress() { - barPath.GetPathToProgress(vertices, 0.0, healthBarValue); - if (vertices.Count == 0) vertices.Add(Vector2.Zero); - Vector2 initialVertex = vertices[0]; - for (int i = 0; i < vertices.Count; i++) - vertices[i] -= initialVertex; - - mainBar.Vertices = vertices; - mainBar.Position = initialVertex; - - barPath.GetPathToProgress(vertices, healthBarValue, Math.Max(glowBarValue, healthBarValue)); - if (vertices.Count == 0) vertices.Add(Vector2.Zero); - initialVertex = vertices[0]; - for (int i = 0; i < vertices.Count; i++) - vertices[i] -= initialVertex; - - glowBar.Vertices = vertices; - glowBar.Position = initialVertex; - - pathVerticesCache.Validate(); + mainBar.EndProgress = (float)healthBarValue; + glowBar.StartProgress = (float)healthBarValue; + glowBar.EndProgress = (float)Math.Max(glowBarValue, healthBarValue); } protected override void Dispose(bool isDisposing) @@ -334,67 +261,5 @@ namespace osu.Game.Screens.Play.HUD if (HealthProcessor.IsNotNull()) HealthProcessor.NewJudgement -= onNewJudgement; } - - private partial class BackgroundPath : SmoothPath - { - private static readonly Color4 colour_white = Color4.White.Opacity(0.8f); - private static readonly Color4 colour_black = Color4.Black.Opacity(0.2f); - - protected override Color4 ColourAt(float position) - { - if (position <= 0.16f) - return colour_white; - - return Interpolation.ValueAt(position, - colour_white, - colour_black, - -0.5f, 1f, Easing.OutQuint); - } - } - - private partial class BarPath : SmoothPath - { - private Colour4 barColour; - - public Colour4 BarColour - { - get => barColour; - set - { - if (barColour == value) - return; - - barColour = value; - InvalidateTexture(); - } - } - - private Colour4 glowColour; - - public Colour4 GlowColour - { - get => glowColour; - set - { - if (glowColour == value) - return; - - glowColour = value; - InvalidateTexture(); - } - } - - public float GlowPortion { get; init; } - - private static readonly Colour4 transparent_black = Colour4.Black.Opacity(0.0f); - - protected override Color4 ColourAt(float position) - { - if (position >= GlowPortion) - return BarColour; - - return Interpolation.ValueAt(position, transparent_black, GlowColour, 0.0, GlowPortion, Easing.InQuint); - } - } } } diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs new file mode 100644 index 0000000000..6bc2232776 --- /dev/null +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs @@ -0,0 +1,227 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +#nullable disable + +using System; +using System.Runtime.InteropServices; +using osu.Framework.Allocation; +using osu.Framework.Extensions.Color4Extensions; +using osu.Framework.Graphics; +using osu.Framework.Graphics.Rendering; +using osu.Framework.Graphics.Shaders; +using osu.Framework.Graphics.Shaders.Types; +using osu.Framework.Graphics.Shapes; +using osu.Framework.Graphics.Sprites; +using osuTK; +using osuTK.Graphics; + +namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts +{ + public partial class ArgonHealthDisplayBar : Box + { + private float endProgress = 1f; + + public float EndProgress + { + get => endProgress; + set + { + if (endProgress == value) + return; + + endProgress = value; + Invalidate(Invalidation.DrawNode); + } + } + + private float startProgress = 0f; + + public float StartProgress + { + get => startProgress; + set + { + if (startProgress == value) + return; + + startProgress = value; + Invalidate(Invalidation.DrawNode); + } + } + + private float radius = 10f; + + public float PathRadius + { + get => radius; + set + { + if (radius == value) + return; + + radius = value; + Invalidate(Invalidation.DrawNode); + } + } + + private float padding = 10f; + + public float PathPadding + { + get => padding; + set + { + if (padding == value) + return; + + padding = value; + Invalidate(Invalidation.DrawNode); + } + } + + private float glowPortion; + + public float GlowPortion + { + get => glowPortion; + set + { + if (glowPortion == value) + return; + + glowPortion = value; + Invalidate(Invalidation.DrawNode); + } + } + + private Colour4 barColour = Color4.White; + + public Colour4 BarColour + { + get => barColour; + set + { + if (barColour == value) + return; + + barColour = value; + Invalidate(Invalidation.DrawNode); + } + } + + private Colour4 glowColour = Color4.White.Opacity(0); + + public Colour4 GlowColour + { + get => glowColour; + set + { + if (glowColour == value) + return; + + glowColour = value; + Invalidate(Invalidation.DrawNode); + } + } + + [BackgroundDependencyLoader] + private void load(ShaderManager shaders) + { + TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "ArgonBarPath"); + } + + protected override void Update() + { + base.Update(); + Invalidate(Invalidation.DrawNode); + } + + protected override DrawNode CreateDrawNode() => new ArgonBarPathDrawNode(this); + + private class ArgonBarPathDrawNode : SpriteDrawNode + { + protected new ArgonHealthDisplayBar Source => (ArgonHealthDisplayBar)base.Source; + + public ArgonBarPathDrawNode(ArgonHealthDisplayBar source) + : base(source) + { + } + + private Vector2 size; + private float startProgress; + private float endProgress; + private float pathRadius; + private float padding; + private float glowPortion; + private Color4 barColour; + private Color4 glowColour; + + public override void ApplyState() + { + base.ApplyState(); + + size = Source.DrawSize; + endProgress = Source.endProgress; + startProgress = Math.Min(Source.startProgress, endProgress); + pathRadius = Source.PathRadius; + padding = Source.PathPadding; + glowPortion = Source.GlowPortion; + barColour = Source.barColour; + glowColour = Source.glowColour; + } + + protected override void Draw(IRenderer renderer) + { + if (pathRadius == 0) + return; + + base.Draw(renderer); + } + + private IUniformBuffer parametersBuffer; + + protected override void BindUniformResources(IShader shader, IRenderer renderer) + { + base.BindUniformResources(shader, renderer); + + parametersBuffer ??= renderer.CreateUniformBuffer(); + parametersBuffer.Data = new ArgonBarPathParameters + { + BarColour = new Vector4(barColour.R, barColour.G, barColour.B, barColour.A), + GlowColour = new Vector4(glowColour.R, glowColour.G, glowColour.B, glowColour.A), + GlowPortion = glowPortion, + Size = size, + StartProgress = startProgress, + EndProgress = endProgress, + PathRadius = pathRadius, + Padding = padding + }; + + shader.BindUniformBlock("m_ArgonBarPathParameters", parametersBuffer); + } + + protected override bool CanDrawOpaqueInterior => false; + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + parametersBuffer?.Dispose(); + } + + [StructLayout(LayoutKind.Sequential, Pack = 1)] + private record struct ArgonBarPathParameters + { + public UniformVector4 BarColour; + public UniformVector4 GlowColour; + public UniformVector2 Size; + public UniformFloat StartProgress; + public UniformFloat EndProgress; + public UniformFloat PathRadius; + public UniformFloat Padding; + public UniformFloat GlowPortion; + private UniformPadding4 pad; + } + } + } +} From d75bf55c586f72ec28ffc5ce228f6ec47338274f Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 11 Jan 2024 18:28:00 +0300 Subject: [PATCH 0922/1118] CI fixes --- .../ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs | 4 ++-- .../Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs index a819fb6f30..781bd8e6e9 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs @@ -33,7 +33,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts } } - private float startProgress = 0f; + private float startProgress; public float StartProgress { @@ -161,7 +161,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts public UniformFloat EndProgress; public UniformFloat PathRadius; public UniformFloat Padding; - private UniformPadding8 pad; + private readonly UniformPadding8 pad; } } } diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs index 6bc2232776..ac73698f4c 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs @@ -35,7 +35,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts } } - private float startProgress = 0f; + private float startProgress; public float StartProgress { @@ -220,7 +220,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts public UniformFloat PathRadius; public UniformFloat Padding; public UniformFloat GlowPortion; - private UniformPadding4 pad; + private readonly UniformPadding4 pad; } } } From bbb36da32312064bc2d154ae2637b44c670ef76c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 11 Jan 2024 18:58:12 +0300 Subject: [PATCH 0923/1118] Don't pass start and end progress to the background --- .../ArgonHealthDisplayBackground.cs | 40 ------------------- 1 file changed, 40 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs index 781bd8e6e9..ccb224eca6 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs @@ -3,7 +3,6 @@ #nullable disable -using System; using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -18,36 +17,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts { public partial class ArgonHealthDisplayBackground : Box { - private float endProgress = 1f; - - public float EndProgress - { - get => endProgress; - set - { - if (endProgress == value) - return; - - endProgress = value; - Invalidate(Invalidation.DrawNode); - } - } - - private float startProgress; - - public float StartProgress - { - get => startProgress; - set - { - if (startProgress == value) - return; - - startProgress = value; - Invalidate(Invalidation.DrawNode); - } - } - private float radius = 10f; public float PathRadius @@ -102,8 +71,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts } private Vector2 size; - private float startProgress; - private float endProgress; private float pathRadius; private float padding; @@ -112,8 +79,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts base.ApplyState(); size = Source.DrawSize; - endProgress = Source.endProgress; - startProgress = Math.Min(Source.startProgress, endProgress); pathRadius = Source.PathRadius; padding = Source.PathPadding; } @@ -136,8 +101,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts parametersBuffer.Data = new ArgonBarPathBackgroundParameters { Size = size, - StartProgress = startProgress, - EndProgress = endProgress, PathRadius = pathRadius, Padding = padding }; @@ -157,11 +120,8 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts private record struct ArgonBarPathBackgroundParameters { public UniformVector2 Size; - public UniformFloat StartProgress; - public UniformFloat EndProgress; public UniformFloat PathRadius; public UniformFloat Padding; - private readonly UniformPadding8 pad; } } } From 101a26a53e3da3e16ec26a5c89891261a2b998c9 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 12 Jan 2024 02:54:04 +0300 Subject: [PATCH 0924/1118] Update start and end progress in one go --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 5 +-- .../ArgonHealthDisplayBar.cs | 37 +++++-------------- 2 files changed, 11 insertions(+), 31 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 9b91252a06..1f633c9e82 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -249,9 +249,8 @@ namespace osu.Game.Screens.Play.HUD private void updatePathProgress() { - mainBar.EndProgress = (float)healthBarValue; - glowBar.StartProgress = (float)healthBarValue; - glowBar.EndProgress = (float)Math.Max(glowBarValue, healthBarValue); + mainBar.ProgressRange = new Vector2(0f, (float)healthBarValue); + glowBar.ProgressRange = new Vector2((float)healthBarValue, (float)Math.Max(glowBarValue, healthBarValue)); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs index ac73698f4c..f92ab529ef 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs @@ -20,32 +20,17 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts { public partial class ArgonHealthDisplayBar : Box { - private float endProgress = 1f; + private Vector2 progressRange = new Vector2(0f, 1f); - public float EndProgress + public Vector2 ProgressRange { - get => endProgress; + get => progressRange; set { - if (endProgress == value) + if (progressRange == value) return; - endProgress = value; - Invalidate(Invalidation.DrawNode); - } - } - - private float startProgress; - - public float StartProgress - { - get => startProgress; - set - { - if (startProgress == value) - return; - - startProgress = value; + progressRange = value; Invalidate(Invalidation.DrawNode); } } @@ -149,8 +134,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts } private Vector2 size; - private float startProgress; - private float endProgress; + private Vector2 progressRange; private float pathRadius; private float padding; private float glowPortion; @@ -162,8 +146,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts base.ApplyState(); size = Source.DrawSize; - endProgress = Source.endProgress; - startProgress = Math.Min(Source.startProgress, endProgress); + progressRange = new Vector2(Math.Min(Source.progressRange.X, Source.progressRange.Y), Source.progressRange.Y); pathRadius = Source.PathRadius; padding = Source.PathPadding; glowPortion = Source.GlowPortion; @@ -192,8 +175,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts GlowColour = new Vector4(glowColour.R, glowColour.G, glowColour.B, glowColour.A), GlowPortion = glowPortion, Size = size, - StartProgress = startProgress, - EndProgress = endProgress, + ProgressRange = progressRange, PathRadius = pathRadius, Padding = padding }; @@ -215,8 +197,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts public UniformVector4 BarColour; public UniformVector4 GlowColour; public UniformVector2 Size; - public UniformFloat StartProgress; - public UniformFloat EndProgress; + public UniformVector2 ProgressRange; public UniformFloat PathRadius; public UniformFloat Padding; public UniformFloat GlowPortion; From e861661037cce1079aee84e00b95efae50804873 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 12 Jan 2024 03:03:38 +0300 Subject: [PATCH 0925/1118] Remove invalidations in update oops --- .../ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs | 6 ------ .../HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs | 6 ------ 2 files changed, 12 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs index ccb224eca6..a98b3dc1f3 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs @@ -53,12 +53,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "ArgonBarPathBackground"); } - protected override void Update() - { - base.Update(); - Invalidate(Invalidation.DrawNode); - } - protected override DrawNode CreateDrawNode() => new ArgonBarPathDrawNode(this); private class ArgonBarPathDrawNode : SpriteDrawNode diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs index f92ab529ef..4f4af02d66 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs @@ -116,12 +116,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts TextureShader = shaders.Load(VertexShaderDescriptor.TEXTURE_2, "ArgonBarPath"); } - protected override void Update() - { - base.Update(); - Invalidate(Invalidation.DrawNode); - } - protected override DrawNode CreateDrawNode() => new ArgonBarPathDrawNode(this); private class ArgonBarPathDrawNode : SpriteDrawNode From c40462811363c7432ede91c4475d7d6e7cd6d28b Mon Sep 17 00:00:00 2001 From: Nitrous Date: Fri, 12 Jan 2024 15:12:02 +0800 Subject: [PATCH 0926/1118] move creation of `PlaybackSettings` to `ReplayPlayer` --- osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs | 9 +++++---- osu.Game/Screens/Play/Player.cs | 3 --- .../Screens/Play/PlayerSettings/PlaybackSettings.cs | 6 ------ osu.Game/Screens/Play/ReplayPlayer.cs | 12 ++++++++++++ osu.Game/Screens/Play/SpectatorPlayer.cs | 2 -- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index dbb0456cd0..b7f3dc36c3 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -12,17 +12,19 @@ namespace osu.Game.Screens.Play.HUD { private const int fade_duration = 200; - public readonly PlaybackSettings PlaybackSettings; - public readonly VisualSettings VisualSettings; + protected override Container Content => content; + + private readonly FillFlowContainer content; + public PlayerSettingsOverlay() { Anchor = Anchor.TopRight; Origin = Anchor.TopRight; AutoSizeAxes = Axes.Both; - Child = new FillFlowContainer + InternalChild = content = new FillFlowContainer { Anchor = Anchor.TopRight, Origin = Anchor.TopRight, @@ -31,7 +33,6 @@ namespace osu.Game.Screens.Play.HUD Spacing = new Vector2(0, 20), Children = new PlayerSettingsGroup[] { - PlaybackSettings = new PlaybackSettings { Expanded = { Value = false } }, VisualSettings = new VisualSettings { Expanded = { Value = false } }, new AudioSettings { Expanded = { Value = false } } } diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b87306b9a2..29c7849685 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -476,9 +476,6 @@ namespace osu.Game.Screens.Play skipOutroOverlay.Expire(); } - if (GameplayClockContainer is MasterGameplayClockContainer master) - HUDOverlay.PlayerSettingsOverlay.PlaybackSettings.UserPlaybackRate.BindTarget = master.UserPlaybackRate; - return container; } diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index e2859868bd..0c9f5bb6ee 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -27,7 +27,6 @@ namespace osu.Game.Screens.Play.PlayerSettings Precision = 0.1, }; - public readonly Bindable AllowControls = new BindableBool(true); private readonly PlayerSliderBar rateSlider; @@ -73,7 +72,6 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.FastBackward, Action = () => seek(-1, seek_fast_amount), - Enabled = { BindTarget = AllowControls }, }, new SeekButton { @@ -81,7 +79,6 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.Backward, Action = () => seek(-1, seek_amount), - Enabled = { BindTarget = AllowControls }, }, play = new IconButton { @@ -100,7 +97,6 @@ namespace osu.Game.Screens.Play.PlayerSettings gameplayClock.Start(); } }, - Enabled = { BindTarget = AllowControls }, }, new SeekButton { @@ -108,7 +104,6 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.Forward, Action = () => seek(1, seek_amount), - Enabled = { BindTarget = AllowControls }, }, new SeekButton { @@ -116,7 +111,6 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.FastForward, Action = () => seek(1, seek_fast_amount), - Enabled = { BindTarget = AllowControls }, }, }, }, diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index ca71a89b48..788eb75283 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -15,6 +15,7 @@ using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; using osu.Game.Screens.Play.HUD; +using osu.Game.Screens.Play.PlayerSettings; using osu.Game.Screens.Ranking; using osu.Game.Users; @@ -49,6 +50,17 @@ namespace osu.Game.Screens.Play this.createScore = createScore; } + protected override void LoadComplete() + { + base.LoadComplete(); + + var playerSettingsOverlay = new PlaybackSettings { Expanded = { Value = false } }; + HUDOverlay.PlayerSettingsOverlay.Add(playerSettingsOverlay); + + if (GameplayClockContainer is MasterGameplayClockContainer master) + playerSettingsOverlay.UserPlaybackRate.BindTarget = master.UserPlaybackRate; + } + protected override void PrepareReplay() { DrawableRuleset?.SetReplayScore(Score); diff --git a/osu.Game/Screens/Play/SpectatorPlayer.cs b/osu.Game/Screens/Play/SpectatorPlayer.cs index dd7418d563..2faead0ee1 100644 --- a/osu.Game/Screens/Play/SpectatorPlayer.cs +++ b/osu.Game/Screens/Play/SpectatorPlayer.cs @@ -68,8 +68,6 @@ namespace osu.Game.Screens.Play master.UserPlaybackRate.Value = 1; } }, true); - - HUDOverlay.PlayerSettingsOverlay.PlaybackSettings.AllowControls.Value = false; } /// From c545a9c242d56a499ddd6be46e69fd0730eed97e Mon Sep 17 00:00:00 2001 From: Nitrous Date: Fri, 12 Jan 2024 15:13:38 +0800 Subject: [PATCH 0927/1118] remove extra new line --- osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 0c9f5bb6ee..0fe3a08985 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -27,7 +27,6 @@ namespace osu.Game.Screens.Play.PlayerSettings Precision = 0.1, }; - private readonly PlayerSliderBar rateSlider; private readonly OsuSpriteText multiplierText; From ccbba8a00b7bcb7404cb31333742eafd21a5e62b Mon Sep 17 00:00:00 2001 From: Nitrous Date: Fri, 12 Jan 2024 17:19:59 +0800 Subject: [PATCH 0928/1118] Avoid NRE due to a beatmap loading with no hit objects. --- osu.Game/Screens/Play/ReplayPlayer.cs | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 788eb75283..f6e4ac489a 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -54,11 +54,14 @@ namespace osu.Game.Screens.Play { base.LoadComplete(); - var playerSettingsOverlay = new PlaybackSettings { Expanded = { Value = false } }; - HUDOverlay.PlayerSettingsOverlay.Add(playerSettingsOverlay); + if (HUDOverlay != null) + { + var playerSettingsOverlay = new PlaybackSettings { Expanded = { Value = false } }; + HUDOverlay.PlayerSettingsOverlay.Add(playerSettingsOverlay); - if (GameplayClockContainer is MasterGameplayClockContainer master) - playerSettingsOverlay.UserPlaybackRate.BindTarget = master.UserPlaybackRate; + if (GameplayClockContainer is MasterGameplayClockContainer master) + playerSettingsOverlay.UserPlaybackRate.BindTarget = master.UserPlaybackRate; + } } protected override void PrepareReplay() From 8d4ba6d4664ead9e095b1b5145b92b2dc93ebb81 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 12 Jan 2024 15:30:16 +0300 Subject: [PATCH 0929/1118] Remove PathPadding property --- .../Screens/Play/HUD/ArgonHealthDisplay.cs | 6 +-- .../ArgonHealthDisplayBackground.cs | 49 +------------------ .../ArgonHealthDisplayBar.cs | 23 +-------- 3 files changed, 5 insertions(+), 73 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 1f633c9e82..0af514a4f9 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -84,9 +84,7 @@ namespace osu.Game.Screens.Play.HUD { new ArgonHealthDisplayBackground { - RelativeSizeAxes = Axes.Both, - PathRadius = MAIN_PATH_RADIUS, - PathPadding = MAIN_PATH_RADIUS + RelativeSizeAxes = Axes.Both }, new Container { @@ -100,7 +98,6 @@ namespace osu.Game.Screens.Play.HUD Blending = BlendingParameters.Additive, Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White), PathRadius = 40f, - PathPadding = 40f, GlowPortion = 0.9f, } }, @@ -111,7 +108,6 @@ namespace osu.Game.Screens.Play.HUD BarColour = main_bar_colour, GlowColour = main_bar_glow_colour, PathRadius = MAIN_PATH_RADIUS, - PathPadding = MAIN_PATH_RADIUS, GlowPortion = 0.6f, } } diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs index a98b3dc1f3..a96c2f97bd 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs @@ -17,36 +17,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts { public partial class ArgonHealthDisplayBackground : Box { - private float radius = 10f; - - public float PathRadius - { - get => radius; - set - { - if (radius == value) - return; - - radius = value; - Invalidate(Invalidation.DrawNode); - } - } - - private float padding = 10f; - - public float PathPadding - { - get => padding; - set - { - if (padding == value) - return; - - padding = value; - Invalidate(Invalidation.DrawNode); - } - } - [BackgroundDependencyLoader] private void load(ShaderManager shaders) { @@ -65,24 +35,12 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts } private Vector2 size; - private float pathRadius; - private float padding; public override void ApplyState() { base.ApplyState(); size = Source.DrawSize; - pathRadius = Source.PathRadius; - padding = Source.PathPadding; - } - - protected override void Draw(IRenderer renderer) - { - if (pathRadius == 0) - return; - - base.Draw(renderer); } private IUniformBuffer parametersBuffer; @@ -94,9 +52,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts parametersBuffer ??= renderer.CreateUniformBuffer(); parametersBuffer.Data = new ArgonBarPathBackgroundParameters { - Size = size, - PathRadius = pathRadius, - Padding = padding + Size = size }; shader.BindUniformBlock("m_ArgonBarPathBackgroundParameters", parametersBuffer); @@ -114,8 +70,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts private record struct ArgonBarPathBackgroundParameters { public UniformVector2 Size; - public UniformFloat PathRadius; - public UniformFloat Padding; + private readonly UniformPadding8 pad; } } } diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs index 4f4af02d66..1938b97d5a 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs @@ -50,21 +50,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts } } - private float padding = 10f; - - public float PathPadding - { - get => padding; - set - { - if (padding == value) - return; - - padding = value; - Invalidate(Invalidation.DrawNode); - } - } - private float glowPortion; public float GlowPortion @@ -130,7 +115,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts private Vector2 size; private Vector2 progressRange; private float pathRadius; - private float padding; private float glowPortion; private Color4 barColour; private Color4 glowColour; @@ -142,7 +126,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts size = Source.DrawSize; progressRange = new Vector2(Math.Min(Source.progressRange.X, Source.progressRange.Y), Source.progressRange.Y); pathRadius = Source.PathRadius; - padding = Source.PathPadding; glowPortion = Source.GlowPortion; barColour = Source.barColour; glowColour = Source.glowColour; @@ -170,8 +153,7 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts GlowPortion = glowPortion, Size = size, ProgressRange = progressRange, - PathRadius = pathRadius, - Padding = padding + PathRadius = pathRadius }; shader.BindUniformBlock("m_ArgonBarPathParameters", parametersBuffer); @@ -193,9 +175,8 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts public UniformVector2 Size; public UniformVector2 ProgressRange; public UniformFloat PathRadius; - public UniformFloat Padding; public UniformFloat GlowPortion; - private readonly UniformPadding4 pad; + private readonly UniformPadding8 pad; } } } From c1e4e51a5fe02cdff146cd9a45d85ba5c5847992 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Fri, 12 Jan 2024 15:34:02 +0300 Subject: [PATCH 0930/1118] Add comment explaining negative container padding --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 0af514a4f9..dcc8f4feec 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -89,6 +89,7 @@ namespace osu.Game.Screens.Play.HUD new Container { RelativeSizeAxes = Axes.Both, + // since we are using bigger path radius we need to expand the draw area outwards to preserve the curve placement Padding = new MarginPadding(-30f), Child = glowBar = new ArgonHealthDisplayBar { From 6572fa4378811663971527a80ea86b9db1c94103 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Fri, 12 Jan 2024 14:59:15 +0100 Subject: [PATCH 0931/1118] Only validate playback rate when in submission context Temporary workaround for https://github.com/ppy/osu/issues/26404. It appears that some audio files do not behave well with BASS, leading BASS to report a contradictory state of affairs (i.e. a track that is in playing state but also not progressing). This appears to be related to seeking specifically, therefore only enable the validation of playback rate in the most sensitive contexts, namely when any sort of score submission is involved. --- .../Visual/Gameplay/TestScenePlayerScoreSubmission.cs | 6 ------ osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 2 +- osu.Game/Screens/Play/SubmittingPlayer.cs | 5 +++++ 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index 96cfcd61c3..f75a2656ef 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -21,7 +21,6 @@ using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko; using osu.Game.Scoring; -using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Tests.Beatmaps; @@ -360,11 +359,6 @@ namespace osu.Game.Tests.Visual.Gameplay AllowImportCompletion = new SemaphoreSlim(1); } - protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) => new MasterGameplayClockContainer(beatmap, gameplayStart) - { - ShouldValidatePlaybackRate = false, - }; - protected override async Task ImportScore(Score score) { ScoreImportStarted = true; diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 8b8bf87436..0d60ec4713 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -44,7 +44,7 @@ namespace osu.Game.Screens.Play /// Whether the audio playback rate should be validated. /// Mostly disabled for tests. /// - internal bool ShouldValidatePlaybackRate { get; init; } = true; + internal bool ShouldValidatePlaybackRate { get; init; } /// /// Whether the audio playback is within acceptable ranges. diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index fb3481cbc4..04abb6162f 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -61,6 +61,11 @@ namespace osu.Game.Screens.Play AddInternal(new PlayerTouchInputDetector()); } + protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) => new MasterGameplayClockContainer(beatmap, gameplayStart) + { + ShouldValidatePlaybackRate = true, + }; + protected override void LoadAsyncComplete() { base.LoadAsyncComplete(); From 58ade18c06524365545aa9adb2a3264e8ad73f80 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Jan 2024 04:49:50 +0900 Subject: [PATCH 0932/1118] Update framework --- osu.Android.props | 2 +- .../Overlays/Settings/Sections/Input/KeyBindingRow_KeyButton.cs | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index 56931bbcb4..e39143ab93 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index c180baeab7..9afeaf7338 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 882f49039029b7dc3e287ccc302d04de89de10df Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 01:32:37 +0100 Subject: [PATCH 0933/1118] lazy load slider tail position --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 8 ++------ osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs | 8 ++++---- osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs | 8 ++++++++ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 506145568e..2aca83ad03 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -98,7 +98,7 @@ namespace osu.Game.Rulesets.Osu.Objects set { repeatCount = value; - updateNestedPositions(); + endPositionCache.Invalidate(); } } @@ -165,7 +165,7 @@ namespace osu.Game.Rulesets.Osu.Objects public Slider() { SamplesBindable.CollectionChanged += (_, _) => UpdateNestedSamples(); - Path.Version.ValueChanged += _ => updateNestedPositions(); + Path.Version.ValueChanged += _ => endPositionCache.Invalidate(); } protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) @@ -218,7 +218,6 @@ namespace osu.Game.Rulesets.Osu.Objects { RepeatIndex = e.SpanIndex, StartTime = e.Time, - Position = EndPosition, StackHeight = StackHeight, ClassicSliderBehaviour = ClassicSliderBehaviour, }); @@ -245,9 +244,6 @@ namespace osu.Game.Rulesets.Osu.Objects if (HeadCircle != null) HeadCircle.Position = Position; - - if (TailCircle != null) - TailCircle.Position = EndPosition; } protected void UpdateNestedSamples() diff --git a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs index 88a34fcb8f..2d5a5b7727 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderEndCircle.cs @@ -14,16 +14,16 @@ namespace osu.Game.Rulesets.Osu.Objects /// public abstract class SliderEndCircle : HitCircle { - private readonly Slider slider; + protected readonly Slider Slider; protected SliderEndCircle(Slider slider) { - this.slider = slider; + Slider = slider; } public int RepeatIndex { get; set; } - public double SpanDuration => slider.SpanDuration; + public double SpanDuration => Slider.SpanDuration; protected override void ApplyDefaultsToSelf(ControlPointInfo controlPointInfo, IBeatmapDifficultyInfo difficulty) { @@ -40,7 +40,7 @@ namespace osu.Game.Rulesets.Osu.Objects else { // The first end circle should fade in with the slider. - TimePreempt += StartTime - slider.StartTime; + TimePreempt += StartTime - Slider.StartTime; } } diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index ceee513412..abe2c7074b 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -1,9 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; +using osuTK; namespace osu.Game.Rulesets.Osu.Objects { @@ -15,6 +17,12 @@ namespace osu.Game.Rulesets.Osu.Objects /// public bool ClassicSliderBehaviour; + public override Vector2 Position + { + get => Slider.EndPosition; + set => throw new NotImplementedException(); + } + public SliderTailCircle(Slider slider) : base(slider) { From 5fa7f6ec53a4430b1db13f74e75e2e56b4a181fc Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 02:21:32 +0100 Subject: [PATCH 0934/1118] make drawables that update from path version update once per frame --- .../PathControlPointConnectionPiece.cs | 16 ++++++++++++++-- .../Skinning/Default/PlaySliderBody.cs | 6 ++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index 67685d21a7..004a091cae 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -51,14 +51,26 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components base.LoadComplete(); hitObjectPosition = hitObject.PositionBindable.GetBoundCopy(); - hitObjectPosition.BindValueChanged(_ => updateConnectingPath()); + hitObjectPosition.BindValueChanged(_ => pathRequiresUpdate = true); pathVersion = hitObject.Path.Version.GetBoundCopy(); - pathVersion.BindValueChanged(_ => updateConnectingPath()); + pathVersion.BindValueChanged(_ => pathRequiresUpdate = true); updateConnectingPath(); } + private bool pathRequiresUpdate; + + protected override void Update() + { + base.Update(); + + if (!pathRequiresUpdate) return; + + updateConnectingPath(); + pathRequiresUpdate = false; + } + /// /// Updates the path connecting this control point to the next one. /// diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs index aa507cbaf0..cbc62a1c7c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs @@ -18,8 +18,6 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default protected IBindable AccentColourBindable { get; private set; } = null!; - private IBindable pathVersion = null!; - [Resolved(CanBeNull = true)] private OsuRulesetConfigManager? config { get; set; } @@ -33,8 +31,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default ScaleBindable = drawableSlider.ScaleBindable.GetBoundCopy(); ScaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true); - pathVersion = drawableSlider.PathVersion.GetBoundCopy(); - pathVersion.BindValueChanged(_ => Refresh()); + drawableObject.DefaultsApplied += _ => Refresh(); + drawableObject.HitObjectApplied += _ => Refresh(); AccentColourBindable = drawableObject.AccentColour.GetBoundCopy(); AccentColourBindable.BindValueChanged(accent => AccentColour = GetBodyAccentColour(skin, accent.NewValue), true); From e1186080b87436d78a591bb888ff8bfa93559dbf Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 02:24:33 +0100 Subject: [PATCH 0935/1118] simplify scheduling logic --- .../PathControlPointConnectionPiece.cs | 16 ++-------------- 1 file changed, 2 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs index 004a091cae..7e7d653dbd 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointConnectionPiece.cs @@ -51,26 +51,14 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components base.LoadComplete(); hitObjectPosition = hitObject.PositionBindable.GetBoundCopy(); - hitObjectPosition.BindValueChanged(_ => pathRequiresUpdate = true); + hitObjectPosition.BindValueChanged(_ => Scheduler.AddOnce(updateConnectingPath)); pathVersion = hitObject.Path.Version.GetBoundCopy(); - pathVersion.BindValueChanged(_ => pathRequiresUpdate = true); + pathVersion.BindValueChanged(_ => Scheduler.AddOnce(updateConnectingPath)); updateConnectingPath(); } - private bool pathRequiresUpdate; - - protected override void Update() - { - base.Update(); - - if (!pathRequiresUpdate) return; - - updateConnectingPath(); - pathRequiresUpdate = false; - } - /// /// Updates the path connecting this control point to the next one. /// From 0934cff501bd580119e386bbc09e1073a12fb272 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sat, 13 Jan 2024 05:22:41 +0900 Subject: [PATCH 0936/1118] Workaround implementation oversight See https://github.com/ppy/osu-framework/pull/6130. --- .../Settings/Sections/Input/KeyBindingRow_KeyButton.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow_KeyButton.cs b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow_KeyButton.cs index 3493baa2ad..adf05a71b9 100644 --- a/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow_KeyButton.cs +++ b/osu.Game/Overlays/Settings/Sections/Input/KeyBindingRow_KeyButton.cs @@ -140,7 +140,8 @@ namespace osu.Game.Overlays.Settings.Sections.Input /// A generated from the full input state. /// The key which triggered this update, and should be used as the binding. public void UpdateKeyCombination(KeyCombination fullState, InputKey triggerKey) => - UpdateKeyCombination(new KeyCombination(fullState.Keys.Where(KeyCombination.IsModifierKey).Append(triggerKey).ToArray())); + // TODO: Distinct() can be removed after https://github.com/ppy/osu-framework/pull/6130 is merged. + UpdateKeyCombination(new KeyCombination(fullState.Keys.Where(KeyCombination.IsModifierKey).Append(triggerKey).Distinct().ToArray())); public void UpdateKeyCombination(KeyCombination newCombination) { From 5303023e574c9e32239bd5afe4bc311241e07faa Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 13 Jan 2024 09:41:53 +0300 Subject: [PATCH 0937/1118] Add failing test case and fix selection assertion --- .../TestSceneFreeModSelectOverlay.cs | 47 +++++++++++++++++-- 1 file changed, 44 insertions(+), 3 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs index f1674401cd..a4feffddfb 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneFreeModSelectOverlay.cs @@ -10,12 +10,14 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Sprites; using osu.Framework.Input; using osu.Framework.Testing; using osu.Game.Overlays.Mods; using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Mods; using osu.Game.Screens.OnlinePlay; +using osu.Game.Utils; using osuTK.Input; namespace osu.Game.Tests.Visual.Multiplayer @@ -23,6 +25,7 @@ namespace osu.Game.Tests.Visual.Multiplayer public partial class TestSceneFreeModSelectOverlay : MultiplayerTestScene { private FreeModSelectOverlay freeModSelectOverlay; + private FooterButtonFreeMods footerButtonFreeMods; private readonly Bindable>> availableMods = new Bindable>>(); [BackgroundDependencyLoader] @@ -119,11 +122,46 @@ namespace osu.Game.Tests.Visual.Multiplayer AddAssert("select all button enabled", () => this.ChildrenOfType().Single().Enabled.Value); } + [Test] + public void TestSelectAllViaFooterButtonThenDeselectFromOverlay() + { + createFreeModSelect(); + + AddAssert("overlay select all button enabled", () => freeModSelectOverlay.ChildrenOfType().Single().Enabled.Value); + AddAssert("footer button displays off", () => footerButtonFreeMods.ChildrenOfType().Any(t => t.Text == "off")); + + AddStep("click footer select all button", () => + { + InputManager.MoveMouseTo(footerButtonFreeMods); + InputManager.Click(MouseButton.Left); + }); + + AddUntilStep("all mods selected", assertAllAvailableModsSelected); + AddAssert("footer button displays all", () => footerButtonFreeMods.ChildrenOfType().Any(t => t.Text == "all")); + + AddStep("click deselect all button", () => + { + InputManager.MoveMouseTo(this.ChildrenOfType().Single()); + InputManager.Click(MouseButton.Left); + }); + AddUntilStep("all mods deselected", () => !freeModSelectOverlay.SelectedMods.Value.Any()); + AddAssert("footer button displays off", () => footerButtonFreeMods.ChildrenOfType().Any(t => t.Text == "off")); + } + private void createFreeModSelect() { - AddStep("create free mod select screen", () => Child = freeModSelectOverlay = new FreeModSelectOverlay + AddStep("create free mod select screen", () => Children = new Drawable[] { - State = { Value = Visibility.Visible } + freeModSelectOverlay = new FreeModSelectOverlay + { + State = { Value = Visibility.Visible } + }, + footerButtonFreeMods = new FooterButtonFreeMods(freeModSelectOverlay) + { + Anchor = Anchor.BottomRight, + Origin = Anchor.BottomRight, + Current = { BindTarget = freeModSelectOverlay.SelectedMods }, + }, }); AddUntilStep("all column content loaded", () => freeModSelectOverlay.ChildrenOfType().Any() @@ -134,10 +172,13 @@ namespace osu.Game.Tests.Visual.Multiplayer { var allAvailableMods = availableMods.Value .Where(pair => pair.Key != ModType.System) - .SelectMany(pair => pair.Value) + .SelectMany(pair => ModUtils.FlattenMods(pair.Value)) .Where(mod => mod.UserPlayable && mod.HasImplementation) .ToList(); + if (freeModSelectOverlay.SelectedMods.Value.Count != allAvailableMods.Count) + return false; + foreach (var availableMod in allAvailableMods) { if (freeModSelectOverlay.SelectedMods.Value.All(selectedMod => selectedMod.GetType() != availableMod.GetType())) From c476843a837882bd6d3eb7164a20203a9e6d1774 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 13 Jan 2024 09:43:20 +0300 Subject: [PATCH 0938/1118] Mark system mods as invalid for selection in mod select overlay --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index baa7e594c1..f9875c5547 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -447,7 +447,7 @@ namespace osu.Game.Overlays.Mods private void filterMods() { foreach (var modState in AllAvailableMods) - modState.ValidForSelection.Value = modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod); + modState.ValidForSelection.Value = modState.Mod.Type != ModType.System && modState.Mod.HasImplementation && IsValidMod.Invoke(modState.Mod); } private void updateMultiplier() From aebf246f627b8658e94654d903c8d05bd23a190d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 13 Jan 2024 09:43:50 +0300 Subject: [PATCH 0939/1118] Change select all mod buttons to check `ValidForSelection` instead of directly checking system mods --- osu.Game/Overlays/Mods/SelectAllModsButton.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Mods/SelectAllModsButton.cs b/osu.Game/Overlays/Mods/SelectAllModsButton.cs index b6b3051a0d..1da762d164 100644 --- a/osu.Game/Overlays/Mods/SelectAllModsButton.cs +++ b/osu.Game/Overlays/Mods/SelectAllModsButton.cs @@ -41,8 +41,8 @@ namespace osu.Game.Overlays.Mods private void updateEnabledState() { Enabled.Value = availableMods.Value - .Where(pair => pair.Key != ModType.System) .SelectMany(pair => pair.Value) + .Where(modState => modState.ValidForSelection.Value) .Any(modState => !modState.Active.Value && modState.Visible); } } From 4cde8685d340006824edf7a17ca2a67323c98279 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 13 Jan 2024 11:16:22 +0300 Subject: [PATCH 0940/1118] Add failing test case --- .../Multiplayer/TestSceneMultiplayer.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 462286335a..201fac37c8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -29,7 +29,9 @@ using osu.Game.Rulesets.Catch; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Taiko; using osu.Game.Scoring; +using osu.Game.Screens.OnlinePlay; using osu.Game.Screens.OnlinePlay.Components; using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Lounge.Components; @@ -1009,6 +1011,54 @@ namespace osu.Game.Tests.Visual.Multiplayer } } + [Test] + public void TestGameplayStartsWhileInSongSelectWithDifferentRuleset() + { + createRoom(() => new Room + { + Name = { Value = "Test Room" }, + QueueMode = { Value = QueueMode.AllPlayers }, + Playlist = + { + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 0)).BeatmapInfo) + { + RulesetID = new OsuRuleset().RulesetInfo.OnlineID, + AllowedMods = new[] { new APIMod { Acronym = "HD" } }, + }, + new PlaylistItem(beatmaps.GetWorkingBeatmap(importedSet.Beatmaps.First(b => b.Ruleset.OnlineID == 1)).BeatmapInfo) + { + RulesetID = new TaikoRuleset().RulesetInfo.OnlineID, + AllowedMods = new[] { new APIMod { Acronym = "HD" } }, + }, + } + }); + + AddStep("join other user and make host", () => + { + multiplayerClient.AddUser(new APIUser { Id = 1234 }); + multiplayerClient.TransferHost(1234); + }); + + AddStep("select hidden", () => multiplayerClient.ChangeUserMods(new[] { new APIMod { Acronym = "HD" } })); + AddStep("make user ready", () => multiplayerClient.ChangeState(MultiplayerUserState.Ready)); + AddStep("press edit on second item", () => this.ChildrenOfType().Single(i => i.Item.RulesetID == 1) + .ChildrenOfType().Single().TriggerClick()); + + AddUntilStep("wait for song select", () => InputManager.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); + AddAssert("ruleset is taiko", () => Ruleset.Value.OnlineID == 1); + + AddStep("start match by other user", () => + { + multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Ready); + multiplayerClient.StartMatch().WaitSafely(); + }); + + AddUntilStep("wait for loading", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.WaitingForLoad); + AddStep("set player loaded", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Loaded)); + AddUntilStep("wait for gameplay to start", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.Playing); + AddAssert("hidden is selected", () => SelectedMods.Value, () => Has.One.TypeOf(typeof(OsuModHidden))); + } + private void enterGameplay() { pressReadyButton(); From c514550dfa0d3521b74d43aa2e0054a8b4325bdf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 13 Jan 2024 11:17:08 +0300 Subject: [PATCH 0941/1118] Fix multiplayer potentially selecting mods of wrong ruleset when starting match --- osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs | 8 ++++---- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 5 +++-- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs index 2cd8e45d28..f35b205bc4 100644 --- a/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Match/RoomSubScreen.cs @@ -75,7 +75,7 @@ namespace osu.Game.Screens.OnlinePlay.Match private BeatmapManager beatmapManager { get; set; } [Resolved] - private RulesetStore rulesets { get; set; } + protected RulesetStore Rulesets { get; private set; } [Resolved] private IAPIProvider api { get; set; } = null!; @@ -422,7 +422,7 @@ namespace osu.Game.Screens.OnlinePlay.Match if (selected == null) return; - var rulesetInstance = rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance(); + var rulesetInstance = Rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance(); Debug.Assert(rulesetInstance != null); var allowedMods = SelectedItem.Value.AllowedMods.Select(m => m.ToMod(rulesetInstance)); @@ -463,7 +463,7 @@ namespace osu.Game.Screens.OnlinePlay.Match if (SelectedItem.Value == null || !this.IsCurrentScreen()) return; - var rulesetInstance = rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance(); + var rulesetInstance = Rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance(); Debug.Assert(rulesetInstance != null); Mods.Value = UserMods.Value.Concat(SelectedItem.Value.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToList(); } @@ -473,7 +473,7 @@ namespace osu.Game.Screens.OnlinePlay.Match if (SelectedItem.Value == null || !this.IsCurrentScreen()) return; - Ruleset.Value = rulesets.GetRuleset(SelectedItem.Value.RulesetID); + Ruleset.Value = Rulesets.GetRuleset(SelectedItem.Value.RulesetID); } private void beginHandlingTrack() diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 56256bb15c..8a85a46a2f 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -241,8 +241,9 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer // update local mods based on room's reported status for the local user (omitting the base call implementation). // this makes the server authoritative, and avoids the local user potentially setting mods that the server is not aware of (ie. if the match was started during the selection being changed). - var ruleset = Ruleset.Value.CreateInstance(); - Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(ruleset)).Concat(SelectedItem.Value.RequiredMods.Select(m => m.ToMod(ruleset))).ToList(); + var rulesetInstance = Rulesets.GetRuleset(SelectedItem.Value.RulesetID)?.CreateInstance(); + Debug.Assert(rulesetInstance != null); + Mods.Value = client.LocalUser.Mods.Select(m => m.ToMod(rulesetInstance)).Concat(SelectedItem.Value.RequiredMods.Select(m => m.ToMod(rulesetInstance))).ToList(); } [Resolved(canBeNull: true)] From fca9b1f53630831c5ea3d56e09148c81b7ca9c57 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 12:50:39 +0100 Subject: [PATCH 0942/1118] Fix so it reacts to PathVersion with Scheduler --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 6 ++++++ osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs | 6 ++++-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 4099d47d61..2f012e33da 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -244,7 +244,13 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables else if (slidingSample.IsPlaying) slidingSample.Stop(); } + } + protected override void UpdateAfterChildren() + { + base.UpdateAfterChildren(); + + // It's important that this is done in UpdateAfterChildren so that the SliderBody has a chance to update its Size and PathOffset on Update. double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1); Ball.UpdateProgress(completionProgress); diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs index cbc62a1c7c..fb31f88d3c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/PlaySliderBody.cs @@ -18,6 +18,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default protected IBindable AccentColourBindable { get; private set; } = null!; + private IBindable pathVersion = null!; + [Resolved(CanBeNull = true)] private OsuRulesetConfigManager? config { get; set; } @@ -31,8 +33,8 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default ScaleBindable = drawableSlider.ScaleBindable.GetBoundCopy(); ScaleBindable.BindValueChanged(scale => PathRadius = OsuHitObject.OBJECT_RADIUS * scale.NewValue, true); - drawableObject.DefaultsApplied += _ => Refresh(); - drawableObject.HitObjectApplied += _ => Refresh(); + pathVersion = drawableSlider.PathVersion.GetBoundCopy(); + pathVersion.BindValueChanged(_ => Scheduler.AddOnce(Refresh)); AccentColourBindable = drawableObject.AccentColour.GetBoundCopy(); AccentColourBindable.BindValueChanged(accent => AccentColour = GetBodyAccentColour(skin, accent.NewValue), true); From ce643aa68f35369be1a975bb1ceb69fb54192cf2 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 13:54:04 +0100 Subject: [PATCH 0943/1118] revert overwriting Position getter in SliderTailCircle It would have very weird implications when combined with the position bindable which would be all wrong and stuff --- osu.Game.Rulesets.Osu/Objects/Slider.cs | 4 ++++ osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs | 8 -------- 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Slider.cs b/osu.Game.Rulesets.Osu/Objects/Slider.cs index 2aca83ad03..032f105ded 100644 --- a/osu.Game.Rulesets.Osu/Objects/Slider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Slider.cs @@ -218,6 +218,7 @@ namespace osu.Game.Rulesets.Osu.Objects { RepeatIndex = e.SpanIndex, StartTime = e.Time, + Position = EndPosition, StackHeight = StackHeight, ClassicSliderBehaviour = ClassicSliderBehaviour, }); @@ -244,6 +245,9 @@ namespace osu.Game.Rulesets.Osu.Objects if (HeadCircle != null) HeadCircle.Position = Position; + + if (TailCircle != null) + TailCircle.Position = EndPosition; } protected void UpdateNestedSamples() diff --git a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs index abe2c7074b..ceee513412 100644 --- a/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs +++ b/osu.Game.Rulesets.Osu/Objects/SliderTailCircle.cs @@ -1,11 +1,9 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; -using osuTK; namespace osu.Game.Rulesets.Osu.Objects { @@ -17,12 +15,6 @@ namespace osu.Game.Rulesets.Osu.Objects /// public bool ClassicSliderBehaviour; - public override Vector2 Position - { - get => Slider.EndPosition; - set => throw new NotImplementedException(); - } - public SliderTailCircle(Slider slider) : base(slider) { From b1fae2bc6a766a6301b8ebdabd3573697ef3020f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 13 Jan 2024 16:24:48 +0300 Subject: [PATCH 0944/1118] Add failing test case --- .../TestScenePlayerScoreSubmission.cs | 27 +++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index f75a2656ef..833cb24f41 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -15,11 +15,13 @@ using osu.Game.Online.Rooms; using osu.Game.Online.Solo; using osu.Game.Rulesets; using osu.Game.Rulesets.Mania; +using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko; +using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Scoring; using osu.Game.Screens.Ranking; using osu.Game.Tests.Beatmaps; @@ -34,12 +36,19 @@ namespace osu.Game.Tests.Visual.Gameplay private Func createCustomBeatmap; private Func createCustomRuleset; + private Func createCustomMods; private DummyAPIAccess dummyAPI => (DummyAPIAccess)API; protected override bool HasCustomSteps => true; - protected override TestPlayer CreatePlayer(Ruleset ruleset) => new FakeImportingPlayer(false); + protected override TestPlayer CreatePlayer(Ruleset ruleset) + { + if (createCustomMods != null) + SelectedMods.Value = SelectedMods.Value.Concat(createCustomMods()).ToList(); + + return new FakeImportingPlayer(false); + } protected new FakeImportingPlayer Player => (FakeImportingPlayer)base.Player; @@ -277,13 +286,27 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("ensure no submission", () => Player.SubmittedScore == null); } - private void createPlayerTest(bool allowFail = false, Func createBeatmap = null, Func createRuleset = null) + [Test] + public void TestNoSubmissionWithModsOfDifferentRuleset() + { + prepareTestAPI(true); + + createPlayerTest(createRuleset: () => new OsuRuleset(), createMods: () => new Mod[] { new TaikoModHidden() }); + + AddUntilStep("wait for token request", () => Player.TokenCreationRequested); + + AddStep("exit", () => Player.Exit()); + AddAssert("ensure no submission", () => Player.SubmittedScore == null); + } + + private void createPlayerTest(bool allowFail = false, Func createBeatmap = null, Func createRuleset = null, Func createMods = null) { CreateTest(() => AddStep("set up requirements", () => { this.allowFail = allowFail; createCustomBeatmap = createBeatmap; createCustomRuleset = createRuleset; + createCustomMods = createMods; })); } From 0c02062780312b56dd2f79301e097b1b21de146a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Sat, 13 Jan 2024 15:52:57 +0300 Subject: [PATCH 0945/1118] Add guard against starting gameplay with invalid mod instances Move guard to `Player` instead --- osu.Game/Screens/Play/Player.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b87306b9a2..ffd585148b 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -213,6 +213,12 @@ namespace osu.Game.Screens.Play if (playableBeatmap == null) return; + if (gameplayMods.Any(m => ruleset.AllMods.All(rulesetMod => m.GetType() != rulesetMod.GetType()))) + { + Logger.Log($@"Gameplay was started with a mod belonging to a ruleset different than '{ruleset.Description}'.", level: LogLevel.Important); + return; + } + mouseWheelDisabled = config.GetBindable(OsuSetting.MouseDisableWheel); if (game != null) From f5d6d52d4c9160affc348e4dd81cbb9a1f5ed352 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 14:47:40 +0100 Subject: [PATCH 0946/1118] Move logic for caching segments and updating path types to PathControlPointVisualiser --- .../Components/PathControlPointPiece.cs | 42 ---------------- .../Components/PathControlPointVisualiser.cs | 48 +++++++++++++++++++ 2 files changed, 48 insertions(+), 42 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 03792d8520..9b40b39a9d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -5,19 +5,15 @@ using System; using System.Collections.Generic; -using System.Linq; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; -using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Shapes; using osu.Framework.Input.Events; using osu.Framework.Localisation; -using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Types; @@ -56,27 +52,11 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private IBindable hitObjectPosition; private IBindable hitObjectScale; - [UsedImplicitly] - private readonly IBindable hitObjectVersion; - public PathControlPointPiece(T hitObject, PathControlPoint controlPoint) { this.hitObject = hitObject; ControlPoint = controlPoint; - // we don't want to run the path type update on construction as it may inadvertently change the hit object. - cachePoints(hitObject); - - hitObjectVersion = hitObject.Path.Version.GetBoundCopy(); - - // schedule ensure that updates are only applied after all operations from a single frame are applied. - // this avoids inadvertently changing the hit object path type for batch operations. - hitObjectVersion.BindValueChanged(_ => Scheduler.AddOnce(() => - { - cachePoints(hitObject); - updatePathType(); - })); - controlPoint.Changed += updateMarkerDisplay; Origin = Anchor.Centre; @@ -214,28 +194,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components protected override void OnDragEnd(DragEndEvent e) => DragEnded?.Invoke(); - private void cachePoints(T hitObject) => PointsInSegment = hitObject.Path.PointsInSegment(ControlPoint); - - /// - /// Handles correction of invalid path types. - /// - private void updatePathType() - { - if (ControlPoint.Type != PathType.PERFECT_CURVE) - return; - - if (PointsInSegment.Count > 3) - ControlPoint.Type = PathType.BEZIER; - - if (PointsInSegment.Count != 3) - return; - - ReadOnlySpan points = PointsInSegment.Select(p => p.Position).ToArray(); - RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points); - if (boundingBox.Width >= 640 || boundingBox.Height >= 480) - ControlPoint.Type = PathType.BEZIER; - } - /// /// Updates the state of the circular control point marker. /// diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index 24e2210b45..aae6275d45 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -9,15 +9,18 @@ using System.Collections.Specialized; using System.Diagnostics; using System.Linq; using Humanizer; +using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Cursor; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; +using osu.Framework.Utils; using osu.Game.Graphics.UserInterface; using osu.Game.Rulesets.Edit; using osu.Game.Rulesets.Objects; @@ -52,6 +55,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components [Resolved(CanBeNull = true)] private IDistanceSnapProvider distanceSnapProvider { get; set; } + [UsedImplicitly] + private readonly IBindable hitObjectVersion; + public PathControlPointVisualiser(T hitObject, bool allowSelection) { this.hitObject = hitObject; @@ -64,6 +70,48 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components Connections = new Container> { RelativeSizeAxes = Axes.Both }, Pieces = new Container> { RelativeSizeAxes = Axes.Both } }; + + hitObjectVersion = hitObject.Path.Version.GetBoundCopy(); + + // schedule ensure that updates are only applied after all operations from a single frame are applied. + // this avoids inadvertently changing the hit object path type for batch operations. + hitObjectVersion.BindValueChanged(_ => Scheduler.AddOnce(cachePointsAndEnsureValidPathTypes)); + } + + /// + /// Caches the PointsInSegment of Pieces and handles correction of invalid path types. + /// + private void cachePointsAndEnsureValidPathTypes() + { + List pointsInCurrentSegment = new List(); + + foreach (var controlPoint in controlPoints) + { + if (controlPoint.Type != null) + pointsInCurrentSegment = new List(); + + pointsInCurrentSegment.Add(controlPoint); + + // Pieces might not be ordered so we need to find the piece corresponding to the current control point. + Pieces.Single(o => o.ControlPoint == controlPoint).PointsInSegment = pointsInCurrentSegment; + } + + foreach (var piece in Pieces) + { + if (piece.ControlPoint.Type != PathType.PERFECT_CURVE) + return; + + if (piece.PointsInSegment.Count > 3) + piece.ControlPoint.Type = PathType.BEZIER; + + if (piece.PointsInSegment.Count != 3) + return; + + ReadOnlySpan points = piece.PointsInSegment.Select(p => p.Position).ToArray(); + RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points); + if (boundingBox.Width >= 640 || boundingBox.Height >= 480) + piece.ControlPoint.Type = PathType.BEZIER; + } } protected override void LoadComplete() From b4f9878b4656e83da770fcd00f1b70dd109e4a84 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 20:39:49 +0100 Subject: [PATCH 0947/1118] working jank solution --- .../Components/PathControlPointVisualiser.cs | 36 ++++++++++++------- osu.Game/Rulesets/Objects/SliderPath.cs | 4 +++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index aae6275d45..a02a07f2b6 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -72,10 +72,27 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components }; hitObjectVersion = hitObject.Path.Version.GetBoundCopy(); + } + + protected override void LoadComplete() + { + base.LoadComplete(); + + inputManager = GetContainingInputManager(); + + controlPoints.CollectionChanged += onControlPointsChanged; + controlPoints.BindTo(hitObject.Path.ControlPoints); // schedule ensure that updates are only applied after all operations from a single frame are applied. // this avoids inadvertently changing the hit object path type for batch operations. - hitObjectVersion.BindValueChanged(_ => Scheduler.AddOnce(cachePointsAndEnsureValidPathTypes)); + hitObject.Path.Validating += cachePointsAndEnsureValidPathTypes; + } + + protected override void Dispose(bool isDisposing) + { + base.Dispose(isDisposing); + + hitObject.Path.Validating -= cachePointsAndEnsureValidPathTypes; } /// @@ -88,7 +105,10 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components foreach (var controlPoint in controlPoints) { if (controlPoint.Type != null) + { + pointsInCurrentSegment.Add(controlPoint); pointsInCurrentSegment = new List(); + } pointsInCurrentSegment.Add(controlPoint); @@ -99,13 +119,13 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components foreach (var piece in Pieces) { if (piece.ControlPoint.Type != PathType.PERFECT_CURVE) - return; + continue; if (piece.PointsInSegment.Count > 3) piece.ControlPoint.Type = PathType.BEZIER; if (piece.PointsInSegment.Count != 3) - return; + continue; ReadOnlySpan points = piece.PointsInSegment.Select(p => p.Position).ToArray(); RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points); @@ -114,16 +134,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components } } - protected override void LoadComplete() - { - base.LoadComplete(); - - inputManager = GetContainingInputManager(); - - controlPoints.CollectionChanged += onControlPointsChanged; - controlPoints.BindTo(hitObject.Path.ControlPoints); - } - /// /// Selects the corresponding to the given , /// and deselects all other s. diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index dc71608132..bafc62ceda 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -25,6 +25,8 @@ namespace osu.Game.Rulesets.Objects private readonly Bindable version = new Bindable(); + public event Action? Validating; + /// /// The user-set distance of the path. If non-null, will match this value, /// and the path will be shortened/lengthened to match this length. @@ -233,6 +235,8 @@ namespace osu.Game.Rulesets.Objects if (pathCache.IsValid) return; + Validating?.Invoke(); + calculatePath(); calculateLength(); From da4d83f8ca2c7ccf5ff651c80df656393b817b7f Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 20:54:44 +0100 Subject: [PATCH 0948/1118] remove the need for caching points in segment --- .../Components/PathControlPointPiece.cs | 2 - .../Components/PathControlPointVisualiser.cs | 57 +++++++++---------- 2 files changed, 27 insertions(+), 32 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 9b40b39a9d..8ddc38c38e 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -37,8 +37,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components public Action DragInProgress; public Action DragEnded; - public List PointsInSegment; - public readonly BindableBool IsSelected = new BindableBool(); public readonly PathControlPoint ControlPoint; diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index a02a07f2b6..d057565c2b 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -9,7 +9,6 @@ using System.Collections.Specialized; using System.Diagnostics; using System.Linq; using Humanizer; -using JetBrains.Annotations; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -55,9 +54,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components [Resolved(CanBeNull = true)] private IDistanceSnapProvider distanceSnapProvider { get; set; } - [UsedImplicitly] - private readonly IBindable hitObjectVersion; - public PathControlPointVisualiser(T hitObject, bool allowSelection) { this.hitObject = hitObject; @@ -70,8 +66,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components Connections = new Container> { RelativeSizeAxes = Axes.Both }, Pieces = new Container> { RelativeSizeAxes = Axes.Both } }; - - hitObjectVersion = hitObject.Path.Version.GetBoundCopy(); } protected override void LoadComplete() @@ -85,20 +79,20 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components // schedule ensure that updates are only applied after all operations from a single frame are applied. // this avoids inadvertently changing the hit object path type for batch operations. - hitObject.Path.Validating += cachePointsAndEnsureValidPathTypes; + hitObject.Path.Validating += ensureValidPathTypes; } protected override void Dispose(bool isDisposing) { base.Dispose(isDisposing); - hitObject.Path.Validating -= cachePointsAndEnsureValidPathTypes; + hitObject.Path.Validating -= ensureValidPathTypes; } /// - /// Caches the PointsInSegment of Pieces and handles correction of invalid path types. + /// Handles correction of invalid path types. /// - private void cachePointsAndEnsureValidPathTypes() + private void ensureValidPathTypes() { List pointsInCurrentSegment = new List(); @@ -107,31 +101,33 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components if (controlPoint.Type != null) { pointsInCurrentSegment.Add(controlPoint); - pointsInCurrentSegment = new List(); + ensureValidPathType(pointsInCurrentSegment); + pointsInCurrentSegment.Clear(); } pointsInCurrentSegment.Add(controlPoint); - - // Pieces might not be ordered so we need to find the piece corresponding to the current control point. - Pieces.Single(o => o.ControlPoint == controlPoint).PointsInSegment = pointsInCurrentSegment; } - foreach (var piece in Pieces) - { - if (piece.ControlPoint.Type != PathType.PERFECT_CURVE) - continue; + ensureValidPathType(pointsInCurrentSegment); + } - if (piece.PointsInSegment.Count > 3) - piece.ControlPoint.Type = PathType.BEZIER; + private void ensureValidPathType(IReadOnlyList segment) + { + var first = segment[0]; - if (piece.PointsInSegment.Count != 3) - continue; + if (first.Type != PathType.PERFECT_CURVE) + return; - ReadOnlySpan points = piece.PointsInSegment.Select(p => p.Position).ToArray(); - RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points); - if (boundingBox.Width >= 640 || boundingBox.Height >= 480) - piece.ControlPoint.Type = PathType.BEZIER; - } + if (segment.Count > 3) + first.Type = PathType.BEZIER; + + if (segment.Count != 3) + return; + + ReadOnlySpan points = segment.Select(p => p.Position).ToArray(); + RectangleF boundingBox = PathApproximator.CircularArcBoundingBox(points); + if (boundingBox.Width >= 640 || boundingBox.Height >= 480) + first.Type = PathType.BEZIER; } /// @@ -298,7 +294,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components /// The path type we want to assign to the given control point piece. private void updatePathType(PathControlPointPiece piece, PathType? type) { - int indexInSegment = piece.PointsInSegment.IndexOf(piece.ControlPoint); + var pointsInSegment = hitObject.Path.PointsInSegment(piece.ControlPoint); + int indexInSegment = pointsInSegment.IndexOf(piece.ControlPoint); if (type?.Type == SplineType.PerfectCurve) { @@ -307,8 +304,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components // and one segment of the previous type. int thirdPointIndex = indexInSegment + 2; - if (piece.PointsInSegment.Count > thirdPointIndex + 1) - piece.PointsInSegment[thirdPointIndex].Type = piece.PointsInSegment[0].Type; + if (pointsInSegment.Count > thirdPointIndex + 1) + pointsInSegment[thirdPointIndex].Type = pointsInSegment[0].Type; } hitObject.Path.ExpectedDistance.Value = null; From 1e3c332658a320fb2dbf49e4a97e2927cafc4dcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sat, 13 Jan 2024 21:39:32 +0100 Subject: [PATCH 0949/1118] Fix broken installer Closes https://github.com/ppy/osu/issues/26510. Time for a rant. Technically, this "broke" with 9e8d07d3144bd4b072d28bd9bd0e255fee410de0, but it is actually an end result of upstream behaviours that I am failing to find a better description for than "utterly broken". Squirrel (the installer we use) has unit tests. Which is great, power to them. However, the method in which that testing is implemented leads to epic levels of WTF breakage. To determine whether Squirrel is being tested right now, it is checking all currently loaded assemblies, and determining that if any loaded assembly contains the magic string of "NUNIT" - among others - it must be being tested right now: https://github.com/clowd/Clowd.Squirrel/blob/24427217482deeeb9f2cacac555525edfc7bd9ac/src/Squirrel/SimpleSplat/PlatformModeDetector.cs#L17-L32 If one assumes that there is no conceivable way that an NUnit assembly *may* be loaded *without* it being a test context, this *may* seem sane. Foreshadowing. (Now, to avoid being hypocritical, we also do this, *but* we do this by checking if the *entry* assembly is an NUnit: https://github.com/ppy/osu-framework/blob/92db55a52742047b42c0b4b25327ce28bd991b44/osu.Framework/Development/DebugUtils.cs#L16-L34 which seems *much* saner, no?) Now, why did this break with 9e8d07d3144bd4b072d28bd9bd0e255fee410de0 *specifically*, you might wonder? Well the reason is this line: https://github.com/ppy/osu/blob/3d3f58c2523f519504d9156a684538e2aa0c094c/osu.Desktop/NVAPI.cs#L183 Yes you are reading this correctly, it's not NVAPI anything itself that breaks this, it is *a log statement*. To be precise, what the log statement *does* to provoke this, is calling into framework. That causes the framework assembly to load, *which* transitively loads the `nunit.framework` assembly. (If you ever find yourself wanting to find out this sort of cursed knowledge - I hope you never need to - you can run something along the lines of dotnet-trace collect --providers Microsoft-Windows-DotNETRuntime:4 -- .\osu!.exe then open the resulting trace in PerfView, and then search the `Microsoft-Windows-DotNETRuntime/AssemblyLoader/Start` log for the cursed assembly. In this case, the relevant entry said something along the lines of HasStack="True" ThreadID="23,924" ProcessorNumber="0" ClrInstanceID="6" AssemblyName="nunit.framework, Version=3.13.3.0, Culture=neutral, PublicKeyToken=2638cd05610744eb" AssemblyPath="" RequestingAssembly="osu.Framework, Version=2024.113.0.0, Culture=neutral, PublicKeyToken=null" AssemblyLoadContext="Default" RequestingAssemblyLoadContext="Default" ActivityID="/#21032/1/26/" Either that or just comment the log line for kicks. But the above is *much* faster.) Now, what *happens* if Squirrel "detects" that it is being "tested"? Well, it will refuse to close after executing the "hooks" defined via `SquirrelAwareApp`: https://github.com/clowd/Clowd.Squirrel/blob/24427217482deeeb9f2cacac555525edfc7bd9ac/src/Squirrel/SquirrelAwareApp.cs#L85-L88 and it will also refuse to create version shortcuts: https://github.com/clowd/Clowd.Squirrel/blob/24427217482deeeb9f2cacac555525edfc7bd9ac/src/Squirrel/UpdateManager.Shortcuts.cs#L63-L65 Sounds familiar, don't it? There are days on which I tire of computers. Today is one of them. --- osu.Desktop/Program.cs | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index a652e31f62..6b95a82703 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -30,12 +30,19 @@ namespace osu.Desktop [STAThread] public static void Main(string[] args) { - // NVIDIA profiles are based on the executable name of a process. - // Lazer and stable share the same executable name. - // Stable sets this setting to "Off", which may not be what we want, so let's force it back to the default "Auto" on startup. - NVAPI.ThreadedOptimisations = NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT; - - // run Squirrel first, as the app may exit after these run + /* + * WARNING: DO NOT PLACE **ANY** CODE ABOVE THE FOLLOWING BLOCK! + * + * Logic handling Squirrel MUST run before EVERYTHING if you do not want to break it. + * To be more precise: Squirrel is internally using a rather... crude method to determine whether it is running under NUnit, + * namely by checking loaded assemblies: + * https://github.com/clowd/Clowd.Squirrel/blob/24427217482deeeb9f2cacac555525edfc7bd9ac/src/Squirrel/SimpleSplat/PlatformModeDetector.cs#L17-L32 + * + * If it finds ANY assembly from the ones listed above - REGARDLESS of the reason why it is loaded - + * the app will then do completely broken things like: + * - not creating system shortcuts (as the logic is if'd out if "running tests") + * - not exiting after the install / first-update / uninstall hooks are ran (as the `Environment.Exit()` calls are if'd out if "running tests") + */ if (OperatingSystem.IsWindows()) { var windowsVersion = Environment.OSVersion.Version; @@ -59,6 +66,11 @@ namespace osu.Desktop setupSquirrel(); } + // NVIDIA profiles are based on the executable name of a process. + // Lazer and stable share the same executable name. + // Stable sets this setting to "Off", which may not be what we want, so let's force it back to the default "Auto" on startup. + NVAPI.ThreadedOptimisations = NvThreadControlSetting.OGL_THREAD_CONTROL_DEFAULT; + // Back up the cwd before DesktopGameHost changes it string cwd = Environment.CurrentDirectory; From 39908f5425d99d04e4c09b833c170193f3715205 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 22:39:09 +0100 Subject: [PATCH 0950/1118] remove Validating event and instead call validation explicitly on edits --- .../Components/PathControlPointVisualiser.cs | 20 ++++++++----------- .../Sliders/SliderPlacementBlueprint.cs | 2 ++ .../Sliders/SliderSelectionBlueprint.cs | 4 ++++ osu.Game/Rulesets/Objects/SliderPath.cs | 4 ---- 4 files changed, 14 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs index d057565c2b..b2d1709531 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointVisualiser.cs @@ -76,23 +76,12 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components controlPoints.CollectionChanged += onControlPointsChanged; controlPoints.BindTo(hitObject.Path.ControlPoints); - - // schedule ensure that updates are only applied after all operations from a single frame are applied. - // this avoids inadvertently changing the hit object path type for batch operations. - hitObject.Path.Validating += ensureValidPathTypes; - } - - protected override void Dispose(bool isDisposing) - { - base.Dispose(isDisposing); - - hitObject.Path.Validating -= ensureValidPathTypes; } /// /// Handles correction of invalid path types. /// - private void ensureValidPathTypes() + public void EnsureValidPathTypes() { List pointsInCurrentSegment = new List(); @@ -113,6 +102,9 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components private void ensureValidPathType(IReadOnlyList segment) { + if (segment.Count == 0) + return; + var first = segment[0]; if (first.Type != PathType.PERFECT_CURVE) @@ -394,6 +386,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components // Maintain the path types in case they got defaulted to bezier at some point during the drag. for (int i = 0; i < hitObject.Path.ControlPoints.Count; i++) hitObject.Path.ControlPoints[i].Type = dragPathTypes[i]; + + EnsureValidPathTypes(); } public void DragEnded() => changeHandler?.EndChange(); @@ -467,6 +461,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders.Components { foreach (var p in Pieces.Where(p => p.IsSelected.Value)) updatePathType(p, type); + + EnsureValidPathTypes(); }); if (countOfState == totalCount) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 28e972bacd..75ed818693 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -316,6 +316,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSlider() { + controlPointVisualiser.EnsureValidPathTypes(); + if (state == SliderPlacementState.Drawing) HitObject.Path.ExpectedDistance.Value = (float)HitObject.Path.CalculatedDistance; else diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs index b3efe1c495..3575e15d1d 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderSelectionBlueprint.cs @@ -254,6 +254,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders // Move the control points from the insertion index onwards to make room for the insertion controlPoints.Insert(insertionIndex, pathControlPoint); + ControlPointVisualiser?.EnsureValidPathTypes(); + HitObject.SnapTo(distanceSnapProvider); return pathControlPoint; @@ -275,6 +277,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders controlPoints.Remove(c); } + ControlPointVisualiser?.EnsureValidPathTypes(); + // Snap the slider to the current beat divisor before checking length validity. HitObject.SnapTo(distanceSnapProvider); diff --git a/osu.Game/Rulesets/Objects/SliderPath.cs b/osu.Game/Rulesets/Objects/SliderPath.cs index bafc62ceda..dc71608132 100644 --- a/osu.Game/Rulesets/Objects/SliderPath.cs +++ b/osu.Game/Rulesets/Objects/SliderPath.cs @@ -25,8 +25,6 @@ namespace osu.Game.Rulesets.Objects private readonly Bindable version = new Bindable(); - public event Action? Validating; - /// /// The user-set distance of the path. If non-null, will match this value, /// and the path will be shortened/lengthened to match this length. @@ -235,8 +233,6 @@ namespace osu.Game.Rulesets.Objects if (pathCache.IsValid) return; - Validating?.Invoke(); - calculatePath(); calculateLength(); From 83e108071a123650991fded528f64ad80421d155 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 22:51:33 +0100 Subject: [PATCH 0951/1118] fix wrong path type being displayed --- .../Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs index 75ed818693..0fa84c91fc 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/SliderPlacementBlueprint.cs @@ -267,6 +267,8 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders segmentStart.Type = PathType.BEZIER; break; } + + controlPointVisualiser.EnsureValidPathTypes(); } private void updateCursor() @@ -316,8 +318,6 @@ namespace osu.Game.Rulesets.Osu.Edit.Blueprints.Sliders private void updateSlider() { - controlPointVisualiser.EnsureValidPathTypes(); - if (state == SliderPlacementState.Drawing) HitObject.Path.ExpectedDistance.Value = (float)HitObject.Path.CalculatedDistance; else From 243b7b6fdad6aa28df1df8a06ecf574ebeff1238 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Sat, 13 Jan 2024 23:17:38 +0100 Subject: [PATCH 0952/1118] fix code quality --- .../Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs index 8ddc38c38e..e741d67e3b 100644 --- a/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs +++ b/osu.Game.Rulesets.Osu/Edit/Blueprints/Sliders/Components/PathControlPointPiece.cs @@ -4,7 +4,6 @@ #nullable disable using System; -using System.Collections.Generic; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Extensions.Color4Extensions; From 68496f7a0e865b3d1d74415d482e78a4747bd454 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Jan 2024 15:13:20 +0900 Subject: [PATCH 0953/1118] Fix scores not showing up on leaderboards during gameplay --- osu.Game/Screens/Select/PlaySongSelect.cs | 8 -------- 1 file changed, 8 deletions(-) diff --git a/osu.Game/Screens/Select/PlaySongSelect.cs b/osu.Game/Screens/Select/PlaySongSelect.cs index 4951504ff5..7b7b8857f3 100644 --- a/osu.Game/Screens/Select/PlaySongSelect.cs +++ b/osu.Game/Screens/Select/PlaySongSelect.cs @@ -146,14 +146,6 @@ namespace osu.Game.Screens.Select } } - public override void OnSuspending(ScreenTransitionEvent e) - { - // Scores will be refreshed on arriving at this screen. - // Clear them to avoid animation overload on returning to song select. - playBeatmapDetailArea.Leaderboard.ClearScores(); - base.OnSuspending(e); - } - public override void OnResuming(ScreenTransitionEvent e) { base.OnResuming(e); From b7d74fda88b98cd8b78df7ae5e907cc8b8fb9c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Jan 2024 09:10:39 +0100 Subject: [PATCH 0954/1118] Revert "Keep editor in frame stable mode when possible" --- .../TestSceneEditorAutoplayFastStreams.cs | 51 ------------------- .../Editor/TestSceneOsuComposerSelection.cs | 2 - .../Edit/DrawableEditorRulesetWrapper.cs | 21 +------- 3 files changed, 1 insertion(+), 73 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu.Tests/Editor/TestSceneEditorAutoplayFastStreams.cs diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneEditorAutoplayFastStreams.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneEditorAutoplayFastStreams.cs deleted file mode 100644 index cf5cd809ef..0000000000 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneEditorAutoplayFastStreams.cs +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -using System.Linq; -using NUnit.Framework; -using osu.Framework.Testing; -using osu.Game.Beatmaps; -using osu.Game.Rulesets.Osu.Objects; -using osu.Game.Rulesets.Osu.Objects.Drawables; -using osu.Game.Tests.Beatmaps; -using osu.Game.Tests.Visual; - -namespace osu.Game.Rulesets.Osu.Tests.Editor -{ - /// - /// This test covers autoplay working correctly in the editor on fast streams. - /// Might seem like a weird test, but frame stability being toggled can cause autoplay to operation incorrectly. - /// This is clearly a bug with the autoplay algorithm, but is worked around at an editor level for now. - /// - public partial class TestSceneEditorAutoplayFastStreams : EditorTestScene - { - protected override IBeatmap CreateBeatmap(RulesetInfo ruleset) - { - var testBeatmap = new TestBeatmap(ruleset, false); - testBeatmap.HitObjects.AddRange(new[] - { - new HitCircle { StartTime = 500 }, - new HitCircle { StartTime = 530 }, - new HitCircle { StartTime = 560 }, - new HitCircle { StartTime = 590 }, - new HitCircle { StartTime = 620 }, - }); - - return testBeatmap; - } - - protected override Ruleset CreateEditorRuleset() => new OsuRuleset(); - - [Test] - public void TestAllHit() - { - AddStep("start playback", () => EditorClock.Start()); - AddUntilStep("wait for all hit", () => - { - DrawableHitCircle[] hitCircles = Editor.ChildrenOfType().OrderBy(s => s.HitObject.StartTime).ToArray(); - - return hitCircles.Length == 5 && hitCircles.All(h => h.IsHit); - }); - } - } -} diff --git a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs index 366f17daee..623cefff6b 100644 --- a/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs +++ b/osu.Game.Rulesets.Osu.Tests/Editor/TestSceneOsuComposerSelection.cs @@ -46,12 +46,10 @@ namespace osu.Game.Rulesets.Osu.Tests.Editor moveMouseToObject(() => slider); AddStep("seek after end", () => EditorClock.Seek(750)); - AddUntilStep("wait for seek", () => !EditorClock.IsSeeking); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddAssert("slider not selected", () => EditorBeatmap.SelectedHitObjects.Count == 0); AddStep("seek to visible", () => EditorClock.Seek(650)); - AddUntilStep("wait for seek", () => !EditorClock.IsSeeking); AddStep("left click", () => InputManager.Click(MouseButton.Left)); AddUntilStep("slider selected", () => EditorBeatmap.SelectedHitObjects.Single() == slider); } diff --git a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs index ebf06bcc4e..174b278d89 100644 --- a/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs +++ b/osu.Game/Rulesets/Edit/DrawableEditorRulesetWrapper.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Extensions.ObjectExtensions; @@ -27,9 +26,6 @@ namespace osu.Game.Rulesets.Edit [Resolved] private EditorBeatmap beatmap { get; set; } = null!; - [Resolved] - private EditorClock editorClock { get; set; } = null!; - public DrawableEditorRulesetWrapper(DrawableRuleset drawableRuleset) { this.drawableRuleset = drawableRuleset; @@ -42,6 +38,7 @@ namespace osu.Game.Rulesets.Edit [BackgroundDependencyLoader] private void load() { + drawableRuleset.FrameStablePlayback = false; Playfield.DisplayJudgements.Value = false; } @@ -68,22 +65,6 @@ namespace osu.Game.Rulesets.Edit Scheduler.AddOnce(regenerateAutoplay); } - protected override void Update() - { - base.Update(); - - // Whenever possible, we want to stay in frame stability playback. - // Without doing so, we run into bugs with some gameplay elements not behaving as expected. - // - // Note that this is not using EditorClock.IsSeeking as that would exit frame stability - // on all seeks. The intention here is to retain frame stability for small seeks. - // - // I still think no gameplay elements should require frame stability in the first place, but maybe that ship has sailed already.. - bool shouldBypassFrameStability = Math.Abs(drawableRuleset.FrameStableClock.CurrentTime - editorClock.CurrentTime) > 1000; - - drawableRuleset.FrameStablePlayback = !shouldBypassFrameStability; - } - private void regenerateAutoplay() { var autoplayMod = drawableRuleset.Mods.OfType().Single(); From eecd868d66a2fe928b76756996d25e588b383309 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Jan 2024 09:24:50 +0100 Subject: [PATCH 0955/1118] Use darker blue for `SliderTailHit` result --- osu.Game/Graphics/OsuColour.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/OsuColour.cs b/osu.Game/Graphics/OsuColour.cs index 0d11d2d4ef..1b5877b966 100644 --- a/osu.Game/Graphics/OsuColour.cs +++ b/osu.Game/Graphics/OsuColour.cs @@ -95,6 +95,7 @@ namespace osu.Game.Graphics case HitResult.SmallTickHit: case HitResult.LargeTickHit: + case HitResult.SliderTailHit: case HitResult.Great: return Blue; From 1cd7656f331172a281b12552c62b1f52ca2bad1a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Jan 2024 09:33:04 +0100 Subject: [PATCH 0956/1118] Reorder hit results so that `SliderTailHit` is next to `SmallTickHit` This addresses https://github.com/ppy/osu/discussions/26507. --- osu.Game/Rulesets/Scoring/HitResult.cs | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/HitResult.cs b/osu.Game/Rulesets/Scoring/HitResult.cs index f307344347..7e58df3cfa 100644 --- a/osu.Game/Rulesets/Scoring/HitResult.cs +++ b/osu.Game/Rulesets/Scoring/HitResult.cs @@ -20,7 +20,7 @@ namespace osu.Game.Rulesets.Scoring /// [Description(@"")] [EnumMember(Value = "none")] - [Order(14)] + [Order(15)] None, /// @@ -71,7 +71,7 @@ namespace osu.Game.Rulesets.Scoring /// Indicates small tick miss. /// [EnumMember(Value = "small_tick_miss")] - [Order(11)] + [Order(12)] SmallTickMiss, /// @@ -87,7 +87,7 @@ namespace osu.Game.Rulesets.Scoring /// [EnumMember(Value = "large_tick_miss")] [Description("-")] - [Order(10)] + [Order(11)] LargeTickMiss, /// @@ -103,7 +103,7 @@ namespace osu.Game.Rulesets.Scoring /// [Description("S Bonus")] [EnumMember(Value = "small_bonus")] - [Order(9)] + [Order(10)] SmallBonus, /// @@ -111,7 +111,7 @@ namespace osu.Game.Rulesets.Scoring /// [Description("L Bonus")] [EnumMember(Value = "large_bonus")] - [Order(8)] + [Order(9)] LargeBonus, /// @@ -119,14 +119,14 @@ namespace osu.Game.Rulesets.Scoring /// [EnumMember(Value = "ignore_miss")] [Description("-")] - [Order(13)] + [Order(14)] IgnoreMiss, /// /// Indicates a hit that should be ignored for scoring purposes. /// [EnumMember(Value = "ignore_hit")] - [Order(12)] + [Order(13)] IgnoreHit, /// @@ -136,14 +136,14 @@ namespace osu.Game.Rulesets.Scoring /// May be paired with . /// [EnumMember(Value = "combo_break")] - [Order(15)] + [Order(16)] ComboBreak, /// /// A special judgement similar to that's used to increase the valuation of the final tick of a slider. /// [EnumMember(Value = "slider_tail_hit")] - [Order(16)] + [Order(8)] SliderTailHit, /// From f85e6add8e7ba95463622904d26202e140ed4434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Jan 2024 12:55:07 +0100 Subject: [PATCH 0957/1118] Add failing test case --- .../Mods/TestSceneOsuModFlashlight.cs | 56 +++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs index a353914cd5..defbb97c8a 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs @@ -1,8 +1,18 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Collections.Generic; +using System.Linq; using NUnit.Framework; +using osu.Framework.Testing; +using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Osu.Beatmaps; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Osu.Objects; +using osu.Game.Rulesets.Osu.Replays; +using osu.Game.Rulesets.Replays; +using osuTK; namespace osu.Game.Rulesets.Osu.Tests.Mods { @@ -21,5 +31,51 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods [Test] public void TestComboBasedSize([Values] bool comboBasedSize) => CreateModTest(new ModTestData { Mod = new OsuModFlashlight { ComboBasedSize = { Value = comboBasedSize } }, PassCondition = () => true }); + + [Test] + public void TestSliderDimsOnlyAfterStartTime() + { + bool sliderDimmedBeforeStartTime = false; + + CreateModTest(new ModTestData + { + Mod = new OsuModFlashlight(), + PassCondition = () => + { + sliderDimmedBeforeStartTime |= + Player.GameplayClockContainer.CurrentTime < 1000 && Player.ChildrenOfType.Flashlight>().Single().FlashlightDim > 0; + return Player.GameplayState.HasPassed && !sliderDimmedBeforeStartTime; + }, + Beatmap = new OsuBeatmap + { + HitObjects = new List + { + new HitCircle { StartTime = 0, }, + new Slider + { + StartTime = 1000, + Path = new SliderPath(new[] + { + new PathControlPoint(), + new PathControlPoint(new Vector2(100)) + }) + } + }, + BeatmapInfo = + { + StackLeniency = 0, + } + }, + ReplayFrames = new List + { + new OsuReplayFrame(0, new Vector2(), OsuAction.LeftButton), + new OsuReplayFrame(990, new Vector2()), + new OsuReplayFrame(1000, new Vector2(), OsuAction.LeftButton), + new OsuReplayFrame(2000, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(2001, new Vector2(100)), + }, + Autoplay = false, + }); + } } } From 0b2b1fc58888b430c16e81119fe2a277567775de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Jan 2024 13:05:02 +0100 Subject: [PATCH 0958/1118] Fix flashlight dim being applied before slider start time Closes https://github.com/ppy/osu/issues/26515. Compare https://github.com/ppy/osu/pull/26053. --- osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 252d7e2762..46c9bdd17f 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableHitObject(DrawableHitObject drawable) { if (drawable is DrawableSlider s) - s.Tracking.ValueChanged += flashlight.OnSliderTrackingChange; + s.Tracking.ValueChanged += _ => flashlight.OnSliderTrackingChange(s); } private partial class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition @@ -66,10 +66,10 @@ namespace osu.Game.Rulesets.Osu.Mods FlashlightSmoothness = 1.4f; } - public void OnSliderTrackingChange(ValueChangedEvent e) + public void OnSliderTrackingChange(DrawableSlider e) { // If a slider is in a tracking state, a further dim should be applied to the (remaining) visible portion of the playfield. - FlashlightDim = e.NewValue ? 0.8f : 0.0f; + FlashlightDim = Time.Current >= e.HitObject.StartTime && e.Tracking.Value ? 0.8f : 0.0f; } protected override bool OnMouseMove(MouseMoveEvent e) From baf3867e17c5d10e63433dcdf429095bce9deaa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Sun, 14 Jan 2024 13:48:47 +0100 Subject: [PATCH 0959/1118] Fix date failing to display on leaderboard for some scores with weird datetimes Addresses https://github.com/ppy/osu/discussions/26517. The score reported has a datetime of 0001/1/1 05:00:00 AM. Bit of a dodge fix but maybe fine? --- osu.Game/Extensions/TimeDisplayExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Extensions/TimeDisplayExtensions.cs b/osu.Game/Extensions/TimeDisplayExtensions.cs index 98633958ee..1b224cfeb7 100644 --- a/osu.Game/Extensions/TimeDisplayExtensions.cs +++ b/osu.Game/Extensions/TimeDisplayExtensions.cs @@ -59,7 +59,8 @@ namespace osu.Game.Extensions /// A short relative string representing the input time. public static string ToShortRelativeTime(this DateTimeOffset time, TimeSpan lowerCutoff) { - if (time == default) + // covers all `DateTimeOffset` instances with the date portion of 0001-01-01. + if (time.Date == default) return "-"; var now = DateTime.Now; From fb4f8d0834ee2dee64eda3e5ba8054faa26130eb Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 14 Jan 2024 22:31:48 +0900 Subject: [PATCH 0960/1118] Update framework --- osu.Android.props | 2 +- osu.Game/osu.Game.csproj | 2 +- osu.iOS.props | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Android.props b/osu.Android.props index e39143ab93..a7376aa5a7 100644 --- a/osu.Android.props +++ b/osu.Android.props @@ -10,7 +10,7 @@ true - + diff --git a/osu.iOS.props b/osu.iOS.props index 9afeaf7338..98e8b136e5 100644 --- a/osu.iOS.props +++ b/osu.iOS.props @@ -23,6 +23,6 @@ iossimulator-x64 - + From 724b4c9507b886fa8af21c6384d01ae0752305d4 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 14 Jan 2024 21:09:49 +0100 Subject: [PATCH 0961/1118] Expand click target of toolbar buttons and clock --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 92 ++++++++++--------- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 77 +++++++++------- .../Overlays/Toolbar/ToolbarHomeButton.cs | 2 +- .../Overlays/Toolbar/ToolbarMusicButton.cs | 2 +- .../Toolbar/ToolbarRulesetTabButton.cs | 2 +- .../Overlays/Toolbar/ToolbarSettingsButton.cs | 2 +- .../Overlays/Toolbar/ToolbarUserButton.cs | 2 +- 7 files changed, 97 insertions(+), 82 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 03a1cfc005..36ff703c82 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -63,6 +63,7 @@ namespace osu.Game.Overlays.Toolbar protected virtual Anchor TooltipAnchor => Anchor.TopLeft; + protected readonly Container ButtonContent; protected ConstrainedIconContainer IconContainer; protected SpriteText DrawableText; protected Box HoverBackground; @@ -80,59 +81,66 @@ namespace osu.Game.Overlays.Toolbar protected ToolbarButton() { - Width = Toolbar.HEIGHT; + AutoSizeAxes = Axes.X; RelativeSizeAxes = Axes.Y; - Padding = new MarginPadding(3); - Children = new Drawable[] { - BackgroundContent = new Container + ButtonContent = new Container { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 6, - CornerExponent = 3f, - Children = new Drawable[] - { - HoverBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(80).Opacity(180), - Blending = BlendingParameters.Additive, - Alpha = 0, - }, - flashBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Colour = Color4.White.Opacity(100), - Blending = BlendingParameters.Additive, - }, - } - }, - Flow = new FillFlowContainer - { - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Anchor = Anchor.TopCentre, - Origin = Anchor.TopCentre, - Padding = new MarginPadding { Left = Toolbar.HEIGHT / 2, Right = Toolbar.HEIGHT / 2 }, + Width = Toolbar.HEIGHT, RelativeSizeAxes = Axes.Y, - AutoSizeAxes = Axes.X, + Padding = new MarginPadding(3), Children = new Drawable[] { - IconContainer = new ConstrainedIconContainer + BackgroundContent = new Container { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, - Size = new Vector2(20), - Alpha = 0, + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 6, + CornerExponent = 3f, + Children = new Drawable[] + { + HoverBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(80).Opacity(180), + Blending = BlendingParameters.Additive, + Alpha = 0, + }, + flashBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Colour = Color4.White.Opacity(100), + Blending = BlendingParameters.Additive, + }, + } }, - DrawableText = new OsuSpriteText + Flow = new FillFlowContainer { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Anchor = Anchor.TopCentre, + Origin = Anchor.TopCentre, + Padding = new MarginPadding { Left = Toolbar.HEIGHT / 2, Right = Toolbar.HEIGHT / 2 }, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Children = new Drawable[] + { + IconContainer = new ConstrainedIconContainer + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + Size = new Vector2(20), + Alpha = 0, + }, + DrawableText = new OsuSpriteText + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + }, }, }, }, diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index 67688155ae..e7959986ae 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -42,52 +42,59 @@ namespace osu.Game.Overlays.Toolbar clockDisplayMode = config.GetBindable(OsuSetting.ToolbarClockDisplayMode); prefer24HourTime = config.GetBindable(OsuSetting.Prefer24HourTime); - Padding = new MarginPadding(3); - Children = new Drawable[] { new Container - { - RelativeSizeAxes = Axes.Both, - Masking = true, - CornerRadius = 6, - CornerExponent = 3f, - Children = new Drawable[] - { - hoverBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Colour = OsuColour.Gray(80).Opacity(180), - Blending = BlendingParameters.Additive, - Alpha = 0, - }, - flashBackground = new Box - { - RelativeSizeAxes = Axes.Both, - Alpha = 0, - Colour = Color4.White.Opacity(100), - Blending = BlendingParameters.Additive, - }, - } - }, - new FillFlowContainer { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, - Direction = FillDirection.Horizontal, - Spacing = new Vector2(5), - Padding = new MarginPadding(10), + Padding = new MarginPadding(3), Children = new Drawable[] { - analog = new AnalogClockDisplay + new Container { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Both, + Masking = true, + CornerRadius = 6, + CornerExponent = 3f, + Children = new Drawable[] + { + hoverBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Colour = OsuColour.Gray(80).Opacity(180), + Blending = BlendingParameters.Additive, + Alpha = 0, + }, + flashBackground = new Box + { + RelativeSizeAxes = Axes.Both, + Alpha = 0, + Colour = Color4.White.Opacity(100), + Blending = BlendingParameters.Additive, + }, + } }, - digital = new DigitalClockDisplay + new FillFlowContainer { - Anchor = Anchor.CentreLeft, - Origin = Anchor.CentreLeft, + RelativeSizeAxes = Axes.Y, + AutoSizeAxes = Axes.X, + Direction = FillDirection.Horizontal, + Spacing = new Vector2(5), + Padding = new MarginPadding(10), + Children = new Drawable[] + { + analog = new AnalogClockDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + }, + digital = new DigitalClockDisplay + { + Anchor = Anchor.CentreLeft, + Origin = Anchor.CentreLeft, + } + } } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs index 70675c1b92..499ca804c9 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarHomeButton.cs @@ -12,7 +12,7 @@ namespace osu.Game.Overlays.Toolbar { public ToolbarHomeButton() { - Width *= 1.4f; + ButtonContent.Width *= 1.4f; Hotkey = GlobalAction.Home; } diff --git a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs index 69597c6b46..5da0056787 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarMusicButton.cs @@ -28,7 +28,7 @@ namespace osu.Game.Overlays.Toolbar public ToolbarMusicButton() { Hotkey = GlobalAction.ToggleNowPlaying; - AutoSizeAxes = Axes.X; + ButtonContent.AutoSizeAxes = Axes.X; } [BackgroundDependencyLoader(true)] diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs index 5500f1c879..98a0191e0a 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Toolbar public RulesetButton() { - Padding = new MarginPadding(3) + ButtonContent.Padding = new MarginPadding(3) { Bottom = 5 }; diff --git a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs index 78df060252..899f58c9c0 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarSettingsButton.cs @@ -10,7 +10,7 @@ namespace osu.Game.Overlays.Toolbar { public ToolbarSettingsButton() { - Width *= 1.4f; + ButtonContent.Width *= 1.4f; Hotkey = GlobalAction.ToggleSettings; } diff --git a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs index 19b98628e6..230974cd59 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarUserButton.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Toolbar public ToolbarUserButton() { - AutoSizeAxes = Axes.X; + ButtonContent.AutoSizeAxes = Axes.X; } [BackgroundDependencyLoader] From 47b385c552462c98705e4a8a8c578cf6e8a42419 Mon Sep 17 00:00:00 2001 From: Susko3 Date: Sun, 14 Jan 2024 21:13:00 +0100 Subject: [PATCH 0962/1118] Move toolbar button padding to a const --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 4 +++- osu.Game/Overlays/Toolbar/ToolbarClock.cs | 2 +- osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 36ff703c82..1da2e1b744 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -26,6 +26,8 @@ namespace osu.Game.Overlays.Toolbar { public abstract partial class ToolbarButton : OsuClickableContainer, IKeyBindingHandler { + public const float PADDING = 3; + protected GlobalAction? Hotkey { get; set; } public void SetIcon(Drawable icon) @@ -90,7 +92,7 @@ namespace osu.Game.Overlays.Toolbar { Width = Toolbar.HEIGHT, RelativeSizeAxes = Axes.Y, - Padding = new MarginPadding(3), + Padding = new MarginPadding(PADDING), Children = new Drawable[] { BackgroundContent = new Container diff --git a/osu.Game/Overlays/Toolbar/ToolbarClock.cs b/osu.Game/Overlays/Toolbar/ToolbarClock.cs index e7959986ae..e1d658b811 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarClock.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarClock.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Toolbar { RelativeSizeAxes = Axes.Y, AutoSizeAxes = Axes.X, - Padding = new MarginPadding(3), + Padding = new MarginPadding(ToolbarButton.PADDING), Children = new Drawable[] { new Container diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs index 98a0191e0a..c224f0f9c9 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs @@ -48,7 +48,7 @@ namespace osu.Game.Overlays.Toolbar public RulesetButton() { - ButtonContent.Padding = new MarginPadding(3) + ButtonContent.Padding = new MarginPadding(PADDING) { Bottom = 5 }; From 0b5cc8fb10c88fb12650762a8b4fef78f406bde8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 14:01:03 +0900 Subject: [PATCH 0963/1118] Fix gameplay counter textures not being cached ahead of time Part of https://github.com/ppy/osu/issues/26535. --- osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs | 6 ++++++ osu.Game/Skinning/LegacySpriteText.cs | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index a11f2f01cd..f16669f865 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -137,6 +137,12 @@ namespace osu.Game.Screens.Play.HUD Spacing = new Vector2(-2f, 0f); Font = new FontUsage(font_name, 1); glyphStore = new GlyphStore(font_name, textures, getLookup); + + // cache common lookups ahead of time. + foreach (char c in new[] { '.', '%', 'x' }) + glyphStore.Get(font_name, c); + for (int i = 0; i < 10; i++) + glyphStore.Get(font_name, (char)('0' + i)); } protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); diff --git a/osu.Game/Skinning/LegacySpriteText.cs b/osu.Game/Skinning/LegacySpriteText.cs index 581e7534e4..fdd8716d5a 100644 --- a/osu.Game/Skinning/LegacySpriteText.cs +++ b/osu.Game/Skinning/LegacySpriteText.cs @@ -50,6 +50,12 @@ namespace osu.Game.Skinning Spacing = new Vector2(-skin.GetFontOverlap(font), 0); glyphStore = new LegacyGlyphStore(fontPrefix, skin, MaxSizePerGlyph); + + // cache common lookups ahead of time. + foreach (char c in FixedWidthExcludeCharacters) + glyphStore.Get(fontPrefix, c); + for (int i = 0; i < 10; i++) + glyphStore.Get(fontPrefix, (char)('0' + i)); } protected override TextBuilder CreateTextBuilder(ITexturedGlyphLookupStore store) => base.CreateTextBuilder(glyphStore); From cd20561843874df1eab24e179201eb1742e77520 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 14:12:10 +0900 Subject: [PATCH 0964/1118] Adjust text slightly --- osu.Game/Localisation/GraphicsSettingsStrings.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/osu.Game/Localisation/GraphicsSettingsStrings.cs b/osu.Game/Localisation/GraphicsSettingsStrings.cs index fc29352ae2..1d14b0a596 100644 --- a/osu.Game/Localisation/GraphicsSettingsStrings.cs +++ b/osu.Game/Localisation/GraphicsSettingsStrings.cs @@ -152,14 +152,13 @@ namespace osu.Game.Localisation /// /// "In order to change the renderer, the game will close. Please open it again." /// - public static LocalisableString ChangeRendererConfirmation => - new TranslatableString(getKey(@"change_renderer_configuration"), @"In order to change the renderer, the game will close. Please open it again."); + public static LocalisableString ChangeRendererConfirmation => new TranslatableString(getKey(@"change_renderer_configuration"), @"In order to change the renderer, the game will close. Please open it again."); /// - /// "Minimise on focus loss" + /// "Minimise osu! when switching to another app" /// - public static LocalisableString MinimiseOnFocusLoss => new TranslatableString(getKey(@"minimise_on_focus_loss"), @"Minimise on focus loss"); + public static LocalisableString MinimiseOnFocusLoss => new TranslatableString(getKey(@"minimise_on_focus_loss"), @"Minimise osu! when switching to another app"); - private static string getKey(string key) => $"{prefix}:{key}"; + private static string getKey(string key) => $@"{prefix}:{key}"; } } From a6c309b61a1edc109dfe0b3b2ff0dca57564628d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 14:12:39 +0900 Subject: [PATCH 0965/1118] Add more keywords --- osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs index adb572e245..71afec88d4 100644 --- a/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs +++ b/osu.Game/Overlays/Settings/Sections/Graphics/LayoutSettings.cs @@ -111,7 +111,7 @@ namespace osu.Game.Overlays.Settings.Sections.Graphics { LabelText = GraphicsSettingsStrings.MinimiseOnFocusLoss, Current = config.GetBindable(FrameworkSetting.MinimiseOnFocusLossInFullscreen), - Keywords = new[] { "alt-tab" }, + Keywords = new[] { "alt-tab", "minimize", "focus", "hide" }, }, safeAreaConsiderationsCheckbox = new SettingsCheckbox { From 6940579b9edb8c275de74c0f1ba4780f45577aaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 14:30:27 +0900 Subject: [PATCH 0966/1118] Remember multiplayer room filter mode As proposed in https://github.com/ppy/osu/discussions/26218. --- osu.Game/Configuration/OsuConfigManager.cs | 4 ++++ osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 6 +++++- .../OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs | 2 ++ 3 files changed, 11 insertions(+), 1 deletion(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index 23686db1f8..c05831d043 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -17,6 +17,7 @@ using osu.Game.Localisation; using osu.Game.Overlays; using osu.Game.Overlays.Mods.Input; using osu.Game.Rulesets.Scoring; +using osu.Game.Screens.OnlinePlay.Lounge.Components; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Filter; using osu.Game.Skinning; @@ -191,6 +192,8 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.EditorLimitedDistanceSnap, false); SetDefault(OsuSetting.EditorShowSpeedChanges, false); + SetDefault(OsuSetting.MultiplayerRoomFilter, RoomPermissionsFilter.All); + SetDefault(OsuSetting.LastProcessedMetadataId, -1); SetDefault(OsuSetting.ComboColourNormalisationAmount, 0.2f, 0f, 1f, 0.01f); @@ -423,5 +426,6 @@ namespace osu.Game.Configuration TouchDisableGameplayTaps, ModSelectTextSearchStartsActive, UserOnlineStatus, + MultiplayerRoomFilter } } diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index fc4a5357c6..6f5ff9c99c 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -4,8 +4,8 @@ #nullable disable using System; -using System.Diagnostics; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; using JetBrains.Annotations; using osu.Framework.Allocation; @@ -19,6 +19,7 @@ using osu.Framework.Input.Events; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Threading; +using osu.Game.Configuration; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Input; @@ -77,6 +78,9 @@ namespace osu.Game.Screens.OnlinePlay.Lounge [CanBeNull] private LeasedBindable selectionLease; + [Resolved] + protected OsuConfigManager Config { get; private set; } + private readonly Bindable filter = new Bindable(new FilterCriteria()); private readonly IBindable operationInProgress = new Bindable(); private readonly IBindable isIdle = new BindableBool(); diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs index 4478179726..a3a6fd2d8e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerLoungeSubScreen.cs @@ -12,6 +12,7 @@ using osu.Framework.Logging; using osu.Framework.Screens; using osu.Framework.Graphics; using osu.Framework.Graphics.UserInterface; +using osu.Game.Configuration; using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.Multiplayer; @@ -51,6 +52,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer roomAccessTypeDropdown = new SlimEnumDropdown { RelativeSizeAxes = Axes.None, + Current = Config.GetBindable(OsuSetting.MultiplayerRoomFilter), Width = 160, }; From d8962ddff8e75a23114266db68de5f6e54eac264 Mon Sep 17 00:00:00 2001 From: Felipe Marins Date: Mon, 15 Jan 2024 03:22:52 -0300 Subject: [PATCH 0967/1118] Select all when pressing enter instead of every mod selection change --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 43f44d682d..8cb990757f 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -565,9 +565,6 @@ namespace osu.Game.Overlays.Mods .ToArray(); SelectedMods.Value = ComputeNewModsFromSelection(SelectedMods.Value, candidateSelection); - - if (SearchTextBox.HasFocus) - SearchTextBox.SelectAll(); } #region Transition handling @@ -724,6 +721,8 @@ namespace osu.Game.Overlays.Mods if (firstMod is not null) firstMod.Active.Value = !firstMod.Active.Value; + SearchTextBox.SelectAll(); + return true; } } From 2d5a39b234b85f9fe235bb746f50185c0e336b55 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 16:01:34 +0900 Subject: [PATCH 0968/1118] Add failing test coverage of duplicates in judgement counter display --- .../Visual/Gameplay/TestSceneJudgementCounter.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneJudgementCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneJudgementCounter.cs index dae6dc7b4b..787c48fb26 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneJudgementCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneJudgementCounter.cs @@ -147,6 +147,16 @@ namespace osu.Game.Tests.Visual.Gameplay AddAssert("Assert max judgement hidden", () => counterDisplay.CounterFlow.ChildrenOfType().First().Alpha == 0); } + [Test] + public void TestNoDuplicates() + { + AddStep("create counter", () => Child = counterDisplay = new TestJudgementCounterDisplay()); + AddStep("Show all judgements", () => counterDisplay.Mode.Value = JudgementCounterDisplay.DisplayMode.All); + AddAssert("Check no duplicates", + () => counterDisplay.CounterFlow.ChildrenOfType().Count(), + () => Is.EqualTo(counterDisplay.CounterFlow.ChildrenOfType().Select(c => c.ResultName.Text).Distinct().Count())); + } + [Test] public void TestCycleDisplayModes() { From e6453853c256245a2d600904b9df29a963268635 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 15:54:00 +0900 Subject: [PATCH 0969/1118] De-dupe displayed hits in judgement counter --- .../Gameplay/TestSceneJudgementCounter.cs | 2 +- .../JudgementCountController.cs | 36 ++++++++++++++----- .../HUD/JudgementCounter/JudgementCounter.cs | 7 ++-- .../JudgementCounterDisplay.cs | 9 +++-- 4 files changed, 39 insertions(+), 15 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneJudgementCounter.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneJudgementCounter.cs index 787c48fb26..894f08e5b2 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneJudgementCounter.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneJudgementCounter.cs @@ -173,7 +173,7 @@ namespace osu.Game.Tests.Visual.Gameplay private int hiddenCount() { - var num = counterDisplay.CounterFlow.Children.First(child => child.Result.Type == HitResult.LargeTickHit); + var num = counterDisplay.CounterFlow.Children.First(child => child.Result.Types.Contains(HitResult.LargeTickHit)); return num.Result.ResultCount.Value; } diff --git a/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCountController.cs b/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCountController.cs index 43c2ae442a..8134c97bac 100644 --- a/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCountController.cs +++ b/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCountController.cs @@ -6,6 +6,7 @@ using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; +using osu.Framework.Localisation; using osu.Game.Rulesets; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -21,18 +22,30 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter [Resolved] private ScoreProcessor scoreProcessor { get; set; } = null!; - public List Results = new List(); + private readonly Dictionary results = new Dictionary(); + + public IEnumerable Counters => counters; + + private readonly List counters = new List(); [BackgroundDependencyLoader] private void load(IBindable ruleset) { - foreach (var result in ruleset.Value.CreateInstance().GetHitResults()) + // Due to weirdness in judgements, some results have the same name and should be aggregated for display purposes. + // There's only one case of this right now ("slider end"). + foreach (var group in ruleset.Value.CreateInstance().GetHitResults().GroupBy(r => r.displayName)) { - Results.Add(new JudgementCount + var judgementCount = new JudgementCount { - Type = result.result, + DisplayName = group.Key, + Types = group.Select(r => r.result).ToArray(), ResultCount = new BindableInt() - }); + }; + + counters.Add(judgementCount); + + foreach (var r in group) + results[r.result] = judgementCount; } } @@ -46,13 +59,20 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter private void updateCount(JudgementResult judgement, bool revert) { - foreach (JudgementCount result in Results.Where(result => result.Type == judgement.Type)) - result.ResultCount.Value = revert ? result.ResultCount.Value - 1 : result.ResultCount.Value + 1; + if (!results.TryGetValue(judgement.Type, out var count)) + return; + + if (revert) + count.ResultCount.Value--; + else + count.ResultCount.Value++; } public struct JudgementCount { - public HitResult Type { get; set; } + public LocalisableString DisplayName { get; set; } + + public HitResult[] Types { get; set; } public BindableInt ResultCount { get; set; } } diff --git a/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounter.cs b/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounter.cs index 6c417faac2..45ed8d749b 100644 --- a/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounter.cs +++ b/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounter.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -44,14 +45,14 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter { Alpha = 0, Font = OsuFont.Numeric.With(size: 8), - Text = ruleset.Value.CreateInstance().GetDisplayNameForHitResult(Result.Type) + Text = Result.DisplayName, } } }; - var result = Result.Type; + var result = Result.Types.First(); - Colour = result.IsBasic() ? colours.ForHitResult(Result.Type) : !result.IsBonus() ? colours.PurpleLight : colours.PurpleLighter; + Colour = result.IsBasic() ? colours.ForHitResult(result) : !result.IsBonus() ? colours.PurpleLight : colours.PurpleLighter; } protected override void LoadComplete() diff --git a/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounterDisplay.cs b/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounterDisplay.cs index 128897ddde..326be55222 100644 --- a/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounterDisplay.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -49,7 +50,7 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter AutoSizeAxes = Axes.Both }; - foreach (var result in judgementCountController.Results) + foreach (var result in judgementCountController.Counters) CounterFlow.Add(createCounter(result)); } @@ -88,7 +89,9 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter if (index == 0 && !ShowMaxJudgement.Value) return false; - if (counter.Result.Type.IsBasic()) + var hitResult = counter.Result.Types.First(); + + if (hitResult.IsBasic()) return true; switch (Mode.Value) @@ -97,7 +100,7 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter return false; case DisplayMode.Normal: - return !counter.Result.Type.IsBonus(); + return !hitResult.IsBonus(); case DisplayMode.All: return true; From 2b45a9b7c629fd9f8df01fc35db37da7f8b511c2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 17:10:16 +0900 Subject: [PATCH 0970/1118] Add failing test coverage showing collection dropdown crash --- .../Visual/SongSelect/TestSceneFilterControl.cs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs index 94c6130f15..58183acb93 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; @@ -76,6 +77,20 @@ namespace osu.Game.Tests.Visual.SongSelect assertCollectionDropdownContains("2"); } + [Test] + public void TestCollectionsCleared() + { + AddStep("add collection", () => writeAndRefresh(r => r.Add(new BeatmapCollection(name: "1")))); + AddStep("add collection", () => writeAndRefresh(r => r.Add(new BeatmapCollection(name: "2")))); + AddStep("add collection", () => writeAndRefresh(r => r.Add(new BeatmapCollection(name: "3")))); + + AddAssert("check count 5", () => control.ChildrenOfType().Single().ChildrenOfType().Count(), () => Is.EqualTo(5)); + + AddStep("delete all collections", () => writeAndRefresh(r => r.RemoveAll())); + + AddAssert("check count 2", () => control.ChildrenOfType().Single().ChildrenOfType().Count(), () => Is.EqualTo(2)); + } + [Test] public void TestCollectionRemovedFromDropdown() { From 0a522d260becbbcc2e7d2835b763208ea76a6d4a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 17:10:30 +0900 Subject: [PATCH 0971/1118] Fix collection dropdown crashing when all collections are deleted at once --- osu.Game/Collections/CollectionDropdown.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Collections/CollectionDropdown.cs b/osu.Game/Collections/CollectionDropdown.cs index 8d83ed3ec9..249a0c35e7 100644 --- a/osu.Game/Collections/CollectionDropdown.cs +++ b/osu.Game/Collections/CollectionDropdown.cs @@ -74,7 +74,7 @@ namespace osu.Game.Collections } else { - foreach (int i in changes.DeletedIndices) + foreach (int i in changes.DeletedIndices.OrderByDescending(i => i)) filters.RemoveAt(i + 1); foreach (int i in changes.InsertedIndices) From 52f8348ee31d1cecdc5a514565811588aaf31073 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 17:48:37 +0900 Subject: [PATCH 0972/1118] Fade hold-for-menu button out completely on non-touch devices --- .../Screens/Play/HUD/HoldForMenuButton.cs | 28 +++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs index 0921a9f18a..1cf3d25dad 100644 --- a/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs +++ b/osu.Game/Screens/Play/HUD/HoldForMenuButton.cs @@ -16,6 +16,7 @@ using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Framework.Threading; using osu.Framework.Utils; +using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Sprites; @@ -44,6 +45,8 @@ namespace osu.Game.Screens.Play.HUD Direction = FillDirection.Horizontal; Spacing = new Vector2(20, 0); Margin = new MarginPadding(10); + + AlwaysPresent = true; } [BackgroundDependencyLoader(true)] @@ -66,9 +69,15 @@ namespace osu.Game.Screens.Play.HUD Action = () => Action(), } }; + AutoSizeAxes = Axes.Both; } + [Resolved] + private SessionStatics sessionStatics { get; set; } + + private Bindable touchActive; + protected override void LoadComplete() { button.HoldActivationDelay.BindValueChanged(v => @@ -78,7 +87,20 @@ namespace osu.Game.Screens.Play.HUD : "press for menu"; }, true); - text.FadeInFromZero(500, Easing.OutQuint).Delay(1500).FadeOut(500, Easing.OutQuint); + touchActive = sessionStatics.GetBindable(Static.TouchInputActive); + + if (touchActive.Value) + { + Alpha = 1f; + text.FadeInFromZero(500, Easing.OutQuint) + .Delay(1500) + .FadeOut(500, Easing.OutQuint); + } + else + { + Alpha = 0; + text.Alpha = 0f; + } base.LoadComplete(); } @@ -99,9 +121,11 @@ namespace osu.Game.Screens.Play.HUD Alpha = 1; else { + float minAlpha = touchActive.Value ? .08f : 0; + Alpha = Interpolation.ValueAt( Math.Clamp(Clock.ElapsedFrameTime, 0, 200), - Alpha, Math.Clamp(1 - positionalAdjust, 0.04f, 1), 0, 200, Easing.OutQuint); + Alpha, Math.Clamp(1 - positionalAdjust, minAlpha, 1), 0, 200, Easing.OutQuint); } } From 2a2a4c416eaf11ac2212ebbc88c19567e6ec22b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 18:17:49 +0900 Subject: [PATCH 0973/1118] Only display offset toast when in local gameplay --- osu.Game/Screens/Play/Player.cs | 6 ------ osu.Game/Screens/Play/SubmittingPlayer.cs | 10 ++++++++++ 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b87306b9a2..96530634fd 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -461,12 +461,6 @@ namespace osu.Game.Screens.Play OnRetry = () => Restart(), OnQuit = () => PerformExit(true), }, - new GameplayOffsetControl - { - Margin = new MarginPadding(20), - Anchor = Anchor.CentreRight, - Origin = Anchor.CentreRight, - } }, }; diff --git a/osu.Game/Screens/Play/SubmittingPlayer.cs b/osu.Game/Screens/Play/SubmittingPlayer.cs index 04abb6162f..171ceea84f 100644 --- a/osu.Game/Screens/Play/SubmittingPlayer.cs +++ b/osu.Game/Screens/Play/SubmittingPlayer.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Threading.Tasks; using JetBrains.Annotations; using osu.Framework.Allocation; +using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Screens; using osu.Game.Beatmaps; @@ -59,6 +60,15 @@ namespace osu.Game.Screens.Play } AddInternal(new PlayerTouchInputDetector()); + + // We probably want to move this display to something more global. + // Probably using the OSD somehow. + AddInternal(new GameplayOffsetControl + { + Margin = new MarginPadding(20), + Anchor = Anchor.CentreRight, + Origin = Anchor.CentreRight, + }); } protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) => new MasterGameplayClockContainer(beatmap, gameplayStart) From 0aa8a20d57a9d50bc39d7ecf2d847314a51d317e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 18:34:32 +0900 Subject: [PATCH 0974/1118] Fix regression in interaction when panels are not selectable --- .../Screens/OnlinePlay/DrawableRoomPlaylistItem.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs index 823f6ea308..800c73cceb 100644 --- a/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs +++ b/osu.Game/Screens/OnlinePlay/DrawableRoomPlaylistItem.cs @@ -365,7 +365,7 @@ namespace osu.Game.Screens.OnlinePlay AutoSizeAxes = Axes.Both, Margin = new MarginPadding { Left = 8, Right = 8 }, }, - mainFillFlow = new MainFlow(() => SelectedItem.Value == Model) + mainFillFlow = new MainFlow(() => SelectedItem.Value == Model || !AllowSelection) { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, @@ -671,13 +671,13 @@ namespace osu.Game.Screens.OnlinePlay public partial class MainFlow : FillFlowContainer { - private readonly Func isSelected; + private readonly Func allowInteraction; - public override bool PropagatePositionalInputSubTree => isSelected(); + public override bool PropagatePositionalInputSubTree => allowInteraction(); - public MainFlow(Func isSelected) + public MainFlow(Func allowInteraction) { - this.isSelected = isSelected; + this.allowInteraction = allowInteraction; } } } From 8e32780888cab9e3e035a8b9a3233c10e066f47e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 19:21:19 +0900 Subject: [PATCH 0975/1118] Fix background dim occasionally getting in a bad state when exiting gameplay --- .../Screens/Backgrounds/BackgroundScreenBeatmap.cs | 2 +- osu.Game/Screens/Play/Player.cs | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs index 85ea881006..185e2cab99 100644 --- a/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs +++ b/osu.Game/Screens/Backgrounds/BackgroundScreenBeatmap.cs @@ -51,7 +51,7 @@ namespace osu.Game.Screens.Backgrounds /// public readonly Bindable DimWhenUserSettingsIgnored = new Bindable(); - internal readonly IBindable IsBreakTime = new Bindable(); + internal readonly Bindable IsBreakTime = new Bindable(); private readonly DimmableBackground dimmable; diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index b87306b9a2..f0f0a58368 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1078,7 +1078,7 @@ namespace osu.Game.Screens.Play b.FadeColour(Color4.White, 250); // bind component bindables. - b.IsBreakTime.BindTo(breakTracker.IsBreakTime); + ((IBindable)b.IsBreakTime).BindTo(breakTracker.IsBreakTime); b.StoryboardReplacesBackground.BindTo(storyboardReplacesBackground); @@ -1238,7 +1238,13 @@ namespace osu.Game.Screens.Play if (this.IsCurrentScreen()) { - ApplyToBackground(b => b.IgnoreUserSettings.Value = true); + ApplyToBackground(b => + { + b.IgnoreUserSettings.Value = true; + + b.IsBreakTime.UnbindFrom(breakTracker.IsBreakTime); + b.IsBreakTime.Value = true; + }); storyboardReplacesBackground.Value = false; } } From 494c1be6553ed9b891b8b05dc37bbe6d50d3f3a7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 15 Jan 2024 13:30:34 +0300 Subject: [PATCH 0976/1118] Fix blatant error in `TestSceneModAccuracyChallenge` --- osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs b/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs index 6bdb9132e1..c5e56c6453 100644 --- a/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs +++ b/osu.Game.Tests/Visual/Mods/TestSceneModAccuracyChallenge.cs @@ -8,6 +8,7 @@ using osu.Game.Rulesets; using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; using osuTK; @@ -29,7 +30,7 @@ namespace osu.Game.Tests.Visual.Mods public void TestMaximumAchievableAccuracy() => CreateModTest(new ModTestData { - Mod = new ModAccuracyChallenge + Mod = new OsuModAccuracyChallenge { MinimumAccuracy = { Value = 0.6 } }, @@ -49,7 +50,7 @@ namespace osu.Game.Tests.Visual.Mods public void TestStandardAccuracy() => CreateModTest(new ModTestData { - Mod = new ModAccuracyChallenge + Mod = new OsuModAccuracyChallenge { MinimumAccuracy = { Value = 0.6 }, AccuracyJudgeMode = { Value = ModAccuracyChallenge.AccuracyMode.Standard } From e3989c854d39ec82d1ea27ada8f70a9dea159633 Mon Sep 17 00:00:00 2001 From: StanR Date: Mon, 15 Jan 2024 16:57:22 +0600 Subject: [PATCH 0977/1118] Change `LoginPanel` to use `LocalUser.Status` for the dropdown --- osu.Game/Overlays/Login/LoginPanel.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs index 513088ba0c..7db3644de6 100644 --- a/osu.Game/Overlays/Login/LoginPanel.cs +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -139,7 +139,7 @@ namespace osu.Game.Overlays.Login }, }; - panel.Status.BindValueChanged(_ => updateDropdownCurrent(), true); + api.LocalUser.Value.Status.BindValueChanged(e => updateDropdownCurrent(e.NewValue), true); dropdown.Current.BindValueChanged(action => { @@ -172,9 +172,9 @@ namespace osu.Game.Overlays.Login ScheduleAfterChildren(() => GetContainingInputManager()?.ChangeFocus(form)); }); - private void updateDropdownCurrent() + private void updateDropdownCurrent(UserStatus? status) { - switch (panel.Status.Value) + switch (status) { case UserStatus.Online: dropdown.Current.Value = UserAction.Online; From 13517869f6bb373fbe6b44df5728d31bc696ef3d Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 15 Jan 2024 14:03:23 +0300 Subject: [PATCH 0978/1118] Apply scale transitions in resume overlay cursor to local container --- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 24 +++++++++++--------- osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs | 20 ++++++++++++---- 2 files changed, 28 insertions(+), 16 deletions(-) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 18351c20ce..710ca9ace7 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -57,17 +57,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor [BackgroundDependencyLoader] private void load() { - InternalChild = cursorScaleContainer = new Container - { - RelativeSizeAxes = Axes.Both, - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - Child = cursorSprite = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) - { - Origin = Anchor.Centre, - Anchor = Anchor.Centre, - } - }; + InternalChild = CreateCursorContent(); userCursorScale = config.GetBindable(OsuSetting.GameplayCursorSize); userCursorScale.ValueChanged += _ => cursorScale.Value = CalculateCursorScale(); @@ -84,6 +74,18 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor cursorScale.Value = CalculateCursorScale(); } + protected virtual Drawable CreateCursorContent() => cursorScaleContainer = new Container + { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Child = cursorSprite = new SkinnableDrawable(new OsuSkinComponentLookup(OsuSkinComponents.Cursor), _ => new DefaultCursor(), confineMode: ConfineMode.NoScaling) + { + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + }, + }; + protected virtual float CalculateCursorScale() { float scale = userCursorScale.Value; diff --git a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs index f5e83f46f2..8a84fe14e5 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuResumeOverlay.cs @@ -65,17 +65,24 @@ namespace osu.Game.Rulesets.Osu.UI public override bool HandlePositionalInput => true; public Action ResumeRequested; + private Container scaleTransitionContainer; public OsuClickToResumeCursor() { RelativePositionAxes = Axes.Both; } - protected override float CalculateCursorScale() + protected override Container CreateCursorContent() => scaleTransitionContainer = new Container { + RelativeSizeAxes = Axes.Both, + Origin = Anchor.Centre, + Anchor = Anchor.Centre, + Child = base.CreateCursorContent(), + }; + + protected override float CalculateCursorScale() => // Force minimum cursor size so it's easily clickable - return Math.Max(1f, base.CalculateCursorScale()); - } + Math.Max(1f, base.CalculateCursorScale()); protected override bool OnHover(HoverEvent e) { @@ -98,7 +105,7 @@ namespace osu.Game.Rulesets.Osu.UI if (!IsHovered) return false; - this.ScaleTo(2, TRANSITION_TIME, Easing.OutQuint); + scaleTransitionContainer.ScaleTo(2, TRANSITION_TIME, Easing.OutQuint); ResumeRequested?.Invoke(); return true; @@ -114,7 +121,10 @@ namespace osu.Game.Rulesets.Osu.UI public void Appear() => Schedule(() => { updateColour(); - this.ScaleTo(4).Then().ScaleTo(1, 1000, Easing.OutQuint); + + // importantly, we perform the scale transition on an underlying container rather than the whole cursor + // to prevent attempts of abuse by the scale change in the cursor's hitbox (see: https://github.com/ppy/osu/issues/26477). + scaleTransitionContainer.ScaleTo(4).Then().ScaleTo(1, 1000, Easing.OutQuint); }); private void updateColour() From 0a02050a8523c6c873b4fbf582f072d649efaec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 12:12:27 +0100 Subject: [PATCH 0979/1118] Fix test crashing at the end in visual test browser --- .../Visual/Multiplayer/TestSceneMultiplayer.cs | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs index 201fac37c8..747805edc8 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneMultiplayer.cs @@ -1033,12 +1033,6 @@ namespace osu.Game.Tests.Visual.Multiplayer } }); - AddStep("join other user and make host", () => - { - multiplayerClient.AddUser(new APIUser { Id = 1234 }); - multiplayerClient.TransferHost(1234); - }); - AddStep("select hidden", () => multiplayerClient.ChangeUserMods(new[] { new APIMod { Acronym = "HD" } })); AddStep("make user ready", () => multiplayerClient.ChangeState(MultiplayerUserState.Ready)); AddStep("press edit on second item", () => this.ChildrenOfType().Single(i => i.Item.RulesetID == 1) @@ -1047,14 +1041,9 @@ namespace osu.Game.Tests.Visual.Multiplayer AddUntilStep("wait for song select", () => InputManager.ChildrenOfType().FirstOrDefault()?.BeatmapSetsLoaded == true); AddAssert("ruleset is taiko", () => Ruleset.Value.OnlineID == 1); - AddStep("start match by other user", () => - { - multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Ready); - multiplayerClient.StartMatch().WaitSafely(); - }); + AddStep("start match", () => multiplayerClient.StartMatch().WaitSafely()); AddUntilStep("wait for loading", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.WaitingForLoad); - AddStep("set player loaded", () => multiplayerClient.ChangeUserState(1234, MultiplayerUserState.Loaded)); AddUntilStep("wait for gameplay to start", () => multiplayerClient.ClientRoom?.State == MultiplayerRoomState.Playing); AddAssert("hidden is selected", () => SelectedMods.Value, () => Has.One.TypeOf(typeof(OsuModHidden))); } From 96c472b870790825e6acbc692a425612f97efb64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 12:33:25 +0100 Subject: [PATCH 0980/1118] Remove unused using directive --- osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs index 58183acb93..a639d50eee 100644 --- a/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs +++ b/osu.Game.Tests/Visual/SongSelect/TestSceneFilterControl.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.IO; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; From 86382f4408baa4bc292b2c510a9db49a8af0f6c9 Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 15 Jan 2024 12:49:40 +0100 Subject: [PATCH 0981/1118] Clarify comment --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index 2f012e33da..f5840248fb 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -250,7 +250,10 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateAfterChildren(); - // It's important that this is done in UpdateAfterChildren so that the SliderBody has a chance to update its Size and PathOffset on Update. + // During slider path editing, the PlaySliderBody is scheduled to refresh once in the Update phase. + // It is crucial to perform the code below in UpdateAfterChildren. This ensures that the SliderBody has the opportunity + // to update its Size and PathOffset beforehand, ensuring correct placement. + double completionProgress = Math.Clamp((Time.Current - HitObject.StartTime) / HitObject.Duration, 0, 1); Ball.UpdateProgress(completionProgress); From 6b844ed8b64db49554031bd397eaa310d4b54601 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 20:29:08 +0900 Subject: [PATCH 0982/1118] Split out judgement pooling concepts from `OsuPlayfield` for reuse --- osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs | 64 +++++--------------- osu.Game/Rulesets/UI/JudgementPooler.cs | 77 ++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 50 deletions(-) create mode 100644 osu.Game/Rulesets/UI/JudgementPooler.cs diff --git a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs index c94057cf6d..411a02c5af 100644 --- a/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs +++ b/osu.Game.Rulesets.Osu/UI/OsuPlayfield.cs @@ -4,13 +4,11 @@ #nullable disable using System; -using System.Collections.Generic; using System.Diagnostics; using System.Linq; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Pooling; using osu.Game.Beatmaps; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Objects; @@ -35,6 +33,8 @@ namespace osu.Game.Rulesets.Osu.UI private readonly ProxyContainer spinnerProxies; private readonly JudgementContainer judgementLayer; + private readonly JudgementPooler judgementPooler; + public SmokeContainer Smoke { get; } public FollowPointRenderer FollowPoints { get; } @@ -42,8 +42,6 @@ namespace osu.Game.Rulesets.Osu.UI protected override GameplayCursorContainer CreateCursor() => new OsuCursorContainer(); - private readonly IDictionary> poolDictionary = new Dictionary>(); - private readonly Container judgementAboveHitObjectLayer; public OsuPlayfield() @@ -65,24 +63,15 @@ namespace osu.Game.Rulesets.Osu.UI HitPolicy = new StartTimeOrderedHitPolicy(); - foreach (var result in Enum.GetValues().Where(r => - { - switch (r) - { - case HitResult.Great: - case HitResult.Ok: - case HitResult.Meh: - case HitResult.Miss: - case HitResult.LargeTickMiss: - case HitResult.IgnoreMiss: - return true; - } - - return false; - })) - poolDictionary.Add(result, new DrawableJudgementPool(result, onJudgementLoaded)); - - AddRangeInternal(poolDictionary.Values); + AddInternal(judgementPooler = new JudgementPooler(new[] + { + HitResult.Great, + HitResult.Ok, + HitResult.Meh, + HitResult.Miss, + HitResult.LargeTickMiss, + HitResult.IgnoreMiss, + }, onJudgementLoaded)); NewResult += onNewResult; } @@ -182,10 +171,10 @@ namespace osu.Game.Rulesets.Osu.UI if (!judgedObject.DisplayResult || !DisplayJudgements.Value) return; - if (!poolDictionary.TryGetValue(result.Type, out var pool)) - return; + var explosion = judgementPooler.Get(result.Type, doj => doj.Apply(result, judgedObject)); - DrawableOsuJudgement explosion = pool.Get(doj => doj.Apply(result, judgedObject)); + if (explosion == null) + return; judgementLayer.Add(explosion); @@ -201,31 +190,6 @@ namespace osu.Game.Rulesets.Osu.UI public void Add(Drawable proxy) => AddInternal(proxy); } - private partial class DrawableJudgementPool : DrawablePool - { - private readonly HitResult result; - private readonly Action onLoaded; - - public DrawableJudgementPool(HitResult result, Action onLoaded) - : base(20) - { - this.result = result; - this.onLoaded = onLoaded; - } - - protected override DrawableOsuJudgement CreateNewDrawable() - { - var judgement = base.CreateNewDrawable(); - - // just a placeholder to initialise the correct drawable hierarchy for this pool. - judgement.Apply(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null); - - onLoaded?.Invoke(judgement); - - return judgement; - } - } - private class OsuHitObjectLifetimeEntry : HitObjectLifetimeEntry { public OsuHitObjectLifetimeEntry(HitObject hitObject) diff --git a/osu.Game/Rulesets/UI/JudgementPooler.cs b/osu.Game/Rulesets/UI/JudgementPooler.cs new file mode 100644 index 0000000000..efec760f15 --- /dev/null +++ b/osu.Game/Rulesets/UI/JudgementPooler.cs @@ -0,0 +1,77 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using System; +using System.Collections.Generic; +using osu.Framework.Allocation; +using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Pooling; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Objects; +using osu.Game.Rulesets.Scoring; + +namespace osu.Game.Rulesets.UI +{ + /// + /// Handles the task of preparing poolable drawable judgements for gameplay usage. + /// + /// The drawable judgement type. + public partial class JudgementPooler : CompositeComponent + where T : DrawableJudgement, new() + { + private readonly IDictionary> poolDictionary = new Dictionary>(); + + private readonly IEnumerable usableHitResults; + private readonly Action? onJudgementInitialLoad; + + public JudgementPooler(IEnumerable usableHitResults, Action? onJudgementInitialLoad = null) + { + this.usableHitResults = usableHitResults; + this.onJudgementInitialLoad = onJudgementInitialLoad; + } + + public T? Get(HitResult result, Action? setupAction) + { + if (!poolDictionary.TryGetValue(result, out var pool)) + return null; + + return pool.Get(setupAction); + } + + [BackgroundDependencyLoader] + private void load() + { + foreach (HitResult result in usableHitResults) + { + var pool = new DrawableJudgementPool(result, onJudgementInitialLoad); + poolDictionary.Add(result, pool); + AddInternal(pool); + } + } + + private partial class DrawableJudgementPool : DrawablePool + { + private readonly HitResult result; + private readonly Action? onLoaded; + + public DrawableJudgementPool(HitResult result, Action? onLoaded) + : base(20) + { + this.result = result; + this.onLoaded = onLoaded; + } + + protected override T CreateNewDrawable() + { + var judgement = base.CreateNewDrawable(); + + // just a placeholder to initialise the correct drawable hierarchy for this pool. + judgement.Apply(new JudgementResult(new HitObject(), new Judgement()) { Type = result }, null); + + onLoaded?.Invoke(judgement); + + return judgement; + } + } + } +} From e9812fac7ae09d4f33302bce80e20a353de9f8e2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 20:37:33 +0900 Subject: [PATCH 0983/1118] Fix osu!mania judgments not being pooled correctly --- osu.Game.Rulesets.Mania/UI/Stage.cs | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/osu.Game.Rulesets.Mania/UI/Stage.cs b/osu.Game.Rulesets.Mania/UI/Stage.cs index fa9af6d157..36286940a8 100644 --- a/osu.Game.Rulesets.Mania/UI/Stage.cs +++ b/osu.Game.Rulesets.Mania/UI/Stage.cs @@ -1,22 +1,23 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - +using System; using System.Collections.Generic; using System.Linq; using osu.Framework.Allocation; +using osu.Framework.Extensions.ObjectExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Pooling; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.Objects.Drawables; +using osu.Game.Rulesets.Mania.Scoring; using osu.Game.Rulesets.Mania.Skinning; using osu.Game.Rulesets.Mania.UI.Components; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.UI; using osu.Game.Rulesets.UI.Scrolling; using osu.Game.Skinning; @@ -40,7 +41,7 @@ namespace osu.Game.Rulesets.Mania.UI private readonly ColumnFlow columnFlow; private readonly JudgementContainer judgements; - private readonly DrawablePool judgementPool; + private readonly JudgementPooler judgementPooler; private readonly Drawable barLineContainer; @@ -48,6 +49,8 @@ namespace osu.Game.Rulesets.Mania.UI private readonly int firstColumnIndex; + private ISkinSource currentSkin = null!; + public Stage(int firstColumnIndex, StageDefinition definition, ref ManiaAction normalColumnStartAction, ref ManiaAction specialColumnStartAction) { this.firstColumnIndex = firstColumnIndex; @@ -65,7 +68,6 @@ namespace osu.Game.Rulesets.Mania.UI InternalChildren = new Drawable[] { - judgementPool = new DrawablePool(2), new Container { Anchor = Anchor.TopCentre, @@ -104,7 +106,7 @@ namespace osu.Game.Rulesets.Mania.UI { RelativeSizeAxes = Axes.Y, }, - new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.StageForeground), _ => null) + new SkinnableDrawable(new ManiaSkinComponentLookup(ManiaSkinComponents.StageForeground)) { RelativeSizeAxes = Axes.Both }, @@ -137,11 +139,13 @@ namespace osu.Game.Rulesets.Mania.UI AddNested(column); } + var hitWindows = new ManiaHitWindows(); + + AddInternal(judgementPooler = new JudgementPooler(Enum.GetValues().Where(r => hitWindows.IsHitResultAllowed(r)))); + RegisterPool(50, 200); } - private ISkinSource currentSkin; - [BackgroundDependencyLoader] private void load(ISkinSource skin) { @@ -170,7 +174,7 @@ namespace osu.Game.Rulesets.Mania.UI base.Dispose(isDisposing); - if (currentSkin != null) + if (currentSkin.IsNotNull()) currentSkin.SourceChanged -= onSkinChanged; } @@ -196,13 +200,13 @@ namespace osu.Game.Rulesets.Mania.UI return; judgements.Clear(false); - judgements.Add(judgementPool.Get(j => + judgements.Add(judgementPooler.Get(result.Type, j => { j.Apply(result, judgedObject); j.Anchor = Anchor.Centre; j.Origin = Anchor.Centre; - })); + })!); } protected override void Update() From 0fbca59523cea2bd7036581c5445c7173ff8123f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 15 Jan 2024 20:49:00 +0900 Subject: [PATCH 0984/1118] Fix osu!taiko judgments not being pooled correctly They weren't being initialised correctly on initial pool. --- osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs | 49 ++++++++++---------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs index 31f8171290..7e3ed7a4d4 100644 --- a/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs +++ b/osu.Game.Rulesets.Taiko/UI/TaikoPlayfield.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Collections.Generic; using System.Linq; @@ -10,7 +8,6 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Pooling; using osu.Framework.Graphics.Primitives; using osu.Game.Graphics; using osu.Game.Rulesets.Objects.Drawables; @@ -42,29 +39,29 @@ namespace osu.Game.Rulesets.Taiko.UI public Container UnderlayElements { get; private set; } = null!; - private Container hitExplosionContainer; - private Container kiaiExplosionContainer; - private JudgementContainer judgementContainer; - private ScrollingHitObjectContainer drumRollHitContainer; - internal Drawable HitTarget; - private SkinnableDrawable mascot; + private Container hitExplosionContainer = null!; + private Container kiaiExplosionContainer = null!; + private JudgementContainer judgementContainer = null!; + private ScrollingHitObjectContainer drumRollHitContainer = null!; + internal Drawable HitTarget = null!; + private SkinnableDrawable mascot = null!; - private readonly IDictionary> judgementPools = new Dictionary>(); + private JudgementPooler judgementPooler = null!; private readonly IDictionary explosionPools = new Dictionary(); - private ProxyContainer topLevelHitContainer; - private InputDrum inputDrum; - private Container rightArea; + private ProxyContainer topLevelHitContainer = null!; + private InputDrum inputDrum = null!; + private Container rightArea = null!; /// /// is purposefully not called on this to prevent i.e. being able to interact /// with bar lines in the editor. /// - private BarLinePlayfield barLinePlayfield; + private BarLinePlayfield barLinePlayfield = null!; - private Container barLineContent; - private Container hitObjectContent; - private Container overlayContent; + private Container barLineContent = null!; + private Container hitObjectContent = null!; + private Container overlayContent = null!; [BackgroundDependencyLoader] private void load(OsuColour colours) @@ -202,13 +199,12 @@ namespace osu.Game.Rulesets.Taiko.UI var hitWindows = new TaikoHitWindows(); - foreach (var result in Enum.GetValues().Where(r => hitWindows.IsHitResultAllowed(r))) - { - judgementPools.Add(result, new DrawablePool(15)); - explosionPools.Add(result, new HitExplosionPool(result)); - } + HitResult[] usableHitResults = Enum.GetValues().Where(r => hitWindows.IsHitResultAllowed(r)).ToArray(); - AddRangeInternal(judgementPools.Values); + AddInternal(judgementPooler = new JudgementPooler(usableHitResults)); + + foreach (var result in usableHitResults) + explosionPools.Add(result, new HitExplosionPool(result)); AddRangeInternal(explosionPools.Values); } @@ -339,7 +335,12 @@ namespace osu.Game.Rulesets.Taiko.UI if (!result.Type.IsScorable()) break; - judgementContainer.Add(judgementPools[result.Type].Get(j => j.Apply(result, judgedObject))); + var judgement = judgementPooler.Get(result.Type, j => j.Apply(result, judgedObject)); + + if (judgement == null) + return; + + judgementContainer.Add(judgement); var type = (judgedObject.HitObject as Hit)?.Type ?? HitType.Centre; addExplosion(judgedObject, result.Type, type); From 96ffe8e737e327517e4d585d1a5dd8dff754032b Mon Sep 17 00:00:00 2001 From: OliBomby Date: Mon, 15 Jan 2024 12:51:08 +0100 Subject: [PATCH 0985/1118] change wording --- osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index f5840248fb..ed4c02a4a9 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -250,7 +250,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables { base.UpdateAfterChildren(); - // During slider path editing, the PlaySliderBody is scheduled to refresh once in the Update phase. + // During slider path editing, the PlaySliderBody is scheduled to refresh once on Update. // It is crucial to perform the code below in UpdateAfterChildren. This ensures that the SliderBody has the opportunity // to update its Size and PathOffset beforehand, ensuring correct placement. From 9bc9db9138ee6d8e103c1f2a8dd87b671cb6a5b8 Mon Sep 17 00:00:00 2001 From: wooster0 Date: Mon, 15 Jan 2024 20:58:43 +0900 Subject: [PATCH 0986/1118] simplify the code even more --- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- osu.Game/Screens/Ranking/ScorePanelList.cs | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index 697d62ad6e..da1609c2c4 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -248,7 +248,7 @@ namespace osu.Game.Screens.Ranking lastFetchCompleted = true; - if (ScorePanelList.IsEmpty) + if (!scores.Any()) { // This can happen if for example a beatmap that is part of a playlist hasn't been played yet. VerticalScrollContent.Add(new MessagePlaceholder(LeaderboardStrings.NoRecordsYet)); diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index 95c90e35a0..b75f3d86ff 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -49,8 +49,6 @@ namespace osu.Game.Screens.Ranking public bool AllPanelsVisible => flow.All(p => p.IsPresent); - public bool IsEmpty => flow.Count == 0; - /// /// The current scroll position. /// From 1d7b63e204bfac6a5350b3221b3c951f2e4e5510 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 15 Jan 2024 14:57:17 +0300 Subject: [PATCH 0987/1118] Move checking logic inside `ModUtils` and somewhat optimise --- osu.Game/Screens/Play/Player.cs | 3 ++- osu.Game/Utils/ModUtils.cs | 32 ++++++++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index ffd585148b..5f52f59ad2 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -38,6 +38,7 @@ using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; using osu.Game.Skinning; using osu.Game.Users; +using osu.Game.Utils; using osuTK.Graphics; namespace osu.Game.Screens.Play @@ -213,7 +214,7 @@ namespace osu.Game.Screens.Play if (playableBeatmap == null) return; - if (gameplayMods.Any(m => ruleset.AllMods.All(rulesetMod => m.GetType() != rulesetMod.GetType()))) + if (!ModUtils.CheckModsBelongToRuleset(ruleset, gameplayMods)) { Logger.Log($@"Gameplay was started with a mod belonging to a ruleset different than '{ruleset.Description}'.", level: LogLevel.Important); return; diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 252579a186..827b199b16 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -229,6 +229,38 @@ namespace osu.Game.Utils return proposedWereValid; } + /// + /// Verifies all mods provided belong to the given ruleset. + /// + /// The ruleset to check the proposed mods against. + /// The mods proposed for checking. + /// Whether all belong to the given . + public static bool CheckModsBelongToRuleset(Ruleset ruleset, IEnumerable proposedMods) + { + var rulesetModsTypes = ruleset.AllMods.Select(m => m.GetType()).ToList(); + + foreach (var proposedMod in proposedMods) + { + bool found = false; + + var proposedModType = proposedMod.GetType(); + + foreach (var rulesetModType in rulesetModsTypes) + { + if (rulesetModType == proposedModType) + { + found = true; + break; + } + } + + if (!found) + return true; + } + + return true; + } + /// /// Given a value of a score multiplier, returns a string version with special handling for a value near 1.00x. /// From d346dd065027d08d374d76f045302c356d05fb9a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 15 Jan 2024 15:01:13 +0300 Subject: [PATCH 0988/1118] Fix `TestModReinstantiation` failing due to custom mod being used --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs | 8 +------- osu.Game/Utils/ModUtils.cs | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs index dbd1ce1f6e..f97372e9b6 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerLoader.cs @@ -13,7 +13,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Localisation; using osu.Framework.Screens; using osu.Framework.Testing; using osu.Framework.Utils; @@ -487,13 +486,8 @@ namespace osu.Game.Tests.Visual.Gameplay } } - private class TestMod : Mod, IApplicableToScoreProcessor + private class TestMod : OsuModDoubleTime, IApplicableToScoreProcessor { - public override string Name => string.Empty; - public override string Acronym => string.Empty; - public override double ScoreMultiplier => 1; - public override LocalisableString Description => string.Empty; - public bool Applied { get; private set; } public void ApplyToScoreProcessor(ScoreProcessor scoreProcessor) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 827b199b16..7a9f93e06f 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -247,7 +247,7 @@ namespace osu.Game.Utils foreach (var rulesetModType in rulesetModsTypes) { - if (rulesetModType == proposedModType) + if (rulesetModType.IsAssignableFrom(proposedModType)) { found = true; break; From 399fc8195a7441c250b528ffb6b8953aedba1aa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 13:22:22 +0100 Subject: [PATCH 0989/1118] Fix cursor ripple pool not loading pooled drawables ahead of time Increment the counter over at https://github.com/ppy/osu-framework/pull/6136. --- osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs index 52486b701a..4cb91aa103 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/CursorRippleVisualiser.cs @@ -33,6 +33,8 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor private void load(OsuRulesetConfigManager? rulesetConfig) { rulesetConfig?.BindWith(OsuRulesetSetting.ShowCursorRipples, showRipples); + + AddInternal(ripplePool); } public bool OnPressed(KeyBindingPressEvent e) From c5351bd14df70f61ab72c782b1dcab43c2d673b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 14:20:07 +0100 Subject: [PATCH 0990/1118] Fix back-to-front set --- osu.Game/Screens/Play/Player.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index f0f0a58368..4eb69a9893 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1243,7 +1243,7 @@ namespace osu.Game.Screens.Play b.IgnoreUserSettings.Value = true; b.IsBreakTime.UnbindFrom(breakTracker.IsBreakTime); - b.IsBreakTime.Value = true; + b.IsBreakTime.Value = false; }); storyboardReplacesBackground.Value = false; } From 91f8144f98adc6f79156d4514840d7b668ced376 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 14:58:46 +0100 Subject: [PATCH 0991/1118] Add test coverage --- .../TestSceneDrawableRoomPlaylist.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index 312135402f..d40283ac74 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -19,8 +19,10 @@ using osu.Game.Beatmaps.Drawables; using osu.Game.Database; using osu.Game.Graphics.Containers; using osu.Game.Graphics.Cursor; +using osu.Game.Graphics.UserInterface; using osu.Game.Models; using osu.Game.Online.API; +using osu.Game.Online.Chat; using osu.Game.Online.Rooms; using osu.Game.Rulesets; using osu.Game.Rulesets.Osu; @@ -302,6 +304,36 @@ namespace osu.Game.Tests.Visual.Multiplayer }); } + [Test] + public void TestSelectableMouseHandling() + { + bool resultsRequested = false; + + AddStep("reset flag", () => resultsRequested = false); + createPlaylist(p => + { + p.AllowSelection = true; + p.AllowShowingResults = true; + p.RequestResults = _ => resultsRequested = true; + }); + + AddStep("move mouse to first item title", () => + { + var drawQuad = playlist.ChildrenOfType().First().ScreenSpaceDrawQuad; + var location = (drawQuad.TopLeft + drawQuad.BottomLeft) / 2 + new Vector2(drawQuad.Width * 0.2f, 0); + InputManager.MoveMouseTo(location); + }); + AddAssert("first item title not hovered", () => playlist.ChildrenOfType().First().IsHovered, () => Is.False); + AddStep("click left mouse", () => InputManager.Click(MouseButton.Left)); + AddUntilStep("first item selected", () => playlist.ChildrenOfType().First().IsSelectedItem, () => Is.True); + // implies being clickable. + AddUntilStep("first item title hovered", () => playlist.ChildrenOfType().First().IsHovered, () => Is.True); + + AddStep("move mouse to second item results button", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(5))); + AddStep("click left mouse", () => InputManager.Click(MouseButton.Left)); + AddUntilStep("results requested", () => resultsRequested); + } + private void moveToItem(int index, Vector2? offset = null) => AddStep($"move mouse to item {index}", () => InputManager.MoveMouseTo(playlist.ChildrenOfType().ElementAt(index), offset)); From d8d1d9264c7bd1b933a21e67e26efebd2107707e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 18:29:51 +0100 Subject: [PATCH 0992/1118] Fix test failure --- .../Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs index d40283ac74..6446ebd35f 100644 --- a/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs +++ b/osu.Game.Tests/Visual/Multiplayer/TestSceneDrawableRoomPlaylist.cs @@ -323,6 +323,7 @@ namespace osu.Game.Tests.Visual.Multiplayer var location = (drawQuad.TopLeft + drawQuad.BottomLeft) / 2 + new Vector2(drawQuad.Width * 0.2f, 0); InputManager.MoveMouseTo(location); }); + AddUntilStep("wait for text load", () => playlist.ChildrenOfType().Any()); AddAssert("first item title not hovered", () => playlist.ChildrenOfType().First().IsHovered, () => Is.False); AddStep("click left mouse", () => InputManager.Click(MouseButton.Left)); AddUntilStep("first item selected", () => playlist.ChildrenOfType().First().IsSelectedItem, () => Is.True); From 8a839f64ed18cfabd6ca8ae98e7ba80441641803 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 18:47:46 +0100 Subject: [PATCH 0993/1118] Add failing test coverage for placeholder shown when it shouldn't be --- .../TestScenePlaylistsResultsScreen.cs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index 727d9da50b..ff1b351b9f 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -17,6 +17,7 @@ using osu.Game.Graphics.UserInterface; using osu.Game.Online.API; using osu.Game.Online.API.Requests; using osu.Game.Online.API.Requests.Responses; +using osu.Game.Online.Placeholders; using osu.Game.Online.Rooms; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Scoring; @@ -139,6 +140,37 @@ namespace osu.Game.Tests.Visual.Playlists } } + [Test] + public void TestNoMoreScoresToTheRight() + { + AddStep("bind delayed handler with scores", () => bindHandler(delayed: true)); + + createResults(); + waitForDisplay(); + + int beforePanelCount = 0; + + AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType().Count()); + AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType().Single().ScrollToEnd(false)); + + AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible); + waitForDisplay(); + + AddAssert($"count increased by {scores_per_result}", () => this.ChildrenOfType().Count() >= beforePanelCount + scores_per_result); + AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden); + + AddStep("get panel count", () => beforePanelCount = this.ChildrenOfType().Count()); + AddStep("bind delayed handler with no scores", () => bindHandler(delayed: true, noScores: true)); + AddStep("scroll to right", () => resultsScreen.ScorePanelList.ChildrenOfType().Single().ScrollToEnd(false)); + + AddAssert("right loading spinner shown", () => resultsScreen.RightSpinner.State.Value == Visibility.Visible); + waitForDisplay(); + + AddAssert("count not increased", () => this.ChildrenOfType().Count() == beforePanelCount); + AddAssert("right loading spinner hidden", () => resultsScreen.RightSpinner.State.Value == Visibility.Hidden); + AddAssert("no placeholders shown", () => this.ChildrenOfType().Count(), () => Is.Zero); + } + [Test] public void TestFetchWhenScrolledToTheLeft() { From 9d2c82452cfee1cfd7b23dc81c78d902938b5945 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 18:47:49 +0100 Subject: [PATCH 0994/1118] Revert "simplify the code even more" This reverts commit 9bc9db9138ee6d8e103c1f2a8dd87b671cb6a5b8. --- osu.Game/Screens/Ranking/ResultsScreen.cs | 2 +- osu.Game/Screens/Ranking/ScorePanelList.cs | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Ranking/ResultsScreen.cs b/osu.Game/Screens/Ranking/ResultsScreen.cs index da1609c2c4..697d62ad6e 100644 --- a/osu.Game/Screens/Ranking/ResultsScreen.cs +++ b/osu.Game/Screens/Ranking/ResultsScreen.cs @@ -248,7 +248,7 @@ namespace osu.Game.Screens.Ranking lastFetchCompleted = true; - if (!scores.Any()) + if (ScorePanelList.IsEmpty) { // This can happen if for example a beatmap that is part of a playlist hasn't been played yet. VerticalScrollContent.Add(new MessagePlaceholder(LeaderboardStrings.NoRecordsYet)); diff --git a/osu.Game/Screens/Ranking/ScorePanelList.cs b/osu.Game/Screens/Ranking/ScorePanelList.cs index b75f3d86ff..95c90e35a0 100644 --- a/osu.Game/Screens/Ranking/ScorePanelList.cs +++ b/osu.Game/Screens/Ranking/ScorePanelList.cs @@ -49,6 +49,8 @@ namespace osu.Game.Screens.Ranking public bool AllPanelsVisible => flow.All(p => p.IsPresent); + public bool IsEmpty => flow.Count == 0; + /// /// The current scroll position. /// From 988794cf90d8bbe803c6a24a1e8c0c8a3ffaf2c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 18:49:41 +0100 Subject: [PATCH 0995/1118] Improve test coverage for empty results --- .../Visual/Playlists/TestScenePlaylistsResultsScreen.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs index ff1b351b9f..e805b03542 100644 --- a/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs +++ b/osu.Game.Tests/Visual/Playlists/TestScenePlaylistsResultsScreen.cs @@ -201,7 +201,8 @@ namespace osu.Game.Tests.Visual.Playlists { AddStep("bind user score info handler", () => bindHandler(noScores: true)); createResults(); - AddAssert("no scores visible", () => resultsScreen.ScorePanelList.GetScorePanels().Count() == 0); + AddAssert("no scores visible", () => !resultsScreen.ScorePanelList.GetScorePanels().Any()); + AddAssert("placeholder shown", () => this.ChildrenOfType().Count(), () => Is.EqualTo(1)); } private void createResults(Func getScore = null) From e8394e6f74351f7b136fe16d2a53033814d1bd56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 19:05:33 +0100 Subject: [PATCH 0996/1118] Add test coverage --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 4b101a52f4..7958b68fb5 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -542,6 +542,11 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("press enter", () => InputManager.Key(Key.Enter)); AddAssert("hidden selected", () => getPanelForMod(typeof(OsuModHidden)).Active.Value); + AddAssert("all text selected in textbox", () => + { + var textBox = modSelectOverlay.ChildrenOfType().Single(); + return textBox.SelectedText == textBox.Text; + }); AddStep("press enter again", () => InputManager.Key(Key.Enter)); AddAssert("hidden deselected", () => !getPanelForMod(typeof(OsuModHidden)).Active.Value); From 8661edfc2fbc3cb9a5df62f8f31a7e1c928eb941 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 15 Jan 2024 21:07:09 +0300 Subject: [PATCH 0997/1118] Organize consts better --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index dcc8f4feec..6fc957874a 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -58,6 +58,8 @@ namespace osu.Game.Screens.Play.HUD public const float MAIN_PATH_RADIUS = 10f; private const float padding = MAIN_PATH_RADIUS * 2; + private const float glow_path_radius = 40f; + private const float main_path_glow_portion = 0.6f; private readonly LayoutValue drawSizeLayout = new LayoutValue(Invalidation.DrawSize); @@ -90,7 +92,7 @@ namespace osu.Game.Screens.Play.HUD { RelativeSizeAxes = Axes.Both, // since we are using bigger path radius we need to expand the draw area outwards to preserve the curve placement - Padding = new MarginPadding(-30f), + Padding = new MarginPadding(MAIN_PATH_RADIUS - glow_path_radius), Child = glowBar = new ArgonHealthDisplayBar { RelativeSizeAxes = Axes.Both, @@ -98,8 +100,8 @@ namespace osu.Game.Screens.Play.HUD GlowColour = main_bar_glow_colour, Blending = BlendingParameters.Additive, Colour = ColourInfo.GradientHorizontal(Color4.White.Opacity(0.8f), Color4.White), - PathRadius = 40f, - GlowPortion = 0.9f, + PathRadius = glow_path_radius, + GlowPortion = (glow_path_radius - MAIN_PATH_RADIUS * (1f - main_path_glow_portion)) / glow_path_radius, } }, mainBar = new ArgonHealthDisplayBar @@ -109,7 +111,7 @@ namespace osu.Game.Screens.Play.HUD BarColour = main_bar_colour, GlowColour = main_bar_glow_colour, PathRadius = MAIN_PATH_RADIUS, - GlowPortion = 0.6f, + GlowPortion = main_path_glow_portion } } }; From c0d4ed4789fbd7de96c131e2e7882ac673d03e97 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 19:07:52 +0100 Subject: [PATCH 0998/1118] Add test coverage of select-all-text-in-search when nothing matched --- .../Visual/UserInterface/TestSceneModSelectOverlay.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs index 7958b68fb5..046954db47 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneModSelectOverlay.cs @@ -551,6 +551,14 @@ namespace osu.Game.Tests.Visual.UserInterface AddStep("press enter again", () => InputManager.Key(Key.Enter)); AddAssert("hidden deselected", () => !getPanelForMod(typeof(OsuModHidden)).Active.Value); + AddStep("apply search matching nothing", () => modSelectOverlay.SearchTerm = "ZZZ"); + AddStep("press enter", () => InputManager.Key(Key.Enter)); + AddAssert("all text not selected in textbox", () => + { + var textBox = modSelectOverlay.ChildrenOfType().Single(); + return textBox.SelectedText != textBox.Text; + }); + AddStep("clear search", () => modSelectOverlay.SearchTerm = string.Empty); AddStep("press enter", () => InputManager.Key(Key.Enter)); AddAssert("mod select hidden", () => modSelectOverlay.State.Value == Visibility.Hidden); From c46615839dcc40bca80f401d8559422492c43cfe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 19:09:50 +0100 Subject: [PATCH 0999/1118] Only select all text in mod search text box if enter press selected anything --- osu.Game/Overlays/Mods/ModSelectOverlay.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Mods/ModSelectOverlay.cs b/osu.Game/Overlays/Mods/ModSelectOverlay.cs index 8cb990757f..a774bb9167 100644 --- a/osu.Game/Overlays/Mods/ModSelectOverlay.cs +++ b/osu.Game/Overlays/Mods/ModSelectOverlay.cs @@ -719,9 +719,10 @@ namespace osu.Game.Overlays.Mods ModState? firstMod = columnFlow.Columns.OfType().FirstOrDefault(m => m.IsPresent)?.AvailableMods.FirstOrDefault(x => x.Visible); if (firstMod is not null) + { firstMod.Active.Value = !firstMod.Active.Value; - - SearchTextBox.SelectAll(); + SearchTextBox.SelectAll(); + } return true; } From 97e08f50715e980d4a987d9f8ca52f7d6d178df1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 15 Jan 2024 21:39:33 +0100 Subject: [PATCH 1000/1118] Fix `ColourHitErrorMeter` not loading pooled drawables ahead of time --- .../HUD/HitErrorMeters/ColourHitErrorMeter.cs | 27 +++++++++---------- 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs index 65f4b50dde..0f2f9dc323 100644 --- a/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs +++ b/osu.Game/Screens/Play/HUD/HitErrorMeters/ColourHitErrorMeter.cs @@ -15,7 +15,6 @@ using osu.Game.Localisation.HUD; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; using osuTK; -using osuTK.Graphics; namespace osu.Game.Screens.Play.HUD.HitErrorMeters { @@ -42,16 +41,21 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters [SettingSource(typeof(ColourHitErrorMeterStrings), nameof(ColourHitErrorMeterStrings.JudgementShape), nameof(ColourHitErrorMeterStrings.JudgementShapeDescription))] public Bindable JudgementShape { get; } = new Bindable(); + private readonly DrawablePool judgementShapePool; private readonly JudgementFlow judgementsFlow; public ColourHitErrorMeter() { AutoSizeAxes = Axes.Both; - InternalChild = judgementsFlow = new JudgementFlow + InternalChildren = new Drawable[] { - JudgementShape = { BindTarget = JudgementShape }, - JudgementSpacing = { BindTarget = JudgementSpacing }, - JudgementCount = { BindTarget = JudgementCount } + judgementShapePool = new DrawablePool(50), + judgementsFlow = new JudgementFlow + { + JudgementShape = { BindTarget = JudgementShape }, + JudgementSpacing = { BindTarget = JudgementSpacing }, + JudgementCount = { BindTarget = JudgementCount } + } }; } @@ -60,7 +64,7 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters if (!judgement.Type.IsScorable() || judgement.Type.IsBonus()) return; - judgementsFlow.Push(GetColourForHitResult(judgement.Type)); + judgementsFlow.Push(judgementShapePool.Get(shape => shape.Colour = GetColourForHitResult(judgement.Type))); } public override void Clear() @@ -105,15 +109,10 @@ namespace osu.Game.Screens.Play.HUD.HitErrorMeters private readonly DrawablePool judgementLinePool = new DrawablePool(50); - public void Push(Color4 colour) + public void Push(HitErrorShape shape) { - judgementLinePool.Get(shape => - { - shape.Colour = colour; - Add(shape); - - removeExtraJudgements(); - }); + Add(shape); + removeExtraJudgements(); } private void removeExtraJudgements() From 09e5b2fb46748d7c03ac7764e9170d2c860ac7ee Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 13:18:43 +0900 Subject: [PATCH 1001/1118] Add test coverage of incorrect default state for `Rank` when hidden is applied --- .../Rulesets/Scoring/ScoreProcessorTest.cs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs index 567bf6968f..73465fae08 100644 --- a/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs +++ b/osu.Game.Tests/Rulesets/Scoring/ScoreProcessorTest.cs @@ -18,10 +18,12 @@ using osu.Game.Rulesets.Mods; using osu.Game.Rulesets.Objects; using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Judgements; +using osu.Game.Rulesets.Osu.Mods; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.UI; +using osu.Game.Scoring; using osu.Game.Scoring.Legacy; using osu.Game.Tests.Beatmaps; @@ -385,6 +387,42 @@ namespace osu.Game.Tests.Rulesets.Scoring Assert.That(scoreProcessor.Accuracy.Value, Is.Not.EqualTo(1)); } + [Test] + public void TestNormalGrades() + { + scoreProcessor.ApplyBeatmap(new Beatmap()); + + Assert.That(scoreProcessor.Rank.Value, Is.EqualTo(ScoreRank.X)); + + scoreProcessor.Accuracy.Value = 0.99f; + Assert.That(scoreProcessor.Rank.Value, Is.EqualTo(ScoreRank.S)); + } + + [Test] + public void TestSilverGrades() + { + scoreProcessor.ApplyBeatmap(new Beatmap()); + Assert.That(scoreProcessor.Rank.Value, Is.EqualTo(ScoreRank.X)); + + scoreProcessor.Mods.Value = new[] { new OsuModHidden() }; + Assert.That(scoreProcessor.Rank.Value, Is.EqualTo(ScoreRank.XH)); + + scoreProcessor.Accuracy.Value = 0.99f; + Assert.That(scoreProcessor.Rank.Value, Is.EqualTo(ScoreRank.SH)); + } + + [Test] + public void TestSilverGradesModsAppliedFirst() + { + scoreProcessor.Mods.Value = new[] { new OsuModHidden() }; + scoreProcessor.ApplyBeatmap(new Beatmap()); + + Assert.That(scoreProcessor.Rank.Value, Is.EqualTo(ScoreRank.XH)); + + scoreProcessor.Accuracy.Value = 0.99f; + Assert.That(scoreProcessor.Rank.Value, Is.EqualTo(ScoreRank.SH)); + } + private class TestJudgement : Judgement { public override HitResult MaxResult { get; } From 902a5436f3e2c2f330bcaae176d07f252f26e14a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 13:12:22 +0900 Subject: [PATCH 1002/1118] Fix silver S/SS not being awarded correctly --- osu.Game/Rulesets/Scoring/ScoreProcessor.cs | 25 ++++++++++++--------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs index 14fa928224..80e751422e 100644 --- a/osu.Game/Rulesets/Scoring/ScoreProcessor.cs +++ b/osu.Game/Rulesets/Scoring/ScoreProcessor.cs @@ -186,16 +186,7 @@ namespace osu.Game.Rulesets.Scoring Ruleset = ruleset; Combo.ValueChanged += combo => HighestCombo.Value = Math.Max(HighestCombo.Value, combo.NewValue); - Accuracy.ValueChanged += accuracy => - { - // Once failed, we shouldn't update the rank anymore. - if (rank.Value == ScoreRank.F) - return; - - rank.Value = RankFromAccuracy(accuracy.NewValue); - foreach (var mod in Mods.Value.OfType()) - rank.Value = mod.AdjustRank(Rank.Value, accuracy.NewValue); - }; + Accuracy.ValueChanged += _ => updateRank(); Mods.ValueChanged += mods => { @@ -205,6 +196,7 @@ namespace osu.Game.Rulesets.Scoring scoreMultiplier *= m.ScoreMultiplier; updateScore(); + updateRank(); }; } @@ -372,6 +364,17 @@ namespace osu.Game.Rulesets.Scoring TotalScore.Value = (long)Math.Round(ComputeTotalScore(comboProgress, accuracyProcess, currentBonusPortion) * scoreMultiplier); } + private void updateRank() + { + // Once failed, we shouldn't update the rank anymore. + if (rank.Value == ScoreRank.F) + return; + + rank.Value = RankFromAccuracy(Accuracy.Value); + foreach (var mod in Mods.Value.OfType()) + rank.Value = mod.AdjustRank(Rank.Value, Accuracy.Value); + } + protected virtual double ComputeTotalScore(double comboProgress, double accuracyProgress, double bonusPortion) { return 500000 * Accuracy.Value * comboProgress + @@ -417,8 +420,8 @@ namespace osu.Game.Rulesets.Scoring TotalScore.Value = 0; Accuracy.Value = 1; Combo.Value = 0; - rank.Value = ScoreRank.X; HighestCombo.Value = 0; + updateRank(); } /// From e75f113a06c4c4d9ec229857ed580d5747c1f143 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 14:14:04 +0900 Subject: [PATCH 1003/1118] Update resources --- osu.Game/osu.Game.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index 07a638f04a..f33ddac66f 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -37,7 +37,7 @@ - + From 34905b20527bf7adf618506975feab634f7632b4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 14:17:21 +0900 Subject: [PATCH 1004/1118] Apply NRT to new classes --- .../ArgonHealthDisplayBackground.cs | 12 +++--------- .../ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs | 6 ++---- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs index a96c2f97bd..b486465cb0 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBackground.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System.Runtime.InteropServices; using osu.Framework.Allocation; using osu.Framework.Graphics; @@ -29,6 +27,8 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts { protected new ArgonHealthDisplayBackground Source => (ArgonHealthDisplayBackground)base.Source; + private IUniformBuffer? parametersBuffer; + public ArgonBarPathDrawNode(ArgonHealthDisplayBackground source) : base(source) { @@ -39,21 +39,15 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts public override void ApplyState() { base.ApplyState(); - size = Source.DrawSize; } - private IUniformBuffer parametersBuffer; - protected override void BindUniformResources(IShader shader, IRenderer renderer) { base.BindUniformResources(shader, renderer); parametersBuffer ??= renderer.CreateUniformBuffer(); - parametersBuffer.Data = new ArgonBarPathBackgroundParameters - { - Size = size - }; + parametersBuffer.Data = new ArgonBarPathBackgroundParameters { Size = size }; shader.BindUniformBlock("m_ArgonBarPathBackgroundParameters", parametersBuffer); } diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs index 1938b97d5a..28e56183bf 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplayParts/ArgonHealthDisplayBar.cs @@ -1,8 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -#nullable disable - using System; using System.Runtime.InteropServices; using osu.Framework.Allocation; @@ -107,6 +105,8 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts { protected new ArgonHealthDisplayBar Source => (ArgonHealthDisplayBar)base.Source; + private IUniformBuffer? parametersBuffer; + public ArgonBarPathDrawNode(ArgonHealthDisplayBar source) : base(source) { @@ -139,8 +139,6 @@ namespace osu.Game.Screens.Play.HUD.ArgonHealthDisplayParts base.Draw(renderer); } - private IUniformBuffer parametersBuffer; - protected override void BindUniformResources(IShader shader, IRenderer renderer) { base.BindUniformResources(shader, renderer); From 451ba1c86131e61245d4d3a9eb232cc47c32ff7e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 15:12:23 +0900 Subject: [PATCH 1005/1118] Ensure `PresentGameplay` doesn't get stuck in loop if no beatmaps available --- osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs index bedaf12c9b..40cd31934f 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditorOverlay.cs @@ -136,10 +136,15 @@ namespace osu.Game.Overlays.SkinEditor globallyReenableBeatmapSkinSetting(); } - public void PresentGameplay() + public void PresentGameplay() => presentGameplay(false); + + private void presentGameplay(bool attemptedBeatmapSwitch) { performer?.PerformFromScreen(screen => { + if (State.Value != Visibility.Visible) + return; + if (beatmap.Value is DummyWorkingBeatmap) { // presume we don't have anything good to play and just bail. @@ -149,8 +154,12 @@ namespace osu.Game.Overlays.SkinEditor // If we're playing the intro, switch away to another beatmap. if (beatmap.Value.BeatmapSetInfo.Protected) { - music.NextTrack(); - Schedule(PresentGameplay); + if (!attemptedBeatmapSwitch) + { + music.NextTrack(); + Schedule(() => presentGameplay(true)); + } + return; } From e3ab7b1e9fd60ac15ae3e3e838425290512cf517 Mon Sep 17 00:00:00 2001 From: Justin Date: Tue, 16 Jan 2024 17:22:22 +1100 Subject: [PATCH 1006/1118] Update disctop to use turnratio like stable --- .../Skinning/Legacy/LegacyNewStyleSpinner.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs index 67a6d5e41a..d4a0f243e4 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyNewStyleSpinner.cs @@ -135,7 +135,11 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy protected override void Update() { base.Update(); - spinningMiddle.Rotation = discTop.Rotation = DrawableSpinner.RotationTracker.Rotation; + + float turnRatio = spinningMiddle.Texture != null ? 0.5f : 1; + discTop.Rotation = DrawableSpinner.RotationTracker.Rotation * turnRatio; + spinningMiddle.Rotation = DrawableSpinner.RotationTracker.Rotation; + discBottom.Rotation = discTop.Rotation / 3; glow.Alpha = DrawableSpinner.Progress; From 0e41d0c9cf1cc46da66bd1c0d11dbc4091ea644a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 15:21:43 +0900 Subject: [PATCH 1007/1118] Add failing test coverage of skin getting nuked This doesn't fail in headless unfortunately. Run a few times manually to confirm. --- .../Navigation/TestSceneScreenNavigation.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs index 8cb993eff2..f59fbc75ac 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneScreenNavigation.cs @@ -35,6 +35,7 @@ using osu.Game.Screens.OnlinePlay.Lounge; using osu.Game.Screens.OnlinePlay.Match.Components; using osu.Game.Screens.OnlinePlay.Playlists; using osu.Game.Screens.Play; +using osu.Game.Screens.Play.HUD; using osu.Game.Screens.Ranking; using osu.Game.Screens.Select; using osu.Game.Screens.Select.Carousel; @@ -834,6 +835,24 @@ namespace osu.Game.Tests.Visual.Navigation AddAssert("exit dialog is shown", () => Game.Dependencies.Get().CurrentDialog is ConfirmExitDialog); } + [Test] + public void TestQuickSkinEditorDoesntNukeSkin() + { + AddStep("import beatmap", () => BeatmapImportHelper.LoadQuickOszIntoOsu(Game).WaitSafely()); + + AddStep("open", () => InputManager.Key(Key.Space)); + AddStep("skin", () => InputManager.Key(Key.E)); + AddStep("editor", () => InputManager.Key(Key.S)); + AddStep("and close immediately", () => InputManager.Key(Key.Escape)); + + AddStep("open again", () => InputManager.Key(Key.S)); + + Player player = null; + + AddUntilStep("wait for player", () => (player = Game.ScreenStack.CurrentScreen as Player) != null); + AddUntilStep("wait for gameplay still has health bar", () => player.ChildrenOfType().Any()); + } + [Test] public void TestTouchScreenDetectionAtSongSelect() { From cd02d00c0352eed86d4f2e5ec9aaf3bcd61a0102 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 15:23:07 +0900 Subject: [PATCH 1008/1118] Fix skin potentially being lost when opening and closing skin editor rapidly --- osu.Game/Overlays/SkinEditor/SkinEditor.cs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/SkinEditor/SkinEditor.cs b/osu.Game/Overlays/SkinEditor/SkinEditor.cs index f972186333..d3af928907 100644 --- a/osu.Game/Overlays/SkinEditor/SkinEditor.cs +++ b/osu.Game/Overlays/SkinEditor/SkinEditor.cs @@ -47,7 +47,7 @@ namespace osu.Game.Overlays.SkinEditor protected override bool StartHidden => true; - private Drawable targetScreen = null!; + private Drawable? targetScreen; private OsuTextFlowContainer headerText = null!; @@ -541,8 +541,14 @@ namespace osu.Game.Overlays.SkinEditor if (!hasBegunMutating) return; + if (targetScreen?.IsLoaded != true) + return; + SkinComponentsContainer[] targetContainers = availableTargets.ToArray(); + if (!targetContainers.All(c => c.ComponentsLoaded)) + return; + foreach (var t in targetContainers) currentSkin.Value.UpdateDrawableTarget(t); From 57a6025a2c492c897531cbb56ae0bdaf136dd03c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 15:52:05 +0900 Subject: [PATCH 1009/1118] Add helper method to bypass judgement woes --- osu.Game/Rulesets/Judgements/JudgementResult.cs | 5 +++++ osu.Game/Rulesets/Scoring/HealthProcessor.cs | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/Judgements/JudgementResult.cs b/osu.Game/Rulesets/Judgements/JudgementResult.cs index 1b915d52b7..b781a13929 100644 --- a/osu.Game/Rulesets/Judgements/JudgementResult.cs +++ b/osu.Game/Rulesets/Judgements/JudgementResult.cs @@ -94,6 +94,11 @@ namespace osu.Game.Rulesets.Judgements /// public bool IsHit => Type.IsHit(); + /// + /// The increase in health resulting from this judgement result. + /// + public double HealthIncrease => Judgement.HealthIncreaseFor(this); + /// /// Creates a new . /// diff --git a/osu.Game/Rulesets/Scoring/HealthProcessor.cs b/osu.Game/Rulesets/Scoring/HealthProcessor.cs index 3e0b6433c2..b5eb755650 100644 --- a/osu.Game/Rulesets/Scoring/HealthProcessor.cs +++ b/osu.Game/Rulesets/Scoring/HealthProcessor.cs @@ -66,7 +66,7 @@ namespace osu.Game.Rulesets.Scoring /// /// The . /// The health increase. - protected virtual double GetHealthIncreaseFor(JudgementResult result) => result.Judgement.HealthIncreaseFor(result); + protected virtual double GetHealthIncreaseFor(JudgementResult result) => result.HealthIncrease; /// /// The default conditions for failing. From 2be8d66d4c75cee98d9aa0e5b60c99f475bb555c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 15:52:18 +0900 Subject: [PATCH 1010/1118] Fix argon health bar showing "miss" bar for bananas --- osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs index 6fc957874a..71996718d9 100644 --- a/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs +++ b/osu.Game/Screens/Play/HUD/ArgonHealthDisplay.cs @@ -136,7 +136,12 @@ namespace osu.Game.Screens.Play.HUD BarHeight.BindValueChanged(_ => updateContentSize(), true); } - private void onNewJudgement(JudgementResult result) => pendingMissAnimation |= !result.IsHit; + private void onNewJudgement(JudgementResult result) + { + // Check the health increase because cases like osu!catch bananas fire `IgnoreMiss`, + // which counts as a miss but doesn't actually subtract any health. + pendingMissAnimation |= !result.IsHit && result.HealthIncrease < 0; + } protected override void Update() { From cdc6621f33f1365cb212e8663e0f948e7dc34bf5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 16:37:05 +0900 Subject: [PATCH 1011/1118] Allow adjusting volume controls via a drag --- osu.Game/Overlays/Volume/VolumeMeter.cs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index d366f0bddb..6ad314c601 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -309,10 +309,24 @@ namespace osu.Game.Overlays.Volume private const double max_acceleration = 5; private const double acceleration_multiplier = 1.8; + private const float mouse_drag_divisor = 20; + private ScheduledDelegate accelerationDebounce; private void resetAcceleration() => accelerationModifier = 1; + protected override bool OnDragStart(DragStartEvent e) + { + adjust(-e.Delta.Y / mouse_drag_divisor, true); + return true; + } + + protected override void OnDrag(DragEvent e) + { + adjust(-e.Delta.Y / mouse_drag_divisor, true); + base.OnDrag(e); + } + private void adjust(double delta, bool isPrecise) { if (delta == 0) From ee26329353ec41e9a9859aad8fc7000760bfac66 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 16:44:50 +0900 Subject: [PATCH 1012/1118] Fix some elements not showing on leaderboard scores when almost off-screen --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index a76f4ae955..1c2a26eafb 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -164,7 +164,8 @@ namespace osu.Game.Online.Leaderboards { Anchor = Anchor.BottomLeft, Origin = Anchor.BottomLeft, - AutoSizeAxes = Axes.Both, + AutoSizeAxes = Axes.X, + Height = 28, Direction = FillDirection.Horizontal, Spacing = new Vector2(10f, 0f), Children = new Drawable[] From c45daa373e24070c78379c71c57d6c1ede6c6c31 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 16 Jan 2024 17:15:25 +0900 Subject: [PATCH 1013/1118] Fix cursor scale animation not matching stable on classic skins --- .../Skinning/Argon/ArgonCursor.cs | 2 +- .../Skinning/Legacy/LegacyCursor.cs | 16 +++++++++- osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs | 11 +++---- .../UI/Cursor/OsuCursorSprite.cs | 19 ------------ .../UI/Cursor/SkinnableCursor.cs | 31 +++++++++++++++++++ 5 files changed, 51 insertions(+), 28 deletions(-) delete mode 100644 osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs create mode 100644 osu.Game.Rulesets.Osu/UI/Cursor/SkinnableCursor.cs diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonCursor.cs index 4ca6abfdf7..15838f3e1b 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonCursor.cs @@ -13,7 +13,7 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Argon { - public partial class ArgonCursor : OsuCursorSprite + public partial class ArgonCursor : SkinnableCursor { public ArgonCursor() { diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs index b0c01d2925..375d81049d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyCursor.cs @@ -9,8 +9,11 @@ using osuTK; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - public partial class LegacyCursor : OsuCursorSprite + public partial class LegacyCursor : SkinnableCursor { + private const float pressed_scale = 1.3f; + private const float released_scale = 1f; + private readonly ISkin skin; private bool spin; @@ -51,5 +54,16 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy if (spin) ExpandTarget.Spin(10000, RotationDirection.Clockwise); } + + public override void Expand() + { + ExpandTarget?.ScaleTo(released_scale) + .ScaleTo(pressed_scale, 100, Easing.Out); + } + + public override void Contract() + { + ExpandTarget?.ScaleTo(released_scale, 100, Easing.Out); + } } } diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs index 710ca9ace7..d8f50c1f5d 100644 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs +++ b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursor.cs @@ -24,15 +24,12 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { public const float SIZE = 28; - private const float pressed_scale = 1.2f; - private const float released_scale = 1f; - private bool cursorExpand; private SkinnableDrawable cursorSprite; private Container cursorScaleContainer = null!; - private Drawable expandTarget => (cursorSprite.Drawable as OsuCursorSprite)?.ExpandTarget ?? cursorSprite; + private SkinnableCursor skinnableCursor => (SkinnableCursor)cursorSprite.Drawable; public IBindable CursorScale => cursorScale; @@ -108,10 +105,10 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor { if (!cursorExpand) return; - expandTarget.ScaleTo(released_scale).ScaleTo(pressed_scale, 400, Easing.OutElasticHalf); + skinnableCursor.Expand(); } - public void Contract() => expandTarget.ScaleTo(released_scale, 400, Easing.OutQuad); + public void Contract() => skinnableCursor.Contract(); /// /// Get the scale applicable to the ActiveCursor based on a beatmap's circle size. @@ -119,7 +116,7 @@ namespace osu.Game.Rulesets.Osu.UI.Cursor public static float GetScaleForCircleSize(float circleSize) => 1f - 0.7f * (1f + circleSize - BeatmapDifficulty.DEFAULT_DIFFICULTY) / BeatmapDifficulty.DEFAULT_DIFFICULTY; - private partial class DefaultCursor : OsuCursorSprite + private partial class DefaultCursor : SkinnableCursor { public DefaultCursor() { diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs b/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs deleted file mode 100644 index aaf8949084..0000000000 --- a/osu.Game.Rulesets.Osu/UI/Cursor/OsuCursorSprite.cs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. -// See the LICENCE file in the repository root for full licence text. - -#nullable disable - -using osu.Framework.Graphics; -using osu.Framework.Graphics.Containers; - -namespace osu.Game.Rulesets.Osu.UI.Cursor -{ - public abstract partial class OsuCursorSprite : CompositeDrawable - { - /// - /// The an optional piece of the cursor to expand when in a clicked state. - /// If null, the whole cursor will be affected by expansion. - /// - public Drawable ExpandTarget { get; protected set; } - } -} diff --git a/osu.Game.Rulesets.Osu/UI/Cursor/SkinnableCursor.cs b/osu.Game.Rulesets.Osu/UI/Cursor/SkinnableCursor.cs new file mode 100644 index 0000000000..09e6f989a4 --- /dev/null +++ b/osu.Game.Rulesets.Osu/UI/Cursor/SkinnableCursor.cs @@ -0,0 +1,31 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Graphics; +using osu.Framework.Graphics.Containers; + +namespace osu.Game.Rulesets.Osu.UI.Cursor +{ + public abstract partial class SkinnableCursor : CompositeDrawable + { + private const float pressed_scale = 1.2f; + private const float released_scale = 1f; + + public virtual void Expand() + { + ExpandTarget?.ScaleTo(released_scale) + .ScaleTo(pressed_scale, 400, Easing.OutElasticHalf); + } + + public virtual void Contract() + { + ExpandTarget?.ScaleTo(released_scale, 400, Easing.OutQuad); + } + + /// + /// The an optional piece of the cursor to expand when in a clicked state. + /// If null, the whole cursor will be affected by expansion. + /// + public Drawable? ExpandTarget { get; protected set; } + } +} From 2935bd4809d1e071d8feb93604629fa94fb2cbbc Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 16 Jan 2024 14:27:24 +0300 Subject: [PATCH 1014/1118] Add test cases for helper method --- osu.Game.Tests/Mods/ModUtilsTest.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/osu.Game.Tests/Mods/ModUtilsTest.cs b/osu.Game.Tests/Mods/ModUtilsTest.cs index 13da69871e..decb0a31ac 100644 --- a/osu.Game.Tests/Mods/ModUtilsTest.cs +++ b/osu.Game.Tests/Mods/ModUtilsTest.cs @@ -7,7 +7,9 @@ using Moq; using NUnit.Framework; using osu.Framework.Localisation; using osu.Game.Rulesets.Mods; +using osu.Game.Rulesets.Osu; using osu.Game.Rulesets.Osu.Mods; +using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Utils; namespace osu.Game.Tests.Mods @@ -310,6 +312,16 @@ namespace osu.Game.Tests.Mods Assert.That(invalid?.Select(t => t.GetType()), Is.EquivalentTo(expectedInvalid)); } + [Test] + public void TestModBelongsToRuleset() + { + Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), Array.Empty())); + Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), new Mod[] { new OsuModDoubleTime() })); + Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), new Mod[] { new OsuModDoubleTime(), new OsuModAccuracyChallenge() })); + Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), new Mod[] { new OsuModDoubleTime(), new ModAccuracyChallenge() }), Is.False); + Assert.That(ModUtils.CheckModsBelongToRuleset(new OsuRuleset(), new Mod[] { new OsuModDoubleTime(), new TaikoModFlashlight() }), Is.False); + } + [Test] public void TestFormatScoreMultiplier() { From 90da39c65d252e157b8a550429e18c944cd65380 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 16 Jan 2024 14:27:30 +0300 Subject: [PATCH 1015/1118] Fix helper method returning incorrect result --- osu.Game/Utils/ModUtils.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Utils/ModUtils.cs b/osu.Game/Utils/ModUtils.cs index 7a9f93e06f..2c9eef41e3 100644 --- a/osu.Game/Utils/ModUtils.cs +++ b/osu.Game/Utils/ModUtils.cs @@ -255,7 +255,7 @@ namespace osu.Game.Utils } if (!found) - return true; + return false; } return true; From 485195d4c25d3b8f13f52f6da7faf72472a21b0e Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Tue, 16 Jan 2024 14:27:40 +0300 Subject: [PATCH 1016/1118] Fix submission test not asserting properly --- osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index 833cb24f41..e2ce3a014c 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -294,6 +294,7 @@ namespace osu.Game.Tests.Visual.Gameplay createPlayerTest(createRuleset: () => new OsuRuleset(), createMods: () => new Mod[] { new TaikoModHidden() }); AddUntilStep("wait for token request", () => Player.TokenCreationRequested); + AddAssert("gameplay not loaded", () => Player.DrawableRuleset == null); AddStep("exit", () => Player.Exit()); AddAssert("ensure no submission", () => Player.SubmittedScore == null); From 7920e93fa91dbe14a610591cb4446a3c08d63819 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 16 Jan 2024 17:36:46 +0300 Subject: [PATCH 1017/1118] Rework GlowingSpriteText --- .../Graphics/Sprites/GlowingSpriteText.cs | 85 +++++++++---------- 1 file changed, 40 insertions(+), 45 deletions(-) diff --git a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs index 1355bfc272..669c5da01e 100644 --- a/osu.Game/Graphics/Sprites/GlowingSpriteText.cs +++ b/osu.Game/Graphics/Sprites/GlowingSpriteText.cs @@ -5,95 +5,90 @@ using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Colour; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.Primitives; using osu.Framework.Graphics.Sprites; using osu.Framework.Localisation; +using osu.Framework.Utils; using osuTK; namespace osu.Game.Graphics.Sprites { - public partial class GlowingSpriteText : Container, IHasText + public partial class GlowingSpriteText : BufferedContainer, IHasText { - private readonly OsuSpriteText spriteText, blurredText; + private const float blur_sigma = 3f; + + // Inflate draw quad to prevent glow from trimming at the edges. + // Padding won't suffice since it will affect text position in cases when it's not centered. + protected override Quad ComputeScreenSpaceDrawQuad() => base.ComputeScreenSpaceDrawQuad().AABBFloat.Inflate(Blur.KernelSize(blur_sigma)); + + private readonly OsuSpriteText text; public LocalisableString Text { - get => spriteText.Text; - set => blurredText.Text = spriteText.Text = value; + get => text.Text; + set => text.Text = value; } public FontUsage Font { - get => spriteText.Font; - set => blurredText.Font = spriteText.Font = value.With(fixedWidth: true); + get => text.Font; + set => text.Font = value.With(fixedWidth: true); } public Vector2 TextSize { - get => spriteText.Size; - set => blurredText.Size = spriteText.Size = value; + get => text.Size; + set => text.Size = value; } public ColourInfo TextColour { - get => spriteText.Colour; - set => spriteText.Colour = value; + get => text.Colour; + set => text.Colour = value; } public ColourInfo GlowColour { - get => blurredText.Colour; - set => blurredText.Colour = value; + get => EffectColour; + set + { + EffectColour = value; + BackgroundColour = value.MultiplyAlpha(0f); + } } public Vector2 Spacing { - get => spriteText.Spacing; - set => spriteText.Spacing = blurredText.Spacing = value; + get => text.Spacing; + set => text.Spacing = value; } public bool UseFullGlyphHeight { - get => spriteText.UseFullGlyphHeight; - set => spriteText.UseFullGlyphHeight = blurredText.UseFullGlyphHeight = value; + get => text.UseFullGlyphHeight; + set => text.UseFullGlyphHeight = value; } public Bindable Current { - get => spriteText.Current; - set => spriteText.Current = value; + get => text.Current; + set => text.Current = value; } public GlowingSpriteText() + : base(cachedFrameBuffer: true) { AutoSizeAxes = Axes.Both; - - Children = new Drawable[] + BlurSigma = new Vector2(blur_sigma); + RedrawOnScale = false; + DrawOriginal = true; + EffectBlending = BlendingParameters.Additive; + EffectPlacement = EffectPlacement.InFront; + Child = text = new OsuSpriteText { - new BufferedContainer(cachedFrameBuffer: true) - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - BlurSigma = new Vector2(4), - RedrawOnScale = false, - RelativeSizeAxes = Axes.Both, - Blending = BlendingParameters.Additive, - Size = new Vector2(3f), - Children = new[] - { - blurredText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Shadow = false, - }, - }, - }, - spriteText = new OsuSpriteText - { - Anchor = Anchor.Centre, - Origin = Anchor.Centre, - Shadow = false, - }, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Shadow = false, }; } } From dfdf700560726d0f8bd87408b3cecd9b0e373f4c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 16 Jan 2024 17:39:37 +0300 Subject: [PATCH 1018/1118] Don't use glowing text in ScoreComponentLabel --- osu.Game/Online/Leaderboards/LeaderboardScore.cs | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/osu.Game/Online/Leaderboards/LeaderboardScore.cs b/osu.Game/Online/Leaderboards/LeaderboardScore.cs index 1c2a26eafb..964f065813 100644 --- a/osu.Game/Online/Leaderboards/LeaderboardScore.cs +++ b/osu.Game/Online/Leaderboards/LeaderboardScore.cs @@ -358,14 +358,12 @@ namespace osu.Game.Online.Leaderboards }, }, }, - new GlowingSpriteText + new OsuSpriteText { Anchor = Anchor.CentreLeft, Origin = Anchor.CentreLeft, - TextColour = Color4.White, - GlowColour = Color4Extensions.FromHex(@"83ccfa"), Text = statistic.Value, - Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold), + Font = OsuFont.GetFont(size: 17, weight: FontWeight.Bold, fixedWidth: true) }, }, }; From 7a7548e89d91849992461c759a01a7e4f5cf97fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 17:09:04 +0100 Subject: [PATCH 1019/1118] Add failing test coverage --- .../Mods/TestSceneOsuModTouchDevice.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index cd51ccd751..5b5ad07fde 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -51,7 +51,6 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods public void TestUserAlreadyHasTouchDeviceActive() { loadPlayer(); - // it is presumed that a previous screen (i.e. song select) will set this up AddStep("set up touchscreen user", () => { currentPlayer.Score.ScoreInfo.Mods = currentPlayer.Score.ScoreInfo.Mods.Append(new OsuModTouchDevice()).ToArray(); @@ -69,6 +68,14 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); } + [Test] + public void TestTouchActivePriorToPlayerLoad() + { + AddStep("set touch input active", () => statics.SetValue(Static.TouchInputActive, true)); + loadPlayer(); + AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); + } + [Test] public void TestTouchDuringBreak() { From a9d086c11954c618e12301eb762edf2df8931187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 17:12:40 +0100 Subject: [PATCH 1020/1118] Fix touch device not always activating when it should See https://github.com/ppy/osu/discussions/26574 --- osu.Game/Screens/Play/PlayerTouchInputDetector.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/PlayerTouchInputDetector.cs b/osu.Game/Screens/Play/PlayerTouchInputDetector.cs index 69c3cd0ded..12fb748e7d 100644 --- a/osu.Game/Screens/Play/PlayerTouchInputDetector.cs +++ b/osu.Game/Screens/Play/PlayerTouchInputDetector.cs @@ -20,12 +20,16 @@ namespace osu.Game.Screens.Play private GameplayState gameplayState { get; set; } = null!; private IBindable touchActive = new BindableBool(); + private IBindable isBreakTime = null!; [BackgroundDependencyLoader] private void load(SessionStatics statics) { touchActive = statics.GetBindable(Static.TouchInputActive); touchActive.BindValueChanged(_ => updateState()); + + isBreakTime = player.IsBreakTime.GetBoundCopy(); + isBreakTime.BindValueChanged(_ => updateState(), true); } private void updateState() @@ -39,7 +43,7 @@ namespace osu.Game.Screens.Play if (gameplayState.Score.ScoreInfo.Mods.OfType().Any()) return; - if (player.IsBreakTime.Value) + if (isBreakTime.Value) return; var touchDeviceMod = gameplayState.Ruleset.GetTouchDeviceMod(); From b16152811d0266905db40187cfbaefe6ebe8c434 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 17:54:49 +0100 Subject: [PATCH 1021/1118] Use until step instead --- osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs index 5b5ad07fde..fdb1cac3e5 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModTouchDevice.cs @@ -73,7 +73,7 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods { AddStep("set touch input active", () => statics.SetValue(Static.TouchInputActive, true)); loadPlayer(); - AddAssert("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); + AddUntilStep("touch device mod activated", () => currentPlayer.Score.ScoreInfo.Mods, () => Has.One.InstanceOf()); } [Test] From e9e5a0b08cfd0fca03443cd0d25e00b14382934d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 18:41:49 +0100 Subject: [PATCH 1022/1118] Add failing test case --- .../Mods/TestSceneOsuModFlashlight.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs index defbb97c8a..1a786b99e8 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs @@ -77,5 +77,44 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Autoplay = false, }); } + + [Test] + public void TestSliderDoesDimAfterStartTimeIfHitEarly() + { + bool sliderDimmed = false; + + CreateModTest(new ModTestData + { + Mod = new OsuModFlashlight(), + PassCondition = () => + { + sliderDimmed |= + Player.GameplayClockContainer.CurrentTime >= 1000 && Player.ChildrenOfType.Flashlight>().Single().FlashlightDim > 0; + return Player.GameplayState.HasPassed && sliderDimmed; + }, + Beatmap = new OsuBeatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 1000, + Path = new SliderPath(new[] + { + new PathControlPoint(), + new PathControlPoint(new Vector2(100)) + }) + } + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(990, new Vector2(), OsuAction.LeftButton), + new OsuReplayFrame(2000, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(2001, new Vector2(100)), + }, + Autoplay = false, + }); + } } } From d0e9402761a37a393d3048689ef72bb287f26237 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 18:47:00 +0100 Subject: [PATCH 1023/1118] Fix flashlight not dimming if slider head is hit early Closes https://github.com/ppy/osu/issues/26551 Fix is a bit nuclear (`OnUpdate` should be considered last resort), but I don't see many better alternatives here as `ApplyCustomUpdateState` does not work... --- osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs index 46c9bdd17f..5a6cc50082 100644 --- a/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu/Mods/OsuModFlashlight.cs @@ -50,7 +50,7 @@ namespace osu.Game.Rulesets.Osu.Mods public void ApplyToDrawableHitObject(DrawableHitObject drawable) { if (drawable is DrawableSlider s) - s.Tracking.ValueChanged += _ => flashlight.OnSliderTrackingChange(s); + s.OnUpdate += _ => flashlight.OnSliderTrackingChange(s); } private partial class OsuFlashlight : Flashlight, IRequireHighFrequencyMousePosition From 24741e96156ebaed5e1abd4f26c090b254d869ab Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 18:54:22 +0100 Subject: [PATCH 1024/1118] Add some more test coverage for good measure --- .../Mods/TestSceneOsuModFlashlight.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs index 1a786b99e8..776d5854d1 100644 --- a/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs +++ b/osu.Game.Rulesets.Osu.Tests/Mods/TestSceneOsuModFlashlight.cs @@ -116,5 +116,44 @@ namespace osu.Game.Rulesets.Osu.Tests.Mods Autoplay = false, }); } + + [Test] + public void TestSliderDoesDimAfterStartTimeIfHitLate() + { + bool sliderDimmed = false; + + CreateModTest(new ModTestData + { + Mod = new OsuModFlashlight(), + PassCondition = () => + { + sliderDimmed |= + Player.GameplayClockContainer.CurrentTime >= 1000 && Player.ChildrenOfType.Flashlight>().Single().FlashlightDim > 0; + return Player.GameplayState.HasPassed && sliderDimmed; + }, + Beatmap = new OsuBeatmap + { + HitObjects = new List + { + new Slider + { + StartTime = 1000, + Path = new SliderPath(new[] + { + new PathControlPoint(), + new PathControlPoint(new Vector2(100)) + }) + } + }, + }, + ReplayFrames = new List + { + new OsuReplayFrame(1100, new Vector2(), OsuAction.LeftButton), + new OsuReplayFrame(2000, new Vector2(100), OsuAction.LeftButton), + new OsuReplayFrame(2001, new Vector2(100)), + }, + Autoplay = false, + }); + } } } From 265a56bede4c66e0632e6a6dad6cf6ed0d7bedd2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 19:40:01 +0100 Subject: [PATCH 1025/1118] Add basic test coverage of taiko health processor --- .../TaikoHealthProcessorTest.cs | 110 ++++++++++++++++++ 1 file changed, 110 insertions(+) create mode 100644 osu.Game.Rulesets.Taiko.Tests/TaikoHealthProcessorTest.cs diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoHealthProcessorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoHealthProcessorTest.cs new file mode 100644 index 0000000000..51dcf1ba91 --- /dev/null +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoHealthProcessorTest.cs @@ -0,0 +1,110 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using NUnit.Framework; +using osu.Game.Rulesets.Judgements; +using osu.Game.Rulesets.Scoring; +using osu.Game.Rulesets.Taiko.Beatmaps; +using osu.Game.Rulesets.Taiko.Judgements; +using osu.Game.Rulesets.Taiko.Objects; +using osu.Game.Rulesets.Taiko.Scoring; + +namespace osu.Game.Rulesets.Taiko.Tests +{ + [TestFixture] + public class TaikoHealthProcessorTest + { + [Test] + public void TestHitsOnlyGreat() + { + var beatmap = new TaikoBeatmap + { + HitObjects = + { + new Hit(), + new Hit { StartTime = 1000 }, + new Hit { StartTime = 2000 }, + new Hit { StartTime = 3000 }, + new Hit { StartTime = 4000 }, + } + }; + + var healthProcessor = new TaikoHealthProcessor(); + healthProcessor.ApplyBeatmap(beatmap); + + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TaikoJudgement()) { Type = HitResult.Great }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], new TaikoJudgement()) { Type = HitResult.Great }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[2], new TaikoJudgement()) { Type = HitResult.Great }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[3], new TaikoJudgement()) { Type = HitResult.Great }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[4], new TaikoJudgement()) { Type = HitResult.Great }); + + Assert.Multiple(() => + { + Assert.That(healthProcessor.Health.Value, Is.EqualTo(1)); + Assert.That(healthProcessor.HasFailed, Is.False); + }); + } + + [Test] + public void TestHitsAboveThreshold() + { + var beatmap = new TaikoBeatmap + { + HitObjects = + { + new Hit(), + new Hit { StartTime = 1000 }, + new Hit { StartTime = 2000 }, + new Hit { StartTime = 3000 }, + new Hit { StartTime = 4000 }, + } + }; + + var healthProcessor = new TaikoHealthProcessor(); + healthProcessor.ApplyBeatmap(beatmap); + + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TaikoJudgement()) { Type = HitResult.Great }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], new TaikoJudgement()) { Type = HitResult.Ok }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[2], new TaikoJudgement()) { Type = HitResult.Ok }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[3], new TaikoJudgement()) { Type = HitResult.Ok }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[4], new TaikoJudgement()) { Type = HitResult.Miss }); + + Assert.Multiple(() => + { + Assert.That(healthProcessor.Health.Value, Is.GreaterThan(0.5)); + Assert.That(healthProcessor.HasFailed, Is.False); + }); + } + + [Test] + public void TestHitsBelowThreshold() + { + var beatmap = new TaikoBeatmap + { + HitObjects = + { + new Hit(), + new Hit { StartTime = 1000 }, + new Hit { StartTime = 2000 }, + new Hit { StartTime = 3000 }, + new Hit { StartTime = 4000 }, + } + }; + + var healthProcessor = new TaikoHealthProcessor(); + healthProcessor.ApplyBeatmap(beatmap); + + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], new TaikoJudgement()) { Type = HitResult.Miss }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[1], new TaikoJudgement()) { Type = HitResult.Ok }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[2], new TaikoJudgement()) { Type = HitResult.Ok }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[3], new TaikoJudgement()) { Type = HitResult.Ok }); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[4], new TaikoJudgement()) { Type = HitResult.Miss }); + + Assert.Multiple(() => + { + Assert.That(healthProcessor.Health.Value, Is.LessThan(0.5)); + Assert.That(healthProcessor.HasFailed, Is.True); + }); + } + } +} From 190f30a4b3c56927c312f6163b5f7920fd014adc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 19:46:00 +0100 Subject: [PATCH 1026/1118] Add failing test cases --- .../TaikoHealthProcessorTest.cs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) diff --git a/osu.Game.Rulesets.Taiko.Tests/TaikoHealthProcessorTest.cs b/osu.Game.Rulesets.Taiko.Tests/TaikoHealthProcessorTest.cs index 51dcf1ba91..f4a1e888c9 100644 --- a/osu.Game.Rulesets.Taiko.Tests/TaikoHealthProcessorTest.cs +++ b/osu.Game.Rulesets.Taiko.Tests/TaikoHealthProcessorTest.cs @@ -106,5 +106,71 @@ namespace osu.Game.Rulesets.Taiko.Tests Assert.That(healthProcessor.HasFailed, Is.True); }); } + + [Test] + public void TestDrumRollOnly() + { + var beatmap = new TaikoBeatmap + { + HitObjects = + { + new DrumRoll { Duration = 2000 } + } + }; + + foreach (var ho in beatmap.HitObjects) + ho.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty); + + var healthProcessor = new TaikoHealthProcessor(); + healthProcessor.ApplyBeatmap(beatmap); + + foreach (var nested in beatmap.HitObjects[0].NestedHitObjects) + { + var nestedJudgement = nested.CreateJudgement(); + healthProcessor.ApplyResult(new JudgementResult(nested, nestedJudgement) { Type = nestedJudgement.MaxResult }); + } + + var judgement = beatmap.HitObjects[0].CreateJudgement(); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], judgement) { Type = judgement.MaxResult }); + + Assert.Multiple(() => + { + Assert.That(healthProcessor.Health.Value, Is.EqualTo(1)); + Assert.That(healthProcessor.HasFailed, Is.False); + }); + } + + [Test] + public void TestSwellOnly() + { + var beatmap = new TaikoBeatmap + { + HitObjects = + { + new DrumRoll { Duration = 2000 } + } + }; + + foreach (var ho in beatmap.HitObjects) + ho.ApplyDefaults(beatmap.ControlPointInfo, beatmap.Difficulty); + + var healthProcessor = new TaikoHealthProcessor(); + healthProcessor.ApplyBeatmap(beatmap); + + foreach (var nested in beatmap.HitObjects[0].NestedHitObjects) + { + var nestedJudgement = nested.CreateJudgement(); + healthProcessor.ApplyResult(new JudgementResult(nested, nestedJudgement) { Type = nestedJudgement.MaxResult }); + } + + var judgement = beatmap.HitObjects[0].CreateJudgement(); + healthProcessor.ApplyResult(new JudgementResult(beatmap.HitObjects[0], judgement) { Type = judgement.MaxResult }); + + Assert.Multiple(() => + { + Assert.That(healthProcessor.Health.Value, Is.EqualTo(1)); + Assert.That(healthProcessor.HasFailed, Is.False); + }); + } } } From fc37c5e4c2bb4daa7a61714555a68b43930fc211 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 20:18:32 +0100 Subject: [PATCH 1027/1118] Fix taiko maps containing only drum rolls / swells not being passable without mods Closes https://github.com/ppy/osu/issues/26370. As was noted in the issue thread stable does not attempt to account for such maps, and the maps are impassable in stable without No Fail. Nevertheless that seems like a pretty anti-player behaviour and I honestly believe that it is fine to change this in lazer. --- .../Scoring/TaikoHealthProcessor.cs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs index d75906f3aa..dfca4ac8c7 100644 --- a/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs +++ b/osu.Game.Rulesets.Taiko/Scoring/TaikoHealthProcessor.cs @@ -31,11 +31,39 @@ namespace osu.Game.Rulesets.Taiko.Scoring /// private double hpMissMultiplier; + /// + /// Sum of all achievable health increases throughout the map. + /// Used to determine if there are any objects that give health. + /// If there are none, health will be forcibly pulled up to 1 to avoid cases of impassable maps. + /// + private double sumOfMaxHealthIncreases; + public TaikoHealthProcessor() : base(0.5) { } + protected override void ApplyResultInternal(JudgementResult result) + { + base.ApplyResultInternal(result); + sumOfMaxHealthIncreases += result.Judgement.MaxHealthIncrease; + } + + protected override void RevertResultInternal(JudgementResult result) + { + base.RevertResultInternal(result); + sumOfMaxHealthIncreases -= result.Judgement.MaxHealthIncrease; + } + + protected override void Reset(bool storeResults) + { + base.Reset(storeResults); + + if (storeResults && sumOfMaxHealthIncreases == 0) + Health.Value = 1; + sumOfMaxHealthIncreases = 0; + } + public override void ApplyBeatmap(IBeatmap beatmap) { base.ApplyBeatmap(beatmap); From 1075b87fb8bfb84ce3404bf93253940992e86671 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 22:19:20 +0100 Subject: [PATCH 1028/1118] Add failing test coverage --- .../Formats/LegacyScoreDecoderTest.cs | 121 ++++++++++++++++++ 1 file changed, 121 insertions(+) diff --git a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs index 4e281cf28e..6e7c8c3631 100644 --- a/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs +++ b/osu.Game.Tests/Beatmaps/Formats/LegacyScoreDecoderTest.cs @@ -3,14 +3,18 @@ #nullable disable +using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; using NUnit.Framework; +using osu.Framework.Extensions; using osu.Framework.Utils; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; +using osu.Game.Beatmaps.Legacy; +using osu.Game.IO.Legacy; using osu.Game.Replays; using osu.Game.Rulesets; using osu.Game.Rulesets.Catch; @@ -247,6 +251,123 @@ namespace osu.Game.Tests.Beatmaps.Formats }); } + [Test] + public void AccuracyAndRankOfStableScorePreserved() + { + var memoryStream = new MemoryStream(); + + // local partial implementation of legacy score encoder + // this is done half for readability, half because `LegacyScoreEncoder` forces `LATEST_VERSION` + // and we want to emulate a stable score here + using (var sw = new SerializationWriter(memoryStream, true)) + { + sw.Write((byte)0); // ruleset id (osu!) + sw.Write(20240116); // version (anything below `LegacyScoreEncoder.FIRST_LAZER_VERSION` is stable) + sw.Write(string.Empty.ComputeMD5Hash()); // beatmap hash, irrelevant to this test + sw.Write("username"); // irrelevant to this test + sw.Write(string.Empty.ComputeMD5Hash()); // score hash, irrelevant to this test + sw.Write((ushort)198); // count300 + sw.Write((ushort)1); // count100 + sw.Write((ushort)0); // count50 + sw.Write((ushort)0); // countGeki + sw.Write((ushort)0); // countKatu + sw.Write((ushort)1); // countMiss + sw.Write(12345678); // total score, irrelevant to this test + sw.Write((ushort)1000); // max combo, irrelevant to this test + sw.Write(false); // full combo, irrelevant to this test + sw.Write((int)LegacyMods.Hidden); // mods + sw.Write(string.Empty); // hp graph, irrelevant + sw.Write(DateTime.Now); // date, irrelevant + sw.Write(Array.Empty()); // replay data, irrelevant + sw.Write((long)1234); // legacy online ID, irrelevant + } + + memoryStream.Seek(0, SeekOrigin.Begin); + var decoded = new TestLegacyScoreDecoder().Parse(memoryStream); + + Assert.Multiple(() => + { + Assert.That(decoded.ScoreInfo.Accuracy, Is.EqualTo((double)(198 * 300 + 100) / (200 * 300))); + Assert.That(decoded.ScoreInfo.Rank, Is.EqualTo(ScoreRank.A)); + }); + } + + [Test] + public void AccuracyAndRankOfLazerScorePreserved() + { + var ruleset = new OsuRuleset().RulesetInfo; + + var scoreInfo = TestResources.CreateTestScoreInfo(ruleset); + scoreInfo.Mods = new Mod[] { new OsuModFlashlight() }; + scoreInfo.Statistics = new Dictionary + { + [HitResult.Great] = 199, + [HitResult.Miss] = 1, + [HitResult.LargeTickHit] = 1, + }; + scoreInfo.MaximumStatistics = new Dictionary + { + [HitResult.Great] = 200, + [HitResult.LargeTickHit] = 1, + }; + + var beatmap = new TestBeatmap(ruleset); + var score = new Score + { + ScoreInfo = scoreInfo, + }; + + var decodedAfterEncode = encodeThenDecode(LegacyBeatmapDecoder.LATEST_VERSION, score, beatmap); + + Assert.Multiple(() => + { + Assert.That(decodedAfterEncode.ScoreInfo.Accuracy, Is.EqualTo((double)(199 * 300 + 30) / (200 * 300 + 30))); + Assert.That(decodedAfterEncode.ScoreInfo.Rank, Is.EqualTo(ScoreRank.SH)); + }); + } + + [Test] + public void AccuracyAndRankOfLazerScoreWithoutLegacyReplaySoloScoreInfoUsesBestEffortFallbackToLegacy() + { + var memoryStream = new MemoryStream(); + + // local partial implementation of legacy score encoder + // this is done half for readability, half because we want to emulate an old lazer score here + // that does not have everything that `LegacyScoreEncoder` now writes to the replay + using (var sw = new SerializationWriter(memoryStream, true)) + { + sw.Write((byte)0); // ruleset id (osu!) + sw.Write(LegacyScoreEncoder.FIRST_LAZER_VERSION); // version + sw.Write(string.Empty.ComputeMD5Hash()); // beatmap hash, irrelevant to this test + sw.Write("username"); // irrelevant to this test + sw.Write(string.Empty.ComputeMD5Hash()); // score hash, irrelevant to this test + sw.Write((ushort)198); // count300 + sw.Write((ushort)0); // count100 + sw.Write((ushort)1); // count50 + sw.Write((ushort)0); // countGeki + sw.Write((ushort)0); // countKatu + sw.Write((ushort)1); // countMiss + sw.Write(12345678); // total score, irrelevant to this test + sw.Write((ushort)1000); // max combo, irrelevant to this test + sw.Write(false); // full combo, irrelevant to this test + sw.Write((int)LegacyMods.Hidden); // mods + sw.Write(string.Empty); // hp graph, irrelevant + sw.Write(DateTime.Now); // date, irrelevant + sw.Write(Array.Empty()); // replay data, irrelevant + sw.Write((long)1234); // legacy online ID, irrelevant + // importantly, no compressed `LegacyReplaySoloScoreInfo` here + } + + memoryStream.Seek(0, SeekOrigin.Begin); + var decoded = new TestLegacyScoreDecoder().Parse(memoryStream); + + Assert.Multiple(() => + { + Assert.That(decoded.ScoreInfo.Accuracy, Is.EqualTo((double)(198 * 300 + 50) / (200 * 300))); + Assert.That(decoded.ScoreInfo.Rank, Is.EqualTo(ScoreRank.A)); + }); + } + private static Score encodeThenDecode(int beatmapVersion, Score score, TestBeatmap beatmap) { var encodeStream = new MemoryStream(); From 17b9d842ab9a051ff6bc43d4ac5eb3d90dedfd93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Tue, 16 Jan 2024 21:08:02 +0100 Subject: [PATCH 1029/1118] Fix incorrect accuracy and rank population when decoding lazer replays Closes https://github.com/ppy/osu/issues/24061. The gist of this change is that if the `LegacyReplaySoloScoreInfo` bolt-on is present in the replay, then it can (and is) used to recompute the accuracy, and rank is computed based on that. This was the missing part of https://github.com/ppy/osu/issues/24061#issuecomment-1888438151. The accuracy would change on import before that because the encode process is _lossy_ if the `LegacyReplaySoloScoreInfo` bolt-on is not used, as the legacy format only has 6 fields for encoding judgement counts, and some judgements that affect accuracy in lazer do not fit into that. Note that this _only_ fixes _relatively_ new lazer scores looking wrong after reimport. - Very old lazer scores, i.e. ones that don't have the `LegacyReplaySoloScoreInfo` bolt-on, obviously can't use it to repopulate. There's really not much good that can be done there, so the stable pathways are used as a fallback that always works. - For stable replays, `ScoreImporter` recalculates the accuracy of the score _again_ in https://github.com/ppy/osu/blob/15a5fd7e4c8e8e3c38382cfd3d5d676d107d7908/osu.Game/Scoring/ScoreImporter.cs#L106-L110 as `StandardisedScoreMigrationTools.UpdateFromLegacy()` recomputes _both_ total score and accuracy. This makes a _semblance_ of sense as it attempts to make the accuracy of stable and lazer replays comparable. In most cases it also won't matter, as the only ruleset where accuracy changed between the legacy implementation and current lazer accuracy is mania. But it is also an inaccurate process (as, again, some of the required data is not in the replay, namely judgement counts of ticks and so on). For whatever's worth, a similar thing happens server-side in https://github.com/ppy/osu-queue-score-statistics/blob/106c2948dbe695efcad5972d32cd46f4b36005cc/osu.Server.Queues.ScoreStatisticsProcessor/Commands/Queue/BatchInserter.cs#L319 - However, _ranks_ of stable scores will still use the local stable reimplementation of ranks, i.e. a 1-miss stable score in osu! ruleset will be an A rather than an S. See importer: https://github.com/ppy/osu-queue-score-statistics/blob/106c2948dbe695efcad5972d32cd46f4b36005cc/osu.Server.Queues.ScoreStatisticsProcessor/Commands/Queue/BatchInserter.cs#L237 (it's the same method which is renamed to `PopulateLegacyAccuracyAndRank()` in this commit). That is all a bit of a mess honestly, but I'm not sure where to even begin there... --- osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs | 23 +++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs index ed11691674..b30fc7aee1 100644 --- a/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs +++ b/osu.Game/Scoring/Legacy/LegacyScoreDecoder.cs @@ -11,6 +11,7 @@ using Newtonsoft.Json; using osu.Game.Beatmaps; using osu.Game.Beatmaps.Formats; using osu.Game.Beatmaps.Legacy; +using osu.Game.Database; using osu.Game.IO.Legacy; using osu.Game.Online.API.Requests.Responses; using osu.Game.Replays; @@ -37,6 +38,7 @@ namespace osu.Game.Scoring.Legacy }; WorkingBeatmap workingBeatmap; + byte[] compressedScoreInfo = null; using (SerializationReader sr = new SerializationReader(stream)) { @@ -105,8 +107,6 @@ namespace osu.Game.Scoring.Legacy else if (version >= 20121008) scoreInfo.LegacyOnlineID = sr.ReadInt32(); - byte[] compressedScoreInfo = null; - if (version >= 30000001) compressedScoreInfo = sr.ReadByteArray(); @@ -130,7 +130,10 @@ namespace osu.Game.Scoring.Legacy } } - PopulateAccuracy(score.ScoreInfo); + if (score.ScoreInfo.IsLegacyScore || compressedScoreInfo == null) + PopulateLegacyAccuracyAndRank(score.ScoreInfo); + else + populateLazerAccuracyAndRank(score.ScoreInfo); // before returning for database import, we must restore the database-sourced BeatmapInfo. // if not, the clone operation in GetPlayableBeatmap will cause a dereference and subsequent database exception. @@ -174,7 +177,7 @@ namespace osu.Game.Scoring.Legacy /// Legacy use only. /// /// The to populate. - public static void PopulateAccuracy(ScoreInfo score) + public static void PopulateLegacyAccuracyAndRank(ScoreInfo score) { int countMiss = score.GetCountMiss() ?? 0; int count50 = score.GetCount50() ?? 0; @@ -273,6 +276,18 @@ namespace osu.Game.Scoring.Legacy } } + private void populateLazerAccuracyAndRank(ScoreInfo scoreInfo) + { + scoreInfo.Accuracy = StandardisedScoreMigrationTools.ComputeAccuracy(scoreInfo); + + var rank = currentRuleset.CreateScoreProcessor().RankFromAccuracy(scoreInfo.Accuracy); + + foreach (var mod in scoreInfo.Mods.OfType()) + rank = mod.AdjustRank(rank, scoreInfo.Accuracy); + + scoreInfo.Rank = rank; + } + private void readLegacyReplay(Replay replay, StreamReader reader) { float lastTime = beatmapOffset; From 9cde04c30c964a62d7e1e92c0c05f3726b8acf17 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 14:12:12 +0900 Subject: [PATCH 1030/1118] Change drag adjustments to be linear (and account for partial deltas) --- osu.Game/Overlays/Volume/VolumeMeter.cs | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index 6ad314c601..a5b496b7d8 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -309,24 +309,37 @@ namespace osu.Game.Overlays.Volume private const double max_acceleration = 5; private const double acceleration_multiplier = 1.8; - private const float mouse_drag_divisor = 20; - private ScheduledDelegate accelerationDebounce; private void resetAcceleration() => accelerationModifier = 1; + private float dragDelta = 0; + protected override bool OnDragStart(DragStartEvent e) { - adjust(-e.Delta.Y / mouse_drag_divisor, true); + dragDelta = 0; + adjustFromDrag(e.Delta); return true; } protected override void OnDrag(DragEvent e) { - adjust(-e.Delta.Y / mouse_drag_divisor, true); + adjustFromDrag(e.Delta); base.OnDrag(e); } + private void adjustFromDrag(Vector2 delta) + { + const float mouse_drag_divisor = 200; + + dragDelta += delta.Y / mouse_drag_divisor; + + if (Math.Abs(dragDelta) < 0.01) return; + + Volume -= dragDelta; + dragDelta = 0; + } + private void adjust(double delta, bool isPrecise) { if (delta == 0) From f11682d44f769fe480ae1bfcfc7f324208fd49a7 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 08:10:02 +0300 Subject: [PATCH 1031/1118] Add failing test case --- .../Visual/Editing/TestSceneTimingScreen.cs | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs index 40aadc8164..56c8c05fe6 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs @@ -4,11 +4,13 @@ #nullable disable using System; +using System.Diagnostics; using System.Linq; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; +using osu.Framework.Graphics.UserInterface; using osu.Framework.Testing; using osu.Game.Beatmaps.ControlPoints; using osu.Game.Graphics.Containers; @@ -177,6 +179,43 @@ namespace osu.Game.Tests.Visual.Editing AddUntilStep("Scrolled to end", () => timingScreen.ChildrenOfType().First().IsScrolledToEnd()); } + [Test] + public void TestEditThenClickAway() + { + AddStep("Add two control points", () => + { + editorBeatmap.ControlPointInfo.Clear(); + editorBeatmap.ControlPointInfo.Add(1000, new TimingControlPoint()); + editorBeatmap.ControlPointInfo.Add(2000, new TimingControlPoint()); + }); + + AddStep("Select second timing point", () => + { + InputManager.MoveMouseTo(Child.ChildrenOfType().Last()); + InputManager.Click(MouseButton.Left); + }); + + AddStep("Scroll to end", () => timingScreen.ChildrenOfType().Single().ChildrenOfType().Single().ScrollToEnd(false)); + AddStep("Modify time signature", () => + { + var timeSignatureTextBox = Child.ChildrenOfType().Single().ChildrenOfType().Single(); + InputManager.MoveMouseTo(timeSignatureTextBox); + InputManager.Click(MouseButton.Left); + + Debug.Assert(!timeSignatureTextBox.Current.Value.Equals("1")); + timeSignatureTextBox.Current.Value = "1"; + }); + + AddStep("Select first timing point", () => + { + InputManager.MoveMouseTo(Child.ChildrenOfType().First()); + InputManager.Click(MouseButton.Left); + }); + + AddAssert("Second timing point changed time signature", () => editorBeatmap.ControlPointInfo.TimingPoints.Last().TimeSignature.Numerator == 1); + AddAssert("First timing point preserved time signature", () => editorBeatmap.ControlPointInfo.TimingPoints.First().TimeSignature.Numerator == 4); + } + protected override void Dispose(bool isDisposing) { Beatmap.Disabled = false; From 46429c5074b05447f8934dd25bd26fcce5e46dec Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 08:10:37 +0300 Subject: [PATCH 1032/1118] Schedule control point switch for settings modifications to apply first --- osu.Game/Screens/Edit/Timing/ControlPointTable.cs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs index 7a27056da3..219575a380 100644 --- a/osu.Game/Screens/Edit/Timing/ControlPointTable.cs +++ b/osu.Game/Screens/Edit/Timing/ControlPointTable.cs @@ -44,11 +44,12 @@ namespace osu.Game.Screens.Edit.Timing { BackgroundFlow.Add(new RowBackground(group) { - Action = () => + // schedule to give time for any modified focused text box to lose focus and commit changes (e.g. BPM / time signature textboxes) before switching to new point. + Action = () => Schedule(() => { SetSelectedRow(group); clock.SeekSmoothlyTo(group.Time); - } + }) }); } From a34d2a3424eec99dec0efb44ae91844348f46f22 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 08:28:47 +0300 Subject: [PATCH 1033/1118] Add small volume meter to test feel --- .../Visual/UserInterface/TestSceneVolumePieces.cs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/UserInterface/TestSceneVolumePieces.cs b/osu.Game.Tests/Visual/UserInterface/TestSceneVolumePieces.cs index 311bae0d50..b85e4c19d1 100644 --- a/osu.Game.Tests/Visual/UserInterface/TestSceneVolumePieces.cs +++ b/osu.Game.Tests/Visual/UserInterface/TestSceneVolumePieces.cs @@ -14,7 +14,7 @@ namespace osu.Game.Tests.Visual.UserInterface { VolumeMeter meter; MuteButton mute; - Add(meter = new VolumeMeter("MASTER", 125, Color4.Blue) { Position = new Vector2(10) }); + Add(meter = new VolumeMeter("MASTER", 125, Color4.Green) { Position = new Vector2(10) }); AddSliderStep("master volume", 0, 10, 0, i => meter.Bindable.Value = i * 0.1); Add(new VolumeMeter("BIG", 250, Color4.Red) @@ -22,6 +22,15 @@ namespace osu.Game.Tests.Visual.UserInterface Anchor = Anchor.Centre, Origin = Anchor.Centre, Position = new Vector2(10), + Margin = new MarginPadding { Left = 250 }, + }); + + Add(new VolumeMeter("SML", 125, Color4.Blue) + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Position = new Vector2(10), + Margin = new MarginPadding { Right = 500 }, }); Add(mute = new MuteButton From 1790a5df03719e0483d4abede98460f4874bb65f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 08:29:55 +0300 Subject: [PATCH 1034/1118] Remove redundant value to appease CI --- osu.Game/Overlays/Volume/VolumeMeter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Volume/VolumeMeter.cs b/osu.Game/Overlays/Volume/VolumeMeter.cs index a5b496b7d8..9ca4c25ab9 100644 --- a/osu.Game/Overlays/Volume/VolumeMeter.cs +++ b/osu.Game/Overlays/Volume/VolumeMeter.cs @@ -313,7 +313,7 @@ namespace osu.Game.Overlays.Volume private void resetAcceleration() => accelerationModifier = 1; - private float dragDelta = 0; + private float dragDelta; protected override bool OnDragStart(DragStartEvent e) { From 05729706eb2633b74dbb97d3e2615f85cd2721e5 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 08:53:24 +0300 Subject: [PATCH 1035/1118] Fix android build CI failing No idea why this is only in android, but okay. --- osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs index 56c8c05fe6..f3d2b5d518 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs @@ -202,7 +202,7 @@ namespace osu.Game.Tests.Visual.Editing InputManager.MoveMouseTo(timeSignatureTextBox); InputManager.Click(MouseButton.Left); - Debug.Assert(!timeSignatureTextBox.Current.Value.Equals("1")); + Debug.Assert(!timeSignatureTextBox.Current.Value.Equals("1", StringComparison.Ordinal)); timeSignatureTextBox.Current.Value = "1"; }); From 42f64c2c44f34bceb9fbbadb8a5cb6d2f8ed04aa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 14:48:33 +0900 Subject: [PATCH 1036/1118] Move load procedure to async method and simplify code --- .../Screens/Play/HUD/PlayerSettingsOverlay.cs | 2 ++ osu.Game/Screens/Play/ReplayPlayer.cs | 20 ++++++++++--------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index b7f3dc36c3..8c74b0254d 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -41,5 +41,7 @@ namespace osu.Game.Screens.Play.HUD protected override void PopIn() => this.FadeIn(fade_duration); protected override void PopOut() => this.FadeOut(fade_duration); + + public void Insert(int i, PlayerSettingsGroup drawable) => content.Insert(i, drawable); } } diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index f6e4ac489a..39df223a9d 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; +using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; @@ -50,18 +51,19 @@ namespace osu.Game.Screens.Play this.createScore = createScore; } - protected override void LoadComplete() + [BackgroundDependencyLoader] + private void load() { - base.LoadComplete(); - - if (HUDOverlay != null) + var playbackSettings = new PlaybackSettings { - var playerSettingsOverlay = new PlaybackSettings { Expanded = { Value = false } }; - HUDOverlay.PlayerSettingsOverlay.Add(playerSettingsOverlay); + Depth = float.MaxValue, + Expanded = { Value = false } + }; - if (GameplayClockContainer is MasterGameplayClockContainer master) - playerSettingsOverlay.UserPlaybackRate.BindTarget = master.UserPlaybackRate; - } + if (GameplayClockContainer is MasterGameplayClockContainer master) + playbackSettings.UserPlaybackRate.BindTo(master.UserPlaybackRate); + + HUDOverlay.PlayerSettingsOverlay.Insert(-1, playbackSettings); } protected override void PrepareReplay() From 042e852a3ec027721a80817a7d3a9538df5904ff Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 14:53:49 +0900 Subject: [PATCH 1037/1118] Adjust playback speed range to allow slower minimum speed --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 4 ++-- osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 8b8bf87436..1b7588d81a 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -35,9 +35,9 @@ namespace osu.Game.Screens.Play public readonly BindableNumber UserPlaybackRate = new BindableDouble(1) { - MinValue = 0.5, + MinValue = 0.05, MaxValue = 2, - Precision = 0.1, + Precision = 0.01, }; /// diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 0fe3a08985..2f37b8877f 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -22,9 +22,9 @@ namespace osu.Game.Screens.Play.PlayerSettings public readonly Bindable UserPlaybackRate = new BindableDouble(1) { - MinValue = 0.5, + MinValue = 0.05, MaxValue = 2, - Precision = 0.1, + Precision = 0.01, }; private readonly PlayerSliderBar rateSlider; @@ -149,7 +149,7 @@ namespace osu.Game.Screens.Play.PlayerSettings protected override void LoadComplete() { base.LoadComplete(); - rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.0}x", true); + rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.00}x", true); if (gameplayClock != null) isPaused.BindTarget = gameplayClock.IsPaused; From e53989faebebb0bf323cac4a0f76e6bebd042939 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 14:54:14 +0900 Subject: [PATCH 1038/1118] Change replay playback adjustment to skew frequency, not tempo --- osu.Game/Screens/Play/MasterGameplayClockContainer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 1b7588d81a..1159e5f6d9 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -274,7 +274,7 @@ namespace osu.Game.Screens.Play track.BindAdjustments(AdjustmentsFromMods); track.AddAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust); - track.AddAdjustment(AdjustableProperty.Tempo, UserPlaybackRate); + track.AddAdjustment(AdjustableProperty.Frequency, UserPlaybackRate); speedAdjustmentsApplied = true; } @@ -286,7 +286,7 @@ namespace osu.Game.Screens.Play track.UnbindAdjustments(AdjustmentsFromMods); track.RemoveAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust); - track.RemoveAdjustment(AdjustableProperty.Tempo, UserPlaybackRate); + track.RemoveAdjustment(AdjustableProperty.Frequency, UserPlaybackRate); speedAdjustmentsApplied = false; } From 2788bd912e87068c370b27ff40333bda3c85139a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 15:12:33 +0900 Subject: [PATCH 1039/1118] Add tooltips and localisation --- .../PlayerSettingsOverlayStrings.cs | 24 +++++++++++++++++++ .../Play/PlayerSettings/PlaybackSettings.cs | 19 ++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 osu.Game/Localisation/PlayerSettingsOverlayStrings.cs diff --git a/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs b/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs new file mode 100644 index 0000000000..1aedd9fc5b --- /dev/null +++ b/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs @@ -0,0 +1,24 @@ +// Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. +// See the LICENCE file in the repository root for full licence text. + +using osu.Framework.Localisation; + +namespace osu.Game.Localisation +{ + public static class PlayerSettingsOverlayStrings + { + private const string prefix = @"osu.Game.Resources.Localisation.PlaybackSettings"; + + /// + /// "Seek backward {0} seconds" + /// + public static LocalisableString SeekBackwardSeconds(double arg0) => new TranslatableString(getKey(@"seek_backward_seconds"), @"Seek backward {0} seconds", arg0); + + /// + /// "Seek forward {0} seconds" + /// + public static LocalisableString SeekForwardSeconds(double arg0) => new TranslatableString(getKey(@"seek_forward_seconds"), @"Seek forward {0} seconds", arg0); + + private static string getKey(string key) => $@"{prefix}:{key}"; + } +} diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 2f37b8877f..44cfa8d811 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -13,6 +13,7 @@ using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; using osu.Game.Screens.Edit.Timing; using osuTK; +using osu.Game.Localisation; namespace osu.Game.Screens.Play.PlayerSettings { @@ -71,6 +72,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.FastBackward, Action = () => seek(-1, seek_fast_amount), + TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_fast_amount / 1000), }, new SeekButton { @@ -78,6 +80,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.Backward, Action = () => seek(-1, seek_amount), + TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_amount / 1000), }, play = new IconButton { @@ -103,6 +106,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.Forward, Action = () => seek(1, seek_amount), + TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(seek_amount / 1000), }, new SeekButton { @@ -110,6 +114,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Origin = Anchor.Centre, Icon = FontAwesome.Solid.FastForward, Action = () => seek(1, seek_fast_amount), + TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(seek_fast_amount / 1000), }, }, }, @@ -137,7 +142,19 @@ namespace osu.Game.Screens.Play.PlayerSettings }, }; - isPaused.BindValueChanged(e => play.Icon = !e.NewValue ? FontAwesome.Regular.PauseCircle : FontAwesome.Regular.PlayCircle, true); + isPaused.BindValueChanged(paused => + { + if (!paused.NewValue) + { + play.TooltipText = ToastStrings.PauseTrack; + play.Icon = FontAwesome.Regular.PauseCircle; + } + else + { + play.TooltipText = ToastStrings.PlayTrack; + play.Icon = FontAwesome.Regular.PlayCircle; + } + }, true); void seek(int direction, double amount) { From e7732caaf79a90c15354502ab307e8927373a46e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 15:13:59 +0900 Subject: [PATCH 1040/1118] Make `PlayerSettingsOverlay`'s api more stringent --- osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs | 2 +- osu.Game/Screens/Play/ReplayPlayer.cs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs index 8c74b0254d..a2b49f6302 100644 --- a/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs +++ b/osu.Game/Screens/Play/HUD/PlayerSettingsOverlay.cs @@ -42,6 +42,6 @@ namespace osu.Game.Screens.Play.HUD protected override void PopIn() => this.FadeIn(fade_duration); protected override void PopOut() => this.FadeOut(fade_duration); - public void Insert(int i, PlayerSettingsGroup drawable) => content.Insert(i, drawable); + public void AddAtStart(PlayerSettingsGroup drawable) => content.Insert(-1, drawable); } } diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 39df223a9d..69913c6555 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -63,7 +63,7 @@ namespace osu.Game.Screens.Play if (GameplayClockContainer is MasterGameplayClockContainer master) playbackSettings.UserPlaybackRate.BindTo(master.UserPlaybackRate); - HUDOverlay.PlayerSettingsOverlay.Insert(-1, playbackSettings); + HUDOverlay.PlayerSettingsOverlay.AddAtStart(playbackSettings); } protected override void PrepareReplay() From 266c7b28e807978ad2e71e664afd454ce5c4dc3d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 15:38:51 +0900 Subject: [PATCH 1041/1118] Show an empty baetmap instead of nothing --- osu.Game.Tournament/Components/SongBar.cs | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/osu.Game.Tournament/Components/SongBar.cs b/osu.Game.Tournament/Components/SongBar.cs index cc1d00f62f..ae59e92e33 100644 --- a/osu.Game.Tournament/Components/SongBar.cs +++ b/osu.Game.Tournament/Components/SongBar.cs @@ -12,6 +12,7 @@ using osu.Game.Beatmaps; using osu.Game.Beatmaps.Legacy; using osu.Game.Extensions; using osu.Game.Graphics; +using osu.Game.Models; using osu.Game.Rulesets; using osu.Game.Screens.Menu; using osuTK; @@ -101,11 +102,25 @@ namespace osu.Game.Tournament.Components private void refreshContent() { - if (beatmap == null) + beatmap ??= new BeatmapInfo { - flow.Clear(); - return; - } + Metadata = new BeatmapMetadata + { + Artist = "unknown", + Title = "no beatmap selected", + Author = new RealmUser { Username = "unknown" }, + }, + DifficultyName = "unknown", + BeatmapSet = new BeatmapSetInfo(), + StarRating = 0, + Difficulty = new BeatmapDifficulty + { + CircleSize = 0, + DrainRate = 0, + OverallDifficulty = 0, + ApproachRate = 0, + }, + }; double bpm = beatmap.BPM; double length = beatmap.Length; From 06aa35a10e106997521389f26d59aadd1047c7d7 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 16:26:01 +0900 Subject: [PATCH 1042/1118] Fix tournament beatmap backgrounds occasionally not loading --- osu.Game.Tournament/Components/TournamentBeatmapPanel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs index 4e0adb30ac..514ba482c4 100644 --- a/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs +++ b/osu.Game.Tournament/Components/TournamentBeatmapPanel.cs @@ -194,7 +194,7 @@ namespace osu.Game.Tournament.Components // Use DelayedLoadWrapper to avoid content unloading when switching away to another screen. protected override DelayedLoadWrapper CreateDelayedLoadWrapper(Func createContentFunc, double timeBeforeLoad) - => new DelayedLoadWrapper(createContentFunc, timeBeforeLoad); + => new DelayedLoadWrapper(createContentFunc(), timeBeforeLoad); } } } From e97b31d82e9aa6c9dace49742ff3c246d27c3368 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 16:40:45 +0900 Subject: [PATCH 1043/1118] Fix test failures --- osu.Game/Screens/Play/ReplayPlayer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 69913c6555..805f907466 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -54,6 +54,9 @@ namespace osu.Game.Screens.Play [BackgroundDependencyLoader] private void load() { + if (!LoadedBeatmapSuccessfully) + return; + var playbackSettings = new PlaybackSettings { Depth = float.MaxValue, From fe06402951ada9980fe5c02308cab9423bacfa20 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 16:50:48 +0900 Subject: [PATCH 1044/1118] Fix incorrect bindable usage --- osu.Game/Overlays/Login/LoginPanel.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/Login/LoginPanel.cs b/osu.Game/Overlays/Login/LoginPanel.cs index 7db3644de6..ea65656eb1 100644 --- a/osu.Game/Overlays/Login/LoginPanel.cs +++ b/osu.Game/Overlays/Login/LoginPanel.cs @@ -38,6 +38,7 @@ namespace osu.Game.Overlays.Login public Action? RequestHide; private readonly IBindable apiState = new Bindable(); + private readonly Bindable userStatus = new Bindable(); [Resolved] private IAPIProvider api { get; set; } = null!; @@ -139,7 +140,8 @@ namespace osu.Game.Overlays.Login }, }; - api.LocalUser.Value.Status.BindValueChanged(e => updateDropdownCurrent(e.NewValue), true); + userStatus.BindTo(api.LocalUser.Value.Status); + userStatus.BindValueChanged(e => updateDropdownCurrent(e.NewValue), true); dropdown.Current.BindValueChanged(action => { From 66c7a29e794197478a597922bbc6c7028515b304 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 16:57:22 +0900 Subject: [PATCH 1045/1118] Add comment about messy methods --- osu.Game/Users/UserPanel.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Users/UserPanel.cs b/osu.Game/Users/UserPanel.cs index ef5c95c422..b88619c8b7 100644 --- a/osu.Game/Users/UserPanel.cs +++ b/osu.Game/Users/UserPanel.cs @@ -98,6 +98,8 @@ namespace osu.Game.Users }; } + // TODO: this whole api is messy. half these Create methods are expected to by the implementation and half are implictly called. + protected abstract Drawable CreateLayout(); /// From 353df2f31227a71da752e40dbdcb650e288e889d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 17:00:30 +0900 Subject: [PATCH 1046/1118] Fix one more incorrect bindable flow and simplify string setters --- osu.Game/Users/UserRankPanel.cs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/osu.Game/Users/UserRankPanel.cs b/osu.Game/Users/UserRankPanel.cs index a2e234cf82..a38962dfc7 100644 --- a/osu.Game/Users/UserRankPanel.cs +++ b/osu.Game/Users/UserRankPanel.cs @@ -2,11 +2,11 @@ // See the LICENCE file in the repository root for full licence text. using osu.Framework.Allocation; +using osu.Framework.Bindables; using osu.Framework.Extensions.LocalisationExtensions; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Input.Events; -using osu.Framework.Localisation; using osu.Game.Online.API; using osu.Game.Online.API.Requests.Responses; using osu.Game.Overlays.Profile.Header.Components; @@ -30,6 +30,8 @@ namespace osu.Game.Users private ProfileValueDisplay globalRankDisplay = null!; private ProfileValueDisplay countryRankDisplay = null!; + private readonly IBindable statistics = new Bindable(); + public UserRankPanel(APIUser user) : base(user) { @@ -42,11 +44,12 @@ namespace osu.Game.Users { BorderColour = ColourProvider?.Light1 ?? Colours.GreyVioletLighter; - api.Statistics.ValueChanged += e => + statistics.BindTo(api.Statistics); + statistics.BindValueChanged(stats => { - globalRankDisplay.Content = e.NewValue?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; - countryRankDisplay.Content = e.NewValue?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-"; - }; + globalRankDisplay.Content = stats.NewValue?.GlobalRank?.ToLocalisableString("\\##,##0") ?? "-"; + countryRankDisplay.Content = stats.NewValue?.CountryRank?.ToLocalisableString("\\##,##0") ?? "-"; + }, true); } protected override Drawable CreateLayout() @@ -184,12 +187,10 @@ namespace osu.Game.Users globalRankDisplay = new ProfileValueDisplay(true) { Title = UsersStrings.ShowRankGlobalSimple, - Content = User.Statistics?.GlobalRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-" }, countryRankDisplay = new ProfileValueDisplay(true) { Title = UsersStrings.ShowRankCountrySimple, - Content = User.Statistics?.CountryRank?.ToLocalisableString("\\##,##0") ?? (LocalisableString)"-" } } } From 45e52854ca50d31182845f3de3ca9b6bd87cf86d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 17:37:35 +0900 Subject: [PATCH 1047/1118] Change key overlay to use the ordering provided by rulesets osu!mania already goes out of its way to order things correctly. Arguably, osu!taiko just did it wrong. --- osu.Game/Rulesets/UI/RulesetInputManager.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/RulesetInputManager.cs b/osu.Game/Rulesets/UI/RulesetInputManager.cs index 35d05b87c0..041c7a13ae 100644 --- a/osu.Game/Rulesets/UI/RulesetInputManager.cs +++ b/osu.Game/Rulesets/UI/RulesetInputManager.cs @@ -167,7 +167,6 @@ namespace osu.Game.Rulesets.UI var triggers = KeyBindingContainer.DefaultKeyBindings .Select(b => b.GetAction()) .Distinct() - .OrderBy(action => action) .Select(action => new KeyCounterActionTrigger(action)) .ToArray(); From a4c0bfd099bb6a5140e1513cb5ac640c085082bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 17:37:20 +0900 Subject: [PATCH 1048/1118] Change taiko's default key ordering to better match display order Fixes the issue originally fixed via https://github.com/ppy/osu/pull/10553 in a slightly different way, allowing more flexibility. --- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index ad3b7b09f7..b80d8218f5 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -69,11 +69,11 @@ namespace osu.Game.Rulesets.Taiko public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { - new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), - new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim), new KeyBinding(InputKey.D, TaikoAction.LeftRim), new KeyBinding(InputKey.F, TaikoAction.LeftCentre), + new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), new KeyBinding(InputKey.J, TaikoAction.RightCentre), + new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim), new KeyBinding(InputKey.K, TaikoAction.RightRim), }; From 123e36a999e90a2698c52b03bb55c0054235d1a4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 17:51:46 +0900 Subject: [PATCH 1049/1118] Change display text for beatmap audio offset adjust to make more sense --- osu.Game/Localisation/BeatmapOffsetControlStrings.cs | 4 ++-- osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Localisation/BeatmapOffsetControlStrings.cs b/osu.Game/Localisation/BeatmapOffsetControlStrings.cs index 632a1ad0ea..b905b7ae1c 100644 --- a/osu.Game/Localisation/BeatmapOffsetControlStrings.cs +++ b/osu.Game/Localisation/BeatmapOffsetControlStrings.cs @@ -10,9 +10,9 @@ namespace osu.Game.Localisation private const string prefix = @"osu.Game.Resources.Localisation.BeatmapOffsetControl"; /// - /// "Beatmap offset" + /// "Audio offset (this beatmap)" /// - public static LocalisableString BeatmapOffset => new TranslatableString(getKey(@"beatmap_offset"), @"Beatmap offset"); + public static LocalisableString AudioOffsetThisBeatmap => new TranslatableString(getKey(@"beatmap_offset"), @"Audio offset (this beatmap)"); /// /// "Previous play:" diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 8efb80e771..7ac9c7186e 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -86,7 +86,7 @@ namespace osu.Game.Screens.Play.PlayerSettings new OffsetSliderBar { KeyboardStep = 5, - LabelText = BeatmapOffsetControlStrings.BeatmapOffset, + LabelText = BeatmapOffsetControlStrings.AudioOffsetThisBeatmap, Current = Current, }, referenceScoreContainer = new FillFlowContainer From cc7be137bc22415dbc3637189a638fb70910f014 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 17:57:49 +0900 Subject: [PATCH 1050/1118] Show tooltip on global offset adjust slider bar --- .../Settings/Sections/Audio/AudioOffsetAdjustControl.cs | 9 ++++++++- .../Screens/Play/PlayerSettings/BeatmapOffsetControl.cs | 2 +- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs index 90f5a59215..ef1691534f 100644 --- a/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs +++ b/osu.Game/Overlays/Settings/Sections/Audio/AudioOffsetAdjustControl.cs @@ -12,12 +12,14 @@ using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Shapes; using osu.Framework.Graphics.UserInterface; +using osu.Framework.Localisation; using osu.Game.Configuration; using osu.Game.Graphics; using osu.Game.Graphics.Containers; using osu.Game.Graphics.UserInterface; using osu.Game.Graphics.UserInterfaceV2; using osu.Game.Localisation; +using osu.Game.Screens.Play.PlayerSettings; using osuTK; namespace osu.Game.Overlays.Settings.Sections.Audio @@ -67,7 +69,7 @@ namespace osu.Game.Overlays.Settings.Sections.Audio Direction = FillDirection.Vertical, Children = new Drawable[] { - new TimeSlider + new OffsetSliderBar { RelativeSizeAxes = Axes.X, Current = { BindTarget = Current }, @@ -157,6 +159,11 @@ namespace osu.Game.Overlays.Settings.Sections.Audio : $@"Based on the last {averageHitErrorHistory.Count} play(s), the suggested offset is {SuggestedOffset.Value:N0} ms."; applySuggestion.Enabled.Value = SuggestedOffset.Value != null; } + + private partial class OffsetSliderBar : RoundedSliderBar + { + public override LocalisableString TooltipText => BeatmapOffsetControl.GetOffsetExplanatoryText(Current.Value); + } } } } diff --git a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs index 7ac9c7186e..9039604471 100644 --- a/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs +++ b/osu.Game/Screens/Play/PlayerSettings/BeatmapOffsetControl.cs @@ -307,7 +307,7 @@ namespace osu.Game.Screens.Play.PlayerSettings } } - public partial class OffsetSliderBar : PlayerSliderBar + private partial class OffsetSliderBar : PlayerSliderBar { protected override Drawable CreateControl() => new CustomSliderBar(); From a66ddc78136a877a697fec6d5232206db328439a Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 18:18:53 +0900 Subject: [PATCH 1051/1118] Change rolling counters to use quicker easing types --- osu.Game/Graphics/UserInterface/PercentageCounter.cs | 2 +- osu.Game/Graphics/UserInterface/RollingCounter.cs | 2 +- osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs | 2 +- osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs | 2 +- osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs | 2 +- osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs | 3 +-- osu.Game/Screens/Play/HUD/ArgonComboCounter.cs | 3 +-- osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs | 3 +-- osu.Game/Screens/Play/HUD/BPMCounter.cs | 2 +- .../Play/HUD/ClicksPerSecond/ClicksPerSecondCounter.cs | 2 +- osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs | 2 +- osu.Game/Screens/Play/HUD/UnstableRateCounter.cs | 2 +- .../Ranking/Expanded/Statistics/AccuracyStatistic.cs | 7 ++++--- 13 files changed, 16 insertions(+), 18 deletions(-) diff --git a/osu.Game/Graphics/UserInterface/PercentageCounter.cs b/osu.Game/Graphics/UserInterface/PercentageCounter.cs index ed06211e8f..1f9103b3bd 100644 --- a/osu.Game/Graphics/UserInterface/PercentageCounter.cs +++ b/osu.Game/Graphics/UserInterface/PercentageCounter.cs @@ -14,7 +14,7 @@ namespace osu.Game.Graphics.UserInterface /// public partial class PercentageCounter : RollingCounter { - protected override double RollingDuration => 750; + protected override double RollingDuration => 375; private float epsilon => 1e-10f; diff --git a/osu.Game/Graphics/UserInterface/RollingCounter.cs b/osu.Game/Graphics/UserInterface/RollingCounter.cs index b80c0e3b58..e69727e047 100644 --- a/osu.Game/Graphics/UserInterface/RollingCounter.cs +++ b/osu.Game/Graphics/UserInterface/RollingCounter.cs @@ -45,7 +45,7 @@ namespace osu.Game.Graphics.UserInterface /// /// Easing for the counter rollover animation. /// - protected virtual Easing RollingEasing => Easing.OutQuint; + protected virtual Easing RollingEasing => Easing.OutQuad; private T displayedCount; diff --git a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs index 94dd96ec1c..b9e4896b21 100644 --- a/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs +++ b/osu.Game/Overlays/Mods/BeatmapAttributesDisplay.cs @@ -196,7 +196,7 @@ namespace osu.Game.Overlays.Mods private partial class BPMDisplay : RollingCounter { - protected override double RollingDuration => 500; + protected override double RollingDuration => 250; protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0 BPM"); diff --git a/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs index b599b53082..a86eba81e4 100644 --- a/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs +++ b/osu.Game/Overlays/Mods/ScoreMultiplierDisplay.cs @@ -145,7 +145,7 @@ namespace osu.Game.Overlays.Mods private partial class EffectCounter : RollingCounter { - protected override double RollingDuration => 500; + protected override double RollingDuration => 250; protected override LocalisableString FormatCount(double count) => ModUtils.FormatScoreMultiplier(count); diff --git a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs index 33b7eaae1c..a3e24b486f 100644 --- a/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs +++ b/osu.Game/Overlays/Mods/VerticalAttributeDisplay.cs @@ -124,7 +124,7 @@ namespace osu.Game.Overlays.Mods private partial class EffectCounter : RollingCounter { - protected override double RollingDuration => 500; + protected override double RollingDuration => 250; protected override LocalisableString FormatCount(double count) => count.ToLocalisableString("0.0#"); diff --git a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs index 171aa3f44b..ca00ab12c7 100644 --- a/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonAccuracyCounter.cs @@ -17,8 +17,7 @@ namespace osu.Game.Screens.Play.HUD { public partial class ArgonAccuracyCounter : GameplayAccuracyCounter, ISerialisableDrawable { - protected override double RollingDuration => 500; - protected override Easing RollingEasing => Easing.OutQuint; + protected override double RollingDuration => 250; [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f) diff --git a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs index 1d6ca3c893..369c753cb0 100644 --- a/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonComboCounter.cs @@ -21,8 +21,7 @@ namespace osu.Game.Screens.Play.HUD { private ArgonCounterTextComponent text = null!; - protected override double RollingDuration => 500; - protected override Easing RollingEasing => Easing.OutQuint; + protected override double RollingDuration => 250; [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f) diff --git a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs index f7ca218767..348327d710 100644 --- a/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs +++ b/osu.Game/Screens/Play/HUD/ArgonScoreCounter.cs @@ -18,8 +18,7 @@ namespace osu.Game.Screens.Play.HUD { private ArgonScoreTextComponent scoreText = null!; - protected override double RollingDuration => 500; - protected override Easing RollingEasing => Easing.OutQuint; + protected override double RollingDuration => 250; [SettingSource("Wireframe opacity", "Controls the opacity of the wire frames behind the digits.")] public BindableFloat WireframeOpacity { get; } = new BindableFloat(0.25f) diff --git a/osu.Game/Screens/Play/HUD/BPMCounter.cs b/osu.Game/Screens/Play/HUD/BPMCounter.cs index cd24237493..9cd285db4c 100644 --- a/osu.Game/Screens/Play/HUD/BPMCounter.cs +++ b/osu.Game/Screens/Play/HUD/BPMCounter.cs @@ -18,7 +18,7 @@ namespace osu.Game.Screens.Play.HUD { public partial class BPMCounter : RollingCounter, ISerialisableDrawable { - protected override double RollingDuration => 750; + protected override double RollingDuration => 375; [Resolved] private IBindable beatmap { get; set; } = null!; diff --git a/osu.Game/Screens/Play/HUD/ClicksPerSecond/ClicksPerSecondCounter.cs b/osu.Game/Screens/Play/HUD/ClicksPerSecond/ClicksPerSecondCounter.cs index 9b5ea309b0..a1cccdef0a 100644 --- a/osu.Game/Screens/Play/HUD/ClicksPerSecond/ClicksPerSecondCounter.cs +++ b/osu.Game/Screens/Play/HUD/ClicksPerSecond/ClicksPerSecondCounter.cs @@ -19,7 +19,7 @@ namespace osu.Game.Screens.Play.HUD.ClicksPerSecond [Resolved] private ClicksPerSecondController controller { get; set; } = null!; - protected override double RollingDuration => 350; + protected override double RollingDuration => 175; public bool UsesFixedAnchor { get; set; } diff --git a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs index 82f116b4ae..f041e120f6 100644 --- a/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs +++ b/osu.Game/Screens/Play/HUD/PerformancePointsCounter.cs @@ -41,7 +41,7 @@ namespace osu.Game.Screens.Play.HUD protected override bool IsRollingProportional => true; - protected override double RollingDuration => 1000; + protected override double RollingDuration => 500; private const float alpha_when_invalid = 0.3f; diff --git a/osu.Game/Screens/Play/HUD/UnstableRateCounter.cs b/osu.Game/Screens/Play/HUD/UnstableRateCounter.cs index 701b8a8732..ab7ab6b3a0 100644 --- a/osu.Game/Screens/Play/HUD/UnstableRateCounter.cs +++ b/osu.Game/Screens/Play/HUD/UnstableRateCounter.cs @@ -23,7 +23,7 @@ namespace osu.Game.Screens.Play.HUD { public bool UsesFixedAnchor { get; set; } - protected override double RollingDuration => 750; + protected override double RollingDuration => 375; private const float alpha_when_invalid = 0.3f; private readonly Bindable valid = new Bindable(); diff --git a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs index 4b8c057235..f1f2c47e20 100644 --- a/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs +++ b/osu.Game/Screens/Ranking/Expanded/Statistics/AccuracyStatistic.cs @@ -44,9 +44,10 @@ namespace osu.Game.Screens.Ranking.Expanded.Statistics private partial class Counter : RollingCounter { - protected override double RollingDuration => AccuracyCircle.ACCURACY_TRANSFORM_DURATION; - - protected override Easing RollingEasing => AccuracyCircle.ACCURACY_TRANSFORM_EASING; + // FormatAccuracy doesn't round, which means if we use the OutPow10 easing the number will stick 0.01% short for some time. + // To avoid that let's use a shorter easing which looks roughly the same. + protected override double RollingDuration => AccuracyCircle.ACCURACY_TRANSFORM_DURATION / 2; + protected override Easing RollingEasing => Easing.OutQuad; protected override LocalisableString FormatCount(double count) => count.FormatAccuracy(); From 5ad2918a75d59c24ecc1d8a66cf6d4292b259bbd Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 11:21:44 +0300 Subject: [PATCH 1052/1118] Allow disabling high resolution texture lookups in `LegacySkin` --- .../Skinning/LegacySkinTextureFallbackTest.cs | 21 +++++++++++++++ osu.Game/Skinning/LegacySkin.cs | 26 ++++++++++++++----- 2 files changed, 40 insertions(+), 7 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs index ca0d4d3cf3..3a7b9af0cb 100644 --- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs +++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs @@ -127,8 +127,29 @@ namespace osu.Game.Tests.NonVisual.Skinning Assert.IsNull(texture); } + [Test] + public void TestDisallowHighResolutionSprites() + { + var textureStore = new TestTextureStore("hitcircle", "hitcircle@2x"); + var legacySkin = new TestLegacySkin(textureStore) { HighResolutionSprites = false }; + + var texture = legacySkin.GetTexture("hitcircle"); + + Assert.IsNotNull(texture); + Assert.That(texture.ScaleAdjust, Is.EqualTo(1)); + + texture = legacySkin.GetTexture("hitcircle@2x"); + + Assert.IsNotNull(texture); + Assert.That(texture.ScaleAdjust, Is.EqualTo(1)); + } + private class TestLegacySkin : LegacySkin { + public bool HighResolutionSprites { get; set; } = true; + + protected override bool AllowHighResolutionSprites => HighResolutionSprites; + public TestLegacySkin(IResourceStore textureStore) : base(new SkinInfo(), new TestResourceProvider(textureStore), null, string.Empty) { diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 8f0cd59b68..10913e7369 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -477,6 +477,11 @@ namespace osu.Game.Skinning return null; } + /// + /// Whether high-resolution textures ("@2x"-suffixed) are allowed to be used by when available. + /// + protected virtual bool AllowHighResolutionSprites => true; + public override Texture? GetTexture(string componentName, WrapMode wrapModeS, WrapMode wrapModeT) { switch (componentName) @@ -488,15 +493,22 @@ namespace osu.Game.Skinning foreach (string name in getFallbackNames(componentName)) { - // some component names (especially user-controlled ones, like `HitX` in mania) - // may contain `@2x` scale specifications. - // stable happens to check for that and strip them, so do the same to match stable behaviour. - string lookupName = name.Replace(@"@2x", string.Empty); + string lookupName = name; + Texture? texture = null; + float ratio = 1; - float ratio = 2; - string twoTimesFilename = $"{Path.ChangeExtension(lookupName, null)}@2x{Path.GetExtension(lookupName)}"; + if (AllowHighResolutionSprites) + { + // some component names (especially user-controlled ones, like `HitX` in mania) + // may contain `@2x` scale specifications. + // stable happens to check for that and strip them, so do the same to match stable behaviour. + lookupName = name.Replace(@"@2x", string.Empty); - var texture = Textures?.Get(twoTimesFilename, wrapModeS, wrapModeT); + string twoTimesFilename = $"{Path.ChangeExtension(lookupName, null)}@2x{Path.GetExtension(lookupName)}"; + + texture = Textures?.Get(twoTimesFilename, wrapModeS, wrapModeT); + ratio = 2; + } if (texture == null) { From 98c65f36c9b81b352f4d2eb575eba0cc7fdf1ed3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 11:21:56 +0300 Subject: [PATCH 1053/1118] Disable high resolution texture lookup for legacy beatmap skins to match stable --- osu.Game/Skinning/LegacyBeatmapSkin.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/Skinning/LegacyBeatmapSkin.cs b/osu.Game/Skinning/LegacyBeatmapSkin.cs index d6ba6ea332..9cd072b607 100644 --- a/osu.Game/Skinning/LegacyBeatmapSkin.cs +++ b/osu.Game/Skinning/LegacyBeatmapSkin.cs @@ -22,6 +22,11 @@ namespace osu.Game.Skinning protected override bool AllowManiaConfigLookups => false; protected override bool UseCustomSampleBanks => true; + // matches stable. references: + // 1. https://github.com/peppy/osu-stable-reference/blob/dc0994645801010d4b628fff5ff79cd3c286ca83/osu!/Graphics/Textures/TextureManager.cs#L115-L137 (beatmap skin textures lookup) + // 2. https://github.com/peppy/osu-stable-reference/blob/dc0994645801010d4b628fff5ff79cd3c286ca83/osu!/Graphics/Textures/TextureManager.cs#L158-L196 (user skin textures lookup) + protected override bool AllowHighResolutionSprites => false; + /// /// Construct a new legacy beatmap skin instance. /// From 1af5d1434c0abc615984fb64ef72d9d83d803740 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Wed, 17 Jan 2024 19:53:23 +0900 Subject: [PATCH 1054/1118] Rename test method slightly --- osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs index f3d2b5d518..6181024230 100644 --- a/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs +++ b/osu.Game.Tests/Visual/Editing/TestSceneTimingScreen.cs @@ -180,7 +180,7 @@ namespace osu.Game.Tests.Visual.Editing } [Test] - public void TestEditThenClickAway() + public void TestEditThenClickAwayAppliesChanges() { AddStep("Add two control points", () => { From a8970d7642a6a2aa1834f4008ddc900997629dae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Wed, 17 Jan 2024 12:24:48 +0100 Subject: [PATCH 1055/1118] Jiggle ordering a bit further to fix revert-to-default buttons showing up --- osu.Game.Rulesets.Taiko/TaikoRuleset.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs index b80d8218f5..24b0ec5d57 100644 --- a/osu.Game.Rulesets.Taiko/TaikoRuleset.cs +++ b/osu.Game.Rulesets.Taiko/TaikoRuleset.cs @@ -69,11 +69,11 @@ namespace osu.Game.Rulesets.Taiko public override IEnumerable GetDefaultKeyBindings(int variant = 0) => new[] { - new KeyBinding(InputKey.D, TaikoAction.LeftRim), - new KeyBinding(InputKey.F, TaikoAction.LeftCentre), - new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), - new KeyBinding(InputKey.J, TaikoAction.RightCentre), new KeyBinding(InputKey.MouseRight, TaikoAction.LeftRim), + new KeyBinding(InputKey.D, TaikoAction.LeftRim), + new KeyBinding(InputKey.MouseLeft, TaikoAction.LeftCentre), + new KeyBinding(InputKey.F, TaikoAction.LeftCentre), + new KeyBinding(InputKey.J, TaikoAction.RightCentre), new KeyBinding(InputKey.K, TaikoAction.RightRim), }; From bcc7dd6af574ecbfb4004f5aaa6265f149578d86 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 15:58:27 +0300 Subject: [PATCH 1056/1118] Add extended test coverage --- .../Skinning/LegacySkinTextureFallbackTest.cs | 25 +++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs index 3a7b9af0cb..ff954211eb 100644 --- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs +++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs @@ -138,10 +138,31 @@ namespace osu.Game.Tests.NonVisual.Skinning Assert.IsNotNull(texture); Assert.That(texture.ScaleAdjust, Is.EqualTo(1)); - texture = legacySkin.GetTexture("hitcircle@2x"); + var twoTimesTexture = legacySkin.GetTexture("hitcircle@2x"); + + Assert.IsNotNull(twoTimesTexture); + Assert.That(twoTimesTexture.ScaleAdjust, Is.EqualTo(1)); + + Assert.AreNotEqual(texture, twoTimesTexture); + } + + [Test] + public void TestAllowHighResolutionSprites() + { + var textureStore = new TestTextureStore("hitcircle", "hitcircle@2x"); + var legacySkin = new TestLegacySkin(textureStore) { HighResolutionSprites = true }; + + var texture = legacySkin.GetTexture("hitcircle"); Assert.IsNotNull(texture); - Assert.That(texture.ScaleAdjust, Is.EqualTo(1)); + Assert.That(texture.ScaleAdjust, Is.EqualTo(2)); + + var twoTimesTexture = legacySkin.GetTexture("hitcircle@2x"); + + Assert.IsNotNull(twoTimesTexture); + Assert.That(twoTimesTexture.ScaleAdjust, Is.EqualTo(2)); + + Assert.AreEqual(texture, twoTimesTexture); } private class TestLegacySkin : LegacySkin From e54d20ea93514a017889ee560f92e06d7a28d37f Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 13:27:44 +0300 Subject: [PATCH 1057/1118] Remove ancient osu-resources lookup path from legacy skin textures --- .../Skinning/Legacy/LegacyApproachCircle.cs | 2 +- .../Skinning/Legacy/LegacyReverseArrow.cs | 8 +-- .../Skinning/GameplaySkinComponentLookup.cs | 4 -- osu.Game/Skinning/LegacySkin.cs | 57 ++++++++----------- 4 files changed, 29 insertions(+), 42 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs index eea6606233..d31c763c2c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs @@ -21,7 +21,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy private DrawableHitObject drawableObject { get; set; } = null!; public LegacyApproachCircle() - : base("Gameplay/osu/approachcircle", OsuHitObject.OBJECT_DIMENSIONS * 2) + : base(@"approachcircle", OsuHitObject.OBJECT_DIMENSIONS * 2) { } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs index 780084115d..ad1fb98aef 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyReverseArrow.cs @@ -34,19 +34,19 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy [BackgroundDependencyLoader] private void load(DrawableHitObject drawableObject, ISkinSource skinSource) { + const string lookup_name = @"reversearrow"; + drawableRepeat = (DrawableSliderRepeat)drawableObject; AutoSizeAxes = Axes.Both; - string lookupName = new OsuSkinComponentLookup(OsuSkinComponents.ReverseArrow).LookupName; - - var skin = skinSource.FindProvider(s => s.GetTexture(lookupName) != null); + var skin = skinSource.FindProvider(s => s.GetTexture(lookup_name) != null); InternalChild = arrow = new Sprite { Anchor = Anchor.Centre, Origin = Anchor.Centre, - Texture = skin?.GetTexture(lookupName)?.WithMaximumSize(maxSize: OsuHitObject.OBJECT_DIMENSIONS * 2), + Texture = skin?.GetTexture(lookup_name)?.WithMaximumSize(maxSize: OsuHitObject.OBJECT_DIMENSIONS * 2), }; textureIsDefaultSkin = skin is ISkinTransformer transformer && transformer.Skin is DefaultLegacySkin; diff --git a/osu.Game/Skinning/GameplaySkinComponentLookup.cs b/osu.Game/Skinning/GameplaySkinComponentLookup.cs index a44bf3a43d..ec159873f8 100644 --- a/osu.Game/Skinning/GameplaySkinComponentLookup.cs +++ b/osu.Game/Skinning/GameplaySkinComponentLookup.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using System.Linq; using osu.Game.Rulesets.Judgements; using osu.Game.Rulesets.Scoring; @@ -28,8 +27,5 @@ namespace osu.Game.Skinning protected virtual string RulesetPrefix => string.Empty; protected virtual string ComponentName => Component.ToString(); - - public string LookupName => - string.Join('/', new[] { "Gameplay", RulesetPrefix, ComponentName }.Where(s => !string.IsNullOrEmpty(s))); } } diff --git a/osu.Game/Skinning/LegacySkin.cs b/osu.Game/Skinning/LegacySkin.cs index 10913e7369..cfa5f972d2 100644 --- a/osu.Game/Skinning/LegacySkin.cs +++ b/osu.Game/Skinning/LegacySkin.cs @@ -491,39 +491,30 @@ namespace osu.Game.Skinning break; } - foreach (string name in getFallbackNames(componentName)) + Texture? texture = null; + float ratio = 1; + + if (AllowHighResolutionSprites) { - string lookupName = name; - Texture? texture = null; - float ratio = 1; + // some component names (especially user-controlled ones, like `HitX` in mania) + // may contain `@2x` scale specifications. + // stable happens to check for that and strip them, so do the same to match stable behaviour. + componentName = componentName.Replace(@"@2x", string.Empty); - if (AllowHighResolutionSprites) - { - // some component names (especially user-controlled ones, like `HitX` in mania) - // may contain `@2x` scale specifications. - // stable happens to check for that and strip them, so do the same to match stable behaviour. - lookupName = name.Replace(@"@2x", string.Empty); + string twoTimesFilename = $"{Path.ChangeExtension(componentName, null)}@2x{Path.GetExtension(componentName)}"; - string twoTimesFilename = $"{Path.ChangeExtension(lookupName, null)}@2x{Path.GetExtension(lookupName)}"; + texture = Textures?.Get(twoTimesFilename, wrapModeS, wrapModeT); - texture = Textures?.Get(twoTimesFilename, wrapModeS, wrapModeT); + if (texture != null) ratio = 2; - } - - if (texture == null) - { - ratio = 1; - texture = Textures?.Get(lookupName, wrapModeS, wrapModeT); - } - - if (texture == null) - continue; - - texture.ScaleAdjust = ratio; - return texture; } - return null; + texture ??= Textures?.Get(componentName, wrapModeS, wrapModeT); + + if (texture != null) + texture.ScaleAdjust = ratio; + + return texture; } public override ISample? GetSample(ISampleInfo sampleInfo) @@ -534,7 +525,7 @@ namespace osu.Game.Skinning lookupNames = getLegacyLookupNames(hitSample); else { - lookupNames = sampleInfo.LookupNames.SelectMany(getFallbackNames); + lookupNames = sampleInfo.LookupNames.SelectMany(getFallbackSampleNames); } foreach (string lookup in lookupNames) @@ -552,7 +543,7 @@ namespace osu.Game.Skinning private IEnumerable getLegacyLookupNames(HitSampleInfo hitSample) { - var lookupNames = hitSample.LookupNames.SelectMany(getFallbackNames); + var lookupNames = hitSample.LookupNames.SelectMany(getFallbackSampleNames); if (!UseCustomSampleBanks && !string.IsNullOrEmpty(hitSample.Suffix)) { @@ -571,13 +562,13 @@ namespace osu.Game.Skinning yield return hitSample.Name; } - private IEnumerable getFallbackNames(string componentName) + private IEnumerable getFallbackSampleNames(string name) { - // May be something like "Gameplay/osu/approachcircle" from lazer, or "Arrows/note1" from a user skin. - yield return componentName; + // May be something like "Gameplay/normal-hitnormal" from lazer. + yield return name; - // Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/osu/approachcircle" -> "approachcircle"). - yield return componentName.Split('/').Last(); + // Fall back to using the last piece for components coming from lazer (e.g. "Gameplay/normal-hitnormal" -> "normal-hitnormal"). + yield return name.Split('/').Last(); } } } From ed1e66b8f99032828b3e52a02b1baf5b10accaeb Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 16:35:39 +0300 Subject: [PATCH 1058/1118] Fix enabling beatmap skin cause hitobjects to use `LegacyApproachCircle` regardless of selected skin --- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index c01d28c8e1..86ca5fff8c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Rulesets.Osu.Objects; @@ -163,7 +164,10 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; case OsuSkinComponents.ApproachCircle: - return new LegacyApproachCircle(); + if (IsProvidingLegacyResources) + return new LegacyApproachCircle(); + + return null; default: throw new UnsupportedSkinComponentException(lookup); From ede0a2269329cde6afbbc82d185b664f17591327 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 16:58:47 +0300 Subject: [PATCH 1059/1118] Remove existing test coverage --- .../Skinning/LegacySkinTextureFallbackTest.cs | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs index 3a7b9af0cb..89103cd4d9 100644 --- a/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs +++ b/osu.Game.Tests/NonVisual/Skinning/LegacySkinTextureFallbackTest.cs @@ -56,24 +56,6 @@ namespace osu.Game.Tests.NonVisual.Skinning "Gameplay/osu/followpoint", 1 }, new object[] - { - new[] { "followpoint@2x", "followpoint" }, - "Gameplay/osu/followpoint", - "followpoint@2x", 2 - }, - new object[] - { - new[] { "followpoint@2x" }, - "Gameplay/osu/followpoint", - "followpoint@2x", 2 - }, - new object[] - { - new[] { "followpoint" }, - "Gameplay/osu/followpoint", - "followpoint", 1 - }, - new object[] { // Looking up a filename with extension specified should work. new[] { "followpoint.png" }, From eaa748f075948e0162db1b38503df2a4ed203ada Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 18:35:34 +0300 Subject: [PATCH 1060/1118] Remove unused using directive --- .../Skinning/Legacy/OsuLegacySkinTransformer.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index 86ca5fff8c..b01c18031c 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -2,7 +2,6 @@ // See the LICENCE file in the repository root for full licence text. using System; -using NUnit.Framework; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Game.Rulesets.Osu.Objects; From 5597b48882b4ae4a00e3869b9bb42133db036ae2 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Wed, 17 Jan 2024 16:38:36 +0300 Subject: [PATCH 1061/1118] Fix storyboard animations stripping path directory on skin lookup --- osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs index cefd51b2aa..fae9ec7f2e 100644 --- a/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs +++ b/osu.Game/Storyboards/Drawables/DrawableStoryboardAnimation.cs @@ -129,7 +129,7 @@ namespace osu.Game.Storyboards.Drawables // When reading from a skin, we match stables weird behaviour where `FrameCount` is ignored // and resources are retrieved until the end of the animation. - var skinTextures = skin.GetTextures(Path.GetFileNameWithoutExtension(Animation.Path)!, default, default, true, string.Empty, null, out _); + var skinTextures = skin.GetTextures(Path.ChangeExtension(Animation.Path, null), default, default, true, string.Empty, null, out _); if (skinTextures.Length > 0) { From 42e4c933d374bc3ebc863ec38bc7951dd0498054 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 18 Jan 2024 04:06:02 +0300 Subject: [PATCH 1062/1118] Fix ConstrainedIconContainer always using masking --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 1 - .../Containers/ConstrainedIconContainer.cs | 18 ------------------ .../Toolbar/ToolbarRulesetTabButton.cs | 11 +---------- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 1665ec52fa..eecf79aa34 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -92,7 +92,6 @@ namespace osu.Game.Beatmaps.Drawables EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0.06f), - Type = EdgeEffectType.Shadow, Radius = 3, }, diff --git a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs index 7722374c69..63ac84fcf7 100644 --- a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs +++ b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs @@ -4,7 +4,6 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osuTK; namespace osu.Game.Graphics.Containers @@ -17,21 +16,9 @@ namespace osu.Game.Graphics.Containers public Drawable Icon { get => InternalChild; - set => InternalChild = value; } - /// - /// Determines an edge effect of this . - /// Edge effects are e.g. glow or a shadow. - /// Only has an effect when is true. - /// - public new EdgeEffectParameters EdgeEffect - { - get => base.EdgeEffect; - set => base.EdgeEffect = value; - } - protected override void Update() { base.Update(); @@ -49,10 +36,5 @@ namespace osu.Game.Graphics.Containers InternalChild.Origin = Anchor.Centre; } } - - public ConstrainedIconContainer() - { - Masking = true; - } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs index c224f0f9c9..3287ac6eaa 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -58,15 +57,7 @@ namespace osu.Game.Overlays.Toolbar { set => Scheduler.AddOnce(() => { - if (value) - { - IconContainer.Colour = Color4Extensions.FromHex("#00FFAA"); - } - else - { - IconContainer.Colour = colours.GrayF; - IconContainer.EdgeEffect = new EdgeEffectParameters(); - } + IconContainer.Colour = value ? Color4Extensions.FromHex("#00FFAA") : colours.GrayF; }); } From c362a93a3688cd13b8befc11ca1a75a59d8172cc Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 02:18:20 +0900 Subject: [PATCH 1063/1118] Change frame stable catch-up method to allow for much faster sync --- .../Gameplay/TestSceneFrameStabilityContainer.cs | 4 +--- .../Visual/Gameplay/TestSceneSongProgress.cs | 5 +---- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 10 ++++++---- 3 files changed, 8 insertions(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs index 534348bed3..98a97e1d23 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneFrameStabilityContainer.cs @@ -129,10 +129,8 @@ namespace osu.Game.Tests.Visual.Gameplay checkRate(1); } - private const int max_frames_catchup = 50; - private void createStabilityContainer(double gameplayStartTime = double.MinValue) => AddStep("create container", () => - mainContainer.Child = new FrameStabilityContainer(gameplayStartTime) { MaxCatchUpFrames = max_frames_catchup } + mainContainer.Child = new FrameStabilityContainer(gameplayStartTime) .WithChild(consumer = new ClockConsumingChild())); private void seekManualTo(double time) => AddStep($"seek manual clock to {time}", () => manualClock.CurrentTime = time); diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index e975a85401..19bb5cdde1 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -45,10 +45,7 @@ namespace osu.Game.Tests.Visual.Gameplay }, gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, skip_target_time) { - Child = frameStabilityContainer = new FrameStabilityContainer - { - MaxCatchUpFrames = 1 - } + Child = frameStabilityContainer = new FrameStabilityContainer() } }); diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 2af9916a6b..c07bd3aef5 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -25,9 +25,9 @@ namespace osu.Game.Rulesets.UI public ReplayInputHandler? ReplayInputHandler { get; set; } /// - /// The number of frames (per parent frame) which can be run in an attempt to catch-up to real-time. + /// The number of CPU milliseconds to spend at most during seek catch-up. /// - public int MaxCatchUpFrames { get; set; } = 5; + private const double max_catchup_milliseconds = 10; /// /// Whether to enable frame-stable playback. @@ -61,6 +61,8 @@ namespace osu.Game.Rulesets.UI /// private readonly FramedClock framedClock; + private readonly Stopwatch stopwatch = new Stopwatch(); + /// /// The current direction of playback to be exposed to frame stable children. /// @@ -99,7 +101,7 @@ namespace osu.Game.Rulesets.UI public override bool UpdateSubTree() { - int loops = MaxCatchUpFrames; + stopwatch.Restart(); do { @@ -112,7 +114,7 @@ namespace osu.Game.Rulesets.UI base.UpdateSubTree(); UpdateSubTreeMasking(this, ScreenSpaceDrawQuad.AABBFloat); - } while (state == PlaybackState.RequiresCatchUp && loops-- > 0); + } while (state == PlaybackState.RequiresCatchUp && stopwatch.ElapsedMilliseconds < max_catchup_milliseconds); return true; } From fb4efd992de542ae5ad57ce192d9626de692e8ab Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 11:52:28 +0900 Subject: [PATCH 1064/1118] Add a fake load to visual test to restore previous testing behaviour --- .../Visual/Gameplay/TestSceneSongProgress.cs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs index 19bb5cdde1..99f0ffb9d0 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestSceneSongProgress.cs @@ -3,6 +3,7 @@ using System; using System.Linq; +using System.Threading; using NUnit.Framework; using osu.Framework.Allocation; using osu.Framework.Extensions.IEnumerableExtensions; @@ -45,7 +46,10 @@ namespace osu.Game.Tests.Visual.Gameplay }, gameplayClockContainer = new MasterGameplayClockContainer(Beatmap.Value, skip_target_time) { - Child = frameStabilityContainer = new FrameStabilityContainer() + Child = frameStabilityContainer = new FrameStabilityContainer + { + Child = new FakeLoad() + } } }); @@ -53,6 +57,15 @@ namespace osu.Game.Tests.Visual.Gameplay Dependencies.CacheAs(frameStabilityContainer); } + private partial class FakeLoad : Drawable + { + protected override void Update() + { + base.Update(); + Thread.Sleep(1); + } + } + [SetUpSteps] public void SetupSteps() { From 799c74cfe533da28509699dd2feea84179a2b8f8 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 02:40:34 +0900 Subject: [PATCH 1065/1118] Simplify gameplay pause sequence --- .../Visual/Gameplay/TestScenePause.cs | 5 +- .../Play/MasterGameplayClockContainer.cs | 66 ------------------- 2 files changed, 1 insertion(+), 70 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index ec3b3e0822..d55af2777f 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -93,15 +93,12 @@ namespace osu.Game.Tests.Visual.Gameplay double currentTime = masterClock.CurrentTime; - bool goingForward = currentTime >= (masterClock.LastStopTime ?? lastStopTime); + bool goingForward = currentTime >= lastStopTime; alwaysGoingForward &= goingForward; if (!goingForward) Logger.Log($"Went too far backwards (last stop: {lastStopTime:N1} current: {currentTime:N1})"); - - if (masterClock.LastStopTime != null) - lastStopTime = masterClock.LastStopTime.Value; }; }); diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 12f541167f..10451963a1 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -7,7 +7,6 @@ using osu.Framework.Allocation; using osu.Framework.Audio; using osu.Framework.Audio.Track; using osu.Framework.Bindables; -using osu.Framework.Graphics; using osu.Framework.Logging; using osu.Framework.Timing; using osu.Game.Beatmaps; @@ -60,17 +59,6 @@ namespace osu.Game.Screens.Play private readonly double skipTargetTime; - /// - /// Stores the time at which the last call was triggered. - /// This is used to ensure we resume from that precise point in time, ignoring the proceeding frequency ramp. - /// - /// Optimally, we'd have gameplay ramp down with the frequency, but I believe this was intentionally disabled - /// to avoid fails occurring after the pause screen has been shown. - /// - /// In the future I want to change this. - /// - internal double? LastStopTime; - [Resolved] private MusicController musicController { get; set; } = null!; @@ -113,71 +101,17 @@ namespace osu.Game.Screens.Play return time; } - protected override void StopGameplayClock() - { - LastStopTime = GameplayClock.CurrentTime; - - if (IsLoaded) - { - // During normal operation, the source is stopped after performing a frequency ramp. - this.TransformBindableTo(GameplayClock.ExternalPauseFrequencyAdjust, 0, 200, Easing.Out).OnComplete(_ => - { - if (IsPaused.Value) - base.StopGameplayClock(); - }); - } - else - { - base.StopGameplayClock(); - - // If not yet loaded, we still want to ensure relevant state is correct, as it is used for offset calculations. - GameplayClock.ExternalPauseFrequencyAdjust.Value = 0; - - // We must also process underlying gameplay clocks to update rate-adjusted offsets with the new frequency adjustment. - // Without doing this, an initial seek may be performed with the wrong offset. - GameplayClock.ProcessFrame(); - } - } - public override void Seek(double time) { - // Safety in case the clock is seeked while stopped. - LastStopTime = null; elapsedValidationTime = null; base.Seek(time); } - protected override void PrepareStart() - { - if (LastStopTime != null) - { - Seek(LastStopTime.Value); - LastStopTime = null; - } - else - base.PrepareStart(); - } - protected override void StartGameplayClock() { addAdjustmentsToTrack(); - base.StartGameplayClock(); - - if (IsLoaded) - { - this.TransformBindableTo(GameplayClock.ExternalPauseFrequencyAdjust, 1, 200, Easing.In); - } - else - { - // If not yet loaded, we still want to ensure relevant state is correct, as it is used for offset calculations. - GameplayClock.ExternalPauseFrequencyAdjust.Value = 1; - - // We must also process underlying gameplay clocks to update rate-adjusted offsets with the new frequency adjustment. - // Without doing this, an initial seek may be performed with the wrong offset. - GameplayClock.ProcessFrame(); - } } /// From 8ab8c90e338f00c784e05fc406540b872cefa5a0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 14:21:02 +0900 Subject: [PATCH 1066/1118] Remove "pause rate adjust" flow --- osu.Game/Beatmaps/FramedBeatmapClock.cs | 11 +++-------- .../Screens/Play/MasterGameplayClockContainer.cs | 2 -- osu.Game/Screens/Play/OffsetCorrectionClock.cs | 14 +++----------- 3 files changed, 6 insertions(+), 21 deletions(-) diff --git a/osu.Game/Beatmaps/FramedBeatmapClock.cs b/osu.Game/Beatmaps/FramedBeatmapClock.cs index 587e6bbeed..d0ffbdd459 100644 --- a/osu.Game/Beatmaps/FramedBeatmapClock.cs +++ b/osu.Game/Beatmaps/FramedBeatmapClock.cs @@ -27,11 +27,6 @@ namespace osu.Game.Beatmaps { private readonly bool applyOffsets; - /// - /// The total frequency adjustment from pause transforms. Should eventually be handled in a better way. - /// - public readonly BindableDouble ExternalPauseFrequencyAdjust = new BindableDouble(1); - private readonly OffsetCorrectionClock? userGlobalOffsetClock; private readonly OffsetCorrectionClock? platformOffsetClock; private readonly OffsetCorrectionClock? userBeatmapOffsetClock; @@ -69,13 +64,13 @@ namespace osu.Game.Beatmaps { // Audio timings in general with newer BASS versions don't match stable. // This only seems to be required on windows. We need to eventually figure out why, with a bit of luck. - platformOffsetClock = new OffsetCorrectionClock(interpolatedTrack, ExternalPauseFrequencyAdjust) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 }; + platformOffsetClock = new OffsetCorrectionClock(interpolatedTrack) { Offset = RuntimeInfo.OS == RuntimeInfo.Platform.Windows ? 15 : 0 }; // User global offset (set in settings) should also be applied. - userGlobalOffsetClock = new OffsetCorrectionClock(platformOffsetClock, ExternalPauseFrequencyAdjust); + userGlobalOffsetClock = new OffsetCorrectionClock(platformOffsetClock); // User per-beatmap offset will be applied to this final clock. - finalClockSource = userBeatmapOffsetClock = new OffsetCorrectionClock(userGlobalOffsetClock, ExternalPauseFrequencyAdjust); + finalClockSource = userBeatmapOffsetClock = new OffsetCorrectionClock(userGlobalOffsetClock); } else { diff --git a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs index 10451963a1..93bdcb1cab 100644 --- a/osu.Game/Screens/Play/MasterGameplayClockContainer.cs +++ b/osu.Game/Screens/Play/MasterGameplayClockContainer.cs @@ -207,7 +207,6 @@ namespace osu.Game.Screens.Play musicController.ResetTrackAdjustments(); track.BindAdjustments(AdjustmentsFromMods); - track.AddAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust); track.AddAdjustment(AdjustableProperty.Frequency, UserPlaybackRate); speedAdjustmentsApplied = true; @@ -219,7 +218,6 @@ namespace osu.Game.Screens.Play return; track.UnbindAdjustments(AdjustmentsFromMods); - track.RemoveAdjustment(AdjustableProperty.Frequency, GameplayClock.ExternalPauseFrequencyAdjust); track.RemoveAdjustment(AdjustableProperty.Frequency, UserPlaybackRate); speedAdjustmentsApplied = false; diff --git a/osu.Game/Screens/Play/OffsetCorrectionClock.cs b/osu.Game/Screens/Play/OffsetCorrectionClock.cs index 207980f45c..e83ed7e464 100644 --- a/osu.Game/Screens/Play/OffsetCorrectionClock.cs +++ b/osu.Game/Screens/Play/OffsetCorrectionClock.cs @@ -1,15 +1,12 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using osu.Framework.Bindables; using osu.Framework.Timing; namespace osu.Game.Screens.Play { public class OffsetCorrectionClock : FramedOffsetClock { - private readonly BindableDouble pauseRateAdjust; - private double offset; public new double Offset @@ -28,10 +25,9 @@ namespace osu.Game.Screens.Play public double RateAdjustedOffset => base.Offset; - public OffsetCorrectionClock(IClock source, BindableDouble pauseRateAdjust) + public OffsetCorrectionClock(IClock source) : base(source) { - this.pauseRateAdjust = pauseRateAdjust; } public override void ProcessFrame() @@ -42,12 +38,8 @@ namespace osu.Game.Screens.Play private void updateOffset() { - // changing this during the pause transform effect will cause a potentially large offset to be suddenly applied as we approach zero rate. - if (pauseRateAdjust.Value == 1) - { - // we always want to apply the same real-time offset, so it should be adjusted by the difference in playback rate (from realtime) to achieve this. - base.Offset = Offset * Rate; - } + // we always want to apply the same real-time offset, so it should be adjusted by the difference in playback rate (from realtime) to achieve this. + base.Offset = Offset * Rate; } } } From a870f99877fa3758307d33fae01e46f5f067af3b Mon Sep 17 00:00:00 2001 From: mouzedrift <43358824+mouzedrift@users.noreply.github.com> Date: Thu, 18 Jan 2024 07:19:29 +0100 Subject: [PATCH 1067/1118] ease out input drum animation and add delay before fading out --- osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs index 8ad419f8bd..d9e94ede4a 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs @@ -159,8 +159,8 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy const float up_time = 50; target.Animate( - t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time) - ).Then( + t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.Out) + ).Delay(100).Then( t => t.FadeOut(up_time) ); } From ee3de9c522685b5f85eeb9c021f9c6eab8b9d9a5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 15:44:27 +0900 Subject: [PATCH 1068/1118] Fix gameplay elements not correcly offsetting to avoid per-ruleset skin layout Closes https://github.com/ppy/osu/issues/26601. --- osu.Game/Screens/Play/HUDOverlay.cs | 49 ++++++++++++++++++----------- 1 file changed, 30 insertions(+), 19 deletions(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index 128f8d5ffd..a9fe393395 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -99,6 +99,9 @@ namespace osu.Game.Screens.Play private readonly SkinComponentsContainer mainComponents; + [CanBeNull] + private readonly SkinComponentsContainer rulesetComponents; + /// /// A flow which sits at the left side of the screen to house leaderboard (and related) components. /// Will automatically be positioned to avoid colliding with top scoring elements. @@ -111,7 +114,6 @@ namespace osu.Game.Screens.Play public HUDOverlay([CanBeNull] DrawableRuleset drawableRuleset, IReadOnlyList mods, bool alwaysShowLeaderboard = true) { - Drawable rulesetComponents; this.drawableRuleset = drawableRuleset; this.mods = mods; @@ -125,8 +127,8 @@ namespace osu.Game.Screens.Play clicksPerSecondController = new ClicksPerSecondController(), InputCountController = new InputCountController(), mainComponents = new HUDComponentsContainer { AlwaysPresent = true, }, - rulesetComponents = drawableRuleset != null - ? new HUDComponentsContainer(drawableRuleset.Ruleset.RulesetInfo) { AlwaysPresent = true, } + drawableRuleset != null + ? (rulesetComponents = new HUDComponentsContainer(drawableRuleset.Ruleset.RulesetInfo) { AlwaysPresent = true, }) : Empty(), playfieldComponents = drawableRuleset != null ? new SkinComponentsContainer(new SkinComponentsContainerLookup(SkinComponentsContainerLookup.TargetArea.Playfield, drawableRuleset.Ruleset.RulesetInfo)) { AlwaysPresent = true, } @@ -256,13 +258,37 @@ namespace osu.Game.Screens.Play // LINQ cast can be removed when IDrawable interface includes Anchor / RelativeSizeAxes. foreach (var element in mainComponents.Components.Cast()) + processDrawable(element); + + if (rulesetComponents != null) + { + foreach (var element in rulesetComponents.Components.Cast()) + processDrawable(element); + } + + if (lowestTopScreenSpaceRight.HasValue) + topRightElements.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceRight.Value)).Y, 0, DrawHeight - topRightElements.DrawHeight); + else + topRightElements.Y = 0; + + if (lowestTopScreenSpaceLeft.HasValue) + LeaderboardFlow.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceLeft.Value)).Y, 0, DrawHeight - LeaderboardFlow.DrawHeight); + else + LeaderboardFlow.Y = 0; + + if (highestBottomScreenSpace.HasValue) + bottomRightElements.Y = BottomScoringElementsHeight = -MathHelper.Clamp(DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y, 0, DrawHeight - bottomRightElements.DrawHeight); + else + bottomRightElements.Y = 0; + + void processDrawable(Drawable element) { // for now align some top components with the bottom-edge of the lowest top-anchored hud element. if (element.Anchor.HasFlagFast(Anchor.y0)) { // health bars are excluded for the sake of hacky legacy skins which extend the health bar to take up the full screen area. if (element is LegacyHealthDisplay) - continue; + return; float bottom = element.ScreenSpaceDrawQuad.BottomRight.Y; @@ -288,21 +314,6 @@ namespace osu.Game.Screens.Play highestBottomScreenSpace = topLeft; } } - - if (lowestTopScreenSpaceRight.HasValue) - topRightElements.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceRight.Value)).Y, 0, DrawHeight - topRightElements.DrawHeight); - else - topRightElements.Y = 0; - - if (lowestTopScreenSpaceLeft.HasValue) - LeaderboardFlow.Y = MathHelper.Clamp(ToLocalSpace(new Vector2(0, lowestTopScreenSpaceLeft.Value)).Y, 0, DrawHeight - LeaderboardFlow.DrawHeight); - else - LeaderboardFlow.Y = 0; - - if (highestBottomScreenSpace.HasValue) - bottomRightElements.Y = BottomScoringElementsHeight = -MathHelper.Clamp(DrawHeight - ToLocalSpace(highestBottomScreenSpace.Value).Y, 0, DrawHeight - bottomRightElements.DrawHeight); - else - bottomRightElements.Y = 0; } private void updateVisibility() From cbfc6091af4265462e89016c3dc6210c58cff2d8 Mon Sep 17 00:00:00 2001 From: mouzedrift <43358824+mouzedrift@users.noreply.github.com> Date: Thu, 18 Jan 2024 08:22:26 +0100 Subject: [PATCH 1069/1118] apply proposed changes --- .../Skinning/Legacy/LegacyInputDrum.cs | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs index d9e94ede4a..28415bb72a 100644 --- a/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs +++ b/osu.Game.Rulesets.Taiko/Skinning/Legacy/LegacyInputDrum.cs @@ -1,7 +1,6 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using osu.Framework.Allocation; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; @@ -153,16 +152,12 @@ namespace osu.Game.Rulesets.Taiko.Skinning.Legacy if (target != null) { - const float alpha_amount = 1; - const float down_time = 80; const float up_time = 50; - target.Animate( - t => t.FadeTo(Math.Min(target.Alpha + alpha_amount, 1), down_time, Easing.Out) - ).Delay(100).Then( - t => t.FadeOut(up_time) - ); + target + .FadeTo(1, down_time * (1 - target.Alpha), Easing.Out) + .Delay(100).FadeOut(up_time); } return false; From ef0b5c94064654f798727ad0dbc12f919e6284d3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 16:31:20 +0900 Subject: [PATCH 1070/1118] Stop playing back samples for nested screens in multiplayer I'd argue this sounds better. --- osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index b527bf98a2..ea855c0474 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -13,6 +13,8 @@ namespace osu.Game.Screens.OnlinePlay public virtual string ShortTitle => Title; + protected override bool PlayExitSound => false; + [Resolved] protected IRoomManager? RoomManager { get; private set; } From fd9c7184e449ffeeadeb3768e80bdb059bcaa92c Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 16:31:33 +0900 Subject: [PATCH 1071/1118] Only play back sample once when traversing up multiple screens in stack --- osu.Game/Screens/OsuScreen.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OsuScreen.cs b/osu.Game/Screens/OsuScreen.cs index 490a1ae6b8..f719ef67c9 100644 --- a/osu.Game/Screens/OsuScreen.cs +++ b/osu.Game/Screens/OsuScreen.cs @@ -223,7 +223,12 @@ namespace osu.Game.Screens public override bool OnExiting(ScreenExitEvent e) { - if (ValidForResume && PlayExitSound) + // Only play the exit sound if we are the last screen in the exit sequence. + // This stops many sample playbacks from stacking when a huge screen purge happens (ie. returning to menu via the home button + // from a deeply nested screen). + bool arrivingAtFinalDestination = e.Next == e.Destination; + + if (ValidForResume && PlayExitSound && arrivingAtFinalDestination) sampleExit?.Play(); if (ValidForResume && logo != null) From 1527ab89ef1dee1994f4629a5ebbcbcf2c9edc8a Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 18 Jan 2024 10:34:23 +0300 Subject: [PATCH 1072/1118] Refactor `DefaultApproachCircle`/`LegacyApproachCircle` to make sense --- .../Skinning/Default/DefaultApproachCircle.cs | 34 +++++++----------- .../Skinning/Legacy/LegacyApproachCircle.cs | 36 ++++++++----------- .../Legacy/OsuLegacySkinTransformer.cs | 2 +- 3 files changed, 28 insertions(+), 44 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultApproachCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultApproachCircle.cs index b65f46c414..272f4b5658 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/DefaultApproachCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/DefaultApproachCircle.cs @@ -3,47 +3,39 @@ using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; +using osu.Framework.Graphics.Textures; using osu.Game.Rulesets.Objects.Drawables; +using osu.Game.Rulesets.Osu.Objects; using osu.Game.Skinning; using osuTK; using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Default { - public partial class DefaultApproachCircle : SkinnableSprite + public partial class DefaultApproachCircle : Sprite { - private readonly IBindable accentColour = new Bindable(); - [Resolved] private DrawableHitObject drawableObject { get; set; } = null!; - public DefaultApproachCircle() - : base("Gameplay/osu/approachcircle") - { - } + private IBindable accentColour = null!; [BackgroundDependencyLoader] - private void load() + private void load(TextureStore textures) { - accentColour.BindTo(drawableObject.AccentColour); + Texture = textures.Get(@"Gameplay/osu/approachcircle").WithMaximumSize(OsuHitObject.OBJECT_DIMENSIONS * 2); + + // account for the sprite being used for the default approach circle being taken from stable, + // when hitcircles have 5px padding on each size. this should be removed if we update the sprite. + Scale = new Vector2(128 / 118f); } protected override void LoadComplete() { base.LoadComplete(); - accentColour.BindValueChanged(colour => Colour = colour.NewValue, true); - } - protected override Drawable CreateDefault(ISkinComponentLookup lookup) - { - var drawable = base.CreateDefault(lookup); - - // Although this is a non-legacy component, osu-resources currently stores approach circle as a legacy-like texture. - // See LegacyApproachCircle for documentation as to why this is required. - drawable.Scale = new Vector2(128 / 118f); - - return drawable; + accentColour = drawableObject.AccentColour.GetBoundCopy(); + accentColour.BindValueChanged(colour => Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs index d31c763c2c..0bdea0cab1 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/LegacyApproachCircle.cs @@ -1,9 +1,10 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using System.Diagnostics; using osu.Framework.Allocation; using osu.Framework.Bindables; -using osu.Framework.Graphics; +using osu.Framework.Graphics.Sprites; using osu.Game.Rulesets.Objects.Drawables; using osu.Game.Rulesets.Osu.Objects; using osu.Game.Skinning; @@ -12,40 +13,31 @@ using osuTK.Graphics; namespace osu.Game.Rulesets.Osu.Skinning.Legacy { - // todo: this should probably not be a SkinnableSprite, as this is always created for legacy skins and is recreated on skin change. - public partial class LegacyApproachCircle : SkinnableSprite + public partial class LegacyApproachCircle : Sprite { - private readonly IBindable accentColour = new Bindable(); - [Resolved] private DrawableHitObject drawableObject { get; set; } = null!; - public LegacyApproachCircle() - : base(@"approachcircle", OsuHitObject.OBJECT_DIMENSIONS * 2) - { - } + private IBindable accentColour = null!; [BackgroundDependencyLoader] - private void load() + private void load(ISkinSource skin) { - accentColour.BindTo(drawableObject.AccentColour); + var texture = skin.GetTexture(@"approachcircle"); + Debug.Assert(texture != null); + Texture = texture.WithMaximumSize(OsuHitObject.OBJECT_DIMENSIONS * 2); + + // account for the sprite being used for the default approach circle being taken from stable, + // when hitcircles have 5px padding on each size. this should be removed if we update the sprite. + Scale = new Vector2(128 / 118f); } protected override void LoadComplete() { base.LoadComplete(); + + accentColour = drawableObject.AccentColour.GetBoundCopy(); accentColour.BindValueChanged(colour => Colour = LegacyColourCompatibility.DisallowZeroAlpha(colour.NewValue), true); } - - protected override Drawable CreateDefault(ISkinComponentLookup lookup) - { - var drawable = base.CreateDefault(lookup); - - // account for the sprite being used for the default approach circle being taken from stable, - // when hitcircles have 5px padding on each size. this should be removed if we update the sprite. - drawable.Scale = new Vector2(128 / 118f); - - return drawable; - } } } diff --git a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs index b01c18031c..d2ebc68c52 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Legacy/OsuLegacySkinTransformer.cs @@ -163,7 +163,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Legacy return null; case OsuSkinComponents.ApproachCircle: - if (IsProvidingLegacyResources) + if (GetTexture(@"approachcircle") != null) return new LegacyApproachCircle(); return null; From 82e7643df55323a7e66fb9dc3351becf3357c469 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 19:45:35 +0900 Subject: [PATCH 1073/1118] Update IPC usages Of note, I've disabled IPC on visual test runners as we generally don't use IPC in these cases. Having it set means that the game will not open while visual tests are open, which has been a complaint from devs in the past. --- .../osu.Game.Rulesets.EmptyFreeform.Tests/VisualTestRunner.cs | 2 +- .../osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs | 2 +- .../osu.Game.Rulesets.EmptyScrolling.Tests/VisualTestRunner.cs | 2 +- .../osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs | 2 +- osu.Desktop/Program.cs | 2 +- .../Visual/Navigation/TestSceneInterProcessCommunication.cs | 2 +- osu.Game.Tournament.Tests/TournamentTestRunner.cs | 2 +- osu.Game/OsuGame.cs | 2 ++ osu.Game/Tests/CleanRunHeadlessGameHost.cs | 2 +- osu.Game/Tests/VisualTestRunner.cs | 2 +- 10 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/VisualTestRunner.cs b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/VisualTestRunner.cs index 03ee7c9204..63c481a623 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/VisualTestRunner.cs +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/VisualTestRunner.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyFreeform.Tests [STAThread] public static int Main(string[] args) { - using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu")) { host.Run(new OsuTestBrowser()); return 0; diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs index 55c0cf6a3b..c44cbb845b 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon.Tests [STAThread] public static int Main(string[] args) { - using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu")) { host.Run(new OsuTestBrowser()); return 0; diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/VisualTestRunner.cs b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/VisualTestRunner.cs index b45505678c..5beb6616a7 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/VisualTestRunner.cs +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/VisualTestRunner.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.EmptyScrolling.Tests [STAThread] public static int Main(string[] args) { - using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu")) { host.Run(new OsuTestBrowser()); return 0; diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs index 55c0cf6a3b..c44cbb845b 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/VisualTestRunner.cs @@ -13,7 +13,7 @@ namespace osu.Game.Rulesets.Pippidon.Tests [STAThread] public static int Main(string[] args) { - using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu", new HostOptions { BindIPC = true })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu")) { host.Run(new OsuTestBrowser()); return 0; diff --git a/osu.Desktop/Program.cs b/osu.Desktop/Program.cs index 6b95a82703..a7453dc0e0 100644 --- a/osu.Desktop/Program.cs +++ b/osu.Desktop/Program.cs @@ -102,7 +102,7 @@ namespace osu.Desktop } } - using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { BindIPC = !tournamentClient })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(gameName, new HostOptions { IPCPort = !tournamentClient ? OsuGame.IPC_PORT : null })) { if (!host.IsPrimaryInstance) { diff --git a/osu.Game.Tests/Visual/Navigation/TestSceneInterProcessCommunication.cs b/osu.Game.Tests/Visual/Navigation/TestSceneInterProcessCommunication.cs index 1ecd38e1d3..83430b5665 100644 --- a/osu.Game.Tests/Visual/Navigation/TestSceneInterProcessCommunication.cs +++ b/osu.Game.Tests/Visual/Navigation/TestSceneInterProcessCommunication.cs @@ -63,7 +63,7 @@ namespace osu.Game.Tests.Visual.Navigation }); AddStep("create IPC sender channels", () => { - ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { BindIPC = true }); + ipcSenderHost = new HeadlessGameHost(gameHost.Name, new HostOptions { IPCPort = OsuGame.IPC_PORT }); osuSchemeLinkIPCSender = new OsuSchemeLinkIPCChannel(ipcSenderHost); archiveImportIPCSender = new ArchiveImportIPCChannel(ipcSenderHost); }); diff --git a/osu.Game.Tournament.Tests/TournamentTestRunner.cs b/osu.Game.Tournament.Tests/TournamentTestRunner.cs index 5f642b14f5..e09d1be22c 100644 --- a/osu.Game.Tournament.Tests/TournamentTestRunner.cs +++ b/osu.Game.Tournament.Tests/TournamentTestRunner.cs @@ -12,7 +12,7 @@ namespace osu.Game.Tournament.Tests [STAThread] public static int Main(string[] args) { - using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development", new HostOptions { BindIPC = true })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development")) { host.Run(new TournamentTestBrowser()); return 0; diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 0d6eb1ea44..7138bf2175 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -80,6 +80,8 @@ namespace osu.Game [Cached(typeof(OsuGame))] public partial class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo, IPerformFromScreenRunner, IOverlayManager, ILinkHandler { + public const int IPC_PORT = 44823; + /// /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). /// diff --git a/osu.Game/Tests/CleanRunHeadlessGameHost.cs b/osu.Game/Tests/CleanRunHeadlessGameHost.cs index f3c69201e2..00e5b38b1a 100644 --- a/osu.Game/Tests/CleanRunHeadlessGameHost.cs +++ b/osu.Game/Tests/CleanRunHeadlessGameHost.cs @@ -27,7 +27,7 @@ namespace osu.Game.Tests [CallerMemberName] string callingMethodName = @"") : base($"{callingMethodName}-{Guid.NewGuid()}", new HostOptions { - BindIPC = bindIPC, + IPCPort = bindIPC ? OsuGame.IPC_PORT : null, }, bypassCleanup: bypassCleanupOnDispose, realtime: realtime) { this.bypassCleanupOnSetup = bypassCleanupOnSetup; diff --git a/osu.Game/Tests/VisualTestRunner.cs b/osu.Game/Tests/VisualTestRunner.cs index e04c71d193..1a9e03b2a4 100644 --- a/osu.Game/Tests/VisualTestRunner.cs +++ b/osu.Game/Tests/VisualTestRunner.cs @@ -12,7 +12,7 @@ namespace osu.Game.Tests [STAThread] public static int Main(string[] args) { - using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development", new HostOptions { BindIPC = true, })) + using (DesktopGameHost host = Host.GetSuitableDesktopHost(@"osu-development")) { host.Run(new OsuTestBrowser()); return 0; From c4e9bcd140bbb59a094c282fc7b21ac04e8ec5d2 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 20:06:53 +0900 Subject: [PATCH 1074/1118] Remove test guarantee of audio time not advancing --- osu.Game.Tests/Visual/Gameplay/TestScenePause.cs | 2 +- osu.Game/Screens/Play/GameplayClockContainer.cs | 10 ---------- 2 files changed, 1 insertion(+), 11 deletions(-) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs index d55af2777f..73aa3be73d 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePause.cs @@ -122,7 +122,7 @@ namespace osu.Game.Tests.Visual.Gameplay resumeAndConfirm(); - AddAssert("Resumed without seeking forward", () => Player.LastResumeTime, () => Is.LessThanOrEqualTo(Player.LastPauseTime)); + AddAssert("continued playing forward", () => Player.LastResumeTime, () => Is.GreaterThanOrEqualTo(Player.LastPauseTime)); AddUntilStep("player playing", () => Player.LocalUserPlaying.Value); } diff --git a/osu.Game/Screens/Play/GameplayClockContainer.cs b/osu.Game/Screens/Play/GameplayClockContainer.cs index 4def1d36bb..c039d1e535 100644 --- a/osu.Game/Screens/Play/GameplayClockContainer.cs +++ b/osu.Game/Screens/Play/GameplayClockContainer.cs @@ -78,8 +78,6 @@ namespace osu.Game.Screens.Play isPaused.Value = false; - PrepareStart(); - // The case which caused this to be added is FrameStabilityContainer, which manages its own current and elapsed time. // Because we generally update our own current time quicker than children can query it (via Start/Seek/Update), // this means that the first frame ever exposed to children may have a non-zero current time. @@ -99,14 +97,6 @@ namespace osu.Game.Screens.Play }); } - /// - /// When is called, this will be run to give an opportunity to prepare the clock at the correct - /// start location. - /// - protected virtual void PrepareStart() - { - } - /// /// Seek to a specific time in gameplay. /// From 4532a409d23bc0cb2fffbecda32991c5278c4ea8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Thu, 18 Jan 2024 04:06:02 +0300 Subject: [PATCH 1075/1118] Fix ConstrainedIconContainer always using masking --- osu.Game/Beatmaps/Drawables/DifficultyIcon.cs | 1 - .../Containers/ConstrainedIconContainer.cs | 18 ------------------ .../Toolbar/ToolbarRulesetTabButton.cs | 11 +---------- 3 files changed, 1 insertion(+), 29 deletions(-) diff --git a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs index 1665ec52fa..eecf79aa34 100644 --- a/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs +++ b/osu.Game/Beatmaps/Drawables/DifficultyIcon.cs @@ -92,7 +92,6 @@ namespace osu.Game.Beatmaps.Drawables EdgeEffect = new EdgeEffectParameters { Colour = Color4.Black.Opacity(0.06f), - Type = EdgeEffectType.Shadow, Radius = 3, }, diff --git a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs index 7722374c69..63ac84fcf7 100644 --- a/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs +++ b/osu.Game/Graphics/Containers/ConstrainedIconContainer.cs @@ -4,7 +4,6 @@ using System; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; -using osu.Framework.Graphics.Effects; using osuTK; namespace osu.Game.Graphics.Containers @@ -17,21 +16,9 @@ namespace osu.Game.Graphics.Containers public Drawable Icon { get => InternalChild; - set => InternalChild = value; } - /// - /// Determines an edge effect of this . - /// Edge effects are e.g. glow or a shadow. - /// Only has an effect when is true. - /// - public new EdgeEffectParameters EdgeEffect - { - get => base.EdgeEffect; - set => base.EdgeEffect = value; - } - protected override void Update() { base.Update(); @@ -49,10 +36,5 @@ namespace osu.Game.Graphics.Containers InternalChild.Origin = Anchor.Centre; } } - - public ConstrainedIconContainer() - { - Masking = true; - } } } diff --git a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs index c224f0f9c9..3287ac6eaa 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarRulesetTabButton.cs @@ -4,7 +4,6 @@ using osu.Framework.Allocation; using osu.Framework.Extensions.Color4Extensions; using osu.Framework.Graphics; -using osu.Framework.Graphics.Effects; using osu.Framework.Graphics.UserInterface; using osu.Framework.Input.Events; using osu.Game.Graphics; @@ -58,15 +57,7 @@ namespace osu.Game.Overlays.Toolbar { set => Scheduler.AddOnce(() => { - if (value) - { - IconContainer.Colour = Color4Extensions.FromHex("#00FFAA"); - } - else - { - IconContainer.Colour = colours.GrayF; - IconContainer.EdgeEffect = new EdgeEffectParameters(); - } + IconContainer.Colour = value ? Color4Extensions.FromHex("#00FFAA") : colours.GrayF; }); } From 2afa4c7e1c761ce998199e39e86cd5d1e9228f49 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 14:10:16 +0900 Subject: [PATCH 1076/1118] Remove redundant `RequiresChildrenUpdate` usage We are already manually calling `base.UpdateSubTree` when we need to. Changing this flag is doing nothing and just adds to the complexity of the implementation. --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index 2af9916a6b..b6bf5ff53a 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -34,8 +34,6 @@ namespace osu.Game.Rulesets.UI /// internal bool FrameStablePlayback { get; set; } = true; - protected override bool RequiresChildrenUpdate => base.RequiresChildrenUpdate && state != PlaybackState.NotValid; - private readonly Bindable isCatchingUp = new Bindable(); private readonly Bindable waitingOnFrames = new Bindable(); From e73910571f35e69e409cec18cc597d232829bfa5 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 14:15:22 +0900 Subject: [PATCH 1077/1118] Allow `FrameStabilityContainer` to continue updating while paused during replay playback --- osu.Game/Rulesets/UI/FrameStabilityContainer.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs index b6bf5ff53a..ee19baaf65 100644 --- a/osu.Game/Rulesets/UI/FrameStabilityContainer.cs +++ b/osu.Game/Rulesets/UI/FrameStabilityContainer.cs @@ -122,7 +122,7 @@ namespace osu.Game.Rulesets.UI // if waiting on frames, run one update loop to determine if frames have arrived. state = PlaybackState.Valid; } - else if (IsPaused.Value) + else if (IsPaused.Value && !hasReplayAttached) { // time should not advance while paused, nor should anything run. state = PlaybackState.NotValid; From dafff26d0ecf6de7f27bb5a329455729ee3ce084 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 14:44:31 +0900 Subject: [PATCH 1078/1118] Tidy up implementation of `PlaybackSettings` --- .../Play/PlayerSettings/PlaybackSettings.cs | 76 +++++++++---------- 1 file changed, 38 insertions(+), 38 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 44cfa8d811..34f086da33 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -11,9 +11,9 @@ using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; +using osu.Game.Localisation; using osu.Game.Screens.Edit.Timing; using osuTK; -using osu.Game.Localisation; namespace osu.Game.Screens.Play.PlayerSettings { @@ -28,26 +28,31 @@ namespace osu.Game.Screens.Play.PlayerSettings Precision = 0.01, }; - private readonly PlayerSliderBar rateSlider; + private PlayerSliderBar rateSlider = null!; - private readonly OsuSpriteText multiplierText; + private OsuSpriteText multiplierText = null!; - private readonly BindableBool isPaused = new BindableBool(); + private readonly IBindable isPaused = new BindableBool(); [Resolved] - private GameplayClockContainer? gameplayClock { get; set; } + private GameplayClockContainer gameplayClock { get; set; } = null!; [Resolved] - private GameplayState? gameplayState { get; set; } + private GameplayState gameplayState { get; set; } = null!; + + private IconButton pausePlay = null!; public PlaybackSettings() : base("playback") + { + } + + [BackgroundDependencyLoader] + private void load() { const double seek_amount = 5000; const double seek_fast_amount = 10000; - IconButton play; - Children = new Drawable[] { new FillFlowContainer @@ -82,7 +87,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Action = () => seek(-1, seek_amount), TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_amount / 1000), }, - play = new IconButton + pausePlay = new IconButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -91,13 +96,10 @@ namespace osu.Game.Screens.Play.PlayerSettings Icon = FontAwesome.Regular.PlayCircle, Action = () => { - if (gameplayClock != null) - { - if (gameplayClock.IsRunning) - gameplayClock.Stop(); - else - gameplayClock.Start(); - } + if (gameplayClock.IsRunning) + gameplayClock.Stop(); + else + gameplayClock.Start(); }, }, new SeekButton @@ -141,26 +143,6 @@ namespace osu.Game.Screens.Play.PlayerSettings }, }, }; - - isPaused.BindValueChanged(paused => - { - if (!paused.NewValue) - { - play.TooltipText = ToastStrings.PauseTrack; - play.Icon = FontAwesome.Regular.PauseCircle; - } - else - { - play.TooltipText = ToastStrings.PlayTrack; - play.Icon = FontAwesome.Regular.PlayCircle; - } - }, true); - - void seek(int direction, double amount) - { - double target = Math.Clamp((gameplayClock?.CurrentTime ?? 0) + (direction * amount), 0, gameplayState?.Beatmap.GetLastObjectTime() ?? 0); - gameplayClock?.Seek(target); - } } protected override void LoadComplete() @@ -168,8 +150,26 @@ namespace osu.Game.Screens.Play.PlayerSettings base.LoadComplete(); rateSlider.Current.BindValueChanged(multiplier => multiplierText.Text = $"{multiplier.NewValue:0.00}x", true); - if (gameplayClock != null) - isPaused.BindTarget = gameplayClock.IsPaused; + isPaused.BindTo(gameplayClock.IsPaused); + isPaused.BindValueChanged(paused => + { + if (!paused.NewValue) + { + pausePlay.TooltipText = ToastStrings.PauseTrack; + pausePlay.Icon = FontAwesome.Regular.PauseCircle; + } + else + { + pausePlay.TooltipText = ToastStrings.PlayTrack; + pausePlay.Icon = FontAwesome.Regular.PlayCircle; + } + }, true); + } + + private void seek(int direction, double amount) + { + double target = Math.Clamp(gameplayClock.CurrentTime + (direction * amount), 0, gameplayState.Beatmap.GetLastObjectTime()); + gameplayClock.Seek(target); } private partial class SeekButton : IconButton From 60e972cd159dcfe653ed19cc2d96ed179701205d Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 14:59:35 +0900 Subject: [PATCH 1079/1118] Add ability to step forward/backwards single frames --- .../PlayerSettingsOverlayStrings.cs | 10 ++++++ .../Play/PlayerSettings/PlaybackSettings.cs | 33 ++++++++++++++++++- 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs b/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs index 1aedd9fc5b..60874da561 100644 --- a/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs +++ b/osu.Game/Localisation/PlayerSettingsOverlayStrings.cs @@ -9,6 +9,16 @@ namespace osu.Game.Localisation { private const string prefix = @"osu.Game.Resources.Localisation.PlaybackSettings"; + /// + /// "Step backward one frame" + /// + public static LocalisableString StepBackward => new TranslatableString(getKey(@"step_backward_frame"), @"Step backward one frame"); + + /// + /// "Step forward one frame" + /// + public static LocalisableString StepForward => new TranslatableString(getKey(@"step_forward_frame"), @"Step forward one frame"); + /// /// "Seek backward {0} seconds" /// diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index 34f086da33..b946805be8 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -2,6 +2,7 @@ // See the LICENCE file in the repository root for full licence text. using System; +using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -87,13 +88,20 @@ namespace osu.Game.Screens.Play.PlayerSettings Action = () => seek(-1, seek_amount), TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_amount / 1000), }, + new SeekButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.StepBackward, + Action = () => seekFrame(-1), + TooltipText = PlayerSettingsOverlayStrings.StepBackward, + }, pausePlay = new IconButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, Scale = new Vector2(1.4f), IconScale = new Vector2(1.4f), - Icon = FontAwesome.Regular.PlayCircle, Action = () => { if (gameplayClock.IsRunning) @@ -103,6 +111,14 @@ namespace osu.Game.Screens.Play.PlayerSettings }, }, new SeekButton + { + Anchor = Anchor.Centre, + Origin = Anchor.Centre, + Icon = FontAwesome.Solid.StepForward, + Action = () => seekFrame(1), + TooltipText = PlayerSettingsOverlayStrings.StepForward, + }, + new SeekButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, @@ -166,6 +182,21 @@ namespace osu.Game.Screens.Play.PlayerSettings }, true); } + private void seekFrame(int direction) + { + gameplayClock.Stop(); + + var frames = gameplayState.Score.Replay.Frames; + + if (frames.Count == 0) + return; + + gameplayClock.Seek(direction < 0 + ? (frames.LastOrDefault(f => f.Time < gameplayClock.CurrentTime) ?? frames.First()).Time + : (frames.FirstOrDefault(f => f.Time > gameplayClock.CurrentTime) ?? frames.Last()).Time + ); + } + private void seek(int direction, double amount) { double target = Math.Clamp(gameplayClock.CurrentTime + (direction * amount), 0, gameplayState.Beatmap.GetLastObjectTime()); From 8c4af58109b202e4505f62634c8e9001397c5d3f Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 15:08:24 +0900 Subject: [PATCH 1080/1118] Keep replay controls expanded by default --- osu.Game/Configuration/OsuConfigManager.cs | 2 ++ osu.Game/Screens/Play/ReplayPlayer.cs | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/osu.Game/Configuration/OsuConfigManager.cs b/osu.Game/Configuration/OsuConfigManager.cs index c05831d043..6b2cb4ee74 100644 --- a/osu.Game/Configuration/OsuConfigManager.cs +++ b/osu.Game/Configuration/OsuConfigManager.cs @@ -142,6 +142,7 @@ namespace osu.Game.Configuration SetDefault(OsuSetting.FadePlayfieldWhenHealthLow, true); SetDefault(OsuSetting.KeyOverlay, false); SetDefault(OsuSetting.ReplaySettingsOverlay, true); + SetDefault(OsuSetting.ReplayPlaybackControlsExpanded, true); SetDefault(OsuSetting.GameplayLeaderboard, true); SetDefault(OsuSetting.AlwaysPlayFirstComboBreak, true); @@ -421,6 +422,7 @@ namespace osu.Game.Configuration ProfileCoverExpanded, EditorLimitedDistanceSnap, ReplaySettingsOverlay, + ReplayPlaybackControlsExpanded, AutomaticallyDownloadMissingBeatmaps, EditorShowSpeedChanges, TouchDisableGameplayTaps, diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index 805f907466..d4d6e7ecd0 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -12,6 +12,7 @@ using osu.Framework.Bindables; using osu.Framework.Input.Bindings; using osu.Framework.Input.Events; using osu.Game.Beatmaps; +using osu.Game.Configuration; using osu.Game.Input.Bindings; using osu.Game.Rulesets.Mods; using osu.Game.Scoring; @@ -52,7 +53,7 @@ namespace osu.Game.Screens.Play } [BackgroundDependencyLoader] - private void load() + private void load(OsuConfigManager config) { if (!LoadedBeatmapSuccessfully) return; @@ -60,7 +61,7 @@ namespace osu.Game.Screens.Play var playbackSettings = new PlaybackSettings { Depth = float.MaxValue, - Expanded = { Value = false } + Expanded = { BindTarget = config.GetBindable(OsuSetting.ReplayPlaybackControlsExpanded) } }; if (GameplayClockContainer is MasterGameplayClockContainer master) From 22cfc8605094aebb6809333582543d6443756899 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 18 Jan 2024 14:24:48 +0300 Subject: [PATCH 1081/1118] Add failing test case --- osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs index 2926b11067..ee0c64aa3f 100644 --- a/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs +++ b/osu.Game.Tests/Visual/Settings/TestSceneSettingsItem.cs @@ -40,8 +40,15 @@ namespace osu.Game.Tests.Visual.Settings AddStep("change value from default", () => textBox.Current.Value = "non-default"); AddUntilStep("restore button shown", () => revertToDefaultButton.Alpha > 0); + AddStep("disable setting", () => textBox.Current.Disabled = true); + AddUntilStep("restore button still shown", () => revertToDefaultButton.Alpha > 0); + + AddStep("enable setting", () => textBox.Current.Disabled = false); AddStep("restore default", () => textBox.Current.SetDefault()); AddUntilStep("restore button hidden", () => revertToDefaultButton.Alpha == 0); + + AddStep("disable setting", () => textBox.Current.Disabled = true); + AddUntilStep("restore button still hidden", () => revertToDefaultButton.Alpha == 0); } [Test] From b97c3ac9ee1c3a0e34afb296dc0e99543d002011 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Thu, 18 Jan 2024 14:20:07 +0300 Subject: [PATCH 1082/1118] Fix revert-to-default button appearing on disabled settings regardless of value --- osu.Game/Overlays/RevertToDefaultButton.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/osu.Game/Overlays/RevertToDefaultButton.cs b/osu.Game/Overlays/RevertToDefaultButton.cs index 582138b0b4..6fa5209f64 100644 --- a/osu.Game/Overlays/RevertToDefaultButton.cs +++ b/osu.Game/Overlays/RevertToDefaultButton.cs @@ -115,7 +115,12 @@ namespace osu.Game.Overlays Enabled.Value = !current.Disabled; - this.FadeTo(current.Disabled ? 0.2f : (current.IsDefault ? 0 : 1), fade_duration, Easing.OutQuint); + if (current.IsDefault) + this.FadeTo(0, fade_duration, Easing.OutQuint); + else if (current.Disabled) + this.FadeTo(0.2f, fade_duration, Easing.OutQuint); + else + this.FadeTo(1, fade_duration, Easing.OutQuint); if (IsHovered && Enabled.Value) { From c50534c8191e3fe408fe9e04dcbaf41c2b9c4577 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 20:38:15 +0900 Subject: [PATCH 1083/1118] Move seek/step logic into `ReplayPlayer` --- .../Play/PlayerSettings/PlaybackSettings.cs | 51 +++++-------------- osu.Game/Screens/Play/ReplayPlayer.cs | 42 +++++++++++---- 2 files changed, 45 insertions(+), 48 deletions(-) diff --git a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs index b946805be8..b3d07421ed 100644 --- a/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs +++ b/osu.Game/Screens/Play/PlayerSettings/PlaybackSettings.cs @@ -1,14 +1,11 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; -using System.Linq; using osu.Framework.Allocation; using osu.Framework.Bindables; using osu.Framework.Graphics; using osu.Framework.Graphics.Containers; using osu.Framework.Graphics.Sprites; -using osu.Game.Beatmaps; using osu.Game.Graphics; using osu.Game.Graphics.Sprites; using osu.Game.Graphics.UserInterface; @@ -36,10 +33,10 @@ namespace osu.Game.Screens.Play.PlayerSettings private readonly IBindable isPaused = new BindableBool(); [Resolved] - private GameplayClockContainer gameplayClock { get; set; } = null!; + private ReplayPlayer replayPlayer { get; set; } = null!; [Resolved] - private GameplayState gameplayState { get; set; } = null!; + private GameplayClockContainer gameplayClock { get; set; } = null!; private IconButton pausePlay = null!; @@ -51,9 +48,6 @@ namespace osu.Game.Screens.Play.PlayerSettings [BackgroundDependencyLoader] private void load() { - const double seek_amount = 5000; - const double seek_fast_amount = 10000; - Children = new Drawable[] { new FillFlowContainer @@ -77,23 +71,23 @@ namespace osu.Game.Screens.Play.PlayerSettings Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.FastBackward, - Action = () => seek(-1, seek_fast_amount), - TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_fast_amount / 1000), + Action = () => replayPlayer.SeekInDirection(-10), + TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(10 * ReplayPlayer.BASE_SEEK_AMOUNT / 1000), }, new SeekButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.Backward, - Action = () => seek(-1, seek_amount), - TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(seek_amount / 1000), + Action = () => replayPlayer.SeekInDirection(-1), + TooltipText = PlayerSettingsOverlayStrings.SeekBackwardSeconds(ReplayPlayer.BASE_SEEK_AMOUNT / 1000), }, new SeekButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.StepBackward, - Action = () => seekFrame(-1), + Action = () => replayPlayer.StepFrame(-1), TooltipText = PlayerSettingsOverlayStrings.StepBackward, }, pausePlay = new IconButton @@ -115,7 +109,7 @@ namespace osu.Game.Screens.Play.PlayerSettings Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.StepForward, - Action = () => seekFrame(1), + Action = () => replayPlayer.StepFrame(1), TooltipText = PlayerSettingsOverlayStrings.StepForward, }, new SeekButton @@ -123,16 +117,16 @@ namespace osu.Game.Screens.Play.PlayerSettings Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.Forward, - Action = () => seek(1, seek_amount), - TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(seek_amount / 1000), + Action = () => replayPlayer.SeekInDirection(1), + TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(ReplayPlayer.BASE_SEEK_AMOUNT / 1000), }, new SeekButton { Anchor = Anchor.Centre, Origin = Anchor.Centre, Icon = FontAwesome.Solid.FastForward, - Action = () => seek(1, seek_fast_amount), - TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(seek_fast_amount / 1000), + Action = () => replayPlayer.SeekInDirection(10), + TooltipText = PlayerSettingsOverlayStrings.SeekForwardSeconds(10 * ReplayPlayer.BASE_SEEK_AMOUNT / 1000), }, }, }, @@ -182,27 +176,6 @@ namespace osu.Game.Screens.Play.PlayerSettings }, true); } - private void seekFrame(int direction) - { - gameplayClock.Stop(); - - var frames = gameplayState.Score.Replay.Frames; - - if (frames.Count == 0) - return; - - gameplayClock.Seek(direction < 0 - ? (frames.LastOrDefault(f => f.Time < gameplayClock.CurrentTime) ?? frames.First()).Time - : (frames.FirstOrDefault(f => f.Time > gameplayClock.CurrentTime) ?? frames.Last()).Time - ); - } - - private void seek(int direction, double amount) - { - double target = Math.Clamp(gameplayClock.CurrentTime + (direction * amount), 0, gameplayState.Beatmap.GetLastObjectTime()); - gameplayClock.Seek(target); - } - private partial class SeekButton : IconButton { public SeekButton() diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index d4d6e7ecd0..a26a2b9904 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -23,8 +23,11 @@ using osu.Game.Users; namespace osu.Game.Screens.Play { + [Cached] public partial class ReplayPlayer : Player, IKeyBindingHandler { + public const double BASE_SEEK_AMOUNT = 1000; + private readonly Func, Score> createScore; private readonly bool replayIsFailedScore; @@ -93,16 +96,22 @@ namespace osu.Game.Screens.Play public bool OnPressed(KeyBindingPressEvent e) { - const double keyboard_seek_amount = 5000; - switch (e.Action) { + case GlobalAction.StepReplayBackward: + StepFrame(-1); + return true; + + case GlobalAction.StepReplayForward: + StepFrame(1); + return true; + case GlobalAction.SeekReplayBackward: - keyboardSeek(-1); + SeekInDirection(-1); return true; case GlobalAction.SeekReplayForward: - keyboardSeek(1); + SeekInDirection(1); return true; case GlobalAction.TogglePauseReplay: @@ -114,13 +123,28 @@ namespace osu.Game.Screens.Play } return false; + } - void keyboardSeek(int direction) - { - double target = Math.Clamp(GameplayClockContainer.CurrentTime + direction * keyboard_seek_amount, 0, GameplayState.Beatmap.GetLastObjectTime()); + public void StepFrame(int direction) + { + GameplayClockContainer.Stop(); - Seek(target); - } + var frames = GameplayState.Score.Replay.Frames; + + if (frames.Count == 0) + return; + + GameplayClockContainer.Seek(direction < 0 + ? (frames.LastOrDefault(f => f.Time < GameplayClockContainer.CurrentTime) ?? frames.First()).Time + : (frames.FirstOrDefault(f => f.Time > GameplayClockContainer.CurrentTime) ?? frames.Last()).Time + ); + } + + public void SeekInDirection(float amount) + { + double target = Math.Clamp(GameplayClockContainer.CurrentTime + amount * BASE_SEEK_AMOUNT, 0, GameplayState.Beatmap.GetLastObjectTime()); + + Seek(target); } public void OnReleased(KeyBindingReleaseEvent e) From 0383bdf6a15d57a0a499b6b88ac53d4eb57b4f2b Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Thu, 18 Jan 2024 20:38:25 +0900 Subject: [PATCH 1084/1118] Add bindings for stepping backward/forward --- osu.Game/Input/Bindings/GlobalActionContainer.cs | 10 +++++++++- osu.Game/Localisation/GlobalActionKeyBindingStrings.cs | 10 ++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/osu.Game/Input/Bindings/GlobalActionContainer.cs b/osu.Game/Input/Bindings/GlobalActionContainer.cs index 5a39c02185..436334cfe1 100644 --- a/osu.Game/Input/Bindings/GlobalActionContainer.cs +++ b/osu.Game/Input/Bindings/GlobalActionContainer.cs @@ -170,6 +170,8 @@ namespace osu.Game.Input.Bindings new KeyBinding(InputKey.MouseMiddle, GlobalAction.TogglePauseReplay), new KeyBinding(InputKey.Left, GlobalAction.SeekReplayBackward), new KeyBinding(InputKey.Right, GlobalAction.SeekReplayForward), + new KeyBinding(InputKey.Comma, GlobalAction.StepReplayBackward), + new KeyBinding(InputKey.Period, GlobalAction.StepReplayForward), new KeyBinding(new[] { InputKey.Control, InputKey.H }, GlobalAction.ToggleReplaySettings), }; @@ -411,7 +413,13 @@ namespace osu.Game.Input.Bindings IncreaseOffset, [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.DecreaseOffset))] - DecreaseOffset + DecreaseOffset, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.StepReplayForward))] + StepReplayForward, + + [LocalisableDescription(typeof(GlobalActionKeyBindingStrings), nameof(GlobalActionKeyBindingStrings.StepReplayBackward))] + StepReplayBackward, } public enum GlobalActionCategory diff --git a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs index ca27d0ff95..703e0ff1ca 100644 --- a/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs +++ b/osu.Game/Localisation/GlobalActionKeyBindingStrings.cs @@ -324,6 +324,16 @@ namespace osu.Game.Localisation /// public static LocalisableString SeekReplayBackward => new TranslatableString(getKey(@"seek_replay_backward"), @"Seek replay backward"); + /// + /// "Seek replay forward one frame" + /// + public static LocalisableString StepReplayForward => new TranslatableString(getKey(@"step_replay_forward"), @"Seek replay forward one frame"); + + /// + /// "Step replay backward one frame" + /// + public static LocalisableString StepReplayBackward => new TranslatableString(getKey(@"step_replay_backward"), @"Step replay backward one frame"); + /// /// "Toggle chat focus" /// From 3eeefd5b7e8708894c26474cf3868fc54e662f61 Mon Sep 17 00:00:00 2001 From: Joseph Madamba Date: Thu, 18 Jan 2024 16:13:48 -0800 Subject: [PATCH 1085/1118] Fix changelog stream user count only accounting for latest build --- osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs | 3 +++ osu.Game/Overlays/Changelog/ChangelogUpdateStreamItem.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs index 76d1941d9d..dac72f2488 100644 --- a/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs +++ b/osu.Game/Online/API/Requests/Responses/APIUpdateStream.cs @@ -28,6 +28,9 @@ namespace osu.Game.Online.API.Requests.Responses [JsonProperty("latest_build")] public APIChangelogBuild LatestBuild { get; set; } + [JsonProperty("user_count")] + public int UserCount { get; set; } + public bool Equals(APIUpdateStream other) => Id == other?.Id; internal static readonly Dictionary KNOWN_STREAMS = new Dictionary diff --git a/osu.Game/Overlays/Changelog/ChangelogUpdateStreamItem.cs b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamItem.cs index 08ea373fb1..30273d2405 100644 --- a/osu.Game/Overlays/Changelog/ChangelogUpdateStreamItem.cs +++ b/osu.Game/Overlays/Changelog/ChangelogUpdateStreamItem.cs @@ -24,7 +24,7 @@ namespace osu.Game.Overlays.Changelog protected override LocalisableString AdditionalText => Value.LatestBuild.DisplayVersion; - protected override LocalisableString InfoText => Value.LatestBuild.Users > 0 ? $"{"user".ToQuantity(Value.LatestBuild.Users, "N0")} online" : null; + protected override LocalisableString InfoText => Value.UserCount > 0 ? $"{"user".ToQuantity(Value.UserCount, "N0")} online" : null; protected override Color4 GetBarColour(OsuColour colours) => Value.Colour; } From 45effdb6dfaddf3e6fc5ed0aa882629e8ff99470 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sat, 20 Jan 2024 08:41:34 +0300 Subject: [PATCH 1086/1118] Remove local vertex batching from triangles backgrounds --- osu.Game/Graphics/Backgrounds/Triangles.cs | 11 +---------- osu.Game/Graphics/Backgrounds/TrianglesV2.cs | 12 +----------- 2 files changed, 2 insertions(+), 21 deletions(-) diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 1a78c1ec5e..8db7f3a1c3 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -15,7 +15,6 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Allocation; using System.Collections.Generic; using osu.Framework.Graphics.Rendering; -using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Lists; using osu.Framework.Bindables; @@ -264,7 +263,6 @@ namespace osu.Game.Graphics.Backgrounds private readonly Vector2 triangleSize = new Vector2(1f, equilateral_triangle_ratio) * triangle_size; private Vector2 size; - private IVertexBatch vertexBatch; public TrianglesDrawNode(Triangles source) : base(source) @@ -290,12 +288,6 @@ namespace osu.Game.Graphics.Backgrounds { base.Draw(renderer); - if (Source.AimCount > 0 && (vertexBatch == null || vertexBatch.Size != Source.AimCount)) - { - vertexBatch?.Dispose(); - vertexBatch = renderer.CreateQuadBatch(Source.AimCount, 1); - } - borderDataBuffer ??= renderer.CreateUniformBuffer(); borderDataBuffer.Data = borderDataBuffer.Data with { @@ -333,7 +325,7 @@ namespace osu.Game.Graphics.Backgrounds triangleQuad.Height ) / relativeSize; - renderer.DrawQuad(texture, drawQuad, colourInfo, new RectangleF(0, 0, 1, 1), vertexBatch.AddAction, textureCoords: textureCoords); + renderer.DrawQuad(texture, drawQuad, colourInfo, new RectangleF(0, 0, 1, 1), textureCoords: textureCoords); } shader.Unbind(); @@ -356,7 +348,6 @@ namespace osu.Game.Graphics.Backgrounds { base.Dispose(isDisposing); - vertexBatch?.Dispose(); borderDataBuffer?.Dispose(); } } diff --git a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs index a20fd78569..4f611d9def 100644 --- a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs +++ b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs @@ -10,7 +10,6 @@ using osu.Framework.Graphics.Primitives; using osu.Framework.Allocation; using System.Collections.Generic; using osu.Framework.Graphics.Rendering; -using osu.Framework.Graphics.Rendering.Vertices; using osu.Framework.Bindables; using osu.Framework.Graphics; @@ -196,8 +195,6 @@ namespace osu.Game.Graphics.Backgrounds private float texelSize; private bool masking; - private IVertexBatch? vertexBatch; - public TrianglesDrawNode(TrianglesV2 source) : base(source) { @@ -235,12 +232,6 @@ namespace osu.Game.Graphics.Backgrounds if (Source.AimCount == 0 || thickness == 0) return; - if (vertexBatch == null || vertexBatch.Size != Source.AimCount) - { - vertexBatch?.Dispose(); - vertexBatch = renderer.CreateQuadBatch(Source.AimCount, 1); - } - borderDataBuffer ??= renderer.CreateUniformBuffer(); borderDataBuffer.Data = borderDataBuffer.Data with { @@ -273,7 +264,7 @@ namespace osu.Game.Graphics.Backgrounds triangleQuad.Height ) / relativeSize; - renderer.DrawQuad(texture, drawQuad, DrawColourInfo.Colour.Interpolate(triangleQuad), new RectangleF(0, 0, 1, 1), vertexBatch.AddAction, textureCoords: textureCoords); + renderer.DrawQuad(texture, drawQuad, DrawColourInfo.Colour.Interpolate(triangleQuad), new RectangleF(0, 0, 1, 1), textureCoords: textureCoords); } shader.Unbind(); @@ -296,7 +287,6 @@ namespace osu.Game.Graphics.Backgrounds { base.Dispose(isDisposing); - vertexBatch?.Dispose(); borderDataBuffer?.Dispose(); } } From 3ad3b052dffe4a1434868ad3cd2fe37c67784d24 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Jan 2024 02:10:36 +0300 Subject: [PATCH 1087/1118] Enable masking is osu logo --- osu.Game/Screens/Menu/OsuLogo.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 75ef8be02e..243612eee1 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -192,6 +192,7 @@ namespace osu.Game.Screens.Menu ColourLight = Color4Extensions.FromHex(@"ff7db7"), ColourDark = Color4Extensions.FromHex(@"de5b95"), RelativeSizeAxes = Axes.Both, + Masking = true }, } }, From a3703d657ab5ba2a93dbd89cad0adb6a21dfbd17 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Jan 2024 02:12:49 +0300 Subject: [PATCH 1088/1118] Enable masking in popup dialog --- osu.Game/Overlays/Dialog/PopupDialog.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 4ac37a63e2..1c1196a1c5 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -150,6 +150,7 @@ namespace osu.Game.Overlays.Dialog ColourLight = Color4Extensions.FromHex(@"271e26"), ColourDark = Color4Extensions.FromHex(@"1e171e"), TriangleScale = 4, + Masking = true }, flashLayer = new Box { From 087d0f03a4595d5eac66bd2f8ffb2ca6ea85264a Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Jan 2024 02:15:48 +0300 Subject: [PATCH 1089/1118] Enable masking in toolbar --- osu.Game/Overlays/Toolbar/ToolbarButton.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index 1da2e1b744..bdd16b347d 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -255,6 +255,7 @@ namespace osu.Game.Overlays.Toolbar RelativeSizeAxes = Axes.Both, ColourLight = OsuColour.Gray(40), ColourDark = OsuColour.Gray(20), + Masking = true }, }; } From 421ae73715ad9f1d3f5dcc331c8ac81304830187 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Jan 2024 02:21:09 +0300 Subject: [PATCH 1090/1118] Enable masking in DrawableCarouselBeatmap --- osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index baf0a14062..ecf65fe55c 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -109,7 +109,8 @@ namespace osu.Game.Screens.Select.Carousel TriangleScale = 2, RelativeSizeAxes = Axes.Both, ColourLight = Color4Extensions.FromHex(@"3a7285"), - ColourDark = Color4Extensions.FromHex(@"123744") + ColourDark = Color4Extensions.FromHex(@"123744"), + Masking = true }, new FillFlowContainer { From 60f7b4ea2f582b8fa2f1f15dd70c0febe0ea711d Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Jan 2024 02:25:29 +0300 Subject: [PATCH 1091/1118] Enable masking in DrawableRank --- osu.Game/Online/Leaderboards/DrawableRank.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Online/Leaderboards/DrawableRank.cs b/osu.Game/Online/Leaderboards/DrawableRank.cs index 5177f35478..bbf981af09 100644 --- a/osu.Game/Online/Leaderboards/DrawableRank.cs +++ b/osu.Game/Online/Leaderboards/DrawableRank.cs @@ -50,6 +50,7 @@ namespace osu.Game.Online.Leaderboards ColourDark = rankColour.Darken(0.1f), ColourLight = rankColour.Lighten(0.1f), Velocity = 0.25f, + Masking = true }, new OsuSpriteText { From 6ba3546be5c2ef8f514e6652f2965bfb70d1b59c Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Jan 2024 02:28:14 +0300 Subject: [PATCH 1092/1118] Enable masking in RoundedButton --- osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index 6aded3fe32..65bd5f49c2 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -68,6 +68,7 @@ namespace osu.Game.Graphics.UserInterfaceV2 SpawnRatio = 0.6f, RelativeSizeAxes = Axes.Both, Depth = float.MaxValue, + Masking = true }); updateColours(); From 1999e772f6df41e35dafb2e185f6ff49a31089bf Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 21 Jan 2024 10:31:27 +0900 Subject: [PATCH 1093/1118] Fix `FollowPointConnection` pool filling up when follow points are hidden Closes https://github.com/ppy/osu/issues/26642. I think this applied to all pooling cases here. --- .../Objects/Pooling/PooledDrawableWithLifetimeContainer.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs b/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs index 3b45acc7bb..1b0176cae5 100644 --- a/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs +++ b/osu.Game/Rulesets/Objects/Pooling/PooledDrawableWithLifetimeContainer.cs @@ -153,6 +153,9 @@ namespace osu.Game.Rulesets.Objects.Pooling protected override bool CheckChildrenLife() { + if (!IsPresent) + return false; + bool aliveChanged = base.CheckChildrenLife(); aliveChanged |= lifetimeManager.Update(Time.Current - PastLifetimeExtension, Time.Current + FutureLifetimeExtension); return aliveChanged; From 2dedead1d12c39fdb2f26385664b9f37105877fa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 21 Jan 2024 10:55:52 +0900 Subject: [PATCH 1094/1118] Allow debug instances to coexist alongside release instances --- osu.Game/OsuGame.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/osu.Game/OsuGame.cs b/osu.Game/OsuGame.cs index 7138bf2175..c244708385 100644 --- a/osu.Game/OsuGame.cs +++ b/osu.Game/OsuGame.cs @@ -80,7 +80,12 @@ namespace osu.Game [Cached(typeof(OsuGame))] public partial class OsuGame : OsuGameBase, IKeyBindingHandler, ILocalUserPlayInfo, IPerformFromScreenRunner, IOverlayManager, ILinkHandler { +#if DEBUG + // Different port allows runnning release and debug builds alongside each other. + public const int IPC_PORT = 44824; +#else public const int IPC_PORT = 44823; +#endif /// /// The amount of global offset to apply when a left/right anchored overlay is displayed (ie. settings or notifications). From e003ecb5937f5d1239b64250e8968908bf41c165 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Jan 2024 04:57:48 +0300 Subject: [PATCH 1095/1118] Change default masking value to true --- osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs | 1 + osu.Game/Graphics/Backgrounds/Triangles.cs | 4 ++-- osu.Game/Graphics/Backgrounds/TrianglesV2.cs | 4 ++-- osu.Game/Graphics/UserInterface/DialogButton.cs | 1 + osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs | 1 - osu.Game/Online/Leaderboards/DrawableRank.cs | 1 - osu.Game/Overlays/Dialog/PopupDialog.cs | 1 - osu.Game/Overlays/Mods/ModSelectColumn.cs | 1 + osu.Game/Overlays/Toolbar/ToolbarButton.cs | 1 - osu.Game/Screens/Menu/OsuLogo.cs | 1 - osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs | 1 - 11 files changed, 7 insertions(+), 10 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs index f1143cf14d..16cd302b88 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs @@ -15,6 +15,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default { TriangleScale = 1.2f; HideAlphaDiscrepancies = false; + Masking = false; } protected override void Update() diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index 8db7f3a1c3..cf74d6ef62 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -78,9 +78,9 @@ namespace osu.Game.Graphics.Backgrounds /// /// If enabled, only the portion of triangles that falls within this 's - /// shape is drawn to the screen. + /// shape is drawn to the screen. Default is true. /// - public bool Masking { get; set; } + public bool Masking { get; set; } = true; /// /// Whether we should drop-off alpha values of triangles more quickly to improve diff --git a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs index 4f611d9def..40b076ebc8 100644 --- a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs +++ b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs @@ -34,9 +34,9 @@ namespace osu.Game.Graphics.Backgrounds /// /// If enabled, only the portion of triangles that falls within this 's - /// shape is drawn to the screen. + /// shape is drawn to the screen. Default is true. /// - public bool Masking { get; set; } + public bool Masking { get; set; } = true; private readonly BindableFloat spawnRatio = new BindableFloat(1f); diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index c920597a95..59acbecbbf 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -150,6 +150,7 @@ namespace osu.Game.Graphics.UserInterface TriangleScale = 4, ColourDark = OsuColour.Gray(0.88f), Shear = new Vector2(-0.2f, 0), + Masking = false }, }, }, diff --git a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs index 65bd5f49c2..6aded3fe32 100644 --- a/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs +++ b/osu.Game/Graphics/UserInterfaceV2/RoundedButton.cs @@ -68,7 +68,6 @@ namespace osu.Game.Graphics.UserInterfaceV2 SpawnRatio = 0.6f, RelativeSizeAxes = Axes.Both, Depth = float.MaxValue, - Masking = true }); updateColours(); diff --git a/osu.Game/Online/Leaderboards/DrawableRank.cs b/osu.Game/Online/Leaderboards/DrawableRank.cs index bbf981af09..5177f35478 100644 --- a/osu.Game/Online/Leaderboards/DrawableRank.cs +++ b/osu.Game/Online/Leaderboards/DrawableRank.cs @@ -50,7 +50,6 @@ namespace osu.Game.Online.Leaderboards ColourDark = rankColour.Darken(0.1f), ColourLight = rankColour.Lighten(0.1f), Velocity = 0.25f, - Masking = true }, new OsuSpriteText { diff --git a/osu.Game/Overlays/Dialog/PopupDialog.cs b/osu.Game/Overlays/Dialog/PopupDialog.cs index 1c1196a1c5..4ac37a63e2 100644 --- a/osu.Game/Overlays/Dialog/PopupDialog.cs +++ b/osu.Game/Overlays/Dialog/PopupDialog.cs @@ -150,7 +150,6 @@ namespace osu.Game.Overlays.Dialog ColourLight = Color4Extensions.FromHex(@"271e26"), ColourDark = Color4Extensions.FromHex(@"1e171e"), TriangleScale = 4, - Masking = true }, flashLayer = new Box { diff --git a/osu.Game/Overlays/Mods/ModSelectColumn.cs b/osu.Game/Overlays/Mods/ModSelectColumn.cs index 1c56763bd9..631bb72ced 100644 --- a/osu.Game/Overlays/Mods/ModSelectColumn.cs +++ b/osu.Game/Overlays/Mods/ModSelectColumn.cs @@ -95,6 +95,7 @@ namespace osu.Game.Overlays.Mods Height = header_height, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Velocity = 0.7f, + Masking = false }, headerText = new OsuTextFlowContainer(t => { diff --git a/osu.Game/Overlays/Toolbar/ToolbarButton.cs b/osu.Game/Overlays/Toolbar/ToolbarButton.cs index bdd16b347d..1da2e1b744 100644 --- a/osu.Game/Overlays/Toolbar/ToolbarButton.cs +++ b/osu.Game/Overlays/Toolbar/ToolbarButton.cs @@ -255,7 +255,6 @@ namespace osu.Game.Overlays.Toolbar RelativeSizeAxes = Axes.Both, ColourLight = OsuColour.Gray(40), ColourDark = OsuColour.Gray(20), - Masking = true }, }; } diff --git a/osu.Game/Screens/Menu/OsuLogo.cs b/osu.Game/Screens/Menu/OsuLogo.cs index 243612eee1..75ef8be02e 100644 --- a/osu.Game/Screens/Menu/OsuLogo.cs +++ b/osu.Game/Screens/Menu/OsuLogo.cs @@ -192,7 +192,6 @@ namespace osu.Game.Screens.Menu ColourLight = Color4Extensions.FromHex(@"ff7db7"), ColourDark = Color4Extensions.FromHex(@"de5b95"), RelativeSizeAxes = Axes.Both, - Masking = true }, } }, diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index ecf65fe55c..4a28b3212e 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -110,7 +110,6 @@ namespace osu.Game.Screens.Select.Carousel RelativeSizeAxes = Axes.Both, ColourLight = Color4Extensions.FromHex(@"3a7285"), ColourDark = Color4Extensions.FromHex(@"123744"), - Masking = true }, new FillFlowContainer { From a69fd8198db3901fffa43a323ddb15a6df029713 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 21 Jan 2024 10:57:23 +0900 Subject: [PATCH 1096/1118] Update framework and other nuget packages - Moq held back because dicks - NUnit held back because large API changes (trivial but effort) - SignalR held back due to api deprecations --- ...u.Game.Rulesets.EmptyFreeform.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- ....Game.Rulesets.EmptyScrolling.Tests.csproj | 2 +- .../osu.Game.Rulesets.Pippidon.Tests.csproj | 2 +- osu.Desktop/osu.Desktop.csproj | 4 ++-- .../osu.Game.Benchmarks.csproj | 2 +- .../osu.Game.Rulesets.Catch.Tests.csproj | 2 +- .../osu.Game.Rulesets.Mania.Tests.csproj | 2 +- .../osu.Game.Rulesets.Osu.Tests.csproj | 2 +- .../osu.Game.Rulesets.Taiko.Tests.csproj | 2 +- osu.Game.Tests/osu.Game.Tests.csproj | 2 +- .../osu.Game.Tournament.Tests.csproj | 2 +- osu.Game/osu.Game.csproj | 20 +++++++++---------- 13 files changed, 23 insertions(+), 23 deletions(-) diff --git a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj index 2baa7ee0e0..5babdb47ff 100644 --- a/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj +++ b/Templates/Rulesets/ruleset-empty/osu.Game.Rulesets.EmptyFreeform.Tests/osu.Game.Rulesets.EmptyFreeform.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index a2308e6dfc..5d64ca832a 100644 --- a/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj index e839d2657c..6796a8962b 100644 --- a/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-empty/osu.Game.Rulesets.EmptyScrolling.Tests/osu.Game.Rulesets.EmptyScrolling.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj index a2308e6dfc..5d64ca832a 100644 --- a/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj +++ b/Templates/Rulesets/ruleset-scrolling-example/osu.Game.Rulesets.Pippidon.Tests/osu.Game.Rulesets.Pippidon.Tests.csproj @@ -10,7 +10,7 @@ - + diff --git a/osu.Desktop/osu.Desktop.csproj b/osu.Desktop/osu.Desktop.csproj index f37cfdc5f1..d6a11fa924 100644 --- a/osu.Desktop/osu.Desktop.csproj +++ b/osu.Desktop/osu.Desktop.csproj @@ -23,9 +23,9 @@ - + - + diff --git a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj index 5de21a68d0..47c93fbd02 100644 --- a/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj +++ b/osu.Game.Benchmarks/osu.Game.Benchmarks.csproj @@ -8,7 +8,7 @@ - + diff --git a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj index c45c85833c..0a77845343 100644 --- a/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj +++ b/osu.Game.Rulesets.Catch.Tests/osu.Game.Rulesets.Catch.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj index b991db408c..877b9c3ea4 100644 --- a/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj +++ b/osu.Game.Rulesets.Mania.Tests/osu.Game.Rulesets.Mania.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj index ea033cda45..9c248abd66 100644 --- a/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj +++ b/osu.Game.Rulesets.Osu.Tests/osu.Game.Rulesets.Osu.Tests.csproj @@ -3,7 +3,7 @@ - + diff --git a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj index 48465bb119..4eb6b0ab3d 100644 --- a/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj +++ b/osu.Game.Rulesets.Taiko.Tests/osu.Game.Rulesets.Taiko.Tests.csproj @@ -2,7 +2,7 @@ - + diff --git a/osu.Game.Tests/osu.Game.Tests.csproj b/osu.Game.Tests/osu.Game.Tests.csproj index ef6c16f2c4..7b08524240 100644 --- a/osu.Game.Tests/osu.Game.Tests.csproj +++ b/osu.Game.Tests/osu.Game.Tests.csproj @@ -4,7 +4,7 @@ - + diff --git a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj index 2cc07dd9ed..3b00f103c4 100644 --- a/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj +++ b/osu.Game.Tournament.Tests/osu.Game.Tournament.Tests.csproj @@ -5,7 +5,7 @@ - + diff --git a/osu.Game/osu.Game.csproj b/osu.Game/osu.Game.csproj index f33ddac66f..1b1abe3971 100644 --- a/osu.Game/osu.Game.csproj +++ b/osu.Game/osu.Game.csproj @@ -21,12 +21,12 @@ - + - - - - + + + + @@ -36,13 +36,13 @@ runtime; build; native; contentfiles; analyzers; buildtransitive - + - + - - - + + + From 1393f52b2bf537bacf2248db501068f5f2b165b8 Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Sun, 21 Jan 2024 05:20:42 +0300 Subject: [PATCH 1097/1118] Rename Masking to ClampToDrawable --- osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs | 2 +- .../Visual/Background/TestSceneTrianglesBackground.cs | 2 +- .../Visual/Background/TestSceneTrianglesV2Background.cs | 2 +- osu.Game/Graphics/Backgrounds/Triangles.cs | 8 ++++---- osu.Game/Graphics/Backgrounds/TrianglesV2.cs | 8 ++++---- osu.Game/Graphics/UserInterface/DialogButton.cs | 2 +- osu.Game/Overlays/Mods/ModSelectColumn.cs | 2 +- .../Screens/Select/Carousel/DrawableCarouselBeatmap.cs | 2 +- 8 files changed, 14 insertions(+), 14 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs index 16cd302b88..566176505d 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs @@ -15,7 +15,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default { TriangleScale = 1.2f; HideAlphaDiscrepancies = false; - Masking = false; + ClampToDrawable = false; } protected override void Update() diff --git a/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs b/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs index 378dd99664..4733b7f92f 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs @@ -40,7 +40,7 @@ namespace osu.Game.Tests.Visual.Background AddSliderStep("Triangle scale", 0f, 10f, 1f, s => triangles.TriangleScale = s); AddSliderStep("Seed", 0, 1000, 0, s => triangles.Reset(s)); - AddToggleStep("Masking", m => triangles.Masking = m); + AddToggleStep("ClampToDrawable", c => triangles.ClampToDrawable = c); } } } diff --git a/osu.Game.Tests/Visual/Background/TestSceneTrianglesV2Background.cs b/osu.Game.Tests/Visual/Background/TestSceneTrianglesV2Background.cs index 01a2464b8e..71d1baddc8 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneTrianglesV2Background.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneTrianglesV2Background.cs @@ -128,7 +128,7 @@ namespace osu.Game.Tests.Visual.Background AddStep("White colour", () => box.Colour = triangles.Colour = maskedTriangles.Colour = Color4.White); AddStep("Vertical gradient", () => box.Colour = triangles.Colour = maskedTriangles.Colour = ColourInfo.GradientVertical(Color4.White, Color4.Red)); AddStep("Horizontal gradient", () => box.Colour = triangles.Colour = maskedTriangles.Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Red)); - AddToggleStep("Masking", m => maskedTriangles.Masking = m); + AddToggleStep("ClampToDrawable", c => maskedTriangles.ClampToDrawable = c); } } } diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index cf74d6ef62..d7bfeb7731 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -80,7 +80,7 @@ namespace osu.Game.Graphics.Backgrounds /// If enabled, only the portion of triangles that falls within this 's /// shape is drawn to the screen. Default is true. /// - public bool Masking { get; set; } = true; + public bool ClampToDrawable { get; set; } = true; /// /// Whether we should drop-off alpha values of triangles more quickly to improve @@ -257,7 +257,7 @@ namespace osu.Game.Graphics.Backgrounds private IShader shader; private Texture texture; - private bool masking; + private bool clamp; private readonly List parts = new List(); private readonly Vector2 triangleSize = new Vector2(1f, equilateral_triangle_ratio) * triangle_size; @@ -276,7 +276,7 @@ namespace osu.Game.Graphics.Backgrounds shader = Source.shader; texture = Source.texture; size = Source.DrawSize; - masking = Source.Masking; + clamp = Source.ClampToDrawable; parts.Clear(); parts.AddRange(Source.parts); @@ -306,7 +306,7 @@ namespace osu.Game.Graphics.Backgrounds Vector2 topLeft = particle.Position - new Vector2(relativeSize.X * 0.5f, 0f); - Quad triangleQuad = masking ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y); + Quad triangleQuad = clamp ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y); var drawQuad = new Quad( Vector2Extensions.Transform(triangleQuad.TopLeft * size, DrawInfo.Matrix), diff --git a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs index 40b076ebc8..f723b1b358 100644 --- a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs +++ b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs @@ -36,7 +36,7 @@ namespace osu.Game.Graphics.Backgrounds /// If enabled, only the portion of triangles that falls within this 's /// shape is drawn to the screen. Default is true. /// - public bool Masking { get; set; } = true; + public bool ClampToDrawable { get; set; } = true; private readonly BindableFloat spawnRatio = new BindableFloat(1f); @@ -193,7 +193,7 @@ namespace osu.Game.Graphics.Backgrounds private Vector2 size; private float thickness; private float texelSize; - private bool masking; + private bool clamp; public TrianglesDrawNode(TrianglesV2 source) : base(source) @@ -208,7 +208,7 @@ namespace osu.Game.Graphics.Backgrounds texture = Source.texture; size = Source.DrawSize; thickness = Source.Thickness; - masking = Source.Masking; + clamp = Source.ClampToDrawable; Quad triangleQuad = new Quad( Vector2Extensions.Transform(Vector2.Zero, DrawInfo.Matrix), @@ -248,7 +248,7 @@ namespace osu.Game.Graphics.Backgrounds { Vector2 topLeft = particle.Position - new Vector2(relativeSize.X * 0.5f, 0f); - Quad triangleQuad = masking ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y); + Quad triangleQuad = clamp ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y); var drawQuad = new Quad( Vector2Extensions.Transform(triangleQuad.TopLeft * size, DrawInfo.Matrix), diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 59acbecbbf..10aca017f1 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -150,7 +150,7 @@ namespace osu.Game.Graphics.UserInterface TriangleScale = 4, ColourDark = OsuColour.Gray(0.88f), Shear = new Vector2(-0.2f, 0), - Masking = false + ClampToDrawable = false }, }, }, diff --git a/osu.Game/Overlays/Mods/ModSelectColumn.cs b/osu.Game/Overlays/Mods/ModSelectColumn.cs index 631bb72ced..05454159c7 100644 --- a/osu.Game/Overlays/Mods/ModSelectColumn.cs +++ b/osu.Game/Overlays/Mods/ModSelectColumn.cs @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Mods Height = header_height, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Velocity = 0.7f, - Masking = false + ClampToDrawable = false }, headerText = new OsuTextFlowContainer(t => { diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs index 4a28b3212e..baf0a14062 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmap.cs @@ -109,7 +109,7 @@ namespace osu.Game.Screens.Select.Carousel TriangleScale = 2, RelativeSizeAxes = Axes.Both, ColourLight = Color4Extensions.FromHex(@"3a7285"), - ColourDark = Color4Extensions.FromHex(@"123744"), + ColourDark = Color4Extensions.FromHex(@"123744") }, new FillFlowContainer { From 18d16018d340b38729b2ddbcb46e3bcb5b3fe3b3 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 21 Jan 2024 12:17:16 +0900 Subject: [PATCH 1098/1118] Fix failing tests due to pooling safety changes --- .../Skinning/TestSceneBarLine.cs | 40 +++++++--------- .../TestSceneDrawableJudgement.cs | 47 ++++++++++--------- 2 files changed, 42 insertions(+), 45 deletions(-) diff --git a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneBarLine.cs b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneBarLine.cs index ab9f57ecc3..a5c18babe2 100644 --- a/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneBarLine.cs +++ b/osu.Game.Rulesets.Mania.Tests/Skinning/TestSceneBarLine.cs @@ -1,10 +1,8 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. -using System; using System.Collections.Generic; using NUnit.Framework; -using osu.Framework.Graphics; using osu.Game.Rulesets.Mania.Beatmaps; using osu.Game.Rulesets.Mania.Objects; using osu.Game.Rulesets.Mania.UI; @@ -16,37 +14,35 @@ namespace osu.Game.Rulesets.Mania.Tests.Skinning [Test] public void TestMinor() { - AddStep("Create barlines", () => recreate()); + AddStep("Create barlines", recreate); } - private void recreate(Func>? createBarLines = null) + private void recreate() { var stageDefinitions = new List { new StageDefinition(4), }; - SetContents(_ => new ManiaPlayfield(stageDefinitions).With(s => + SetContents(_ => { - if (createBarLines != null) + var maniaPlayfield = new ManiaPlayfield(stageDefinitions); + + // Must be scheduled so the pool is loaded before we try and retrieve from it. + Schedule(() => { - var barLines = createBarLines(); - - foreach (var b in barLines) - s.Add(b); - - return; - } - - for (int i = 0; i < 64; i++) - { - s.Add(new BarLine + for (int i = 0; i < 64; i++) { - StartTime = Time.Current + i * 500, - Major = i % 4 == 0, - }); - } - })); + maniaPlayfield.Add(new BarLine + { + StartTime = Time.Current + i * 500, + Major = i % 4 == 0, + }); + } + }); + + return maniaPlayfield; + }); } } } diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs index 874130233a..5f5596cbb3 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneDrawableJudgement.cs @@ -25,16 +25,16 @@ namespace osu.Game.Rulesets.Osu.Tests [Resolved] private OsuConfigManager config { get; set; } = null!; - private readonly List> pools; + private readonly List> pools = new List>(); - public TestSceneDrawableJudgement() + [TestCaseSource(nameof(validResults))] + public void Test(HitResult result) { - pools = new List>(); - - foreach (HitResult result in Enum.GetValues(typeof(HitResult)).OfType().Skip(1)) - showResult(result); + showResult(result); } + private static IEnumerable validResults => Enum.GetValues().Skip(1); + [Test] public void TestHitLightingDisabled() { @@ -72,32 +72,33 @@ namespace osu.Game.Rulesets.Osu.Tests pools.Add(pool = new DrawablePool(1)); else { - pool = pools[poolIndex]; - // We need to make sure neither the pool nor the judgement get disposed when new content is set, and they both share the same parent. + pool = pools[poolIndex]; ((Container)pool.Parent!).Clear(false); } var container = new Container { RelativeSizeAxes = Axes.Both, - Children = new Drawable[] - { - pool, - pool.Get(j => j.Apply(new JudgementResult(new HitObject - { - StartTime = Time.Current - }, new Judgement()) - { - Type = result, - }, null)).With(j => - { - j.Anchor = Anchor.Centre; - j.Origin = Anchor.Centre; - }) - } + Child = pool, }; + // Must be scheduled so the pool is loaded before we try and retrieve from it. + Schedule(() => + { + container.Add(pool.Get(j => j.Apply(new JudgementResult(new HitObject + { + StartTime = Time.Current + }, new Judgement()) + { + Type = result, + }, null)).With(j => + { + j.Anchor = Anchor.Centre; + j.Origin = Anchor.Centre; + })); + }); + poolIndex++; return container; }); From 02369139b3054213e6598337a66ea868a5ff56c4 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Sun, 21 Jan 2024 13:11:41 +0900 Subject: [PATCH 1099/1118] Remove `FillFlow` overhead of argon counters --- .../Play/HUD/ArgonCounterTextComponent.cs | 51 +++++++++---------- 1 file changed, 25 insertions(+), 26 deletions(-) diff --git a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs index f16669f865..f8c82feddd 100644 --- a/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs +++ b/osu.Game/Screens/Play/HUD/ArgonCounterTextComponent.cs @@ -42,35 +42,30 @@ namespace osu.Game.Screens.Play.HUD Origin = anchor; AutoSizeAxes = Axes.Both; - InternalChild = new FillFlowContainer + InternalChildren = new Drawable[] { - AutoSizeAxes = Axes.Both, - Direction = FillDirection.Vertical, - Children = new Drawable[] + labelText = new OsuSpriteText { - labelText = new OsuSpriteText + Alpha = 0, + Text = label.GetValueOrDefault(), + Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold), + Margin = new MarginPadding { Left = 2.5f }, + }, + NumberContainer = new Container + { + AutoSizeAxes = Axes.Both, + Children = new[] { - Alpha = 0, - Text = label.GetValueOrDefault(), - Font = OsuFont.Torus.With(size: 12, weight: FontWeight.Bold), - Margin = new MarginPadding { Left = 2.5f }, - }, - NumberContainer = new Container - { - AutoSizeAxes = Axes.Both, - Children = new[] + wireframesPart = new ArgonCounterSpriteText(wireframesLookup) { - wireframesPart = new ArgonCounterSpriteText(wireframesLookup) - { - Anchor = anchor, - Origin = anchor, - }, - textPart = new ArgonCounterSpriteText(textLookup) - { - Anchor = anchor, - Origin = anchor, - }, - } + Anchor = anchor, + Origin = anchor, + }, + textPart = new ArgonCounterSpriteText(textLookup) + { + Anchor = anchor, + Origin = anchor, + }, } } }; @@ -110,7 +105,11 @@ namespace osu.Game.Screens.Play.HUD { base.LoadComplete(); WireframeOpacity.BindValueChanged(v => wireframesPart.Alpha = v.NewValue, true); - ShowLabel.BindValueChanged(s => labelText.Alpha = s.NewValue ? 1 : 0, true); + ShowLabel.BindValueChanged(s => + { + labelText.Alpha = s.NewValue ? 1 : 0; + NumberContainer.Y = s.NewValue ? 12 : 0; + }, true); } private partial class ArgonCounterSpriteText : OsuSpriteText From bab14dce31dcc408a600ebe4d0a94648b2cbaae6 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 22 Jan 2024 02:07:09 +0300 Subject: [PATCH 1100/1118] Add failing test case --- .../TestSceneSliderApplication.cs | 39 +++++++++++++++++-- 1 file changed, 35 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs index f41dd913ab..380a2087ac 100644 --- a/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs +++ b/osu.Game.Rulesets.Osu.Tests/TestSceneSliderApplication.cs @@ -32,7 +32,7 @@ namespace osu.Game.Rulesets.Osu.Tests { DrawableSlider dho = null; - AddStep("create slider", () => Child = dho = new DrawableSlider(prepareObject(new Slider + AddStep("create slider", () => Child = dho = new DrawableSlider(applyDefaults(new Slider { Position = new Vector2(256, 192), IndexInCurrentCombo = 0, @@ -47,7 +47,7 @@ namespace osu.Game.Rulesets.Osu.Tests AddWaitStep("wait for progression", 1); - AddStep("apply new slider", () => dho.Apply(prepareObject(new Slider + AddStep("apply new slider", () => dho.Apply(applyDefaults(new Slider { Position = new Vector2(256, 192), ComboIndex = 1, @@ -75,7 +75,7 @@ namespace osu.Game.Rulesets.Osu.Tests Child = new SkinProvidingContainer(provider) { RelativeSizeAxes = Axes.Both, - Child = dho = new DrawableSlider(prepareObject(new Slider + Child = dho = new DrawableSlider(applyDefaults(new Slider { Position = new Vector2(256, 192), IndexInCurrentCombo = 0, @@ -97,7 +97,38 @@ namespace osu.Game.Rulesets.Osu.Tests AddAssert("ball is red", () => dho.ChildrenOfType().Single().BallColour == Color4.Red); } - private Slider prepareObject(Slider slider) + [Test] + public void TestIncreaseRepeatCount() + { + DrawableSlider dho = null; + + AddStep("create slider", () => + { + Child = dho = new DrawableSlider(applyDefaults(new Slider + { + Position = new Vector2(256, 192), + IndexInCurrentCombo = 0, + StartTime = Time.Current, + Path = new SliderPath(PathType.LINEAR, new[] + { + Vector2.Zero, + new Vector2(150, 100), + new Vector2(300, 0), + }) + })); + }); + + AddStep("increase repeat count", () => + { + dho.HitObject.RepeatCount++; + applyDefaults(dho.HitObject); + }); + + AddAssert("repeat got custom anchor", () => + dho.ChildrenOfType().Single().RelativeAnchorPosition == Vector2.Divide(dho.SliderBody!.PathOffset, dho.DrawSize)); + } + + private Slider applyDefaults(Slider slider) { slider.ApplyDefaults(new ControlPointInfo(), new BeatmapDifficulty()); return slider; From 57b2d018a99a7307d09984687299b03c027040cf Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 22 Jan 2024 02:07:29 +0300 Subject: [PATCH 1101/1118] Fix slider sometimes not updating relative anchor position --- .../Objects/Drawables/DrawableSlider.cs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs index ed4c02a4a9..baec200107 100644 --- a/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs +++ b/osu.Game.Rulesets.Osu/Objects/Drawables/DrawableSlider.cs @@ -67,7 +67,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables private Container repeatContainer; private PausableSkinnableSound slidingSample; - private readonly LayoutValue drawSizeLayout; + private readonly LayoutValue relativeAnchorPositionLayout; public DrawableSlider() : this(null) @@ -85,7 +85,7 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables AlwaysPresent = true, Alpha = 0 }; - AddLayout(drawSizeLayout = new LayoutValue(Invalidation.DrawSize | Invalidation.MiscGeometry)); + AddLayout(relativeAnchorPositionLayout = new LayoutValue(Invalidation.DrawSize | Invalidation.MiscGeometry)); } [BackgroundDependencyLoader] @@ -190,6 +190,8 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables repeatContainer.Add(repeat); break; } + + relativeAnchorPositionLayout.Invalidate(); } protected override void ClearNestedHitObjects() @@ -265,14 +267,14 @@ namespace osu.Game.Rulesets.Osu.Objects.Drawables Size = SliderBody?.Size ?? Vector2.Zero; OriginPosition = SliderBody?.PathOffset ?? Vector2.Zero; - if (!drawSizeLayout.IsValid) + if (!relativeAnchorPositionLayout.IsValid) { Vector2 pos = Vector2.Divide(OriginPosition, DrawSize); foreach (var obj in NestedHitObjects) obj.RelativeAnchorPosition = pos; Ball.RelativeAnchorPosition = pos; - drawSizeLayout.Validate(); + relativeAnchorPositionLayout.Validate(); } } From cec4f670d1f84e39b4fa918b13e57fbed087f65b Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Mon, 22 Jan 2024 03:12:23 +0300 Subject: [PATCH 1102/1118] Reduce allocation overhead in BeatmapCarousel --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 4408634787..1f2103ff61 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -899,7 +899,7 @@ namespace osu.Game.Screens.Select // Update externally controlled state of currently visible items (e.g. x-offset and opacity). // This is a per-frame update on all drawable panels. - foreach (DrawableCarouselItem item in Scroll.Children) + foreach (DrawableCarouselItem item in Scroll.ScrollContent) { updateItem(item); From 2bd9cd5d3410a281cff18f285a01c355a9c9b8e3 Mon Sep 17 00:00:00 2001 From: Salman Ahmed Date: Mon, 22 Jan 2024 04:39:08 +0300 Subject: [PATCH 1103/1118] Fix blueprint container not handling right clicks correctly while moveing an element --- .../Screens/Edit/Compose/Components/BlueprintContainer.cs | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs index 110beb0fa6..2d6e234e57 100644 --- a/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs +++ b/osu.Game/Screens/Edit/Compose/Components/BlueprintContainer.cs @@ -114,7 +114,7 @@ namespace osu.Game.Screens.Edit.Compose.Components protected override bool OnMouseDown(MouseDownEvent e) { bool selectionPerformed = performMouseDownActions(e); - bool movementPossible = prepareSelectionMovement(); + bool movementPossible = prepareSelectionMovement(e); // check if selection has occurred if (selectionPerformed) @@ -536,9 +536,13 @@ namespace osu.Game.Screens.Edit.Compose.Components /// /// Attempts to begin the movement of any selected blueprints. /// + /// The defining the beginning of a movement. /// Whether a movement is possible. - private bool prepareSelectionMovement() + private bool prepareSelectionMovement(MouseDownEvent e) { + if (e.Button == MouseButton.Right) + return false; + if (!SelectionHandler.SelectedBlueprints.Any()) return false; From 74f05a5c4b28d1493e7556588f0589cb20821650 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jan 2024 15:54:24 +0900 Subject: [PATCH 1104/1118] Use container itself rather than `ScrollContent` --- osu.Game/Screens/Select/BeatmapCarousel.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 1f2103ff61..35d534cf68 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -899,7 +899,7 @@ namespace osu.Game.Screens.Select // Update externally controlled state of currently visible items (e.g. x-offset and opacity). // This is a per-frame update on all drawable panels. - foreach (DrawableCarouselItem item in Scroll.ScrollContent) + foreach (DrawableCarouselItem item in Scroll) { updateItem(item); From 1f0ad5cff24f4c445d4dd8d6b5c4be1e3de98472 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jan 2024 15:56:16 +0900 Subject: [PATCH 1105/1118] Apply same fix in more places --- osu.Game/Screens/Select/BeatmapCarousel.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Select/BeatmapCarousel.cs b/osu.Game/Screens/Select/BeatmapCarousel.cs index 35d534cf68..70ecde3858 100644 --- a/osu.Game/Screens/Select/BeatmapCarousel.cs +++ b/osu.Game/Screens/Select/BeatmapCarousel.cs @@ -868,7 +868,7 @@ namespace osu.Game.Screens.Select { var toDisplay = visibleItems.GetRange(displayedRange.first, displayedRange.last - displayedRange.first + 1); - foreach (var panel in Scroll.Children) + foreach (var panel in Scroll) { Debug.Assert(panel.Item != null); @@ -1094,7 +1094,7 @@ namespace osu.Game.Screens.Select // to enter clamp-special-case mode where it animates completely differently to normal. float scrollChange = scrollTarget.Value - Scroll.Current; Scroll.ScrollTo(scrollTarget.Value, false); - foreach (var i in Scroll.Children) + foreach (var i in Scroll) i.Y += scrollChange; break; } From 3e5fe66e58b3bf3e36761511883b8c0170f7c0e0 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jan 2024 16:37:56 +0900 Subject: [PATCH 1106/1118] Fix potential null reference in player screen transition handling See https://github.com/ppy/osu/actions/runs/7607677219/job/20715418536?pr=26660. --- osu.Game/Screens/Play/Player.cs | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/Player.cs b/osu.Game/Screens/Play/Player.cs index db39d06c54..ad1f9ec897 100644 --- a/osu.Game/Screens/Play/Player.cs +++ b/osu.Game/Screens/Play/Player.cs @@ -1240,9 +1240,14 @@ namespace osu.Game.Screens.Play { b.IgnoreUserSettings.Value = true; - b.IsBreakTime.UnbindFrom(breakTracker.IsBreakTime); - b.IsBreakTime.Value = false; + // May be null if the load never completed. + if (breakTracker != null) + { + b.IsBreakTime.UnbindFrom(breakTracker.IsBreakTime); + b.IsBreakTime.Value = false; + } }); + storyboardReplacesBackground.Value = false; } } From 3dd18c777a578acbc8ded48cb3ea92683391fc5e Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jan 2024 17:00:35 +0900 Subject: [PATCH 1107/1118] Remove a couple more overrides --- osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs | 2 -- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 7 +------ osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs | 2 +- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs index 6f5ff9c99c..3792a67896 100644 --- a/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Lounge/LoungeSubScreen.cs @@ -40,8 +40,6 @@ namespace osu.Game.Screens.OnlinePlay.Lounge { public override string Title => "Lounge"; - protected override bool PlayExitSound => false; - protected override BackgroundScreen CreateBackground() => new LoungeBackgroundScreen { SelectedRoom = { BindTarget = SelectedRoom } diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index 8a85a46a2f..aab7ac3280 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -44,8 +44,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer public override string ShortTitle => "room"; - protected override bool PlayExitSound => !exitConfirmed; - [Resolved] private MultiplayerClient client { get; set; } @@ -249,15 +247,13 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved(canBeNull: true)] private IDialogOverlay dialogOverlay { get; set; } - private bool exitConfirmed; - public override bool OnExiting(ScreenExitEvent e) { // room has not been created yet or we're offline; exit immediately. if (client.Room == null || !IsConnected) return base.OnExiting(e); - if (!exitConfirmed && dialogOverlay != null) + if (dialogOverlay != null) { if (dialogOverlay.CurrentDialog is ConfirmDialog confirmDialog) confirmDialog.PerformOkAction(); @@ -265,7 +261,6 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { dialogOverlay.Push(new ConfirmDialog("Are you sure you want to leave this multiplayer match?", () => { - exitConfirmed = true; if (this.IsCurrentScreen()) this.Exit(); })); diff --git a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs index ea855c0474..fa1ee004c9 100644 --- a/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/OnlinePlaySubScreen.cs @@ -13,7 +13,7 @@ namespace osu.Game.Screens.OnlinePlay public virtual string ShortTitle => Title; - protected override bool PlayExitSound => false; + protected sealed override bool PlayExitSound => false; [Resolved] protected IRoomManager? RoomManager { get; private set; } From cd551b1abd4912b7d93fedeabd0b21fcffa9f1bd Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jan 2024 18:01:21 +0900 Subject: [PATCH 1108/1118] Fix star fountains sometimes resetting visually Addresses https://github.com/ppy/osu/discussions/26622. --- osu.Game/Screens/Menu/StarFountain.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/osu.Game/Screens/Menu/StarFountain.cs b/osu.Game/Screens/Menu/StarFountain.cs index fd59ec3573..dd5171c6be 100644 --- a/osu.Game/Screens/Menu/StarFountain.cs +++ b/osu.Game/Screens/Menu/StarFountain.cs @@ -3,6 +3,7 @@ using osu.Framework.Allocation; using osu.Framework.Graphics.Textures; +using osu.Framework.Threading; using osu.Framework.Utils; using osu.Game.Graphics; using osu.Game.Skinning; @@ -43,8 +44,6 @@ namespace osu.Game.Screens.Menu private const double shoot_duration = 800; - protected override bool CanSpawnParticles => lastShootTime != null && Time.Current - lastShootTime < shoot_duration; - [Resolved] private ISkinSource skin { get; set; } = null!; @@ -57,7 +56,6 @@ namespace osu.Game.Screens.Menu private void load(TextureStore textures) { Texture = skin.GetTexture("Menu/fountain-star") ?? textures.Get("Menu/fountain-star"); - Active.Value = true; } protected override FallingParticle CreateParticle() @@ -81,8 +79,15 @@ namespace osu.Game.Screens.Menu return lastShootDirection * x_velocity_from_direction * (float)(1 - 2 * (Clock.CurrentTime - lastShootTime!.Value) / shoot_duration) + getRandomVariance(x_velocity_random_variance); } + private ScheduledDelegate? deactivateDelegate; + public void Shoot(int direction) { + Active.Value = true; + + deactivateDelegate?.Cancel(); + deactivateDelegate = Scheduler.AddDelayed(() => Active.Value = false, shoot_duration); + lastShootTime = Clock.CurrentTime; lastShootDirection = direction; } From 47e9846315244e9f4824a8fedcb8e6c4dee3cd44 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jan 2024 18:48:48 +0900 Subject: [PATCH 1109/1118] Adjust slider tick / end miss animations to be less busy --- .../Skinning/Argon/ArgonJudgementPiece.cs | 11 ++++++----- osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs | 9 ++++----- osu.Game/Skinning/LegacyJudgementPieceOld.cs | 10 +++------- 3 files changed, 13 insertions(+), 17 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs index bb61bd37c1..9a5abba4fb 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Argon/ArgonJudgementPiece.cs @@ -65,14 +65,15 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon if (Result == HitResult.IgnoreMiss || Result == HitResult.LargeTickMiss) { this.RotateTo(-45); - this.ScaleTo(1.8f); + this.ScaleTo(1.6f); this.ScaleTo(1.2f, 100, Easing.In); - this.MoveTo(Vector2.Zero); - this.MoveToOffset(new Vector2(0, 10), 800, Easing.InQuint); + this.FadeOutFromOne(400); } else if (Result.IsMiss()) { + this.FadeOutFromOne(800); + this.ScaleTo(1.6f); this.ScaleTo(1, 100, Easing.In); @@ -84,14 +85,14 @@ namespace osu.Game.Rulesets.Osu.Skinning.Argon } else { + this.FadeOutFromOne(800); + JudgementText .FadeInFromZero(300, Easing.OutQuint) .ScaleTo(Vector2.One) .ScaleTo(new Vector2(1.2f), 1800, Easing.OutQuint); } - this.FadeOutFromOne(800); - ringExplosion?.PlayAnimation(); } diff --git a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs index ada651b60e..c7103cd9fd 100644 --- a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs +++ b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs @@ -45,11 +45,10 @@ namespace osu.Game.Rulesets.Judgements if (Result == HitResult.IgnoreMiss || Result == HitResult.LargeTickMiss) { this.RotateTo(-45); - this.ScaleTo(1.8f); + this.ScaleTo(1.6f); this.ScaleTo(1.2f, 100, Easing.In); - this.MoveTo(Vector2.Zero); - this.MoveToOffset(new Vector2(0, 10), 800, Easing.InQuint); + this.FadeOutFromOne(400); } else if (Result.IsMiss()) { @@ -61,9 +60,9 @@ namespace osu.Game.Rulesets.Judgements this.RotateTo(0); this.RotateTo(40, 800, Easing.InQuint); - } - this.FadeOutFromOne(800); + this.FadeOutFromOne(800); + } } public Drawable? GetAboveHitObjectsProxiedContent() => null; diff --git a/osu.Game/Skinning/LegacyJudgementPieceOld.cs b/osu.Game/Skinning/LegacyJudgementPieceOld.cs index 1834a17279..a9f68bd378 100644 --- a/osu.Game/Skinning/LegacyJudgementPieceOld.cs +++ b/osu.Game/Skinning/LegacyJudgementPieceOld.cs @@ -63,14 +63,10 @@ namespace osu.Game.Skinning // missed ticks / slider end don't get the normal animation. if (isMissedTick()) { - this.ScaleTo(1.6f); - this.ScaleTo(1, 100, Easing.In); + this.ScaleTo(1.2f); + this.ScaleTo(1f, 100, Easing.In); - if (legacyVersion > 1.0m) - { - this.MoveTo(new Vector2(0, -2f)); - this.MoveToOffset(new Vector2(0, 10), fade_out_delay + fade_out_length, Easing.In); - } + this.FadeOutFromOne(400); } else { From 9b2740f8a4032dfa568f2bbcc1ee0609a56c2bb3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Jan 2024 11:11:15 +0100 Subject: [PATCH 1110/1118] Fix score submission test failures due to checking audio playback validity (again) As seen in https://github.com/ppy/osu/actions/runs/7607899979/job/20716013982?pr=26662#step:5:75 In https://github.com/ppy/osu/pull/26484, I went "well if I'm moving the enabling of validation of playback rate to `SubmittingPlayer` context, then surely I can remove the local override in the test scene, right?" Reader: Apparently I did not notice that `FakeImportingPlayer : TestPlayer : SoloPlayer : SubmittingPlayer`. So no, I could not remove the local override in the test scene. You could probably attempt to conjure up some excuse about deep inheritance hierarchies here but nah. Really just a failure to read on my behalf as usual. --- .../Visual/Gameplay/TestScenePlayerScoreSubmission.cs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs index e2ce3a014c..5e22e47572 100644 --- a/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs +++ b/osu.Game.Tests/Visual/Gameplay/TestScenePlayerScoreSubmission.cs @@ -23,6 +23,7 @@ using osu.Game.Rulesets.Scoring; using osu.Game.Rulesets.Taiko; using osu.Game.Rulesets.Taiko.Mods; using osu.Game.Scoring; +using osu.Game.Screens.Play; using osu.Game.Screens.Ranking; using osu.Game.Tests.Beatmaps; @@ -383,6 +384,11 @@ namespace osu.Game.Tests.Visual.Gameplay AllowImportCompletion = new SemaphoreSlim(1); } + protected override GameplayClockContainer CreateGameplayClockContainer(WorkingBeatmap beatmap, double gameplayStart) => new MasterGameplayClockContainer(beatmap, gameplayStart) + { + ShouldValidatePlaybackRate = false, + }; + protected override async Task ImportScore(Score score) { ScoreImportStarted = true; From 99d3fcc3261a5f1ec9f8270d734e5e5f93318ba9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Jan 2024 13:45:55 +0100 Subject: [PATCH 1111/1118] Fix crash if ruleset components container is not present --- osu.Game/Screens/Play/HUDOverlay.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/Play/HUDOverlay.cs b/osu.Game/Screens/Play/HUDOverlay.cs index a9fe393395..b5482f2a5b 100644 --- a/osu.Game/Screens/Play/HUDOverlay.cs +++ b/osu.Game/Screens/Play/HUDOverlay.cs @@ -172,7 +172,10 @@ namespace osu.Game.Screens.Play }, }; - hideTargets = new List { mainComponents, rulesetComponents, playfieldComponents, topRightElements }; + hideTargets = new List { mainComponents, playfieldComponents, topRightElements }; + + if (rulesetComponents != null) + hideTargets.Add(rulesetComponents); if (!alwaysShowLeaderboard) hideTargets.Add(LeaderboardFlow); From 15df6b1da16cd745e45e60dc150bed29be2f2775 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Mon, 22 Jan 2024 21:47:38 +0900 Subject: [PATCH 1112/1118] Revert keyboard seek speed change --- osu.Game/Screens/Play/ReplayPlayer.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/osu.Game/Screens/Play/ReplayPlayer.cs b/osu.Game/Screens/Play/ReplayPlayer.cs index a26a2b9904..3c5b85662a 100644 --- a/osu.Game/Screens/Play/ReplayPlayer.cs +++ b/osu.Game/Screens/Play/ReplayPlayer.cs @@ -107,11 +107,11 @@ namespace osu.Game.Screens.Play return true; case GlobalAction.SeekReplayBackward: - SeekInDirection(-1); + SeekInDirection(-5); return true; case GlobalAction.SeekReplayForward: - SeekInDirection(1); + SeekInDirection(5); return true; case GlobalAction.TogglePauseReplay: From 17d05b0fdfd392423c708bd368cd02a77116d0bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Jan 2024 14:23:46 +0100 Subject: [PATCH 1113/1118] Fix non-miss drawable judgements fading out instantly on triangles skin `else if` proves to be insidious once again. --- osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs index c7103cd9fd..7330f138ce 100644 --- a/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs +++ b/osu.Game/Rulesets/Judgements/DefaultJudgementPiece.cs @@ -49,8 +49,10 @@ namespace osu.Game.Rulesets.Judgements this.ScaleTo(1.2f, 100, Easing.In); this.FadeOutFromOne(400); + return; } - else if (Result.IsMiss()) + + if (Result.IsMiss()) { this.ScaleTo(1.6f); this.ScaleTo(1, 100, Easing.In); @@ -60,9 +62,9 @@ namespace osu.Game.Rulesets.Judgements this.RotateTo(0); this.RotateTo(40, 800, Easing.InQuint); - - this.FadeOutFromOne(800); } + + this.FadeOutFromOne(800); } public Drawable? GetAboveHitObjectsProxiedContent() => null; From 9aa7c7f59197b9754e98e1ba5ce5e4730f4d5f4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Dach?= Date: Mon, 22 Jan 2024 15:35:03 +0100 Subject: [PATCH 1114/1118] Revert incorrect removal of `exitConfirmed` flag Removing it meant that it was _literally impossible_ to exit multiplayer. --- .../OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs index aab7ac3280..a37314de0e 100644 --- a/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs +++ b/osu.Game/Screens/OnlinePlay/Multiplayer/MultiplayerMatchSubScreen.cs @@ -247,13 +247,15 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer [Resolved(canBeNull: true)] private IDialogOverlay dialogOverlay { get; set; } + private bool exitConfirmed; + public override bool OnExiting(ScreenExitEvent e) { // room has not been created yet or we're offline; exit immediately. if (client.Room == null || !IsConnected) return base.OnExiting(e); - if (dialogOverlay != null) + if (!exitConfirmed && dialogOverlay != null) { if (dialogOverlay.CurrentDialog is ConfirmDialog confirmDialog) confirmDialog.PerformOkAction(); @@ -261,6 +263,7 @@ namespace osu.Game.Screens.OnlinePlay.Multiplayer { dialogOverlay.Push(new ConfirmDialog("Are you sure you want to leave this multiplayer match?", () => { + exitConfirmed = true; if (this.IsCurrentScreen()) this.Exit(); })); From 069af13aaf1d1c716ce207f9c9d2eab48f013eaa Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Jan 2024 05:26:16 +0900 Subject: [PATCH 1115/1118] Reduce enumerator overhead in `GameplayLeaderboard` --- osu.Game/Online/Leaderboards/Leaderboard.cs | 4 ++-- osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/osu.Game/Online/Leaderboards/Leaderboard.cs b/osu.Game/Online/Leaderboards/Leaderboard.cs index 67f2590ad8..0fd9597ac0 100644 --- a/osu.Game/Online/Leaderboards/Leaderboard.cs +++ b/osu.Game/Online/Leaderboards/Leaderboard.cs @@ -287,7 +287,7 @@ namespace osu.Game.Online.Leaderboards double delay = 0; - foreach (var s in scoreFlowContainer.Children) + foreach (var s in scoreFlowContainer) { using (s.BeginDelayedSequence(delay)) s.Show(); @@ -384,7 +384,7 @@ namespace osu.Game.Online.Leaderboards if (scoreFlowContainer == null) return; - foreach (var c in scoreFlowContainer.Children) + foreach (var c in scoreFlowContainer) { float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, scoreFlowContainer).Y; float bottomY = topY + LeaderboardScore.HEIGHT; diff --git a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs index d990af32e7..d2b6b834f8 100644 --- a/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs +++ b/osu.Game/Screens/Play/HUD/GameplayLeaderboard.cs @@ -128,7 +128,7 @@ namespace osu.Game.Screens.Play.HUD if (!scroll.IsScrolledToEnd()) fadeBottom -= panel_height; // logic is mostly shared with Leaderboard, copied here for simplicity. - foreach (var c in Flow.Children) + foreach (var c in Flow) { float topY = c.ToSpaceOfOtherDrawable(Vector2.Zero, Flow).Y; float bottomY = topY + panel_height; From 02bb506cce02df332ff4f884462bc5e24dfac949 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Jan 2024 05:32:11 +0900 Subject: [PATCH 1116/1118] Avoid using `.Children` for enumeration in other locations --- osu.Desktop.slnf | 13 ++++++------- osu.Game/Graphics/Containers/WaveContainer.cs | 4 ++-- .../Graphics/UserInterface/BreadcrumbControl.cs | 2 +- osu.Game/Graphics/UserInterface/OsuTabControl.cs | 4 ++-- osu.Game/Graphics/UserInterface/StarCounter.cs | 2 +- osu.Game/Overlays/Chat/Listing/ChannelListing.cs | 2 +- osu.Game/Overlays/OverlayStreamControl.cs | 4 ++-- osu.Game/Screens/Menu/ButtonSystem.cs | 2 +- .../HUD/JudgementCounter/JudgementCounterDisplay.cs | 2 +- .../Select/Carousel/DrawableCarouselBeatmapSet.cs | 2 +- 10 files changed, 18 insertions(+), 19 deletions(-) diff --git a/osu.Desktop.slnf b/osu.Desktop.slnf index 503e5935f5..606988ccdf 100644 --- a/osu.Desktop.slnf +++ b/osu.Desktop.slnf @@ -16,15 +16,14 @@ "osu.Game.Tournament.Tests\\osu.Game.Tournament.Tests.csproj", "osu.Game.Tournament\\osu.Game.Tournament.csproj", "osu.Game\\osu.Game.csproj", - - "Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform\\osu.Game.Rulesets.EmptyFreeform.csproj", "Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform.Tests\\osu.Game.Rulesets.EmptyFreeform.Tests.csproj", - "Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj", + "Templates\\Rulesets\\ruleset-empty\\osu.Game.Rulesets.EmptyFreeform\\osu.Game.Rulesets.EmptyFreeform.csproj", "Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj", - "Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling\\osu.Game.Rulesets.EmptyScrolling.csproj", + "Templates\\Rulesets\\ruleset-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj", "Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling.Tests\\osu.Game.Rulesets.EmptyScrolling.Tests.csproj", - "Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj", - "Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj" + "Templates\\Rulesets\\ruleset-scrolling-empty\\osu.Game.Rulesets.EmptyScrolling\\osu.Game.Rulesets.EmptyScrolling.csproj", + "Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon.Tests\\osu.Game.Rulesets.Pippidon.Tests.csproj", + "Templates\\Rulesets\\ruleset-scrolling-example\\osu.Game.Rulesets.Pippidon\\osu.Game.Rulesets.Pippidon.csproj" ] } -} +} \ No newline at end of file diff --git a/osu.Game/Graphics/Containers/WaveContainer.cs b/osu.Game/Graphics/Containers/WaveContainer.cs index 5abc66d2ac..2ae4dc5a76 100644 --- a/osu.Game/Graphics/Containers/WaveContainer.cs +++ b/osu.Game/Graphics/Containers/WaveContainer.cs @@ -122,7 +122,7 @@ namespace osu.Game.Graphics.Containers protected override void PopIn() { - foreach (var w in wavesContainer.Children) + foreach (var w in wavesContainer) w.Show(); contentContainer.MoveToY(0, APPEAR_DURATION, Easing.OutQuint); @@ -132,7 +132,7 @@ namespace osu.Game.Graphics.Containers protected override void PopOut() { - foreach (var w in wavesContainer.Children) + foreach (var w in wavesContainer) w.Hide(); contentContainer.MoveToY(2, DISAPPEAR_DURATION, Easing.In); diff --git a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs index fc0770d896..af4b3849af 100644 --- a/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs +++ b/osu.Game/Graphics/UserInterface/BreadcrumbControl.cs @@ -33,7 +33,7 @@ namespace osu.Game.Graphics.UserInterface Current.ValueChanged += index => { - foreach (var t in TabContainer.Children.OfType()) + foreach (var t in TabContainer.OfType()) { int tIndex = TabContainer.IndexOf(t); int tabIndex = TabContainer.IndexOf(TabMap[index.NewValue]); diff --git a/osu.Game/Graphics/UserInterface/OsuTabControl.cs b/osu.Game/Graphics/UserInterface/OsuTabControl.cs index 05309760e7..c260c92b43 100644 --- a/osu.Game/Graphics/UserInterface/OsuTabControl.cs +++ b/osu.Game/Graphics/UserInterface/OsuTabControl.cs @@ -37,7 +37,7 @@ namespace osu.Game.Graphics.UserInterface if (Dropdown is IHasAccentColour dropdown) dropdown.AccentColour = value; - foreach (var i in TabContainer.Children.OfType()) + foreach (var i in TabContainer.OfType()) i.AccentColour = value; } } @@ -48,7 +48,7 @@ namespace osu.Game.Graphics.UserInterface protected override TabItem CreateTabItem(T value) => new OsuTabItem(value); - protected virtual float StripWidth => TabContainer.Children.Sum(c => c.IsPresent ? c.DrawWidth + TabContainer.Spacing.X : 0) - TabContainer.Spacing.X; + protected virtual float StripWidth => TabContainer.Sum(c => c.IsPresent ? c.DrawWidth + TabContainer.Spacing.X : 0) - TabContainer.Spacing.X; /// /// Whether entries should be automatically populated if is an type. diff --git a/osu.Game/Graphics/UserInterface/StarCounter.cs b/osu.Game/Graphics/UserInterface/StarCounter.cs index fe986b275e..720f479216 100644 --- a/osu.Game/Graphics/UserInterface/StarCounter.cs +++ b/osu.Game/Graphics/UserInterface/StarCounter.cs @@ -101,7 +101,7 @@ namespace osu.Game.Graphics.UserInterface public void StopAnimation() { animate(current); - foreach (var star in stars.Children) + foreach (var star in stars) star.FinishTransforms(true); } diff --git a/osu.Game/Overlays/Chat/Listing/ChannelListing.cs b/osu.Game/Overlays/Chat/Listing/ChannelListing.cs index 809ea2f11d..1699dcceb0 100644 --- a/osu.Game/Overlays/Chat/Listing/ChannelListing.cs +++ b/osu.Game/Overlays/Chat/Listing/ChannelListing.cs @@ -63,7 +63,7 @@ namespace osu.Game.Overlays.Chat.Listing flow.ChildrenEnumerable = newChannels.Where(c => c.Type == ChannelType.Public) .Select(c => new ChannelListingItem(c)); - foreach (var item in flow.Children) + foreach (var item in flow) { item.OnRequestJoin += channel => OnRequestJoin?.Invoke(channel); item.OnRequestLeave += channel => OnRequestLeave?.Invoke(channel); diff --git a/osu.Game/Overlays/OverlayStreamControl.cs b/osu.Game/Overlays/OverlayStreamControl.cs index 84de384fb5..bc37a57cab 100644 --- a/osu.Game/Overlays/OverlayStreamControl.cs +++ b/osu.Game/Overlays/OverlayStreamControl.cs @@ -41,7 +41,7 @@ namespace osu.Game.Overlays protected override bool OnHover(HoverEvent e) { - foreach (var streamBadge in TabContainer.Children.OfType>()) + foreach (var streamBadge in TabContainer.OfType>()) streamBadge.UserHoveringArea = true; return base.OnHover(e); @@ -49,7 +49,7 @@ namespace osu.Game.Overlays protected override void OnHoverLost(HoverLostEvent e) { - foreach (var streamBadge in TabContainer.Children.OfType>()) + foreach (var streamBadge in TabContainer.OfType>()) streamBadge.UserHoveringArea = false; base.OnHoverLost(e); diff --git a/osu.Game/Screens/Menu/ButtonSystem.cs b/osu.Game/Screens/Menu/ButtonSystem.cs index b2b3fbd626..d742d2377f 100644 --- a/osu.Game/Screens/Menu/ButtonSystem.cs +++ b/osu.Game/Screens/Menu/ButtonSystem.cs @@ -343,7 +343,7 @@ namespace osu.Game.Screens.Menu { buttonArea.ButtonSystemState = state; - foreach (var b in buttonArea.Children.OfType()) + foreach (var b in buttonArea.OfType()) b.ButtonSystemState = state; } diff --git a/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounterDisplay.cs b/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounterDisplay.cs index 326be55222..25e5464205 100644 --- a/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounterDisplay.cs +++ b/osu.Game/Screens/Play/HUD/JudgementCounter/JudgementCounterDisplay.cs @@ -64,7 +64,7 @@ namespace osu.Game.Screens.Play.HUD.JudgementCounter CounterFlow.Direction = convertedDirection; - foreach (var counter in CounterFlow.Children) + foreach (var counter in CounterFlow) counter.Direction.Value = convertedDirection; }, true); diff --git a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs index 369db37e63..bd659d7423 100644 --- a/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs +++ b/osu.Game/Screens/Select/Carousel/DrawableCarouselBeatmapSet.cs @@ -242,7 +242,7 @@ namespace osu.Game.Screens.Select.Carousel bool isSelected = Item?.State.Value == CarouselItemState.Selected; - foreach (var panel in beatmapContainer.Children) + foreach (var panel in beatmapContainer) { Debug.Assert(panel.Item != null); From da992ccc55e6712b18b3b91b8864e7c44238f9ca Mon Sep 17 00:00:00 2001 From: Andrei Zavatski Date: Tue, 23 Jan 2024 04:54:27 +0300 Subject: [PATCH 1117/1118] Implement per-axis triangles clamping --- .../Skinning/Default/TrianglesPiece.cs | 3 +- .../TestSceneTrianglesBackground.cs | 8 +++-- .../TestSceneTrianglesV2Background.cs | 8 +++-- osu.Game/Graphics/Backgrounds/Triangles.cs | 36 +++++++++++-------- osu.Game/Graphics/Backgrounds/TrianglesV2.cs | 36 +++++++++++-------- .../Graphics/UserInterface/DialogButton.cs | 2 +- osu.Game/Overlays/Mods/ModSelectColumn.cs | 4 +-- 7 files changed, 59 insertions(+), 38 deletions(-) diff --git a/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs b/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs index 566176505d..512ac8ee3e 100644 --- a/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs +++ b/osu.Game.Rulesets.Osu/Skinning/Default/TrianglesPiece.cs @@ -1,6 +1,7 @@ // Copyright (c) ppy Pty Ltd . Licensed under the MIT Licence. // See the LICENCE file in the repository root for full licence text. +using osu.Framework.Graphics; using osu.Game.Graphics.Backgrounds; namespace osu.Game.Rulesets.Osu.Skinning.Default @@ -15,7 +16,7 @@ namespace osu.Game.Rulesets.Osu.Skinning.Default { TriangleScale = 1.2f; HideAlphaDiscrepancies = false; - ClampToDrawable = false; + ClampAxes = Axes.None; } protected override void Update() diff --git a/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs b/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs index 4733b7f92f..dd4c372193 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneTrianglesBackground.cs @@ -29,7 +29,8 @@ namespace osu.Game.Tests.Visual.Background ColourDark = Color4.Gray, Anchor = Anchor.Centre, Origin = Anchor.Centre, - Size = new Vector2(0.9f) + Size = new Vector2(0.9f), + ClampAxes = Axes.None } }; } @@ -40,7 +41,10 @@ namespace osu.Game.Tests.Visual.Background AddSliderStep("Triangle scale", 0f, 10f, 1f, s => triangles.TriangleScale = s); AddSliderStep("Seed", 0, 1000, 0, s => triangles.Reset(s)); - AddToggleStep("ClampToDrawable", c => triangles.ClampToDrawable = c); + AddStep("ClampAxes X", () => triangles.ClampAxes = Axes.X); + AddStep("ClampAxes Y", () => triangles.ClampAxes = Axes.Y); + AddStep("ClampAxes Both", () => triangles.ClampAxes = Axes.Both); + AddStep("ClampAxes None", () => triangles.ClampAxes = Axes.None); } } } diff --git a/osu.Game.Tests/Visual/Background/TestSceneTrianglesV2Background.cs b/osu.Game.Tests/Visual/Background/TestSceneTrianglesV2Background.cs index 71d1baddc8..4713852c0b 100644 --- a/osu.Game.Tests/Visual/Background/TestSceneTrianglesV2Background.cs +++ b/osu.Game.Tests/Visual/Background/TestSceneTrianglesV2Background.cs @@ -86,7 +86,8 @@ namespace osu.Game.Tests.Visual.Background { Anchor = Anchor.Centre, Origin = Anchor.Centre, - RelativeSizeAxes = Axes.Both + RelativeSizeAxes = Axes.Both, + ClampAxes = Axes.None } } }, @@ -128,7 +129,10 @@ namespace osu.Game.Tests.Visual.Background AddStep("White colour", () => box.Colour = triangles.Colour = maskedTriangles.Colour = Color4.White); AddStep("Vertical gradient", () => box.Colour = triangles.Colour = maskedTriangles.Colour = ColourInfo.GradientVertical(Color4.White, Color4.Red)); AddStep("Horizontal gradient", () => box.Colour = triangles.Colour = maskedTriangles.Colour = ColourInfo.GradientHorizontal(Color4.White, Color4.Red)); - AddToggleStep("ClampToDrawable", c => maskedTriangles.ClampToDrawable = c); + AddStep("ClampAxes X", () => maskedTriangles.ClampAxes = Axes.X); + AddStep("ClampAxes Y", () => maskedTriangles.ClampAxes = Axes.Y); + AddStep("ClampAxes Both", () => maskedTriangles.ClampAxes = Axes.Both); + AddStep("ClampAxes None", () => maskedTriangles.ClampAxes = Axes.None); } } } diff --git a/osu.Game/Graphics/Backgrounds/Triangles.cs b/osu.Game/Graphics/Backgrounds/Triangles.cs index d7bfeb7731..e877915fac 100644 --- a/osu.Game/Graphics/Backgrounds/Triangles.cs +++ b/osu.Game/Graphics/Backgrounds/Triangles.cs @@ -77,10 +77,10 @@ namespace osu.Game.Graphics.Backgrounds } /// - /// If enabled, only the portion of triangles that falls within this 's - /// shape is drawn to the screen. Default is true. + /// Controls on which the portion of triangles that falls within this 's + /// shape is drawn to the screen. Default is Axes.Both. /// - public bool ClampToDrawable { get; set; } = true; + public Axes ClampAxes { get; set; } = Axes.Both; /// /// Whether we should drop-off alpha values of triangles more quickly to improve @@ -257,7 +257,7 @@ namespace osu.Game.Graphics.Backgrounds private IShader shader; private Texture texture; - private bool clamp; + private Axes clampAxes; private readonly List parts = new List(); private readonly Vector2 triangleSize = new Vector2(1f, equilateral_triangle_ratio) * triangle_size; @@ -276,7 +276,7 @@ namespace osu.Game.Graphics.Backgrounds shader = Source.shader; texture = Source.texture; size = Source.DrawSize; - clamp = Source.ClampToDrawable; + clampAxes = Source.ClampAxes; parts.Clear(); parts.AddRange(Source.parts); @@ -306,7 +306,7 @@ namespace osu.Game.Graphics.Backgrounds Vector2 topLeft = particle.Position - new Vector2(relativeSize.X * 0.5f, 0f); - Quad triangleQuad = clamp ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y); + Quad triangleQuad = getClampedQuad(clampAxes, topLeft, relativeSize); var drawQuad = new Quad( Vector2Extensions.Transform(triangleQuad.TopLeft * size, DrawInfo.Matrix), @@ -331,17 +331,23 @@ namespace osu.Game.Graphics.Backgrounds shader.Unbind(); } - private static Quad clampToDrawable(Vector2 topLeft, Vector2 size) + private static Quad getClampedQuad(Axes clampAxes, Vector2 topLeft, Vector2 size) { - float leftClamped = Math.Clamp(topLeft.X, 0f, 1f); - float topClamped = Math.Clamp(topLeft.Y, 0f, 1f); + Vector2 clampedTopLeft = topLeft; - return new Quad( - leftClamped, - topClamped, - Math.Clamp(topLeft.X + size.X, 0f, 1f) - leftClamped, - Math.Clamp(topLeft.Y + size.Y, 0f, 1f) - topClamped - ); + if (clampAxes == Axes.X || clampAxes == Axes.Both) + { + clampedTopLeft.X = Math.Clamp(topLeft.X, 0f, 1f); + size.X = Math.Clamp(topLeft.X + size.X, 0f, 1f) - clampedTopLeft.X; + } + + if (clampAxes == Axes.Y || clampAxes == Axes.Both) + { + clampedTopLeft.Y = Math.Clamp(topLeft.Y, 0f, 1f); + size.Y = Math.Clamp(topLeft.Y + size.Y, 0f, 1f) - clampedTopLeft.Y; + } + + return new Quad(clampedTopLeft.X, clampedTopLeft.Y, size.X, size.Y); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs index f723b1b358..706b05f5ad 100644 --- a/osu.Game/Graphics/Backgrounds/TrianglesV2.cs +++ b/osu.Game/Graphics/Backgrounds/TrianglesV2.cs @@ -33,10 +33,10 @@ namespace osu.Game.Graphics.Backgrounds protected virtual bool CreateNewTriangles => true; /// - /// If enabled, only the portion of triangles that falls within this 's - /// shape is drawn to the screen. Default is true. + /// Controls on which the portion of triangles that falls within this 's + /// shape is drawn to the screen. Default is Axes.Both. /// - public bool ClampToDrawable { get; set; } = true; + public Axes ClampAxes { get; set; } = Axes.Both; private readonly BindableFloat spawnRatio = new BindableFloat(1f); @@ -193,7 +193,7 @@ namespace osu.Game.Graphics.Backgrounds private Vector2 size; private float thickness; private float texelSize; - private bool clamp; + private Axes clampAxes; public TrianglesDrawNode(TrianglesV2 source) : base(source) @@ -208,7 +208,7 @@ namespace osu.Game.Graphics.Backgrounds texture = Source.texture; size = Source.DrawSize; thickness = Source.Thickness; - clamp = Source.ClampToDrawable; + clampAxes = Source.ClampAxes; Quad triangleQuad = new Quad( Vector2Extensions.Transform(Vector2.Zero, DrawInfo.Matrix), @@ -248,7 +248,7 @@ namespace osu.Game.Graphics.Backgrounds { Vector2 topLeft = particle.Position - new Vector2(relativeSize.X * 0.5f, 0f); - Quad triangleQuad = clamp ? clampToDrawable(topLeft, relativeSize) : new Quad(topLeft.X, topLeft.Y, relativeSize.X, relativeSize.Y); + Quad triangleQuad = getClampedQuad(clampAxes, topLeft, relativeSize); var drawQuad = new Quad( Vector2Extensions.Transform(triangleQuad.TopLeft * size, DrawInfo.Matrix), @@ -270,17 +270,23 @@ namespace osu.Game.Graphics.Backgrounds shader.Unbind(); } - private static Quad clampToDrawable(Vector2 topLeft, Vector2 size) + private static Quad getClampedQuad(Axes clampAxes, Vector2 topLeft, Vector2 size) { - float leftClamped = Math.Clamp(topLeft.X, 0f, 1f); - float topClamped = Math.Clamp(topLeft.Y, 0f, 1f); + Vector2 clampedTopLeft = topLeft; - return new Quad( - leftClamped, - topClamped, - Math.Clamp(topLeft.X + size.X, 0f, 1f) - leftClamped, - Math.Clamp(topLeft.Y + size.Y, 0f, 1f) - topClamped - ); + if (clampAxes == Axes.X || clampAxes == Axes.Both) + { + clampedTopLeft.X = Math.Clamp(topLeft.X, 0f, 1f); + size.X = Math.Clamp(topLeft.X + size.X, 0f, 1f) - clampedTopLeft.X; + } + + if (clampAxes == Axes.Y || clampAxes == Axes.Both) + { + clampedTopLeft.Y = Math.Clamp(topLeft.Y, 0f, 1f); + size.Y = Math.Clamp(topLeft.Y + size.Y, 0f, 1f) - clampedTopLeft.Y; + } + + return new Quad(clampedTopLeft.X, clampedTopLeft.Y, size.X, size.Y); } protected override void Dispose(bool isDisposing) diff --git a/osu.Game/Graphics/UserInterface/DialogButton.cs b/osu.Game/Graphics/UserInterface/DialogButton.cs index 10aca017f1..c39f41bf72 100644 --- a/osu.Game/Graphics/UserInterface/DialogButton.cs +++ b/osu.Game/Graphics/UserInterface/DialogButton.cs @@ -150,7 +150,7 @@ namespace osu.Game.Graphics.UserInterface TriangleScale = 4, ColourDark = OsuColour.Gray(0.88f), Shear = new Vector2(-0.2f, 0), - ClampToDrawable = false + ClampAxes = Axes.Y }, }, }, diff --git a/osu.Game/Overlays/Mods/ModSelectColumn.cs b/osu.Game/Overlays/Mods/ModSelectColumn.cs index 05454159c7..b2c5a054e1 100644 --- a/osu.Game/Overlays/Mods/ModSelectColumn.cs +++ b/osu.Game/Overlays/Mods/ModSelectColumn.cs @@ -34,7 +34,7 @@ namespace osu.Game.Overlays.Mods var hsv = new Colour4(value.R, value.G, value.B, 1f).ToHSV(); var trianglesColour = Colour4.FromHSV(hsv.X, hsv.Y + 0.2f, hsv.Z - 0.1f); - triangles.Colour = ColourInfo.GradientVertical(trianglesColour, trianglesColour.MultiplyAlpha(0f)); + triangles.Colour = ColourInfo.GradientVertical(trianglesColour, value); } } @@ -95,7 +95,7 @@ namespace osu.Game.Overlays.Mods Height = header_height, Shear = new Vector2(-ShearedOverlayContainer.SHEAR, 0), Velocity = 0.7f, - ClampToDrawable = false + ClampAxes = Axes.Y }, headerText = new OsuTextFlowContainer(t => { From c8cd7ebe6c66b57cbcfef2d841deacb60fdbd755 Mon Sep 17 00:00:00 2001 From: Dean Herbert Date: Tue, 23 Jan 2024 17:29:31 +0900 Subject: [PATCH 1118/1118] Fix now playing beatmap backgrounds not being correctly centred https://github.com/ppy/osu/discussions/26679 --- osu.Game/Overlays/NowPlayingOverlay.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/osu.Game/Overlays/NowPlayingOverlay.cs b/osu.Game/Overlays/NowPlayingOverlay.cs index 7ec52364d4..ab99370603 100644 --- a/osu.Game/Overlays/NowPlayingOverlay.cs +++ b/osu.Game/Overlays/NowPlayingOverlay.cs @@ -405,6 +405,8 @@ namespace osu.Game.Overlays RelativeSizeAxes = Axes.Both, Colour = OsuColour.Gray(150), FillMode = FillMode.Fill, + Anchor = Anchor.Centre, + Origin = Anchor.Centre, }, new Box {